]> git.ipfire.org Git - people/ms/pakfire.git/blame - src/libpakfire/file.c
archive: Store MIME type of files
[people/ms/pakfire.git] / src / libpakfire / file.c
CommitLineData
221cc3ce
MT
1/*#############################################################################
2# #
3# Pakfire - The IPFire package management system #
4# Copyright (C) 2014 Pakfire development team #
5# #
6# This program is free software: you can redistribute it and/or modify #
7# it under the terms of the GNU General Public License as published by #
8# the Free Software Foundation, either version 3 of the License, or #
9# (at your option) any later version. #
10# #
11# This program is distributed in the hope that it will be useful, #
12# but WITHOUT ANY WARRANTY; without even the implied warranty of #
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14# GNU General Public License for more details. #
15# #
16# You should have received a copy of the GNU General Public License #
17# along with this program. If not, see <http://www.gnu.org/licenses/>. #
18# #
19#############################################################################*/
20
5e9463ec 21#include <errno.h>
c064d9ec 22#include <fnmatch.h>
3fca5032 23#include <libgen.h>
293881bc 24#include <limits.h>
c98132d1 25#include <linux/limits.h>
221cc3ce
MT
26#include <stdlib.h>
27#include <string.h>
a09b95e0 28#include <sys/stat.h>
221cc3ce 29#include <sys/types.h>
221cc3ce
MT
30#include <time.h>
31
9a6e3e2d
MT
32#include <archive_entry.h>
33
df71dc60
MT
34#include <gelf.h>
35
221cc3ce 36#include <pakfire/constants.h>
c52e0148 37#include <pakfire/digest.h>
221cc3ce 38#include <pakfire/file.h>
3fca5032 39#include <pakfire/logging.h>
883b3be9 40#include <pakfire/pakfire.h>
9f953e68 41#include <pakfire/private.h>
d973a13d 42#include <pakfire/string.h>
221cc3ce
MT
43#include <pakfire/util.h>
44
c0b051bb
MT
45enum pakfire_file_verification_status {
46 PAKFIRE_FILE_NOENT = (1 << 0),
9e09e361
MT
47 PAKFIRE_FILE_TYPE_CHANGED = (1 << 1),
48 PAKFIRE_FILE_PERMISSIONS_CHANGED = (1 << 2),
49 PAKFIRE_FILE_DEV_CHANGED = (1 << 3),
50 PAKFIRE_FILE_SIZE_CHANGED = (1 << 4),
51 PAKFIRE_FILE_OWNER_CHANGED = (1 << 5),
52 PAKFIRE_FILE_GROUP_CHANGED = (1 << 6),
0eeac4a5
MT
53 PAKFIRE_FILE_CTIME_CHANGED = (1 << 7),
54 PAKFIRE_FILE_MTIME_CHANGED = (1 << 8),
76011205 55 PAKFIRE_FILE_PAYLOAD_CHANGED = (1 << 9),
c0b051bb
MT
56};
57
5803b5f6 58struct pakfire_file {
ac4c607b 59 struct pakfire* pakfire;
5e9463ec 60 int nrefs;
221cc3ce 61
e8d3b985 62 // The relative path
3b9e3970 63 char path[PATH_MAX];
e8d3b985
MT
64
65 // The absolute path in the file system
0027da6f 66 char abspath[PATH_MAX];
221cc3ce 67
e8d3b985 68 // File Ownership
302e3253
MT
69 char uname[LOGIN_NAME_MAX];
70 char gname[LOGIN_NAME_MAX];
e8d3b985 71
a09b95e0
MT
72 // Stat
73 struct stat st;
d03fa9a3 74
59d8c727
MT
75 // Link destinations
76 char hardlink[PATH_MAX];
77 char symlink[PATH_MAX];
78
65131b30 79 // Digests
c52e0148 80 struct pakfire_digests digests;
5e9463ec 81
210aabe9
MT
82 // MIME Type
83 char mimetype[NAME_MAX];
84
71f6f465
MT
85 // Class
86 int class;
87
c0b051bb
MT
88 // Verification Status
89 int verify_status;
90
f7f44921
MT
91 // Hardening Issues
92 int hardening_issues;
93 int hardening_check_done:1;
94
5e9463ec
MT
95 #warning TODO capabilities, config, data
96 // capabilities
97 //int is_configfile;
98 //int is_datafile;
99};
100
eb5daf3f 101static int pakfire_file_from_archive_entry(struct pakfire_file* file, struct archive_entry* entry) {
210aabe9 102 char* buffer = NULL;
49a39ad9 103 const char* path = NULL;
eb5daf3f
MT
104 const char* attr = NULL;
105 const void* value = NULL;
106 size_t size = 0;
107 int r = 0;
108
109 // Set abspath
49a39ad9
MT
110 path = archive_entry_sourcepath(entry);
111 if (path) {
112 // Make path absolute
113 path = pakfire_path_abspath(path);
004f0160
MT
114 if (!path) {
115 r = 1;
49a39ad9 116 goto ERROR;
004f0160 117 }
49a39ad9
MT
118
119 // Set
120 r = pakfire_file_set_abspath(file, path);
121 if (r) {
122 ERROR(file->pakfire, "Could not set abspath: %m\n");
123 goto ERROR;
124 }
eb5daf3f
MT
125 }
126
127 // Set path
49a39ad9 128 path = archive_entry_pathname(entry);
eb5daf3f 129 if (path) {
eb5daf3f
MT
130 r = pakfire_file_set_path(file, path);
131 if (r) {
132 ERROR(file->pakfire, "Could not set path: %m\n");
133 goto ERROR;
134 }
135 }
136
137 // Set links
138 pakfire_file_set_hardlink(file, archive_entry_hardlink(entry));
139 pakfire_file_set_symlink(file, archive_entry_symlink(entry));
140
f8733b31
MT
141 pakfire_file_set_nlink(file, archive_entry_nlink(entry));
142 pakfire_file_set_inode(file, archive_entry_ino64(entry));
143 pakfire_file_set_dev(file, archive_entry_dev(entry));
144
eb5daf3f
MT
145 // Set size
146 pakfire_file_set_size(file, archive_entry_size(entry));
147
148 // Set mode
149 pakfire_file_set_mode(file, archive_entry_mode(entry));
150
151 // Set dev type
152 if (archive_entry_dev_is_set(entry))
153 pakfire_file_set_dev(file, archive_entry_dev(entry));
154
302e3253
MT
155 // Set uname
156 pakfire_file_set_uname(file, archive_entry_uname(entry));
eb5daf3f 157
302e3253
MT
158 // Set gname
159 pakfire_file_set_gname(file, archive_entry_gname(entry));
eb5daf3f
MT
160
161 // Set times
162 pakfire_file_set_ctime(file, archive_entry_ctime(entry));
163 pakfire_file_set_mtime(file, archive_entry_mtime(entry));
164
210aabe9 165 // Reset iterating over extended attributes
471ccfc2
MT
166 archive_entry_xattr_reset(entry);
167
eb5daf3f
MT
168 // Read any extended attributes
169 while (archive_entry_xattr_next(entry, &attr, &value, &size) == ARCHIVE_OK) {
210aabe9
MT
170 // MIME type
171 if (strcmp(attr, "PAKFIRE.mimetype") == 0) {
172 // Copy the value into a NULL-terminated buffer
173 r = asprintf(&buffer, "%.*s", (int)size, (const char*)value);
174 if (r < 0)
175 goto ERROR;
176
177 // Assign the value
178 r = pakfire_file_set_mimetype(file, buffer);
179 if (r)
180 goto ERROR;
181
d98740de 182 // Digest: SHA-3-512
210aabe9 183 } else if (strcmp(attr, "PAKFIRE.digests.sha3_512") == 0) {
d98740de
MT
184 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_SHA3_512, value, size);
185 if (r)
186 goto ERROR;
187
188 // Digest: SHA-3-256
189 } else if (strcmp(attr, "PAKFIRE.digests.sha3_256") == 0) {
190 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_SHA3_256, value, size);
191 if (r)
192 goto ERROR;
193
f1e6c5df 194 // Digest: BLAKE2b512
d98740de 195 } else if (strcmp(attr, "PAKFIRE.digests.blake2b512") == 0) {
f1e6c5df
MT
196 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_BLAKE2B512, value, size);
197 if (r)
198 goto ERROR;
199
200 // Digest: BLAKE2b512
201 } else if (strcmp(attr, "PAKFIRE.digests.blake2s256") == 0) {
202 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_BLAKE2S256, value, size);
203 if (r)
204 goto ERROR;
205
4500ed0a
MT
206 // Digest: SHA-2-512
207 } else if (strcmp(attr, "PAKFIRE.digests.sha2_512") == 0) {
208 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_SHA2_512, value, size);
eb5daf3f
MT
209 if (r)
210 goto ERROR;
211
4500ed0a
MT
212 // Digest: SHA-2-256
213 } else if (strcmp(attr, "PAKFIRE.digests.sha2_256") == 0) {
214 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_SHA2_256, value, size);
eb5daf3f
MT
215 if (r)
216 goto ERROR;
217
218 } else {
219 DEBUG(file->pakfire, "Received an unknown extended attribute: %s\n", attr);
220 }
221 }
222
223ERROR:
210aabe9
MT
224 if (buffer)
225 free(buffer);
226
eb5daf3f
MT
227 return r;
228}
229
ac4c607b 230PAKFIRE_EXPORT int pakfire_file_create(struct pakfire_file** file, struct pakfire* pakfire) {
5803b5f6 231 struct pakfire_file* f = calloc(1, sizeof(*f));
5e9463ec 232 if (!f)
7403779a 233 return 1;
5e9463ec 234
e8d3b985 235 // Store reference to Pakfire
883b3be9 236 f->pakfire = pakfire_ref(pakfire);
e8d3b985
MT
237
238 // Initialize reference counter
5e9463ec
MT
239 f->nrefs = 1;
240
241 *file = f;
242 return 0;
d03fa9a3
MT
243}
244
61b0856b
MT
245int pakfire_file_create_from_path(struct pakfire_file** file,
246 struct pakfire* pakfire, const char* path) {
247 struct archive* reader = NULL;
248 struct archive_entry* entry = NULL;
249 int r = 1;
250
251 // Allocate a reader
252 reader = pakfire_make_archive_disk_reader(pakfire, 0);
253 if (!reader)
254 goto ERROR;
255
256 // Allocate a new archive entry
257 entry = archive_entry_new();
258 if (!entry)
259 goto ERROR;
260
261 // Set source path
262 archive_entry_copy_sourcepath(entry, path);
263
264 // Read all file attributes from disk
265 r = archive_read_disk_entry_from_file(reader, entry, -1, NULL);
266 if (r) {
267 ERROR(pakfire, "Could not read from %s: %m\n", path);
268 goto ERROR;
269 }
270
271 // Create file
272 r = pakfire_file_create_from_archive_entry(file, pakfire, entry);
273 if (r)
274 goto ERROR;
275
276ERROR:
277 if (r)
278 ERROR(pakfire, "Could not create file from path %s: %m\n", path);
279 if (entry)
280 archive_entry_free(entry);
281 if (reader)
282 archive_read_free(reader);
283
284 return r;
285}
286
ac4c607b 287int pakfire_file_create_from_archive_entry(struct pakfire_file** file, struct pakfire* pakfire,
d0985e31
MT
288 struct archive_entry* entry) {
289 int r = pakfire_file_create(file, pakfire);
290 if (r)
291 return r;
292
293 // Copy archive entry
eb5daf3f 294 r = pakfire_file_from_archive_entry(*file, entry);
d0985e31
MT
295 if (r)
296 goto ERROR;
297
298 return 0;
299
300ERROR:
301 pakfire_file_unref(*file);
302 *file = NULL;
303
304 return r;
305}
306
49a39ad9 307struct archive_entry* pakfire_file_archive_entry(struct pakfire_file* file, int digest_types) {
d7b476ef 308 const char* path = NULL;
d4b2ef2d 309 int r;
d7b476ef 310
5acd852e
MT
311 struct archive_entry* entry = archive_entry_new();
312 if (!entry) {
313 ERROR(file->pakfire, "Could not allocate archive entry: %m\n");
314 return NULL;
315 }
316
5acd852e
MT
317 // Set source path
318 archive_entry_copy_sourcepath(entry, file->abspath);
319
49a39ad9 320 // Set path
d7b476ef
MT
321 path = pakfire_file_get_path(file);
322 if (path && *path == '/') {
323 archive_entry_copy_pathname(entry, path + 1);
324 }
49a39ad9 325
5acd852e
MT
326 // Set links
327 if (*file->hardlink)
328 archive_entry_set_hardlink(entry, file->hardlink);
329 if (*file->symlink)
330 archive_entry_set_symlink(entry, file->symlink);
331
f8733b31
MT
332 archive_entry_set_nlink(entry, pakfire_file_get_nlink(file));
333 archive_entry_set_ino64(entry, pakfire_file_get_inode(file));
334 archive_entry_set_dev(entry, pakfire_file_get_dev(file));
335
5acd852e
MT
336 // Set size
337 archive_entry_set_size(entry, pakfire_file_get_size(file));
338
339 // Set mode
340 archive_entry_set_mode(entry, pakfire_file_get_mode(file));
341
302e3253
MT
342 // Set uname
343 archive_entry_set_uname(entry, pakfire_file_get_uname(file));
5acd852e 344
302e3253
MT
345 // Set gname
346 archive_entry_set_gname(entry, pakfire_file_get_gname(file));
5acd852e
MT
347
348 // Set times
349 archive_entry_set_ctime(entry, pakfire_file_get_ctime(file), 0);
350 archive_entry_set_mtime(entry, pakfire_file_get_mtime(file), 0);
351
210aabe9
MT
352 // Set MIME type
353 const char* mimetype = pakfire_file_get_mimetype(file);
354 if (mimetype) {
355 archive_entry_xattr_add_entry(entry,
356 "PAKFIRE.mimetype", mimetype, strlen(mimetype));
357 }
358
d4b2ef2d
MT
359 // Compute any required file digests
360 r = pakfire_file_compute_digests(file, digest_types);
361 if (r)
362 goto ERROR;
363
399d14c2
MT
364 // Copy digests
365
d98740de 366 // SHA-3-512
045fa504
MT
367 if ((digest_types && PAKFIRE_DIGEST_SHA3_512)
368 && pakfire_digest_set(file->digests.sha3_512))
d98740de
MT
369 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.sha3_512",
370 file->digests.sha3_512, sizeof(file->digests.sha3_512));
371
372 // SHA-3-256
045fa504
MT
373 if ((digest_types && PAKFIRE_DIGEST_SHA3_256) &&
374 pakfire_digest_set(file->digests.sha3_256))
d98740de
MT
375 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.sha3_256",
376 file->digests.sha3_256, sizeof(file->digests.sha3_256));
377
f1e6c5df 378 // BLAKE2b512
045fa504
MT
379 if ((digest_types && PAKFIRE_DIGEST_BLAKE2B512) &&
380 pakfire_digest_set(file->digests.blake2b512))
f1e6c5df
MT
381 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.blake2b512",
382 file->digests.blake2b512, sizeof(file->digests.blake2b512));
383
384 // BLAKE2s256
045fa504
MT
385 if ((digest_types && PAKFIRE_DIGEST_BLAKE2S256) &&
386 pakfire_digest_set(file->digests.blake2s256))
f1e6c5df
MT
387 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.blake2s256",
388 file->digests.blake2s256, sizeof(file->digests.blake2s256));
389
4500ed0a 390 // SHA-2-512
045fa504
MT
391 if ((digest_types && PAKFIRE_DIGEST_SHA2_512) &&
392 pakfire_digest_set(file->digests.sha2_512))
4500ed0a
MT
393 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.sha2_512",
394 file->digests.sha2_512, sizeof(file->digests.sha2_512));
399d14c2 395
4500ed0a 396 // SHA-2-256
045fa504
MT
397 if ((digest_types && PAKFIRE_DIGEST_SHA2_512) &&
398 pakfire_digest_set(file->digests.sha2_256))
4500ed0a
MT
399 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.sha2_256",
400 file->digests.sha2_256, sizeof(file->digests.sha2_256));
5acd852e
MT
401
402 return entry;
d4b2ef2d
MT
403
404ERROR:
405 if (entry)
406 archive_entry_free(entry);
407
408 return NULL;
5acd852e
MT
409}
410
5803b5f6 411static void pakfire_file_free(struct pakfire_file* file) {
5e8dfbeb 412 pakfire_unref(file->pakfire);
f0d6233d 413 free(file);
221cc3ce
MT
414}
415
5803b5f6 416PAKFIRE_EXPORT struct pakfire_file* pakfire_file_ref(struct pakfire_file* file) {
5e9463ec 417 file->nrefs++;
221cc3ce 418
5e9463ec
MT
419 return file;
420}
221cc3ce 421
5803b5f6 422PAKFIRE_EXPORT struct pakfire_file* pakfire_file_unref(struct pakfire_file* file) {
5e9463ec
MT
423 if (--file->nrefs > 0)
424 return file;
425
426 pakfire_file_free(file);
427 return NULL;
221cc3ce
MT
428}
429
5852b822
MT
430#define pakfire_file_strmode(file, buffer) \
431 __pakfire_file_strmode(file, buffer, sizeof(buffer))
432
433static int __pakfire_file_strmode(struct pakfire_file* file, char* s, const size_t length) {
434 int r;
435
436 static const mode_t permbits[] = {
437 0400,
438 0200,
439 0100,
440 0040,
441 0020,
442 0010,
443 0004,
444 0002,
445 0001
446 };
447
448 const mode_t mode = pakfire_file_get_mode(file);
449 const mode_t type = pakfire_file_get_type(file);
450
451 // Set some default string
452 r = __pakfire_string_set(s, length, "?rwxrwxrwx ");
453 if (r)
454 return r;
455
456 switch (type) {
457 case S_IFREG:
458 s[0] = '-';
459 break;
460
461 case S_IFBLK:
462 s[0] = 'b';
463 break;
464
465 case S_IFCHR:
466 s[0] = 'c';
467 break;
468
469 case S_IFDIR:
470 s[0] = 'd';
471 break;
472
473 case S_IFLNK:
474 s[0] = 'l';
475 break;
476
477 case S_IFSOCK:
478 s[0] = 's';
479 break;
480
481 case S_IFIFO:
482 s[0] = 'p';
483 break;
484
485 default:
486 if (*file->hardlink) {
487 s[0] = 'h';
488 break;
489 }
490 }
491
492 for (unsigned int i = 0; i < 9; i++) {
493 if (mode & permbits[i])
494 continue;
495
496 s[i+1] = '-';
497 }
498
499 if (mode & S_ISUID) {
500 if (mode & 0100)
501 s[3] = 's';
502 else
503 s[3] = 'S';
504 }
505
506 if (mode & S_ISGID) {
507 if (mode & 0010)
508 s[6] = 's';
509 else
510 s[6] = 'S';
511 }
512
513 if (mode & S_ISVTX) {
514 if (mode & 0001)
515 s[9] = 't';
516 else
517 s[9] = 'T';
518 }
519
520#if 0
521 if (file->caps)
522 s[10] = '+';
523#endif
524
525 return 0;
526}
527
f7f44921
MT
528char* pakfire_file_dump(struct pakfire_file* file, int flags) {
529 char* buffer = "";
5852b822
MT
530 int r;
531
532 char mode[12];
533 char time[32];
534
535 // Format mode
f7f44921
MT
536 if (flags & PAKFIRE_FILE_DUMP_MODE) {
537 r = pakfire_file_strmode(file, mode);
538 if (r)
539 goto ERROR;
540
541 r = asprintf(&buffer, "%s %s", buffer, mode);
542 if (r < 0)
543 goto ERROR;
544 }
545
546 // Format ownership
547 if (flags & PAKFIRE_FILE_DUMP_OWNERSHIP) {
548 r = asprintf(&buffer, "%s %s/%s", buffer, file->uname, file->gname);
549 if (r < 0)
550 goto ERROR;
551 }
552
553 // Format size
554 if (flags & PAKFIRE_FILE_DUMP_SIZE) {
555 r = asprintf(&buffer, "%s %8zu", buffer, file->st.st_size);
556 if (r < 0)
557 goto ERROR;
558 }
5852b822
MT
559
560 // Format time
f7f44921
MT
561 if (flags & PAKFIRE_FILE_DUMP_TIME) {
562 r = pakfire_strftime(time, "%Y-%m-%d %H:%M", file->st.st_ctime);
563 if (r)
564 goto ERROR;
565
566 r = asprintf(&buffer, "%s %s", buffer, time);
567 if (r < 0)
568 goto ERROR;
569 }
5852b822 570
f7f44921
MT
571 // Format path
572 r = asprintf(&buffer, "%s %s", buffer, file->path);
5852b822 573 if (r < 0)
f7f44921 574 goto ERROR;
5852b822 575
a3bf05ad 576 // Append symlink target
f7f44921
MT
577 if (flags & PAKFIRE_FILE_DUMP_LINK_TARGETS) {
578 switch (pakfire_file_get_type(file)) {
579 case S_IFLNK:
580 r = asprintf(&buffer, "%s -> %s", buffer, file->symlink);
581 if (r < 0)
582 return NULL;
a3bf05ad 583
f7f44921
MT
584 default:
585 break;
586 }
587 }
588
589 // Hardning Status
590 if (flags & PAKFIRE_FILE_DUMP_HARDENING) {
591 if (pakfire_file_matches_class(file, PAKFIRE_FILE_ELF)) {
592 // Stack-smashing Protection
593 if (file->hardening_issues & PAKFIRE_FILE_NO_SSP) {
594 r = asprintf(&buffer, "%s [NO-SSP]", buffer);
595 if (r < 0)
596 goto ERROR;
597 }
598
599 // Position-independent Executable
600 if (file->hardening_issues & PAKFIRE_FILE_NO_PIE) {
601 r = asprintf(&buffer, "%s [NO-PIE]", buffer);
602 if (r < 0)
603 goto ERROR;
604 }
03d5abd3
MT
605
606 // Executable Stack
607 if (file->hardening_issues & PAKFIRE_FILE_EXECSTACK) {
608 r = asprintf(&buffer, "%s [EXECSTACK]", buffer);
609 if (r < 0)
610 goto ERROR;
611 }
612
613 // Not Partially RELRO
614 if (file->hardening_issues & PAKFIRE_FILE_NO_PARTIALLY_RELRO) {
615 r = asprintf(&buffer, "%s [NO-PART-RELRO]", buffer);
616 if (r < 0)
617 goto ERROR;
618 }
f7f44921 619 }
a3bf05ad
MT
620 }
621
5852b822 622 return buffer;
f7f44921
MT
623
624ERROR:
625 if (buffer)
626 free(buffer);
627
628 return NULL;
5852b822
MT
629}
630
5803b5f6 631PAKFIRE_EXPORT int pakfire_file_cmp(struct pakfire_file* file1, struct pakfire_file* file2) {
32485f6c
MT
632 const char* path1 = pakfire_file_get_path(file1);
633 const char* path2 = pakfire_file_get_path(file2);
221cc3ce 634
32485f6c 635 return strcmp(path1, path2);
221cc3ce
MT
636}
637
5803b5f6 638const char* pakfire_file_get_abspath(struct pakfire_file* file) {
e4c2f7a9
MT
639 return file->abspath;
640}
641
5803b5f6 642int pakfire_file_set_abspath(struct pakfire_file* file, const char* path) {
49a39ad9
MT
643 int r;
644
d13bd5b7
MT
645 // Check if path is set and absolute
646 if (!path || *path != '/') {
647 errno = EINVAL;
648 return 1;
649 }
650
49a39ad9
MT
651 // Store the abspath
652 r = pakfire_string_set(file->abspath, path);
653 if (r)
654 goto ERROR;
655
656 // Store path if it isn't set, yet
657 if (!*file->path) {
658 r = pakfire_file_set_path(file, path);
659 if (r)
660 goto ERROR;
661 }
662
663 return r;
664
665ERROR:
666 ERROR(file->pakfire, "Could not set abspath '%s': %m\n", path);
667 return r;
3b9e3970
MT
668}
669
5803b5f6 670PAKFIRE_EXPORT const char* pakfire_file_get_path(struct pakfire_file* file) {
32485f6c 671 return file->path;
221cc3ce
MT
672}
673
5803b5f6 674PAKFIRE_EXPORT int pakfire_file_set_path(struct pakfire_file* file, const char* path) {
12a327de 675 int r = 1;
520213e9 676
2cc8b5f4
MT
677 // Check if path is set
678 if (!path) {
d13bd5b7 679 errno = EINVAL;
12a327de 680 goto ERROR;
d13bd5b7
MT
681 }
682
2cc8b5f4
MT
683 // Strip any leading dots from paths
684 if (pakfire_string_startswith(path, "./"))
685 path++;
686
687 switch (*path) {
688 // Just store the path if it is absolute
689 case '/':
690 r = pakfire_string_set(file->path, path);
691 if (r)
692 goto ERROR;
693 break;
694
695 // Handle relative paths
696 default:
697 r = pakfire_string_format(file->path, "/%s", path);
698 if (r)
699 goto ERROR;
700 break;
701 }
520213e9
MT
702
703 // Set abspath if it isn't set, yet
704 if (!*file->abspath) {
2cc8b5f4 705 r = pakfire_file_set_abspath(file, file->path);
520213e9 706 if (r)
12a327de 707 goto ERROR;
520213e9
MT
708 }
709
710 return r;
12a327de
MT
711
712ERROR:
713 ERROR(file->pakfire, "Could not set path '%s': %m\n", path);
714 return r;
221cc3ce
MT
715}
716
59d8c727
MT
717PAKFIRE_EXPORT const char* pakfire_file_get_hardlink(struct pakfire_file* file) {
718 if (!*file->hardlink)
719 return NULL;
720
721 return file->hardlink;
722}
723
724PAKFIRE_EXPORT void pakfire_file_set_hardlink(struct pakfire_file* file, const char* link) {
3c8eb581 725 pakfire_string_set(file->hardlink, link);
59d8c727
MT
726}
727
728PAKFIRE_EXPORT const char* pakfire_file_get_symlink(struct pakfire_file* file) {
729 if (!*file->symlink)
730 return NULL;
731
732 return file->symlink;
733}
734
735PAKFIRE_EXPORT void pakfire_file_set_symlink(struct pakfire_file* file, const char* link) {
3c8eb581 736 pakfire_string_set(file->symlink, link);
59d8c727
MT
737}
738
f8733b31
MT
739PAKFIRE_EXPORT nlink_t pakfire_file_get_nlink(struct pakfire_file* file) {
740 return file->st.st_nlink;
741}
742
743PAKFIRE_EXPORT void pakfire_file_set_nlink(struct pakfire_file* file, const nlink_t nlink) {
744 file->st.st_nlink = nlink;
745}
746
747PAKFIRE_EXPORT ino_t pakfire_file_get_inode(struct pakfire_file* file) {
748 return file->st.st_ino;
749}
750
751PAKFIRE_EXPORT void pakfire_file_set_inode(struct pakfire_file* file, const ino_t ino) {
752 file->st.st_ino = ino;
753}
754
755PAKFIRE_EXPORT dev_t pakfire_file_get_dev(struct pakfire_file* file) {
756 return file->st.st_dev;
757}
758
759PAKFIRE_EXPORT void pakfire_file_set_dev(struct pakfire_file* file, const dev_t dev) {
760 file->st.st_dev = dev;
761}
762
5803b5f6 763PAKFIRE_EXPORT int pakfire_file_get_type(struct pakfire_file* file) {
a09b95e0 764 return file->st.st_mode & S_IFMT;
221cc3ce
MT
765}
766
a09b95e0
MT
767PAKFIRE_EXPORT off_t pakfire_file_get_size(struct pakfire_file* file) {
768 return file->st.st_size;
221cc3ce
MT
769}
770
a09b95e0
MT
771PAKFIRE_EXPORT void pakfire_file_set_size(struct pakfire_file* file, off_t size) {
772 file->st.st_size = size;
221cc3ce
MT
773}
774
302e3253
MT
775PAKFIRE_EXPORT const char* pakfire_file_get_uname(struct pakfire_file* file) {
776 return file->uname;
221cc3ce
MT
777}
778
302e3253
MT
779PAKFIRE_EXPORT int pakfire_file_set_uname(struct pakfire_file* file, const char* uname) {
780 return pakfire_string_set(file->uname, uname);
221cc3ce
MT
781}
782
302e3253
MT
783PAKFIRE_EXPORT const char* pakfire_file_get_gname(struct pakfire_file* file) {
784 return file->gname;
221cc3ce
MT
785}
786
302e3253
MT
787PAKFIRE_EXPORT int pakfire_file_set_gname(struct pakfire_file* file, const char* gname) {
788 return pakfire_string_set(file->gname, gname);
221cc3ce
MT
789}
790
5803b5f6 791PAKFIRE_EXPORT mode_t pakfire_file_get_mode(struct pakfire_file* file) {
a09b95e0 792 return file->st.st_mode;
221cc3ce
MT
793}
794
5803b5f6 795PAKFIRE_EXPORT void pakfire_file_set_mode(struct pakfire_file* file, mode_t mode) {
a09b95e0 796 file->st.st_mode = mode;
221cc3ce
MT
797}
798
134545d5 799PAKFIRE_EXPORT mode_t pakfire_file_get_perms(struct pakfire_file* file) {
966368aa 800 return file->st.st_mode & ~S_IFMT;
134545d5
MT
801}
802
803PAKFIRE_EXPORT void pakfire_file_set_perms(struct pakfire_file* file, const mode_t perms) {
804 // Clear any previous permissions
a09b95e0 805 file->st.st_mode &= S_IFMT;
134545d5
MT
806
807 // Set new bits (with format cleared)
a09b95e0 808 file->st.st_mode |= ~S_IFMT & perms;
134545d5
MT
809}
810
5803b5f6 811PAKFIRE_EXPORT time_t pakfire_file_get_ctime(struct pakfire_file* file) {
a09b95e0 812 return file->st.st_ctime;
ef4e8460
MT
813}
814
5803b5f6 815PAKFIRE_EXPORT void pakfire_file_set_ctime(struct pakfire_file* file, time_t time) {
a09b95e0 816 file->st.st_ctime = time;
ef4e8460
MT
817}
818
5803b5f6 819PAKFIRE_EXPORT time_t pakfire_file_get_mtime(struct pakfire_file* file) {
a09b95e0 820 return file->st.st_mtime;
221cc3ce
MT
821}
822
5803b5f6 823PAKFIRE_EXPORT void pakfire_file_set_mtime(struct pakfire_file* file, time_t time) {
a09b95e0 824 file->st.st_mtime = time;
221cc3ce
MT
825}
826
65131b30 827PAKFIRE_EXPORT const unsigned char* pakfire_file_get_digest(
c52e0148 828 struct pakfire_file* file, const enum pakfire_digest_types type, size_t* length) {
65131b30 829
9802aaf6 830 switch (type) {
d98740de
MT
831 case PAKFIRE_DIGEST_SHA3_512:
832 if (!pakfire_digest_set(file->digests.sha3_512))
833 return NULL;
834
835 if (length)
836 *length = sizeof(file->digests.sha3_512);
837
838 return file->digests.sha3_512;
839
840 case PAKFIRE_DIGEST_SHA3_256:
841 if (!pakfire_digest_set(file->digests.sha3_256))
842 return NULL;
843
844 if (length)
845 *length = sizeof(file->digests.sha3_256);
846
847 return file->digests.sha3_256;
848
f1e6c5df
MT
849 case PAKFIRE_DIGEST_BLAKE2B512:
850 if (!pakfire_digest_set(file->digests.blake2b512))
851 return NULL;
852
853 if (length)
854 *length = sizeof(file->digests.blake2b512);
855
856 return file->digests.blake2b512;
857
858 case PAKFIRE_DIGEST_BLAKE2S256:
859 if (!pakfire_digest_set(file->digests.blake2s256))
860 return NULL;
861
862 if (length)
863 *length = sizeof(file->digests.blake2s256);
864
865 return file->digests.blake2s256;
866
4500ed0a
MT
867 case PAKFIRE_DIGEST_SHA2_512:
868 if (!pakfire_digest_set(file->digests.sha2_512))
9802aaf6 869 return NULL;
65131b30 870
9802aaf6 871 if (length)
4500ed0a 872 *length = sizeof(file->digests.sha2_512);
65131b30 873
4500ed0a 874 return file->digests.sha2_512;
9802aaf6 875
4500ed0a
MT
876 case PAKFIRE_DIGEST_SHA2_256:
877 if (!pakfire_digest_set(file->digests.sha2_256))
9802aaf6 878 return NULL;
5e8dfbeb 879
9802aaf6 880 if (length)
4500ed0a 881 *length = sizeof(file->digests.sha2_256);
5e8dfbeb 882
4500ed0a 883 return file->digests.sha2_256;
2c4b4a02
MT
884
885 case PAKFIRE_DIGEST_UNDEFINED:
886 break;
5e8dfbeb
MT
887 }
888
9802aaf6 889 return NULL;
5e8dfbeb
MT
890}
891
892PAKFIRE_EXPORT int pakfire_file_set_digest(struct pakfire_file* file,
c52e0148 893 const enum pakfire_digest_types type, const unsigned char* digest, const size_t length) {
9802aaf6 894 if (!digest) {
5e8dfbeb
MT
895 errno = EINVAL;
896 return 1;
897 }
898
399d14c2
MT
899 // Check buffer length
900 if (pakfire_digest_length(type) != length) {
901 ERROR(file->pakfire, "Digest has an incorrect length of %zu byte(s)\n", length);
902 errno = ENOMSG;
903 return 1;
904 }
905
906 // Store the digest
9802aaf6 907 switch (type) {
d98740de
MT
908 case PAKFIRE_DIGEST_SHA3_512:
909 memcpy(file->digests.sha3_512, digest, sizeof(file->digests.sha3_512));
910 break;
911
912 case PAKFIRE_DIGEST_SHA3_256:
913 memcpy(file->digests.sha3_256, digest, sizeof(file->digests.sha3_256));
914 break;
915
f1e6c5df
MT
916 case PAKFIRE_DIGEST_BLAKE2B512:
917 memcpy(file->digests.blake2b512, digest, sizeof(file->digests.blake2b512));
918 break;
919
920 case PAKFIRE_DIGEST_BLAKE2S256:
921 memcpy(file->digests.blake2s256, digest, sizeof(file->digests.blake2s256));
922 break;
923
4500ed0a
MT
924 case PAKFIRE_DIGEST_SHA2_512:
925 memcpy(file->digests.sha2_512, digest, sizeof(file->digests.sha2_512));
9802aaf6 926 break;
65131b30 927
4500ed0a
MT
928 case PAKFIRE_DIGEST_SHA2_256:
929 memcpy(file->digests.sha2_256, digest, sizeof(file->digests.sha2_256));
9802aaf6 930 break;
2c4b4a02
MT
931
932 case PAKFIRE_DIGEST_UNDEFINED:
933 errno = ENOTSUP;
934 return 1;
5e8dfbeb
MT
935 }
936
5e8dfbeb 937 return 0;
221cc3ce
MT
938}
939
5803b5f6 940static int pakfire_file_levels(struct pakfire_file* file) {
3fca5032
MT
941 if (!*file->path)
942 return 0;
943
944 int levels = 0;
945
946 for (char* p = file->path; *p; p++) {
947 if (*p == '/')
948 levels++;
949 }
950
951 return levels;
952}
953
d5a13ade
MT
954FILE* pakfire_file_open(struct pakfire_file* file) {
955 FILE* f = fopen(file->abspath, "r");
956 if (!f)
957 ERROR(file->pakfire, "Could not open %s: %m\n", file->abspath);
958
959 return f;
960}
961
1e76689a
MT
962static int __pakfire_file_compute_digests(struct pakfire_file* file,
963 struct pakfire_digests* digests, const int types) {
964 FILE* f = NULL;
965 int r = 1;
966
967 // Skip this for anything that isn't a regular file
968 if (!S_ISREG(file->st.st_mode))
969 return 0;
970
971 // Reset digests
972 pakfire_digests_reset(digests, types);
973
974 // Open the file
975 f = pakfire_file_open(file);
976 if (!f)
977 goto ERROR;
978
979 // Compute digests
980 r = pakfire_digests_compute_from_file(file->pakfire, digests, types, f);
981 if (r)
982 goto ERROR;
983
984ERROR:
985 if (f)
986 fclose(f);
987
988 return r;
989}
990
991int pakfire_file_compute_digests(struct pakfire_file* file, const int types) {
6b32db11 992 return __pakfire_file_compute_digests(file, &file->digests, types);
1e76689a
MT
993}
994
ac71886a 995int pakfire_file_remove(struct pakfire_file* file) {
3fca5032
MT
996 if (!*file->abspath) {
997 errno = EINVAL;
998 return 1;
999 }
1000
1001 DEBUG(file->pakfire, "Removing %s...\n", file->path);
1002
1003 int r = remove(file->abspath);
1004 if (r) {
da89e02f
MT
1005 switch (errno) {
1006 // Ignore when we could not remove directories
1007 case ENOTEMPTY:
1008 return 0;
1009
1010 // Ignore if the file didn't exist
1011 case ENOENT:
1012 return 0;
1013
1014 default:
1015 break;
1016 }
3fca5032 1017
b1772bfb 1018 ERROR(file->pakfire, "Could not remove %s (%s): %m\n", file->path, file->abspath);
3fca5032
MT
1019 }
1020
ac71886a
MT
1021 return r;
1022}
1023
7434fa90
MT
1024int pakfire_file_symlink_target_exists(struct pakfire_file* file) {
1025 // Fail if this got called for anything that isn't a symlink
1026 if (!S_ISLNK(file->st.st_mode)) {
1027 errno = EINVAL;
1028 return -1;
1029 }
1030
1031 return pakfire_path_exists(file->abspath);
1032}
1033
210aabe9
MT
1034/*
1035 MIME Type
1036*/
1037
1038int pakfire_file_detect_mimetype(struct pakfire_file* file) {
1039 // Only process regular files
1040 if (!S_ISREG(file->st.st_mode))
1041 return 0;
1042
1043 // Skip if MIME type is already set
1044 if (*file->mimetype)
1045 return 0;
1046
1047 // Fetch the magic cookie
1048 magic_t magic = pakfire_get_magic(file->pakfire);
1049 if (!magic)
1050 return 1;
1051
1052 // Check the file
1053 const char* mimetype = magic_file(magic, file->abspath);
1054 if (!mimetype) {
1055 ERROR(file->pakfire, "Could not classify %s: %s\n", file->path, magic_error(magic));
1056 return 1;
1057 }
1058
1059 DEBUG(file->pakfire, "Classified %s as %s\n", file->path, mimetype);
1060
1061 // Store the value
1062 return pakfire_file_set_mimetype(file, mimetype);
1063}
1064
1065PAKFIRE_EXPORT const char* pakfire_file_get_mimetype(struct pakfire_file* file) {
1066 // Return nothing on an empty mimetype
1067 if (!*file->mimetype)
1068 return NULL;
1069
1070 return file->mimetype;
1071}
1072
1073PAKFIRE_EXPORT int pakfire_file_set_mimetype(
1074 struct pakfire_file* file, const char* mimetype) {
1075 // Store the value
1076 return pakfire_string_set(file->mimetype, mimetype);
1077}
1078
71f6f465
MT
1079/*
1080 Classification
1081*/
1082
df71dc60
MT
1083static int setup_libelf(struct pakfire* pakfire) {
1084 // Initialize libelf
1085 if (elf_version(EV_CURRENT) == EV_NONE) {
1086 ERROR(pakfire, "Could not initialize libelf: %s\n", elf_errmsg(-1));
1087
1088 return 1;
1089 }
1090
1091 return 0;
1092}
1093
71f6f465
MT
1094static int pakfire_file_classify_mode(struct pakfire_file* file) {
1095 // Check for regular files
1096 if (S_ISREG(file->st.st_mode))
1097 file->class |= PAKFIRE_FILE_REGULAR;
1098
1099 // Check for directories
1100 else if (S_ISDIR(file->st.st_mode))
1101 file->class |= PAKFIRE_FILE_DIRECTORY;
1102
1103 // Check for symlinks
1104 else if (S_ISLNK(file->st.st_mode))
1105 file->class |= PAKFIRE_FILE_SYMLINK;
1106
1107 // Check for character devices
1108 else if (S_ISCHR(file->st.st_mode))
1109 file->class |= PAKFIRE_FILE_CHARACTER;
1110
1111 // Check for block devices
1112 else if (S_ISBLK(file->st.st_mode))
1113 file->class |= PAKFIRE_FILE_BLOCK;
1114
1115 // Check for FIFO pipes
1116 else if (S_ISFIFO(file->st.st_mode))
1117 file->class |= PAKFIRE_FILE_FIFO;
1118
1119 // Check for sockets
1120 else if (S_ISSOCK(file->st.st_mode))
1121 file->class |= PAKFIRE_FILE_SOCKET;
1122
1123 return 0;
1124}
1125
1126static const struct extension {
1127 const char* extension;
1128 int class;
1129} extensions[] = {
d0831491
MT
1130 { "*.a", PAKFIRE_FILE_STATIC_LIBRARY },
1131 { "*.la", PAKFIRE_FILE_LIBTOOL_ARCHIVE },
71f6f465
MT
1132 { "*.pm", PAKFIRE_FILE_PERL },
1133 { "*.pc", PAKFIRE_FILE_PKGCONFIG },
1134 { NULL , 0 },
1135};
1136
1137static int pakfire_file_classify_extension(struct pakfire_file* file) {
1138 for (const struct extension* e = extensions; e->extension; e++) {
1139 if (pakfire_file_matches(file, e->extension)) {
1140 file->class |= e->class;
1141 break;
1142 }
1143 }
1144
1145 return 0;
1146}
1147
1148static const struct mimetype {
1149 const char* mimetype;
1150 int class;
1151} mimetypes[] = {
210aabe9 1152 { "application/x-pie-executable", PAKFIRE_FILE_EXECUTABLE },
71f6f465
MT
1153 { "application/x-sharedlibary", PAKFIRE_FILE_EXECUTABLE },
1154 { "text/x-perl", PAKFIRE_FILE_PERL },
1155 { NULL, 0 },
1156};
1157
1158static int pakfire_file_classify_magic(struct pakfire_file* file) {
210aabe9
MT
1159 int r;
1160
71f6f465
MT
1161 // Don't run this if the file has already been classified
1162 if (file->class & ~PAKFIRE_FILE_REGULAR)
1163 return 0;
1164
210aabe9
MT
1165 // Detect the MIME type
1166 r = pakfire_file_detect_mimetype(file);
1167 if (r)
1168 return r;
71f6f465 1169
210aabe9
MT
1170 // Fetch the MIME type
1171 const char* mimetype = pakfire_file_get_mimetype(file);
1172 if (!mimetype)
71f6f465 1173 return 1;
71f6f465 1174
210aabe9 1175 // Match the MIME type with a flag
71f6f465
MT
1176 for (const struct mimetype* m = mimetypes; m->mimetype; m++) {
1177 if (strcmp(m->mimetype, mimetype) == 0) {
1178 file->class |= m->class;
1179 break;
1180 }
1181 }
1182
1183 return 0;
1184}
1185
df71dc60
MT
1186static int pakfire_file_classify_elf(struct pakfire_file* file) {
1187 FILE* f = NULL;
1188 Elf* elf = NULL;
1189 int r;
1190
1191 // Don't run this if we already know that file is an ELF file
1192 if (file->class & PAKFIRE_FILE_ELF)
1193 return 0;
1194
1195 // Setup libelf
1196 r = setup_libelf(file->pakfire);
1197 if (r)
1198 return r;
1199
1200 // Open the file
1201 f = fopen(file->abspath, "r");
1202 if (!f) {
1203 ERROR(file->pakfire, "Could not open %s: %m\n", file->path);
1204 return 1;
1205 }
1206
1207 // Try to open the ELF file
1208 elf = elf_begin(fileno(f), ELF_C_READ, NULL);
1209 if (!elf) {
1210 // We fail silently here, because this file might be in a different format
1211 goto ERROR;
1212 }
1213
1214 switch (elf_kind(elf)) {
1215 // Mark this file as an ELF file
1216 case ELF_K_ELF:
1217 file->class |= PAKFIRE_FILE_ELF;
1218 break;
1219
1220 // Ignore everything else
1221 default:
1222 break;
1223 }
1224
1225ERROR:
1226 if (elf)
1227 elf_end(elf);
1228 if (f)
1229 fclose(f);
1230
1231 return 0;
1232}
1233
71f6f465
MT
1234int pakfire_file_classify(struct pakfire_file* file) {
1235 int r;
1236
1237 if (!file->class) {
1238 // First, check the mode so that we won't run magic on directories, symlinks, ...
1239 r = pakfire_file_classify_mode(file);
1240 if (r)
1241 goto ERROR;
1242
1243 // Only run this for regular files
1244 if (file->class & PAKFIRE_FILE_REGULAR) {
1245 // Then check for the extension
1246 r = pakfire_file_classify_extension(file);
1247 if (r)
1248 goto ERROR;
1249
1250 // After that, we will use libmagic...
1251 r = pakfire_file_classify_magic(file);
1252 if (r)
df71dc60
MT
1253 goto ERROR;
1254
1255 // Check if the file is an ELF file
1256 r = pakfire_file_classify_elf(file);
1257 if (r)
71f6f465
MT
1258 goto ERROR;
1259 }
1260 }
1261
1262 return file->class;
1263
1264ERROR:
1265 // Reset the class
1266 file->class = PAKFIRE_FILE_UNKNOWN;
1267
1268 return r;
1269}
1270
1271int pakfire_file_matches_class(struct pakfire_file* file, const int class) {
1272 return pakfire_file_classify(file) & class;
1273}
1274
ac71886a
MT
1275/*
1276 This function tries to remove the file after it has been packaged.
1277
1278 It will try to delete any parent directories as well and ignore if directories
1279 cannot be deleted because they might contain other files
1280*/
1281int pakfire_file_cleanup(struct pakfire_file* file) {
1282 char path[PATH_MAX];
1283
1284 // Try removing the file
1285 int r = pakfire_file_remove(file);
1286 if (r)
1287 return r;
1288
3fca5032
MT
1289 // Create a working copy of abspath
1290 r = pakfire_string_set(path, file->abspath);
a60955af 1291 if (r)
3fca5032
MT
1292 return r;
1293
1294 // See how many levels this file has
1295 int levels = pakfire_file_levels(file);
1296
1297 // Walk all the way up and remove all parent directories if possible
1298 while (--levels) {
1299 dirname(path);
1300
1301 // Break if path is suddenly empty
1302 if (!*path)
1303 break;
1304
a8783709
MT
1305 DEBUG(file->pakfire, "Trying to remove parent directory %s\n", path);
1306
3fca5032 1307 r = rmdir(path);
3fca5032 1308
a8783709
MT
1309 // Break on any error
1310 if (r)
1311 break;
3fca5032
MT
1312 }
1313
a942166c 1314 return 0;
3fca5032 1315}
cf9bd6f4 1316
9e09e361 1317static int pakfire_file_verify_mode(struct pakfire_file* file, const struct stat* st) {
a09b95e0
MT
1318 const mode_t type = pakfire_file_get_type(file);
1319
9e09e361 1320 // Did the type change?
a09b95e0 1321 if (type != (st->st_mode & S_IFMT)) {
9e09e361
MT
1322 file->verify_status |= PAKFIRE_FILE_TYPE_CHANGED;
1323
1324 DEBUG(file->pakfire, "%s: File Type changed\n", file->path);
1325 }
1326
a09b95e0
MT
1327 const mode_t perms = pakfire_file_get_perms(file);
1328
9e09e361 1329 // Check permissions
a09b95e0 1330 if (perms != (st->st_mode & 0777)) {
9e09e361
MT
1331 file->verify_status |= PAKFIRE_FILE_PERMISSIONS_CHANGED;
1332
1333 DEBUG(file->pakfire, "%s: Permissions changed\n", file->path);
1334 }
1335
f8733b31
MT
1336#if 0
1337 // XXX This does not check what it is supposed to check
1338
9e09e361
MT
1339 // Check if device node changed
1340 if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
a09b95e0
MT
1341 const dev_t dev = pakfire_file_get_dev(file);
1342
1343 if (dev != st->st_dev) {
9e09e361
MT
1344 file->verify_status |= PAKFIRE_FILE_DEV_CHANGED;
1345
1346 DEBUG(file->pakfire, "%s: Device Node changed\n", file->path);
1347 }
1348 }
f8733b31 1349#endif
9e09e361
MT
1350
1351 return 0;
1352}
1353
c0b051bb
MT
1354static int pakfire_file_verify_size(struct pakfire_file* file, const struct stat* st) {
1355 // Nothing to do if size matches
a09b95e0 1356 if (file->st.st_size == st->st_size)
c0b051bb
MT
1357 return 0;
1358
1359 // Size differs
d2b0e219 1360 file->verify_status |= PAKFIRE_FILE_SIZE_CHANGED;
c0b051bb
MT
1361
1362 DEBUG(file->pakfire, "%s: Filesize differs (expected %zu, got %zu byte(s))\n",
a09b95e0 1363 file->path, file->st.st_size, st->st_size);
c0b051bb
MT
1364
1365 return 0;
1366}
1367
1368static int pakfire_file_verify_ownership(struct pakfire_file* file, const struct stat* st) {
1369 // Fetch UID/GID
1370#if 0
1371 const uid_t uid = pakfire_unmap_id(file->pakfire, st->st_uid);
1372 const gid_t gid = pakfire_unmap_id(file->pakfire, st->st_gid);
1373#else
1374 const uid_t uid = st->st_uid;
1375 const gid_t gid = st->st_gid;
1376#endif
1377
1378 // Fetch owner & group
302e3253
MT
1379 struct passwd* owner = pakfire_getpwnam(file->pakfire, file->uname);
1380 struct group* group = pakfire_getgrnam(file->pakfire, file->gname);
c0b051bb
MT
1381
1382 // Check if owner matches
1383 if (!owner || owner->pw_uid != uid) {
d2b0e219 1384 file->verify_status |= PAKFIRE_FILE_OWNER_CHANGED;
c0b051bb
MT
1385
1386 DEBUG(file->pakfire, "%s: Owner differs\n", file->path);
1387 }
1388
1389 // Check if group matches
1390 if (!group || group->gr_gid != gid) {
d2b0e219 1391 file->verify_status |= PAKFIRE_FILE_GROUP_CHANGED;
c0b051bb
MT
1392
1393 DEBUG(file->pakfire, "%s: Group differs\n", file->path);
1394 }
1395
1396 return 0;
1397}
1398
0eeac4a5
MT
1399static int pakfire_file_verify_timestamps(struct pakfire_file* file, const struct stat* st) {
1400 // Check creation time
a09b95e0 1401 if (file->st.st_ctime != st->st_ctime) {
0eeac4a5
MT
1402 file->verify_status |= PAKFIRE_FILE_CTIME_CHANGED;
1403
1404 DEBUG(file->pakfire, "%s: Creation time changed\n", file->path);
1405 }
1406
1407 // Check modification time
a09b95e0 1408 if (file->st.st_mtime != st->st_mtime) {
0eeac4a5
MT
1409 file->verify_status |= PAKFIRE_FILE_MTIME_CHANGED;
1410
1411 DEBUG(file->pakfire, "%s: Modification time changed\n", file->path);
1412 }
1413
1414 return 0;
1415}
1416
76011205 1417static int pakfire_file_verify_payload(struct pakfire_file* file, const struct stat* st) {
76011205
MT
1418 int r;
1419
c52e0148 1420 struct pakfire_digests computed_digests;
293881bc 1421 int digest_types = PAKFIRE_DIGEST_UNDEFINED;
76011205
MT
1422
1423 // Nothing to do for anything that isn't a regular file
1424 if (!S_ISREG(st->st_mode))
1425 return 0;
1426
1427 // Fast-path if size changed. The payload will have changed, too
1428 if (file->verify_status & PAKFIRE_FILE_SIZE_CHANGED) {
1429 file->verify_status |= PAKFIRE_FILE_PAYLOAD_CHANGED;
1430 return 0;
1431 }
1432
1433 // Check if this file has any digests at all
6b32db11 1434 digest_types = pakfire_digest_has_any(&file->digests);
76011205 1435
293881bc
MT
1436 if (!digest_types) {
1437 ERROR(file->pakfire, "%s: No digests available\n", file->path);
1438 return 0;
76011205
MT
1439 }
1440
293881bc 1441 // Compute digests
1e76689a 1442 r = __pakfire_file_compute_digests(file, &computed_digests, digest_types);
293881bc 1443 if (r)
76011205 1444 goto ERROR;
76011205 1445
293881bc
MT
1446 // Compare digests
1447 r = pakfire_digests_compare(file->pakfire, &file->digests, &computed_digests, digest_types);
76011205
MT
1448 if (r) {
1449 file->verify_status |= PAKFIRE_FILE_PAYLOAD_CHANGED;
1450
293881bc 1451 DEBUG(file->pakfire, "%s: Digest(s) do not match\n", file->path);
76011205
MT
1452 }
1453
76011205 1454ERROR:
76011205
MT
1455 return r;
1456}
1457
cf9bd6f4
MT
1458/*
1459 Verify the file - i.e. does the metadata match what is on disk?
1460*/
c0b051bb
MT
1461int pakfire_file_verify(struct pakfire_file* file, int* status) {
1462 struct stat st;
1463 int r;
1464
cf9bd6f4
MT
1465 DEBUG(file->pakfire, "Verifying %s...\n", file->path);
1466
c0b051bb
MT
1467 // stat() the file
1468 r = lstat(file->abspath, &st);
1469 if (r) {
1470 // File does not exist
1471 if (errno == ENOENT) {
1472 file->verify_status |= PAKFIRE_FILE_NOENT;
1473 return 1;
1474 }
1475
1476 // Raise any other errors from stat()
1477 return r;
1478 }
1479
9e09e361
MT
1480 // Verify mode
1481 r = pakfire_file_verify_mode(file, &st);
1482 if (r)
1483 return r;
1484
c0b051bb
MT
1485 // Verify size
1486 r = pakfire_file_verify_size(file, &st);
1487 if (r)
1488 return r;
1489
1490 // Verify ownership
1491 r = pakfire_file_verify_ownership(file, &st);
1492 if (r)
1493 return r;
1494
0eeac4a5
MT
1495 // Verify timestamps
1496 r = pakfire_file_verify_timestamps(file, &st);
1497 if (r)
1498 return r;
1499
76011205
MT
1500 // Verify payload
1501 r = pakfire_file_verify_payload(file, &st);
1502 if (r)
1503 return r;
1504
cf9bd6f4
MT
1505 return 0;
1506}
c064d9ec
MT
1507
1508PAKFIRE_EXPORT int pakfire_file_matches(struct pakfire_file* file, const char* pattern) {
1509 int r;
1510
1511 // Don't match on no pattern
1512 if (!pattern)
1513 return 0;
1514
1515 // Check if the pattern matches
1516 r = fnmatch(pattern, file->path, 0);
1517 switch (r) {
1518 // Match
1519 case 0:
1520 return 1;
1521
1522 // No Match
1523 case FNM_NOMATCH:
1524 return 0;
1525
1526 default:
1527 return -1;
1528 }
1529}
2dcb43e7
MT
1530
1531/*
1532 ELF Stuff
1533*/
1534
1535static int pakfire_file_open_elf(struct pakfire_file* file,
1536 int (*callback)(struct pakfire_file* file, Elf* elf, void* data), void* data) {
1537 FILE* f = NULL;
1538 Elf* elf = NULL;
1539 int r;
1540
1541 // Don't run this for non-ELF files
1542 if (!pakfire_file_matches_class(file, PAKFIRE_FILE_ELF)) {
1543 errno = EINVAL;
1544 return 1;
1545 }
1546
1547 // Setup libelf
1548 r = setup_libelf(file->pakfire);
1549 if (r)
1550 return r;
1551
1552 // Open the file
1553 f = fopen(file->abspath, "r");
1554 if (!f) {
1555 ERROR(file->pakfire, "Could not open %s: %m\n", file->abspath);
1556 return 1;
1557 }
1558
1559 // Parse the ELF header
1560 elf = elf_begin(fileno(f), ELF_C_READ, NULL);
1561 if (!elf) {
1562 ERROR(file->pakfire, "Could not open ELF file: %s\n", elf_errmsg(-1));
1563 r = 1;
1564 goto ERROR;
1565 }
1566
1567 // Check if this is an ELF file
1568 switch (elf_kind(elf)) {
1569 case ELF_K_ELF:
1570 break;
1571
1572 default:
1573 ERROR(file->pakfire, "%s is not an ELF object\n", file->path);
1574 r = 1;
1575 goto ERROR;
1576 }
1577
1578 // Call the callback
1579 r = callback(file, elf, data);
1580
1581ERROR:
1582 if (elf)
1583 elf_end(elf);
1584 if (f)
1585 fclose(f);
1586
1587 return r;
1588}
1589
8e8de3c1
MT
1590static int __pakfire_file_is_stripped(struct pakfire_file* file, Elf* elf, void* data) {
1591 Elf_Scn* section = NULL;
1592 GElf_Shdr shdr;
1593
1594 // Walk through all sections
1595 for (;;) {
1596 section = elf_nextscn(elf, section);
1597 if (!section)
1598 break;
1599
1600 // Fetch the section header
1601 gelf_getshdr(section, &shdr);
1602
1603 switch (shdr.sh_type) {
1604 // Break if we found the symbol table
1605 case SHT_SYMTAB:
1606 return 0;
1607 }
1608 }
1609
1610 // Not found
1611 DEBUG(file->pakfire, "%s has no debug sections\n", file->path);
1612
1613 return 1;
1614}
1615
1616int pakfire_file_is_stripped(struct pakfire_file* file) {
1617 // Don't run this for non-ELF files
1618 if (!pakfire_file_matches_class(file, PAKFIRE_FILE_ELF)) {
1619 errno = EINVAL;
1620 return -1;
1621 }
1622
1623 return pakfire_file_open_elf(file, __pakfire_file_is_stripped, NULL);
1624}
1625
f7f44921 1626static int __pakfire_file_hardening_check_ssp(
2dcb43e7
MT
1627 struct pakfire_file* file, Elf* elf, void* data) {
1628 Elf_Scn* section = NULL;
1629 GElf_Shdr section_header;
1630 Elf_Data* elf_data = NULL;
1631 GElf_Sym symbol;
1632 const char* name = NULL;
1633
1634 // Walk through all sections
1635 for (;;) {
1636 section = elf_nextscn(elf, section);
fe2b6b35
MT
1637 if (!section) {
1638 ERROR(file->pakfire, "%s has no symbol table\n", file->path);
f7f44921 1639 return 1;
fe2b6b35 1640 }
2dcb43e7
MT
1641
1642 // Fetch the section header
1643 gelf_getshdr(section, &section_header);
1644
1645 // Break if we found the symbol table
1646 if (section_header.sh_type == SHT_SYMTAB)
1647 break;
1648 }
1649
1650 // Fetch a pointer to the section data
1651 elf_data = elf_getdata(section, NULL);
1652
1653 // Walk through all symbols
1654 for (unsigned int i = 0; i < section_header.sh_size / section_header.sh_entsize; i++) {
1655 gelf_getsym(elf_data, i, &symbol);
1656
1657 // Fetch the symbol name
1658 name = elf_strptr(elf, section_header.sh_link, symbol.st_name);
1659
1660 // Skip empty section names
1661 if (!name || !*name)
1662 continue;
1663
f7f44921 1664 // Exit if there is a symbol called "__stack_chk_fail"
2dcb43e7 1665 if (pakfire_string_startswith(name, "__stack_chk_fail"))
f7f44921 1666 return 0;
2dcb43e7
MT
1667 }
1668
f7f44921
MT
1669 // The file does not seem to have SSP enabled
1670 file->hardening_issues |= PAKFIRE_FILE_NO_SSP;
1671
2dcb43e7
MT
1672 return 0;
1673}
1674
f7f44921
MT
1675static int pakfire_file_hardening_check_ssp(struct pakfire_file* file) {
1676 return pakfire_file_open_elf(file, __pakfire_file_hardening_check_ssp, NULL);
2dcb43e7 1677}
2a67d72c 1678
f7f44921 1679static int __pakfire_file_hardening_check_pie(
2a67d72c
MT
1680 struct pakfire_file* file, Elf* elf, void* data) {
1681 GElf_Ehdr eheader;
1682
1683 // Fetch the ELF header
1684 if (!gelf_getehdr(elf, &eheader)) {
1685 ERROR(file->pakfire, "Could not parse ELF header: %s\n", elf_errmsg(-1));
f7f44921 1686 return 1;
2a67d72c
MT
1687 }
1688
1689 // Check for the correct header type
1690 switch (eheader.e_type) {
f7f44921 1691 // Dynamic Objects are good
2a67d72c 1692 case ET_DYN:
f7f44921 1693 break;
2a67d72c 1694
f7f44921 1695 // Anything else is bad
2a67d72c 1696 default:
f7f44921 1697 file->hardening_issues |= PAKFIRE_FILE_NO_PIE;
2a67d72c
MT
1698 break;
1699 }
1700
1701 return 0;
1702}
1703
f7f44921
MT
1704static int pakfire_file_hardening_check_pie(struct pakfire_file* file) {
1705 return pakfire_file_open_elf(file, __pakfire_file_hardening_check_pie, NULL);
1706}
1707
dae43e1f
MT
1708static int __pakfire_file_hardening_check_execstack(
1709 struct pakfire_file* file, Elf* elf, void* data) {
1710 GElf_Phdr phdr;
1711 int r;
1712
1713 size_t phnum = 0;
1714
1715 // Fetch the total numbers of program headers
1716 r = elf_getphdrnum(elf, &phnum);
1717 if (r) {
1718 ERROR(file->pakfire, "Could not fetch number of program headers: %s\n",
1719 elf_errmsg(-1));
1720 return 1;
1721 }
1722
1723 // Walk through all program headers
1724 for (unsigned int i = 0; i < phnum; i++) {
1725 if (!gelf_getphdr(elf, i, &phdr)) {
1726 ERROR(file->pakfire, "Could not parse program header: %s\n", elf_errmsg(-1));
1727 return 1;
1728 }
1729
1730 switch (phdr.p_type) {
1731 case PT_GNU_STACK:
1732 DEBUG(file->pakfire,
1733 "%s: GNU_STACK flags: %c%c%c\n",
1734 file->path,
1735 (phdr.p_flags & PF_R) ? 'R' : '-',
1736 (phdr.p_flags & PF_W) ? 'W' : '-',
1737 (phdr.p_flags & PF_X) ? 'X' : '-'
1738 );
1739
1740 // The stack cannot be writable and executable
1741 if ((phdr.p_flags & PF_W) && (phdr.p_flags & PF_X))
1742 file->hardening_issues |= PAKFIRE_FILE_EXECSTACK;
1743
1744 // Done
1745 return 0;
1746
1747 default:
1748 break;
1749 }
1750 }
1751
1752 return 0;
1753}
1754
1755static int pakfire_file_hardening_check_execstack(struct pakfire_file* file) {
1756 return pakfire_file_open_elf(file, __pakfire_file_hardening_check_execstack, NULL);
1757}
1758
d265bed6
MT
1759static __pakfire_file_hardening_check_partially_relro(
1760 struct pakfire_file* file, Elf* elf, void* data) {
1761 GElf_Phdr phdr;
1762 int r;
1763
1764 size_t phnum = 0;
1765
1766 // Fetch the total numbers of program headers
1767 r = elf_getphdrnum(elf, &phnum);
1768 if (r) {
1769 ERROR(file->pakfire, "Could not fetch number of program headers: %s\n",
1770 elf_errmsg(-1));
1771 return 1;
1772 }
1773
1774 // Walk through all program headers
1775 for (unsigned int i = 0; i < phnum; i++) {
1776 if (!gelf_getphdr(elf, i, &phdr)) {
1777 ERROR(file->pakfire, "Could not parse program header: %s\n", elf_errmsg(-1));
1778 return 1;
1779 }
1780
1781 switch (phdr.p_type) {
1782 case PT_GNU_RELRO:
1783 return 0;
1784
1785 default:
1786 break;
1787 }
1788 }
1789
1790 // This file does not seem to have PT_GNU_RELRO set
1791 file->hardening_issues |= PAKFIRE_FILE_NO_PARTIALLY_RELRO;
1792
1793 return 0;
1794}
1795
1796static int pakfire_file_hardening_check_relro(struct pakfire_file* file) {
1797 return pakfire_file_open_elf(file, __pakfire_file_hardening_check_partially_relro, NULL);
1798}
1799
1800
f7f44921
MT
1801int pakfire_file_check_hardening(struct pakfire_file* file, int* issues) {
1802 int r;
1803
1804 // Return previous result if this has been run before
1805 if (!file->hardening_check_done) {
1806 // Check for SSP
1807 r = pakfire_file_hardening_check_ssp(file);
1808 if (r)
1809 return r;
1810
1811 // Check for PIE
1812 r = pakfire_file_hardening_check_pie(file);
1813 if (r)
1814 return r;
1815
dae43e1f
MT
1816 // Check for executable stacks
1817 r = pakfire_file_hardening_check_execstack(file);
1818 if (r)
1819 return r;
1820
d265bed6
MT
1821 // Check for RELRO
1822 r = pakfire_file_hardening_check_relro(file);
1823 if (r)
1824 return r;
1825
f7f44921
MT
1826 // All checks done
1827 file->hardening_check_done = 1;
1828 }
1829
1830 // Return any issues
1831 if (issues)
1832 *issues = file->hardening_issues;
1833
1834 return 0;
2a67d72c 1835}