]> wimlib.net Git - wimlib/blobdiff - programs/imagex.c
wimextract: Suggest --nullglob
[wimlib] / programs / imagex.c
index 0f800a846cba94f709f5f95660e1edf8c674fda8..1a8407045dfaa46f30411fb43b844942b11bbbc2 100644 (file)
@@ -130,9 +130,6 @@ 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 int imagex_progress_func(enum wimlib_progress_msg msg,
-                               const union wimlib_progress_info *info);
-
 static bool imagex_be_quiet = false;
 static FILE *imagex_info_file;
 
@@ -157,7 +154,6 @@ enum {
        IMAGEX_EXTRACT_XML_OPTION,
        IMAGEX_FLAGS_OPTION,
        IMAGEX_FORCE_OPTION,
-       IMAGEX_HARDLINK_OPTION,
        IMAGEX_HEADER_OPTION,
        IMAGEX_INCLUDE_INVALID_NAMES_OPTION,
        IMAGEX_LAZY_OPTION,
@@ -190,7 +186,6 @@ enum {
        IMAGEX_STAGING_DIR_OPTION,
        IMAGEX_STREAMS_INTERFACE_OPTION,
        IMAGEX_STRICT_ACLS_OPTION,
-       IMAGEX_SYMLINK_OPTION,
        IMAGEX_THREADS_OPTION,
        IMAGEX_TO_STDOUT_OPTION,
        IMAGEX_UNIX_DATA_OPTION,
@@ -203,8 +198,6 @@ enum {
 
 static const struct option apply_options[] = {
        {T("check"),       no_argument,       NULL, IMAGEX_CHECK_OPTION},
-       {T("hardlink"),    no_argument,       NULL, IMAGEX_HARDLINK_OPTION},
-       {T("symlink"),     no_argument,       NULL, IMAGEX_SYMLINK_OPTION},
        {T("verbose"),     no_argument,       NULL, IMAGEX_VERBOSE_OPTION},
        {T("ref"),         required_argument, NULL, IMAGEX_REF_OPTION},
        {T("unix-data"),   no_argument,       NULL, IMAGEX_UNIX_DATA_OPTION},
@@ -289,6 +282,7 @@ static const struct option export_options[] = {
        {T("rebuild"),     no_argument,       NULL, IMAGEX_REBUILD_OPTION},
        {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},
        {NULL, 0, NULL, 0},
 };
 
@@ -371,6 +365,7 @@ static const struct option unmount_options[] = {
        {T("check"),   no_argument, NULL, IMAGEX_CHECK_OPTION},
        {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
        {T("lazy"),    no_argument, NULL, IMAGEX_LAZY_OPTION},
+       {T("force"),    no_argument, NULL, IMAGEX_FORCE_OPTION},
        {T("new-image"), no_argument, NULL, IMAGEX_NEW_IMAGE_OPTION},
        {NULL, 0, NULL, 0},
 };
@@ -510,7 +505,6 @@ set_compress_slow(void)
                                .nice_match_length = 96,
                                .num_optim_passes = 4,
                                .max_search_depth = 100,
-                               .max_matches_per_pos = 10,
                                .main_nostat_cost = 15,
                                .len_nostat_cost = 15,
                                .aligned_nostat_cost = 7,
@@ -526,7 +520,6 @@ set_compress_slow(void)
                .max_match_length = UINT32_MAX,
                .nice_match_length = 96,
                .max_search_depth = 100,
-               .max_matches_per_pos = 10,
                .optim_array_length = 1024,
        };
 
@@ -583,8 +576,7 @@ wim_reference_globs(WIMStruct *wim, struct string_set *set, int open_flags)
        return wimlib_reference_resource_files(wim, set->strings,
                                               set->num_strings,
                                               WIMLIB_REF_FLAG_GLOB_ENABLE,
-                                              open_flags,
-                                              imagex_progress_func);
+                                              open_flags);
 }
 
 static void
@@ -1023,18 +1015,18 @@ report_scan_progress(const struct wimlib_progress_info_scan *scan, bool done)
                last_scan_progress = *scan;
        }
 }
