XBPS Library API 20251227
The X Binary Package System
transaction_files.c
1/*-
2 * Copyright (c) 2019 Juan Romero Pardines.
3 * Copyright (c) 2019 Duncan Overbruck <mail@duncano.de>.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <dirent.h>
28#include <errno.h>
29#include <fcntl.h>
30#include <limits.h>
31#include <stdlib.h>
32#include <string.h>
33
34#include <archive.h>
35#include <archive_entry.h>
36
37#include "xbps_api_impl.h"
38#include "uthash.h"
39
40enum type {
41 TYPE_LINK = 1,
42 TYPE_DIR,
43 TYPE_FILE,
44 TYPE_CONFFILE,
45};
46
47struct item {
48 char *file;
49 size_t len;
50 struct {
51 const char *pkgname;
52 const char *pkgver;
53 char *sha256;
54 const char *target;
55 uint64_t size;
56 enum type type;
57 /* index is the index of the package update/install/removal in the transaction
58 * and is used to decide which package should remove the given file or dir */
59 unsigned int index;
60 bool preserve;
61 bool update;
62 bool removepkg;
63 } old, new;
64 bool deleted;
65 UT_hash_handle hh;
66};
67
68/* hash table to look up files by path */
69static struct item *hashtab = NULL;
70
71/* list of files to be sorted using qsort */
72static struct item **items = NULL;
73static size_t itemsidx = 0;
74static size_t itemssz = 0;
75
76static struct item *
77lookupItem(const char *file)
78{
79 struct item *item = NULL;
80
81 assert(file);
82
83 HASH_FIND_STR(hashtab, file, item);
84 return item;
85}
86
87static struct item *
88addItem(const char *file)
89{
90 struct item *item = calloc(1, sizeof (struct item));
91 if (item == NULL)
92 return NULL;
93
94 assert(file);
95 assert(item);
96
97 if (itemsidx+1 >= itemssz) {
98 itemssz = itemssz ? itemssz*2 : 64;
99 items = realloc(items, itemssz*sizeof (struct item *));
100 if (items == NULL) {
101 free(item);
102 return NULL;
103 }
104 }
105 items[itemsidx++] = item;
106
107 if ((item->file = xbps_xasprintf(".%s", file)) == NULL) {
108 free(item);
109 return NULL;
110 }
111 item->len = strlen(item->file);
112
113 /*
114 * File paths are stored relative, but looked up absolute.
115 * Skip the leading . (dot) and substract it from the length.
116 */
117 HASH_ADD_KEYPTR(hh, hashtab, item->file+1, item->len-1, item);
118
119 return item;
120}
121
122static const char *
123typestr(enum type typ)
124{
125 switch (typ) {
126 case TYPE_LINK: return "symlink";
127 case TYPE_DIR: return "directory";
128 case TYPE_FILE: return "file";
129 case TYPE_CONFFILE: return "configuration file";
130 default: return NULL;
131 }
132}
133
134static bool
135match_preserved_file(struct xbps_handle *xhp, const char *file)
136{
137 if (xhp->preserved_files == NULL)
138 return false;
139
140 assert(file && *file == '.');
141 return xbps_match_string_in_array(xhp->preserved_files, file+1);
142}
143
144static bool
145has_dir_prefix(const char *dir, size_t dirlen, const char *file)
146{
147 return strncmp(file, dir, dirlen) == 0 && file[dirlen] == '/';
148}
149
150static bool
151can_delete_directory(const char *dir, size_t dirlen, size_t max)
152{
153 struct item *item;
154 size_t rmcount = 0, fcount = 0;
155 DIR *dp;
156
157 dp = opendir(dir);
158 if (dp == NULL) {
159 if (errno == ENOENT) {
160 return true;
161 } else {
162 xbps_dbg_printf("[files] %s: %s: %s\n",
163 __func__, dir, strerror(errno));
164 return false;
165 }
166 }
167
168 /*
169 * 1. Check if there is tracked directory content,
170 * which can't be deleted.
171 * 2. Count deletable directory content.
172 */
173 for (size_t i = 0; i < max; i++) {
174 item = items[i];
175 if (has_dir_prefix(dir, dirlen, item->file)) {
176 if (!item->deleted) {
177 closedir(dp);
178 return false;
179 }
180 rmcount++;
181 }
182 }
183
184 /*
185 * Check if directory contains more files than we can
186 * delete.
187 */
188 while (readdir(dp) != 0)
189 fcount++;
190
191 /* ignore '.' and '..' */
192 fcount -= 2;
193
194 if (fcount <= rmcount) {
195 xbps_dbg_printf("[files] only removed %zu out of %zu files: %s\n",
196 rmcount, fcount, dir);
197 }
198 closedir(dp);
199
200 return fcount <= rmcount;
201}
202
203static int
204collect_obsoletes(struct xbps_handle *xhp)
205{
206 /* These are symlinks in Void and must not be removed */
207 const char *basesymlinks[] = {
208 "/bin",
209 "/sbin",
210 "/usr/sbin",
211 "/lib",
212 "/lib32",
213 "/lib64",
214 "/usr/lib32",
215 "/usr/lib64",
216 "/var/run",
217 };
218 xbps_dictionary_t obsd;
219 struct item *item;
220 int rv = 0;
221
222 if (xhp->transd == NULL)
223 return ENOTSUP;
224
225 if (!xbps_dictionary_get_dict(xhp->transd, "obsolete_files", &obsd))
226 return ENOENT;
227
228 /*
229 * Iterate over all files, longest paths first,
230 * to check if directory contents of removed
231 * directories can be deleted.
232 *
233 * - Check if a file is obsolete
234 * - Check if obsolete file can be deleted.
235 * - Check if directory needs and can be deleted.
236 */
237 for (size_t i = 0; i < itemsidx; i++) {
238 xbps_array_t a;
239 const char *pkgname;
240 bool alloc = false, found = false;
241
242 item = items[i];
243
244 if (match_preserved_file(xhp, item->file)) {
245 xbps_dbg_printf("[obsoletes] %s: file exists on disk"
246 " and must be preserved: %s\n", item->old.pkgver, item->file);
247 continue;
248 }
249
250 if (item->new.type == 0) {
251 /*
252 * File was removed and is not provided by any
253 * new package.
254 * Probably obsolete.
255 */
256 if (item->old.preserve) {
257 xbps_dbg_printf("[files] %s: skipping `preserve` %s: %s\n",
258 item->old.pkgver, typestr(item->old.type), item->file);
259 continue;
260 }
261 } else if (item->new.type == TYPE_CONFFILE) {
262 /*
263 * Ignore conf files.
264 */
265 continue;
266 } else if (item->old.type == 0) {
267 /* XXX: add this new behaviour? */
268#if 0
269 /*
270 * Check if new file (untracked until now) exists.
271 */
272 if (access(item->file, F_OK) == 0) {
273 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL,
274 EEXIST, item->new.pkgver,
275 "%s: file `%s' already exists.",
276 item->new.pkgver, item->file);
277 rv = EEXIST;
278 break;
279 }
280#endif
281 continue;
282 } else if (item->old.type == TYPE_DIR &&
283 item->new.type != TYPE_DIR && item->new.type != 0) {
284 /*
285 * Directory replaced by a file or symlink.
286 * We MUST be able to delete the directory.
287 */
288 xbps_dbg_printf("[files] %s: directory changed to %s: %s\n",
289 item->new.pkgver, typestr(item->new.type), item->file);
290 if (!can_delete_directory(item->file, item->len, i)) {
291 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL,
292 ENOTEMPTY, item->old.pkgver,
293 "%s: directory `%s' can not be deleted.",
294 item->old.pkgver, item->file);
295 return ENOTEMPTY;
296 }
297 } else if (item->new.type != item->old.type) {
298 /*
299 * File type changed, we have to delete it.
300 */
301 } else {
302 continue;
303 }
304
305 /*
306 * Make sure to not remove any symlink of root directory.
307 */
308 for (uint8_t x = 0; x < __arraycount(basesymlinks); x++) {
309 if (strcmp(item->file+1, basesymlinks[x]) == 0) {
310 found = true;
311 break;
312 }
313 }
314 if (found)
315 continue;
316
317 /*
318 * Skip unexisting files and keep files with hash mismatch.
319 */
320 if (item->old.sha256 != NULL) {
321 rv = xbps_file_sha256_check(item->file, item->old.sha256);
322 switch (rv) {
323 case 0:
324 /* hash matches, we can safely delete and/or overwrite it */
325 break;
326 case ENOENT:
327 /* mark unexisting files as deleted and ignore ENOENT */
328 rv = 0;
329 item->deleted = true;
330 continue;
331 case ERANGE:
332 /* hash mismatch don't delete it */
333 rv = 0;
334 /*
335 * If the file is removed by uninstalling the package,
336 * no new package provides it and its not force removed,
337 * keep the file.
338 */
339 if (item->old.removepkg && !item->new.pkgname &&
340 (xhp->flags & XBPS_FLAG_FORCE_REMOVE_FILES) != 0) {
341 xbps_dbg_printf("[obsoletes] %s: SHA256 mismatch,"
342 " force remove %s: %s\n",
343 item->old.pkgname, typestr(item->old.type),
344 item->file+1);
345 break;
346 }
347 xbps_dbg_printf("[obsoletes] %s: SHA256 mismatch,"
348 " skipping remove %s: %s\n",
349 item->old.pkgname, typestr(item->old.type),
350 item->file+1);
351 continue;
352 default:
353 break;
354 }
355 }
356
357 /*
358 * On package removal without force, keep symlinks if target changed.
359 */
360 if (item->old.pkgname && item->old.removepkg &&
361 item->old.type == TYPE_LINK && !item->new.pkgname &&
362 (xhp->flags & XBPS_FLAG_FORCE_REMOVE_FILES) == 0) {
363 char path[PATH_MAX], *lnk;
364 const char *file = item->file+1;
365 if (strcmp(xhp->rootdir, "/") != 0) {
366 snprintf(path, sizeof(path), "%s%s",
367 xhp->rootdir, item->file+1);
368 file = path;
369 }
370 lnk = xbps_symlink_target(xhp, file, item->old.target);
371 if (lnk == NULL) {
372 xbps_dbg_printf("[obsoletes] %s "
373 "symlink_target: %s\n", item->file+1, strerror(errno));
374 continue;
375 }
376 if (strcmp(lnk, item->old.target) != 0) {
377 xbps_dbg_printf("[obsoletes] %s: skipping modified"
378 " symlink (stored `%s' current `%s'): %s\n",
379 item->old.pkgname, item->old.target, lnk, item->file+1);
380 free(lnk);
381 continue;
382 }
383 free(lnk);
384 }
385
386 /*
387 * Choose which package removes the obsolete files,
388 * based which packages is installed/unpacked first.
389 * This is necessary to not delete files
390 * after it was installed by another package.
391 */
392 if (item->old.pkgname && item->new.pkgname) {
393 pkgname = item->old.index > item->new.index ?
394 item->new.pkgname : item->old.pkgname;
395 } else if (item->old.pkgname) {
396 pkgname = item->old.pkgname;
397 } else {
398 pkgname = item->new.pkgname;
399 }
400 assert(pkgname);
401
402 xbps_dbg_printf("[obsoletes] %s: removes %s: %s\n",
403 pkgname, typestr(item->old.type), item->file+1);
404
405 /*
406 * Mark file as being deleted, this is used when
407 * checking if a directory can be deleted.
408 */
409 item->deleted = true;
410
411 /*
412 * Add file to the packages `obsolete_files` dict
413 */
414 if ((a = xbps_dictionary_get(obsd, pkgname)) == NULL) {
415 if (!(a = xbps_array_create()) ||
416 !(xbps_dictionary_set(obsd, pkgname, a)))
417 return ENOMEM;
418 alloc = true;
419 }
420 if (!xbps_array_add_cstring(a, item->file)) {
421 if (alloc)
422 xbps_object_release(a);
423 return ENOMEM;
424 }
425 if (alloc)
426 xbps_object_release(a);
427 }
428
429 return rv;
430}
431
432static int
433collect_file(struct xbps_handle *xhp, const char *file, size_t size,
434 const char *pkgname, const char *pkgver, unsigned int idx,
435 const char *sha256, enum type type, bool update, bool removepkg,
436 bool preserve, bool removefile, const char *target)
437{
438 struct item *item;
439
440 assert(file);
441
442 if ((item = lookupItem(file)) == NULL) {
443 item = addItem(file);
444 if (item == NULL)
445 return ENOMEM;
446 item->deleted = false;
447 goto add;
448 }
449
450 if (removefile) {
451 if (item->old.type == 0) {
452 /*
453 * File wasn't removed before.
454 */
455 } else if (type == TYPE_DIR && item->old.type == TYPE_DIR) {
456 /*
457 * Multiple packages removing the same directory.
458 * Record the last package to remove this directory.
459 */
460 if (idx < item->old.index || item->old.preserve)
461 return 0;
462 item->old.pkgname = pkgname;
463 item->old.pkgver = pkgver;
464 item->old.index = idx;
465 item->old.preserve = preserve;
466 item->old.update = update;
467 item->old.removepkg = removepkg;
468 return 0;
469 } else {
470 /*
471 * Multiple packages removing the same file.
472 * Shouldn't happen, but its not fatal.
473 */
474 xbps_dbg_printf("[files] %s: file already removed"
475 " by package `%s': %s\n", pkgver, item->old.pkgver, file);
476
477 /*
478 * Check if `preserve` is violated.
479 */
480 if (item->old.preserve && !preserve) {
481 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL,
482 EPERM, item->old.pkgver,
483 "%s: preserved file `%s' removed by %s.",
484 item->old.pkgver, file, pkgver);
485 return EPERM;
486 } else if (preserve && !item->old.preserve) {
487 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL,
488 EPERM, pkgver,
489 "%s: preserved file `%s' removed by %s.",
490 pkgver, file, item->old.pkgver);
491 return EPERM;
492 }
493 return 0;
494 }
495 goto add;
496 } else {
497 /*
498 * Multiple packages creating the same directory.
499 */
500 if (item->new.type == 0) {
501 /*
502 * File wasn't created before.
503 */
504 } else if (type == TYPE_DIR && item->new.type == TYPE_DIR) {
505 /*
506 * Multiple packages creating the same directory.
507 */
508 return 0;
509 } else {
510 /*
511 * Multiple packages creating the same file.
512 * This should never happen in a transaction.
513 */
514 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL,
515 EEXIST, pkgver,
516 "%s: file `%s' already installed by package %s.",
517 pkgver, file, item->new.pkgver);
518 if (xhp->flags & XBPS_FLAG_IGNORE_FILE_CONFLICTS)
519 return 0;
520
521 return EEXIST;
522 }
523 goto add;
524 }
525
526 return 0;
527add:
528 if (removefile) {
529 item->old.pkgname = pkgname;
530 item->old.pkgver = pkgver;
531 item->old.type = type;
532 item->old.size = size;
533 item->old.index = idx;
534 item->old.preserve = preserve;
535 item->old.update = update;
536 item->old.removepkg = removepkg;
537 item->old.target = target;
538 if (sha256)
539 item->old.sha256 = strdup(sha256);
540 } else {
541 item->new.pkgname = pkgname;
542 item->new.pkgver = pkgver;
543 item->new.type = type;
544 item->new.size = size;
545 item->new.index = idx;
546 item->new.preserve = preserve;
547 item->new.update = update;
548 item->new.removepkg = removepkg;
549 item->new.target = target;
550 }
551 if (item->old.type && item->new.type) {
552 /*
553 * The file was removed by one package
554 * and installed by another package.
555 */
556 if (strcmp(item->new.pkgname, item->old.pkgname) != 0) {
557 if (removefile) {
558 xbps_dbg_printf("[files] %s: %s moved to"
559 " package `%s': %s\n", pkgver, typestr(item->old.type),
560 item->new.pkgver, file);
561 } else {
562 xbps_dbg_printf("[files] %s: %s moved from"
563 " package `%s': %s\n", pkgver, typestr(item->new.type),
564 item->old.pkgver, file);
565 }
566 }
567 }
568
569 return 0;
570}
571
572static int
573collect_files(struct xbps_handle *xhp, xbps_dictionary_t d,
574 const char *pkgname, const char *pkgver, unsigned int idx,
575 bool update, bool removepkg, bool preserve, bool removefile)
576{
577 xbps_array_t a;
578 xbps_dictionary_t filed;
579 uint64_t size;
580 unsigned int i;
581 int rv = 0;
582 const char *file, *sha256 = NULL;
583 bool error = false;
584
585 if ((a = xbps_dictionary_get(d, "files"))) {
586 for (i = 0; i < xbps_array_count(a); i++) {
587 filed = xbps_array_get(a, i);
588 xbps_dictionary_get_cstring_nocopy(filed, "file", &file);
589 if (removefile)
590 xbps_dictionary_get_cstring_nocopy(filed, "sha256", &sha256);
591 size = 0;
592 xbps_dictionary_get_uint64(filed, "size", &size);
593 rv = collect_file(xhp, file, size, pkgname, pkgver, idx, sha256,
594 TYPE_FILE, update, removepkg, preserve, removefile, NULL);
595 if (rv == EEXIST) {
596 error = true;
597 continue;
598 } else if (rv != 0) {
599 goto out;
600 }
601 }
602 }
603 if ((a = xbps_dictionary_get(d, "conf_files"))) {
604 for (i = 0; i < xbps_array_count(a); i++) {
605 filed = xbps_array_get(a, i);
606 xbps_dictionary_get_cstring_nocopy(filed, "file", &file);
607 size = 0;
608 xbps_dictionary_get_uint64(filed, "size", &size);
609 if (removefile)
610 xbps_dictionary_get_cstring_nocopy(filed, "sha256", &sha256);
611#if 0
612 /* XXX: how to handle conf_file size */
613 if (removefile && stat(file, &st) != -1 && size != (uint64_t)st.st_size)
614 size = 0;
615#endif
616 rv = collect_file(xhp, file, size, pkgname, pkgver, idx, sha256,
617 TYPE_CONFFILE, update, removepkg, preserve, removefile, NULL);
618 if (rv == EEXIST) {
619 error = true;
620 continue;
621 } else if (rv != 0) {
622 goto out;
623 }
624 }
625 }
626 if ((a = xbps_dictionary_get(d, "links"))) {
627 for (i = 0; i < xbps_array_count(a); i++) {
628 const char *target = NULL;
629 filed = xbps_array_get(a, i);
630 xbps_dictionary_get_cstring_nocopy(filed, "file", &file);
631 xbps_dictionary_get_cstring_nocopy(filed, "target", &target);
632 assert(target);
633 rv = collect_file(xhp, file, 0, pkgname, pkgver, idx, NULL,
634 TYPE_LINK, update, removepkg, preserve, removefile, target);
635 if (rv == EEXIST) {
636 error = true;
637 continue;
638 } else if (rv != 0) {
639 goto out;
640 }
641 }
642 }
643 if ((a = xbps_dictionary_get(d, "dirs"))) {
644 for (i = 0; i < xbps_array_count(a); i++) {
645 filed = xbps_array_get(a, i);
646 xbps_dictionary_get_cstring_nocopy(filed, "file", &file);
647 rv = collect_file(xhp, file, 0, pkgname, pkgver, idx, NULL,
648 TYPE_DIR, update, removepkg, preserve, removefile, NULL);
649 if (rv == EEXIST) {
650 error = true;
651 continue;
652 } else if (rv != 0) {
653 goto out;
654 }
655 }
656 }
657
658out:
659 if (error)
660 rv = EEXIST;
661
662 return rv;
663}
664
665static int
666collect_binpkg_files(struct xbps_handle *xhp, xbps_dictionary_t pkg_repod,
667 unsigned int idx, bool update)
668{
669 xbps_dictionary_t filesd;
670 struct archive *ar = NULL;
671 struct archive_entry *entry;
672 struct stat st;
673 const char *pkgver, *pkgname;
674 char *bpkg;
675 /* size_t entry_size; */
676 int rv = 0, pkg_fd = -1;
677
678 xbps_dictionary_get_cstring_nocopy(pkg_repod, "pkgver", &pkgver);
679 assert(pkgver);
680 xbps_dictionary_get_cstring_nocopy(pkg_repod, "pkgname", &pkgname);
681 assert(pkgname);
682
683 bpkg = xbps_repository_pkg_path(xhp, pkg_repod);
684 if (bpkg == NULL) {
685 rv = errno;
686 goto out;
687 }
688
689 if ((ar = archive_read_new()) == NULL) {
690 rv = errno;
691 goto out;
692 }
693
694 /*
695 * Enable support for tar format and gzip/bzip2/lzma compression methods.
696 */
697 archive_read_support_filter_gzip(ar);
698 archive_read_support_filter_bzip2(ar);
699 archive_read_support_filter_xz(ar);
700 archive_read_support_filter_lz4(ar);
701 archive_read_support_filter_zstd(ar);
702 archive_read_support_format_tar(ar);
703
704 pkg_fd = open(bpkg, O_RDONLY|O_CLOEXEC);
705 if (pkg_fd == -1) {
706 rv = errno;
707 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL,
708 rv, pkgver,
709 "%s: failed to open binary package `%s': %s",
710 pkgver, bpkg, strerror(rv));
711 goto out;
712 }
713 if (fstat(pkg_fd, &st) == -1) {
714 rv = errno;
715 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL,
716 rv, pkgver,
717 "%s: failed to fstat binary package `%s': %s",
718 pkgver, bpkg, strerror(rv));
719 goto out;
720 }
721 if (archive_read_open_fd(ar, pkg_fd, st.st_blksize) == ARCHIVE_FATAL) {
722 rv = xbps_archive_errno(ar);
723 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL,
724 rv, pkgver,
725 "%s: failed to read binary package `%s': %s",
726 pkgver, bpkg, strerror(rv));
727 goto out;
728 }
729
730 for (uint8_t i = 0; i < 4; i++) {
731 const char *entry_pname;
732 int ar_rv = archive_read_next_header(ar, &entry);
733 if (ar_rv == ARCHIVE_EOF || ar_rv == ARCHIVE_FATAL)
734 break;
735 else if (ar_rv == ARCHIVE_RETRY)
736 continue;
737
738 entry_pname = archive_entry_pathname(entry);
739 if ((strcmp("./files.plist", entry_pname)) == 0) {
740 filesd = xbps_archive_get_dictionary(ar, entry);
741 if (filesd == NULL) {
742 rv = EINVAL;
743 goto out;
744 }
745 rv = collect_files(xhp, filesd, pkgname, pkgver, idx,
746 update, false, false, false);
747 xbps_object_release(filesd);
748 goto out;
749 }
750 archive_read_data_skip(ar);
751 }
752
753out:
754 if (pkg_fd != -1)
755 close(pkg_fd);
756 if (ar != NULL)
757 archive_read_free(ar);
758 free(bpkg);
759 return rv;
760}
761
762static int
763pathcmp(const void *l1, const void *l2)
764{
765 const struct item *a = *(const struct item * const*)l1;
766 const struct item *b = *(const struct item * const*)l2;
767 return (a->len < b->len) - (b->len < a->len);
768}
769
770static void
771cleanup(void)
772{
773 struct item *item, *itmp;
774
775 HASH_ITER(hh, hashtab, item, itmp) {
776 HASH_DEL(hashtab, item);
777 free(item->file);
778 free(item->old.sha256);
779 free(item->new.sha256);
780 free(item);
781 }
782 free(items);
783}
784
785/*
786 * xbps_transaction_files:
787 *
788 * - read files from each installed package in the transaction
789 * - read files from each binary package in the transaction
790 *
791 * - Find file conflicts between packages before starting the transaction
792 *
793 * - Schedule the removal of files
794 * - unlink files before extracting the package if the file type changed,
795 * a symlink becomes a directory or a directory becomes a regular file
796 * or symlink.
797 * - directories replaced with other file types are checked to be empty
798 * to avoid ENOTEMPTY while unpacking packages.
799 * - the last package removing a file out of a directory
800 * will try to remove that directory to avoid ENOTEMPTY
801 * - the removal of obsolete files and directory is sorted by
802 * path length so that directory content is removed before
803 * removing the directory.
804 */
805int HIDDEN
806xbps_transaction_files(struct xbps_handle *xhp, xbps_object_iterator_t iter)
807{
808 xbps_dictionary_t pkgd, filesd;
809 xbps_object_t obj;
810 xbps_trans_type_t ttype;
811 const char *pkgver, *pkgname;
812 int rv = 0;
813 unsigned int idx = 0;
814
815 assert(xhp);
816 assert(iter);
817
818 while ((obj = xbps_object_iterator_next(iter)) != NULL) {
819 bool update = false;
820
821 /* increment the index of the given package package in the transaction */
822 idx++;
823
824 /* ignore pkgs in hold mode or in unpacked state */
825 ttype = xbps_transaction_pkg_type(obj);
826 if (ttype == XBPS_TRANS_HOLD || ttype == XBPS_TRANS_CONFIGURE) {
827 continue;
828 }
829
830 if (!xbps_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver)) {
831 return EINVAL;
832 }
833 if (!xbps_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname)) {
834 return EINVAL;
835 }
836
837 update = (ttype == XBPS_TRANS_UPDATE);
838
839 if (ttype == XBPS_TRANS_INSTALL || ttype == XBPS_TRANS_REINSTALL || ttype == XBPS_TRANS_UPDATE) {
840 xbps_set_cb_state(xhp, XBPS_STATE_FILES, 0, pkgver,
841 "%s: collecting files...", pkgver);
842 rv = collect_binpkg_files(xhp, obj, idx, update);
843 if (rv != 0)
844 goto out;
845 }
846
847 /*
848 * Always just try to get the package from the pkgdb:
849 * update and remove always have a previous package,
850 * `hold` and `configure` are skipped.
851 * And finally the reason to do is, `install` could be
852 * a reinstallation, in which case the files list could
853 * different between old and new "install".
854 */
855 pkgd = xbps_pkgdb_get_pkg(xhp, pkgname);
856 if (pkgd) {
857 const char *oldpkgver;
858 bool preserve = false;
859 bool removepkg = (ttype == XBPS_TRANS_REMOVE);
860
861 xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &oldpkgver);
862 if (!xbps_dictionary_get_bool(obj, "preserve", &preserve))
863 preserve = false;
864
865 filesd = xbps_pkgdb_get_pkg_files(xhp, pkgname);
866 if (filesd == NULL) {
867 continue;
868 }
869
870 assert(oldpkgver);
871 xbps_set_cb_state(xhp, XBPS_STATE_FILES, 0, oldpkgver,
872 "%s: collecting files...", oldpkgver);
873 rv = collect_files(xhp, filesd, pkgname, pkgver, idx,
874 update, removepkg, preserve, true);
875 if (rv != 0)
876 goto out;
877 }
878 }
879 xbps_object_iterator_reset(iter);
880
881 /*
882 * Sort items by path length, to make it easier to find files in
883 * directories.
884 */
885 qsort(items, itemsidx, sizeof (struct item *), pathcmp);
886
887 if (chdir(xhp->rootdir) == -1) {
888 rv = errno;
889 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL, rv, xhp->rootdir,
890 "failed to chdir to rootdir `%s': %s",
891 xhp->rootdir, strerror(errno));
892 }
893
894out:
895 if (rv != 0)
896 return rv;
897
898 rv = collect_obsoletes(xhp);
899 cleanup();
900 return rv;
901}
char rootdir[XBPS_MAXPATH]
Definition xbps.h:664
xbps_dictionary_t transd
Definition xbps.h:597
int flags
Definition xbps.h:693
Generic XBPS structure handler for initialization.
Definition xbps.h:560
void xbps_dbg_printf(const char *fmt,...)
Prints debug messages to stderr.
Definition log.c:67
xbps_dictionary_t xbps_pkgdb_get_pkg_files(struct xbps_handle *xhp, const char *pkg)
Definition pkgdb.c:533
xbps_dictionary_t xbps_pkgdb_get_pkg(struct xbps_handle *xhp, const char *pkg)
Definition pkgdb.c:408
bool xbps_match_string_in_array(xbps_array_t array, const char *val)
xbps_trans_type_t xbps_transaction_pkg_type(xbps_dictionary_t pkg_repod)
xbps_trans_type_t
Definition xbps.h:1390
char * xbps_xasprintf(const char *fmt,...) __attribute__((format(printf
int xbps_file_sha256_check(const char *file, const char *sha256)
Definition util_hash.c:201
char * xbps_symlink_target(struct xbps_handle *xhp, const char *path, const char *target)
Definition util.c:634
char * xbps_repository_pkg_path(struct xbps_handle *xhp, xbps_dictionary_t pkgd)
Definition util.c:389