X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=programs%2Fimagex.c;h=c1d817b970490255eb9dfc303f80737b95fe5c50;hp=c3985dfd04b1e0e03d155f0ecdc7f54e630af2b4;hb=145a20679ef30eafd0defbed295127f27aca27e9;hpb=708468e9d2c0db257caa258890743450f53a0709 diff --git a/programs/imagex.c b/programs/imagex.c index c3985dfd..c1d817b9 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -22,7 +22,10 @@ * along with this program. If not, see . */ -#include "config.h" +#ifdef HAVE_CONFIG_H +# include "config.h" /* Need for PACKAGE_VERSION, etc. */ +#endif + #include "wimlib.h" #include "wimlib_tchar.h" @@ -40,19 +43,23 @@ #include #ifdef HAVE_ALLOCA_H -#include +# include #endif #ifdef __WIN32__ # include "imagex-win32.h" # define tbasename win32_wbasename # define tglob win32_wglob +# define OS_PREFERRED_PATH_SEPARATOR L'\\' +# define OS_PREFERRED_PATH_SEPARATOR_STRING L"\\" #else /* __WIN32__ */ # include # include # include # define tbasename basename # define tglob glob +# define OS_PREFERRED_PATH_SEPARATOR '/' +# define OS_PREFERRED_PATH_SEPARATOR_STRING "/" #endif /* !__WIN32 */ @@ -80,7 +87,7 @@ enum imagex_op_type { }; static void usage(int cmd_type); -static void usage_all(); +static void usage_all(void); static bool imagex_be_quiet = false; @@ -101,6 +108,7 @@ IMAGEX_PROGNAME" apply WIMFILE [IMAGE_NUM | IMAGE_NAME | all]\n" " (DIRECTORY | NTFS_VOLUME) [--check] [--hardlink]\n" " [--symlink] [--verbose] [--ref=\"GLOB\"] [--unix-data]\n" " [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n" +" [--include-invalid-names]\n" ), [CAPTURE] = T( @@ -117,7 +125,7 @@ IMAGEX_PROGNAME" delete WIMFILE (IMAGE_NUM | IMAGE_NAME | all) [--check] [--soft ), [DIR] = T( -IMAGEX_PROGNAME" dir WIMFILE (IMAGE_NUM | IMAGE_NAME | all)\n" +IMAGEX_PROGNAME" dir WIMFILE (IMAGE_NUM | IMAGE_NAME | all) [--path=PATH]\n" ), [EXPORT] = T( @@ -131,6 +139,7 @@ T( IMAGEX_PROGNAME" extract WIMFILE (IMAGE_NUM | IMAGE_NAME) [PATH...]\n" " [--check] [--ref=\"GLOB\"] [--verbose] [--unix-data]\n" " [--no-acls] [--strict-acls] [--to-stdout] [--dest-dir=DIR]\n" +" [--include-invalid-names]\n" ), [INFO] = T( @@ -165,13 +174,13 @@ IMAGEX_PROGNAME" split WIMFILE SPLIT_WIMFILE PART_SIZE_MB [--check]\n" ), [UNMOUNT] = T( -IMAGEX_PROGNAME" unmount DIRECTORY [--commit] [--check] [--rebuild]\n" +IMAGEX_PROGNAME" unmount DIRECTORY [--commit] [--check] [--rebuild] [--lazy]\n" ), [UPDATE] = T( -IMAGEX_PROGNAME" update WIMFILE IMAGE [--check] [--rebuild]\n" +IMAGEX_PROGNAME" update WIMFILE [IMAGE_NUM | IMAGE_NAME] [--check] [--rebuild]\n" " [--threads=NUM_THREADS] [DEFAULT_ADD_OPTIONS]\n" -" [DEFAULT_DELETE_OPTIONS] < CMDFILE\n" +" [DEFAULT_DELETE_OPTIONS] [--command=STRING] [< CMDFILE]\n" ), }; @@ -179,20 +188,22 @@ IMAGEX_PROGNAME" update WIMFILE IMAGE [--check] [--rebuild]\n" static void recommend_man_page(const tchar *cmd_name) { + const tchar *format_str; #ifdef __WIN32__ - tprintf(T("See "IMAGEX_PROGNAME"-%"TS".pdf in the " - "doc directory for more details.\n"), - cmd_name); + format_str = T("See "IMAGEX_PROGNAME"%"TS"%"TS".pdf in the " + "doc directory for more details.\n"); #else - tprintf(T("Try `man "IMAGEX_PROGNAME"-%"TS"' " - "for more details.\n"), cmd_name); + format_str = T("Try `man "IMAGEX_PROGNAME"%"TS"%"TS"' " + "for more details.\n"); #endif + tprintf(format_str, *cmd_name ? T("-") : T(""), cmd_name); } enum { IMAGEX_ALLOW_OTHER_OPTION, IMAGEX_BOOT_OPTION, IMAGEX_CHECK_OPTION, + IMAGEX_COMMAND_OPTION, IMAGEX_COMMIT_OPTION, IMAGEX_COMPRESS_OPTION, IMAGEX_CONFIG_OPTION, @@ -204,10 +215,13 @@ enum { IMAGEX_FORCE_OPTION, IMAGEX_HARDLINK_OPTION, IMAGEX_HEADER_OPTION, + IMAGEX_INCLUDE_INVALID_NAMES_OPTION, + IMAGEX_LAZY_OPTION, IMAGEX_LOOKUP_TABLE_OPTION, IMAGEX_METADATA_OPTION, IMAGEX_NORPFIX_OPTION, IMAGEX_NO_ACLS_OPTION, + IMAGEX_PATH_OPTION, IMAGEX_REBUILD_OPTION, IMAGEX_RECOMPRESS_OPTION, IMAGEX_RECURSIVE_OPTION, @@ -238,6 +252,7 @@ static const struct option apply_options[] = { {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION}, {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION}, {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION}, + {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION}, {NULL, 0, NULL, 0}, }; static const struct option capture_or_append_options[] = { @@ -265,6 +280,11 @@ static const struct option delete_options[] = { {NULL, 0, NULL, 0}, }; +static const struct option dir_options[] = { + {T("path"), required_argument, NULL, IMAGEX_PATH_OPTION}, + {NULL, 0, NULL, 0}, +}; + static const struct option export_options[] = { {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION}, {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, @@ -285,6 +305,7 @@ static const struct option extract_options[] = { {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION}, {T("dest-dir"), required_argument, NULL, IMAGEX_DEST_DIR_OPTION}, {T("to-stdout"), no_argument, NULL, IMAGEX_TO_STDOUT_OPTION}, + {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION}, {NULL, 0, NULL, 0}, }; @@ -331,6 +352,7 @@ static const struct option unmount_options[] = { {T("commit"), no_argument, NULL, IMAGEX_COMMIT_OPTION}, {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION}, + {T("lazy"), no_argument, NULL, IMAGEX_LAZY_OPTION}, {NULL, 0, NULL, 0}, }; @@ -342,14 +364,23 @@ static const struct option update_options[] = { {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION}, {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION}, + {T("command"), required_argument, NULL, IMAGEX_COMMAND_OPTION}, + + /* Default delete options */ {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION}, {T("recursive"), no_argument, NULL, IMAGEX_RECURSIVE_OPTION}, + + /* Global add option */ {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION}, + + /* Default add options */ + {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION}, {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION}, {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION}, {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION}, {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION}, {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION}, + {NULL, 0, NULL, 0}, }; @@ -955,18 +986,6 @@ stdin_get_text_contents(size_t *num_tchars_ret) return translate_text_to_tstr(contents, num_bytes, num_tchars_ret); } -/* Return 0 if a path names a file to which the current user has write access; - * -1 otherwise (and print an error message). */ -static int -file_writable(const tchar *path) -{ - int ret; - ret = taccess(path, W_OK); - if (ret != 0) - imagex_error_with_errno(T("Can't modify \"%"TS"\""), path); - return ret; -} - #define TO_PERCENT(numerator, denominator) \ (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator))) @@ -986,16 +1005,41 @@ get_data_type(int ctype) return NULL; } +#define GIBIBYTE_MIN_NBYTES 10000000000ULL +#define MEBIBYTE_MIN_NBYTES 10000000ULL +#define KIBIBYTE_MIN_NBYTES 10000ULL + +static unsigned +get_unit(uint64_t total_bytes, const tchar **name_ret) +{ + if (total_bytes >= GIBIBYTE_MIN_NBYTES) { + *name_ret = T("GiB"); + return 30; + } else if (total_bytes >= MEBIBYTE_MIN_NBYTES) { + *name_ret = T("MiB"); + return 20; + } else if (total_bytes >= KIBIBYTE_MIN_NBYTES) { + *name_ret = T("KiB"); + return 10; + } else { + *name_ret = T("bytes"); + return 0; + } +} + /* Progress callback function passed to various wimlib functions. */ static int imagex_progress_func(enum wimlib_progress_msg msg, const union wimlib_progress_info *info) { unsigned percent_done; + unsigned unit_shift; + const tchar *unit_name; if (imagex_be_quiet) return 0; switch (msg) { case WIMLIB_PROGRESS_MSG_WRITE_STREAMS: + unit_shift = get_unit(info->write_streams.total_bytes, &unit_name); percent_done = TO_PERCENT(info->write_streams.completed_bytes, info->write_streams.total_bytes); if (info->write_streams.completed_streams == 0) { @@ -1006,10 +1050,12 @@ imagex_progress_func(enum wimlib_progress_msg msg, data_type, info->write_streams.num_threads, (info->write_streams.num_threads == 1) ? T("") : T("s")); } - tprintf(T("\r%"PRIu64" MiB of %"PRIu64" MiB (uncompressed) " + tprintf(T("\r%"PRIu64" %"TS" of %"PRIu64" %"TS" (uncompressed) " "written (%u%% done)"), - info->write_streams.completed_bytes >> 20, - info->write_streams.total_bytes >> 20, + info->write_streams.completed_bytes >> unit_shift, + unit_name, + info->write_streams.total_bytes >> unit_shift, + unit_name, percent_done); if (info->write_streams.completed_bytes >= info->write_streams.total_bytes) tputchar(T('\n')); @@ -1017,7 +1063,8 @@ imagex_progress_func(enum wimlib_progress_msg msg, case WIMLIB_PROGRESS_MSG_SCAN_BEGIN: tprintf(T("Scanning \"%"TS"\""), info->scan.source); if (*info->scan.wim_target_path) { - tprintf(T(" (loading as WIM path: \"/%"TS"\")...\n"), + tprintf(T(" (loading as WIM path: " + "\""WIMLIB_WIM_PATH_SEPARATOR_STRING"%"TS"\")...\n"), info->scan.wim_target_path); } else { tprintf(T(" (loading as root of WIM image)...\n")); @@ -1032,24 +1079,30 @@ imagex_progress_func(enum wimlib_progress_msg msg, /*case WIMLIB_PROGRESS_MSG_SCAN_END:*/ /*break;*/ case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY: + unit_shift = get_unit(info->integrity.total_bytes, &unit_name); percent_done = TO_PERCENT(info->integrity.completed_bytes, info->integrity.total_bytes); - tprintf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" MiB " - "of %"PRIu64" MiB (%u%%) done"), + tprintf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" %"TS" " + "of %"PRIu64" %"TS" (%u%%) done"), info->integrity.filename, - info->integrity.completed_bytes >> 20, - info->integrity.total_bytes >> 20, + info->integrity.completed_bytes >> unit_shift, + unit_name, + info->integrity.total_bytes >> unit_shift, + unit_name, percent_done); if (info->integrity.completed_bytes == info->integrity.total_bytes) tputchar(T('\n')); break; case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY: + unit_shift = get_unit(info->integrity.total_bytes, &unit_name); percent_done = TO_PERCENT(info->integrity.completed_bytes, info->integrity.total_bytes); - tprintf(T("\rCalculating integrity table for WIM: %"PRIu64" MiB " - "of %"PRIu64" MiB (%u%%) done"), - info->integrity.completed_bytes >> 20, - info->integrity.total_bytes >> 20, + tprintf(T("\rCalculating integrity table for WIM: %"PRIu64" %"TS" " + "of %"PRIu64" %"TS" (%u%%) done"), + info->integrity.completed_bytes >> unit_shift, + unit_name, + info->integrity.total_bytes >> unit_shift, + unit_name, percent_done); if (info->integrity.completed_bytes == info->integrity.total_bytes) tputchar(T('\n')); @@ -1065,7 +1118,8 @@ imagex_progress_func(enum wimlib_progress_msg msg, info->extract.target); break; case WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN: - tprintf(T("Extracting \"%"TS"\" from image %d (\"%"TS"\") " + tprintf(T("Extracting " + "\""WIMLIB_WIM_PATH_SEPARATOR_STRING"%"TS"\" from image %d (\"%"TS"\") " "in \"%"TS"\" to \"%"TS"\"\n"), info->extract.extract_root_wim_source_path, info->extract.image, @@ -1080,10 +1134,13 @@ imagex_progress_func(enum wimlib_progress_msg msg, case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS: percent_done = TO_PERCENT(info->extract.completed_bytes, info->extract.total_bytes); + unit_shift = get_unit(info->extract.total_bytes, &unit_name); tprintf(T("\rExtracting files: " - "%"PRIu64" MiB of %"PRIu64" MiB (%u%%) done"), - info->extract.completed_bytes >> 20, - info->extract.total_bytes >> 20, + "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"), + info->extract.completed_bytes >> unit_shift, + unit_name, + info->extract.total_bytes >> unit_shift, + unit_name, percent_done); if (info->extract.completed_bytes >= info->extract.total_bytes) tputchar(T('\n')); @@ -1104,23 +1161,29 @@ imagex_progress_func(enum wimlib_progress_msg msg, case WIMLIB_PROGRESS_MSG_JOIN_STREAMS: percent_done = TO_PERCENT(info->join.completed_bytes, info->join.total_bytes); + unit_shift = get_unit(info->join.total_bytes, &unit_name); tprintf(T("Writing resources from part %u of %u: " - "%"PRIu64 " MiB of %"PRIu64" MiB (%u%%) written\n"), + "%"PRIu64 " %"TS" of %"PRIu64" %"TS" (%u%%) written\n"), (info->join.completed_parts == info->join.total_parts) ? info->join.completed_parts : info->join.completed_parts + 1, info->join.total_parts, - info->join.completed_bytes >> 20, - info->join.total_bytes >> 20, + info->join.completed_bytes >> unit_shift, + unit_name, + info->join.total_bytes >> unit_shift, + unit_name, percent_done); break; case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART: percent_done = TO_PERCENT(info->split.completed_bytes, info->split.total_bytes); - tprintf(T("Writing \"%"TS"\": %"PRIu64" MiB of " - "%"PRIu64" MiB (%u%%) written\n"), + unit_shift = get_unit(info->split.total_bytes, &unit_name); + tprintf(T("Writing \"%"TS"\": %"PRIu64" %"TS" of " + "%"PRIu64" %"TS" (%u%%) written\n"), info->split.part_name, - info->split.completed_bytes >> 20, - info->split.total_bytes >> 20, + info->split.completed_bytes >> unit_shift, + unit_name, + info->split.total_bytes >> unit_shift, + unit_name, percent_done); break; case WIMLIB_PROGRESS_MSG_SPLIT_END_PART: @@ -1129,6 +1192,25 @@ imagex_progress_func(enum wimlib_progress_msg msg, info->split.cur_part_number); } break; + case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND: + switch (info->update.command->op) { + case WIMLIB_UPDATE_OP_DELETE: + tprintf(T("Deleted WIM path " + "\""WIMLIB_WIM_PATH_SEPARATOR_STRING "%"TS"\"\n"), + info->update.command->delete.wim_path); + break; + case WIMLIB_UPDATE_OP_RENAME: + tprintf(T("Renamed WIM path " + "\""WIMLIB_WIM_PATH_SEPARATOR_STRING "%"TS"\" => " + "\""WIMLIB_WIM_PATH_SEPARATOR_STRING "%"TS"\"\n"), + info->update.command->rename.wim_source_path, + info->update.command->rename.wim_target_path); + break; + case WIMLIB_UPDATE_OP_ADD: + default: + break; + } + break; default: break; } @@ -1233,7 +1315,9 @@ update_command_add_option(int op, const tchar *option, bool recognized = true; switch (op) { case WIMLIB_UPDATE_OP_ADD: - if (!tstrcmp(option, T("--unix-data"))) + if (!tstrcmp(option, T("--verbose"))) + cmd->add.add_flags |= WIMLIB_ADD_IMAGE_FLAG_VERBOSE; + else if (!tstrcmp(option, T("--unix-data"))) cmd->add.add_flags |= WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA; else if (!tstrcmp(option, T("--no-acls")) || !tstrcmp(option, T("--noacls"))) cmd->add.add_flags |= WIMLIB_ADD_IMAGE_FLAG_NO_ACLS; @@ -1252,7 +1336,7 @@ update_command_add_option(int op, const tchar *option, else recognized = false; break; - case WIMLIB_UPDATE_OP_RENAME: + default: recognized = false; break; } @@ -1347,8 +1431,8 @@ parse_update_command(tchar *line, size_t len, if (!update_command_add_option(op, next_string, command)) { imagex_error(T("Unrecognized option \"%"TS"\" to " - "update command on line %zu"), - next_string, line_number); + "update command \"%"TS"\" on line %zu"), + next_string, command_name, line_number); return false; } @@ -1471,6 +1555,10 @@ imagex_apply(int argc, tchar **argv) case IMAGEX_RPFIX_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX; break; + case IMAGEX_INCLUDE_INVALID_NAMES_OPTION: + extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES; + extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS; + break; default: usage(APPLY); return -1; @@ -1504,7 +1592,7 @@ imagex_apply(int argc, tchar **argv) num_images = wimlib_get_num_images(w); if (argc == 2 && num_images != 1) { imagex_error(T("\"%"TS"\" contains %d images; Please select one " - "(or all)"), wimfile, num_images); + "(or all)."), wimfile, num_images); usage(APPLY); ret = -1; goto out; @@ -1562,7 +1650,7 @@ static int imagex_capture_or_append(int argc, tchar **argv) { int c; - int open_flags = 0; + int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS; int add_image_flags = WIMLIB_ADD_IMAGE_FLAG_EXCLUDE_VERBOSE; int write_flags = 0; int compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS; @@ -1590,6 +1678,7 @@ imagex_capture_or_append(int argc, tchar **argv) bool capture_sources_malloced; struct wimlib_capture_source *capture_sources; size_t num_sources; + bool name_defaulted; for_opt(c, capture_or_append_options) { switch (c) { @@ -1661,13 +1750,16 @@ imagex_capture_or_append(int argc, tchar **argv) if (argc >= 3) { name = argv[2]; + name_defaulted = false; } else { /* Set default name to SOURCE argument, omitting any directory * prefixes and trailing slashes. This requires making a copy - * of @source. */ + * of @source. Leave some free characters at the end in case we + * append a number to keep the name unique. */ source_name_len = tstrlen(source); - source_copy = alloca((source_name_len + 1) * sizeof(tchar)); + source_copy = alloca((source_name_len + 1 + 25) * sizeof(tchar)); name = tbasename(tstrcpy(source_copy, source)); + name_defaulted = true; } /* Image description defaults to NULL if not given. */ desc = (argc >= 4) ? argv[3] : NULL; @@ -1746,6 +1838,20 @@ imagex_capture_or_append(int argc, tchar **argv) } } } + + if (cmd == APPEND && name_defaulted) { + /* If the user did not specify an image name, and the basename + * of the source already exists as an image name in the WIM + * file, append a suffix to make it unique. */ + unsigned long conflict_idx; + tchar *name_end = tstrchr(name, T('\0')); + for (conflict_idx = 1; + wimlib_image_name_in_use(w, name); + conflict_idx++) + { + tsprintf(name_end, T(" (%lu)"), conflict_idx); + } + } #ifdef __WIN32__ win32_acquire_capture_privileges(); #endif @@ -1777,8 +1883,6 @@ imagex_capture_or_append(int argc, tchar **argv) ret = wimlib_write(w, wimfile, WIMLIB_ALL_IMAGES, write_flags, num_threads, imagex_progress_func); } - if (ret == WIMLIB_ERR_REOPEN) - ret = 0; if (ret != 0) imagex_error(T("Failed to write the WIM file \"%"TS"\""), wimfile); @@ -1807,7 +1911,7 @@ static int imagex_delete(int argc, tchar **argv) { int c; - int open_flags = 0; + int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS; int write_flags = 0; const tchar *wimfile; const tchar *image_num_or_name; @@ -1843,10 +1947,6 @@ imagex_delete(int argc, tchar **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, imagex_progress_func); if (ret != 0) @@ -1865,8 +1965,6 @@ imagex_delete(int argc, tchar **argv) } ret = wimlib_overwrite(w, write_flags, 0, imagex_progress_func); - if (ret == WIMLIB_ERR_REOPEN) - ret = 0; if (ret != 0) { imagex_error(T("Failed to write the file \"%"TS"\" with image " "deleted"), wimfile); @@ -1876,56 +1974,80 @@ out: return ret; } +static int +print_full_path(const struct wimlib_wim_dentry *wdentry, void *_ignore) +{ + int ret = tprintf(T("%"TS"\n"), wdentry->full_path); + return (ret >= 0) ? 0 : -1; +} + /* Print the files contained in an image(s) in a WIM file. */ static int imagex_dir(int argc, tchar **argv) { const tchar *wimfile; - WIMStruct *w; + WIMStruct *wim = NULL; int image; int ret; - int num_images; + const tchar *path = T(""); + int c; - if (argc < 2) { + for_opt(c, dir_options) { + switch (c) { + case IMAGEX_PATH_OPTION: + path = optarg; + break; + default: + goto out_usage; + } + } + argc -= optind; + argv += optind; + + if (argc < 1) { imagex_error(T("Must specify a WIM file")); - usage(DIR); - return -1; + goto out_usage; } - if (argc > 3) { + if (argc > 2) { imagex_error(T("Too many arguments")); - usage(DIR); - return -1; + goto out_usage; } - wimfile = argv[1]; - ret = wimlib_open_wim(wimfile, WIMLIB_OPEN_FLAG_SPLIT_OK, &w, + wimfile = argv[0]; + ret = wimlib_open_wim(wimfile, WIMLIB_OPEN_FLAG_SPLIT_OK, &wim, imagex_progress_func); - if (ret != 0) - return ret; + if (ret) + goto out; - if (argc == 3) { - image = wimlib_resolve_image(w, argv[2]); - ret = verify_image_exists(image, argv[2], wimfile); - if (ret != 0) - goto out; + if (argc == 2) { + image = wimlib_resolve_image(wim, argv[1]); + ret = verify_image_exists(image, argv[1], wimfile); + if (ret) + goto out_wimlib_free; } else { /* Image was not specified. If the WIM only contains one image, * choose that one; otherwise, print an error. */ - num_images = wimlib_get_num_images(w); + int num_images = wimlib_get_num_images(wim); if (num_images != 1) { - imagex_error(T("The file \"%"TS"\" contains %d images; Please " + imagex_error(T("\"%"TS"\" contains %d images; Please " "select one."), wimfile, num_images); - usage(DIR); - ret = -1; - goto out; + wimlib_free(wim); + goto out_usage; } image = 1; } - ret = wimlib_print_files(w, image); + ret = wimlib_iterate_dir_tree(wim, image, path, + WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE, + print_full_path, NULL); +out_wimlib_free: + wimlib_free(wim); out: - wimlib_free(w); return ret; +out_usage: + usage(DIR); + ret = -1; + goto out; } /* Exports one, or all, images from a WIM file to a new WIM file or an existing @@ -2018,12 +2140,8 @@ imagex_export(int argc, tchar **argv) ret = -1; goto out; } - ret = wimlib_open_wim(dest_wimfile, open_flags, &dest_w, - imagex_progress_func); - if (ret != 0) - goto out; - - ret = file_writable(dest_wimfile); + ret = wimlib_open_wim(dest_wimfile, open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS, + &dest_w, imagex_progress_func); if (ret != 0) goto out; @@ -2082,8 +2200,6 @@ imagex_export(int argc, tchar **argv) ret = wimlib_overwrite(dest_w, write_flags, num_threads, imagex_progress_func); out: - if (ret == WIMLIB_ERR_REOPEN) - ret = 0; wimlib_free(src_w); wimlib_free(dest_w); if (additional_swms) { @@ -2115,18 +2231,19 @@ free_extract_commands(struct wimlib_extract_command *cmds, size_t num_cmds, } static struct wimlib_extract_command * -prepare_extract_commands(tchar **argv, int argc, int extract_flags, - tchar *dest_dir, size_t *num_cmds_ret) +prepare_extract_commands(tchar **paths, unsigned num_paths, + int extract_flags, tchar *dest_dir, + size_t *num_cmds_ret) { struct wimlib_extract_command *cmds; size_t num_cmds; tchar *emptystr = T(""); - if (argc == 0) { - argc = 1; - argv = &emptystr; + if (num_paths == 0) { + num_paths = 1; + paths = &emptystr; } - num_cmds = argc; + num_cmds = num_paths; cmds = calloc(num_cmds, sizeof(cmds[0])); if (!cmds) { imagex_error(T("Out of memory!")); @@ -2135,18 +2252,18 @@ prepare_extract_commands(tchar **argv, int argc, int extract_flags, for (size_t i = 0; i < num_cmds; i++) { cmds[i].extract_flags = extract_flags; - cmds[i].wim_source_path = argv[i]; - if (is_root_wim_path(argv[i])) { + cmds[i].wim_source_path = paths[i]; + if (is_root_wim_path(paths[i])) { cmds[i].fs_dest_path = dest_dir; } else { - size_t len = tstrlen(dest_dir) + 1 + tstrlen(argv[i]); + size_t len = tstrlen(dest_dir) + 1 + tstrlen(paths[i]); cmds[i].fs_dest_path = malloc((len + 1) * sizeof(tchar)); if (!cmds[i].fs_dest_path) { free_extract_commands(cmds, num_cmds, dest_dir); return NULL; } - tsprintf(cmds[i].fs_dest_path, T("%"TS"/%"TS), dest_dir, - tbasename(argv[i])); + tsprintf(cmds[i].fs_dest_path, T("%"TS""OS_PREFERRED_PATH_SEPARATOR_STRING"%"TS), + dest_dir, tbasename(paths[i])); } } *num_cmds_ret = num_cmds; @@ -2201,20 +2318,20 @@ imagex_extract(int argc, tchar **argv) extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT; imagex_be_quiet = true; break; + case IMAGEX_INCLUDE_INVALID_NAMES_OPTION: + extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES; + extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS; + break; default: - usage(EXTRACT); - ret = -1; - goto out; + goto out_usage; } } argc -= optind; argv += optind; - if (argc < 2) { - usage(EXTRACT); - ret = -1; - goto out; - } + if (argc < 2) + goto out_usage; + wimfile = argv[0]; image_num_or_name = argv[1]; @@ -2251,7 +2368,7 @@ imagex_extract(int argc, tchar **argv) win32_acquire_restore_privileges(); #endif - ret = wimlib_extract_files(wim, image, 0, cmds, num_cmds, + ret = wimlib_extract_files(wim, image, cmds, num_cmds, 0, additional_swms, num_additional_swms, imagex_progress_func); if (ret == 0) { @@ -2276,6 +2393,10 @@ out_free_cmds: free_extract_commands(cmds, num_cmds, dest_dir); out: return ret; +out_usage: + usage(EXTRACT); + ret = -1; + goto out; } /* Prints information about a WIM file; also can mark an image as bootable, @@ -2474,11 +2595,7 @@ imagex_info(int argc, tchar **argv) } else { /* Modification operations */ - if (total_parts != 1) { - imagex_error(T("Modifying a split WIM is not supported.")); - ret = -1; - goto out; - } + if (image == WIMLIB_ALL_IMAGES) image = 1; @@ -2497,7 +2614,9 @@ imagex_info(int argc, tchar **argv) } else { tprintf(T("Marking image %d as bootable.\n"), image); - wimlib_set_boot_idx(w, image); + ret = wimlib_set_boot_idx(w, image); + if (ret) + goto out; } } if (new_name) { @@ -2536,21 +2655,12 @@ imagex_info(int argc, tchar **argv) if (boot || new_name || new_desc || (check && !wimlib_has_integrity_table(w))) { - int write_flags; - - ret = file_writable(wimfile); - if (ret != 0) - goto out; + int write_flags = 0; if (check) - write_flags = WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; - else - write_flags = 0; - + write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; ret = wimlib_overwrite(w, write_flags, 1, imagex_progress_func); - if (ret == WIMLIB_ERR_REOPEN) - ret = 0; } else { tprintf(T("The file \"%"TS"\" was not modified because nothing " "needed to be done.\n"), wimfile); @@ -2619,8 +2729,10 @@ imagex_mount_rw_or_ro(int argc, tchar **argv) unsigned num_additional_swms = 0; const tchar *staging_dir = NULL; - if (!tstrcmp(argv[0], T("mountrw"))) + if (!tstrcmp(argv[0], T("mountrw"))) { mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE; + open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS; + } for_opt(c, mount_options) { switch (c) { @@ -2683,7 +2795,7 @@ imagex_mount_rw_or_ro(int argc, tchar **argv) image = 1; num_images = wimlib_get_num_images(w); if (num_images != 1) { - imagex_error(T("The file \"%"TS"\" contains %d images; Please " + imagex_error(T("\"%"TS"\" contains %d images; Please " "select one."), wimfile, num_images); usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) ? MOUNTRW : MOUNT); @@ -2699,12 +2811,6 @@ imagex_mount_rw_or_ro(int argc, tchar **argv) goto out; } - if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { - ret = file_writable(wimfile); - if (ret != 0) - goto out; - } - ret = wimlib_mount_image(w, image, dir, mount_flags, additional_swms, num_additional_swms, staging_dir); if (ret != 0) { @@ -2732,7 +2838,7 @@ static int imagex_optimize(int argc, tchar **argv) { int c; - int open_flags = 0; + int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS; int write_flags = WIMLIB_WRITE_FLAG_REBUILD; int ret; WIMStruct *w; @@ -2772,7 +2878,7 @@ imagex_optimize(int argc, tchar **argv) ret = wimlib_open_wim(wimfile, open_flags, &w, imagex_progress_func); - if (ret != 0) + if (ret) return ret; old_size = file_get_size(argv[0]); @@ -2870,6 +2976,9 @@ imagex_unmount(int argc, tchar **argv) case IMAGEX_REBUILD_OPTION: unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD; break; + case IMAGEX_LAZY_OPTION: + unmount_flags |= WIMLIB_UNMOUNT_FLAG_LAZY; + break; default: usage(UNMOUNT); return -1; @@ -2900,17 +3009,19 @@ imagex_update(int argc, tchar **argv) int image; WIMStruct *wim; int ret; - int open_flags = 0; - int write_flags = WIMLIB_WRITE_FLAG_SOFT_DELETE; - int update_flags = 0; + int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS; + int write_flags = 0; + int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS; int default_add_flags = WIMLIB_ADD_IMAGE_FLAG_EXCLUDE_VERBOSE; int default_delete_flags = 0; unsigned num_threads = 0; int c; - tchar *cmd_file_contents; + tchar *cmd_file_contents = NULL; size_t cmd_file_nchars; struct wimlib_update_command *cmds; size_t num_cmds; + int num_images; + tchar *command_str = NULL; const tchar *config_file = NULL; tchar *config_str; @@ -2918,6 +3029,7 @@ imagex_update(int argc, tchar **argv) for_opt(c, update_options) { switch (c) { + /* Generic or write options */ case IMAGEX_THREADS_OPTION: num_threads = parse_num_threads(optarg); if (num_threads == UINT_MAX) { @@ -2932,15 +3044,39 @@ imagex_update(int argc, tchar **argv) case IMAGEX_REBUILD_OPTION: write_flags |= WIMLIB_WRITE_FLAG_REBUILD; break; + case IMAGEX_COMMAND_OPTION: + if (command_str) { + imagex_error(T("--command may only be specified " + "one time. Please provide\n" + " the update commands " + "on standard input instead.")); + ret = -1; + goto out; + } + command_str = tstrdup(optarg); + if (!command_str) { + imagex_error(T("Out of memory!")); + ret = -1; + goto out; + } + break; + /* Default delete options */ case IMAGEX_FORCE_OPTION: default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE; break; case IMAGEX_RECURSIVE_OPTION: default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE; break; + + /* Global add option */ case IMAGEX_CONFIG_OPTION: config_file = optarg; break; + + /* Default add options */ + case IMAGEX_VERBOSE_OPTION: + default_add_flags |= WIMLIB_ADD_IMAGE_FLAG_VERBOSE; + break; case IMAGEX_DEREFERENCE_OPTION: default_add_flags |= WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE; break; @@ -2963,6 +3099,7 @@ imagex_update(int argc, tchar **argv) if (argc < 1 || argc > 2) goto out_usage; wimfile = argv[0]; + ret = wimlib_open_wim(wimfile, open_flags, &wim, imagex_progress_func); if (ret) goto out; @@ -2979,6 +3116,15 @@ imagex_update(int argc, tchar **argv) if (ret) goto out_wimlib_free; + num_images = wimlib_get_num_images(wim); + if (argc == 1 && num_images != 1) { + imagex_error(T("\"%"TS"\" contains %d images; Please select one."), + wimfile, num_images); + usage(UPDATE); + ret = -1; + goto out_wimlib_free; + } + /* Parse capture configuration file if specified */ if (config_file) { size_t config_len; @@ -2997,31 +3143,39 @@ imagex_update(int argc, tchar **argv) config = &default_capture_config; } - /* Read update commands from standard input */ - if (isatty(STDIN_FILENO)) { - tputs(T("Reading update commands from standard input...")); - recommend_man_page(T("update")); - } - cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars); - if (!cmd_file_contents) { - ret = -1; - goto out_free_config; - } + /* Read update commands from standard input, or the command string if + * specified. */ + if (command_str) { + cmds = parse_update_command_file(&command_str, tstrlen(command_str), + &num_cmds); + } else { + if (isatty(STDIN_FILENO)) { + tputs(T("Reading update commands from standard input...")); + recommend_man_page(T("update")); + } + cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars); + if (!cmd_file_contents) { + ret = -1; + goto out_free_config; + } - /* Parse the update commands */ - cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars, - &num_cmds); + /* Parse the update commands */ + cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars, + &num_cmds); + } if (!cmds) { ret = -1; goto out_free_cmd_file_contents; } /* Set default flags and capture config on the update commands */ + bool have_add_command = false; for (size_t i = 0; i < num_cmds; i++) { switch (cmds[i].op) { case WIMLIB_UPDATE_OP_ADD: cmds[i].add.add_flags |= default_add_flags; cmds[i].add.config = config; + have_add_command = true; break; case WIMLIB_UPDATE_OP_DELETE: cmds[i].delete.delete_flags |= default_delete_flags; @@ -3031,16 +3185,25 @@ imagex_update(int argc, tchar **argv) } } +#ifdef __WIN32__ + if (have_add_command) + win32_acquire_capture_privileges(); +#endif + /* Execute the update commands */ ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags, imagex_progress_func); if (ret) - goto out_free_cmds; + goto out_release_privs; /* Overwrite the updated WIM */ ret = wimlib_overwrite(wim, write_flags, num_threads, imagex_progress_func); -out_free_cmds: +out_release_privs: +#ifdef __WIN32__ + if (have_add_command) + win32_release_capture_privileges(); +#endif free(cmds); out_free_cmd_file_contents: free(cmd_file_contents); @@ -3053,6 +3216,7 @@ out_free_config: out_wimlib_free: wimlib_free(wim); out: + free(command_str); return ret; out_usage: usage(UPDATE); @@ -3089,7 +3253,7 @@ static const struct imagex_command imagex_commands[] = { }; static void -version() +version(void) { static const tchar *s = T( @@ -3152,7 +3316,7 @@ usage(int cmd_type) } static void -usage_all() +usage_all(void) { tfputs(T("Usage:\n"), stdout); for (int i = 0; i < ARRAY_LEN(usage_strings); i++) @@ -3164,9 +3328,9 @@ usage_all() "\n" " The compression TYPE may be \"maximum\", \"fast\", or \"none\".\n" "\n" -" Try `man "IMAGEX_PROGNAME"' for more information.\n" ); tfputs(extra, stdout); + recommend_man_page(T("")); } /* Entry point for wimlib's ImageX implementation. On UNIX the command