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; 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);
/* /*