X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=programs%2Fimagex.c;h=28f0266356429b91879b6d1e59f421f435bcecf5;hp=762459e84ebc36cd42399c4308dea96b2d6604c1;hb=e3cbda91f4a1a844bc8edb5b5240b18f8261a6df;hpb=0f25f61f874e53e1784e0f74e94fe4daad4a4b5b diff --git a/programs/imagex.c b/programs/imagex.c index 762459e8..28f02663 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers + * Copyright (C) 2012-2017 Eric Biggers * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -131,14 +131,31 @@ enum { static void usage(int cmd, FILE *fp); static void usage_all(FILE *fp); static void recommend_man_page(int cmd, FILE *fp); -static const tchar *get_cmd_string(int cmd, bool nospace); +static const tchar *get_cmd_string(int cmd, bool only_short_form); -static bool imagex_be_quiet = false; static FILE *imagex_info_file; -#define imagex_printf(format, ...) \ +#define imagex_printf(format, ...) \ + if (imagex_info_file) \ tfprintf(imagex_info_file, format, ##__VA_ARGS__) +static void imagex_suppress_output(void) +{ + imagex_info_file = NULL; +} + +static void imagex_output_to_stderr(void) +{ + if (imagex_info_file) + imagex_info_file = stderr; +} + +static void imagex_flush_output(void) +{ + if (imagex_info_file) + fflush(imagex_info_file); +} + enum { IMAGEX_ALLOW_OTHER_OPTION, IMAGEX_BLOBS_OPTION, @@ -147,8 +164,8 @@ enum { IMAGEX_CHUNK_SIZE_OPTION, IMAGEX_COMMAND_OPTION, IMAGEX_COMMIT_OPTION, + IMAGEX_COMPACT_OPTION, IMAGEX_COMPRESS_OPTION, - IMAGEX_COMPRESS_SLOW_OPTION, IMAGEX_CONFIG_OPTION, IMAGEX_DEBUG_OPTION, IMAGEX_DELTA_FROM_OPTION, @@ -159,6 +176,7 @@ enum { IMAGEX_FLAGS_OPTION, IMAGEX_FORCE_OPTION, IMAGEX_HEADER_OPTION, + IMAGEX_IMAGE_PROPERTY_OPTION, IMAGEX_INCLUDE_INVALID_NAMES_OPTION, IMAGEX_LAZY_OPTION, IMAGEX_METADATA_OPTION, @@ -180,8 +198,8 @@ enum { IMAGEX_RECOMPRESS_OPTION, IMAGEX_RECURSIVE_OPTION, IMAGEX_REF_OPTION, - IMAGEX_RESUME_OPTION, IMAGEX_RPFIX_OPTION, + IMAGEX_SNAPSHOT_OPTION, IMAGEX_SOFT_OPTION, IMAGEX_SOLID_CHUNK_SIZE_OPTION, IMAGEX_SOLID_COMPRESS_OPTION, @@ -193,6 +211,7 @@ enum { IMAGEX_THREADS_OPTION, IMAGEX_TO_STDOUT_OPTION, IMAGEX_UNIX_DATA_OPTION, + IMAGEX_UNSAFE_COMPACT_OPTION, IMAGEX_UPDATE_OF_OPTION, IMAGEX_VERBOSE_OPTION, IMAGEX_WIMBOOT_CONFIG_OPTION, @@ -212,10 +231,8 @@ static const struct option apply_options[] = { {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}, - - /* --resume is undocumented for now as it needs improvement. */ - {T("resume"), no_argument, NULL, IMAGEX_RESUME_OPTION}, {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION}, + {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION}, {NULL, 0, NULL, 0}, }; @@ -225,18 +242,15 @@ static const struct option capture_or_append_options[] = { {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION}, - {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION}, {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION}, {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION}, - {T("pack-streams"), no_argument, NULL, IMAGEX_SOLID_OPTION}, {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION}, - {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION}, {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION}, - {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION}, {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION}, {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION}, {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION}, {T("flags"), required_argument, NULL, IMAGEX_FLAGS_OPTION}, + {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION}, {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION}, {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION}, {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION}, @@ -252,12 +266,15 @@ static const struct option capture_or_append_options[] = { {T("update-of"), required_argument, NULL, IMAGEX_UPDATE_OF_OPTION}, {T("delta-from"), required_argument, NULL, IMAGEX_DELTA_FROM_OPTION}, {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION}, + {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION}, + {T("snapshot"), no_argument, NULL, IMAGEX_SNAPSHOT_OPTION}, {NULL, 0, NULL, 0}, }; static const struct option delete_options[] = { {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, {T("soft"), no_argument, NULL, IMAGEX_SOFT_OPTION}, + {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION}, {NULL, 0, NULL, 0}, }; @@ -265,6 +282,7 @@ static const struct option dir_options[] = { {T("path"), required_argument, NULL, IMAGEX_PATH_OPTION}, {T("detailed"), no_argument, NULL, IMAGEX_DETAILED_OPTION}, {T("one-file-only"), no_argument, NULL, IMAGEX_ONE_FILE_ONLY_OPTION}, + {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION}, {NULL, 0, NULL, 0}, }; @@ -275,14 +293,10 @@ static const struct option export_options[] = { {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION}, {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION}, - {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION}, {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION}, {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION}, - {T("pack-streams"),no_argument, NULL, IMAGEX_SOLID_OPTION}, {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION}, - {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION}, {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION}, - {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION}, {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION}, {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION}, {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION}, @@ -290,6 +304,7 @@ static const struct option export_options[] = { {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION}, {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION}, {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION}, + {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION}, {NULL, 0, NULL, 0}, }; @@ -310,6 +325,7 @@ static const struct option extract_options[] = { {T("nullglob"), no_argument, NULL, IMAGEX_NULLGLOB_OPTION}, {T("preserve-dir-structure"), no_argument, NULL, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION}, {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION}, + {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION}, {NULL, 0, NULL, 0}, }; @@ -322,8 +338,8 @@ static const struct option info_options[] = { {T("header"), no_argument, NULL, IMAGEX_HEADER_OPTION}, {T("lookup-table"), no_argument, NULL, IMAGEX_BLOBS_OPTION}, {T("blobs"), no_argument, NULL, IMAGEX_BLOBS_OPTION}, - {T("metadata"), no_argument, NULL, IMAGEX_METADATA_OPTION}, {T("xml"), no_argument, NULL, IMAGEX_XML_OPTION}, + {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION}, {NULL, 0, NULL, 0}, }; @@ -332,6 +348,7 @@ static const struct option join_options[] = { {NULL, 0, NULL, 0}, }; +#if WIM_MOUNTING_SUPPORTED static const struct option mount_options[] = { {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, {T("debug"), no_argument, NULL, IMAGEX_DEBUG_OPTION}, @@ -342,6 +359,7 @@ static const struct option mount_options[] = { {T("allow-other"), no_argument, NULL, IMAGEX_ALLOW_OTHER_OPTION}, {NULL, 0, NULL, 0}, }; +#endif static const struct option optimize_options[] = { {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, @@ -349,19 +367,15 @@ static const struct option optimize_options[] = { {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION}, {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION}, - {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION}, - {T("recompress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION}, {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION}, {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION}, - {T("pack-streams"),no_argument, NULL, IMAGEX_SOLID_OPTION}, {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION}, - {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION}, {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION}, - {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION}, {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION}, {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION}, {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION}, {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION}, + {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION}, {NULL, 0, NULL, 0}, }; @@ -370,6 +384,7 @@ static const struct option split_options[] = { {NULL, 0, NULL, 0}, }; +#if WIM_MOUNTING_SUPPORTED static const struct option unmount_options[] = { {T("commit"), no_argument, NULL, IMAGEX_COMMIT_OPTION}, {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, @@ -379,6 +394,7 @@ static const struct option unmount_options[] = { {T("new-image"), no_argument, NULL, IMAGEX_NEW_IMAGE_OPTION}, {NULL, 0, NULL, 0}, }; +#endif static const struct option update_options[] = { /* Careful: some of the options here set the defaults for update @@ -406,6 +422,7 @@ static const struct option update_options[] = { {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION}, {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION}, {T("no-replace"), no_argument, NULL, IMAGEX_NO_REPLACE_OPTION}, + {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION}, {NULL, 0, NULL, 0}, }; @@ -490,7 +507,7 @@ verify_image_exists_and_is_single(int image, const tchar *image_name, static void print_available_compression_types(FILE *fp) { - static const tchar *s = + static const tchar * const s = T( "Available compression types:\n" "\n" @@ -503,9 +520,9 @@ print_available_compression_types(FILE *fp) tfputs(s, fp); } -/* Parse the argument to --compress */ +/* Parse the argument to --compress or --solid-compress */ static int -get_compression_type(tchar *optarg) +get_compression_type(tchar *optarg, bool solid) { int ctype; unsigned int compression_level = 0; @@ -528,15 +545,27 @@ get_compression_type(tchar *optarg) if (!tstrcasecmp(optarg, T("maximum")) || !tstrcasecmp(optarg, T("lzx")) || - !tstrcasecmp(optarg, T("max"))) + !tstrcasecmp(optarg, T("max"))) { ctype = WIMLIB_COMPRESSION_TYPE_LZX; - else if (!tstrcasecmp(optarg, T("fast")) || !tstrcasecmp(optarg, T("xpress"))) + } else if (!tstrcasecmp(optarg, T("fast")) || !tstrcasecmp(optarg, T("xpress"))) { ctype = WIMLIB_COMPRESSION_TYPE_XPRESS; - else if (!tstrcasecmp(optarg, T("recovery")) || !tstrcasecmp(optarg, T("lzms"))) + } else if (!tstrcasecmp(optarg, T("recovery"))) { + if (!solid) { + tfprintf(stderr, +T( +"Warning: use of '--compress=recovery' is discouraged because it behaves\n" +" differently from DISM. Instead, you typically want to use '--solid' to\n" +" create a solid LZMS-compressed WIM or \"ESD file\", similar to DISM's\n" +" /compress:recovery. But if you really want *non-solid* LZMS compression,\n" +" then you may suppress this warning by specifying '--compress=lzms' instead\n" +" of '--compress=recovery'.\n")); + } + ctype = WIMLIB_COMPRESSION_TYPE_LZMS; + } else if (!tstrcasecmp(optarg, T("lzms"))) { ctype = WIMLIB_COMPRESSION_TYPE_LZMS; - else if (!tstrcasecmp(optarg, T("none"))) + } else if (!tstrcasecmp(optarg, T("none"))) { ctype = WIMLIB_COMPRESSION_TYPE_NONE; - else { + } else { imagex_error(T("Invalid compression type \"%"TS"\"!"), optarg); print_available_compression_types(stderr); return WIMLIB_COMPRESSION_TYPE_INVALID; @@ -547,69 +576,135 @@ get_compression_type(tchar *optarg) return ctype; } -static void -set_compress_slow(void) +/* Parse the argument to --compact */ +static int +set_compact_mode(const tchar *arg, int *extract_flags) { -#if 0 - fprintf(stderr, "WARNING: the '--compress-slow' option is deprecated.\n" - " Use the '--compress=TYPE:LEVEL' option instead.\n"); -#endif - wimlib_set_default_compression_level(-1, 100); + int flag = 0; + if (!tstrcasecmp(arg, T("xpress4k"))) + flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K; + else if (!tstrcasecmp(arg, T("xpress8k"))) + flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K; + else if (!tstrcasecmp(arg, T("xpress16k"))) + flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K; + else if (!tstrcasecmp(arg, T("lzx"))) + flag = WIMLIB_EXTRACT_FLAG_COMPACT_LZX; + + if (flag) { + *extract_flags |= flag; + return 0; + } + + imagex_error(T( +"\"%"TS"\" is not a recognized System Compression format. The options are:" +"\n" +" --compact=xpress4k\n" +" --compact=xpress8k\n" +" --compact=xpress16k\n" +" --compact=lzx\n" + ), arg); + return -1; } -struct string_set { - const tchar **strings; + +struct string_list { + tchar **strings; unsigned num_strings; unsigned num_alloc_strings; }; -#define STRING_SET_INITIALIZER \ +#define STRING_LIST_INITIALIZER \ { .strings = NULL, .num_strings = 0, .num_alloc_strings = 0, } -#define STRING_SET(_strings) \ - struct string_set _strings = STRING_SET_INITIALIZER +#define STRING_LIST(_strings) \ + struct string_list _strings = STRING_LIST_INITIALIZER static int -string_set_append(struct string_set *set, const tchar *glob) +string_list_append(struct string_list *list, tchar *glob) { - unsigned num_alloc_strings = set->num_alloc_strings; + unsigned num_alloc_strings = list->num_alloc_strings; - if (set->num_strings == num_alloc_strings) { - const tchar **new_strings; + if (list->num_strings == num_alloc_strings) { + tchar **new_strings; num_alloc_strings += 4; - new_strings = realloc(set->strings, - sizeof(set->strings[0]) * num_alloc_strings); + new_strings = realloc(list->strings, + sizeof(list->strings[0]) * num_alloc_strings); if (!new_strings) { imagex_error(T("Out of memory!")); return -1; } - set->strings = new_strings; - set->num_alloc_strings = num_alloc_strings; + list->strings = new_strings; + list->num_alloc_strings = num_alloc_strings; } - set->strings[set->num_strings++] = glob; + list->strings[list->num_strings++] = glob; return 0; } static void -string_set_destroy(struct string_set *set) +string_list_destroy(struct string_list *list) { - free(set->strings); + free(list->strings); } static int -wim_reference_globs(WIMStruct *wim, struct string_set *set, int open_flags) +wim_reference_globs(WIMStruct *wim, struct string_list *list, int open_flags) { - return wimlib_reference_resource_files(wim, set->strings, - set->num_strings, + return wimlib_reference_resource_files(wim, (const tchar **)list->strings, + list->num_strings, WIMLIB_REF_FLAG_GLOB_ENABLE, open_flags); } +static int +append_image_property_argument(struct string_list *image_properties) +{ + if (!tstrchr(optarg, '=')) { + imagex_error(T("'--image-property' argument " + "must be in the form NAME=VALUE")); + return -1; + } + return string_list_append(image_properties, optarg); +} + +static int +apply_image_properties(struct string_list *image_properties, + WIMStruct *wim, int image, bool *any_changes_ret) +{ + bool any_changes = false; + for (unsigned i = 0; i < image_properties->num_strings; i++) { + tchar *name, *value; + const tchar *current_value; + int ret; + + name = image_properties->strings[i]; + value = tstrchr(name, '='); + *value++ = '\0'; + + current_value = wimlib_get_image_property(wim, image, name); + if (current_value && !tstrcmp(current_value, value)) { + imagex_printf(T("The %"TS" property of image %d " + "already has value \"%"TS"\".\n"), + name, image, value); + } else { + imagex_printf(T("Setting the %"TS" property of image " + "%d to \"%"TS"\".\n"), + name, image, value); + ret = wimlib_set_image_property(wim, image, name, value); + if (ret) + return ret; + any_changes = true; + } + } + if (any_changes_ret) + *any_changes_ret = any_changes; + return 0; +} + static void do_resource_not_found_warning(const tchar *wimfile, const struct wimlib_wim_info *info, - const struct string_set *refglobs) + const struct string_list *refglobs) { if (info->total_parts > 1) { if (refglobs->num_strings == 0) { @@ -627,6 +722,17 @@ do_resource_not_found_warning(const tchar *wimfile, } } +static void +do_metadata_not_found_warning(const tchar *wimfile, + const struct wimlib_wim_info *info) +{ + if (info->part_number != 1) { + imagex_error(T("\"%"TS"\" is not the first part of the split WIM.\n" + " You must specify the first part."), + wimfile); + } +} + /* Returns the size of a file given its name, or -1 if the file does not exist * or its size cannot be determined. */ static off_t @@ -1052,28 +1158,27 @@ imagex_progress_func(enum wimlib_progress_msg msg, unsigned unit_shift; const tchar *unit_name; - if (imagex_be_quiet) - return WIMLIB_PROGRESS_STATUS_CONTINUE; switch (msg) { case WIMLIB_PROGRESS_MSG_WRITE_STREAMS: { - static bool first = true; - if (first) { - imagex_printf(T("Writing %"TS"-compressed data " - "using %u thread%"TS"\n"), - wimlib_get_compression_type_string( - info->write_streams.compression_type), - info->write_streams.num_threads, - (info->write_streams.num_threads == 1) ? T("") : T("s")); - first = false; + static bool started; + if (!started) { + if (info->write_streams.compression_type != WIMLIB_COMPRESSION_TYPE_NONE) { + imagex_printf(T("Using %"TS" compression " + "with %u thread%"TS"\n"), + wimlib_get_compression_type_string( + info->write_streams.compression_type), + info->write_streams.num_threads, + (info->write_streams.num_threads == 1) ? T("") : T("s")); + } + started = true; } } 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); - imagex_printf(T("\r%"PRIu64" %"TS" of %"PRIu64" %"TS" (uncompressed) " - "written (%u%% done)"), + imagex_printf(T("\rArchiving file data: %"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"), info->write_streams.completed_bytes >> unit_shift, unit_name, info->write_streams.total_bytes >> unit_shift, @@ -1181,7 +1286,7 @@ imagex_progress_func(enum wimlib_progress_msg msg, percent_done = TO_PERCENT(info->extract.completed_bytes, info->extract.total_bytes); unit_shift = get_unit(info->extract.total_bytes, &unit_name); - imagex_printf(T("\rExtracting files: " + imagex_printf(T("\rExtracting file data: " "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"), info->extract.completed_bytes >> unit_shift, unit_name, @@ -1291,7 +1396,7 @@ imagex_progress_func(enum wimlib_progress_msg msg, default: break; } - fflush(imagex_info_file); + imagex_flush_output(); return WIMLIB_PROGRESS_STATUS_CONTINUE; } @@ -1568,7 +1673,7 @@ imagex_apply(int argc, tchar **argv, int cmd) const tchar *image_num_or_name = NULL; int extract_flags = 0; - STRING_SET(refglobs); + STRING_LIST(refglobs); for_opt(c, apply_options) { switch (c) { @@ -1579,7 +1684,7 @@ imagex_apply(int argc, tchar **argv, int cmd) /* No longer does anything. */ break; case IMAGEX_REF_OPTION: - ret = string_set_append(&refglobs, optarg); + ret = string_list_append(&refglobs, optarg); if (ret) goto out_free_refglobs; break; @@ -1605,12 +1710,14 @@ imagex_apply(int argc, tchar **argv, int cmd) extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES; extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS; break; - case IMAGEX_RESUME_OPTION: - extract_flags |= WIMLIB_EXTRACT_FLAG_RESUME; - break; case IMAGEX_WIMBOOT_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT; break; + case IMAGEX_COMPACT_OPTION: + ret = set_compact_mode(optarg, &extract_flags); + if (ret) + goto out_free_refglobs; + break; default: goto out_usage; } @@ -1718,11 +1825,13 @@ imagex_apply(int argc, tchar **argv, int cmd) " make sure you have " "concatenated together all parts.")); } + } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND && wim) { + do_metadata_not_found_warning(wimfile, &info); } out_wimlib_free: wimlib_free(wim); out_free_refglobs: - string_set_destroy(&refglobs); + string_list_destroy(&refglobs); return ret; out_usage: @@ -1742,7 +1851,8 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) int open_flags = 0; int add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE | WIMLIB_ADD_FLAG_WINCONFIG | - WIMLIB_ADD_FLAG_VERBOSE; + WIMLIB_ADD_FLAG_VERBOSE | + WIMLIB_ADD_FLAG_FILE_PATHS_UNNEEDED; int write_flags = 0; int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID; uint32_t chunk_size = UINT32_MAX; @@ -1751,11 +1861,10 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) const tchar *wimfile; int wim_fd; const tchar *name; - const tchar *desc; - const tchar *flags_element = NULL; + STRING_LIST(image_properties); WIMStruct *wim; - STRING_SET(base_wimfiles); + STRING_LIST(base_wimfiles); WIMStruct **base_wims; WIMStruct *template_wim; @@ -1796,13 +1905,10 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG; break; case IMAGEX_COMPRESS_OPTION: - compression_type = get_compression_type(optarg); + compression_type = get_compression_type(optarg, false); if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) goto out_err; break; - case IMAGEX_COMPRESS_SLOW_OPTION: - set_compress_slow(); - break; case IMAGEX_CHUNK_SIZE_OPTION: chunk_size = parse_chunk_size(optarg); if (chunk_size == UINT32_MAX) @@ -1814,7 +1920,7 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) goto out_err; break; case IMAGEX_SOLID_COMPRESS_OPTION: - solid_ctype = get_compression_type(optarg); + solid_ctype = get_compression_type(optarg, true); if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID) goto out_err; break; @@ -1824,8 +1930,18 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) case IMAGEX_NO_SOLID_SORT_OPTION: write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT; break; - case IMAGEX_FLAGS_OPTION: - flags_element = optarg; + case IMAGEX_FLAGS_OPTION: { + tchar *p = alloca((6 + tstrlen(optarg) + 1) * sizeof(tchar)); + tsprintf(p, T("FLAGS=%"TS), optarg); + ret = string_list_append(&image_properties, p); + if (ret) + goto out; + break; + } + case IMAGEX_IMAGE_PROPERTY_OPTION: + ret = append_image_property_argument(&image_properties); + if (ret) + goto out; break; case IMAGEX_DEREFERENCE_OPTION: add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE; @@ -1885,19 +2001,25 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) } break; case IMAGEX_DELTA_FROM_OPTION: - if (cmd != CMD_CAPTURE) { - imagex_error(T("'--delta-from' is only " - "valid for capture!")); - goto out_usage; - } - ret = string_set_append(&base_wimfiles, optarg); + ret = string_list_append(&base_wimfiles, optarg); if (ret) - goto out_free_base_wimfiles; + goto out; write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS; break; case IMAGEX_WIMBOOT_OPTION: add_flags |= WIMLIB_ADD_FLAG_WIMBOOT; break; + case IMAGEX_UNSAFE_COMPACT_OPTION: + if (cmd != CMD_APPEND) { + imagex_error(T("'--unsafe-compact' is only " + "valid for append!")); + goto out_err; + } + write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT; + break; + case IMAGEX_SNAPSHOT_OPTION: + add_flags |= WIMLIB_ADD_FLAG_SNAPSHOT; + break; default: goto out_usage; } @@ -1950,7 +2072,7 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) } wim_fd = STDOUT_FILENO; wimfile = NULL; - imagex_info_file = stderr; + imagex_output_to_stderr(); set_fd_to_binary_mode(wim_fd); } @@ -1998,11 +2120,15 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) name = tbasename(tstrcpy(source_copy, source)); name_defaulted = true; } - /* Image description defaults to NULL if not given. */ - if (argc >= 4) - desc = argv[3]; - else - desc = NULL; + + /* Image description (if given). */ + if (argc >= 4) { + tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar)); + tsprintf(p, T("DESCRIPTION=%"TS), argv[3]); + ret = string_list_append(&image_properties, p); + if (ret) + goto out; + } if (source_list) { /* Set up capture sources in source list mode */ @@ -2055,11 +2181,21 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) ret = wimlib_set_output_chunk_size(wim, chunk_size); if (ret) goto out_free_wim; - } else if ((add_flags & WIMLIB_ADD_FLAG_WIMBOOT) && - compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS) { - ret = wimlib_set_output_chunk_size(wim, 4096); - if (ret) - goto out_free_wim; + } else if ((add_flags & WIMLIB_ADD_FLAG_WIMBOOT)) { + + int ctype = compression_type; + + if (cmd == CMD_APPEND) { + struct wimlib_wim_info info; + wimlib_get_wim_info(wim, &info); + ctype = info.compression_type; + } + + if (ctype == WIMLIB_COMPRESSION_TYPE_XPRESS) { + ret = wimlib_set_output_chunk_size(wim, 4096); + if (ret) + goto out_free_wim; + } } if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) { ret = wimlib_set_output_pack_compression_type(wim, solid_ctype); @@ -2201,29 +2337,18 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) if (ret) goto out_free_template_wim; - if (desc || flags_element || template_image_name_or_num) { - /* User provided or element, or an image - * on which the added one is to be based has been specified with - * --update-of. Get the index of the image we just - * added, then use it to call the appropriate functions. */ + if (image_properties.num_strings || template_image_name_or_num) { + /* User asked to set additional image properties, or an image on + * which the added one is to be based has been specified with + * --update-of. */ struct wimlib_wim_info info; wimlib_get_wim_info(wim, &info); - if (desc) { - ret = wimlib_set_image_descripton(wim, - info.image_count, - desc); - if (ret) - goto out_free_template_wim; - } - - if (flags_element) { - ret = wimlib_set_image_flags(wim, info.image_count, - flags_element); - if (ret) - goto out_free_template_wim; - } + ret = apply_image_properties(&image_properties, wim, + info.image_count, NULL); + if (ret) + goto out_free_template_wim; /* Reference template image if the user provided one. */ if (template_image_name_or_num) { @@ -2267,15 +2392,16 @@ out_free_capture_sources: free(capture_sources); out_free_source_list_contents: free(source_list_contents); -out_free_base_wimfiles: - string_set_destroy(&base_wimfiles); +out: + string_list_destroy(&image_properties); + string_list_destroy(&base_wimfiles); return ret; out_usage: usage(cmd, stderr); out_err: ret = -1; - goto out_free_base_wimfiles; + goto out; } /* Remove image(s) from a WIM. */ @@ -2300,6 +2426,9 @@ imagex_delete(int argc, tchar **argv, int cmd) case IMAGEX_SOFT_OPTION: write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE; break; + case IMAGEX_UNSAFE_COMPACT_OPTION: + write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT; + break; default: goto out_usage; } @@ -2385,21 +2514,21 @@ static const struct { #define TIMESTR_MAX 100 static void -timespec_to_string(const struct timespec *spec, tchar *buf) +print_time(const tchar *type, const struct wimlib_timespec *wts, + int32_t high_part) { - time_t t = spec->tv_sec; + tchar timestr[TIMESTR_MAX]; + time_t t; struct tm tm; - gmtime_r(&t, &tm); - tstrftime(buf, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm); - buf[TIMESTR_MAX - 1] = '\0'; -} -static void -print_time(const tchar *type, const struct timespec *spec) -{ - tchar timestr[TIMESTR_MAX]; + if (sizeof(wts->tv_sec) == 4 && sizeof(t) > sizeof(wts->tv_sec)) + t = (uint32_t)wts->tv_sec | ((uint64_t)high_part << 32); + else + t = wts->tv_sec; - timespec_to_string(spec, timestr); + gmtime_r(&t, &tm); + tstrftime(timestr, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm); + timestr[TIMESTR_MAX - 1] = '\0'; tprintf(T("%-20"TS"= %"TS"\n"), type, timestr); } @@ -2435,26 +2564,26 @@ print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info) attr_string[0] = T('\0'); if (info->pipable) - tstrcat(attr_string, "Pipable, "); + tstrcat(attr_string, T("Pipable, ")); if (info->has_integrity_table) - tstrcat(attr_string, "Integrity info, "); + tstrcat(attr_string, T("Integrity info, ")); if (info->has_rpfix) - tstrcat(attr_string, "Relative path junction, "); + tstrcat(attr_string, T("Relative path junction, ")); if (info->resource_only) - tstrcat(attr_string, "Resource only, "); + tstrcat(attr_string, T("Resource only, ")); if (info->metadata_only) - tstrcat(attr_string, "Metadata only, "); + tstrcat(attr_string, T("Metadata only, ")); if (info->is_marked_readonly) - tstrcat(attr_string, "Readonly, "); + tstrcat(attr_string, T("Readonly, ")); - p = tstrchr(attr_string, '\0'); + p = tstrchr(attr_string, T('\0')); if (p >= &attr_string[2] && p[-1] == T(' ') && p[-2] == T(',')) - p[-2] = '\0'; + p[-2] = T('\0'); tprintf(T("Attributes: %"TS"\n\n"), attr_string); } @@ -2513,6 +2642,7 @@ print_blobs(WIMStruct *wim) wimlib_iterate_lookup_table(wim, 0, print_resource, NULL); } +#ifndef __WIN32__ static void default_print_security_descriptor(const uint8_t *sd, size_t size) { @@ -2520,11 +2650,29 @@ default_print_security_descriptor(const uint8_t *sd, size_t size) print_byte_field(sd, size); tputchar(T('\n')); } +#endif + +static bool +is_null_guid(const uint8_t *guid) +{ + static const uint8_t null_guid[WIMLIB_GUID_LEN]; + + return !memcmp(guid, null_guid, WIMLIB_GUID_LEN); +} + +static void +print_guid(const tchar *label, const uint8_t *guid) +{ + if (is_null_guid(guid)) + return; + tprintf(T("%-20"TS"= 0x"), label); + print_byte_field(guid, WIMLIB_GUID_LEN); + tputchar(T('\n')); +} static void print_dentry_detailed(const struct wimlib_dir_entry *dentry) { - tprintf(T( "----------------------------------------------------------------------------\n")); tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path); @@ -2541,9 +2689,12 @@ print_dentry_detailed(const struct wimlib_dir_entry *dentry) dentry->security_descriptor_size); } - print_time(T("Creation Time"), &dentry->creation_time); - print_time(T("Last Write Time"), &dentry->last_write_time); - print_time(T("Last Access Time"), &dentry->last_access_time); + print_time(T("Creation Time"), + &dentry->creation_time, dentry->creation_time_high); + print_time(T("Last Write Time"), + &dentry->last_write_time, dentry->last_write_time_high); + print_time(T("Last Access Time"), + &dentry->last_access_time, dentry->last_access_time_high); if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT) @@ -2559,6 +2710,13 @@ print_dentry_detailed(const struct wimlib_dir_entry *dentry) dentry->unix_mode, dentry->unix_rdev); } + if (!is_null_guid(dentry->object_id.object_id)) { + print_guid(T("Object ID"), dentry->object_id.object_id); + print_guid(T("Birth Volume ID"), dentry->object_id.birth_volume_id); + print_guid(T("Birth Object ID"), dentry->object_id.birth_object_id); + print_guid(T("Domain ID"), dentry->object_id.domain_id); + } + for (uint32_t i = 0; i <= dentry->num_named_streams; i++) { if (dentry->streams[i].stream_name) { tprintf(T("\tNamed data stream \"%"TS"\":\n"), @@ -2600,6 +2758,8 @@ imagex_dir(int argc, tchar **argv, int cmd) }; int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE; + STRING_LIST(refglobs); + for_opt(c, dir_options) { switch (c) { case IMAGEX_PATH_OPTION: @@ -2611,6 +2771,11 @@ imagex_dir(int argc, tchar **argv, int cmd) case IMAGEX_ONE_FILE_ONLY_OPTION: iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE; break; + case IMAGEX_REF_OPTION: + ret = string_list_append(&refglobs, optarg); + if (ret) + goto out_free_refglobs; + break; default: goto out_usage; } @@ -2631,7 +2796,7 @@ imagex_dir(int argc, tchar **argv, int cmd) ret = wimlib_open_wim_with_progress(wimfile, 0, &wim, imagex_progress_func, NULL); if (ret) - goto out; + goto out_free_refglobs; if (argc >= 2) { image = wimlib_resolve_image(wim, argv[1]); @@ -2655,17 +2820,30 @@ imagex_dir(int argc, tchar **argv, int cmd) image = 1; } + if (refglobs.num_strings) { + ret = wim_reference_globs(wim, &refglobs, 0); + if (ret) + goto out_wimlib_free; + } + ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags, print_dentry, &options); + if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) { + struct wimlib_wim_info info; + + wimlib_get_wim_info(wim, &info); + do_metadata_not_found_warning(wimfile, &info); + } out_wimlib_free: wimlib_free(wim); -out: +out_free_refglobs: + string_list_destroy(&refglobs); return ret; out_usage: usage(CMD_DIR, stderr); ret = -1; - goto out; + goto out_free_refglobs; } /* Exports one, or all, images from a WIM file to a new WIM file or an existing @@ -2691,7 +2869,7 @@ imagex_export(int argc, tchar **argv, int cmd) int image; struct stat stbuf; bool wim_is_new; - STRING_SET(refglobs); + STRING_LIST(refglobs); unsigned num_threads = 0; uint32_t chunk_size = UINT32_MAX; uint32_t solid_chunk_size = UINT32_MAX; @@ -2710,14 +2888,10 @@ imagex_export(int argc, tchar **argv, int cmd) write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY; break; case IMAGEX_COMPRESS_OPTION: - compression_type = get_compression_type(optarg); + compression_type = get_compression_type(optarg, false); if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) goto out_err; break; - case IMAGEX_COMPRESS_SLOW_OPTION: - set_compress_slow(); - write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS; - break; case IMAGEX_RECOMPRESS_OPTION: write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS; break; @@ -2738,12 +2912,12 @@ imagex_export(int argc, tchar **argv, int cmd) goto out_err; break; case IMAGEX_SOLID_COMPRESS_OPTION: - solid_ctype = get_compression_type(optarg); + solid_ctype = get_compression_type(optarg, true); if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID) goto out_err; break; case IMAGEX_REF_OPTION: - ret = string_set_append(&refglobs, optarg); + ret = string_list_append(&refglobs, optarg); if (ret) goto out_free_refglobs; break; @@ -2764,6 +2938,9 @@ imagex_export(int argc, tchar **argv, int cmd) case IMAGEX_WIMBOOT_OPTION: export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT; break; + case IMAGEX_UNSAFE_COMPACT_OPTION: + write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT; + break; default: goto out_usage; } @@ -2804,7 +2981,7 @@ imagex_export(int argc, tchar **argv, int cmd) #endif dest_wimfile = NULL; dest_wim_fd = STDOUT_FILENO; - imagex_info_file = stderr; + imagex_output_to_stderr(); set_fd_to_binary_mode(dest_wim_fd); } errno = ENOENT; @@ -2812,9 +2989,9 @@ imagex_export(int argc, tchar **argv, int cmd) wim_is_new = false; /* Destination file exists. */ - if (!S_ISREG(stbuf.st_mode)) { - imagex_error(T("\"%"TS"\" is not a regular file"), - dest_wimfile); + if (!S_ISREG(stbuf.st_mode) && !S_ISBLK(stbuf.st_mode)) { + imagex_error(T("\"%"TS"\" is not a regular file " + "or block device"), dest_wimfile); ret = -1; goto out_free_src_wim; } @@ -2853,6 +3030,13 @@ imagex_export(int argc, tchar **argv, int cmd) goto out_free_src_wim; } + if (write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) { + imagex_error(T("'--unsafe-compact' is only valid when " + "exporting to an existing WIM file!")); + ret = -1; + goto out_free_src_wim; + } + /* dest_wimfile is not an existing file, so create a new WIM. */ if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) { @@ -2930,6 +3114,8 @@ imagex_export(int argc, tchar **argv, int cmd) if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) { do_resource_not_found_warning(src_wimfile, &src_info, &refglobs); + } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) { + do_metadata_not_found_warning(src_wimfile, &src_info); } goto out_free_dest_wim; } @@ -2948,7 +3134,7 @@ out_free_dest_wim: out_free_src_wim: wimlib_free(src_wim); out_free_refglobs: - string_set_destroy(&refglobs); + string_list_destroy(&refglobs); return ret; out_usage: @@ -2975,7 +3161,7 @@ imagex_extract(int argc, tchar **argv, int cmd) WIMLIB_EXTRACT_FLAG_STRICT_GLOB; int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE; - STRING_SET(refglobs); + STRING_LIST(refglobs); tchar *root_path = WIMLIB_WIM_ROOT_PATH; @@ -2988,7 +3174,7 @@ imagex_extract(int argc, tchar **argv, int cmd) /* No longer does anything. */ break; case IMAGEX_REF_OPTION: - ret = string_set_append(&refglobs, optarg); + ret = string_list_append(&refglobs, optarg); if (ret) goto out_free_refglobs; break; @@ -3009,8 +3195,7 @@ imagex_extract(int argc, tchar **argv, int cmd) break; case IMAGEX_TO_STDOUT_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT; - imagex_info_file = stderr; - imagex_be_quiet = true; + imagex_suppress_output(); set_fd_to_binary_mode(STDOUT_FILENO); break; case IMAGEX_INCLUDE_INVALID_NAMES_OPTION: @@ -3029,6 +3214,11 @@ imagex_extract(int argc, tchar **argv, int cmd) case IMAGEX_WIMBOOT_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT; break; + case IMAGEX_COMPACT_OPTION: + ret = set_compact_mode(optarg, &extract_flags); + if (ret) + goto out_free_refglobs; + break; default: goto out_usage; } @@ -3101,8 +3291,7 @@ imagex_extract(int argc, tchar **argv, int cmd) } if (ret == 0) { - if (!imagex_be_quiet) - imagex_printf(T("Done extracting files.\n")); + imagex_printf(T("Done extracting files.\n")); } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) { if ((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_GLOB | WIMLIB_EXTRACT_FLAG_GLOB_PATHS)) @@ -3122,11 +3311,16 @@ imagex_extract(int argc, tchar **argv, int cmd) wimlib_get_wim_info(wim, &info); do_resource_not_found_warning(wimfile, &info, &refglobs); + } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) { + struct wimlib_wim_info info; + + wimlib_get_wim_info(wim, &info); + do_metadata_not_found_warning(wimfile, &info); } out_wimlib_free: wimlib_free(wim); out_free_refglobs: - string_set_destroy(&refglobs); + string_list_destroy(&refglobs); return ret; out_usage: @@ -3152,8 +3346,7 @@ imagex_info(int argc, tchar **argv, int cmd) const tchar *xml_out_file = NULL; const tchar *wimfile; const tchar *image_num_or_name; - const tchar *new_name; - const tchar *new_desc; + STRING_LIST(image_properties); WIMStruct *wim; int image; int ret; @@ -3187,10 +3380,11 @@ imagex_info(int argc, tchar **argv, int cmd) xml_out_file = optarg; short_header = false; break; - case IMAGEX_METADATA_OPTION: - imagex_error(T("The --metadata option has been removed. " - "Use 'wimdir --detail' instead.")); - goto out_err; + case IMAGEX_IMAGE_PROPERTY_OPTION: + ret = append_image_property_argument(&image_properties); + if (ret) + goto out; + break; default: goto out_usage; } @@ -3203,8 +3397,24 @@ imagex_info(int argc, tchar **argv, int cmd) wimfile = argv[0]; image_num_or_name = (argc >= 2) ? argv[1] : T("all"); - new_name = (argc >= 3) ? argv[2] : NULL; - new_desc = (argc >= 4) ? argv[3] : NULL; + + if (argc >= 3) { + /* NEW_NAME */ + tchar *p = alloca((5 + tstrlen(argv[2]) + 1) * sizeof(tchar)); + tsprintf(p, T("NAME=%"TS), argv[2]); + ret = string_list_append(&image_properties, p); + if (ret) + goto out; + } + + if (argc >= 4) { + /* NEW_DESC */ + tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar)); + tsprintf(p, T("DESCRIPTION=%"TS), argv[3]); + ret = string_list_append(&image_properties, p); + if (ret) + goto out; + } if (check && nocheck) { imagex_error(T("Can't specify both --check and --nocheck")); @@ -3245,17 +3455,17 @@ imagex_info(int argc, tchar **argv, int cmd) "image in a multi-image WIM")); goto out_wimlib_free; } - if (new_name) { - imagex_error(T("Cannot specify the NEW_NAME " - "without specifying a specific " - "image in a multi-image WIM")); + if (image_properties.num_strings) { + imagex_error(T("Can't change image properties without " + "specifying a specific image in a " + "multi-image WIM")); goto out_wimlib_free; } } /* Operations that print information are separated from operations that * recreate the WIM file. */ - if (!new_name && !boot) { + if (!image_properties.num_strings && !boot) { /* Read-only operations */ @@ -3314,15 +3524,15 @@ imagex_info(int argc, tchar **argv, int cmd) ret = 0; } else { - /* Modification operations */ + bool any_property_changes; if (image == WIMLIB_ALL_IMAGES) image = 1; - if (image == WIMLIB_NO_IMAGE && new_name) { - imagex_error(T("Cannot specify new_name (\"%"TS"\") " - "when using image 0"), new_name); + if (image == WIMLIB_NO_IMAGE && image_properties.num_strings) { + imagex_error(T("Cannot change image properties " + "when using image 0")); ret = -1; goto out_wimlib_free; } @@ -3342,40 +3552,15 @@ imagex_info(int argc, tchar **argv, int cmd) goto out_wimlib_free; } } - if (new_name) { - if (!tstrcmp(wimlib_get_image_name(wim, image), new_name)) - { - imagex_printf(T("Image %d is already named \"%"TS"\".\n"), - image, new_name); - new_name = NULL; - } else { - imagex_printf(T("Changing the name of image %d to " - "\"%"TS"\".\n"), image, new_name); - ret = wimlib_set_image_name(wim, image, new_name); - if (ret) - goto out_wimlib_free; - } - } - if (new_desc) { - const tchar *old_desc; - old_desc = wimlib_get_image_description(wim, image); - if (old_desc && !tstrcmp(old_desc, new_desc)) { - imagex_printf(T("The description of image %d is already " - "\"%"TS"\".\n"), image, new_desc); - new_desc = NULL; - } else { - imagex_printf(T("Changing the description of image %d " - "to \"%"TS"\".\n"), image, new_desc); - ret = wimlib_set_image_descripton(wim, image, - new_desc); - if (ret) - goto out_wimlib_free; - } - } + + ret = apply_image_properties(&image_properties, wim, image, + &any_property_changes); + if (ret) + goto out_wimlib_free; /* Only call wimlib_overwrite() if something actually needs to * be changed. */ - if (boot || new_name || new_desc || + if (boot || any_property_changes || (check && !info.has_integrity_table) || (nocheck && info.has_integrity_table)) { @@ -3396,6 +3581,7 @@ imagex_info(int argc, tchar **argv, int cmd) out_wimlib_free: wimlib_free(wim); out: + string_list_destroy(&image_properties); return ret; out_usage: @@ -3467,7 +3653,7 @@ imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd) int image; int ret; - STRING_SET(refglobs); + STRING_LIST(refglobs); if (cmd == CMD_MOUNTRW) { mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE; @@ -3499,7 +3685,7 @@ imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd) } break; case IMAGEX_REF_OPTION: - ret = string_set_append(&refglobs, optarg); + ret = string_list_append(&refglobs, optarg); if (ret) goto out_free_refglobs; break; @@ -3556,14 +3742,18 @@ imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd) ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir); if (ret) { - imagex_error(T("Failed to mount image %d from \"%"TS"\" " - "on \"%"TS"\""), - image, wimfile, dir); + if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) { + do_metadata_not_found_warning(wimfile, &info); + } else { + imagex_error(T("Failed to mount image %d from \"%"TS"\" " + "on \"%"TS"\""), + image, wimfile, dir); + } } out_free_wim: wimlib_free(wim); out_free_refglobs: - string_set_destroy(&refglobs); + string_list_destroy(&refglobs); return ret; out_usage: @@ -3602,14 +3792,10 @@ imagex_optimize(int argc, tchar **argv, int cmd) break; case IMAGEX_COMPRESS_OPTION: write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS; - compression_type = get_compression_type(optarg); + compression_type = get_compression_type(optarg, false); if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) goto out_err; break; - case IMAGEX_COMPRESS_SLOW_OPTION: - set_compress_slow(); - write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS; - break; case IMAGEX_RECOMPRESS_OPTION: write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS; break; @@ -3624,7 +3810,7 @@ imagex_optimize(int argc, tchar **argv, int cmd) goto out_err; break; case IMAGEX_SOLID_COMPRESS_OPTION: - solid_ctype = get_compression_type(optarg); + solid_ctype = get_compression_type(optarg, true); if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID) goto out_err; break; @@ -3646,6 +3832,9 @@ imagex_optimize(int argc, tchar **argv, int cmd) case IMAGEX_NOT_PIPABLE_OPTION: write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE; break; + case IMAGEX_UNSAFE_COMPACT_OPTION: + write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT; + break; default: goto out_usage; } @@ -3943,6 +4132,9 @@ imagex_update(int argc, tchar **argv, int cmd) case IMAGEX_NO_REPLACE_OPTION: default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE; break; + case IMAGEX_UNSAFE_COMPACT_OPTION: + write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT; + break; default: goto out_usage; } @@ -4080,13 +4272,13 @@ imagex_verify(int argc, tchar **argv, int cmd) WIMStruct *wim; int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; int verify_flags = 0; - STRING_SET(refglobs); + STRING_LIST(refglobs); int c; for_opt(c, verify_options) { switch (c) { case IMAGEX_REF_OPTION: - ret = string_set_append(&refglobs, optarg); + ret = string_list_append(&refglobs, optarg); if (ret) goto out_free_refglobs; break; @@ -4142,7 +4334,7 @@ imagex_verify(int argc, tchar **argv, int cmd) out_wimlib_free: wimlib_free(wim); out_free_refglobs: - string_set_destroy(&refglobs); + string_list_destroy(&refglobs); return ret; out_usage: @@ -4197,14 +4389,15 @@ static const struct imagex_command imagex_commands[] = { #endif -static const tchar *usage_strings[] = { +static const tchar * const usage_strings[] = { [CMD_APPEND] = T( " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n" " [--boot] [--check] [--nocheck] [--config=FILE]\n" " [--threads=NUM_THREADS] [--no-acls] [--strict-acls]\n" " [--rpfix] [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n" -" [--wimboot] [--unix-data] [--dereference]\n" +" [--delta-from=WIMFILE] [--wimboot] [--unix-data]\n" +" [--dereference] [--snapshot]\n" ), [CMD_APPLY] = T( @@ -4212,6 +4405,7 @@ T( " [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n" " [--no-attributes] [--rpfix] [--norpfix]\n" " [--include-invalid-names] [--wimboot] [--unix-data]\n" +" [--compact=FORMAT]\n" ), [CMD_CAPTURE] = T( @@ -4221,6 +4415,7 @@ T( " [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n" " [--update-of=[WIMFILE:]IMAGE] [--delta-from=WIMFILE]\n" " [--wimboot] [--unix-data] [--dereference] [--solid]\n" +" [--snapshot]\n" ), [CMD_DELETE] = T( @@ -4228,7 +4423,7 @@ T( ), [CMD_DIR] = T( -" %"TS" WIMFILE IMAGE [--path=PATH] [--detailed]\n" +" %"TS" WIMFILE [IMAGE] [--path=PATH] [--detailed]\n" ), [CMD_EXPORT] = T( @@ -4251,6 +4446,7 @@ T( " %"TS" WIMFILE [IMAGE [NEW_NAME [NEW_DESC]]]\n" " [--boot] [--check] [--nocheck] [--xml]\n" " [--extract-xml FILE] [--header] [--blobs]\n" +" [--image-property NAME=VALUE]\n" ), [CMD_JOIN] = T( @@ -4305,21 +4501,18 @@ T( static const tchar *invocation_name; static int invocation_cmd = CMD_NONE; -static const tchar *get_cmd_string(int cmd, bool nospace) +static const tchar *get_cmd_string(int cmd, bool only_short_form) { static tchar buf[50]; - if (cmd == CMD_NONE) { + + if (cmd == CMD_NONE) return T("wimlib-imagex"); - } else if (invocation_cmd != CMD_NONE) { + + if (only_short_form || invocation_cmd != CMD_NONE) { tsprintf(buf, T("wim%"TS), imagex_commands[cmd].name); } else { - const tchar *format; - - if (nospace) - format = T("%"TS"-%"TS""); - else - format = T("%"TS" %"TS""); - tsprintf(buf, format, invocation_name, imagex_commands[cmd].name); + tsprintf(buf, T("%"TS" %"TS), invocation_name, + imagex_commands[cmd].name); } return buf; } @@ -4327,23 +4520,27 @@ static const tchar *get_cmd_string(int cmd, bool nospace) static void version(void) { - static const tchar *s = + uint32_t vers = wimlib_get_version(); + + static const tchar * const fmt = T( -"wimlib-imagex (distributed with " PACKAGE " " PACKAGE_VERSION ")\n" -"Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers\n" +"wimlib-imagex " PACKAGE_VERSION " (using wimlib %u.%u.%u)\n" +"Copyright (C) 2012-2017 Eric Biggers\n" "License GPLv3+; GNU GPL version 3 or later .\n" "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n" "\n" "Report bugs to "PACKAGE_BUGREPORT".\n" ); - tfputs(s, stdout); + tfprintf(stdout, fmt, + vers >> 20, (vers >> 10) & 0x3ff, vers & 0x3ff); } static void -help_or_version(int argc, tchar **argv, int cmd) +do_common_options(int *argc_p, tchar **argv, int cmd) { + int argc = *argc_p; int i; const tchar *p; @@ -4360,9 +4557,18 @@ help_or_version(int argc, tchar **argv, int cmd) } else if (!tstrcmp(p, T("version"))) { version(); exit(0); - } + } else if (!tstrcmp(p, T("quiet"))) { + imagex_suppress_output(); + memmove(&argv[i], &argv[i + 1], + (argc - i) * sizeof(argv[i])); + argc--; + i--; + } else if (!*p) /* reached "--", no more options */ + break; } } + + *argc_p = argc; } static void @@ -4379,8 +4585,7 @@ recommend_man_page(int cmd, FILE *fp) format_str = T("Some uncommon options are not listed;\n" "See %"TS".pdf in the doc directory for more details.\n"); #else - format_str = T("Some uncommon options are not listed;\n" - "Try `man %"TS"' for more details.\n"); + format_str = T("Some uncommon options are not listed; see `man %"TS"' for more details.\n"); #endif tfprintf(fp, format_str, get_cmd_string(cmd, true)); } @@ -4402,7 +4607,7 @@ usage_all(FILE *fp) print_usage_string(cmd, fp); tfprintf(fp, T("\n")); } - static const tchar *extra = + static const tchar * const extra = T( " %"TS" --help\n" " %"TS" --version\n" @@ -4417,16 +4622,17 @@ usage_all(FILE *fp) recommend_man_page(CMD_NONE, fp); } +#ifdef __WIN32__ +extern int wmain(int argc, wchar_t **argv); +#define main wmain +#endif + /* Entry point for wimlib's ImageX implementation. On UNIX the command * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be * something else), while on Windows the command arguments will be UTF-16LE * encoded 'wchar_t' strings. */ int -#ifdef __WIN32__ -wmain(int argc, wchar_t **argv, wchar_t **envp) -#else -main(int argc, char **argv) -#endif +main(int argc, tchar **argv) { int ret; int init_flags = 0; @@ -4435,31 +4641,6 @@ main(int argc, char **argv) imagex_info_file = stdout; invocation_name = tbasename(argv[0]); -#ifndef __WIN32__ - if (getenv("WIMLIB_IMAGEX_USE_UTF8")) { - init_flags |= WIMLIB_INIT_FLAG_ASSUME_UTF8; - } else { - char *codeset; - - setlocale(LC_ALL, ""); - codeset = nl_langinfo(CODESET); - if (!strstr(codeset, "UTF-8") && - !strstr(codeset, "UTF8") && - !strstr(codeset, "utf-8") && - !strstr(codeset, "utf8")) - { - fprintf(stderr, -"WARNING: Running %"TS" in a UTF-8 locale is recommended!\n" -" Maybe try: `export LANG=en_US.UTF-8'?\n" -" Alternatively, set the environmental variable WIMLIB_IMAGEX_USE_UTF8\n" -" to any value to force wimlib to use UTF-8.\n", - invocation_name); - - } - } - -#endif /* !__WIN32__ */ - { tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE")); if (igcase != NULL) { @@ -4512,11 +4693,8 @@ main(int argc, char **argv) } } - /* Handle --help and --version. --help can be either for the program as - * a whole (cmd == CMD_NONE) or just for a specific command (cmd != - * CMD_NONE). Note: help_or_version() will not return if a --help or - * --version argument was found. */ - help_or_version(argc, argv, cmd); + /* Handle common options. May exit early (for --help or --version). */ + do_common_options(&argc, argv, cmd); /* Bail if a valid command was not specified. */ if (cmd == CMD_NONE) {