static bool check_remove_pkg_files(struct xbps_handle *xhp, xbps_dictionary_t pkgd, const char *pkgver, uid_t euid) { struct stat st; xbps_array_t array; xbps_object_iterator_t iter; xbps_object_t obj; const char *objs[] = { "files", "conf_files", "links", "dirs" }; const char *file; char path[PATH_MAX]; bool fail = false; for (uint8_t i = 0; i < __arraycount(objs); i++) { array = xbps_dictionary_get(pkgd, objs[i]); if (array == NULL || xbps_array_count(array) == 0) continue; iter = xbps_array_iter_from_dict(pkgd, objs[i]); if (iter == NULL) continue; while ((obj = xbps_object_iterator_next(iter))) { xbps_dictionary_get_cstring_nocopy(obj, "file", &file); snprintf(path, sizeof(path), "%s/%s", xhp->rootdir, file); /* * Check if effective user ID owns the file; this is * enough to ensure the user has write permissions * on the directory. */ if (euid == 0 || (!lstat(path, &st) && euid == st.st_uid)) { /* success */ continue; } if (errno != ENOENT) { /* * only bail out if something else than ENOENT * is returned. */ int rv = errno; if (rv == 0) { /* lstat succeeds but euid != uid */ rv = EPERM; } fail = true; xbps_set_cb_state(xhp, XBPS_STATE_REMOVE_FILE_FAIL, rv, pkgver, "%s: cannot remove `%s': %s", pkgver, file, strerror(rv)); } errno = 0; } xbps_object_iterator_release(iter); } return fail; }
int HIDDEN xbps_remove_pkg_files(struct xbps_handle *xhp, xbps_dictionary_t dict, const char *key, const char *pkgver) { struct stat st; xbps_array_t array; xbps_object_iterator_t iter; xbps_object_t obj; const char *file, *sha256, *curobj = NULL; char *path = NULL, *pkgname = NULL; char buf[PATH_MAX]; int rv = 0; assert(xbps_object_type(dict) == XBPS_TYPE_DICTIONARY); assert(key != NULL); array = xbps_dictionary_get(dict, key); if (xbps_array_count(array) == 0) return 0; iter = xbps_array_iter_from_dict(dict, key); if (iter == NULL) return ENOMEM; if (strcmp(key, "files") == 0) curobj = "file"; else if (strcmp(key, "conf_files") == 0) curobj = "configuration file"; else if (strcmp(key, "links") == 0) curobj = "link"; else if (strcmp(key, "dirs") == 0) curobj = "directory"; pkgname = xbps_pkg_name(pkgver); assert(pkgname); while ((obj = xbps_object_iterator_next(iter))) { xbps_dictionary_get_cstring_nocopy(obj, "file", &file); path = xbps_xasprintf("%s/%s", xhp->rootdir, file); if ((strcmp(key, "files") == 0) || (strcmp(key, "conf_files") == 0)) { /* * Check SHA256 hash in regular files and * configuration files. */ xbps_dictionary_get_cstring_nocopy(obj, "sha256", &sha256); rv = xbps_file_hash_check(path, sha256); if (rv == ENOENT) { /* missing file, ignore it */ xbps_set_cb_state(xhp, XBPS_STATE_REMOVE_FILE_HASH_FAIL, rv, pkgver, "%s: failed to check hash for %s `%s': %s", pkgver, curobj, file, strerror(rv)); free(path); rv = 0; continue; } else if (rv == ERANGE) { rv = 0; if ((xhp->flags & XBPS_FLAG_FORCE_REMOVE_FILES) == 0) { xbps_set_cb_state(xhp, XBPS_STATE_REMOVE_FILE_HASH_FAIL, 0, pkgver, "%s: %s `%s' SHA256 mismatch, " "preserving file", pkgver, curobj, file); free(path); continue; } else { xbps_set_cb_state(xhp, XBPS_STATE_REMOVE_FILE_HASH_FAIL, 0, pkgver, "%s: %s `%s' SHA256 mismatch, " "forcing removal", pkgver, curobj, file); } } else if (rv != 0 && rv != ERANGE) { xbps_set_cb_state(xhp, XBPS_STATE_REMOVE_FILE_HASH_FAIL, rv, pkgver, "%s: [remove] failed to check hash for " "%s `%s': %s", pkgver, curobj, file, strerror(rv)); free(path); break; } } else if (strcmp(key, "links") == 0) { /* * All regular files from package were removed at this * point, so we will only remove dangling symlinks. */ if (realpath(path, buf) == NULL) { if (errno != ENOENT && errno != ELOOP) { free(path); rv = errno; break; } } if (stat(buf, &st) == 0) { free(path); continue; } } /* * Remove the object if possible. */ if (remove(path) == -1) { xbps_set_cb_state(xhp, XBPS_STATE_REMOVE_FILE_FAIL, errno, pkgver, "%s: failed to remove %s `%s': %s", pkgver, curobj, file, strerror(errno)); errno = 0; } else { /* success */ xbps_set_cb_state(xhp, XBPS_STATE_REMOVE_FILE, 0, pkgver, "Removed %s `%s'", curobj, file); } free(path); } xbps_object_iterator_release(iter); free(pkgname); return rv; }
/* * Returns 1 if entry should be installed, 0 if don't or -1 on error. */ int HIDDEN xbps_entry_install_conf_file(struct xbps_handle *xhp, xbps_dictionary_t filesd, struct archive_entry *entry, const char *entry_pname, const char *pkgver, const char *pkgname) { xbps_dictionary_t forigd; xbps_object_t obj, obj2; xbps_object_iterator_t iter, iter2; const char *cffile, *sha256_new = NULL; char *buf, *sha256_cur = NULL, *sha256_orig = NULL; int rv = 0; assert(xbps_object_type(filesd) == XBPS_TYPE_DICTIONARY); assert(entry != NULL); assert(entry_pname != NULL); assert(pkgver != NULL); iter = xbps_array_iter_from_dict(filesd, "conf_files"); if (iter == NULL) return -1; /* * Get original hash for the file from current * installed package. */ xbps_dbg_printf(xhp, "%s: processing conf_file %s\n", pkgver, entry_pname); forigd = xbps_pkgdb_get_pkg_metadata(xhp, pkgname); if (forigd == NULL) { xbps_dbg_printf(xhp, "%s: conf_file %s not currently " "installed\n", pkgver, entry_pname); rv = 1; goto out; } iter2 = xbps_array_iter_from_dict(forigd, "conf_files"); if (iter2 != NULL) { while ((obj2 = xbps_object_iterator_next(iter2))) { xbps_dictionary_get_cstring_nocopy(obj2, "file", &cffile); buf = xbps_xasprintf(".%s", cffile); if (strcmp(entry_pname, buf) == 0) { xbps_dictionary_get_cstring(obj2, "sha256", &sha256_orig); free(buf); break; } free(buf); buf = NULL; } xbps_object_iterator_release(iter2); } /* * First case: original hash not found, install new file. */ if (sha256_orig == NULL) { xbps_dbg_printf(xhp, "%s: conf_file %s not installed\n", pkgver, entry_pname); rv = 1; goto out; } /* * Compare original, installed and new hash for current file. */ while ((obj = xbps_object_iterator_next(iter))) { xbps_dictionary_get_cstring_nocopy(obj, "file", &cffile); buf = xbps_xasprintf(".%s", cffile); if (strcmp(entry_pname, buf)) { free(buf); buf = NULL; continue; } sha256_cur = xbps_file_hash(buf); free(buf); xbps_dictionary_get_cstring_nocopy(obj, "sha256", &sha256_new); if (sha256_cur == NULL) { if (errno == ENOENT) { /* * File not installed, install new one. */ xbps_dbg_printf(xhp, "%s: conf_file %s not " "installed\n", pkgver, entry_pname); rv = 1; break; } else { rv = -1; break; } } /* * Orig = X, Curr = X, New = X * * Keep file as is (no changes). */ if ((strcmp(sha256_orig, sha256_cur) == 0) && (strcmp(sha256_orig, sha256_new) == 0) && (strcmp(sha256_cur, sha256_new) == 0)) { xbps_dbg_printf(xhp, "%s: conf_file %s orig = X, " "cur = X, new = X\n", pkgver, entry_pname); rv = 0; break; /* * Orig = X, Curr = X, New = Y * * Install new file (installed file hasn't been modified). */ } else if ((strcmp(sha256_orig, sha256_cur) == 0) && (strcmp(sha256_orig, sha256_new)) && (strcmp(sha256_cur, sha256_new))) { xbps_set_cb_state(xhp, XBPS_STATE_CONFIG_FILE, 0, pkgver, "Updating configuration file `%s' provided " "by `%s'.", cffile, pkgver); rv = 1; break; /* * Orig = X, Curr = Y, New = X * * Keep installed file as is because it has been modified, * but new package doesn't contain new changes compared * to the original version. */ } else if ((strcmp(sha256_orig, sha256_new) == 0) && (strcmp(sha256_cur, sha256_new)) && (strcmp(sha256_orig, sha256_cur))) { xbps_set_cb_state(xhp, XBPS_STATE_CONFIG_FILE, 0, pkgver, "Keeping modified configuration file `%s'.", cffile); rv = 0; break; /* * Orig = X, Curr = Y, New = Y * * Keep file as is because changes made are compatible * with new version. */ } else if ((strcmp(sha256_cur, sha256_new) == 0) && (strcmp(sha256_orig, sha256_new)) && (strcmp(sha256_orig, sha256_cur))) { xbps_dbg_printf(xhp, "%s: conf_file %s orig = X, " "cur = Y, new = Y\n", pkgver, entry_pname); rv = 0; break; /* * Orig = X, Curr = Y, New = Z * * Install new file as <file>.new-<version> */ } else if ((strcmp(sha256_orig, sha256_cur)) && (strcmp(sha256_cur, sha256_new)) && (strcmp(sha256_orig, sha256_new))) { const char *version; version = xbps_pkg_version(pkgver); assert(version); buf = xbps_xasprintf(".%s.new-%s", cffile, version); xbps_set_cb_state(xhp, XBPS_STATE_CONFIG_FILE, 0, pkgver, "Installing new configuration file to " "`%s.new-%s'.", cffile, version); archive_entry_set_pathname(entry, buf); free(buf); rv = 1; break; } } out: if (sha256_orig) free(sha256_orig); if (sha256_cur) free(sha256_cur); xbps_object_iterator_release(iter); xbps_dbg_printf(xhp, "%s: conf_file %s returned %d\n", pkgver, entry_pname, rv); return rv; }
static int remove_pkg_files(struct xbps_handle *xhp, xbps_dictionary_t dict, const char *key, const char *pkgver) { xbps_array_t array; xbps_object_iterator_t iter; xbps_object_t obj; const char *curobj = NULL; /* These are symlinks in Void and must not be removed */ const char *basesymlinks[] = { "/bin", "/sbin", "/usr/sbin", "/lib", "/lib32", "/lib64", "/usr/lib32", "/usr/lib64", "/var/run", }; int rv = 0; assert(xbps_object_type(dict) == XBPS_TYPE_DICTIONARY); assert(key != NULL); array = xbps_dictionary_get(dict, key); if (xbps_array_count(array) == 0) return 0; iter = xbps_array_iter_from_dict(dict, key); if (iter == NULL) return ENOMEM; if (strcmp(key, "files") == 0) curobj = "file"; else if (strcmp(key, "conf_files") == 0) curobj = "configuration file"; else if (strcmp(key, "links") == 0) curobj = "link"; else if (strcmp(key, "dirs") == 0) curobj = "directory"; xbps_object_iterator_reset(iter); while ((obj = xbps_object_iterator_next(iter))) { const char *file, *sha256; char path[PATH_MAX]; bool found; xbps_dictionary_get_cstring_nocopy(obj, "file", &file); snprintf(path, sizeof(path), "%s/%s", xhp->rootdir, file); if ((strcmp(key, "files") == 0) || (strcmp(key, "conf_files") == 0)) { /* * Check SHA256 hash in regular files and * configuration files. */ xbps_dictionary_get_cstring_nocopy(obj, "sha256", &sha256); rv = xbps_file_hash_check(path, sha256); if (rv == ENOENT) { /* missing file, ignore it */ xbps_set_cb_state(xhp, XBPS_STATE_REMOVE_FILE_HASH_FAIL, rv, pkgver, "%s: failed to check hash for %s `%s': %s", pkgver, curobj, file, strerror(rv)); rv = 0; continue; } else if (rv == ERANGE) { rv = 0; if ((xhp->flags & XBPS_FLAG_FORCE_REMOVE_FILES) == 0) { xbps_set_cb_state(xhp, XBPS_STATE_REMOVE_FILE_HASH_FAIL, 0, pkgver, "%s: %s `%s' SHA256 mismatch, " "preserving file", pkgver, curobj, file); continue; } else { xbps_set_cb_state(xhp, XBPS_STATE_REMOVE_FILE_HASH_FAIL, 0, pkgver, "%s: %s `%s' SHA256 mismatch, " "forcing removal", pkgver, curobj, file); } } else if (rv != 0 && rv != ERANGE) { xbps_set_cb_state(xhp, XBPS_STATE_REMOVE_FILE_HASH_FAIL, rv, pkgver, "%s: [remove] failed to check hash for " "%s `%s': %s", pkgver, curobj, file, strerror(rv)); break; } } /* * Make sure to not remove any symlink of root directory. */ found = false; for (uint8_t i = 0; i < __arraycount(basesymlinks); i++) { if (strcmp(file, basesymlinks[i]) == 0) { found = true; xbps_dbg_printf(xhp, "[remove] %s ignoring " "%s removal\n", pkgver, file); break; } } if (found) { continue; } if (strcmp(key, "links") == 0) { const char *target = NULL; char *lnk; xbps_dictionary_get_cstring_nocopy(obj, "target", &target); assert(target); lnk = xbps_symlink_target(xhp, path, target); if (lnk == NULL) { xbps_dbg_printf(xhp, "[remove] %s " "symlink_target: %s\n", path, strerror(errno)); continue; } if (strcmp(lnk, target)) { xbps_dbg_printf(xhp, "[remove] %s symlink " "modified (stored %s current %s)\n", path, target, lnk); if ((xhp->flags & XBPS_FLAG_FORCE_REMOVE_FILES) == 0) { free(lnk); continue; } } free(lnk); } /* * Remove the object if possible. */ if (remove(path) == -1) { xbps_set_cb_state(xhp, XBPS_STATE_REMOVE_FILE_FAIL, errno, pkgver, "%s: failed to remove %s `%s': %s", pkgver, curobj, file, strerror(errno)); } else { /* success */ xbps_set_cb_state(xhp, XBPS_STATE_REMOVE_FILE, 0, pkgver, "Removed %s `%s'", curobj, file); } } xbps_object_iterator_release(iter); return rv; }
int exec_transaction(struct xbps_handle *xhp, int maxcols, bool yes, bool drun) { xbps_array_t array; struct transaction *trans; uint64_t fsize = 0, isize = 0; char freesize[8], instsize[8]; int rv = 0; trans = calloc(1, sizeof(*trans)); if (trans == NULL) return ENOMEM; if ((rv = xbps_transaction_prepare(xhp)) != 0) { if (rv == ENODEV) { array = xbps_dictionary_get(xhp->transd, "missing_deps"); if (xbps_array_count(array)) { /* missing dependencies */ print_array(array); fprintf(stderr, "Transaction aborted due to unresolved dependencies.\n"); } } else if (rv == ENOEXEC) { array = xbps_dictionary_get(xhp->transd, "missing_shlibs"); if (xbps_array_count(array)) { /* missing shlibs */ print_array(array); fprintf(stderr, "Transaction aborted due to unresolved shlibs.\n"); } } else if (rv == EAGAIN) { /* conflicts */ array = xbps_dictionary_get(xhp->transd, "conflicts"); print_array(array); fprintf(stderr, "Transaction aborted due to conflicting packages.\n"); } else if (rv == ENOSPC) { /* not enough free space */ xbps_dictionary_get_uint64(xhp->transd, "total-installed-size", &isize); if (xbps_humanize_number(instsize, (int64_t)isize) == -1) { xbps_error_printf("humanize_number2 returns " "%s\n", strerror(errno)); return -1; } xbps_dictionary_get_uint64(xhp->transd, "disk-free-size", &fsize); if (xbps_humanize_number(freesize, (int64_t)fsize) == -1) { xbps_error_printf("humanize_number2 returns " "%s\n", strerror(errno)); return -1; } fprintf(stderr, "Transaction aborted due to insufficient disk " "space (need %s, got %s free).\n", instsize, freesize); } else { xbps_dbg_printf(xhp, "Empty transaction dictionary: %s\n", strerror(errno)); } goto out; } #ifdef FULL_DEBUG xbps_dbg_printf(xhp, "Dictionary before transaction happens:\n"); xbps_dbg_printf_append(xhp, "%s", xbps_dictionary_externalize(xhp->transd)); #endif trans->xhp = xhp; trans->d = xhp->transd; trans->iter = xbps_array_iter_from_dict(xhp->transd, "packages"); assert(trans->iter); /* * dry-run mode, show what would be done but don't run anything. */ if (drun) { show_actions(trans->iter); goto out; } /* * Show download/installed size for the transaction. */ if ((rv = show_transaction_sizes(trans, maxcols)) != 0) goto out; /* * Ask interactively (if -y not set). */ if (!yes && !yesno("Do you want to continue?")) { printf("Aborting!\n"); goto out; } /* * It's time to run the transaction! */ if ((rv = xbps_transaction_commit(xhp)) == 0) { printf("\n%u downloaded, %u installed, %u updated, " "%u configured, %u removed.\n", trans->dl_pkgcnt, trans->inst_pkgcnt, trans->up_pkgcnt, trans->cf_pkgcnt + trans->inst_pkgcnt, trans->rm_pkgcnt); } out: if (trans->iter) xbps_object_iterator_release(trans->iter); if (trans) free(trans); return rv; }
int xbps_transaction_commit(struct xbps_handle *xhp) { prop_object_t obj; prop_object_iterator_t iter; size_t i; const char *pkgname, *version, *pkgver, *tract; int rv = 0; bool update, install, sr; assert(prop_object_type(xhp->transd) == PROP_TYPE_DICTIONARY); update = install = false; iter = xbps_array_iter_from_dict(xhp->transd, "packages"); if (iter == NULL) return EINVAL; /* * Download binary packages (if they come from a remote repository). */ xbps_set_cb_state(xhp, XBPS_STATE_TRANS_DOWNLOAD, 0, NULL, NULL, NULL); if ((rv = download_binpkgs(xhp, iter)) != 0) goto out; /* * Check SHA256 hashes for binary packages in transaction. */ xbps_set_cb_state(xhp, XBPS_STATE_TRANS_VERIFY, 0, NULL, NULL, NULL); if ((rv = check_binpkgs_hash(xhp, iter)) != 0) goto out; /* * Install, update, configure or remove packages as specified * in the transaction dictionary. */ xbps_set_cb_state(xhp, XBPS_STATE_TRANS_RUN, 0, NULL, NULL, NULL); i = 0; while ((obj = prop_object_iterator_next(iter)) != NULL) { if ((xhp->transaction_frequency_flush > 0) && (++i >= xhp->transaction_frequency_flush)) { rv = xbps_pkgdb_update(xhp, true); if (rv != 0 && rv != ENOENT) goto out; i = 0; } update = false; prop_dictionary_get_cstring_nocopy(obj, "transaction", &tract); prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname); prop_dictionary_get_cstring_nocopy(obj, "version", &version); prop_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver); if (strcmp(tract, "remove") == 0) { update = false; sr = false; /* * Remove package. */ prop_dictionary_get_bool(obj, "remove-and-update", &update); prop_dictionary_get_bool(obj, "softreplace", &sr); rv = xbps_remove_pkg(xhp, pkgname, version, update, sr); if (rv != 0) goto out; } else if (strcmp(tract, "configure") == 0) { /* * Reconfigure pending package. */ rv = xbps_configure_pkg(xhp, pkgname, false, false, false); if (rv != 0) goto out; } else { /* * Install or update a package. */ if (strcmp(tract, "update") == 0) update = true; else install = true; if (update) { /* * Update a package: execute pre-remove * action if found before unpacking. */ xbps_set_cb_state(xhp, XBPS_STATE_UPDATE, 0, pkgname, version, NULL); rv = xbps_remove_pkg(xhp, pkgname, version, true, false); if (rv != 0) { xbps_set_cb_state(xhp, XBPS_STATE_UPDATE_FAIL, rv, pkgname, version, "%s: [trans] failed to update " "package to `%s': %s", pkgver, version, strerror(rv)); goto out; } } else { /* Install a package */ xbps_set_cb_state(xhp, XBPS_STATE_INSTALL, 0, pkgname, version, NULL); } /* * Unpack binary package. */ if ((rv = xbps_unpack_binary_pkg(xhp, obj)) != 0) goto out; /* * Register package. */ if ((rv = xbps_register_pkg(xhp, obj, false)) != 0) goto out; } } prop_object_iterator_reset(iter); /* force a flush now packages were removed/unpacked */ if ((rv = xbps_pkgdb_update(xhp, true)) != 0) goto out; /* if there are no packages to install or update we are done */ if (!update && !install) goto out; /* * Configure all unpacked packages. */ xbps_set_cb_state(xhp, XBPS_STATE_TRANS_CONFIGURE, 0, NULL, NULL, NULL); i = 0; while ((obj = prop_object_iterator_next(iter)) != NULL) { if (xhp->transaction_frequency_flush > 0 && ++i >= xhp->transaction_frequency_flush) { if ((rv = xbps_pkgdb_update(xhp, true)) != 0) goto out; i = 0; } prop_dictionary_get_cstring_nocopy(obj, "transaction", &tract); if ((strcmp(tract, "remove") == 0) || (strcmp(tract, "configure") == 0)) continue; prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname); prop_dictionary_get_cstring_nocopy(obj, "version", &version); update = false; if (strcmp(tract, "update") == 0) update = true; rv = xbps_configure_pkg(xhp, pkgname, false, update, false); if (rv != 0) goto out; /* * Notify client callback when a package has been * installed or updated. */ if (update) { xbps_set_cb_state(xhp, XBPS_STATE_UPDATE_DONE, 0, pkgname, version, NULL); } else { xbps_set_cb_state(xhp, XBPS_STATE_INSTALL_DONE, 0, pkgname, version, NULL); } } /* Force a flush now that packages are configured */ rv = xbps_pkgdb_update(xhp, true); out: prop_object_iterator_release(iter); return rv; }
int exec_transaction(struct xbps_handle *xhp, int maxcols, bool yes, bool drun) { xbps_array_t mdeps, cflicts; struct transaction *trans; int rv = 0; trans = calloc(1, sizeof(*trans)); if (trans == NULL) return ENOMEM; if ((rv = xbps_transaction_prepare(xhp)) != 0) { if (rv == ENODEV) { mdeps = xbps_dictionary_get(xhp->transd, "missing_deps"); /* missing packages */ show_missing_deps(mdeps); fprintf(stderr, "Transaction aborted due to missing/conflicting packages.\n"); goto out; } else if (rv == EAGAIN) { /* conflicts */ cflicts = xbps_dictionary_get(xhp->transd, "conflicts"); show_conflicts(cflicts); fprintf(stderr, "Transaction aborted due to missing/conflicting packages.\n"); goto out; } xbps_dbg_printf(xhp, "Empty transaction dictionary: %s\n", strerror(errno)); return rv; } xbps_dbg_printf(xhp, "Dictionary before transaction happens:\n"); xbps_dbg_printf_append(xhp, "%s", xbps_dictionary_externalize(xhp->transd)); trans->d = xhp->transd; trans->iter = xbps_array_iter_from_dict(xhp->transd, "packages"); assert(trans->iter); /* * dry-run mode, show what would be done but don't run anything. */ if (drun) { show_actions(trans->iter); goto out; } /* * Show download/installed size for the transaction. */ if ((rv = show_transaction_sizes(trans, maxcols)) != 0) goto out; /* * Ask interactively (if -y not set). */ if (!yes && !yesno("Do you want to continue?")) { printf("Aborting!\n"); goto out; } /* * It's time to run the transaction! */ if ((rv = xbps_transaction_commit(xhp)) == 0) { printf("\n%u installed, %u updated, " "%u configured, %u removed.\n", trans->inst_pkgcnt, trans->up_pkgcnt, trans->cf_pkgcnt + trans->inst_pkgcnt, trans->rm_pkgcnt); } out: if (trans->iter) xbps_object_iterator_release(trans->iter); if (trans) free(trans); return rv; }
static int compute_transaction_stats(struct xbps_handle *xhp) { xbps_dictionary_t pkg_metad; xbps_object_iterator_t iter; xbps_object_t obj; struct statvfs svfs; uint64_t rootdir_free_size, tsize, dlsize, instsize, rmsize; uint32_t inst_pkgcnt, up_pkgcnt, cf_pkgcnt, rm_pkgcnt, dl_pkgcnt; const char *tract, *pkgver, *repo; inst_pkgcnt = up_pkgcnt = cf_pkgcnt = rm_pkgcnt = dl_pkgcnt = 0; rootdir_free_size = tsize = dlsize = instsize = rmsize = 0; iter = xbps_array_iter_from_dict(xhp->transd, "packages"); if (iter == NULL) return EINVAL; while ((obj = xbps_object_iterator_next(iter)) != NULL) { bool preserve = false; /* * Count number of pkgs to be removed, configured, * installed and updated. */ xbps_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver); xbps_dictionary_get_cstring_nocopy(obj, "transaction", &tract); xbps_dictionary_get_cstring_nocopy(obj, "repository", &repo); xbps_dictionary_get_bool(obj, "preserve", &preserve); if (strcmp(tract, "configure") == 0) { cf_pkgcnt++; continue; } else if (strcmp(tract, "install") == 0) { inst_pkgcnt++; } else if (strcmp(tract, "update") == 0) { up_pkgcnt++; } else if (strcmp(tract, "remove") == 0) { rm_pkgcnt++; } tsize = 0; if ((strcmp(tract, "install") == 0) || (strcmp(tract, "update") == 0)) { xbps_dictionary_get_uint64(obj, "installed_size", &tsize); instsize += tsize; if (xbps_repository_is_remote(repo) && !xbps_binpkg_exists(xhp, obj)) { xbps_dictionary_get_uint64(obj, "filename-size", &tsize); /* signature file: 512 bytes */ tsize += 512; dlsize += tsize; instsize += tsize; dl_pkgcnt++; xbps_dictionary_set_bool(obj, "download", true); } } /* * If removing or updating a package, get installed_size * from pkg's metadata dictionary. */ if ((strcmp(tract, "remove") == 0) || ((strcmp(tract, "update") == 0) && !preserve)) { char *pkgname; pkgname = xbps_pkg_name(pkgver); assert(pkgname); pkg_metad = xbps_pkgdb_get_pkg(xhp, pkgname); free(pkgname); if (pkg_metad == NULL) continue; xbps_dictionary_get_uint64(pkg_metad, "installed_size", &tsize); rmsize += tsize; } } xbps_object_iterator_release(iter); if (instsize > rmsize) { instsize -= rmsize; rmsize = 0; } else if (rmsize > instsize) { rmsize -= instsize; instsize = 0; } else { instsize = rmsize = 0; } if (!xbps_dictionary_set_uint32(xhp->transd, "total-install-pkgs", inst_pkgcnt)) return EINVAL; if (!xbps_dictionary_set_uint32(xhp->transd, "total-update-pkgs", up_pkgcnt)) return EINVAL; if (!xbps_dictionary_set_uint32(xhp->transd, "total-configure-pkgs", cf_pkgcnt)) return EINVAL; if (!xbps_dictionary_set_uint32(xhp->transd, "total-remove-pkgs", rm_pkgcnt)) return EINVAL; if (!xbps_dictionary_set_uint32(xhp->transd, "total-download-pkgs", dl_pkgcnt)) return EINVAL; if (!xbps_dictionary_set_uint64(xhp->transd, "total-installed-size", instsize)) return EINVAL; if (!xbps_dictionary_set_uint64(xhp->transd, "total-download-size", dlsize)) return EINVAL; if (!xbps_dictionary_set_uint64(xhp->transd, "total-removed-size", rmsize)) return EINVAL; /* Get free space from target rootdir: return ENOSPC if there's not enough space */ if (statvfs(xhp->rootdir, &svfs) == -1) { xbps_dbg_printf(xhp, "%s: statvfs failed: %s\n", __func__, strerror(errno)); return 0; } /* compute free space on disk */ rootdir_free_size = svfs.f_bfree * svfs.f_bsize; if (!xbps_dictionary_set_uint64(xhp->transd, "disk-free-size", rootdir_free_size)) return EINVAL; if (instsize > rootdir_free_size) return ENOSPC; return 0; }