-
 /* Progress callback function passed to various wimlib functions. */
-static int
+static enum wimlib_progress_status
 imagex_progress_func(enum wimlib_progress_msg msg,
-                    const union wimlib_progress_info *info)
+                    union wimlib_progress_info *info,
+                    void *_ignored_context)
 {
        unsigned percent_done;
        unsigned unit_shift;
        const tchar *unit_name;
 
        if (imagex_be_quiet)
-               return 0;
+               return WIMLIB_PROGRESS_STATUS_CONTINUE;
        switch (msg) {
        case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
                {
@@ -1093,6 +1085,24 @@ imagex_progress_func(enum wimlib_progress_msg msg,
                                        "absolute symbolic links as-is)\n"),
                                        info->scan.cur_path, info->scan.symlink_target);
                        break;
+               case WIMLIB_SCAN_DENTRY_FIXED_SYMLINK:
+                       /* Symlink fixups are enabled by default.  This is
+                        * mainly intended for Windows, which for some reason
+                        * uses absolute junctions (with drive letters!) in the
+                        * default installation.  On UNIX-like systems, warn the
+                        * user when fixing the target of an absolute symbolic
+                        * link, so they know to disable this if they want.
+                        * (Although, more likely they will get the warning
+                        * about an absolute symbolic link with an out-of-tree
+                        * target first.)  */
+               #ifndef __WIN32__
+                       imagex_printf(T("\nWARNING: Adjusted target of "
+                                       "absolute symbolic link \"%"TS"\"\n"
+                                       "           (Use --norpfix to capture "
+                                       "absolute symbolic links as-is)\n"),
+                                       info->scan.cur_path);
+               #endif
+                       break;
                }
                break;
        case WIMLIB_PROGRESS_MSG_SCAN_END:
@@ -1159,15 +1169,6 @@ imagex_progress_func(enum wimlib_progress_msg msg,
                                      info->extract.total_parts);
                }
                break;
-       case WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS:
-               imagex_printf(T("Setting timestamps on all extracted files...\n"));
-               break;
-       case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END:
-               if (info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
-                       imagex_printf(T("Unmounting NTFS volume \"%"TS"\"...\n"),
-                               info->extract.target);
-               }
-               break;
        case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
                percent_done = TO_PERCENT(info->split.completed_bytes,
                                          info->split.total_bytes);
@@ -1214,11 +1215,25 @@ imagex_progress_func(enum wimlib_progress_msg msg,
                imagex_printf(T("\nExtracting \"%"TS"\" as normal file (not WIMBoot pointer)\n"),
                              info->wimboot_exclude.path_in_wim);
                break;
+       case WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN:
+               if (info->unmount.mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
+                       if (info->unmount.unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) {
+                               imagex_printf(T("Committing changes to %"TS" (image %d)\n"),
+                                             info->unmount.mounted_wim,
+                                             info->unmount.mounted_image);
+                       } else {
+                               imagex_printf(T("Discarding changes to %"TS" (image %d)\n"),
+                                             info->unmount.mounted_wim,
+                                             info->unmount.mounted_image);
+                               imagex_printf(T("\t(Use --commit to keep changes.)\n"));
+                       }
+               }
+               break;
        default:
                break;
        }
        fflush(imagex_info_file);
-       return 0;
+       return WIMLIB_PROGRESS_STATUS_CONTINUE;
 }
 
 static unsigned
