unpack: improvements to handle more cases of modified files.
- Do not unpack unmodified symlinks. - Do not unpack files that only differ in mode, just chmod(2) new perms. - Do not create pkg's metadata dir if this already exists.
This commit is contained in:
parent
e545429fa1
commit
4023c8115b
@ -154,6 +154,33 @@ remove_metafile(struct xbps_handle *xhp,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
find_pkg_symlink_target(prop_dictionary_t d, const char *file)
|
||||||
|
{
|
||||||
|
prop_array_t links;
|
||||||
|
prop_object_t obj;
|
||||||
|
size_t i;
|
||||||
|
const char *pkgfile, *tgt = NULL;
|
||||||
|
char *rfile;
|
||||||
|
|
||||||
|
assert(d);
|
||||||
|
|
||||||
|
links = prop_dictionary_get(d, "links");
|
||||||
|
assert(links);
|
||||||
|
|
||||||
|
for (i = 0; i < prop_array_count(links); i++) {
|
||||||
|
rfile = strchr(file, '.') + 1;
|
||||||
|
obj = prop_array_get(links, i);
|
||||||
|
prop_dictionary_get_cstring_nocopy(obj, "file", &pkgfile);
|
||||||
|
if (strcmp(rfile, pkgfile) == 0) {
|
||||||
|
prop_dictionary_get_cstring_nocopy(obj, "target", &tgt);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tgt;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
unpack_archive(struct xbps_handle *xhp,
|
unpack_archive(struct xbps_handle *xhp,
|
||||||
prop_dictionary_t pkg_repod,
|
prop_dictionary_t pkg_repod,
|
||||||
@ -168,11 +195,11 @@ unpack_archive(struct xbps_handle *xhp,
|
|||||||
struct archive_entry *entry;
|
struct archive_entry *entry;
|
||||||
size_t i, entry_idx = 0;
|
size_t i, entry_idx = 0;
|
||||||
const char *file, *entry_pname, *transact, *pkgname;
|
const char *file, *entry_pname, *transact, *pkgname;
|
||||||
const char *version, *pkgver, *fname;
|
const char *version, *pkgver, *fname, *tgtlnk;
|
||||||
char *buf = NULL, *pkgfilesd = NULL, *pkgpropsd = NULL;
|
char *buf, *buf2, *p, *p2, *pkgfilesd = NULL, *pkgpropsd = NULL;
|
||||||
int ar_rv, rv, flags;
|
int ar_rv, rv, rv_stat, flags;
|
||||||
bool preserve, update, conf_file, file_exists, skip_obsoletes;
|
bool preserve, update, conf_file, file_exists, skip_obsoletes;
|
||||||
bool softreplace;
|
bool softreplace, skip_extract;
|
||||||
|
|
||||||
assert(prop_object_type(pkg_repod) == PROP_TYPE_DICTIONARY);
|
assert(prop_object_type(pkg_repod) == PROP_TYPE_DICTIONARY);
|
||||||
assert(ar != NULL);
|
assert(ar != NULL);
|
||||||
@ -233,6 +260,7 @@ unpack_archive(struct xbps_handle *xhp,
|
|||||||
else if (ar_rv == ARCHIVE_RETRY)
|
else if (ar_rv == ARCHIVE_RETRY)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
skip_extract = false;
|
||||||
entry_statp = archive_entry_stat(entry);
|
entry_statp = archive_entry_stat(entry);
|
||||||
entry_pname = archive_entry_pathname(entry);
|
entry_pname = archive_entry_pathname(entry);
|
||||||
flags = set_extract_flags();
|
flags = set_extract_flags();
|
||||||
@ -359,60 +387,115 @@ unpack_archive(struct xbps_handle *xhp,
|
|||||||
* Otherwise skip extracting it.
|
* Otherwise skip extracting it.
|
||||||
*/
|
*/
|
||||||
conf_file = file_exists = false;
|
conf_file = file_exists = false;
|
||||||
|
rv_stat = stat(entry_pname, &st);
|
||||||
|
|
||||||
if (S_ISREG(entry_statp->st_mode)) {
|
if (S_ISREG(entry_statp->st_mode)) {
|
||||||
buf = strchr(entry_pname, '.') + 1;
|
buf = strchr(entry_pname, '.') + 1;
|
||||||
assert(buf != NULL);
|
assert(buf != NULL);
|
||||||
if (xbps_entry_is_a_conf_file(propsd, buf))
|
if (rv_stat == 0) {
|
||||||
conf_file = true;
|
|
||||||
if (stat(entry_pname, &st) == 0) {
|
|
||||||
file_exists = true;
|
file_exists = true;
|
||||||
rv = xbps_file_hash_check_dictionary(xhp, filesd,
|
/*
|
||||||
conf_file ? "conf_files" : "files", buf);
|
* Handle configuration files. Check if current
|
||||||
|
* entry is a configuration file and take action
|
||||||
|
* if required. Skip packages that don't have
|
||||||
|
* "conf_files" array on its XBPS_PKGPROPS
|
||||||
|
* dictionary.
|
||||||
|
*/
|
||||||
|
if (xbps_entry_is_a_conf_file(propsd, buf)) {
|
||||||
|
conf_file = true;
|
||||||
|
if (xhp->unpack_cb != NULL)
|
||||||
|
xucd.entry_is_conf = true;
|
||||||
|
|
||||||
if (rv == -1) {
|
rv = xbps_entry_install_conf_file(xhp,
|
||||||
/* error */
|
filesd, entry, entry_pname,
|
||||||
xbps_dbg_printf(xhp,
|
pkgname, version);
|
||||||
"%s-%s: failed to check"
|
if (rv == -1) {
|
||||||
" hash for `%s': %s\n", pkgname,
|
/* error */
|
||||||
version, entry_pname,
|
goto out;
|
||||||
strerror(errno));
|
} else if (rv == 0) {
|
||||||
goto out;
|
/*
|
||||||
} else if (rv == 0) {
|
* Keep curfile as is.
|
||||||
/*
|
*/
|
||||||
* Always set entry perms in existing
|
skip_extract = true;
|
||||||
* file, even when hash is matched.
|
}
|
||||||
*/
|
} else {
|
||||||
if (chmod(entry_pname,
|
rv = xbps_file_hash_check_dictionary(
|
||||||
entry_statp->st_mode) != 0) {
|
xhp, filesd, "files", buf);
|
||||||
|
if (rv == -1) {
|
||||||
|
/* error */
|
||||||
xbps_dbg_printf(xhp,
|
xbps_dbg_printf(xhp,
|
||||||
"%s-%s: failed "
|
"%s-%s: failed to check"
|
||||||
"to set perms %s to %s: %s\n",
|
" hash for `%s': %s\n",
|
||||||
pkgname, version,
|
pkgname, version,
|
||||||
archive_entry_strmode(entry),
|
|
||||||
entry_pname,
|
entry_pname,
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
rv = EINVAL;
|
|
||||||
goto out;
|
goto out;
|
||||||
|
} else if (rv == 0) {
|
||||||
|
/*
|
||||||
|
* hash match, skip extraction.
|
||||||
|
*/
|
||||||
|
xbps_dbg_printf(xhp,
|
||||||
|
"%s-%s: file %s "
|
||||||
|
"matches existing SHA256, "
|
||||||
|
"skipping...\n", pkgname,
|
||||||
|
version, entry_pname);
|
||||||
|
skip_extract = true;
|
||||||
}
|
}
|
||||||
xbps_dbg_printf(xhp,
|
|
||||||
"%s-%s: entry %s perms "
|
|
||||||
"to %s.\n", pkgname, version,
|
|
||||||
entry_pname,
|
|
||||||
archive_entry_strmode(entry));
|
|
||||||
/*
|
|
||||||
* hash match, skip extraction.
|
|
||||||
*/
|
|
||||||
xbps_dbg_printf(xhp,
|
|
||||||
"%s-%s: entry %s "
|
|
||||||
"matches current SHA256, "
|
|
||||||
"skipping...\n", pkgname,
|
|
||||||
version, entry_pname);
|
|
||||||
archive_read_data_skip(ar);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (S_ISLNK(entry_statp->st_mode)) {
|
||||||
|
/*
|
||||||
|
* Check if current link from binpkg hasn't been
|
||||||
|
* modified, otherwise extract new link.
|
||||||
|
*/
|
||||||
|
if (stat(entry_pname, &st) == 0) {
|
||||||
|
buf = realpath(entry_pname, NULL);
|
||||||
|
assert(buf);
|
||||||
|
p = strlen(xhp->rootdir) + buf;
|
||||||
|
assert(p);
|
||||||
|
tgtlnk = find_pkg_symlink_target(filesd,
|
||||||
|
entry_pname);
|
||||||
|
assert(tgtlnk);
|
||||||
|
p2 = xbps_xasprintf(".%s", tgtlnk);
|
||||||
|
assert(p2);
|
||||||
|
buf2 = realpath(p2, NULL);
|
||||||
|
assert(buf2);
|
||||||
|
free(p2);
|
||||||
|
p2 = strlen(xhp->rootdir) + buf2;
|
||||||
|
if (strcmp(p, p2) == 0) {
|
||||||
|
xbps_dbg_printf(xhp, "%s-%s: symlink "
|
||||||
|
"%s matched, skipping...\n",
|
||||||
|
pkgname, version, entry_pname);
|
||||||
|
skip_extract = true;
|
||||||
|
}
|
||||||
|
free(buf);
|
||||||
|
free(buf2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!update && conf_file && file_exists) {
|
/*
|
||||||
|
* Check if current file mode differs from file mode
|
||||||
|
* in binpkg and apply perms if true.
|
||||||
|
*/
|
||||||
|
if (file_exists && (entry_statp->st_mode != st.st_mode)) {
|
||||||
|
if (chmod(entry_pname,
|
||||||
|
entry_statp->st_mode) != 0) {
|
||||||
|
xbps_dbg_printf(xhp,
|
||||||
|
"%s-%s: failed "
|
||||||
|
"to set perms %s to %s: %s\n",
|
||||||
|
pkgname, version,
|
||||||
|
archive_entry_strmode(entry),
|
||||||
|
entry_pname,
|
||||||
|
strerror(errno));
|
||||||
|
rv = EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
xbps_dbg_printf(xhp, "%s-%s: entry %s perms "
|
||||||
|
"to %s.\n", pkgname, version, entry_pname,
|
||||||
|
archive_entry_strmode(entry));
|
||||||
|
skip_extract = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!update && conf_file && file_exists && !skip_extract) {
|
||||||
/*
|
/*
|
||||||
* If installing new package preserve old configuration
|
* If installing new package preserve old configuration
|
||||||
* file but renaming it to <file>.old.
|
* file but renaming it to <file>.old.
|
||||||
@ -427,29 +510,11 @@ unpack_archive(struct xbps_handle *xhp,
|
|||||||
pkgname, version,
|
pkgname, version,
|
||||||
"Renamed old configuration file "
|
"Renamed old configuration file "
|
||||||
"`%s' to `%s.old'.", entry_pname, entry_pname);
|
"`%s' to `%s.old'.", entry_pname, entry_pname);
|
||||||
} else if (update && conf_file && file_exists) {
|
}
|
||||||
/*
|
|
||||||
* Handle configuration files. Check if current entry is
|
|
||||||
* a configuration file and take action if required. Skip
|
|
||||||
* packages that don't have the "conf_files" array in
|
|
||||||
* the XBPS_PKGPROPS dictionary.
|
|
||||||
*/
|
|
||||||
if (xhp->unpack_cb != NULL)
|
|
||||||
xucd.entry_is_conf = true;
|
|
||||||
|
|
||||||
rv = xbps_entry_install_conf_file(xhp, filesd,
|
if (skip_extract) {
|
||||||
entry, entry_pname, pkgname, version);
|
archive_read_data_skip(ar);
|
||||||
if (rv == -1) {
|
continue;
|
||||||
/* error */
|
|
||||||
goto out;
|
|
||||||
} else if (rv == 0) {
|
|
||||||
/*
|
|
||||||
* Keep current configuration file
|
|
||||||
* as is now and pass to next entry.
|
|
||||||
*/
|
|
||||||
archive_read_data_skip(ar);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Reset entry_pname again because if entry's pathname
|
* Reset entry_pname again because if entry's pathname
|
||||||
@ -465,10 +530,11 @@ unpack_archive(struct xbps_handle *xhp,
|
|||||||
rv, pkgname, version,
|
rv, pkgname, version,
|
||||||
"%s: [unpack] failed to extract file `%s': %s",
|
"%s: [unpack] failed to extract file `%s': %s",
|
||||||
pkgver, entry_pname, strerror(rv));
|
pkgver, entry_pname, strerror(rv));
|
||||||
}
|
} else {
|
||||||
if (xhp->unpack_cb != NULL) {
|
if (xhp->unpack_cb != NULL) {
|
||||||
xucd.entry_extract_count++;
|
xucd.entry_extract_count++;
|
||||||
(*xhp->unpack_cb)(xhp, &xucd, xhp->unpack_cb_data);
|
(*xhp->unpack_cb)(xhp, &xucd, xhp->unpack_cb_data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
@ -524,21 +590,23 @@ unpack_archive(struct xbps_handle *xhp,
|
|||||||
}
|
}
|
||||||
out1:
|
out1:
|
||||||
/*
|
/*
|
||||||
* Create pkg metadata directory.
|
* Create pkg metadata directory if doesn't exist.
|
||||||
*/
|
*/
|
||||||
buf = xbps_xasprintf("%s/metadata/%s", XBPS_META_PATH, pkgname);
|
buf = xbps_xasprintf("%s/metadata/%s", XBPS_META_PATH, pkgname);
|
||||||
if (buf == NULL) {
|
if (buf == NULL) {
|
||||||
rv = ENOMEM;
|
rv = ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (xbps_mkpath(buf, 0755) == -1) {
|
if (access(buf, R_OK|X_OK) == -1) {
|
||||||
xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
|
if (xbps_mkpath(buf, 0755) == -1) {
|
||||||
errno, pkgname, version,
|
xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
|
||||||
"%s: [unpack] failed to create pkg metadir `%s': %s",
|
errno, pkgname, version,
|
||||||
buf, pkgver, strerror(errno));
|
"%s: [unpack] failed to create pkg metadir "
|
||||||
free(buf);
|
"`%s': %s", buf, pkgver, strerror(errno));
|
||||||
rv = errno;
|
free(buf);
|
||||||
goto out;
|
rv = errno;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
free(buf);
|
free(buf);
|
||||||
/*
|
/*
|
||||||
|
Loading…
Reference in New Issue
Block a user