X-Git-Url: https://wimlib.net/git/?a=blobdiff_plain;f=programs%2Fimagex.c;h=9b8af6e047d3caf16baaa544e9faefd4d55421f0;hb=5cdd60306facd14cc9dcc24471386451294ca73c;hp=688b1e172dbdef2266f66d68dfe68bed785d1d03;hpb=f4a194333c5856d466d096c306f709edd19d1cc8;p=wimlib diff --git a/programs/imagex.c b/programs/imagex.c index 688b1e17..9b8af6e0 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -31,13 +31,13 @@ #include #include #include +#include #include +#include +#include #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0])) -#define swap(a, b) ({ typeof(a) __a = (a); typeof(b) __b = (b); \ - a = __b; b = __a; }) - #define for_opt(c, opts) while ((c = getopt_long_only(argc, (char**)argv, "", \ opts, NULL)) != -1) @@ -53,6 +53,7 @@ enum imagex_op_type { JOIN, MOUNT, MOUNTRW, + OPTIMIZE, SPLIT, UNMOUNT, }; @@ -62,44 +63,47 @@ static void usage_all(); static const char *usage_strings[] = { [APPEND] = -" imagex append (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n" -" [DESCRIPTION] [--boot] [--check] [--flags EDITION_ID]\n" -" [--verbose] [--dereference] [--config=FILE]\n", +"imagex append (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n" +" [DESCRIPTION] [--boot] [--check] [--flags EDITION_ID]\n" +" [--verbose] [--dereference] [--config=FILE]\n" +" [--threads=NUM_THREADS] [--rebuild]\n", [APPLY] = -" imagex apply WIMFILE [IMAGE_NUM | IMAGE_NAME | all]\n" -" (DIRECTORY | NTFS_VOLUME) [--check] [--hardlink]\n" -" [--symlink] [--verbose] [--ref=\"GLOB\"]\n", +"imagex apply WIMFILE [IMAGE_NUM | IMAGE_NAME | all]\n" +" (DIRECTORY | NTFS_VOLUME) [--check] [--hardlink]\n" +" [--symlink] [--verbose] [--ref=\"GLOB\"]\n", [CAPTURE] = -" imagex capture (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n" -" [DESCRIPTION] [--boot] [--check] [--compress=TYPE]\n" -" [--flags EDITION_ID] [--verbose] [--dereference]\n" -" [--config=FILE]\n", +"imagex capture (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n" +" [DESCRIPTION] [--boot] [--check] [--compress=TYPE]\n" +" [--flags EDITION_ID] [--verbose] [--dereference]\n" +" [--config=FILE] [--threads=NUM_THREADS]\n", [DELETE] = -" imagex delete WIMFILE (IMAGE_NUM | IMAGE_NAME | all) [--check]\n", +"imagex delete WIMFILE (IMAGE_NUM | IMAGE_NAME | all) [--check] [--soft]\n", [DIR] = -" imagex dir WIMFILE (IMAGE_NUM | IMAGE_NAME | all)\n", +"imagex dir WIMFILE (IMAGE_NUM | IMAGE_NAME | all)\n", [EXPORT] = -" imagex export SRC_WIMFILE (SRC_IMAGE_NUM | SRC_IMAGE_NAME | all ) \n" -" DEST_WIMFILE [DEST_IMAGE_NAME]\n" -" [DEST_IMAGE_DESCRIPTION] [--boot] [--check]\n" -" [--compress=TYPE] [--ref=\"GLOB\"]\n", +"imagex export SRC_WIMFILE (SRC_IMAGE_NUM | SRC_IMAGE_NAME | all ) \n" +" DEST_WIMFILE [DEST_IMAGE_NAME] [DEST_IMAGE_DESCRIPTION]\n" +" [--boot] [--check] [--compress=TYPE] [--ref=\"GLOB\"]\n" +" [--threads=NUM_THREADS] [--rebuild]\n", [INFO] = -" imagex info WIMFILE [IMAGE_NUM | IMAGE_NAME] [NEW_NAME]\n" -" [NEW_DESC] [--boot] [--check] [--header] [--lookup-table]\n" -" [--xml] [--extract-xml FILE] [--metadata]\n", +"imagex info WIMFILE [IMAGE_NUM | IMAGE_NAME] [NEW_NAME]\n" +" [NEW_DESC] [--boot] [--check] [--header] [--lookup-table]\n" +" [--xml] [--extract-xml FILE] [--metadata]\n", [JOIN] = -" imagex join [--check] WIMFILE SPLIT_WIM...\n", +"imagex join [--check] WIMFILE SPLIT_WIM...\n", [MOUNT] = -" imagex mount WIMFILE (IMAGE_NUM | IMAGE_NAME) DIRECTORY\n" -" [--check] [--debug] [--streams-interface=INTERFACE]\n" -" [--ref=\"GLOB\"]\n", +"imagex mount WIMFILE (IMAGE_NUM | IMAGE_NAME) DIRECTORY\n" +" [--check] [--debug] [--streams-interface=INTERFACE]\n" +" [--ref=\"GLOB\"]\n", [MOUNTRW] = -" imagex mountrw WIMFILE [IMAGE_NUM | IMAGE_NAME] DIRECTORY\n" -" [--check] [--debug] [--streams-interface=INTERFACE]\n", +"imagex mountrw WIMFILE [IMAGE_NUM | IMAGE_NAME] DIRECTORY\n" +" [--check] [--debug] [--streams-interface=INTERFACE]\n", +[OPTIMIZE] = +"imagex optimize WIMFILE [--check] [--recompress]\n", [SPLIT] = -" imagex split WIMFILE SPLIT_WIMFILE PART_SIZE_MB [--check]\n", +"imagex split WIMFILE SPLIT_WIMFILE PART_SIZE_MB [--check]\n", [UNMOUNT] = -" imagex unmount DIRECTORY [--commit] [--check]\n", +"imagex unmount DIRECTORY [--commit] [--check]\n", }; static const struct option common_options[] = { @@ -124,10 +128,13 @@ static const struct option capture_or_append_options[] = { {"dereference", no_argument, NULL, 'L'}, {"flags", required_argument, NULL, 'f'}, {"verbose", no_argument, NULL, 'v'}, + {"threads", required_argument, NULL, 't'}, + {"rebuild", no_argument, NULL, 'R'}, {NULL, 0, NULL, 0}, }; static const struct option delete_options[] = { {"check", no_argument, NULL, 'c'}, + {"soft", no_argument, NULL, 's'}, {NULL, 0, NULL, 0}, }; @@ -136,6 +143,8 @@ static const struct option export_options[] = { {"check", no_argument, NULL, 'c'}, {"compress", required_argument, NULL, 'x'}, {"ref", required_argument, NULL, 'r'}, + {"threads", required_argument, NULL, 't'}, + {"rebuild", no_argument, NULL, 'R'}, {NULL, 0, NULL, 0}, }; @@ -163,6 +172,12 @@ static const struct option mount_options[] = { {NULL, 0, NULL, 0}, }; +static const struct option optimize_options[] = { + {"check", no_argument, NULL, 'c'}, + {"recompress", no_argument, NULL, 'r'}, + {NULL, 0, NULL, 0}, +}; + static const struct option split_options[] = { {"check", no_argument, NULL, 'c'}, {NULL, 0, NULL, 0}, @@ -199,29 +214,17 @@ static void imagex_error_with_errno(const char *format, ...) va_end(va); } -static const char *path_basename(const char *path) -{ - const char *p = path; - while (*p) - p++; - p--; - - /* Trailing slashes. */ - while ((p != path - 1) && *p == '/') - p--; - - while ((p != path - 1) && *p != '/') - p--; - - return p + 1; -} - - -static int verify_image_exists(int image) +static int verify_image_exists(int image, const char *image_name, + const char *wim_name) { if (image == WIM_NO_IMAGE) { - imagex_error("Not a valid image"); - return WIMLIB_ERR_INVALID_IMAGE; + imagex_error("\"%s\" is not a valid image in `%s'!\n" + " Please specify a 1-based imagex index or " + "image name.\n" + " You may use `imagex info' to list the images " + "contained in a WIM.", + image_name, wim_name); + return -1; } return 0; } @@ -229,16 +232,17 @@ static int verify_image_exists(int image) static int verify_image_is_single(int image) { if (image == WIM_ALL_IMAGES) { - imagex_error("Cannot specify all images for this action"); - return WIMLIB_ERR_INVALID_IMAGE; + imagex_error("Cannot specify all images for this action!"); + return -1; } return 0; } -static int verify_image_exists_and_is_single(int image) +static int verify_image_exists_and_is_single(int image, const char *image_name, + const char *wim_name) { int ret; - ret = verify_image_exists(image); + ret = verify_image_exists(image, image_name, wim_name); if (ret == 0) ret = verify_image_is_single(image); return ret; @@ -259,6 +263,15 @@ static int get_compression_type(const char *optarg) } } +static off_t file_get_size(const char *filename) +{ + struct stat st; + if (stat(filename, &st) == 0) + return st.st_size; + else + return (off_t)-1; +} + static char *file_get_contents(const char *filename, size_t *len_ret) { struct stat stbuf; @@ -298,6 +311,15 @@ out_fclose: return NULL; } +static int file_writable(const char *path) +{ + int ret; + ret = access(path, F_OK | W_OK); + if (ret != 0) + imagex_error_with_errno("Can't modify `%s'", path); + return ret; +} + static int open_swms_from_glob(const char *swm_glob, const char *first_part, int open_flags, @@ -355,6 +377,19 @@ out: } +static unsigned parse_num_threads(const char *optarg) +{ + char *tmp; + unsigned nthreads = strtoul(optarg, &tmp, 10); + if (nthreads == UINT_MAX || *tmp || tmp == optarg) { + imagex_error("Number of threads must be a non-negative integer!"); + return UINT_MAX; + } else { + return nthreads; + } +} + + /* Extract one image, or all images, from a WIM file into a directory. */ static int imagex_apply(int argc, const char **argv) { @@ -368,7 +403,8 @@ static int imagex_apply(int argc, const char **argv) const char *wimfile; const char *dir; const char *image_num_or_name; - int extract_flags = 0; + int extract_flags = WIMLIB_EXTRACT_FLAG_SEQUENTIAL | + WIMLIB_EXTRACT_FLAG_SHOW_PROGRESS; const char *swm_glob = NULL; WIMStruct **additional_swms = NULL; @@ -417,7 +453,7 @@ static int imagex_apply(int argc, const char **argv) return ret; image = wimlib_resolve_image(w, image_num_or_name); - ret = verify_image_exists(image); + ret = verify_image_exists(image, image_num_or_name, wimfile); if (ret != 0) goto out; @@ -467,9 +503,11 @@ static int imagex_apply(int argc, const char **argv) additional_swms, num_additional_swms); out: wimlib_free(w); - if (additional_swms) + if (additional_swms) { for (unsigned i = 0; i < num_additional_swms; i++) wimlib_free(additional_swms[i]); + free(additional_swms); + } return ret; } @@ -477,7 +515,7 @@ static int imagex_capture_or_append(int argc, const char **argv) { int c; int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS; - int add_image_flags = 0; + int add_image_flags = WIMLIB_ADD_IMAGE_FLAG_SHOW_PROGRESS; int write_flags = WIMLIB_WRITE_FLAG_SHOW_PROGRESS; int compression_type = WIM_COMPRESSION_TYPE_XPRESS; const char *dir; @@ -493,6 +531,7 @@ static int imagex_capture_or_append(int argc, const char **argv) int cur_image; char *default_name; int cmd = strcmp(argv[0], "append") ? CAPTURE : APPEND; + unsigned num_threads = 0; for_opt(c, capture_or_append_options) { switch (c) { @@ -521,6 +560,14 @@ static int imagex_capture_or_append(int argc, const char **argv) add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_VERBOSE; write_flags |= WIMLIB_WRITE_FLAG_VERBOSE; break; + case 't': + num_threads = parse_num_threads(optarg); + if (num_threads == UINT_MAX) + return -1; + break; + case 'R': + write_flags |= WIMLIB_WRITE_FLAG_REBUILD; + break; default: usage(cmd); return -1; @@ -596,10 +643,14 @@ out_write: if (ret != 0) goto out; } - if (cmd == APPEND) - ret = wimlib_overwrite(w, write_flags); - else - ret = wimlib_write(w, wimfile, WIM_ALL_IMAGES, write_flags); + if (cmd == APPEND) { + ret = wimlib_overwrite(w, write_flags, num_threads); + } else { + ret = wimlib_write(w, wimfile, WIM_ALL_IMAGES, write_flags, + num_threads); + } + if (ret == WIMLIB_ERR_REOPEN) + ret = 0; if (ret != 0) imagex_error("Failed to write the WIM file `%s'", wimfile); out: @@ -626,6 +677,9 @@ static int imagex_delete(int argc, const char **argv) open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; + case 's': + write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE; + break; default: usage(DELETE); return -1; @@ -645,13 +699,17 @@ static int imagex_delete(int argc, const char **argv) wimfile = argv[0]; image_num_or_name = argv[1]; + ret = file_writable(wimfile); + if (ret != 0) + return ret; + ret = wimlib_open_wim(wimfile, open_flags, &w); if (ret != 0) return ret; image = wimlib_resolve_image(w, image_num_or_name); - ret = verify_image_exists(image); + ret = verify_image_exists(image, image_num_or_name, wimfile); if (ret != 0) goto out; @@ -661,7 +719,9 @@ static int imagex_delete(int argc, const char **argv) goto out; } - ret = wimlib_overwrite(w, write_flags); + ret = wimlib_overwrite(w, write_flags, 0); + if (ret == WIMLIB_ERR_REOPEN) + ret = 0; if (ret != 0) { imagex_error("Failed to write the file `%s' with image " "deleted", wimfile); @@ -698,7 +758,7 @@ static int imagex_dir(int argc, const char **argv) if (argc == 3) { image = wimlib_resolve_image(w, argv[2]); - ret = verify_image_exists(image); + ret = verify_image_exists(image, argv[2], wimfile); if (ret != 0) goto out; } else { @@ -745,6 +805,7 @@ static int imagex_export(int argc, const char **argv) const char *swm_glob = NULL; WIMStruct **additional_swms = NULL; unsigned num_additional_swms = 0; + unsigned num_threads = 0; for_opt(c, export_options) { switch (c) { @@ -764,6 +825,14 @@ static int imagex_export(int argc, const char **argv) case 'r': swm_glob = optarg; break; + case 't': + num_threads = parse_num_threads(optarg); + if (num_threads == UINT_MAX) + return -1; + break; + case 'R': + write_flags |= WIMLIB_WRITE_FLAG_REBUILD; + break; default: usage(EXPORT); return -1; @@ -803,6 +872,10 @@ static int imagex_export(int argc, const char **argv) if (ret != 0) goto out; + ret = file_writable(dest_wimfile); + if (ret != 0) + return ret; + dest_ctype = wimlib_get_compression_type(dest_w); if (compression_type_specified && compression_type != dest_ctype) @@ -832,7 +905,7 @@ static int imagex_export(int argc, const char **argv) } image = wimlib_resolve_image(src_w, src_image_num_or_name); - ret = verify_image_exists(image); + ret = verify_image_exists(image, src_image_num_or_name, src_wimfile); if (ret != 0) goto out; @@ -853,15 +926,19 @@ static int imagex_export(int argc, const char **argv) if (wim_is_new) ret = wimlib_write(dest_w, dest_wimfile, WIM_ALL_IMAGES, - write_flags); + write_flags, num_threads); else - ret = wimlib_overwrite(dest_w, write_flags); + ret = wimlib_overwrite(dest_w, write_flags, num_threads); out: + if (ret == WIMLIB_ERR_REOPEN) + ret = 0; wimlib_free(src_w); wimlib_free(dest_w); - if (additional_swms) + if (additional_swms) { for (unsigned i = 0; i < num_additional_swms; i++) wimlib_free(additional_swms[i]); + free(additional_swms); + } return ret; } @@ -1045,7 +1122,6 @@ static int imagex_info(int argc, const char **argv) } else { /* Modification operations */ - if (total_parts != 1) { imagex_error("Modifying a split WIM is not supported."); return -1; @@ -1101,14 +1177,26 @@ static int imagex_info(int argc, const char **argv) } } - /* Only call wimlib_overwrite_xml_and_header() if something - * actually needs to be changed. */ + /* Only call wimlib_overwrite() if something actually needs to + * be changed. */ if (boot || new_name || new_desc || check != wimlib_has_integrity_table(w)) { - ret = wimlib_overwrite_xml_and_header(w, check ? - WIMLIB_WRITE_FLAG_CHECK_INTEGRITY | - WIMLIB_WRITE_FLAG_SHOW_PROGRESS : 0); + ret = file_writable(wimfile); + if (ret != 0) + return ret; + + int write_flags; + if (check) { + write_flags = WIMLIB_WRITE_FLAG_CHECK_INTEGRITY | + WIMLIB_WRITE_FLAG_SHOW_PROGRESS; + } else { + write_flags = 0; + } + + ret = wimlib_overwrite(w, write_flags, 1); + if (ret == WIMLIB_ERR_REOPEN) + ret = 0; } else { printf("The file `%s' was not modified because nothing " "needed to be done.\n", wimfile); @@ -1222,21 +1310,26 @@ static int imagex_mount_rw_or_ro(int argc, const char **argv) num_images = wimlib_get_num_images(w); if (num_images != 1) { imagex_error("The file `%s' contains %d images; Please " - "select one", wimfile, num_images); + "select one.", wimfile, num_images); usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) ? MOUNTRW : MOUNT); - ret = WIMLIB_ERR_INVALID_IMAGE; + ret = -1; goto out; } dir = argv[1]; } else { image = wimlib_resolve_image(w, argv[1]); dir = argv[2]; + ret = verify_image_exists_and_is_single(image, argv[1], wimfile); + if (ret != 0) + goto out; } - ret = verify_image_exists_and_is_single(image); - if (ret != 0) - goto out; + if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { + ret = file_writable(wimfile); + if (ret != 0) + return ret; + } ret = wimlib_mount(w, image, dir, mount_flags, additional_swms, num_additional_swms); @@ -1247,9 +1340,11 @@ static int imagex_mount_rw_or_ro(int argc, const char **argv) } out: wimlib_free(w); - if (additional_swms) + if (additional_swms) { for (unsigned i = 0; i < num_additional_swms; i++) wimlib_free(additional_swms[i]); + free(additional_swms); + } return ret; mount_usage: usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) @@ -1257,6 +1352,74 @@ mount_usage: return -1; } +static int imagex_optimize(int argc, const char **argv) +{ + int c; + int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS; + int write_flags = WIMLIB_WRITE_FLAG_REBUILD | + WIMLIB_WRITE_FLAG_SHOW_PROGRESS; + int ret; + WIMStruct *w; + const char *wimfile; + off_t old_size; + off_t new_size; + + for_opt(c, optimize_options) { + switch (c) { + case 'c': + open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; + write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; + break; + case 'r': + write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS; + break; + default: + usage(OPTIMIZE); + return -1; + } + } + argc -= optind; + argv += optind; + + if (argc != 1) { + usage(OPTIMIZE); + return -1; + } + + wimfile = argv[0]; + + ret = wimlib_open_wim(wimfile, open_flags, &w); + if (ret != 0) + return ret; + + old_size = file_get_size(argv[0]); + printf("`%s' original size: ", wimfile); + if (old_size == -1) + puts("Unknown"); + else + printf("%"PRIu64" KiB\n", old_size >> 10); + + ret = wimlib_overwrite(w, write_flags, 0); + + new_size = file_get_size(argv[0]); + printf("`%s' optimized size: ", wimfile); + if (new_size == -1) + puts("Unknown"); + else + printf("%"PRIu64" KiB\n", new_size >> 10); + + fputs("Space saved: ", stdout); + if (new_size != -1 && old_size != -1) { + printf("%lld KiB\n", + ((long long)old_size - (long long)new_size) >> 10); + } else { + puts("Unknown"); + } + + wimlib_free(w); + return ret; +} + /* Split a WIM into a spanned set */ static int imagex_split(int argc, const char **argv) { @@ -1345,6 +1508,7 @@ static const struct imagex_command imagex_commands[] = { {"join", imagex_join, JOIN}, {"mount", imagex_mount_rw_or_ro, MOUNT}, {"mountrw", imagex_mount_rw_or_ro, MOUNTRW}, + {"optimize",imagex_optimize, OPTIMIZE}, {"split", imagex_split, SPLIT}, {"unmount", imagex_unmount, UNMOUNT}, }; @@ -1398,19 +1562,19 @@ static void help_or_version(int argc, const char **argv) static void usage(int cmd_type) { const struct imagex_command *cmd; - puts("IMAGEX: Usage:"); - fputs(usage_strings[cmd_type], stdout); - for_imagex_command(cmd) + printf("Usage: %s", usage_strings[cmd_type]); + for_imagex_command(cmd) { if (cmd->cmd == cmd_type) printf("\nTry `man imagex-%s' for more details.\n", cmd->name); + } } static void usage_all() { puts("IMAGEX: Usage:"); for (int i = 0; i < ARRAY_LEN(usage_strings); i++) - fputs(usage_strings[i], stdout); + printf(" %s", usage_strings[i]); static const char *extra = " imagex --help\n" " imagex --version\n"