@@ -1458,7 +1473,7 @@ parse_update_command_file(tchar **cmd_file_contents_p, size_t cmd_file_nchars,
 }
 
 /* Apply one image, or all images, from a WIM file to a directory, OR apply
- * one image from a WIM file to a NTFS volume.  */
+ * one image from a WIM file to an NTFS volume.  */
 static int
 imagex_apply(int argc, tchar **argv, int cmd)
 {
@@ -1480,12 +1495,6 @@ imagex_apply(int argc, tchar **argv, int cmd)
                case IMAGEX_CHECK_OPTION:
                        open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
                        break;
-               case IMAGEX_HARDLINK_OPTION:
-                       extract_flags |= WIMLIB_EXTRACT_FLAG_HARDLINK;
-                       break;
-               case IMAGEX_SYMLINK_OPTION:
-                       extract_flags |= WIMLIB_EXTRACT_FLAG_SYMLINK;
-                       break;
                case IMAGEX_VERBOSE_OPTION:
                        /* No longer does anything.  */
                        break;
@@ -1544,8 +1553,8 @@ imagex_apply(int argc, tchar **argv, int cmd)
                }
                wim = NULL;
        } else {
-               ret = wimlib_open_wim(wimfile, open_flags, &wim,
-                                     imagex_progress_func);
+               ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
+                                                   imagex_progress_func, NULL);
                if (ret)
                        goto out_free_refglobs;
 
@@ -1588,7 +1597,7 @@ imagex_apply(int argc, tchar **argv, int cmd)
 
 #ifndef __WIN32__
        {
-               /* Interpret a regular file or block device target as a NTFS
+               /* Interpret a regular file or block device target as an NTFS
                 * volume.  */
                struct stat stbuf;
 
@@ -1607,14 +1616,16 @@ imagex_apply(int argc, tchar **argv, int cmd)
 #endif
 
        if (wim) {
-               ret = wimlib_extract_image(wim, image, target, extract_flags,
-                                          imagex_progress_func);
+               ret = wimlib_extract_image(wim, image, target, extract_flags);
        } else {
                set_fd_to_binary_mode(STDIN_FILENO);
-               ret = wimlib_extract_image_from_pipe(STDIN_FILENO,
-                                                    image_num_or_name,
-                                                    target, extract_flags,
-                                                    imagex_progress_func);
+               ret = wimlib_extract_image_from_pipe_with_progress(
+                                          STDIN_FILENO,
+                                          image_num_or_name,
+                                          target,
+                                          extract_flags,
+                                          imagex_progress_func,
+                                          NULL);
        }
        if (ret == 0) {
                imagex_printf(T("Done applying WIM image.\n"));
@@ -1822,9 +1833,19 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd)
 
 
        if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
+               /* No compression type specified.  Use the default.  */
+
                if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_WIMBOOT) {
+                       /* With --wimboot, default to XPRESS compression.  */
                        compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
+               } else if (write_flags & WIMLIB_WRITE_FLAG_PACK_STREAMS) {
+                       /* With --pack-streams or --solid, default to LZMS
+                        * compression.  (However, this will not affect packed
+                        * resources!)  */
+                       compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
                } else {
+                       /* Otherwise, default to LZX compression in fast mode.
+                        */
                        compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
                        if (!compress_slow && pack_ctype != WIMLIB_COMPRESSION_TYPE_LZX) {
                                struct wimlib_lzx_compressor_params params = {
@@ -1946,13 +1967,17 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd)
        }
 
        /* Open the existing WIM, or create a new one.  */
-       if (cmd == CMD_APPEND)
-               ret = wimlib_open_wim(wimfile, open_flags, &wim,
-                                     imagex_progress_func);
-       else
+       if (cmd == CMD_APPEND) {
+               ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
+                                                   imagex_progress_func, NULL);
+               if (ret)
+                       goto out_free_capture_sources;
+       } else {
                ret = wimlib_create_new_wim(compression_type, &wim);
-       if (ret)
-               goto out_free_capture_sources;
+               if (ret)
+                       goto out_free_capture_sources;
+               wimlib_register_progress_function(wim, imagex_progress_func, NULL);
+       }
 
        /* Set chunk size if non-default.  */
        if (chunk_size != UINT32_MAX) {
@@ -2025,9 +2050,10 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd)
                }
 
                for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
