NTFS capture updates
[wimlib] / programs / imagex.c
index 45d7dd1eca76aa1c5d3264cbb2ce4363b1288de4..fe7f3be9866de20ee66888a4c6ab3e89f59bad0f 100644 (file)
@@ -80,23 +80,27 @@ static const char *path_basename(const char *path)
 
 static const char *usage_strings[] = {
 [APPEND] = 
-"    imagex append DIRECTORY WIMFILE [\"IMAGE_NAME\"] [\"DESCRIPTION\"] [--boot]\n"
-"                  [--check] [--flags EDITIONID] [--dereference]\n",
+"    imagex append (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n"
+"                  [DESCRIPTION] [--boot] [--check] [--flags EDITION_ID]\n"
+"                  [--verbose] [--dereference] [--config=FILE]\n",
 [APPLY] = 
-"    imagex apply WIMFILE [IMAGE_NUM | IMAGE_NAME | all] DIRECTORY [--check]\n"
-"                 [--hardlink] [--symlink] [--verbose]\n",
+"    imagex apply WIMFILE [IMAGE_NUM | IMAGE_NAME | all]\n"
+"                 (DIRECTORY | NTFS_VOLUME) [--check] [--hardlink]\n"
+"                 [--symlink] [--verbose]\n",
 [CAPTURE] = 
-"    imagex capture DIRECTORY WIMFILE [\"IMAGE_NAME\"] [\"DESCRIPTION\"]\n"
-"       l           [--boot] [--check] [--compress[=TYPE]]\n"
-"                   [--flags \"EditionID\"] [--verbose] [--dereference]\n",
+"    imagex capture (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n"
+"                   [DESCRIPTION] [--boot] [--check] [--compress=TYPE]\n"
+"                   [--flags EDITION_ID] [--verbose] [--dereference]\n"
+"                   [--config=FILE]\n",
 [DELETE] = 
 "    imagex delete WIMFILE (IMAGE_NUM | IMAGE_NAME | all) [--check]\n",
 [DIR] = 
-"    imagex dir WIMFILE (IMAGE_NUM | IMAGE_NAME | \"all\")\n",
+"    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\"] [\"DEST_IMAGE_DESCRIPTION\"]\n"
-"                  [--boot] [--check] [--compress[=TYPE]]\n",
+"                  DEST_WIMFILE [DEST_IMAGE_NAME]\n"
+"                  [DEST_IMAGE_DESCRIPTION] [--boot] [--check]\n"
+"                  [--compress=TYPE]\n",
 [INFO] = 
 "    imagex info WIMFILE [IMAGE_NUM | IMAGE_NAME] [NEW_NAME]\n"
 "                [NEW_DESC] [--boot] [--check] [--header] [--lookup-table]\n"
@@ -105,12 +109,12 @@ static const char *usage_strings[] = {
 "    imagex join [--check] WIMFILE SPLIT_WIM...\n",
 [MOUNT] = 
 "    imagex mount WIMFILE (IMAGE_NUM | IMAGE_NAME) DIRECTORY\n"
-"                 [--check] [--debug] [--stream-interface=INTERFACE]\n",
+"                 [--check] [--debug] [--streams-interface=INTERFACE]\n",
 [MOUNTRW] = 
 "    imagex mountrw WIMFILE [IMAGE_NUM | IMAGE_NAME] DIRECTORY\n"
-"                   [--check] [--debug] [--stream-interface=INTERFACE]\n",
+"                   [--check] [--debug] [--streams-interface=INTERFACE]\n",
 [SPLIT] = 
-"    imagex split WIMFILE SPLIT_WIMFILE PART_SIZE [--check]\n",
+"    imagex split WIMFILE SPLIT_WIMFILE PART_SIZE_MB [--check]\n",
 [UNMOUNT] = 
 "    imagex unmount DIRECTORY [--commit] [--check]\n",
 };
@@ -122,10 +126,12 @@ static const struct option common_options[] = {
 };
 
 static const struct option append_options[] = {
-       {"boot",   no_argument,       NULL, 'b'},
-       {"check",  no_argument,       NULL, 'c'},
-       {"flags",    required_argument, NULL, 'f'},
-       {"dereference", no_argument, NULL, 'L'},
+       {"boot",        no_argument,       NULL, 'b'},
+       {"check",       no_argument,       NULL, 'c'},
+       {"config",      required_argument, NULL, 'C'},
+       {"dereference", no_argument,       NULL, 'L'},
+       {"flags",       required_argument, NULL, 'f'},
+       {"verbose",     no_argument,       NULL, 'v'},
        {NULL, 0, NULL, 0},
 };
 static const struct option apply_options[] = {
@@ -133,16 +139,16 @@ static const struct option apply_options[] = {
        {"hardlink", no_argument,       NULL, 'h'},
        {"symlink",  no_argument,       NULL, 's'},
        {"verbose",  no_argument,       NULL, 'v'},
-       {"mkntfs-args", required_argument, NULL, 'm'},
        {NULL, 0, NULL, 0},
 };
 static const struct option capture_options[] = {
-       {"boot",     no_argument,       NULL, 'b'},
-       {"check",    no_argument,       NULL, 'c'},
-       {"compress", optional_argument, NULL, 'x'},
-       {"flags",    required_argument, NULL, 'f'},
-       {"verbose",  no_argument,       NULL,'v'},
-       {"dereference", no_argument, NULL, 'L'},
+       {"boot",        no_argument,       NULL, 'b'},
+       {"check",       no_argument,       NULL, 'c'},
+       {"compress",    required_argument, NULL, 'x'},
+       {"config",      required_argument, NULL, 'C'},
+       {"dereference", no_argument,       NULL, 'L'},
+       {"flags",       required_argument, NULL, 'f'},
+       {"verbose",     no_argument,       NULL, 'v'},
        {NULL, 0, NULL, 0},
 };
 static const struct option delete_options[] = {
@@ -151,20 +157,20 @@ static const struct option delete_options[] = {
 };
 
 static const struct option export_options[] = {
-       {"boot",       no_argument, NULL, 'b'},
-       {"check",      no_argument , NULL, 'c'},
-       {"compress",   optional_argument, NULL, 'x'},
+       {"boot",       no_argument,       NULL, 'b'},
+       {"check",      no_argument,       NULL, 'c'},
+       {"compress",   required_argument, NULL, 'x'},
        {NULL, 0, NULL, 0},
 };
 
 static const struct option info_options[] = {
        {"boot",         no_argument, NULL, 'b'},
        {"check",        no_argument, NULL, 'c'},
+       {"extract-xml",  required_argument, NULL, 'X'},
        {"header",       no_argument, NULL, 'h'},
        {"lookup-table", no_argument, NULL, 'l'},
-       {"xml",          no_argument, NULL, 'x'},
-       {"extract-xml",  required_argument, NULL, 'X'},
        {"metadata",     no_argument, NULL, 'm'},
+       {"xml",          no_argument, NULL, 'x'},
        {NULL, 0, NULL, 0},
 };
 
@@ -176,7 +182,7 @@ static const struct option join_options[] = {
 static const struct option mount_options[] = {
        {"check", no_argument, NULL, 'c'},
        {"debug", no_argument, NULL, 'd'},
-       {"stream-interface", required_argument, NULL, 's'},
+       {"streams-interface", required_argument, NULL, 's'},
        {NULL, 0, NULL, 0},
 };
 
@@ -278,8 +284,6 @@ static int verify_image_exists_and_is_single(int image)
 
 static int get_compression_type(const char *optarg)
 {
-       if (!optarg)
-               return WIM_COMPRESSION_TYPE_LZX;
        if (strcasecmp(optarg, "maximum") == 0 || strcasecmp(optarg, "lzx") == 0)
                return WIM_COMPRESSION_TYPE_LZX;
        else if (strcasecmp(optarg, "fast") == 0 || strcasecmp(optarg, "xpress") == 0)
@@ -293,6 +297,45 @@ static int get_compression_type(const char *optarg)
        }
 }
 
+static const char *file_get_contents(const char *filename, size_t *len_ret)
+{
+       struct stat stbuf;
+       char *buf;
+       size_t len;
+       FILE *fp;
+
+       if (stat(filename, &stbuf) != 0) {
+               imagex_error_with_errno("Failed to stat the file `%s'", filename);
+               return NULL;
+       }
+       len = stbuf.st_size;
+
+       fp = fopen(filename, "rb");
+       if (!fp) {
+               imagex_error_with_errno("Failed to open the file `%s'", filename);
+               return NULL;
+       }
+
+       buf = malloc(len);
+       if (!buf) {
+               imagex_error("Failed to allocate buffer of %zu bytes to hold "
+                            "contents of file `%s'", len, filename);
+               goto out_fclose;
+       }
+       if (fread(buf, 1, len, fp) != len) {
+               imagex_error_with_errno("Failed to read %lu bytes from the "
+                                       "file `%s'", len, filename);
+               goto out_free_buf;
+       }
+       *len_ret = len;
+       return buf;
+out_free_buf:
+       free(buf);
+out_fclose:
+       fclose(fp);
+       return NULL;
+}
+
 static int imagex_append(int argc, const char **argv)
 {
        int c;
@@ -304,8 +347,12 @@ static int imagex_append(int argc, const char **argv)
        const char *wimfile;
        const char *name;
        const char *desc;
+       const char *config_file = NULL;
+       const char *config_str = NULL;
+       size_t config_len = 0;
        WIMStruct *w;
        int ret;
+       int cur_image;
 
        for_opt(c, append_options) {
                switch (c) {
@@ -316,12 +363,18 @@ static int imagex_append(int argc, const char **argv)
                        open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
                        write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
                        break;
+               case 'C':
+                       config_file = optarg;
+                       break;
                case 'f':
                        flags_element = optarg;
                        break;
                case 'L':
                        add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE;
                        break;
+               case 'v':
+                       add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_VERBOSE;
+                       break;
                default:
                        usage(APPEND);
                        return -1;
@@ -338,16 +391,56 @@ static int imagex_append(int argc, const char **argv)
        name    = (argc >= 3) ? argv[2] : path_basename(dir);
        desc    = (argc >= 4) ? argv[3] : NULL;
 
+       if (config_file) {
+               config_str = file_get_contents(config_file, &config_len);
+               if (!config_str)
+                       return -1;
+       }
+
        ret = wimlib_open_wim(wimfile, open_flags, &w);
        if (ret != 0)
                return ret;
 
-       ret = wimlib_add_image(w, dir, name, desc, 
-                              flags_element, add_image_flags);
+#ifdef WITH_NTFS_3G
+       struct stat stbuf;
+
+       ret = stat(dir, &stbuf);
+       if (ret == 0) {
+               if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
+                       const char *ntfs_device = dir;
+                       printf("Capturing WIM image NTFS filesystem on `%s'\n",
+                              ntfs_device);
+                       ret = wimlib_add_image_from_ntfs_volume(w, ntfs_device,
+                                                               name,
+                                                               config_str,
+                                                               config_len,
+                                                               add_image_flags);
+                       goto out_write;
+               }
+       } else {
+               if (errno != -ENOENT)
+                       imagex_error_with_errno("Failed to stat `%s'", dir);
+       }
+#endif
+       ret = wimlib_add_image(w, dir, name, config_str, config_len,
+                              add_image_flags);
+
+out_write:
        if (ret != 0)
-               goto done;
+               goto out;
+       cur_image = wimlib_get_num_images(w);
+       if (desc) {
+               ret = wimlib_set_image_descripton(w, cur_image, desc);
+               if (ret != 0)
+                       goto out;
+       }
+       if (flags_element) {
+               ret = wimlib_set_image_flags(w, cur_image, flags_element);
+               if (ret != 0)
+                       goto out;
+       }
        ret = wimlib_overwrite(w, write_flags);
-done:
+out:
        wimlib_free(w);
        return ret;
 }
@@ -364,10 +457,8 @@ static int imagex_apply(int argc, const char **argv)
        const char *wimfile;
        const char *dir;
        const char *image_num_or_name;
-       const char *mkntfs_args = "";
        int extract_flags = 0;
 
-
        for_opt(c, apply_options) {
                switch (c) {
                case 'c':
@@ -382,9 +473,6 @@ static int imagex_apply(int argc, const char **argv)
                case 'v':
                        extract_flags |= WIMLIB_EXTRACT_FLAG_VERBOSE;
                        break;
-               case 'm':
-                       mkntfs_args = optarg;
-                       break;
                default:
                        usage(APPLY);
                        return -1;
@@ -406,11 +494,6 @@ static int imagex_apply(int argc, const char **argv)
                dir = argv[2];
        }
 
-#ifdef WITH_NTFS_3G
-       char tmpdir[strlen(dir) + 50];
-       tmpdir[0] = '\0';
-#endif
-
        ret = wimlib_open_wim(wimfile, open_flags, &w);
        if (ret != 0)
                goto out;
@@ -430,102 +513,28 @@ static int imagex_apply(int argc, const char **argv)
        }
 
 #ifdef WITH_NTFS_3G
-       /* Check to see if a block device file or regular file was specified.
-        * If so, try to create a NTFS filesystem on it, mount it on a temporary
-        * directory, and replace @dir with the name of the temporary directory
-        * so that we extract the files to the NTFS filesystem. */
        struct stat stbuf;
 
        ret = stat(dir, &stbuf);
-       if (ret != 0)
-               imagex_error_with_errno("Failed to stat `%s'", dir);
-       if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
-               extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
-
-               const char *dev = dir;
-               printf("Making NTFS filesystem on `%s'\n", dev);
-
-               char mkntfs_cmdline[sizeof("mkntfs ") + strlen(mkntfs_args) +
-                                   sizeof(" ") + strlen(dev)];
-               sprintf(mkntfs_cmdline, "mkntfs %s %s", mkntfs_args, dev);
-               puts(mkntfs_cmdline);
-               ret = system(mkntfs_cmdline);
-               if (ret == -1) {
-                       imagex_error_with_errno("Failed to execute the "
-                                               "`mkntfs' program");
-                       return -1;
-               } else if (ret > 0) {
-                       imagex_error("`mkntfs' exited with failure status");
-                       imagex_error("Note: You can pass additional arguments "
-                                    "to `mkntfs' using the --mkntfs-args "
-                                    "argument");
-                       return -1;
-               }
-               sprintf(tmpdir, "/tmp/imagex-%d-ntfsmount-%s-XXXXXX",
-                               getpid(), dev);
-               dir = mkdtemp(tmpdir);
-               if (!dir) {
-                       imagex_error_with_errno("Failed to create "
-                                               "temporary directory "
-                                               "`%s'", tmpdir);
-               }
-               char ntfs_3g_cmdline[sizeof("ntfs-3g ") + strlen(dev) +
-                                    sizeof(" ") + strlen(dir)];
-               sprintf(ntfs_3g_cmdline, "ntfs-3g %s %s", dev, dir);
-               puts(ntfs_3g_cmdline);
-               ret = system(ntfs_3g_cmdline);
-               if (ret == -1) {
-                       imagex_error_with_errno("Failed to execute the "
-                                               "`ntfs-3g' program");
-                       ret = -1;
-                       goto out;
-               } else if (ret > 0) {
-                       imagex_error("`ntfs-3g' exited with failure status");
-                       ret = -1;
+       if (ret == 0) {
+               if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
+                       const char *ntfs_device = dir;
+                       printf("Applying image %d of `%s' to NTFS filesystem on `%s'\n",
+                              image, wimfile, ntfs_device);
+                       ret = wimlib_apply_image_to_ntfs_volume(w, image,
+                                                               ntfs_device,
+                                                               extract_flags);
                        goto out;
                }
-               printf("Applying image %d of `%s' to NTFS filesystem on `%s'\n",
-                      image, wimfile, dev);
+       } else {
+               if (errno != -ENOENT)
+                       imagex_error_with_errno("Failed to stat `%s'", dir);
        }
 #endif
 
        ret = wimlib_extract_image(w, image, dir, extract_flags);
 out:
        wimlib_free(w);
-#ifdef WITH_NTFS_3G
-       if (tmpdir[0] != '\0') {
-               /* Unmount and remove the NTFS-3g mounted directory */
-
-               int pid = fork();
-               int status;
-               if (pid == -1) {
-                       imagex_error_with_errno("Failed to fork()");
-                       return -1;
-               }
-               if (pid == 0) {
-                       execlp("fusermount", "fusermount", "-u", dir, NULL);
-                       imagex_error_with_errno("Failed to execute `fusermount'");
-                       return -1;
-               }
-
-               if (waitpid(pid, &status, 0) == -1) {
-                       imagex_error_with_errno("Failed to wait for "
-                                               "fusermount process to "
-                                               "terminate");
-                       return -1;
-               }
-
-               if (status != 0) {
-                       imagex_error("fusermount exited with status %d",
-                                    status);
-                       return -1;
-               }
-               if (rmdir(tmpdir) != 0) {
-                       imagex_error_with_errno("Failed to remove temporary "
-                                               "directory `%s'", tmpdir);
-               }
-       }
-#endif
        return ret;
 }
 
@@ -536,13 +545,17 @@ static int imagex_capture(int argc, const char **argv)
        int c;
        int add_image_flags = 0;
        int write_flags = WIMLIB_WRITE_FLAG_SHOW_PROGRESS;
-       int compression_type = WIM_COMPRESSION_TYPE_NONE;
+       int compression_type = WIM_COMPRESSION_TYPE_XPRESS;
        const char *flags_element = NULL;
        const char *dir;
        const char *wimfile;
        const char *name;
        const char *desc;
+       const char *config_file = NULL;
+       const char *config_str = NULL;
+       size_t config_len = 0;
        WIMStruct *w;
+       int cur_image;
        int ret;
 
        for_opt(c, capture_options) {
@@ -553,6 +566,9 @@ static int imagex_capture(int argc, const char **argv)
                case 'c':
                        write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
                        break;
+               case 'C':
+                       config_file = optarg;
+                       break;
                case 'x':
                        compression_type = get_compression_type(optarg);
                        if (compression_type == WIM_COMPRESSION_TYPE_INVALID)
@@ -563,9 +579,7 @@ static int imagex_capture(int argc, const char **argv)
                        break;
                case 'v':
                        add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_VERBOSE;
-                       break;
-               case 'N':
-                       add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_NTFS;
+                       write_flags |= WIMLIB_WRITE_FLAG_VERBOSE;
                        break;
                case 'L':
                        add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE;
@@ -587,21 +601,61 @@ static int imagex_capture(int argc, const char **argv)
        name    = (argc >= 3) ? argv[2] : dir;
        desc    = (argc >= 4) ? argv[3] : NULL;
 
+       if (config_file) {
+               config_str = file_get_contents(config_file, &config_len);
+               if (!config_str)
+                       return -1;
+       }
+
        ret = wimlib_create_new_wim(compression_type, &w);
        if (ret != 0)
                return ret;
 
-       ret = wimlib_add_image(w, dir, name, desc, flags_element, 
-                              add_image_flags);
+#ifdef WITH_NTFS_3G
+       struct stat stbuf;
+
+       ret = stat(dir, &stbuf);
+       if (ret == 0) {
+               if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
+                       const char *ntfs_device = dir;
+                       printf("Capturing WIM image NTFS filesystem on `%s'\n",
+                              ntfs_device);
+                       ret = wimlib_add_image_from_ntfs_volume(w, ntfs_device,
+                                                               name,
+                                                               config_str,
+                                                               config_len,
+                                                               add_image_flags);
+                       goto out_write;
+               }
+       } else {
+               if (errno != -ENOENT)
+                       imagex_error_with_errno("Failed to stat `%s'", dir);
+       }
+#endif
+       ret = wimlib_add_image(w, dir, name, config_str,
+                              config_len, add_image_flags);
+
+out_write:
        if (ret != 0) {
                imagex_error("Failed to add the image `%s'", dir);
-               goto done;
+               goto out;
+       }
+       cur_image = wimlib_get_num_images(w);
+       if (desc) {
+               ret = wimlib_set_image_descripton(w, cur_image, desc);
+               if (ret != 0)
+                       goto out;
+       }
+       if (flags_element) {
+               ret = wimlib_set_image_flags(w, cur_image, flags_element);
+               if (ret != 0)
+                       goto out;
        }
 
        ret = wimlib_write(w, wimfile, WIM_ALL_IMAGES, write_flags);
        if (ret != 0)
                imagex_error("Failed to write the WIM file `%s'", wimfile);
-done:
+out:
        wimlib_free(w);
        return ret;
 }
@@ -737,7 +791,7 @@ static int imagex_export(int argc, const char **argv)
        int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
        int export_flags = 0;
        int write_flags = WIMLIB_WRITE_FLAG_SHOW_PROGRESS;
-       int compression_type = WIM_COMPRESSION_TYPE_NONE;
+       int compression_type = WIM_COMPRESSION_TYPE_XPRESS;
        bool compression_type_specified = false;
        const char *src_wimfile;
        const char *src_image_num_or_name;
@@ -792,7 +846,7 @@ static int imagex_export(int argc, const char **argv)
        if (stat(dest_wimfile, &stbuf) == 0) {
                wim_is_new = false;
                /* Destination file exists. */
-               if (!S_ISREG(stbuf.st_mode)) {
+               if (!S_ISREG(stbuf.st_mode) && !S_ISLNK(stbuf.st_mode)) {
                        imagex_error("`%s' is not a regular file",
                                        dest_wimfile);
                        goto done;