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') {
83 xbps_dictionary_t pkg_repod,
88 xbps_dictionary_t binpkg_filesd, pkg_filesd, obsd;
89 xbps_array_t array, obsoletes;
90 const struct stat *entry_statp;
93 struct archive_entry *entry;
95 const char *entry_pname, *pkgname;
97 int ar_rv, rv, error, entry_type, flags;
98 bool preserve, file_exists, keep_conf_file;
99 bool skip_extract, force, xucd_stats;
102 binpkg_filesd = pkg_filesd = NULL;
103 force = preserve =
false;
105 ar_rv = rv = error = 0;
107 xbps_dictionary_get_bool(pkg_repod,
"preserve", &preserve);
109 memset(&xucd, 0,
sizeof(xucd));
113 if (!xbps_dictionary_get_cstring_nocopy(pkg_repod,
"pkgname", &pkgname)) {
117 if (xhp->
flags & XBPS_FLAG_FORCE_UNPACK) {
125 xbps_dictionary_get_dict(xhp->
transd,
"obsolete_files", &obsd) &&
126 (obsoletes = xbps_dictionary_get(obsd, pkgname))) {
127 for (
unsigned int i = 0; i < xbps_array_count(obsoletes); i++) {
128 const char *obsolete = NULL;
129 xbps_array_get_cstring_nocopy(obsoletes, i, &obsolete);
130 if (remove(obsolete) == -1) {
131 xbps_set_cb_state(xhp,
132 XBPS_STATE_REMOVE_FILE_OBSOLETE_FAIL,
134 "%s: failed to remove obsolete entry `%s': %s",
135 pkgver, obsolete, strerror(errno));
138 xbps_set_cb_state(xhp,
139 XBPS_STATE_REMOVE_FILE_OBSOLETE,
140 0, pkgver,
"%s: removed obsolete entry: %s", pkgver, obsolete);
147 flags = set_extract_flags(euid);
159 for (uint8_t i = 0; i < 4; i++) {
160 ar_rv = archive_read_next_header(ar, &entry);
161 if (ar_rv == ARCHIVE_EOF || ar_rv == ARCHIVE_FATAL)
164 entry_pname = archive_entry_pathname(entry);
168 if (strcmp(
"./INSTALL", entry_pname) == 0 ||
169 strcmp(
"./REMOVE", entry_pname) == 0 ||
170 strcmp(
"./props.plist", entry_pname) == 0) {
171 archive_read_data_skip(ar);
172 }
else if (strcmp(
"./files.plist", entry_pname) == 0) {
173 binpkg_filesd = xbps_archive_get_dictionary(ar, entry);
174 if (binpkg_filesd == NULL) {
186 if (ar_rv == ARCHIVE_FATAL) {
187 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL, rv, pkgver,
188 "%s: [unpack] 1: failed to extract files: %s",
189 pkgver, archive_error_string(ar));
196 if (binpkg_filesd == NULL) {
197 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL, ENODEV, pkgver,
198 "%s: [unpack] invalid binary package `%s'.", pkgver, fname);
212 ar_rv = archive_read_next_header(ar, &entry);
213 if (ar_rv == ARCHIVE_EOF || ar_rv == ARCHIVE_FATAL)
215 else if (ar_rv == ARCHIVE_RETRY)
218 entry_pname = archive_entry_pathname(entry);
222 entry_size = archive_entry_size(entry);
223 entry_type = archive_entry_filetype(entry);
224 entry_statp = archive_entry_stat(entry);
228 if (entry_type == AE_IFDIR) {
229 archive_read_data_skip(ar);
237 if (entry_pname[0] !=
'.') {
239 pkgver, entry_pname);
240 archive_read_data_skip(ar);
249 xucd.pkgver = pkgver;
250 xucd.entry = entry_pname;
251 xucd.entry_size = entry_size;
252 xucd.entry_is_conf =
false;
257 if (binpkg_filesd && !xucd_stats) {
258 array = xbps_dictionary_get(binpkg_filesd,
"files");
259 xucd.entry_total_count +=
260 (ssize_t)xbps_array_count(array);
261 array = xbps_dictionary_get(binpkg_filesd,
"conf_files");
262 xucd.entry_total_count +=
263 (ssize_t)xbps_array_count(array);
264 array = xbps_dictionary_get(binpkg_filesd,
"links");
265 xucd.entry_total_count +=
266 (ssize_t)xbps_array_count(array);
274 xbps_dbg_printf(
"[unpack] %s skipped (matched by a pattern)\n", entry_pname+1);
275 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FILE_PRESERVED, 0,
276 pkgver,
"%s: file `%s' won't be extracted, "
277 "it matches a noextract pattern.", pkgver, entry_pname);
278 archive_read_data_skip(ar);
286 skip_extract = file_exists = keep_conf_file =
false;
287 if (lstat(entry_pname, &st) == 0)
293 if (file_exists && match_preserved_file(xhp, entry_pname)) {
294 archive_read_data_skip(ar);
296 "and must be preserved, skipping.\n", entry_pname);
297 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FILE_PRESERVED, 0,
298 pkgver,
"%s: file `%s' won't be extracted, "
299 "it's preserved.", pkgver, entry_pname);
307 if (!force && (entry_type == AE_IFREG)) {
308 file = entry_pname + 1;
309 keep_conf_file = xbps_entry_is_a_conf_file(binpkg_filesd, file);
317 if (file_exists && !keep_conf_file &&
318 ((entry_statp->st_mode & S_IFMT) != (st.st_mode & S_IFMT)))
319 (void)remove(entry_pname);
321 if (!force && (entry_type == AE_IFREG)) {
322 if (file_exists && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) {
329 if (keep_conf_file) {
331 xucd.entry_is_conf =
true;
333 rv = xbps_entry_install_conf_file(xhp,
334 binpkg_filesd, pkg_filesd, entry,
335 entry_pname, pkgver, S_ISLNK(st.st_mode));
339 }
else if (rv == 0) {
347 rv = xbps_file_hash_check_dictionary(
348 xhp, binpkg_filesd,
"files", file);
352 "%s: failed to check"
353 " hash for `%s': %s\n",
357 }
else if (rv == 0) {
363 "matches existing SHA256, "
365 pkgver, entry_pname);
376 if ((!force && file_exists && skip_extract && (euid == 0)) &&
377 (((archive_entry_uid(entry) != st.st_uid)) ||
378 ((archive_entry_gid(entry) != st.st_gid)))) {
379 if (lchown(entry_pname,
380 archive_entry_uid(entry),
381 archive_entry_gid(entry)) != 0) {
384 "to set uid/gid to %"PRIu64
":%"PRIu64
" (%s)\n",
385 pkgver, archive_entry_uid(entry),
386 archive_entry_gid(entry),
390 "uid/gid to %"PRIu64
":%"PRIu64
".\n", pkgver, entry_pname,
391 archive_entry_uid(entry),
392 archive_entry_gid(entry));
399 if (!force && file_exists && skip_extract &&
400 (archive_entry_mode(entry) != st.st_mode)) {
401 if (chmod(entry_pname,
402 archive_entry_mode(entry)) != 0) {
405 "to set perms %s to %s: %s\n",
406 pkgver, archive_entry_strmode(entry),
413 "mode to %s.\n", pkgver, entry_pname,
414 archive_entry_strmode(entry));
416 if (!force && skip_extract) {
417 archive_read_data_skip(ar);
424 entry_pname = archive_entry_pathname(entry);
430 if (archive_read_extract(ar, entry, flags) != 0) {
431 error = xbps_archive_errno(ar);
432 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
434 "%s: [unpack] failed to extract file `%s': %s",
435 pkgver, entry_pname, strerror(error));
439 xucd.entry = entry_pname;
440 xucd.entry_extract_count++;
448 if (error || ar_rv == ARCHIVE_FATAL) {
452 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL, rv, pkgver,
453 "%s: [unpack] failed to extract files: %s",
454 pkgver, strerror(rv));
460 if (xbps_dictionary_count(binpkg_filesd)) {
463 prev_umask = umask(022);
465 if (!xbps_dictionary_externalize_to_file(binpkg_filesd, buf)) {
469 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
470 rv, pkgver,
"%s: [unpack] failed to externalize pkg "
471 "pkg metadata files: %s", pkgver, strerror(rv));
481 if (!xbps_dictionary_count(binpkg_filesd)) {
486 xbps_object_release(binpkg_filesd);
492xbps_unpack_binary_pkg(
struct xbps_handle *xhp, xbps_dictionary_t pkg_repod)
495 struct archive *ar = NULL;
499 int pkg_fd = -1, rv = 0;
502 assert(xbps_object_type(pkg_repod) == XBPS_TYPE_DICTIONARY);
504 xbps_dictionary_get_cstring_nocopy(pkg_repod,
"pkgver", &pkgver);
505 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK, 0, pkgver, NULL);
509 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
511 "%s: [unpack] cannot determine binary package "
512 "file: %s", pkgver, strerror(errno));
516 if ((ar = archive_read_new()) == NULL)
521 archive_read_support_filter_gzip(ar);
522 archive_read_support_filter_bzip2(ar);
523 archive_read_support_filter_xz(ar);
524 archive_read_support_filter_lz4(ar);
525 archive_read_support_filter_zstd(ar);
526 archive_read_support_format_tar(ar);
528 myumask = umask(022);
530 pkg_fd = open(bpkg, O_RDONLY|O_CLOEXEC);
533 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
535 "%s: [unpack] failed to open binary package `%s': %s",
536 pkgver, bpkg, strerror(rv));
539 if (fstat(pkg_fd, &st) == -1) {
541 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
543 "%s: [unpack] failed to fstat binary package `%s': %s",
544 pkgver, bpkg, strerror(rv));
547 if (archive_read_open_fd(ar, pkg_fd, st.st_blksize) == ARCHIVE_FATAL) {
548 rv = xbps_archive_errno(ar);
549 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
551 "%s: [unpack] failed to read binary package `%s': %s",
552 pkgver, bpkg, strerror(rv));
558 if (access(xhp->
metadir, R_OK|X_OK) == -1) {
571 if ((rv = unpack_archive(xhp, pkg_repod, pkgver, bpkg, ar)) != 0) {
572 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL, rv, pkgver,
573 "%s: [unpack] failed to unpack files from archive: %s",
574 pkgver, strerror(rv));
581 XBPS_PKG_STATE_UNPACKED)) != 0) {
582 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
584 "%s: [unpack] failed to set state to unpacked: %s",
585 pkgver, strerror(rv));
589 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
591 "%s: [unpack] failed to register alternatives: %s",
592 pkgver, strerror(rv));
599 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.
#define xbps_unreachable()
Log and abort for code that should be unreachable.
void xbps_error_printf(const char *fmt,...)
Prints error messages to stderr.
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)