-                       ret = wimlib_open_wim(base_wimfiles.strings[i],
-                                             open_flags, &base_wims[i],
-                                             imagex_progress_func);
+                       ret = wimlib_open_wim_with_progress(
+                                   base_wimfiles.strings[i],
+                                   open_flags, &base_wims[i],
+                                   imagex_progress_func, NULL);
                        if (ret)
                                goto out_free_base_wims;
 
@@ -2061,8 +2087,11 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd)
                } else if (template_wimfile == wimfile) {
                        template_wim = wim;
                } else {
-                       ret = wimlib_open_wim(template_wimfile, open_flags,
-                                             &template_wim, imagex_progress_func);
+                       ret = wimlib_open_wim_with_progress(template_wimfile,
+                                                           open_flags,
+                                                           &template_wim,
+                                                           imagex_progress_func,
+                                                           NULL);
                        if (ret)
                                goto out_free_base_wims;
                }
@@ -2098,8 +2127,7 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd)
                                           num_sources,
                                           name,
                                           config_file,
-                                          add_image_flags,
-                                          imagex_progress_func);
+                                          add_image_flags);
        if (ret)
                goto out_free_template_wim;
 
@@ -2136,7 +2164,7 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd)
                                                              info.image_count,
                                                              template_wim,
                                                              template_image,
-                                                             0, NULL);
+                                                             0);
                        if (ret)
                                goto out_free_template_wim;
                }
@@ -2145,16 +2173,13 @@ 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) {
-               ret = wimlib_overwrite(wim, write_flags, num_threads,
-                                      imagex_progress_func);
+               ret = wimlib_overwrite(wim, write_flags, num_threads);
        } else if (wimfile) {
                ret = wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES,
-                                  write_flags, num_threads,
-                                  imagex_progress_func);
+                                  write_flags, num_threads);
        } else {
                ret = wimlib_write_to_fd(wim, wim_fd, WIMLIB_ALL_IMAGES,
-                                        write_flags, num_threads,
-                                        imagex_progress_func);
+                                        write_flags, num_threads);
        }
 out_free_template_wim:
        /* template_wim may alias base_wims[0] or wim.  */
@@ -2222,8 +2247,8 @@ imagex_delete(int argc, tchar **argv, int cmd)
        wimfile = argv[0];
        image_num_or_name = argv[1];
 
-       ret = wimlib_open_wim(wimfile, open_flags, &wim,
-                             imagex_progress_func);
+       ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
+                                           imagex_progress_func, NULL);
        if (ret)
                goto out;
 
@@ -2240,7 +2265,7 @@ imagex_delete(int argc, tchar **argv, int cmd)
                goto out_wimlib_free;
        }
 
