dpkg: support config file overwrite options
No binary (build failed)
—
(log yok)
| @@ -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.)"); */ | |
| @@ -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]" | |