From 3d43aed10c94bfcfc4a85b9b66bc60c18d22c2b1 Mon Sep 17 00:00:00 2001 From: "Kim B. Heino" Date: Mon, 29 Mar 2010 14:18:27 +0200 Subject: [PATCH] dpkg: support config file overwrite options function old new delta filter_rename_config - 244 +244 static.dpkg_longopts - 112 +112 dpkg_main 3901 3980 +79 unpack_package 515 587 +72 append_control_file_to_llist - 72 +72 data_extract_all_prefix 88 116 +28 ------------------------------------------------------------------------------ (add/remove: 3/0 grow/shrink: 3/0 up/down: 607/0) Total: 607 bytes Signed-off-by: Kim B. Heino Signed-off-by: Denys Vlasenko --- archival/dpkg.c | 126 ++++++++++++++++++++++++++++++++++++++++++------ include/usage.h | 17 ++++++- 2 files changed, 127 insertions(+), 16 deletions(-) diff --git a/archival/dpkg.c b/archival/dpkg.c index 7187ad6a0..befb91d6a 100644 --- a/archival/dpkg.c +++ b/archival/dpkg.c @@ -1308,7 +1308,7 @@ static void list_packages(const char *pattern) name_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->name]; vers_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->version]; - if (pattern && fnmatch(pattern, name_str, 0)) + if (pattern && fnmatch(pattern, name_str, 0) != 0) continue; /* get abbreviation for status field 1 */ @@ -1491,6 +1491,58 @@ static char *deb_extract_control_file_to_buffer(archive_handle_t *ar_handle, lli return ar_handle->dpkg__sub_archive->dpkg__buffer; } +static void append_control_file_to_llist(const char *package_name, const char *control_name, llist_t **ll) +{ + FILE *fp; + char *filename, *line; + + filename = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, control_name); + fp = fopen_for_read(filename); + free(filename); + if (fp != NULL) { + while ((line = xmalloc_fgetline(fp)) != NULL) + llist_add_to(ll, line); + fclose(fp); + } +} + +static char FAST_FUNC filter_rename_config(archive_handle_t *archive_handle) +{ + int fd; + char *name_ptr = archive_handle->file_header->name + 1; + + /* Is this file marked as config file? */ + if (!find_list_entry(archive_handle->accept, name_ptr)) + return EXIT_SUCCESS; /* no */ + + fd = open(name_ptr, O_RDONLY); + if (fd >= 0) { + md5_ctx_t md5; + char *md5line, *buf; + int count; + + /* Calculate MD5 of existing file */ + buf = xmalloc(4096); + md5_begin(&md5); + while ((count = safe_read(fd, buf, 4096)) > 0) + md5_hash(buf, count, &md5); + md5_end(buf, &md5); /* using buf as result storage */ + close(fd); + + md5line = xmalloc(16 * 2 + 2 + strlen(name_ptr) + 1); + sprintf(bin2hex(md5line, buf, 16), " %s", name_ptr); + free(buf); + + /* Is it changed after install? */ + if (find_list_entry(archive_handle->accept, md5line) == NULL) { + printf("Warning: Creating %s as %s.dpkg-new\n", name_ptr, name_ptr); + archive_handle->file_header->name = xasprintf("%s.dpkg-new", archive_handle->file_header->name); + } + free(md5line); + } + return EXIT_SUCCESS; +} + static void FAST_FUNC data_extract_all_prefix(archive_handle_t *archive_handle) { char *name_ptr = archive_handle->file_header->name; @@ -1500,7 +1552,7 @@ static void FAST_FUNC data_extract_all_prefix(archive_handle_t *archive_handle) name_ptr++; /* Skip all leading "./" and "../" */ while (name_ptr[0] == '.') { - if (name_ptr[1] == '.' && name_ptr[2] == '/') + if (name_ptr[1] == '.') name_ptr++; if (name_ptr[1] != '/') break; @@ -1510,9 +1562,26 @@ static void FAST_FUNC data_extract_all_prefix(archive_handle_t *archive_handle) if (name_ptr[0] != '\0') { archive_handle->file_header->name = xasprintf("%s%s", archive_handle->dpkg__buffer, name_ptr); data_extract_all(archive_handle); + if (fnmatch("*.dpkg-new", archive_handle->file_header->name, 0) == 0) { + /* remove .dpkg-new suffix */ + archive_handle->file_header->name[strlen(archive_handle->file_header->name) - 9] = '\0'; + } } } +enum { + OPT_configure = (1 << 0), + OPT_force = (1 << 1), + OPT_install = (1 << 2), + OPT_list_installed = (1 << 3), + OPT_purge = (1 << 4), + OPT_remove = (1 << 5), + OPT_unpack = (1 << 6), + OPT_force_ignore_depends = (1 << 7), + OPT_force_confnew = (1 << 8), + OPT_force_confold = (1 << 9), +}; + static void unpack_package(deb_file_t *deb_file) { const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name]; @@ -1523,14 +1592,21 @@ static void unpack_package(deb_file_t *deb_file) archive_handle_t *archive_handle; FILE *out_stream; llist_t *accept_list; + llist_t *conffile_list; int i; /* If existing version, remove it first */ + conffile_list = NULL; if (strcmp(name_hashtable[get_status(status_num, 3)], "installed") == 0) { /* Package is already installed, remove old version first */ printf("Preparing to replace %s %s (using %s)...\n", package_name, name_hashtable[package_hashtable[status_package_num]->version], deb_file->filename); + + /* Read md5sums from old package */ + if (!(option_mask32 & OPT_force_confold)) + append_control_file_to_llist(package_name, "md5sums", &conffile_list); + remove_package(status_package_num, 0); } else { printf("Unpacking %s (from %s)...\n", package_name, deb_file->filename); @@ -1558,9 +1634,15 @@ static void unpack_package(deb_file_t *deb_file) /* Run the preinst prior to extracting */ run_package_script_or_die(package_name, "preinst"); + /* Don't overwrite existing config files */ + if (!(option_mask32 & OPT_force_confnew)) + append_control_file_to_llist(package_name, "conffiles", &conffile_list); + /* Extract data.tar.gz to the root directory */ archive_handle = init_archive_deb_ar(deb_file->filename); init_archive_deb_data(archive_handle); + archive_handle->dpkg__sub_archive->accept = conffile_list; + archive_handle->dpkg__sub_archive->filter = filter_rename_config; archive_handle->dpkg__sub_archive->action_data = data_extract_all_prefix; archive_handle->dpkg__sub_archive->dpkg__buffer = (char*)"/"; /* huh? */ archive_handle->dpkg__sub_archive->ah_flags |= ARCHIVE_UNLINK_OLD; @@ -1614,23 +1696,37 @@ int dpkg_main(int argc UNUSED_PARAM, char **argv) int state_status; int status_num; int i; - enum { - OPT_configure = 0x1, - OPT_force_ignore_depends = 0x2, - OPT_install = 0x4, - OPT_list_installed = 0x8, - OPT_purge = 0x10, - OPT_remove = 0x20, - OPT_unpack = 0x40, - }; +#if ENABLE_LONG_OPTS + static const char dpkg_longopts[] ALIGN1 = +// FIXME: we use -C non-compatibly, should be: +// "-C|--audit Check for broken package(s)" + "configure\0" No_argument "C" + "force\0" Required_argument "F" + "install\0" No_argument "i" + "list\0" No_argument "l" + "purge\0" No_argument "P" + "remove\0" No_argument "r" + "unpack\0" No_argument "u" + "force-depends\0" No_argument "\xff" + "force-confnew\0" No_argument "\xfe" + "force-confold\0" No_argument "\xfd" + ; +#endif INIT_G(); + IF_LONG_OPTS(applet_long_options = dpkg_longopts); opt = getopt32(argv, "CF:ilPru", &str_f); //if (opt & OPT_configure) ... // -C - if (opt & OPT_force_ignore_depends) { // -F (--force in official dpkg) - if (strcmp(str_f, "depends")) - opt &= ~OPT_force_ignore_depends; + if (opt & OPT_force) { // -F (--force in official dpkg) + if (strcmp(str_f, "depends") == 0) + opt |= OPT_force_ignore_depends; + else if (strcmp(str_f, "confnew") == 0) + opt |= OPT_force_confnew; + else if (strcmp(str_f, "confold") == 0) + opt |= OPT_force_confold; + else bb_show_usage(); + option_mask32 = opt; } //if (opt & OPT_install) ... // -i //if (opt & OPT_list_installed) ... // -l @@ -1639,7 +1735,7 @@ int dpkg_main(int argc UNUSED_PARAM, char **argv) //if (opt & OPT_unpack) ... // -u (--unpack in official dpkg) argv += optind; /* check for non-option argument if expected */ - if (!opt || (!argv[0] && !(opt && OPT_list_installed))) + if (!opt || (!argv[0] && !(opt & OPT_list_installed))) bb_show_usage(); /* puts("(Reading database ... xxxxx files and directories installed.)"); */ diff --git a/include/usage.h b/include/usage.h index 8b026b43a..cf576c666 100644 --- a/include/usage.h +++ b/include/usage.h @@ -931,13 +931,28 @@ #define dpkg_full_usage "\n\n" \ "Install, remove and manage Debian packages\n" \ "\nOptions:" \ + IF_LONG_OPTS( \ + "\n -i,--install Install the package" \ + "\n -l,--list List of installed packages" \ + "\n --configure Configure an unpackaged package" \ + "\n -P,--purge Purge all files of a package" \ + "\n -r,--remove Remove all but the configuration files for a package" \ + "\n --unpack Unpack a package, but don't configure it" \ + "\n --force-depends Ignore dependency problems" \ + "\n --force-confnew Overwrite existing config files when installing" \ + "\n --force-confold Keep old config files when installing" \ + ) \ + IF_NOT_LONG_OPTS( \ "\n -i Install the package" \ "\n -l List of installed packages" \ "\n -C Configure an unpackaged package" \ - "\n -F depends Ignore dependency problems" \ "\n -P Purge all files of a package" \ "\n -r Remove all but the configuration files for a package" \ "\n -u Unpack a package, but don't configure it" \ + "\n -F depends Ignore dependency problems" \ + "\n -F confnew Overwrite existing config files when installing" \ + "\n -F confold Keep old config files when installing" \ + ) #define dpkg_deb_trivial_usage \ "[-cefxX] FILE [argument]"