-       ret = wimlib_overwrite(wim, write_flags, 0, imagex_progress_func);
+       ret = wimlib_overwrite(wim, write_flags, 0);
        if (ret) {
                imagex_error(T("Failed to write the file \"%"TS"\" with image "
                               "deleted"), wimfile);
@@ -2435,6 +2460,13 @@ print_dentry_detailed(const struct wimlib_dir_entry *dentry)
        tprintf(T("Link Group ID       = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
        tprintf(T("Link Count          = %"PRIu32"\n"), dentry->num_links);
 
+       if (dentry->unix_mode != 0) {
+               tprintf(T("UNIX Data           = uid:%"PRIu32" gid:%"PRIu32" "
+                         "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
+                       dentry->unix_uid, dentry->unix_gid,
+                       dentry->unix_mode, dentry->unix_rdev);
+       }
+
        for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
                if (dentry->streams[i].stream_name) {
                        tprintf(T("\tData stream \"%"TS"\":\n"),
@@ -2500,7 +2532,8 @@ imagex_dir(int argc, tchar **argv, int cmd)
        }
 
        wimfile = argv[0];
-       ret = wimlib_open_wim(wimfile, 0, &wim, imagex_progress_func);
+       ret = wimlib_open_wim_with_progress(wimfile, 0, &wim,
+                                           imagex_progress_func, NULL);
        if (ret)
                goto out;
 
@@ -2546,7 +2579,7 @@ imagex_export(int argc, tchar **argv, int cmd)
 {
        int c;
        int open_flags = 0;
-       int export_flags = 0;
+       int export_flags = WIMLIB_EXPORT_FLAG_GIFT;
        int write_flags = 0;
        int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
        const tchar *src_wimfile;
@@ -2626,6 +2659,9 @@ imagex_export(int argc, tchar **argv, int cmd)
                case IMAGEX_NOT_PIPABLE_OPTION:
                        write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
                        break;
+               case IMAGEX_WIMBOOT_OPTION:
+                       export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT;
+                       break;
                default:
                        goto out_usage;
                }
@@ -2639,8 +2675,8 @@ imagex_export(int argc, tchar **argv, int cmd)
        dest_wimfile          = argv[2];
        dest_name             = (argc >= 4) ? argv[3] : NULL;
        dest_desc             = (argc >= 5) ? argv[4] : NULL;
-       ret = wimlib_open_wim(src_wimfile, open_flags, &src_wim,
-                             imagex_progress_func);
+       ret = wimlib_open_wim_with_progress(src_wimfile, open_flags, &src_wim,
+                                           imagex_progress_func, NULL);
        if (ret)
                goto out_free_refglobs;
 
@@ -2680,9 +2716,12 @@ imagex_export(int argc, tchar **argv, int cmd)
                        ret = -1;
                        goto out_free_src_wim;
                }
-               ret = wimlib_open_wim(dest_wimfile,
-                                     open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS,
-                                     &dest_wim, imagex_progress_func);
+               ret = wimlib_open_wim_with_progress(dest_wimfile,
+                                                   open_flags |
+                                                       WIMLIB_OPEN_FLAG_WRITE_ACCESS,
+                                                   &dest_wim,
+                                                   imagex_progress_func,
+                                                   NULL);
                if (ret)
                        goto out_free_src_wim;
 
@@ -2716,18 +2755,34 @@ imagex_export(int argc, tchar **argv, int cmd)
 
                if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
                        /* The user did not specify a compression type; default
-                        * to that of the source WIM.  */
-
-                       compression_type = src_info.compression_type;
+                        * to that of the source WIM, unless --pack-streams,
+                        * --solid, or --wimboot was specified.   */
+
+                       if (write_flags & WIMLIB_WRITE_FLAG_PACK_STREAMS)
+                               compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
+                       else if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
+                               compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
+                       else
+                               compression_type = src_info.compression_type;
                }
                ret = wimlib_create_new_wim(compression_type, &dest_wim);
                if (ret)
                        goto out_free_src_wim;
 
-               /* Use same chunk size if compression type is the same.  */
-               if (compression_type == src_info.compression_type &&
-                   chunk_size == UINT32_MAX)
+               wimlib_register_progress_function(dest_wim,
+                                                 imagex_progress_func, NULL);
+
+               if ((export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
+                   && compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS)
+               {
+                       /* For --wimboot export, use small XPRESS chunks.  */
+                       wimlib_set_output_chunk_size(dest_wim, 4096);
+               } else if (compression_type == src_info.compression_type &&
+                          chunk_size == UINT32_MAX)
+               {
+                       /* Use same chunk size if compression type is the same.  */
                        wimlib_set_output_chunk_size(dest_wim, src_info.chunk_size);
+               }
        }
 
        if (chunk_size != UINT32_MAX) {
@@ -2768,7 +2823,7 @@ imagex_export(int argc, tchar **argv, int cmd)
        }
 
        ret = wimlib_export_image(src_wim, image, dest_wim, dest_name,
-                                 dest_desc, export_flags, imagex_progress_func);
+                                 dest_desc, export_flags);
        if (ret) {
                if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
                        do_resource_not_found_warning(src_wimfile,
@@ -2778,16 +2833,14 @@ imagex_export(int argc, tchar **argv, int cmd)
        }
 
        if (!wim_is_new)
-               ret = wimlib_overwrite(dest_wim, write_flags, num_threads,
-                                      imagex_progress_func);
+               ret = wimlib_overwrite(dest_wim, write_flags, num_threads);
        else if (dest_wimfile)
                ret = wimlib_write(dest_wim, dest_wimfile, WIMLIB_ALL_IMAGES,
-                                  write_flags, num_threads,
-                                  imagex_progress_func);
+                                  write_flags, num_threads);
        else
                ret = wimlib_write_to_fd(dest_wim, dest_wim_fd,
                                         WIMLIB_ALL_IMAGES, write_flags,
-                                        num_threads, imagex_progress_func);
+                                        num_threads);
 out_free_dest_wim:
        wimlib_free(dest_wim);
 out_free_src_wim:
@@ -2856,6 +2909,7 @@ imagex_extract(int argc, tchar **argv, int cmd)
                        extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
                        imagex_info_file = stderr;
                        imagex_be_quiet = true;
+                       set_fd_to_binary_mode(STDOUT_FILENO);
                        break;
                case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
                        extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
@@ -2896,7 +2950,8 @@ imagex_extract(int argc, tchar **argv, int cmd)
        argc -= 2;
        argv += 2;
 
-       ret = wimlib_open_wim(wimfile, open_flags, &wim, imagex_progress_func);
+       ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
+                                           imagex_progress_func, NULL);
        if (ret)
                goto out_free_refglobs;
 
