]> wimlib.net Git - wimlib/blobdiff - programs/imagex.c
imagex: warn that --update-of is unreliable on Windows
[wimlib] / programs / imagex.c
index f149d642302525ba672e89f1811d5c1aceac60a7..2d407fac422b885dd6a4882b51abe25f93aec54c 100644 (file)
@@ -6,7 +6,7 @@
  */
 
 /*
- * Copyright (C) 2012-2017 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
 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
@@ -133,12 +137,29 @@ static void usage_all(FILE *fp);
 static void recommend_man_page(int cmd, FILE *fp);
 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,6 +356,7 @@ 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},
 };
 
@@ -348,6 +377,7 @@ 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},
@@ -364,6 +394,7 @@ 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},
 };
 
@@ -386,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},
@@ -1141,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:
                {
@@ -1381,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;
 }
 
@@ -1825,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 |
@@ -1852,7 +1886,7 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd)
        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;
@@ -1880,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:
@@ -1984,13 +2020,11 @@ 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_list_append(&base_wimfiles, optarg);
                        if (ret)
                                goto out;
@@ -2000,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;
                }
@@ -2044,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 "
@@ -2055,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
@@ -2073,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;
@@ -2151,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,
@@ -2175,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;
@@ -2224,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;
@@ -2276,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,
@@ -2314,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,
@@ -2357,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,
@@ -2367,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]);
@@ -2411,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:
@@ -2504,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);
 }
@@ -2679,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)
@@ -2869,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:
@@ -2968,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;
@@ -3182,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:
@@ -3270,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))
@@ -3326,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;
@@ -3340,6 +3416,7 @@ imagex_info(int argc, tchar **argv, int cmd)
        int image;
        int ret;
        int open_flags = 0;
+       int write_flags = 0;
        struct wimlib_wim_info info;
 
        for_opt(c, info_options) {
@@ -3348,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;
@@ -3405,14 +3485,6 @@ imagex_info(int argc, tchar **argv, int cmd)
                        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)
@@ -3550,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 "
@@ -3575,7 +3643,6 @@ out:
 
 out_usage:
        usage(CMD_INFO, stderr);
-out_err:
        ret = -1;
        goto out;
 }
@@ -3594,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:
@@ -3774,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:
@@ -3921,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:
@@ -4066,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:
@@ -4385,7 +4460,8 @@ T(
 "                    [--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(
@@ -4411,7 +4487,7 @@ T(
 ),
 [CMD_DIR] =
 T(
-"    %"TS" WIMFILE IMAGE [--path=PATH] [--detailed]\n"
+"    %"TS" WIMFILE [IMAGE] [--path=PATH] [--detailed]\n"
 ),
 [CMD_EXPORT] =
 T(
@@ -4508,23 +4584,23 @@ static const tchar *get_cmd_string(int cmd, bool only_short_form)
 static void
 version(void)
 {
-       static const tchar * const s =
+       static const tchar * const fmt =
        T(
-"wimlib-imagex (distributed with " PACKAGE " " PACKAGE_VERSION ")\n"
-"Copyright (C) 2012-2017 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 <http://gnu.org/licenses/gpl.html>.\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;
 
@@ -4541,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
@@ -4668,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) {