]> wimlib.net Git - wimlib/blobdiff - programs/imagex.c
Fix sequential extraction, and include progress info
[wimlib] / programs / imagex.c
index 979ef76f82212d885ea78638477e15190f6438ad..9b8af6e047d3caf16baaa544e9faefd4d55421f0 100644 (file)
 #include <string.h>
 #include <errno.h>
 #include <libgen.h>
+#include <limits.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <inttypes.h>
 
 #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
 
@@ -51,6 +53,7 @@ enum imagex_op_type {
        JOIN,
        MOUNT,
        MOUNTRW,
+       OPTIMIZE,
        SPLIT,
        UNMOUNT,
 };
@@ -62,7 +65,8 @@ static const char *usage_strings[] = {
 [APPEND] =
 "imagex append (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n"
 "                     [DESCRIPTION] [--boot] [--check] [--flags EDITION_ID]\n"
-"                     [--verbose] [--dereference] [--config=FILE]\n",
+"                     [--verbose] [--dereference] [--config=FILE]\n"
+"                     [--threads=NUM_THREADS] [--rebuild]\n",
 [APPLY] =
 "imagex apply WIMFILE [IMAGE_NUM | IMAGE_NAME | all]\n"
 "                    (DIRECTORY | NTFS_VOLUME) [--check] [--hardlink]\n"
@@ -71,16 +75,16 @@ static const char *usage_strings[] = {
 "imagex capture (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n"
 "                      [DESCRIPTION] [--boot] [--check] [--compress=TYPE]\n"
 "                      [--flags EDITION_ID] [--verbose] [--dereference]\n"
-"                   [--config=FILE]\n",
+"                      [--config=FILE] [--threads=NUM_THREADS]\n",
 [DELETE] =
-"imagex delete WIMFILE (IMAGE_NUM | IMAGE_NAME | all) [--check]\n",
+"imagex delete WIMFILE (IMAGE_NUM | IMAGE_NAME | all) [--check] [--soft]\n",
 [DIR] =
 "imagex dir WIMFILE (IMAGE_NUM | IMAGE_NAME | all)\n",
 [EXPORT] =
 "imagex export SRC_WIMFILE (SRC_IMAGE_NUM | SRC_IMAGE_NAME | all ) \n"
-"                     DEST_WIMFILE [DEST_IMAGE_NAME]\n"
-"                     [DEST_IMAGE_DESCRIPTION] [--boot] [--check]\n"
-"                     [--compress=TYPE] [--ref=\"GLOB\"]\n",
+"              DEST_WIMFILE [DEST_IMAGE_NAME] [DEST_IMAGE_DESCRIPTION]\n"
+"              [--boot] [--check] [--compress=TYPE] [--ref=\"GLOB\"]\n"
+"              [--threads=NUM_THREADS] [--rebuild]\n",
 [INFO] =
 "imagex info WIMFILE [IMAGE_NUM | IMAGE_NAME] [NEW_NAME]\n"
 "                   [NEW_DESC] [--boot] [--check] [--header] [--lookup-table]\n"
@@ -94,6 +98,8 @@ static const char *usage_strings[] = {
 [MOUNTRW] =
 "imagex mountrw WIMFILE [IMAGE_NUM | IMAGE_NAME] DIRECTORY\n"
 "                      [--check] [--debug] [--streams-interface=INTERFACE]\n",
+[OPTIMIZE] =
+"imagex optimize WIMFILE [--check] [--recompress]\n",
 [SPLIT] =
 "imagex split WIMFILE SPLIT_WIMFILE PART_SIZE_MB [--check]\n",
 [UNMOUNT] =
@@ -122,10 +128,13 @@ static const struct option capture_or_append_options[] = {
        {"dereference", no_argument,       NULL, 'L'},
        {"flags",       required_argument, NULL, 'f'},
        {"verbose",     no_argument,       NULL, 'v'},
+       {"threads",     required_argument, NULL, 't'},
+       {"rebuild",     no_argument,       NULL, 'R'},
        {NULL, 0, NULL, 0},
 };
 static const struct option delete_options[] = {
        {"check", no_argument, NULL, 'c'},
+       {"soft",  no_argument, NULL, 's'},
        {NULL, 0, NULL, 0},
 };
 
@@ -134,6 +143,8 @@ static const struct option export_options[] = {
        {"check",      no_argument,       NULL, 'c'},
        {"compress",   required_argument, NULL, 'x'},
        {"ref",        required_argument, NULL, 'r'},
+       {"threads",    required_argument, NULL, 't'},
+       {"rebuild",    no_argument,       NULL, 'R'},
        {NULL, 0, NULL, 0},
 };
 
@@ -161,6 +172,12 @@ static const struct option mount_options[] = {
        {NULL, 0, NULL, 0},
 };
 
+static const struct option optimize_options[] = {
+       {"check",      no_argument, NULL, 'c'},
+       {"recompress", no_argument, NULL, 'r'},
+       {NULL, 0, NULL, 0},
+};
+
 static const struct option split_options[] = {
        {"check", no_argument, NULL, 'c'},
        {NULL, 0, NULL, 0},
@@ -246,6 +263,15 @@ static int get_compression_type(const char *optarg)
        }
 }
 
+static off_t file_get_size(const char *filename)
+{
+       struct stat st;
+       if (stat(filename, &st) == 0)
+               return st.st_size;
+       else
+               return (off_t)-1;
+}
+
 static char *file_get_contents(const char *filename, size_t *len_ret)
 {
        struct stat stbuf;
@@ -287,7 +313,6 @@ out_fclose:
 
 static int file_writable(const char *path)
 {
-       struct stat stbuf;
        int ret;
        ret = access(path, F_OK | W_OK);
        if (ret != 0)
@@ -352,6 +377,19 @@ out:
 }
 
 
+static unsigned parse_num_threads(const char *optarg)
+{
+       char *tmp;
+       unsigned nthreads = strtoul(optarg, &tmp, 10);
+       if (nthreads == UINT_MAX || *tmp || tmp == optarg) {
+               imagex_error("Number of threads must be a non-negative integer!");
+               return UINT_MAX;
+       } else {
+               return nthreads;
+       }
+}
+
+
 /* Extract one image, or all images, from a WIM file into a directory. */
 static int imagex_apply(int argc, const char **argv)
 {
@@ -365,7 +403,8 @@ static int imagex_apply(int argc, const char **argv)
        const char *wimfile;
        const char *dir;
        const char *image_num_or_name;
-       int extract_flags = 0;
+       int extract_flags = WIMLIB_EXTRACT_FLAG_SEQUENTIAL |
+                           WIMLIB_EXTRACT_FLAG_SHOW_PROGRESS;
 
        const char *swm_glob = NULL;
        WIMStruct **additional_swms = NULL;
@@ -476,7 +515,7 @@ static int imagex_capture_or_append(int argc, const char **argv)
 {
        int c;
        int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
-       int add_image_flags = 0;
+       int add_image_flags = WIMLIB_ADD_IMAGE_FLAG_SHOW_PROGRESS;
        int write_flags = WIMLIB_WRITE_FLAG_SHOW_PROGRESS;
        int compression_type = WIM_COMPRESSION_TYPE_XPRESS;
        const char *dir;
@@ -492,6 +531,7 @@ static int imagex_capture_or_append(int argc, const char **argv)
        int cur_image;
        char *default_name;
        int cmd = strcmp(argv[0], "append") ? CAPTURE : APPEND;
+       unsigned num_threads = 0;
 
        for_opt(c, capture_or_append_options) {
                switch (c) {
@@ -520,6 +560,14 @@ static int imagex_capture_or_append(int argc, const char **argv)
                        add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_VERBOSE;
                        write_flags |= WIMLIB_WRITE_FLAG_VERBOSE;
                        break;
+               case 't':
+                       num_threads = parse_num_threads(optarg);
+                       if (num_threads == UINT_MAX)
+                               return -1;
+                       break;
+               case 'R':
+                       write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
+                       break;
                default:
                        usage(cmd);
                        return -1;
@@ -595,10 +643,14 @@ out_write:
                if (ret != 0)
                        goto out;
        }
-       if (cmd == APPEND)
-               ret = wimlib_overwrite(w, write_flags);
-       else
-               ret = wimlib_write(w, wimfile, WIM_ALL_IMAGES, write_flags);
+       if (cmd == APPEND) {
+               ret = wimlib_overwrite(w, write_flags, num_threads);
+       } else {
+               ret = wimlib_write(w, wimfile, WIM_ALL_IMAGES, write_flags,
+                                  num_threads);
+       }
+       if (ret == WIMLIB_ERR_REOPEN)
+               ret = 0;
        if (ret != 0)
                imagex_error("Failed to write the WIM file `%s'", wimfile);
 out:
@@ -625,6 +677,9 @@ static int imagex_delete(int argc, const char **argv)
                        open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
                        write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
                        break;
+               case 's':
+                       write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
+                       break;
                default:
                        usage(DELETE);
                        return -1;
@@ -664,7 +719,9 @@ static int imagex_delete(int argc, const char **argv)
                goto out;
        }
 
-       ret = wimlib_overwrite(w, write_flags);
+       ret = wimlib_overwrite(w, write_flags, 0);
+       if (ret == WIMLIB_ERR_REOPEN)
+               ret = 0;
        if (ret != 0) {
                imagex_error("Failed to write the file `%s' with image "
                             "deleted", wimfile);
@@ -748,6 +805,7 @@ static int imagex_export(int argc, const char **argv)
        const char *swm_glob = NULL;
        WIMStruct **additional_swms = NULL;
        unsigned num_additional_swms = 0;
+       unsigned num_threads = 0;
 
        for_opt(c, export_options) {
                switch (c) {
@@ -767,6 +825,14 @@ static int imagex_export(int argc, const char **argv)
                case 'r':
                        swm_glob = optarg;
                        break;
+               case 't':
+                       num_threads = parse_num_threads(optarg);
+                       if (num_threads == UINT_MAX)
+                               return -1;
+                       break;
+               case 'R':
+                       write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
+                       break;
                default:
                        usage(EXPORT);
                        return -1;
@@ -860,10 +926,12 @@ static int imagex_export(int argc, const char **argv)
 
        if (wim_is_new)
                ret = wimlib_write(dest_w, dest_wimfile, WIM_ALL_IMAGES,
-                                  write_flags);
+                                  write_flags, num_threads);
        else
-               ret = wimlib_overwrite(dest_w, write_flags);
+               ret = wimlib_overwrite(dest_w, write_flags, num_threads);
 out:
+       if (ret == WIMLIB_ERR_REOPEN)
+               ret = 0;
        wimlib_free(src_w);
        wimlib_free(dest_w);
        if (additional_swms) {
@@ -1109,8 +1177,8 @@ static int imagex_info(int argc, const char **argv)
                        }
                }
 
-               /* Only call wimlib_overwrite_xml_and_header() if something
-                * actually needs to be changed. */
+               /* Only call wimlib_overwrite() if something actually needs to
+                * be changed. */
                if (boot || new_name || new_desc ||
                                check != wimlib_has_integrity_table(w)) {
 
@@ -1118,9 +1186,17 @@ static int imagex_info(int argc, const char **argv)
                        if (ret != 0)
                                return ret;
 
-                       ret = wimlib_overwrite_xml_and_header(w, check ?
-                                       WIMLIB_WRITE_FLAG_CHECK_INTEGRITY |
-                                       WIMLIB_WRITE_FLAG_SHOW_PROGRESS : 0);
+                       int write_flags;
+                       if (check) {
+                               write_flags = WIMLIB_WRITE_FLAG_CHECK_INTEGRITY |
+                                             WIMLIB_WRITE_FLAG_SHOW_PROGRESS;
+                       } else {
+                               write_flags = 0;
+                       }
+
+                       ret = wimlib_overwrite(w, write_flags, 1);
+                       if (ret == WIMLIB_ERR_REOPEN)
+                               ret = 0;
                } else {
                        printf("The file `%s' was not modified because nothing "
                                        "needed to be done.\n", wimfile);
@@ -1128,7 +1204,7 @@ static int imagex_info(int argc, const char **argv)
                }
        }
 out:
-       /*wimlib_free(w);*/
+       wimlib_free(w);
        return ret;
 }
 
@@ -1264,9 +1340,11 @@ static int imagex_mount_rw_or_ro(int argc, const char **argv)
        }
 out:
        wimlib_free(w);
-       if (additional_swms)
+       if (additional_swms) {
                for (unsigned i = 0; i < num_additional_swms; i++)
                        wimlib_free(additional_swms[i]);
+               free(additional_swms);
+       }
        return ret;
 mount_usage:
        usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)
@@ -1274,6 +1352,74 @@ mount_usage:
        return -1;
 }
 
+static int imagex_optimize(int argc, const char **argv)
+{
+       int c;
+       int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
+       int write_flags = WIMLIB_WRITE_FLAG_REBUILD |
+                         WIMLIB_WRITE_FLAG_SHOW_PROGRESS;
+       int ret;
+       WIMStruct *w;
+       const char *wimfile;
+       off_t old_size;
+       off_t new_size;
+
+       for_opt(c, optimize_options) {
+               switch (c) {
+               case 'c':
+                       open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
+                       write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
+                       break;
+               case 'r':
+                       write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
+                       break;
+               default:
+                       usage(OPTIMIZE);
+                       return -1;
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       if (argc != 1) {
+               usage(OPTIMIZE);
+               return -1;
+       }
+
+       wimfile = argv[0];
+
+       ret = wimlib_open_wim(wimfile, open_flags, &w);
+       if (ret != 0)
+               return ret;
+
+       old_size = file_get_size(argv[0]);
+       printf("`%s' original size: ", wimfile);
+       if (old_size == -1)
+               puts("Unknown");
+       else
+               printf("%"PRIu64" KiB\n", old_size >> 10);
+
+       ret = wimlib_overwrite(w, write_flags, 0);
+
+       new_size = file_get_size(argv[0]);
+       printf("`%s' optimized size: ", wimfile);
+       if (new_size == -1)
+               puts("Unknown");
+       else
+               printf("%"PRIu64" KiB\n", new_size >> 10);
+
+       fputs("Space saved: ", stdout);
+       if (new_size != -1 && old_size != -1) {
+               printf("%lld KiB\n",
+                      ((long long)old_size - (long long)new_size) >> 10);
+       } else {
+               puts("Unknown");
+       }
+
+       wimlib_free(w);
+       return ret;
+}
+
 /* Split a WIM into a spanned set */
 static int imagex_split(int argc, const char **argv)
 {
@@ -1362,6 +1508,7 @@ static const struct imagex_command imagex_commands[] = {
        {"join",    imagex_join,              JOIN},
        {"mount",   imagex_mount_rw_or_ro,    MOUNT},
        {"mountrw", imagex_mount_rw_or_ro,    MOUNTRW},
+       {"optimize",imagex_optimize,          OPTIMIZE},
        {"split",   imagex_split,             SPLIT},
        {"unmount", imagex_unmount,           UNMOUNT},
 };
@@ -1427,7 +1574,7 @@ static void usage_all()
 {
        puts("IMAGEX: Usage:");
        for (int i = 0; i < ARRAY_LEN(usage_strings); i++)
-               fputs(usage_strings[i], stdout);
+               printf("    %s", usage_strings[i]);
        static const char *extra =
 "    imagex --help\n"
 "    imagex --version\n"