35#include <archive_entry.h>
37#include "xbps_api_impl.h"
69static struct item *hashtab = NULL;
72static struct item **items = NULL;
73static size_t itemsidx = 0;
74static size_t itemssz = 0;
77lookupItem(
const char *file)
79 struct item *item = NULL;
83 HASH_FIND_STR(hashtab, file, item);
88addItem(
const char *file)
90 struct item *item = calloc(1,
sizeof (
struct item));
97 if (itemsidx+1 >= itemssz) {
98 itemssz = itemssz ? itemssz*2 : 64;
99 items = realloc(items, itemssz*
sizeof (
struct item *));
105 items[itemsidx++] = item;
111 item->len = strlen(item->file);
117 HASH_ADD_KEYPTR(hh, hashtab, item->file+1, item->len-1, item);
123typestr(
enum type 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;
135match_preserved_file(
struct xbps_handle *xhp,
const char *file)
137 if (xhp->preserved_files == NULL)
140 assert(file && *file ==
'.');
145has_dir_prefix(
const char *dir,
size_t dirlen,
const char *file)
147 return strncmp(file, dir, dirlen) == 0 && file[dirlen] ==
'/';
151can_delete_directory(
const char *dir,
size_t dirlen,
size_t max)
154 size_t rmcount = 0, fcount = 0;
159 if (errno == ENOENT) {
163 __func__, dir, strerror(errno));
173 for (
size_t i = 0; i < max; i++) {
175 if (has_dir_prefix(dir, dirlen, item->file)) {
176 if (!item->deleted) {
188 while (readdir(dp) != 0)
194 if (fcount <= rmcount) {
196 rmcount, fcount, dir);
200 return fcount <= rmcount;
207 const char *basesymlinks[] = {
218 xbps_dictionary_t obsd;
225 if (!xbps_dictionary_get_dict(xhp->
transd,
"obsolete_files", &obsd))
237 for (
size_t i = 0; i < itemsidx; i++) {
240 bool alloc =
false, found =
false;
244 if (match_preserved_file(xhp, item->file)) {
246 " and must be preserved: %s\n", item->old.pkgver, item->file);
250 if (item->new.type == 0) {
256 if (item->old.preserve) {
258 item->old.pkgver, typestr(item->old.type), item->file);
261 }
else if (item->new.type == TYPE_CONFFILE) {
266 }
else if (item->old.type == 0) {
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);
282 }
else if (item->old.type == TYPE_DIR &&
283 item->new.type != TYPE_DIR && item->new.type != 0) {
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);
297 }
else if (item->new.type != item->old.type) {
308 for (uint8_t x = 0; x < __arraycount(basesymlinks); x++) {
309 if (strcmp(item->file+1, basesymlinks[x]) == 0) {
320 if (item->old.sha256 != NULL) {
329 item->deleted =
true;
339 if (item->old.removepkg && !item->new.pkgname &&
340 (xhp->
flags & XBPS_FLAG_FORCE_REMOVE_FILES) != 0) {
342 " force remove %s: %s\n",
343 item->old.pkgname, typestr(item->old.type),
348 " skipping remove %s: %s\n",
349 item->old.pkgname, typestr(item->old.type),
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",
373 "symlink_target: %s\n", item->file+1, strerror(errno));
376 if (strcmp(lnk, item->old.target) != 0) {
378 " symlink (stored `%s' current `%s'): %s\n",
379 item->old.pkgname, item->old.target, lnk, item->file+1);
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;
398 pkgname = item->new.pkgname;
403 pkgname, typestr(item->old.type), item->file+1);
409 item->deleted =
true;
414 if ((a = xbps_dictionary_get(obsd, pkgname)) == NULL) {
415 if (!(a = xbps_array_create()) ||
416 !(xbps_dictionary_set(obsd, pkgname, a)))
420 if (!xbps_array_add_cstring(a, item->file)) {
422 xbps_object_release(a);
426 xbps_object_release(a);
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)
442 if ((item = lookupItem(file)) == NULL) {
443 item = addItem(file);
446 item->deleted =
false;
451 if (item->old.type == 0) {
455 }
else if (type == TYPE_DIR && item->old.type == TYPE_DIR) {
460 if (idx < item->old.index || item->old.preserve)
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;
475 " by package `%s': %s\n", pkgver, item->old.pkgver, file);
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);
486 }
else if (preserve && !item->old.preserve) {
487 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL,
489 "%s: preserved file `%s' removed by %s.",
490 pkgver, file, item->old.pkgver);
500 if (item->new.type == 0) {
504 }
else if (type == TYPE_DIR && item->new.type == TYPE_DIR) {
514 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL,
516 "%s: file `%s' already installed by package %s.",
517 pkgver, file, item->new.pkgver);
518 if (xhp->
flags & XBPS_FLAG_IGNORE_FILE_CONFLICTS)
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;
539 item->old.sha256 = strdup(sha256);
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;
551 if (item->old.type && item->new.type) {
556 if (strcmp(item->new.pkgname, item->old.pkgname) != 0) {
559 " package `%s': %s\n", pkgver, typestr(item->old.type),
560 item->new.pkgver, file);
563 " package `%s': %s\n", pkgver, typestr(item->new.type),
564 item->old.pkgver, file);
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)
578 xbps_dictionary_t filed;
582 const char *file, *sha256 = NULL;
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);
590 xbps_dictionary_get_cstring_nocopy(filed,
"sha256", &sha256);
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);
598 }
else if (rv != 0) {
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);
608 xbps_dictionary_get_uint64(filed,
"size", &size);
610 xbps_dictionary_get_cstring_nocopy(filed,
"sha256", &sha256);
613 if (removefile && stat(file, &st) != -1 && size != (uint64_t)st.st_size)
616 rv = collect_file(xhp, file, size, pkgname, pkgver, idx, sha256,
617 TYPE_CONFFILE, update, removepkg, preserve, removefile, NULL);
621 }
else if (rv != 0) {
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);
633 rv = collect_file(xhp, file, 0, pkgname, pkgver, idx, NULL,
634 TYPE_LINK, update, removepkg, preserve, removefile, target);
638 }
else if (rv != 0) {
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);
652 }
else if (rv != 0) {
666collect_binpkg_files(
struct xbps_handle *xhp, xbps_dictionary_t pkg_repod,
667 unsigned int idx,
bool update)
669 xbps_dictionary_t filesd;
670 struct archive *ar = NULL;
671 struct archive_entry *entry;
673 const char *pkgver, *pkgname;
676 int rv = 0, pkg_fd = -1;
678 xbps_dictionary_get_cstring_nocopy(pkg_repod,
"pkgver", &pkgver);
680 xbps_dictionary_get_cstring_nocopy(pkg_repod,
"pkgname", &pkgname);
689 if ((ar = archive_read_new()) == NULL) {
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);
704 pkg_fd = open(bpkg, O_RDONLY|O_CLOEXEC);
707 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL,
709 "%s: failed to open binary package `%s': %s",
710 pkgver, bpkg, strerror(rv));
713 if (fstat(pkg_fd, &st) == -1) {
715 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL,
717 "%s: failed to fstat binary package `%s': %s",
718 pkgver, bpkg, strerror(rv));
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,
725 "%s: failed to read binary package `%s': %s",
726 pkgver, bpkg, strerror(rv));
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)
735 else if (ar_rv == ARCHIVE_RETRY)
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) {
745 rv = collect_files(xhp, filesd, pkgname, pkgver, idx,
746 update,
false,
false,
false);
747 xbps_object_release(filesd);
750 archive_read_data_skip(ar);
757 archive_read_free(ar);
763pathcmp(
const void *l1,
const void *l2)
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);
773 struct item *item, *itmp;
775 HASH_ITER(hh, hashtab, item, itmp) {
776 HASH_DEL(hashtab, item);
778 free(item->old.sha256);
779 free(item->new.sha256);
806xbps_transaction_files(
struct xbps_handle *xhp, xbps_object_iterator_t iter)
808 xbps_dictionary_t pkgd, filesd;
811 const char *pkgver, *pkgname;
813 unsigned int idx = 0;
818 while ((obj = xbps_object_iterator_next(iter)) != NULL) {
826 if (ttype == XBPS_TRANS_HOLD || ttype == XBPS_TRANS_CONFIGURE) {
830 if (!xbps_dictionary_get_cstring_nocopy(obj,
"pkgver", &pkgver)) {
833 if (!xbps_dictionary_get_cstring_nocopy(obj,
"pkgname", &pkgname)) {
837 update = (ttype == XBPS_TRANS_UPDATE);
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);
857 const char *oldpkgver;
858 bool preserve =
false;
859 bool removepkg = (ttype == XBPS_TRANS_REMOVE);
861 xbps_dictionary_get_cstring_nocopy(pkgd,
"pkgver", &oldpkgver);
862 if (!xbps_dictionary_get_bool(obj,
"preserve", &preserve))
866 if (filesd == NULL) {
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);
879 xbps_object_iterator_reset(iter);
885 qsort(items, itemsidx,
sizeof (
struct item *), pathcmp);
887 if (chdir(xhp->
rootdir) == -1) {
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));
898 rv = collect_obsoletes(xhp);
char rootdir[XBPS_MAXPATH]
Generic XBPS structure handler for initialization.
void xbps_dbg_printf(const char *fmt,...)
Prints debug messages to stderr.
xbps_dictionary_t xbps_pkgdb_get_pkg_files(struct xbps_handle *xhp, const char *pkg)
xbps_dictionary_t xbps_pkgdb_get_pkg(struct xbps_handle *xhp, const char *pkg)
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)
char * xbps_xasprintf(const char *fmt,...) __attribute__((format(printf
int xbps_file_sha256_check(const char *file, const char *sha256)
char * xbps_symlink_target(struct xbps_handle *xhp, const char *path, const char *target)
char * xbps_repository_pkg_path(struct xbps_handle *xhp, xbps_dictionary_t pkgd)