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 ==
'.');
145can_delete_directory(
const char *file,
size_t len,
size_t max)
148 size_t rmcount = 0, fcount = 0;
153 if (errno == ENOENT) {
156 xbps_dbg_printf(
"[files] %s: %s: %s\n",
157 __func__, file, strerror(errno));
167 for (
size_t i = 0; i < max; i++) {
169 if (strncmp(item->file, file, len) == 0) {
170 if (!item->deleted) {
182 while (readdir(dp) != 0)
188 if (fcount <= rmcount) {
189 xbps_dbg_printf(
"[files] only removed %zu out of %zu files: %s\n",
190 rmcount, fcount, file);
194 return fcount <= rmcount;
201 const char *basesymlinks[] = {
212 xbps_dictionary_t obsd;
219 if (!xbps_dictionary_get_dict(xhp->
transd,
"obsolete_files", &obsd))
231 for (
size_t i = 0; i < itemsidx; i++) {
234 bool alloc =
false, found =
false;
238 if (match_preserved_file(xhp, item->file)) {
239 xbps_dbg_printf(
"[obsoletes] %s: file exists on disk"
240 " and must be preserved: %s\n", item->old.pkgver, item->file);
244 if (item->new.type == 0) {
250 if (item->old.preserve && item->old.update) {
251 xbps_dbg_printf(
"[files] %s: skipping `preserve` %s: %s\n",
252 item->old.pkgver, typestr(item->old.type), item->file);
255 }
else if (item->new.type == TYPE_CONFFILE) {
260 }
else if (item->old.type == 0) {
266 if (access(item->file, F_OK) == 0) {
267 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL,
268 EEXIST, item->new.pkgver,
269 "%s: file `%s' already exists.",
270 item->new.pkgver, item->file);
276 }
else if (item->old.type == TYPE_DIR &&
277 item->new.type != TYPE_DIR && item->new.type != 0) {
282 xbps_dbg_printf(
"[files] %s: directory changed to %s: %s\n",
283 item->new.pkgver, typestr(item->new.type), item->file);
284 if (!can_delete_directory(item->file, item->len, i)) {
285 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL,
286 ENOTEMPTY, item->old.pkgver,
287 "%s: directory `%s' can not be deleted.",
288 item->old.pkgver, item->file);
291 }
else if (item->new.type != item->old.type) {
302 for (uint8_t x = 0; x < __arraycount(basesymlinks); x++) {
303 if (strcmp(item->file+1, basesymlinks[x]) == 0) {
314 if (item->old.sha256 != NULL) {
323 item->deleted =
true;
333 if (item->old.removepkg && !item->new.pkgname &&
334 (xhp->
flags & XBPS_FLAG_FORCE_REMOVE_FILES) != 0) {
335 xbps_dbg_printf(
"[obsoletes] %s: SHA256 mismatch,"
336 " force remove %s: %s\n",
337 item->old.pkgname, typestr(item->old.type),
341 xbps_dbg_printf(
"[obsoletes] %s: SHA256 mismatch,"
342 " skipping remove %s: %s\n",
343 item->old.pkgname, typestr(item->old.type),
354 if (item->old.pkgname && item->old.removepkg &&
355 item->old.type == TYPE_LINK && !item->new.pkgname &&
356 (xhp->
flags & XBPS_FLAG_FORCE_REMOVE_FILES) == 0) {
357 char path[PATH_MAX], *lnk;
358 const char *file = item->file+1;
359 if (strcmp(xhp->
rootdir,
"/") != 0) {
360 snprintf(path,
sizeof(path),
"%s%s",
366 xbps_dbg_printf(
"[obsoletes] %s "
367 "symlink_target: %s\n", item->file+1, strerror(errno));
370 if (strcmp(lnk, item->old.target) != 0) {
371 xbps_dbg_printf(
"[obsoletes] %s: skipping modified"
372 " symlink (stored `%s' current `%s'): %s\n",
373 item->old.pkgname, item->old.target, lnk, item->file+1);
386 if (item->old.pkgname && item->new.pkgname) {
387 pkgname = item->old.index > item->new.index ?
388 item->new.pkgname : item->old.pkgname;
389 }
else if (item->old.pkgname) {
390 pkgname = item->old.pkgname;
392 pkgname = item->new.pkgname;
396 xbps_dbg_printf(
"[obsoletes] %s: removes %s: %s\n",
397 pkgname, typestr(item->old.type), item->file+1);
403 item->deleted =
true;
408 if ((a = xbps_dictionary_get(obsd, pkgname)) == NULL) {
409 if (!(a = xbps_array_create()) ||
410 !(xbps_dictionary_set(obsd, pkgname, a)))
414 if (!xbps_array_add_cstring(a, item->file)) {
416 xbps_object_release(a);
420 xbps_object_release(a);
427collect_file(
struct xbps_handle *xhp,
const char *file,
size_t size,
428 const char *pkgname,
const char *pkgver,
unsigned int idx,
429 const char *sha256,
enum type type,
bool update,
bool removepkg,
430 bool preserve,
bool removefile,
const char *target)
436 if ((item = lookupItem(file)) == NULL) {
437 item = addItem(file);
440 item->deleted =
false;
445 if (item->old.type == 0) {
449 }
else if (type == TYPE_DIR && item->old.type == TYPE_DIR) {
454 if (idx < item->old.index || item->old.preserve)
456 item->old.pkgname = pkgname;
457 item->old.pkgver = pkgver;
458 item->old.index = idx;
459 item->old.preserve = preserve;
460 item->old.update = update;
461 item->old.removepkg = removepkg;
468 xbps_dbg_printf(
"[files] %s: file already removed"
469 " by package `%s': %s\n", pkgver, item->old.pkgver, file);
474 if (item->old.preserve && !preserve) {
475 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL,
476 EPERM, item->old.pkgver,
477 "%s: preserved file `%s' removed by %s.",
478 item->old.pkgver, file, pkgver);
480 }
else if (preserve && !item->old.preserve) {
481 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL,
483 "%s: preserved file `%s' removed by %s.",
484 pkgver, file, item->old.pkgver);
494 if (item->new.type == 0) {
498 }
else if (type == TYPE_DIR && item->new.type == TYPE_DIR) {
508 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL,
510 "%s: file `%s' already installed by package %s.",
511 pkgver, file, item->new.pkgver);
512 if (xhp->
flags & XBPS_FLAG_IGNORE_FILE_CONFLICTS)
523 item->old.pkgname = pkgname;
524 item->old.pkgver = pkgver;
525 item->old.type = type;
526 item->old.size = size;
527 item->old.index = idx;
528 item->old.preserve = preserve;
529 item->old.update = update;
530 item->old.removepkg = removepkg;
531 item->old.target = target;
533 item->old.sha256 = strdup(sha256);
535 item->new.pkgname = pkgname;
536 item->new.pkgver = pkgver;
537 item->new.type = type;
538 item->new.size = size;
539 item->new.index = idx;
540 item->new.preserve = preserve;
541 item->new.update = update;
542 item->new.removepkg = removepkg;
543 item->new.target = target;
545 if (item->old.type && item->new.type) {
550 if (strcmp(item->new.pkgname, item->old.pkgname) != 0) {
552 xbps_dbg_printf(
"[files] %s: %s moved to"
553 " package `%s': %s\n", pkgver, typestr(item->old.type),
554 item->new.pkgver, file);
556 xbps_dbg_printf(
"[files] %s: %s moved from"
557 " package `%s': %s\n", pkgver, typestr(item->new.type),
558 item->old.pkgver, file);
567collect_files(
struct xbps_handle *xhp, xbps_dictionary_t d,
568 const char *pkgname,
const char *pkgver,
unsigned int idx,
569 bool update,
bool removepkg,
bool preserve,
bool removefile)
572 xbps_dictionary_t filed;
576 const char *file, *sha256 = NULL;
579 if ((a = xbps_dictionary_get(d,
"files"))) {
580 for (i = 0; i < xbps_array_count(a); i++) {
581 filed = xbps_array_get(a, i);
582 xbps_dictionary_get_cstring_nocopy(filed,
"file", &file);
584 xbps_dictionary_get_cstring_nocopy(filed,
"sha256", &sha256);
586 xbps_dictionary_get_uint64(filed,
"size", &size);
587 rv = collect_file(xhp, file, size, pkgname, pkgver, idx, sha256,
588 TYPE_FILE, update, removepkg, preserve, removefile, NULL);
592 }
else if (rv != 0) {
597 if ((a = xbps_dictionary_get(d,
"conf_files"))) {
598 for (i = 0; i < xbps_array_count(a); i++) {
599 filed = xbps_array_get(a, i);
600 xbps_dictionary_get_cstring_nocopy(filed,
"file", &file);
602 xbps_dictionary_get_uint64(filed,
"size", &size);
604 xbps_dictionary_get_cstring_nocopy(filed,
"sha256", &sha256);
607 if (removefile && stat(file, &st) != -1 && size != (uint64_t)st.st_size)
610 rv = collect_file(xhp, file, size, pkgname, pkgver, idx, sha256,
611 TYPE_CONFFILE, update, removepkg, preserve, removefile, NULL);
615 }
else if (rv != 0) {
620 if ((a = xbps_dictionary_get(d,
"links"))) {
621 for (i = 0; i < xbps_array_count(a); i++) {
622 const char *target = NULL;
623 filed = xbps_array_get(a, i);
624 xbps_dictionary_get_cstring_nocopy(filed,
"file", &file);
625 xbps_dictionary_get_cstring_nocopy(filed,
"target", &target);
627 rv = collect_file(xhp, file, 0, pkgname, pkgver, idx, NULL,
628 TYPE_LINK, update, removepkg, preserve, removefile, target);
632 }
else if (rv != 0) {
637 if ((a = xbps_dictionary_get(d,
"dirs"))) {
638 for (i = 0; i < xbps_array_count(a); i++) {
639 filed = xbps_array_get(a, i);
640 xbps_dictionary_get_cstring_nocopy(filed,
"file", &file);
641 rv = collect_file(xhp, file, 0, pkgname, pkgver, idx, NULL,
642 TYPE_DIR, update, removepkg, preserve, removefile, NULL);
646 }
else if (rv != 0) {
660collect_binpkg_files(
struct xbps_handle *xhp, xbps_dictionary_t pkg_repod,
661 unsigned int idx,
bool update)
663 xbps_dictionary_t filesd;
664 struct archive *ar = NULL;
665 struct archive_entry *entry;
667 const char *pkgver, *pkgname;
670 int rv = 0, pkg_fd = -1;
672 xbps_dictionary_get_cstring_nocopy(pkg_repod,
"pkgver", &pkgver);
674 xbps_dictionary_get_cstring_nocopy(pkg_repod,
"pkgname", &pkgname);
683 if ((ar = archive_read_new()) == NULL) {
691 archive_read_support_filter_gzip(ar);
692 archive_read_support_filter_bzip2(ar);
693 archive_read_support_filter_xz(ar);
694 archive_read_support_filter_lz4(ar);
695 archive_read_support_filter_zstd(ar);
696 archive_read_support_format_tar(ar);
698 pkg_fd = open(bpkg, O_RDONLY|O_CLOEXEC);
701 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL,
703 "%s: failed to open binary package `%s': %s",
704 pkgver, bpkg, strerror(rv));
707 if (fstat(pkg_fd, &st) == -1) {
709 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL,
711 "%s: failed to fstat binary package `%s': %s",
712 pkgver, bpkg, strerror(rv));
715 if (archive_read_open_fd(ar, pkg_fd, st.st_blksize) == ARCHIVE_FATAL) {
716 rv = archive_errno(ar);
717 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL,
719 "%s: failed to read binary package `%s': %s",
720 pkgver, bpkg, strerror(rv));
724 for (uint8_t i = 0; i < 4; i++) {
725 const char *entry_pname;
726 int ar_rv = archive_read_next_header(ar, &entry);
727 if (ar_rv == ARCHIVE_EOF || ar_rv == ARCHIVE_FATAL)
729 else if (ar_rv == ARCHIVE_RETRY)
732 entry_pname = archive_entry_pathname(entry);
733 if ((strcmp(
"./files.plist", entry_pname)) == 0) {
734 filesd = xbps_archive_get_dictionary(ar, entry);
735 if (filesd == NULL) {
739 rv = collect_files(xhp, filesd, pkgname, pkgver, idx,
740 update,
false,
false,
false);
741 xbps_object_release(filesd);
744 archive_read_data_skip(ar);
751 archive_read_free(ar);
757pathcmp(
const void *l1,
const void *l2)
759 const struct item *a = *(
const struct item *
const*)l1;
760 const struct item *b = *(
const struct item *
const*)l2;
761 return (a->len < b->len) - (b->len < a->len);
767 struct item *item, *itmp;
769 HASH_ITER(hh, hashtab, item, itmp) {
770 HASH_DEL(hashtab, item);
772 free(item->old.sha256);
773 free(item->new.sha256);
800xbps_transaction_files(
struct xbps_handle *xhp, xbps_object_iterator_t iter)
802 xbps_dictionary_t pkgd, filesd;
805 const char *pkgver, *pkgname;
807 unsigned int idx = 0;
812 while ((obj = xbps_object_iterator_next(iter)) != NULL) {
820 if (ttype == XBPS_TRANS_HOLD || ttype == XBPS_TRANS_CONFIGURE) {
824 if (!xbps_dictionary_get_cstring_nocopy(obj,
"pkgver", &pkgver)) {
827 if (!xbps_dictionary_get_cstring_nocopy(obj,
"pkgname", &pkgname)) {
831 update = (ttype == XBPS_TRANS_UPDATE);
833 if (ttype == XBPS_TRANS_INSTALL || ttype == XBPS_TRANS_REINSTALL || ttype == XBPS_TRANS_UPDATE) {
834 xbps_set_cb_state(xhp, XBPS_STATE_FILES, 0, pkgver,
835 "%s: collecting files...", pkgver);
836 rv = collect_binpkg_files(xhp, obj, idx, update);
851 const char *oldpkgver;
852 bool preserve =
false;
853 bool removepkg = (ttype == XBPS_TRANS_REMOVE);
855 xbps_dictionary_get_cstring_nocopy(pkgd,
"pkgver", &oldpkgver);
856 if (!xbps_dictionary_get_bool(obj,
"preserve", &preserve))
860 if (filesd == NULL) {
865 xbps_set_cb_state(xhp, XBPS_STATE_FILES, 0, oldpkgver,
866 "%s: collecting files...", oldpkgver);
867 rv = collect_files(xhp, filesd, pkgname, pkgver, idx,
868 update, removepkg, preserve,
true);
873 xbps_object_iterator_reset(iter);
879 qsort(items, itemsidx,
sizeof (
struct item *), pathcmp);
881 if (chdir(xhp->
rootdir) == -1) {
883 xbps_set_cb_state(xhp, XBPS_STATE_FILES_FAIL, rv, xhp->
rootdir,
884 "failed to chdir to rootdir `%s': %s",
885 xhp->
rootdir, strerror(errno));
892 rv = collect_obsoletes(xhp);
char rootdir[XBPS_MAXPATH]
Generic XBPS structure handler for initialization.
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)