diff --git a/NEWS b/NEWS index bcdd2d5e..fbebd9c9 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,9 @@ xbps-0.10.0 (???): + * libxbps: when updating packages, only files that have been modified + (compared to current installed file) are now unpacked. This should + save some writes to storage and make the process a bit smoother as well. + * Moved install transaction code to libxbps. The API has been extended with xbps_transaction_commit() which expects the transaction dictionary as its argument. This function will execute transaction tasks, like diff --git a/include/xbps_api.h b/include/xbps_api.h index 3b513a69..846f1d8c 100644 --- a/include/xbps_api.h +++ b/include/xbps_api.h @@ -55,7 +55,7 @@ */ #define XBPS_PKGINDEX_VERSION "1.2" -#define XBPS_API_VERSION "20110729" +#define XBPS_API_VERSION "20110731" #define XBPS_VERSION "0.10.0" /** @@ -1430,9 +1430,9 @@ char *xbps_file_hash(const char *file); * @return The sha256 hash string if found, NULL otherwise * and errno is set appropiately. */ -const char *xbps_file_hash_from_dictionary(prop_dictionary_t d, - const char *key, - const char *file); +const char *xbps_file_hash_dictionary(prop_dictionary_t d, + const char *key, + const char *file); /** * Compares the sha256 hash of the file \a file with the sha256 @@ -1446,6 +1446,20 @@ const char *xbps_file_hash_from_dictionary(prop_dictionary_t d, */ int xbps_file_hash_check(const char *file, const char *sha256); +/** + * Checks if \a file matches the sha256 hash specified in the array + * with key \a key in the proplib dictionary \a d. + * + * @param[in] d Proplib dictionary to look in. + * @param[in] key Proplib array key to match for file. + * @param[in] file Pathname to a file. + * + * @return 0 if hash is matched, -1 on error and 1 if no match. + */ +int xbps_file_hash_check_dictionary(prop_dictionary_t d, + const char *key, + const char *file); + /** * Checks if a package is currently installed by matching a package * pattern string. diff --git a/lib/package_unpack.c b/lib/package_unpack.c index 16bd3a3f..c67021c4 100644 --- a/lib/package_unpack.c +++ b/lib/package_unpack.c @@ -157,6 +157,7 @@ unpack_archive(prop_dictionary_t pkg_repod, { prop_dictionary_t propsd = NULL, filesd = NULL, old_filesd = NULL; prop_array_t array; + const struct stat *entry_statp; struct archive_entry *entry; size_t nmetadata = 0, entry_idx = 0; const char *entry_pname, *transact; @@ -200,6 +201,7 @@ unpack_archive(prop_dictionary_t pkg_repod, * Process the archive files. */ while (archive_read_next_header(ar, &entry) == ARCHIVE_OK) { + entry_statp = archive_entry_stat(entry); entry_pname = archive_entry_pathname(entry); flags = set_extract_flags(); @@ -324,33 +326,56 @@ unpack_archive(prop_dictionary_t pkg_repod, xhp->xucd->entry_total_count += (ssize_t)prop_array_count(array); - /* - * 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. - */ - rv = xbps_entry_is_a_conf_file(propsd, entry_pname); - if (rv == -1) { - /* error */ - goto out; - } else if (rv == 1) { - if (xhp->xucd != NULL) - xhp->xucd->entry_is_conf = true; - - rv = xbps_entry_install_conf_file(filesd, - entry, entry_pname, pkgname, version); + if (update && S_ISREG(entry_statp->st_mode)) { + /* + * 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. + */ + rv = xbps_entry_is_a_conf_file(propsd, entry_pname); if (rv == -1) { /* error */ goto out; - } else if (rv == 0) { + } else if (rv == 1) { + if (xhp->xucd != NULL) + xhp->xucd->entry_is_conf = true; + + rv = xbps_entry_install_conf_file(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. + */ + archive_read_data_skip(ar); + RUN_PROGRESS_CB(); + continue; + } + } else { /* - * Keep current configuration file - * as is now and pass to next entry. + * Current entry is not a configuration file, + * check if installed file matches sha256 hash. + * If true, there is no need to extract it. */ - archive_read_data_skip(ar); - RUN_PROGRESS_CB(); - continue; + rv = xbps_file_hash_check_dictionary(filesd, + "files", entry_pname); + if (rv == -1) { + xbps_dbg_printf("%s-%s: failed to check" + " hash for `%s': %s\n", pkgname, + version, entry_pname, + strerror(errno)); + /* error */ + goto out; + } else if (rv == 0) { + /* hash match, skip */ + archive_read_data_skip(ar); + RUN_PROGRESS_CB(); + continue; + } } } /* diff --git a/lib/util_hash.c b/lib/util_hash.c index 596693f4..f60bcff1 100644 --- a/lib/util_hash.c +++ b/lib/util_hash.c @@ -137,9 +137,9 @@ xbps_file_hash_check(const char *file, const char *sha256) } const char * -xbps_file_hash_from_dictionary(prop_dictionary_t d, - const char *key, - const char *file) +xbps_file_hash_dictionary(prop_dictionary_t d, + const char *key, + const char *file) { prop_object_t obj; prop_object_iterator_t iter; @@ -152,8 +152,10 @@ xbps_file_hash_from_dictionary(prop_dictionary_t d, curfile = sha256 = NULL; iter = xbps_array_iter_from_dict(d, key); - if (iter == NULL) + if (iter == NULL) { + errno = ENOENT; return NULL; + } while ((obj = prop_object_iterator_next(iter)) != NULL) { prop_dictionary_get_cstring_nocopy(obj, "file", &curfile); @@ -170,3 +172,32 @@ xbps_file_hash_from_dictionary(prop_dictionary_t d, return sha256; } + +int +xbps_file_hash_check_dictionary(prop_dictionary_t d, + const char *key, + const char *file) +{ + const char *sha256d; + int rv; + + assert(d != NULL); + assert(key != NULL); + assert(file != NULL); + + sha256d = xbps_file_hash_dictionary(d, key, file); + if (sha256d == NULL) { + if (errno == ENOENT) + return 1; /* no match, file not found */ + + return -1; /* error */ + } + + rv = xbps_file_hash_check(file, sha256d); + if (rv == 0) + return 0; /* matched */ + else if (rv == ERANGE || rv == ENOENT) + return 1; /* no match */ + else + return -1; /* error */ +}