X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=programs%2Fimagex.c;h=2d407fac422b885dd6a4882b51abe25f93aec54c;hp=4abcbac2a9e7ce353d92b56350c0754fbe8e10fa;hb=50a62891f75afdbfde97031125fbaccd4ed0419c;hpb=415bd4fd3ec1d99adb26b1f76b897aab07a4c27f diff --git a/programs/imagex.c b/programs/imagex.c index 4abcbac2..2d407fac 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2012-2016 Eric Biggers + * Copyright (C) 2012-2018 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 @@ -58,6 +58,10 @@ static inline void set_fd_to_binary_mode(int fd) { } +/* NetBSD is missing getopt_long_only() but has getopt_long() */ +#ifndef HAVE_GETOPT_LONG_ONLY +# define getopt_long_only getopt_long +#endif #endif /* !__WIN32 */ /* Don't confuse the user by presenting the mounting commands on Windows when @@ -131,14 +135,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, @@ -150,6 +171,7 @@ enum { IMAGEX_COMPACT_OPTION, IMAGEX_COMPRESS_OPTION, IMAGEX_CONFIG_OPTION, + IMAGEX_CREATE_OPTION, IMAGEX_DEBUG_OPTION, IMAGEX_DELTA_FROM_OPTION, IMAGEX_DEREFERENCE_OPTION, @@ -160,6 +182,7 @@ enum { IMAGEX_FORCE_OPTION, IMAGEX_HEADER_OPTION, IMAGEX_IMAGE_PROPERTY_OPTION, + IMAGEX_INCLUDE_INTEGRITY_OPTION, IMAGEX_INCLUDE_INVALID_NAMES_OPTION, IMAGEX_LAZY_OPTION, IMAGEX_METADATA_OPTION, @@ -224,6 +247,7 @@ static const struct option capture_or_append_options[] = { {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, + {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION}, {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION}, {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION}, {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION}, @@ -251,11 +275,13 @@ static const struct option capture_or_append_options[] = { {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}, + {T("create"), no_argument, NULL, IMAGEX_CREATE_OPTION}, {NULL, 0, NULL, 0}, }; static const struct option delete_options[] = { {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, + {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION}, {T("soft"), no_argument, NULL, IMAGEX_SOFT_OPTION}, {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION}, {NULL, 0, NULL, 0}, @@ -274,6 +300,7 @@ static const struct option export_options[] = { {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, + {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION}, {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION}, {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION}, {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION}, @@ -317,6 +344,7 @@ static const struct option info_options[] = { {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, + {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION}, {T("extract-xml"), required_argument, NULL, IMAGEX_EXTRACT_XML_OPTION}, {T("header"), no_argument, NULL, IMAGEX_HEADER_OPTION}, {T("lookup-table"), no_argument, NULL, IMAGEX_BLOBS_OPTION}, @@ -328,9 +356,11 @@ static const struct option info_options[] = { static const struct option join_options[] = { {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, + {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION}, {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}, @@ -341,11 +371,13 @@ 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}, {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, + {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION}, {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION}, {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION}, {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION}, @@ -362,9 +394,11 @@ static const struct option optimize_options[] = { static const struct option split_options[] = { {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, + {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION}, {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}, @@ -374,6 +408,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 @@ -382,6 +417,7 @@ static const struct option update_options[] = { * update_command_add_option(). */ {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION}, {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, + {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION}, {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION}, {T("command"), required_argument, NULL, IMAGEX_COMMAND_OPTION}, {T("wimboot-config"), required_argument, NULL, IMAGEX_WIMBOOT_CONFIG_OPTION}, @@ -486,7 +522,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" @@ -586,68 +622,68 @@ set_compact_mode(const tchar *arg, int *extract_flags) } -struct string_set { +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, 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) { + 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, (const tchar **)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_set *image_properties) +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_set_append(image_properties, optarg); + return string_list_append(image_properties, optarg); } static int -apply_image_properties(struct string_set *image_properties, +apply_image_properties(struct string_list *image_properties, WIMStruct *wim, int image, bool *any_changes_ret) { bool any_changes = false; @@ -683,7 +719,7 @@ apply_image_properties(struct string_set *image_properties, 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) { @@ -1137,8 +1173,6 @@ 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: { @@ -1377,7 +1411,7 @@ imagex_progress_func(enum wimlib_progress_msg msg, default: break; } - fflush(imagex_info_file); + imagex_flush_output(); return WIMLIB_PROGRESS_STATUS_CONTINUE; } @@ -1654,7 +1688,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) { @@ -1665,7 +1699,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; @@ -1812,7 +1846,7 @@ imagex_apply(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: @@ -1821,14 +1855,18 @@ out_usage: goto out_free_refglobs; } -/* Create a WIM image from a directory tree, NTFS volume, or multiple files or - * directory trees. 'wimlib-imagex capture': create a new WIM file containing - * the desired image. 'wimlib-imagex append': add a new image to an existing - * WIM file. */ +/* + * Create a WIM image from a directory tree, NTFS volume, or multiple files or + * directory trees. 'wimcapture': create a new WIM file containing the desired + * image. 'wimappend': add a new image to an existing WIM file; or, with + * '--create' behave like 'wimcapture' if the WIM file doesn't exist. + */ static int imagex_capture_or_append(int argc, tchar **argv, int cmd) { int c; + bool create = false; + bool appending = (cmd == CMD_APPEND); int open_flags = 0; int add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE | WIMLIB_ADD_FLAG_WINCONFIG | @@ -1842,13 +1880,13 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) const tchar *wimfile; int wim_fd; const tchar *name; - STRING_SET(image_properties); + STRING_LIST(image_properties); WIMStruct *wim; - STRING_SET(base_wimfiles); + STRING_LIST(base_wimfiles); WIMStruct **base_wims; - WIMStruct *template_wim; + WIMStruct *template_wim = NULL; const tchar *template_wimfile = NULL; const tchar *template_image_name_or_num = NULL; int template_image = WIMLIB_NO_IMAGE; @@ -1876,6 +1914,8 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) break; case IMAGEX_CHECK_OPTION: open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; + /* fall-through */ + case IMAGEX_INCLUDE_INTEGRITY_OPTION: write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; case IMAGEX_NOCHECK_OPTION: @@ -1914,7 +1954,7 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) case IMAGEX_FLAGS_OPTION: { tchar *p = alloca((6 + tstrlen(optarg) + 1) * sizeof(tchar)); tsprintf(p, T("FLAGS=%"TS), optarg); - ret = string_set_append(&image_properties, p); + ret = string_list_append(&image_properties, p); if (ret) goto out; break; @@ -1980,14 +2020,12 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) template_image_name_or_num = optarg; } } + #ifdef __WIN32__ + imagex_printf(T("[WARNING] '--update-of' is unreliable on Windows!\n")); + #endif 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; write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS; @@ -1996,16 +2034,18 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) 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; + case IMAGEX_CREATE_OPTION: + if (cmd == CMD_CAPTURE) { + imagex_error(T("'--create' is only valid for 'wimappend', not 'wimcapture'")); + goto out_err; + } + create = true; + break; default: goto out_usage; } @@ -2040,6 +2080,8 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) if (!tstrcmp(wimfile, T("-"))) { /* Writing captured WIM to standard output. */ + if (create) + appending = false; #if 0 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) { imagex_error("Can't write a non-pipable WIM to " @@ -2051,15 +2093,37 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) #else write_flags |= WIMLIB_WRITE_FLAG_PIPABLE; #endif - if (cmd == CMD_APPEND) { + if (appending) { imagex_error(T("Using standard output for append does " "not make sense.")); goto out_err; } wim_fd = STDOUT_FILENO; wimfile = NULL; - imagex_info_file = stderr; + imagex_output_to_stderr(); set_fd_to_binary_mode(wim_fd); + } else { + struct stat stbuf; + + /* Check for 'wimappend --create' acting as wimcapture */ + if (create && tstat(wimfile, &stbuf) != 0 && errno == ENOENT) { + + appending = false; + + /* Ignore '--update-of' for the target WIMFILE */ + if (template_image_name_or_num && + (!template_wimfile || + !tstrcmp(template_wimfile, wimfile))) + { + template_image_name_or_num = NULL; + template_wimfile = NULL; + } + } + } + + if ((write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) && !appending) { + imagex_error(T("'--unsafe-compact' is only valid for append!")); + goto out_err; } /* If template image was specified using --update-of=IMAGE rather @@ -2069,7 +2133,7 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) /* Capturing delta WIM based on single WIM: default to * base WIM. */ template_wimfile = base_wimfiles.strings[0]; - } else if (cmd == CMD_APPEND) { + } else if (appending) { /* Appending to WIM: default to WIM being appended to. */ template_wimfile = wimfile; @@ -2111,7 +2175,7 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) if (argc >= 4) { tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar)); tsprintf(p, T("DESCRIPTION=%"TS), argv[3]); - ret = string_set_append(&image_properties, p); + ret = string_list_append(&image_properties, p); if (ret) goto out; } @@ -2147,7 +2211,7 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) } /* Open the existing WIM, or create a new one. */ - if (cmd == CMD_APPEND) { + if (appending) { ret = wimlib_open_wim_with_progress(wimfile, open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS, &wim, @@ -2171,7 +2235,7 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) int ctype = compression_type; - if (cmd == CMD_APPEND) { + if (appending) { struct wimlib_wim_info info; wimlib_get_wim_info(wim, &info); ctype = info.compression_type; @@ -2220,7 +2284,7 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) /* 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. */ - if (cmd == CMD_APPEND && name_defaulted) { + if (appending && name_defaulted) { unsigned long conflict_idx; tchar *name_end = tstrchr(name, T('\0')); for (conflict_idx = 1; @@ -2272,13 +2336,19 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) * open the WIM if needed and parse the image index. */ if (template_image_name_or_num) { - - if (base_wimfiles.num_strings == 1 && - template_wimfile == base_wimfiles.strings[0]) { - template_wim = base_wims[0]; - } else if (template_wimfile == wimfile) { + if (appending && !tstrcmp(template_wimfile, wimfile)) { template_wim = wim; } else { + for (size_t i = 0; i < base_wimfiles.num_strings; i++) { + if (!tstrcmp(template_wimfile, + base_wimfiles.strings[i])) { + template_wim = base_wims[i]; + break; + } + } + } + + if (!template_wim) { ret = wimlib_open_wim_with_progress(template_wimfile, open_flags, &template_wim, @@ -2310,8 +2380,6 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) template_wimfile); if (ret) goto out_free_template_wim; - } else { - template_wim = NULL; } ret = wimlib_add_image_multisource(wim, @@ -2353,7 +2421,7 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) /* Write the new WIM or overwrite the existing WIM with the new image * appended. */ - if (cmd == CMD_APPEND) { + if (appending) { ret = wimlib_overwrite(wim, write_flags, num_threads); } else if (wimfile) { ret = wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES, @@ -2363,10 +2431,13 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) write_flags, num_threads); } out_free_template_wim: - /* template_wim may alias base_wims[0] or wim. */ - if ((base_wimfiles.num_strings != 1 || template_wim != base_wims[0]) && - template_wim != wim) - wimlib_free(template_wim); + /* 'template_wim' may alias 'wim' or any of the 'base_wims' */ + if (template_wim == wim) + goto out_free_base_wims; + for (size_t i = 0; i < base_wimfiles.num_strings; i++) + if (template_wim == base_wims[i]) + goto out_free_base_wims; + wimlib_free(template_wim); out_free_base_wims: for (size_t i = 0; i < base_wimfiles.num_strings; i++) wimlib_free(base_wims[i]); @@ -2379,8 +2450,8 @@ out_free_capture_sources: out_free_source_list_contents: free(source_list_contents); out: - string_set_destroy(&image_properties); - string_set_destroy(&base_wimfiles); + string_list_destroy(&image_properties); + string_list_destroy(&base_wimfiles); return ret; out_usage: @@ -2407,6 +2478,8 @@ imagex_delete(int argc, tchar **argv, int cmd) switch (c) { case IMAGEX_CHECK_OPTION: open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; + /* fall-through */ + case IMAGEX_INCLUDE_INTEGRITY_OPTION: write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; case IMAGEX_SOFT_OPTION: @@ -2500,21 +2573,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); } @@ -2638,10 +2711,27 @@ default_print_security_descriptor(const uint8_t *sd, size_t size) } #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_dentry_detailed(const struct wimlib_dir_entry *dentry) +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); @@ -2658,9 +2748,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) @@ -2676,6 +2769,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"), @@ -2717,7 +2817,7 @@ imagex_dir(int argc, tchar **argv, int cmd) }; int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE; - STRING_SET(refglobs); + STRING_LIST(refglobs); for_opt(c, dir_options) { switch (c) { @@ -2731,7 +2831,7 @@ imagex_dir(int argc, tchar **argv, int cmd) iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE; break; case IMAGEX_REF_OPTION: - ret = string_set_append(&refglobs, optarg); + ret = string_list_append(&refglobs, optarg); if (ret) goto out_free_refglobs; break; @@ -2796,7 +2896,7 @@ imagex_dir(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: @@ -2828,7 +2928,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; @@ -2841,6 +2941,8 @@ imagex_export(int argc, tchar **argv, int cmd) break; case IMAGEX_CHECK_OPTION: open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; + /* fall-through */ + case IMAGEX_INCLUDE_INTEGRITY_OPTION: write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; case IMAGEX_NOCHECK_OPTION: @@ -2876,7 +2978,7 @@ imagex_export(int argc, tchar **argv, int cmd) 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; @@ -2940,7 +3042,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; @@ -2948,9 +3050,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; } @@ -3093,7 +3195,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: @@ -3120,7 +3222,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; @@ -3133,7 +3235,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; @@ -3154,8 +3256,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: @@ -3242,17 +3343,22 @@ imagex_extract(int argc, tchar **argv, int cmd) argc -= num_paths; argv += num_paths; } else { + const tchar *listfile = argv[0] + 1; + + if (!tstrcmp(listfile, T("-"))) { + tputs(T("Reading pathlist file from standard input...")); + listfile = NULL; + } + ret = wimlib_extract_pathlist(wim, image, dest_dir, - argv[0] + 1, - extract_flags); + listfile, extract_flags); argc--; argv++; } } 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)) @@ -3281,7 +3387,7 @@ imagex_extract(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: @@ -3298,8 +3404,6 @@ imagex_info(int argc, tchar **argv, int cmd) { int c; bool boot = false; - bool check = false; - bool nocheck = false; bool header = false; bool blobs = false; bool xml = false; @@ -3307,11 +3411,12 @@ imagex_info(int argc, tchar **argv, int cmd) const tchar *xml_out_file = NULL; const tchar *wimfile; const tchar *image_num_or_name; - STRING_SET(image_properties); + STRING_LIST(image_properties); WIMStruct *wim; int image; int ret; int open_flags = 0; + int write_flags = 0; struct wimlib_wim_info info; for_opt(c, info_options) { @@ -3320,10 +3425,13 @@ imagex_info(int argc, tchar **argv, int cmd) boot = true; break; case IMAGEX_CHECK_OPTION: - check = true; + open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; + /* fall-through */ + case IMAGEX_INCLUDE_INTEGRITY_OPTION: + write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; case IMAGEX_NOCHECK_OPTION: - nocheck = true; + write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY; break; case IMAGEX_HEADER_OPTION: header = true; @@ -3363,7 +3471,7 @@ imagex_info(int argc, tchar **argv, int cmd) /* NEW_NAME */ tchar *p = alloca((5 + tstrlen(argv[2]) + 1) * sizeof(tchar)); tsprintf(p, T("NAME=%"TS), argv[2]); - ret = string_set_append(&image_properties, p); + ret = string_list_append(&image_properties, p); if (ret) goto out; } @@ -3372,19 +3480,11 @@ imagex_info(int argc, tchar **argv, int cmd) /* NEW_DESC */ tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar)); tsprintf(p, T("DESCRIPTION=%"TS), argv[3]); - ret = string_set_append(&image_properties, p); + ret = string_list_append(&image_properties, p); if (ret) goto out; } - if (check && nocheck) { - imagex_error(T("Can't specify both --check and --nocheck")); - goto out_err; - } - - if (check) - open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; - ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim, imagex_progress_func, NULL); if (ret) @@ -3522,15 +3622,11 @@ imagex_info(int argc, tchar **argv, int cmd) /* Only call wimlib_overwrite() if something actually needs to * be changed. */ if (boot || any_property_changes || - (check && !info.has_integrity_table) || - (nocheck && info.has_integrity_table)) + ((write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) && + !info.has_integrity_table) || + ((write_flags & WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY) && + info.has_integrity_table)) { - int write_flags = 0; - - if (check) - write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; - if (nocheck) - write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY; ret = wimlib_overwrite(wim, write_flags, 1); } else { imagex_printf(T("The file \"%"TS"\" was not modified " @@ -3542,12 +3638,11 @@ imagex_info(int argc, tchar **argv, int cmd) out_wimlib_free: wimlib_free(wim); out: - string_set_destroy(&image_properties); + string_list_destroy(&image_properties); return ret; out_usage: usage(CMD_INFO, stderr); -out_err: ret = -1; goto out; } @@ -3566,6 +3661,8 @@ imagex_join(int argc, tchar **argv, int cmd) switch (c) { case IMAGEX_CHECK_OPTION: swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; + /* fall-through */ + case IMAGEX_INCLUDE_INTEGRITY_OPTION: wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; default: @@ -3614,7 +3711,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; @@ -3646,7 +3743,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; @@ -3714,7 +3811,7 @@ imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd) out_free_wim: wimlib_free(wim); out_free_refglobs: - string_set_destroy(&refglobs); + string_list_destroy(&refglobs); return ret; out_usage: @@ -3746,6 +3843,8 @@ imagex_optimize(int argc, tchar **argv, int cmd) switch (c) { case IMAGEX_CHECK_OPTION: open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; + /* fall-through */ + case IMAGEX_INCLUDE_INTEGRITY_OPTION: write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; case IMAGEX_NOCHECK_OPTION: @@ -3893,6 +3992,8 @@ imagex_split(int argc, tchar **argv, int cmd) switch (c) { case IMAGEX_CHECK_OPTION: open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; + /* fall-through */ + case IMAGEX_INCLUDE_INTEGRITY_OPTION: write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; default: @@ -4038,6 +4139,8 @@ imagex_update(int argc, tchar **argv, int cmd) break; case IMAGEX_CHECK_OPTION: open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; + /* fall-through */ + case IMAGEX_INCLUDE_INTEGRITY_OPTION: write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; case IMAGEX_REBUILD_OPTION: @@ -4233,13 +4336,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; @@ -4295,7 +4398,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: @@ -4350,14 +4453,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] [--snapshot]\n" +" [--delta-from=WIMFILE] [--wimboot] [--unix-data]\n" +" [--dereference] [--snapshot] [--create]\n" ), [CMD_APPLY] = T( @@ -4383,7 +4487,7 @@ T( ), [CMD_DIR] = T( -" %"TS" WIMFILE IMAGE [--path=PATH] [--detailed]\n" +" %"TS" WIMFILE [IMAGE] [--path=PATH] [--detailed]\n" ), [CMD_EXPORT] = T( @@ -4461,21 +4565,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; } @@ -4483,23 +4584,23 @@ static const tchar *get_cmd_string(int cmd, bool nospace) static void version(void) { - static const tchar *s = + static const tchar * const fmt = T( -"wimlib-imagex (distributed with " PACKAGE " " PACKAGE_VERSION ")\n" -"Copyright (C) 2012-2016 Eric Biggers\n" +"wimlib-imagex " PACKAGE_VERSION " (using wimlib %"TS")\n" +"Copyright (C) 2012-2018 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, wimlib_get_version_string()); } - 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; @@ -4516,9 +4617,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 @@ -4535,8 +4645,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)); } @@ -4558,7 +4667,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" @@ -4592,31 +4701,6 @@ main(int argc, tchar **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) { @@ -4669,11 +4753,8 @@ main(int argc, tchar **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) {