@@ -2931,15 +2986,13 @@ imagex_extract(int argc, tchar **argv, int cmd)
                        ret = wimlib_extract_paths(wim, image, dest_dir,
                                                   (const tchar **)argv,
                                                   num_paths,
-                                                  extract_flags | notlist_extract_flags,
-                                                  imagex_progress_func);
+                                                  extract_flags | notlist_extract_flags);
                        argc -= num_paths;
                        argv += num_paths;
                } else {
                        ret = wimlib_extract_pathlist(wim, image, dest_dir,
                                                      argv[0] + 1,
-                                                     extract_flags,
-                                                     imagex_progress_func);
+                                                     extract_flags);
                        argc--;
                        argv++;
                }
@@ -2949,6 +3002,15 @@ imagex_extract(int argc, tchar **argv, int cmd)
                if (!imagex_be_quiet)
                        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))
+                       == (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
+                           WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
+               {
+                       tfprintf(stderr,
+                                T("Note: You can use the '--nullglob' "
+                                  "option to ignore missing files.\n"));
+               }
                tfprintf(stderr, T("Note: You can use `%"TS"' to see what "
                                   "files and directories\n"
                                   "      are in the WIM image.\n"),
@@ -3050,7 +3112,8 @@ imagex_info(int argc, tchar **argv, int cmd)
        if (check)
                open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
 
-       ret = wimlib_open_wim(wimfile, open_flags, &wim, imagex_progress_func);
+       ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
+                                           imagex_progress_func, NULL);
        if (ret)
                goto out;
 
@@ -3220,8 +3283,7 @@ imagex_info(int argc, tchar **argv, int cmd)
                                write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
                        if (nocheck)
                                write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
-                       ret = wimlib_overwrite(wim, write_flags, 1,
-                                              imagex_progress_func);
+                       ret = wimlib_overwrite(wim, write_flags, 1);
                } else {
                        imagex_printf(T("The file \"%"TS"\" was not modified "
                                        "because nothing needed to be done.\n"),
@@ -3270,12 +3332,13 @@ imagex_join(int argc, tchar **argv, int cmd)
                goto out_usage;
        }
        output_path = argv[0];
-       ret = wimlib_join((const tchar * const *)++argv,
-                         --argc,
-                         output_path,
-                         swm_open_flags,
-                         wim_write_flags,
-                         imagex_progress_func);
+       ret = wimlib_join_with_progress((const tchar * const *)++argv,
+                                       --argc,
+                                       output_path,
+                                       swm_open_flags,
+                                       wim_write_flags,
+                                       imagex_progress_func,
+                                       NULL);
 out:
        return ret;
 
@@ -3355,7 +3418,8 @@ imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd)
 
        wimfile = argv[0];
 
