]> git.ipfire.org Git - people/stevee/pakfire.git/blame - src/libpakfire/file.c
macros: Don't install logrotate files
[people/stevee/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
221cc3ce 34#include <pakfire/constants.h>
c52e0148 35#include <pakfire/digest.h>
221cc3ce 36#include <pakfire/file.h>
3fca5032 37#include <pakfire/logging.h>
883b3be9 38#include <pakfire/pakfire.h>
9f953e68 39#include <pakfire/private.h>
d973a13d 40#include <pakfire/string.h>
221cc3ce
MT
41#include <pakfire/util.h>
42
c0b051bb
MT
43enum pakfire_file_verification_status {
44 PAKFIRE_FILE_NOENT = (1 << 0),
9e09e361
MT
45 PAKFIRE_FILE_TYPE_CHANGED = (1 << 1),
46 PAKFIRE_FILE_PERMISSIONS_CHANGED = (1 << 2),
47 PAKFIRE_FILE_DEV_CHANGED = (1 << 3),
48 PAKFIRE_FILE_SIZE_CHANGED = (1 << 4),
49 PAKFIRE_FILE_OWNER_CHANGED = (1 << 5),
50 PAKFIRE_FILE_GROUP_CHANGED = (1 << 6),
0eeac4a5
MT
51 PAKFIRE_FILE_CTIME_CHANGED = (1 << 7),
52 PAKFIRE_FILE_MTIME_CHANGED = (1 << 8),
76011205 53 PAKFIRE_FILE_PAYLOAD_CHANGED = (1 << 9),
c0b051bb
MT
54};
55
5803b5f6 56struct pakfire_file {
ac4c607b 57 struct pakfire* pakfire;
5e9463ec 58 int nrefs;
221cc3ce 59
e8d3b985 60 // The relative path
3b9e3970 61 char path[PATH_MAX];
e8d3b985
MT
62
63 // The absolute path in the file system
0027da6f 64 char abspath[PATH_MAX];
221cc3ce 65
e8d3b985 66 // File Ownership
302e3253
MT
67 char uname[LOGIN_NAME_MAX];
68 char gname[LOGIN_NAME_MAX];
e8d3b985 69
a09b95e0
MT
70 // Stat
71 struct stat st;
d03fa9a3 72
59d8c727
MT
73 // Link destinations
74 char hardlink[PATH_MAX];
75 char symlink[PATH_MAX];
76
65131b30 77 // Digests
c52e0148 78 struct pakfire_digests digests;
5e9463ec 79
71f6f465
MT
80 // Class
81 int class;
82
c0b051bb
MT
83 // Verification Status
84 int verify_status;
85
5e9463ec
MT
86 #warning TODO capabilities, config, data
87 // capabilities
88 //int is_configfile;
89 //int is_datafile;
90};
91
eb5daf3f 92static int pakfire_file_from_archive_entry(struct pakfire_file* file, struct archive_entry* entry) {
49a39ad9 93 const char* path = NULL;
eb5daf3f
MT
94 const char* attr = NULL;
95 const void* value = NULL;
96 size_t size = 0;
97 int r = 0;
98
99 // Set abspath
49a39ad9
MT
100 path = archive_entry_sourcepath(entry);
101 if (path) {
102 // Make path absolute
103 path = pakfire_path_abspath(path);
004f0160
MT
104 if (!path) {
105 r = 1;
49a39ad9 106 goto ERROR;
004f0160 107 }
49a39ad9
MT
108
109 // Set
110 r = pakfire_file_set_abspath(file, path);
111 if (r) {
112 ERROR(file->pakfire, "Could not set abspath: %m\n");
113 goto ERROR;
114 }
eb5daf3f
MT
115 }
116
117 // Set path
49a39ad9 118 path = archive_entry_pathname(entry);
eb5daf3f 119 if (path) {
eb5daf3f
MT
120 r = pakfire_file_set_path(file, path);
121 if (r) {
122 ERROR(file->pakfire, "Could not set path: %m\n");
123 goto ERROR;
124 }
125 }
126
127 // Set links
128 pakfire_file_set_hardlink(file, archive_entry_hardlink(entry));
129 pakfire_file_set_symlink(file, archive_entry_symlink(entry));
130
f8733b31
MT
131 pakfire_file_set_nlink(file, archive_entry_nlink(entry));
132 pakfire_file_set_inode(file, archive_entry_ino64(entry));
133 pakfire_file_set_dev(file, archive_entry_dev(entry));
134
eb5daf3f
MT
135 // Set size
136 pakfire_file_set_size(file, archive_entry_size(entry));
137
138 // Set mode
139 pakfire_file_set_mode(file, archive_entry_mode(entry));
140
141 // Set dev type
142 if (archive_entry_dev_is_set(entry))
143 pakfire_file_set_dev(file, archive_entry_dev(entry));
144
302e3253
MT
145 // Set uname
146 pakfire_file_set_uname(file, archive_entry_uname(entry));
eb5daf3f 147
302e3253
MT
148 // Set gname
149 pakfire_file_set_gname(file, archive_entry_gname(entry));
eb5daf3f
MT
150
151 // Set times
152 pakfire_file_set_ctime(file, archive_entry_ctime(entry));
153 pakfire_file_set_mtime(file, archive_entry_mtime(entry));
154
155 // Read any extended attributes
156 while (archive_entry_xattr_next(entry, &attr, &value, &size) == ARCHIVE_OK) {
d98740de
MT
157 // Digest: SHA-3-512
158 if (strcmp(attr, "PAKFIRE.digests.sha3_512") == 0) {
159 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_SHA3_512, value, size);
160 if (r)
161 goto ERROR;
162
163 // Digest: SHA-3-256
164 } else if (strcmp(attr, "PAKFIRE.digests.sha3_256") == 0) {
165 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_SHA3_256, value, size);
166 if (r)
167 goto ERROR;
168
f1e6c5df 169 // Digest: BLAKE2b512
d98740de 170 } else if (strcmp(attr, "PAKFIRE.digests.blake2b512") == 0) {
f1e6c5df
MT
171 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_BLAKE2B512, value, size);
172 if (r)
173 goto ERROR;
174
175 // Digest: BLAKE2b512
176 } else if (strcmp(attr, "PAKFIRE.digests.blake2s256") == 0) {
177 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_BLAKE2S256, value, size);
178 if (r)
179 goto ERROR;
180
4500ed0a
MT
181 // Digest: SHA-2-512
182 } else if (strcmp(attr, "PAKFIRE.digests.sha2_512") == 0) {
183 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_SHA2_512, value, size);
eb5daf3f
MT
184 if (r)
185 goto ERROR;
186
4500ed0a
MT
187 // Digest: SHA-2-256
188 } else if (strcmp(attr, "PAKFIRE.digests.sha2_256") == 0) {
189 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_SHA2_256, value, size);
eb5daf3f
MT
190 if (r)
191 goto ERROR;
192
193 } else {
194 DEBUG(file->pakfire, "Received an unknown extended attribute: %s\n", attr);
195 }
196 }
197
198ERROR:
199 return r;
200}
201
ac4c607b 202PAKFIRE_EXPORT int pakfire_file_create(struct pakfire_file** file, struct pakfire* pakfire) {
5803b5f6 203 struct pakfire_file* f = calloc(1, sizeof(*f));
5e9463ec 204 if (!f)
7403779a 205 return 1;
5e9463ec 206
e8d3b985 207 // Store reference to Pakfire
883b3be9 208 f->pakfire = pakfire_ref(pakfire);
e8d3b985
MT
209
210 // Initialize reference counter
5e9463ec
MT
211 f->nrefs = 1;
212
213 *file = f;
214 return 0;
d03fa9a3
MT
215}
216
61b0856b
MT
217int pakfire_file_create_from_path(struct pakfire_file** file,
218 struct pakfire* pakfire, const char* path) {
219 struct archive* reader = NULL;
220 struct archive_entry* entry = NULL;
221 int r = 1;
222
223 // Allocate a reader
224 reader = pakfire_make_archive_disk_reader(pakfire, 0);
225 if (!reader)
226 goto ERROR;
227
228 // Allocate a new archive entry
229 entry = archive_entry_new();
230 if (!entry)
231 goto ERROR;
232
233 // Set source path
234 archive_entry_copy_sourcepath(entry, path);
235
236 // Read all file attributes from disk
237 r = archive_read_disk_entry_from_file(reader, entry, -1, NULL);
238 if (r) {
239 ERROR(pakfire, "Could not read from %s: %m\n", path);
240 goto ERROR;
241 }
242
243 // Create file
244 r = pakfire_file_create_from_archive_entry(file, pakfire, entry);
245 if (r)
246 goto ERROR;
247
248ERROR:
249 if (r)
250 ERROR(pakfire, "Could not create file from path %s: %m\n", path);
251 if (entry)
252 archive_entry_free(entry);
253 if (reader)
254 archive_read_free(reader);
255
256 return r;
257}
258
ac4c607b 259int pakfire_file_create_from_archive_entry(struct pakfire_file** file, struct pakfire* pakfire,
d0985e31
MT
260 struct archive_entry* entry) {
261 int r = pakfire_file_create(file, pakfire);
262 if (r)
263 return r;
264
265 // Copy archive entry
eb5daf3f 266 r = pakfire_file_from_archive_entry(*file, entry);
d0985e31
MT
267 if (r)
268 goto ERROR;
269
270 return 0;
271
272ERROR:
273 pakfire_file_unref(*file);
274 *file = NULL;
275
276 return r;
277}
278
49a39ad9 279struct archive_entry* pakfire_file_archive_entry(struct pakfire_file* file, int digest_types) {
d7b476ef 280 const char* path = NULL;
d4b2ef2d 281 int r;
d7b476ef 282
5acd852e
MT
283 struct archive_entry* entry = archive_entry_new();
284 if (!entry) {
285 ERROR(file->pakfire, "Could not allocate archive entry: %m\n");
286 return NULL;
287 }
288
5acd852e
MT
289 // Set source path
290 archive_entry_copy_sourcepath(entry, file->abspath);
291
49a39ad9 292 // Set path
d7b476ef
MT
293 path = pakfire_file_get_path(file);
294 if (path && *path == '/') {
295 archive_entry_copy_pathname(entry, path + 1);
296 }
49a39ad9 297
5acd852e
MT
298 // Set links
299 if (*file->hardlink)
300 archive_entry_set_hardlink(entry, file->hardlink);
301 if (*file->symlink)
302 archive_entry_set_symlink(entry, file->symlink);
303
f8733b31
MT
304 archive_entry_set_nlink(entry, pakfire_file_get_nlink(file));
305 archive_entry_set_ino64(entry, pakfire_file_get_inode(file));
306 archive_entry_set_dev(entry, pakfire_file_get_dev(file));
307
5acd852e
MT
308 // Set size
309 archive_entry_set_size(entry, pakfire_file_get_size(file));
310
311 // Set mode
312 archive_entry_set_mode(entry, pakfire_file_get_mode(file));
313
302e3253
MT
314 // Set uname
315 archive_entry_set_uname(entry, pakfire_file_get_uname(file));
5acd852e 316
302e3253
MT
317 // Set gname
318 archive_entry_set_gname(entry, pakfire_file_get_gname(file));
5acd852e
MT
319
320 // Set times
321 archive_entry_set_ctime(entry, pakfire_file_get_ctime(file), 0);
322 archive_entry_set_mtime(entry, pakfire_file_get_mtime(file), 0);
323
d4b2ef2d
MT
324 // Compute any required file digests
325 r = pakfire_file_compute_digests(file, digest_types);
326 if (r)
327 goto ERROR;
328
399d14c2
MT
329 // Copy digests
330
d98740de 331 // SHA-3-512
045fa504
MT
332 if ((digest_types && PAKFIRE_DIGEST_SHA3_512)
333 && pakfire_digest_set(file->digests.sha3_512))
d98740de
MT
334 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.sha3_512",
335 file->digests.sha3_512, sizeof(file->digests.sha3_512));
336
337 // SHA-3-256
045fa504
MT
338 if ((digest_types && PAKFIRE_DIGEST_SHA3_256) &&
339 pakfire_digest_set(file->digests.sha3_256))
d98740de
MT
340 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.sha3_256",
341 file->digests.sha3_256, sizeof(file->digests.sha3_256));
342
f1e6c5df 343 // BLAKE2b512
045fa504
MT
344 if ((digest_types && PAKFIRE_DIGEST_BLAKE2B512) &&
345 pakfire_digest_set(file->digests.blake2b512))
f1e6c5df
MT
346 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.blake2b512",
347 file->digests.blake2b512, sizeof(file->digests.blake2b512));
348
349 // BLAKE2s256
045fa504
MT
350 if ((digest_types && PAKFIRE_DIGEST_BLAKE2S256) &&
351 pakfire_digest_set(file->digests.blake2s256))
f1e6c5df
MT
352 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.blake2s256",
353 file->digests.blake2s256, sizeof(file->digests.blake2s256));
354
4500ed0a 355 // SHA-2-512
045fa504
MT
356 if ((digest_types && PAKFIRE_DIGEST_SHA2_512) &&
357 pakfire_digest_set(file->digests.sha2_512))
4500ed0a
MT
358 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.sha2_512",
359 file->digests.sha2_512, sizeof(file->digests.sha2_512));
399d14c2 360
4500ed0a 361 // SHA-2-256
045fa504
MT
362 if ((digest_types && PAKFIRE_DIGEST_SHA2_512) &&
363 pakfire_digest_set(file->digests.sha2_256))
4500ed0a
MT
364 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.sha2_256",
365 file->digests.sha2_256, sizeof(file->digests.sha2_256));
5acd852e
MT
366
367 return entry;
d4b2ef2d
MT
368
369ERROR:
370 if (entry)
371 archive_entry_free(entry);
372
373 return NULL;
5acd852e
MT
374}
375
5803b5f6 376static void pakfire_file_free(struct pakfire_file* file) {
5e8dfbeb 377 pakfire_unref(file->pakfire);
f0d6233d 378 free(file);
221cc3ce
MT
379}
380
5803b5f6 381PAKFIRE_EXPORT struct pakfire_file* pakfire_file_ref(struct pakfire_file* file) {
5e9463ec 382 file->nrefs++;
221cc3ce 383
5e9463ec
MT
384 return file;
385}
221cc3ce 386
5803b5f6 387PAKFIRE_EXPORT struct pakfire_file* pakfire_file_unref(struct pakfire_file* file) {
5e9463ec
MT
388 if (--file->nrefs > 0)
389 return file;
390
391 pakfire_file_free(file);
392 return NULL;
221cc3ce
MT
393}
394
5852b822
MT
395#define pakfire_file_strmode(file, buffer) \
396 __pakfire_file_strmode(file, buffer, sizeof(buffer))
397
398static int __pakfire_file_strmode(struct pakfire_file* file, char* s, const size_t length) {
399 int r;
400
401 static const mode_t permbits[] = {
402 0400,
403 0200,
404 0100,
405 0040,
406 0020,
407 0010,
408 0004,
409 0002,
410 0001
411 };
412
413 const mode_t mode = pakfire_file_get_mode(file);
414 const mode_t type = pakfire_file_get_type(file);
415
416 // Set some default string
417 r = __pakfire_string_set(s, length, "?rwxrwxrwx ");
418 if (r)
419 return r;
420
421 switch (type) {
422 case S_IFREG:
423 s[0] = '-';
424 break;
425
426 case S_IFBLK:
427 s[0] = 'b';
428 break;
429
430 case S_IFCHR:
431 s[0] = 'c';
432 break;
433
434 case S_IFDIR:
435 s[0] = 'd';
436 break;
437
438 case S_IFLNK:
439 s[0] = 'l';
440 break;
441
442 case S_IFSOCK:
443 s[0] = 's';
444 break;
445
446 case S_IFIFO:
447 s[0] = 'p';
448 break;
449
450 default:
451 if (*file->hardlink) {
452 s[0] = 'h';
453 break;
454 }
455 }
456
457 for (unsigned int i = 0; i < 9; i++) {
458 if (mode & permbits[i])
459 continue;
460
461 s[i+1] = '-';
462 }
463
464 if (mode & S_ISUID) {
465 if (mode & 0100)
466 s[3] = 's';
467 else
468 s[3] = 'S';
469 }
470
471 if (mode & S_ISGID) {
472 if (mode & 0010)
473 s[6] = 's';
474 else
475 s[6] = 'S';
476 }
477
478 if (mode & S_ISVTX) {
479 if (mode & 0001)
480 s[9] = 't';
481 else
482 s[9] = 'T';
483 }
484
485#if 0
486 if (file->caps)
487 s[10] = '+';
488#endif
489
490 return 0;
491}
492
493char* pakfire_file_dump(struct pakfire_file* file) {
494 char* buffer = NULL;
495 int r;
496
497 char mode[12];
498 char time[32];
499
500 // Format mode
501 r = pakfire_file_strmode(file, mode);
502 if (r)
503 return NULL;
504
505 // Format time
506 r = pakfire_strftime(time, "%Y-%m-%d %H:%M", file->st.st_ctime);
507 if (r)
508 return NULL;
509
510 // Put everything together
511 r = asprintf(&buffer, "%s %s/%s %8zu %s %s",
302e3253 512 mode, file->uname, file->gname, file->st.st_size, time, file->path);
5852b822
MT
513 if (r < 0)
514 return NULL;
515
516 return buffer;
517}
518
5803b5f6 519PAKFIRE_EXPORT int pakfire_file_cmp(struct pakfire_file* file1, struct pakfire_file* file2) {
32485f6c
MT
520 const char* path1 = pakfire_file_get_path(file1);
521 const char* path2 = pakfire_file_get_path(file2);
221cc3ce 522
32485f6c 523 return strcmp(path1, path2);
221cc3ce
MT
524}
525
5803b5f6 526const char* pakfire_file_get_abspath(struct pakfire_file* file) {
e4c2f7a9
MT
527 return file->abspath;
528}
529
5803b5f6 530int pakfire_file_set_abspath(struct pakfire_file* file, const char* path) {
49a39ad9
MT
531 int r;
532
d13bd5b7
MT
533 // Check if path is set and absolute
534 if (!path || *path != '/') {
535 errno = EINVAL;
536 return 1;
537 }
538
49a39ad9
MT
539 // Store the abspath
540 r = pakfire_string_set(file->abspath, path);
541 if (r)
542 goto ERROR;
543
544 // Store path if it isn't set, yet
545 if (!*file->path) {
546 r = pakfire_file_set_path(file, path);
547 if (r)
548 goto ERROR;
549 }
550
551 return r;
552
553ERROR:
554 ERROR(file->pakfire, "Could not set abspath '%s': %m\n", path);
555 return r;
3b9e3970
MT
556}
557
5803b5f6 558PAKFIRE_EXPORT const char* pakfire_file_get_path(struct pakfire_file* file) {
32485f6c 559 return file->path;
221cc3ce
MT
560}
561
5803b5f6 562PAKFIRE_EXPORT int pakfire_file_set_path(struct pakfire_file* file, const char* path) {
12a327de 563 int r = 1;
520213e9 564
2cc8b5f4
MT
565 // Check if path is set
566 if (!path) {
d13bd5b7 567 errno = EINVAL;
12a327de 568 goto ERROR;
d13bd5b7
MT
569 }
570
2cc8b5f4
MT
571 // Strip any leading dots from paths
572 if (pakfire_string_startswith(path, "./"))
573 path++;
574
575 switch (*path) {
576 // Just store the path if it is absolute
577 case '/':
578 r = pakfire_string_set(file->path, path);
579 if (r)
580 goto ERROR;
581 break;
582
583 // Handle relative paths
584 default:
585 r = pakfire_string_format(file->path, "/%s", path);
586 if (r)
587 goto ERROR;
588 break;
589 }
520213e9
MT
590
591 // Set abspath if it isn't set, yet
592 if (!*file->abspath) {
2cc8b5f4 593 r = pakfire_file_set_abspath(file, file->path);
520213e9 594 if (r)
12a327de 595 goto ERROR;
520213e9
MT
596 }
597
598 return r;
12a327de
MT
599
600ERROR:
601 ERROR(file->pakfire, "Could not set path '%s': %m\n", path);
602 return r;
221cc3ce
MT
603}
604
59d8c727
MT
605PAKFIRE_EXPORT const char* pakfire_file_get_hardlink(struct pakfire_file* file) {
606 if (!*file->hardlink)
607 return NULL;
608
609 return file->hardlink;
610}
611
612PAKFIRE_EXPORT void pakfire_file_set_hardlink(struct pakfire_file* file, const char* link) {
3c8eb581 613 pakfire_string_set(file->hardlink, link);
59d8c727
MT
614}
615
616PAKFIRE_EXPORT const char* pakfire_file_get_symlink(struct pakfire_file* file) {
617 if (!*file->symlink)
618 return NULL;
619
620 return file->symlink;
621}
622
623PAKFIRE_EXPORT void pakfire_file_set_symlink(struct pakfire_file* file, const char* link) {
3c8eb581 624 pakfire_string_set(file->symlink, link);
59d8c727
MT
625}
626
f8733b31
MT
627PAKFIRE_EXPORT nlink_t pakfire_file_get_nlink(struct pakfire_file* file) {
628 return file->st.st_nlink;
629}
630
631PAKFIRE_EXPORT void pakfire_file_set_nlink(struct pakfire_file* file, const nlink_t nlink) {
632 file->st.st_nlink = nlink;
633}
634
635PAKFIRE_EXPORT ino_t pakfire_file_get_inode(struct pakfire_file* file) {
636 return file->st.st_ino;
637}
638
639PAKFIRE_EXPORT void pakfire_file_set_inode(struct pakfire_file* file, const ino_t ino) {
640 file->st.st_ino = ino;
641}
642
643PAKFIRE_EXPORT dev_t pakfire_file_get_dev(struct pakfire_file* file) {
644 return file->st.st_dev;
645}
646
647PAKFIRE_EXPORT void pakfire_file_set_dev(struct pakfire_file* file, const dev_t dev) {
648 file->st.st_dev = dev;
649}
650
5803b5f6 651PAKFIRE_EXPORT int pakfire_file_get_type(struct pakfire_file* file) {
a09b95e0 652 return file->st.st_mode & S_IFMT;
221cc3ce
MT
653}
654
a09b95e0
MT
655PAKFIRE_EXPORT off_t pakfire_file_get_size(struct pakfire_file* file) {
656 return file->st.st_size;
221cc3ce
MT
657}
658
a09b95e0
MT
659PAKFIRE_EXPORT void pakfire_file_set_size(struct pakfire_file* file, off_t size) {
660 file->st.st_size = size;
221cc3ce
MT
661}
662
302e3253
MT
663PAKFIRE_EXPORT const char* pakfire_file_get_uname(struct pakfire_file* file) {
664 return file->uname;
221cc3ce
MT
665}
666
302e3253
MT
667PAKFIRE_EXPORT int pakfire_file_set_uname(struct pakfire_file* file, const char* uname) {
668 return pakfire_string_set(file->uname, uname);
221cc3ce
MT
669}
670
302e3253
MT
671PAKFIRE_EXPORT const char* pakfire_file_get_gname(struct pakfire_file* file) {
672 return file->gname;
221cc3ce
MT
673}
674
302e3253
MT
675PAKFIRE_EXPORT int pakfire_file_set_gname(struct pakfire_file* file, const char* gname) {
676 return pakfire_string_set(file->gname, gname);
221cc3ce
MT
677}
678
5803b5f6 679PAKFIRE_EXPORT mode_t pakfire_file_get_mode(struct pakfire_file* file) {
a09b95e0 680 return file->st.st_mode;
221cc3ce
MT
681}
682
5803b5f6 683PAKFIRE_EXPORT void pakfire_file_set_mode(struct pakfire_file* file, mode_t mode) {
a09b95e0 684 file->st.st_mode = mode;
221cc3ce
MT
685}
686
134545d5 687PAKFIRE_EXPORT mode_t pakfire_file_get_perms(struct pakfire_file* file) {
a09b95e0 688 return file->st.st_mode & ~AE_IFMT;
134545d5
MT
689}
690
691PAKFIRE_EXPORT void pakfire_file_set_perms(struct pakfire_file* file, const mode_t perms) {
692 // Clear any previous permissions
a09b95e0 693 file->st.st_mode &= S_IFMT;
134545d5
MT
694
695 // Set new bits (with format cleared)
a09b95e0 696 file->st.st_mode |= ~S_IFMT & perms;
134545d5
MT
697}
698
5803b5f6 699PAKFIRE_EXPORT time_t pakfire_file_get_ctime(struct pakfire_file* file) {
a09b95e0 700 return file->st.st_ctime;
ef4e8460
MT
701}
702
5803b5f6 703PAKFIRE_EXPORT void pakfire_file_set_ctime(struct pakfire_file* file, time_t time) {
a09b95e0 704 file->st.st_ctime = time;
ef4e8460
MT
705}
706
5803b5f6 707PAKFIRE_EXPORT time_t pakfire_file_get_mtime(struct pakfire_file* file) {
a09b95e0 708 return file->st.st_mtime;
221cc3ce
MT
709}
710
5803b5f6 711PAKFIRE_EXPORT void pakfire_file_set_mtime(struct pakfire_file* file, time_t time) {
a09b95e0 712 file->st.st_mtime = time;
221cc3ce
MT
713}
714
65131b30 715PAKFIRE_EXPORT const unsigned char* pakfire_file_get_digest(
c52e0148 716 struct pakfire_file* file, const enum pakfire_digest_types type, size_t* length) {
65131b30 717
9802aaf6 718 switch (type) {
d98740de
MT
719 case PAKFIRE_DIGEST_SHA3_512:
720 if (!pakfire_digest_set(file->digests.sha3_512))
721 return NULL;
722
723 if (length)
724 *length = sizeof(file->digests.sha3_512);
725
726 return file->digests.sha3_512;
727
728 case PAKFIRE_DIGEST_SHA3_256:
729 if (!pakfire_digest_set(file->digests.sha3_256))
730 return NULL;
731
732 if (length)
733 *length = sizeof(file->digests.sha3_256);
734
735 return file->digests.sha3_256;
736
f1e6c5df
MT
737 case PAKFIRE_DIGEST_BLAKE2B512:
738 if (!pakfire_digest_set(file->digests.blake2b512))
739 return NULL;
740
741 if (length)
742 *length = sizeof(file->digests.blake2b512);
743
744 return file->digests.blake2b512;
745
746 case PAKFIRE_DIGEST_BLAKE2S256:
747 if (!pakfire_digest_set(file->digests.blake2s256))
748 return NULL;
749
750 if (length)
751 *length = sizeof(file->digests.blake2s256);
752
753 return file->digests.blake2s256;
754
4500ed0a
MT
755 case PAKFIRE_DIGEST_SHA2_512:
756 if (!pakfire_digest_set(file->digests.sha2_512))
9802aaf6 757 return NULL;
65131b30 758
9802aaf6 759 if (length)
4500ed0a 760 *length = sizeof(file->digests.sha2_512);
65131b30 761
4500ed0a 762 return file->digests.sha2_512;
9802aaf6 763
4500ed0a
MT
764 case PAKFIRE_DIGEST_SHA2_256:
765 if (!pakfire_digest_set(file->digests.sha2_256))
9802aaf6 766 return NULL;
5e8dfbeb 767
9802aaf6 768 if (length)
4500ed0a 769 *length = sizeof(file->digests.sha2_256);
5e8dfbeb 770
4500ed0a 771 return file->digests.sha2_256;
2c4b4a02
MT
772
773 case PAKFIRE_DIGEST_UNDEFINED:
774 break;
5e8dfbeb
MT
775 }
776
9802aaf6 777 return NULL;
5e8dfbeb
MT
778}
779
780PAKFIRE_EXPORT int pakfire_file_set_digest(struct pakfire_file* file,
c52e0148 781 const enum pakfire_digest_types type, const unsigned char* digest, const size_t length) {
9802aaf6 782 if (!digest) {
5e8dfbeb
MT
783 errno = EINVAL;
784 return 1;
785 }
786
399d14c2
MT
787 // Check buffer length
788 if (pakfire_digest_length(type) != length) {
789 ERROR(file->pakfire, "Digest has an incorrect length of %zu byte(s)\n", length);
790 errno = ENOMSG;
791 return 1;
792 }
793
794 // Store the digest
9802aaf6 795 switch (type) {
d98740de
MT
796 case PAKFIRE_DIGEST_SHA3_512:
797 memcpy(file->digests.sha3_512, digest, sizeof(file->digests.sha3_512));
798 break;
799
800 case PAKFIRE_DIGEST_SHA3_256:
801 memcpy(file->digests.sha3_256, digest, sizeof(file->digests.sha3_256));
802 break;
803
f1e6c5df
MT
804 case PAKFIRE_DIGEST_BLAKE2B512:
805 memcpy(file->digests.blake2b512, digest, sizeof(file->digests.blake2b512));
806 break;
807
808 case PAKFIRE_DIGEST_BLAKE2S256:
809 memcpy(file->digests.blake2s256, digest, sizeof(file->digests.blake2s256));
810 break;
811
4500ed0a
MT
812 case PAKFIRE_DIGEST_SHA2_512:
813 memcpy(file->digests.sha2_512, digest, sizeof(file->digests.sha2_512));
9802aaf6 814 break;
65131b30 815
4500ed0a
MT
816 case PAKFIRE_DIGEST_SHA2_256:
817 memcpy(file->digests.sha2_256, digest, sizeof(file->digests.sha2_256));
9802aaf6 818 break;
2c4b4a02
MT
819
820 case PAKFIRE_DIGEST_UNDEFINED:
821 errno = ENOTSUP;
822 return 1;
5e8dfbeb
MT
823 }
824
5e8dfbeb 825 return 0;
221cc3ce
MT
826}
827
5803b5f6 828static int pakfire_file_levels(struct pakfire_file* file) {
3fca5032
MT
829 if (!*file->path)
830 return 0;
831
832 int levels = 0;
833
834 for (char* p = file->path; *p; p++) {
835 if (*p == '/')
836 levels++;
837 }
838
839 return levels;
840}
841
d5a13ade
MT
842FILE* pakfire_file_open(struct pakfire_file* file) {
843 FILE* f = fopen(file->abspath, "r");
844 if (!f)
845 ERROR(file->pakfire, "Could not open %s: %m\n", file->abspath);
846
847 return f;
848}
849
1e76689a
MT
850static int __pakfire_file_compute_digests(struct pakfire_file* file,
851 struct pakfire_digests* digests, const int types) {
852 FILE* f = NULL;
853 int r = 1;
854
855 // Skip this for anything that isn't a regular file
856 if (!S_ISREG(file->st.st_mode))
857 return 0;
858
859 // Reset digests
860 pakfire_digests_reset(digests, types);
861
862 // Open the file
863 f = pakfire_file_open(file);
864 if (!f)
865 goto ERROR;
866
867 // Compute digests
868 r = pakfire_digests_compute_from_file(file->pakfire, digests, types, f);
869 if (r)
870 goto ERROR;
871
872ERROR:
873 if (f)
874 fclose(f);
875
876 return r;
877}
878
879int pakfire_file_compute_digests(struct pakfire_file* file, const int types) {
6b32db11 880 return __pakfire_file_compute_digests(file, &file->digests, types);
1e76689a
MT
881}
882
ac71886a 883int pakfire_file_remove(struct pakfire_file* file) {
3fca5032
MT
884 if (!*file->abspath) {
885 errno = EINVAL;
886 return 1;
887 }
888
889 DEBUG(file->pakfire, "Removing %s...\n", file->path);
890
891 int r = remove(file->abspath);
892 if (r) {
da89e02f
MT
893 switch (errno) {
894 // Ignore when we could not remove directories
895 case ENOTEMPTY:
896 return 0;
897
898 // Ignore if the file didn't exist
899 case ENOENT:
900 return 0;
901
902 default:
903 break;
904 }
3fca5032 905
b1772bfb 906 ERROR(file->pakfire, "Could not remove %s (%s): %m\n", file->path, file->abspath);
3fca5032
MT
907 }
908
ac71886a
MT
909 return r;
910}
911
71f6f465
MT
912/*
913 Classification
914*/
915
916static int pakfire_file_classify_mode(struct pakfire_file* file) {
917 // Check for regular files
918 if (S_ISREG(file->st.st_mode))
919 file->class |= PAKFIRE_FILE_REGULAR;
920
921 // Check for directories
922 else if (S_ISDIR(file->st.st_mode))
923 file->class |= PAKFIRE_FILE_DIRECTORY;
924
925 // Check for symlinks
926 else if (S_ISLNK(file->st.st_mode))
927 file->class |= PAKFIRE_FILE_SYMLINK;
928
929 // Check for character devices
930 else if (S_ISCHR(file->st.st_mode))
931 file->class |= PAKFIRE_FILE_CHARACTER;
932
933 // Check for block devices
934 else if (S_ISBLK(file->st.st_mode))
935 file->class |= PAKFIRE_FILE_BLOCK;
936
937 // Check for FIFO pipes
938 else if (S_ISFIFO(file->st.st_mode))
939 file->class |= PAKFIRE_FILE_FIFO;
940
941 // Check for sockets
942 else if (S_ISSOCK(file->st.st_mode))
943 file->class |= PAKFIRE_FILE_SOCKET;
944
945 return 0;
946}
947
948static const struct extension {
949 const char* extension;
950 int class;
951} extensions[] = {
d0831491
MT
952 { "*.a", PAKFIRE_FILE_STATIC_LIBRARY },
953 { "*.la", PAKFIRE_FILE_LIBTOOL_ARCHIVE },
71f6f465
MT
954 { "*.pm", PAKFIRE_FILE_PERL },
955 { "*.pc", PAKFIRE_FILE_PKGCONFIG },
956 { NULL , 0 },
957};
958
959static int pakfire_file_classify_extension(struct pakfire_file* file) {
960 for (const struct extension* e = extensions; e->extension; e++) {
961 if (pakfire_file_matches(file, e->extension)) {
962 file->class |= e->class;
963 break;
964 }
965 }
966
967 return 0;
968}
969
970static const struct mimetype {
971 const char* mimetype;
972 int class;
973} mimetypes[] = {
974 { "application/x-sharedlibary", PAKFIRE_FILE_EXECUTABLE },
975 { "text/x-perl", PAKFIRE_FILE_PERL },
976 { NULL, 0 },
977};
978
979static int pakfire_file_classify_magic(struct pakfire_file* file) {
980 // Don't run this if the file has already been classified
981 if (file->class & ~PAKFIRE_FILE_REGULAR)
982 return 0;
983
984 // Fetch the magic cookie
985 magic_t magic = pakfire_get_magic(file->pakfire);
986 if (!magic)
987 return 1;
988
989 // Check the file
990 const char* mimetype = magic_file(magic, file->abspath);
991 if (!mimetype) {
992 ERROR(file->pakfire, "Could not classify %s: %s\n", file->path, magic_error(magic));
993 return 1;
994 }
995
996 DEBUG(file->pakfire, "Classified %s as %s\n", file->path, mimetype);
997
998 for (const struct mimetype* m = mimetypes; m->mimetype; m++) {
999 if (strcmp(m->mimetype, mimetype) == 0) {
1000 file->class |= m->class;
1001 break;
1002 }
1003 }
1004
1005 return 0;
1006}
1007
1008int pakfire_file_classify(struct pakfire_file* file) {
1009 int r;
1010
1011 if (!file->class) {
1012 // First, check the mode so that we won't run magic on directories, symlinks, ...
1013 r = pakfire_file_classify_mode(file);
1014 if (r)
1015 goto ERROR;
1016
1017 // Only run this for regular files
1018 if (file->class & PAKFIRE_FILE_REGULAR) {
1019 // Then check for the extension
1020 r = pakfire_file_classify_extension(file);
1021 if (r)
1022 goto ERROR;
1023
1024 // After that, we will use libmagic...
1025 r = pakfire_file_classify_magic(file);
1026 if (r)
1027 goto ERROR;
1028 }
1029 }
1030
1031 return file->class;
1032
1033ERROR:
1034 // Reset the class
1035 file->class = PAKFIRE_FILE_UNKNOWN;
1036
1037 return r;
1038}
1039
1040int pakfire_file_matches_class(struct pakfire_file* file, const int class) {
1041 return pakfire_file_classify(file) & class;
1042}
1043
ac71886a
MT
1044/*
1045 This function tries to remove the file after it has been packaged.
1046
1047 It will try to delete any parent directories as well and ignore if directories
1048 cannot be deleted because they might contain other files
1049*/
1050int pakfire_file_cleanup(struct pakfire_file* file) {
1051 char path[PATH_MAX];
1052
1053 // Try removing the file
1054 int r = pakfire_file_remove(file);
1055 if (r)
1056 return r;
1057
3fca5032
MT
1058 // Create a working copy of abspath
1059 r = pakfire_string_set(path, file->abspath);
a60955af 1060 if (r)
3fca5032
MT
1061 return r;
1062
1063 // See how many levels this file has
1064 int levels = pakfire_file_levels(file);
1065
1066 // Walk all the way up and remove all parent directories if possible
1067 while (--levels) {
1068 dirname(path);
1069
1070 // Break if path is suddenly empty
1071 if (!*path)
1072 break;
1073
a8783709
MT
1074 DEBUG(file->pakfire, "Trying to remove parent directory %s\n", path);
1075
3fca5032 1076 r = rmdir(path);
3fca5032 1077
a8783709
MT
1078 // Break on any error
1079 if (r)
1080 break;
3fca5032
MT
1081 }
1082
a942166c 1083 return 0;
3fca5032 1084}
cf9bd6f4 1085
9e09e361 1086static int pakfire_file_verify_mode(struct pakfire_file* file, const struct stat* st) {
a09b95e0
MT
1087 const mode_t type = pakfire_file_get_type(file);
1088
9e09e361 1089 // Did the type change?
a09b95e0 1090 if (type != (st->st_mode & S_IFMT)) {
9e09e361
MT
1091 file->verify_status |= PAKFIRE_FILE_TYPE_CHANGED;
1092
1093 DEBUG(file->pakfire, "%s: File Type changed\n", file->path);
1094 }
1095
a09b95e0
MT
1096 const mode_t perms = pakfire_file_get_perms(file);
1097
9e09e361 1098 // Check permissions
a09b95e0 1099 if (perms != (st->st_mode & 0777)) {
9e09e361
MT
1100 file->verify_status |= PAKFIRE_FILE_PERMISSIONS_CHANGED;
1101
1102 DEBUG(file->pakfire, "%s: Permissions changed\n", file->path);
1103 }
1104
f8733b31
MT
1105#if 0
1106 // XXX This does not check what it is supposed to check
1107
9e09e361
MT
1108 // Check if device node changed
1109 if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
a09b95e0
MT
1110 const dev_t dev = pakfire_file_get_dev(file);
1111
1112 if (dev != st->st_dev) {
9e09e361
MT
1113 file->verify_status |= PAKFIRE_FILE_DEV_CHANGED;
1114
1115 DEBUG(file->pakfire, "%s: Device Node changed\n", file->path);
1116 }
1117 }
f8733b31 1118#endif
9e09e361
MT
1119
1120 return 0;
1121}
1122
c0b051bb
MT
1123static int pakfire_file_verify_size(struct pakfire_file* file, const struct stat* st) {
1124 // Nothing to do if size matches
a09b95e0 1125 if (file->st.st_size == st->st_size)
c0b051bb
MT
1126 return 0;
1127
1128 // Size differs
d2b0e219 1129 file->verify_status |= PAKFIRE_FILE_SIZE_CHANGED;
c0b051bb
MT
1130
1131 DEBUG(file->pakfire, "%s: Filesize differs (expected %zu, got %zu byte(s))\n",
a09b95e0 1132 file->path, file->st.st_size, st->st_size);
c0b051bb
MT
1133
1134 return 0;
1135}
1136
1137static int pakfire_file_verify_ownership(struct pakfire_file* file, const struct stat* st) {
1138 // Fetch UID/GID
1139#if 0
1140 const uid_t uid = pakfire_unmap_id(file->pakfire, st->st_uid);
1141 const gid_t gid = pakfire_unmap_id(file->pakfire, st->st_gid);
1142#else
1143 const uid_t uid = st->st_uid;
1144 const gid_t gid = st->st_gid;
1145#endif
1146
1147 // Fetch owner & group
302e3253
MT
1148 struct passwd* owner = pakfire_getpwnam(file->pakfire, file->uname);
1149 struct group* group = pakfire_getgrnam(file->pakfire, file->gname);
c0b051bb
MT
1150
1151 // Check if owner matches
1152 if (!owner || owner->pw_uid != uid) {
d2b0e219 1153 file->verify_status |= PAKFIRE_FILE_OWNER_CHANGED;
c0b051bb
MT
1154
1155 DEBUG(file->pakfire, "%s: Owner differs\n", file->path);
1156 }
1157
1158 // Check if group matches
1159 if (!group || group->gr_gid != gid) {
d2b0e219 1160 file->verify_status |= PAKFIRE_FILE_GROUP_CHANGED;
c0b051bb
MT
1161
1162 DEBUG(file->pakfire, "%s: Group differs\n", file->path);
1163 }
1164
1165 return 0;
1166}
1167
0eeac4a5
MT
1168static int pakfire_file_verify_timestamps(struct pakfire_file* file, const struct stat* st) {
1169 // Check creation time
a09b95e0 1170 if (file->st.st_ctime != st->st_ctime) {
0eeac4a5
MT
1171 file->verify_status |= PAKFIRE_FILE_CTIME_CHANGED;
1172
1173 DEBUG(file->pakfire, "%s: Creation time changed\n", file->path);
1174 }
1175
1176 // Check modification time
a09b95e0 1177 if (file->st.st_mtime != st->st_mtime) {
0eeac4a5
MT
1178 file->verify_status |= PAKFIRE_FILE_MTIME_CHANGED;
1179
1180 DEBUG(file->pakfire, "%s: Modification time changed\n", file->path);
1181 }
1182
1183 return 0;
1184}
1185
76011205 1186static int pakfire_file_verify_payload(struct pakfire_file* file, const struct stat* st) {
76011205
MT
1187 int r;
1188
c52e0148 1189 struct pakfire_digests computed_digests;
293881bc 1190 int digest_types = PAKFIRE_DIGEST_UNDEFINED;
76011205
MT
1191
1192 // Nothing to do for anything that isn't a regular file
1193 if (!S_ISREG(st->st_mode))
1194 return 0;
1195
1196 // Fast-path if size changed. The payload will have changed, too
1197 if (file->verify_status & PAKFIRE_FILE_SIZE_CHANGED) {
1198 file->verify_status |= PAKFIRE_FILE_PAYLOAD_CHANGED;
1199 return 0;
1200 }
1201
1202 // Check if this file has any digests at all
6b32db11 1203 digest_types = pakfire_digest_has_any(&file->digests);
76011205 1204
293881bc
MT
1205 if (!digest_types) {
1206 ERROR(file->pakfire, "%s: No digests available\n", file->path);
1207 return 0;
76011205
MT
1208 }
1209
293881bc 1210 // Compute digests
1e76689a 1211 r = __pakfire_file_compute_digests(file, &computed_digests, digest_types);
293881bc 1212 if (r)
76011205 1213 goto ERROR;
76011205 1214
293881bc
MT
1215 // Compare digests
1216 r = pakfire_digests_compare(file->pakfire, &file->digests, &computed_digests, digest_types);
76011205
MT
1217 if (r) {
1218 file->verify_status |= PAKFIRE_FILE_PAYLOAD_CHANGED;
1219
293881bc 1220 DEBUG(file->pakfire, "%s: Digest(s) do not match\n", file->path);
76011205
MT
1221 }
1222
76011205 1223ERROR:
76011205
MT
1224 return r;
1225}
1226
cf9bd6f4
MT
1227/*
1228 Verify the file - i.e. does the metadata match what is on disk?
1229*/
c0b051bb
MT
1230int pakfire_file_verify(struct pakfire_file* file, int* status) {
1231 struct stat st;
1232 int r;
1233
cf9bd6f4
MT
1234 DEBUG(file->pakfire, "Verifying %s...\n", file->path);
1235
c0b051bb
MT
1236 // stat() the file
1237 r = lstat(file->abspath, &st);
1238 if (r) {
1239 // File does not exist
1240 if (errno == ENOENT) {
1241 file->verify_status |= PAKFIRE_FILE_NOENT;
1242 return 1;
1243 }
1244
1245 // Raise any other errors from stat()
1246 return r;
1247 }
1248
9e09e361
MT
1249 // Verify mode
1250 r = pakfire_file_verify_mode(file, &st);
1251 if (r)
1252 return r;
1253
c0b051bb
MT
1254 // Verify size
1255 r = pakfire_file_verify_size(file, &st);
1256 if (r)
1257 return r;
1258
1259 // Verify ownership
1260 r = pakfire_file_verify_ownership(file, &st);
1261 if (r)
1262 return r;
1263
0eeac4a5
MT
1264 // Verify timestamps
1265 r = pakfire_file_verify_timestamps(file, &st);
1266 if (r)
1267 return r;
1268
76011205
MT
1269 // Verify payload
1270 r = pakfire_file_verify_payload(file, &st);
1271 if (r)
1272 return r;
1273
cf9bd6f4
MT
1274 return 0;
1275}
c064d9ec
MT
1276
1277PAKFIRE_EXPORT int pakfire_file_matches(struct pakfire_file* file, const char* pattern) {
1278 int r;
1279
1280 // Don't match on no pattern
1281 if (!pattern)
1282 return 0;
1283
1284 // Check if the pattern matches
1285 r = fnmatch(pattern, file->path, 0);
1286 switch (r) {
1287 // Match
1288 case 0:
1289 return 1;
1290
1291 // No Match
1292 case FNM_NOMATCH:
1293 return 0;
1294
1295 default:
1296 return -1;
1297 }
1298}