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;
92 const struct stat *entry_statp;
95 struct archive_entry *entry;
97 const char *entry_pname, *pkgname;
99 int ar_rv, rv, error, entry_type, flags;
100 bool preserve, update, file_exists, keep_conf_file;
101 bool skip_extract, force, xucd_stats;
104 binpkg_filesd = pkg_filesd = NULL;
105 force = preserve = update = file_exists =
false;
107 ar_rv = rv = error = entry_type = flags = 0;
109 xbps_dictionary_get_bool(pkg_repod,
"preserve", &preserve);
112 memset(&xucd, 0,
sizeof(xucd));
116 if (!xbps_dictionary_get_cstring_nocopy(pkg_repod,
"pkgname", &pkgname)) {
120 if (xhp->
flags & XBPS_FLAG_FORCE_UNPACK) {
124 if (ttype == XBPS_TRANS_UPDATE) {
132 xbps_dictionary_get_dict(xhp->
transd,
"obsolete_files", &obsd) &&
133 (obsoletes = xbps_dictionary_get(obsd, pkgname))) {
134 for (
unsigned int i = 0; i < xbps_array_count(obsoletes); i++) {
135 const char *file = NULL;
136 xbps_array_get_cstring_nocopy(obsoletes, i, &file);
137 if (remove(file) == -1) {
138 xbps_set_cb_state(xhp,
139 XBPS_STATE_REMOVE_FILE_OBSOLETE_FAIL,
141 "%s: failed to remove obsolete entry `%s': %s",
142 pkgver, file, strerror(errno));
145 xbps_set_cb_state(xhp,
146 XBPS_STATE_REMOVE_FILE_OBSOLETE,
147 0, pkgver,
"%s: removed obsolete entry: %s", pkgver, file);
154 flags = set_extract_flags(euid);
166 for (uint8_t i = 0; i < 4; i++) {
167 ar_rv = archive_read_next_header(ar, &entry);
168 if (ar_rv == ARCHIVE_EOF || ar_rv == ARCHIVE_FATAL)
171 entry_pname = archive_entry_pathname(entry);
172 entry_size = archive_entry_size(entry);
174 if (strcmp(
"./INSTALL", entry_pname) == 0 ||
175 strcmp(
"./REMOVE", entry_pname) == 0 ||
176 strcmp(
"./props.plist", entry_pname) == 0) {
177 archive_read_data_skip(ar);
178 }
else if (strcmp(
"./files.plist", entry_pname) == 0) {
179 binpkg_filesd = xbps_archive_get_dictionary(ar, entry);
180 if (binpkg_filesd == NULL) {
192 if (ar_rv == ARCHIVE_FATAL) {
193 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL, rv, pkgver,
194 "%s: [unpack] 1: failed to extract files: %s",
195 pkgver, archive_error_string(ar));
202 if (binpkg_filesd == NULL) {
203 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL, ENODEV, pkgver,
204 "%s: [unpack] invalid binary package `%s'.", pkgver, fname);
218 ar_rv = archive_read_next_header(ar, &entry);
219 if (ar_rv == ARCHIVE_EOF || ar_rv == ARCHIVE_FATAL)
221 else if (ar_rv == ARCHIVE_RETRY)
224 entry_pname = archive_entry_pathname(entry);
225 entry_size = archive_entry_size(entry);
226 entry_type = archive_entry_filetype(entry);
227 entry_statp = archive_entry_stat(entry);
231 if (entry_type == AE_IFDIR) {
232 archive_read_data_skip(ar);
240 xucd.pkgver = pkgver;
241 xucd.entry = entry_pname;
242 xucd.entry_size = entry_size;
243 xucd.entry_is_conf =
false;
248 if (binpkg_filesd && !xucd_stats) {
249 array = xbps_dictionary_get(binpkg_filesd,
"files");
250 xucd.entry_total_count +=
251 (ssize_t)xbps_array_count(array);
252 array = xbps_dictionary_get(binpkg_filesd,
"conf_files");
253 xucd.entry_total_count +=
254 (ssize_t)xbps_array_count(array);
255 array = xbps_dictionary_get(binpkg_filesd,
"links");
256 xucd.entry_total_count +=
257 (ssize_t)xbps_array_count(array);
265 xbps_dbg_printf(
"[unpack] %s skipped (matched by a pattern)\n", entry_pname+1);
266 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FILE_PRESERVED, 0,
267 pkgver,
"%s: file `%s' won't be extracted, "
268 "it matches a noextract pattern.", pkgver, entry_pname);
269 archive_read_data_skip(ar);
277 skip_extract = file_exists = keep_conf_file =
false;
278 if (lstat(entry_pname, &st) == 0)
284 if (file_exists && match_preserved_file(xhp, entry_pname)) {
285 archive_read_data_skip(ar);
286 xbps_dbg_printf(
"[unpack] `%s' exists on disk "
287 "and must be preserved, skipping.\n", entry_pname);
288 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FILE_PRESERVED, 0,
289 pkgver,
"%s: file `%s' won't be extracted, "
290 "it's preserved.", pkgver, entry_pname);
298 if (!force && (entry_type == AE_IFREG)) {
299 buf = strchr(entry_pname,
'.') + 1;
301 keep_conf_file = xbps_entry_is_a_conf_file(binpkg_filesd, buf);
309 if (file_exists && !keep_conf_file &&
310 ((entry_statp->st_mode & S_IFMT) != (st.st_mode & S_IFMT)))
311 (void)remove(entry_pname);
313 if (!force && (entry_type == AE_IFREG)) {
314 if (file_exists && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) {
321 if (keep_conf_file) {
323 xucd.entry_is_conf =
true;
325 rv = xbps_entry_install_conf_file(xhp,
326 binpkg_filesd, pkg_filesd, entry,
327 entry_pname, pkgver, S_ISLNK(st.st_mode));
331 }
else if (rv == 0) {
339 rv = xbps_file_hash_check_dictionary(
340 xhp, binpkg_filesd,
"files", buf);
344 "%s: failed to check"
345 " hash for `%s': %s\n",
349 }
else if (rv == 0) {
355 "matches existing SHA256, "
357 pkgver, entry_pname);
368 if ((!force && file_exists && skip_extract && (euid == 0)) &&
369 (((archive_entry_uid(entry) != st.st_uid)) ||
370 ((archive_entry_gid(entry) != st.st_gid)))) {
371 if (lchown(entry_pname,
372 archive_entry_uid(entry),
373 archive_entry_gid(entry)) != 0) {
376 "to set uid/gid to %"PRIu64
":%"PRIu64
" (%s)\n",
377 pkgver, archive_entry_uid(entry),
378 archive_entry_gid(entry),
381 xbps_dbg_printf(
"%s: entry %s changed "
382 "uid/gid to %"PRIu64
":%"PRIu64
".\n", pkgver, entry_pname,
383 archive_entry_uid(entry),
384 archive_entry_gid(entry));
391 if (!force && file_exists && skip_extract &&
392 (archive_entry_mode(entry) != st.st_mode)) {
393 if (chmod(entry_pname,
394 archive_entry_mode(entry)) != 0) {
397 "to set perms %s to %s: %s\n",
398 pkgver, archive_entry_strmode(entry),
404 xbps_dbg_printf(
"%s: entry %s changed file "
405 "mode to %s.\n", pkgver, entry_pname,
406 archive_entry_strmode(entry));
408 if (!force && skip_extract) {
409 archive_read_data_skip(ar);
416 entry_pname = archive_entry_pathname(entry);
420 if (archive_read_extract(ar, entry, flags) != 0) {
421 error = archive_errno(ar);
422 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
424 "%s: [unpack] failed to extract file `%s': %s",
425 pkgver, entry_pname, strerror(error));
429 xucd.entry = entry_pname;
430 xucd.entry_extract_count++;
438 if (error || ar_rv == ARCHIVE_FATAL) {
442 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL, rv, pkgver,
443 "%s: [unpack] failed to extract files: %s",
444 pkgver, strerror(rv));
450 if (xbps_dictionary_count(binpkg_filesd)) {
452 prev_umask = umask(022);
454 if (!xbps_dictionary_externalize_to_file(binpkg_filesd, buf)) {
458 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
459 rv, pkgver,
"%s: [unpack] failed to externalize pkg "
460 "pkg metadata files: %s", pkgver, strerror(rv));
470 if (!xbps_dictionary_count(binpkg_filesd)) {
475 xbps_object_release(binpkg_filesd);
481xbps_unpack_binary_pkg(
struct xbps_handle *xhp, xbps_dictionary_t pkg_repod)
484 struct archive *ar = NULL;
488 int pkg_fd = -1, rv = 0;
491 assert(xbps_object_type(pkg_repod) == XBPS_TYPE_DICTIONARY);
493 xbps_dictionary_get_cstring_nocopy(pkg_repod,
"pkgver", &pkgver);
494 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK, 0, pkgver, NULL);
498 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
500 "%s: [unpack] cannot determine binary package "
501 "file: %s", pkgver, strerror(errno));
505 if ((ar = archive_read_new()) == NULL)
510 archive_read_support_filter_gzip(ar);
511 archive_read_support_filter_bzip2(ar);
512 archive_read_support_filter_xz(ar);
513 archive_read_support_filter_lz4(ar);
514 archive_read_support_filter_zstd(ar);
515 archive_read_support_format_tar(ar);
517 myumask = umask(022);
519 pkg_fd = open(bpkg, O_RDONLY|O_CLOEXEC);
522 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
524 "%s: [unpack] failed to open binary package `%s': %s",
525 pkgver, bpkg, strerror(rv));
528 if (fstat(pkg_fd, &st) == -1) {
530 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
532 "%s: [unpack] failed to fstat binary package `%s': %s",
533 pkgver, bpkg, strerror(rv));
536 if (archive_read_open_fd(ar, pkg_fd, st.st_blksize) == ARCHIVE_FATAL) {
537 rv = archive_errno(ar);
538 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
540 "%s: [unpack] failed to read binary package `%s': %s",
541 pkgver, bpkg, strerror(rv));
547 if (access(xhp->
metadir, R_OK|X_OK) == -1) {
560 if ((rv = unpack_archive(xhp, pkg_repod, pkgver, bpkg, ar)) != 0) {
561 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL, rv, pkgver,
562 "%s: [unpack] failed to unpack files from archive: %s",
563 pkgver, strerror(rv));
570 XBPS_PKG_STATE_UNPACKED)) != 0) {
571 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
573 "%s: [unpack] failed to set state to unpacked: %s",
574 pkgver, strerror(rv));
578 xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
580 "%s: [unpack] failed to register alternatives: %s",
581 pkgver, strerror(rv));
588 archive_read_free(ar);
int xbps_alternatives_register(struct xbps_handle *xhp, xbps_dictionary_t pkgd)
char metadir[XBPS_MAXPATH+sizeof(XBPS_META_PATH)]
void(* unpack_cb)(const struct xbps_unpack_cb_data *, void *)
Structure to be passed to the unpack function callback.
Generic XBPS structure handler for initialization.
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)
xbps_trans_type_t xbps_transaction_pkg_type(xbps_dictionary_t pkg_repod)
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)