-       ret = wimlib_open_wim(wimfile, open_flags, &wim, imagex_progress_func);
+       ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
+                                           imagex_progress_func, NULL);
        if (ret)
                goto out_free_refglobs;
 
@@ -3489,7 +3553,8 @@ imagex_optimize(int argc, tchar **argv, int cmd)
 
        wimfile = argv[0];
 
-       ret = wimlib_open_wim(wimfile, open_flags, &wim, imagex_progress_func);
+       ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
+                                           imagex_progress_func, NULL);
        if (ret)
                goto out;
 
@@ -3524,8 +3589,7 @@ imagex_optimize(int argc, tchar **argv, int cmd)
        else
                tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
 
-       ret = wimlib_overwrite(wim, write_flags, num_threads,
-                              imagex_progress_func);
+       ret = wimlib_overwrite(wim, write_flags, num_threads);
        if (ret) {
                imagex_error(T("Optimization of \"%"TS"\" failed."), wimfile);
                goto out_wimlib_free;
@@ -3593,11 +3657,12 @@ imagex_split(int argc, tchar **argv, int cmd)
                               "floating-point number of megabytes."));
                goto out_err;
        }
-       ret = wimlib_open_wim(argv[0], open_flags, &wim, imagex_progress_func);
+       ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim,
+                                           imagex_progress_func, NULL);
        if (ret)
                goto out;
 
-       ret = wimlib_split(wim, argv[1], part_size, write_flags, imagex_progress_func);
+       ret = wimlib_split(wim, argv[1], part_size, write_flags);
        wimlib_free(wim);
 out:
        return ret;
@@ -3630,7 +3695,14 @@ imagex_unmount(int argc, tchar **argv, int cmd)
                        unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
                        break;
                case IMAGEX_LAZY_OPTION:
-                       unmount_flags |= WIMLIB_UNMOUNT_FLAG_LAZY;
+               case IMAGEX_FORCE_OPTION:
+                       /* Now, unmount is lazy by default.  However, committing
+                        * the image will fail with
+                        * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open
+                        * file descriptors on the WIM image.  The
+                        * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file
+                        * descriptors to be closed.  */
+                       unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE;
                        break;
                case IMAGEX_NEW_IMAGE_OPTION:
                        unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
@@ -3650,13 +3722,19 @@ imagex_unmount(int argc, tchar **argv, int cmd)
                                       "without --commit also specified!"));
                        goto out_err;
                }
-               imagex_printf(T("Committing changes as new image...\n"));
        }
 
-       ret = wimlib_unmount_image(argv[0], unmount_flags,
-                                  imagex_progress_func);
-       if (ret)
+       ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
+                                                imagex_progress_func, NULL);
+       if (ret) {
                imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
+               if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
+                       imagex_printf(T(
+                               "\tNote: Use --commit --force to force changes "
+                                       "to be committed, regardless\n"
+                               "\t      of open files.\n"));
+               }
+       }
 out:
        return ret;
 
@@ -3771,7 +3849,8 @@ imagex_update(int argc, tchar **argv, int cmd)
                goto out_usage;
        wimfile = argv[0];
 
-       ret = wimlib_open_wim(wimfile, open_flags, &wim, imagex_progress_func);
+       ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
+                                           imagex_progress_func, NULL);
        if (ret)
                goto out_free_command_str;
 
