39#include <archive_entry.h>
41#include "xbps_api_impl.h"
43#define EXTRACT_FLAGS ARCHIVE_EXTRACT_SECURE_NODOTDOT | \
44 ARCHIVE_EXTRACT_SECURE_SYMLINKS | \
45 ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS | \
46 ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | \
47 ARCHIVE_EXTRACT_UNLINK
48#define FEXTRACT_FLAGS ARCHIVE_EXTRACT_OWNER | EXTRACT_FLAGS
52set_extract_flags(uid_t euid)
57 flags = FEXTRACT_FLAGS;
59 flags = EXTRACT_FLAGS;
65match_preserved_file(
struct xbps_handle *xhp,
const char *entry)
69 if (xhp->preserved_files == NULL)
72 if (entry[0] ==
'.' && entry[1] !=
'\0') {
73 file = strchr(entry,
'.') + 1;
84 xbps_dictionary_t pkg_repod,
89 xbps_dictionary_t binpkg_filesd, pkg_filesd, obsd;
90 xbps_array_t array, obsoletes;
91 const struct stat *entry_statp;
94 struct archive_entry *entry;
96 const char *entry_pname, *pkgname;
98 int ar_rv, rv, error, entry_type, flags;
99 bool preserve, file_exists, keep_conf_file;
100 bool skip_extract, force, xucd_stats;
103 binpkg_filesd = pkg_filesd = NULL;
104 force = preserve =
false;
106 ar_rv = rv = error = 0;
108 xbps_dictionary_get_bool(pkg_repod,
"preserve", &preserve);
110 memset(&xucd, 0,
sizeof(xucd));
114 if (!xbps_dictionary_get_cstring_nocopy(pkg_repod,
"pkgname", &pkgname)) {
118 if (xhp->
flags & XBPS_FLAG_FORCE_UNPACK) {
126 xbps_dictionary_get_dict(xhp->
transd,
"obsolete_files", &obsd) &&
127 (obsoletes = xbps_dictionary_get(obsd, pkgname))) {
128 for (
unsigned int i = 0; i < xbps_array_count(obsoletes); i++) {
129 const char *obsolete = NULL;
130 xbps_array_get_cstring_nocopy(obsoletes, i, &obsolete);
131 if (remove(obsolete) == -1) {
132 xbps_set_cb_state(xhp,
133 XBPS_STATE_REMOVE_FILE_OBSOLETE_FAIL,
135 "%s: failed to remove obsolete entry `%s': %s",
136 pkgver, obsolete, strerror(errno));
139 xbps_set_cb_state(xhp,
140 XBPS_STATE_REMOVE_FILE_OBSOLETE,
141 0, pkgver,
"%s: removed obsolete entry: %s", pkgver, obsolete);
148 flags = set_extract_flags(euid);
160 for (uint8_t i = 0; i < 4; i++) {
161 ar_rv = archive_read_next_header(ar, &entry);
162 if (ar_rv == ARCHIVE_EOF || ar_rv == ARCHIVE_FATAL)
165 entry_pname = archive_entry_pathname(entry);
167 if (strcmp(
"./INSTALL", entry_pname) == 0 ||
168 strcmp(
"./REMOVE", entry_pname) == 0 ||
169 strcmp(
"./props.plist", entry_pname) == 0) {
170 archive_read_data_skip(ar);
171 }
else if (strcmp(
"./files.plist", entry_pname) == 0) {
172 binpkg_filesd = xbps_archive_get_dictionary(ar, entry);
173 if (binpkg_filesd == NULL) {
185 if (ar_rv == ARCHIVE_FATAL) {
186 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL, rv, pkgver,
187 "%s: [unpack] 1: failed to extract files: %s",
188 pkgver, archive_error_string(ar));
195 if (binpkg_filesd == NULL) {
196 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL, ENODEV, pkgver,
197 "%s: [unpack] invalid binary package `%s'.", pkgver, fname);
211 ar_rv = archive_read_next_header(ar, &entry);
212 if (ar_rv == ARCHIVE_EOF || ar_rv == ARCHIVE_FATAL)
214 else if (ar_rv == ARCHIVE_RETRY)
217 entry_pname = archive_entry_pathname(entry);
218 entry_size = archive_entry_size(entry);
219 entry_type = archive_entry_filetype(entry);
220 entry_statp = archive_entry_stat(entry);
224 if (entry_type == AE_IFDIR) {
225 archive_read_data_skip(ar);
233 xucd.pkgver = pkgver;
234 xucd.entry = entry_pname;
235 xucd.entry_size = entry_size;
236 xucd.entry_is_conf =
false;
241 if (binpkg_filesd && !xucd_stats) {
242 array = xbps_dictionary_get(binpkg_filesd,
"files");
243 xucd.entry_total_count +=
244 (ssize_t)xbps_array_count(array);
245 array = xbps_dictionary_get(binpkg_filesd,
"conf_files");
246 xucd.entry_total_count +=
247 (ssize_t)xbps_array_count(array);
248 array = xbps_dictionary_get(binpkg_filesd,
"links");
249 xucd.entry_total_count +=
250 (ssize_t)xbps_array_count(array);
258 xbps_dbg_printf(
"[unpack] %s skipped (matched by a pattern)\n", entry_pname+1);
259 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FILE_PRESERVED, 0,
260 pkgver,
"%s: file `%s' won't be extracted, "
261 "it matches a noextract pattern.", pkgver, entry_pname);
262 archive_read_data_skip(ar);
270 skip_extract = file_exists = keep_conf_file =
false;
271 if (lstat(entry_pname, &st) == 0)
277 if (file_exists && match_preserved_file(xhp, entry_pname)) {
278 archive_read_data_skip(ar);
280 "and must be preserved, skipping.\n", entry_pname);
281 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FILE_PRESERVED, 0,
282 pkgver,
"%s: file `%s' won't be extracted, "
283 "it's preserved.", pkgver, entry_pname);
291 if (!force && (entry_type == AE_IFREG)) {
292 file = strchr(entry_pname,
'.') + 1;
293 assert(file != NULL);
294 keep_conf_file = xbps_entry_is_a_conf_file(binpkg_filesd, file);
302 if (file_exists && !keep_conf_file &&
303 ((entry_statp->st_mode & S_IFMT) != (st.st_mode & S_IFMT)))
304 (void)remove(entry_pname);
306 if (!force && (entry_type == AE_IFREG)) {
307 if (file_exists && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) {
314 if (keep_conf_file) {
316 xucd.entry_is_conf =
true;
318 rv = xbps_entry_install_conf_file(xhp,
319 binpkg_filesd, pkg_filesd, entry,
320 entry_pname, pkgver, S_ISLNK(st.st_mode));
324 }
else if (rv == 0) {
332 rv = xbps_file_hash_check_dictionary(
333 xhp, binpkg_filesd,
"files", file);
337 "%s: failed to check"
338 " hash for `%s': %s\n",
342 }
else if (rv == 0) {
348 "matches existing SHA256, "
350 pkgver, entry_pname);
361 if ((!force && file_exists && skip_extract && (euid == 0)) &&
362 (((archive_entry_uid(entry) != st.st_uid)) ||
363 ((archive_entry_gid(entry) != st.st_gid)))) {
364 if (lchown(entry_pname,
365 archive_entry_uid(entry),
366 archive_entry_gid(entry)) != 0) {
369 "to set uid/gid to %"PRIu64
":%"PRIu64
" (%s)\n",
370 pkgver, archive_entry_uid(entry),
371 archive_entry_gid(entry),
375 "uid/gid to %"PRIu64
":%"PRIu64
".\n", pkgver, entry_pname,
376 archive_entry_uid(entry),
377 archive_entry_gid(entry));
384 if (!force && file_exists && skip_extract &&
385 (archive_entry_mode(entry) != st.st_mode)) {
386 if (chmod(entry_pname,
387 archive_entry_mode(entry)) != 0) {
390 "to set perms %s to %s: %s\n",
391 pkgver, archive_entry_strmode(entry),
398 "mode to %s.\n", pkgver, entry_pname,
399 archive_entry_strmode(entry));
401 if (!force && skip_extract) {
402 archive_read_data_skip(ar);
409 entry_pname = archive_entry_pathname(entry);
413 if (archive_read_extract(ar, entry, flags) != 0) {
414 error = xbps_archive_errno(ar);
415 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
417 "%s: [unpack] failed to extract file `%s': %s",
418 pkgver, entry_pname, strerror(error));
422 xucd.entry = entry_pname;
423 xucd.entry_extract_count++;
431 if (error || ar_rv == ARCHIVE_FATAL) {
435 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL, rv, pkgver,
436 "%s: [unpack] failed to extract files: %s",
437 pkgver, strerror(rv));
443 if (xbps_dictionary_count(binpkg_filesd)) {
446 prev_umask = umask(022);
448 if (!xbps_dictionary_externalize_to_file(binpkg_filesd, buf)) {
452 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
453 rv, pkgver,
"%s: [unpack] failed to externalize pkg "
454 "pkg metadata files: %s", pkgver, strerror(rv));
464 if (!xbps_dictionary_count(binpkg_filesd)) {
469 xbps_object_release(binpkg_filesd);
475xbps_unpack_binary_pkg(
struct xbps_handle *xhp, xbps_dictionary_t pkg_repod)
478 struct archive *ar = NULL;
482 int pkg_fd = -1, rv = 0;
485 assert(xbps_object_type(pkg_repod) == XBPS_TYPE_DICTIONARY);
487 xbps_dictionary_get_cstring_nocopy(pkg_repod,
"pkgver", &pkgver);
488 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK, 0, pkgver, NULL);
492 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
494 "%s: [unpack] cannot determine binary package "
495 "file: %s", pkgver, strerror(errno));
499 if ((ar = archive_read_new()) == NULL)
504 archive_read_support_filter_gzip(ar);
505 archive_read_support_filter_bzip2(ar);
506 archive_read_support_filter_xz(ar);
507 archive_read_support_filter_lz4(ar);
508 archive_read_support_filter_zstd(ar);
509 archive_read_support_format_tar(ar);
511 myumask = umask(022);
513 pkg_fd = open(bpkg, O_RDONLY|O_CLOEXEC);
516 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
518 "%s: [unpack] failed to open binary package `%s': %s",
519 pkgver, bpkg, strerror(rv));
522 if (fstat(pkg_fd, &st) == -1) {
524 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
526 "%s: [unpack] failed to fstat binary package `%s': %s",
527 pkgver, bpkg, strerror(rv));
530 if (archive_read_open_fd(ar, pkg_fd, st.st_blksize) == ARCHIVE_FATAL) {
531 rv = xbps_archive_errno(ar);
532 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
534 "%s: [unpack] failed to read binary package `%s': %s",
535 pkgver, bpkg, strerror(rv));
541 if (access(xhp->
metadir, R_OK|X_OK) == -1) {
554 if ((rv = unpack_archive(xhp, pkg_repod, pkgver, bpkg, ar)) != 0) {
555 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL, rv, pkgver,
556 "%s: [unpack] failed to unpack files from archive: %s",
557 pkgver, strerror(rv));
564 XBPS_PKG_STATE_UNPACKED)) != 0) {
565 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
567 "%s: [unpack] failed to set state to unpacked: %s",
568 pkgver, strerror(rv));
572 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
574 "%s: [unpack] failed to register alternatives: %s",
575 pkgver, strerror(rv));
582 archive_read_free(ar);
int xbps_alternatives_register(struct xbps_handle *xhp, xbps_dictionary_t pkgd)
void(* unpack_cb)(const struct xbps_unpack_cb_data *, void *)
char metadir[XBPS_MAXPATH]
Structure to be passed to the unpack function callback.
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)
int xbps_set_pkg_state_dictionary(xbps_dictionary_t dict, pkg_state_t state)
bool xbps_match_string_in_array(xbps_array_t array, const char *val)
char * xbps_xasprintf(const char *fmt,...) __attribute__((format(printf
bool xbps_patterns_match(xbps_array_t patterns, const char *path)
int xbps_mkpath(const char *path, mode_t mode)
ssize_t xbps_pkg_path(struct xbps_handle *xhp, char *dst, size_t dstsz, xbps_dictionary_t pkgd)