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:
Juan RP 2012-10-26 10:27:24 +02:00
parent e545429fa1
commit 4023c8115b

View File

@ -154,6 +154,33 @@ remove_metafile(struct xbps_handle *xhp,
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
unpack_archive(struct xbps_handle *xhp,
prop_dictionary_t pkg_repod,
@ -168,11 +195,11 @@ unpack_archive(struct xbps_handle *xhp,
struct archive_entry *entry;
size_t i, entry_idx = 0;
const char *file, *entry_pname, *transact, *pkgname;
const char *version, *pkgver, *fname;
char *buf = NULL, *pkgfilesd = NULL, *pkgpropsd = NULL;
int ar_rv, rv, flags;
const char *version, *pkgver, *fname, *tgtlnk;
char *buf, *buf2, *p, *p2, *pkgfilesd = NULL, *pkgpropsd = NULL;
int ar_rv, rv, rv_stat, flags;
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(ar != NULL);
@ -233,6 +260,7 @@ unpack_archive(struct xbps_handle *xhp,
else if (ar_rv == ARCHIVE_RETRY)
continue;
skip_extract = false;
entry_statp = archive_entry_stat(entry);
entry_pname = archive_entry_pathname(entry);
flags = set_extract_flags();
@ -359,29 +387,96 @@ unpack_archive(struct xbps_handle *xhp,
* Otherwise skip extracting it.
*/
conf_file = file_exists = false;
rv_stat = stat(entry_pname, &st);
if (S_ISREG(entry_statp->st_mode)) {
buf = strchr(entry_pname, '.') + 1;
assert(buf != NULL);
if (xbps_entry_is_a_conf_file(propsd, buf))
conf_file = true;
if (stat(entry_pname, &st) == 0) {
if (rv_stat == 0) {
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;
rv = xbps_entry_install_conf_file(xhp,
filesd, entry, entry_pname,
pkgname, version);
if (rv == -1) {
/* error */
goto out;
} else if (rv == 0) {
/*
* Keep curfile as is.
*/
skip_extract = true;
}
} else {
rv = xbps_file_hash_check_dictionary(
xhp, filesd, "files", buf);
if (rv == -1) {
/* error */
xbps_dbg_printf(xhp,
"%s-%s: failed to check"
" hash for `%s': %s\n", pkgname,
version, entry_pname,
" hash for `%s': %s\n",
pkgname, version,
entry_pname,
strerror(errno));
goto out;
} else if (rv == 0) {
/*
* Always set entry perms in existing
* file, even when hash is matched.
* hash match, skip extraction.
*/
xbps_dbg_printf(xhp,
"%s-%s: file %s "
"matches existing SHA256, "
"skipping...\n", pkgname,
version, entry_pname);
skip_extract = true;
}
}
}
} 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);
}
}
/*
* 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,
@ -394,25 +489,13 @@ unpack_archive(struct xbps_handle *xhp,
rv = EINVAL;
goto out;
}
xbps_dbg_printf(xhp,
"%s-%s: entry %s perms "
"to %s.\n", pkgname, version,
entry_pname,
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;
skip_extract = true;
}
}
}
if (!update && conf_file && file_exists) {
if (!update && conf_file && file_exists && !skip_extract) {
/*
* If installing new package preserve old configuration
* file but renaming it to <file>.old.
@ -427,30 +510,12 @@ unpack_archive(struct xbps_handle *xhp,
pkgname, version,
"Renamed old configuration file "
"`%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,
entry, entry_pname, pkgname, version);
if (rv == -1) {
/* error */
goto out;
} else if (rv == 0) {
/*
* Keep current configuration file
* as is now and pass to next entry.
*/
if (skip_extract) {
archive_read_data_skip(ar);
continue;
}
}
/*
* Reset entry_pname again because if entry's pathname
* has been changed it will become a dangling pointer.
@ -465,12 +530,13 @@ unpack_archive(struct xbps_handle *xhp,
rv, pkgname, version,
"%s: [unpack] failed to extract file `%s': %s",
pkgver, entry_pname, strerror(rv));
}
} else {
if (xhp->unpack_cb != NULL) {
xucd.entry_extract_count++;
(*xhp->unpack_cb)(xhp, &xucd, xhp->unpack_cb_data);
}
}
}
/*
* If there was any error extracting files from archive, error out.
*/
@ -524,22 +590,24 @@ unpack_archive(struct xbps_handle *xhp,
}
out1:
/*
* Create pkg metadata directory.
* Create pkg metadata directory if doesn't exist.
*/
buf = xbps_xasprintf("%s/metadata/%s", XBPS_META_PATH, pkgname);
if (buf == NULL) {
rv = ENOMEM;
goto out;
}
if (access(buf, R_OK|X_OK) == -1) {
if (xbps_mkpath(buf, 0755) == -1) {
xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
errno, pkgname, version,
"%s: [unpack] failed to create pkg metadir `%s': %s",
buf, pkgver, strerror(errno));
"%s: [unpack] failed to create pkg metadir "
"`%s': %s", buf, pkgver, strerror(errno));
free(buf);
rv = errno;
goto out;
}
}
free(buf);
/*
* Externalize XBPS_PKGFILES and XBPS_PKGPROPS into pkg's