@@ -3847,8 +3926,7 @@ imagex_update(int argc, tchar **argv, int cmd)
        }
 
        /* Execute the update commands */
-       ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags,
-                                 imagex_progress_func);
+       ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags);
        if (ret)
                goto out_free_cmds;
 
@@ -3864,15 +3942,13 @@ imagex_update(int argc, tchar **argv, int cmd)
                cmd.add.config_file = NULL;
                cmd.add.add_flags = 0;
 
-               ret = wimlib_update_image(wim, image, &cmd, 1,
-                                         update_flags, imagex_progress_func);
+               ret = wimlib_update_image(wim, image, &cmd, 1, update_flags);
                if (ret)
                        goto out_free_cmds;
        }
 
        /* Overwrite the updated WIM */
-       ret = wimlib_overwrite(wim, write_flags, num_threads,
-                              imagex_progress_func);
+       ret = wimlib_overwrite(wim, write_flags, num_threads);
 out_free_cmds:
        free(cmds);
 out_free_cmd_file_contents:
@@ -3934,8 +4010,8 @@ T(
 "    %"TS" WIMFILE [(IMAGE_NUM | IMAGE_NAME | all)]\n"
 "                    (DIRECTORY | NTFS_VOLUME) [--check] [--ref=\"GLOB\"]\n"
 "                    [--no-acls] [--strict-acls] [--no-attributes]\n"
-"                    [--rpfix] [--norpfix] [--hardlink] [--symlink]\n"
-"                    [--include-invalid-names] [--wimboot]\n"
+"                    [--rpfix] [--norpfix] [--include-invalid-names]\n"
+"                    [--wimboot] [--unix-data]\n"
 ),
 [CMD_CAPTURE] =
 T(
@@ -3945,7 +4021,7 @@ T(
 "                    [--dereference] [--config=FILE] [--threads=NUM_THREADS]\n"
 "                    [--source-list] [--no-acls] [--strict-acls] [--rpfix]\n"
 "                    [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n"
-"                    [--delta-from=WIMFILE] [--wimboot]\n"
+"                    [--delta-from=WIMFILE] [--wimboot] [--unix-data]\n"
 ),
 [CMD_DELETE] =
 T(
@@ -3986,13 +4062,13 @@ T(
 T(
 "    %"TS" WIMFILE [(IMAGE_NUM | IMAGE_NAME)] DIRECTORY\n"
 "                    [--check] [--streams-interface=INTERFACE]\n"
-"                    [--ref=\"GLOB\"] [--allow-other]\n"
+"                    [--ref=\"GLOB\"] [--allow-other] [--unix-data]\n"
 ),
 [CMD_MOUNTRW] =
 T(
 "    %"TS" WIMFILE [(IMAGE_NUM | IMAGE_NAME)] DIRECTORY\n"
 "                    [--check] [--streams-interface=INTERFACE]\n"
-"                    [--staging-dir=CMD_DIR] [--allow-other]\n"
+"                    [--staging-dir=CMD_DIR] [--allow-other] [--unix-data]\n"
 ),
 #endif
 [CMD_OPTIMIZE] =
@@ -4008,8 +4084,8 @@ T(
 #if WIM_MOUNTING_SUPPORTED
 [CMD_UNMOUNT] =
 T(
-"    %"TS" DIRECTORY [--commit] [--check] [--rebuild] [--lazy]\n"
-"                    [--new-image]\n"
+"    %"TS" DIRECTORY [--commit] [--force] [--new-image]\n"
+"                         [--check] [--rebuild]\n"
 ),
 #endif
 [CMD_UPDATE] =
@@ -4135,7 +4211,7 @@ usage_all(FILE *fp)
 
 /* 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 an Windows the command arguments will be UTF-16LE
+ * something else), while on Windows the command arguments will be UTF-16LE
  * encoded 'wchar_t' strings. */
 int
 #ifdef __WIN32__