X-Git-Url: https://wimlib.net/git/?a=blobdiff_plain;ds=sidebyside;f=programs%2Fimagex.c;h=12bfbc6201e888c839073c2611adc0266e7674af;hb=35dd893208119869db6a90b1d1663f9aea88a3f7;hp=a1b9cea29654fce6423005309277ba4ee73f0056;hpb=6f7956a06fcf92a304fae93e393e8eaee34e92d5;p=wimlib diff --git a/programs/imagex.c b/programs/imagex.c index a1b9cea2..12bfbc62 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,7 @@ #define for_opt(c, opts) while ((c = getopt_long_only(argc, (char**)argv, "", \ opts, NULL)) != -1) + enum imagex_op_type { APPEND, APPLY, @@ -74,37 +76,41 @@ static const char *path_basename(const char *path) static const char *usage_strings[] = { [APPEND] = -" imagex append DIRECTORY WIMFILE [\"IMAGE_NAME\"] [\"DESCRIPTION\"] [--boot]\n" -" [--check] [--config CONFIG_FILE] [--flags EDITIONID] [--verify]\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" -" [--verify] [--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" -" [--boot] [--check] [--compress[=TYPE]] [--config CONFIG_FILE]\n" -" [--flags \"EditionID\"] [--norpfix] [--verify] [--verbose]\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" -" [--xml] [--extract-xml FILE] [--metadata]\n", +" [NEW_DESC] [--boot] [--check] [--header] [--lookup-table]\n" +" [--xml] [--extract-xml FILE] [--metadata]\n", [JOIN] = " imagex join [--check] WIMFILE SPLIT_WIM...\n", [MOUNT] = " imagex mount WIMFILE (IMAGE_NUM | IMAGE_NAME) DIRECTORY\n" -" [--check] [--debug]\n", +" [--check] [--debug] [--streams-interface=INTERFACE]\n", [MOUNTRW] = " imagex mountrw WIMFILE [IMAGE_NUM | IMAGE_NAME] DIRECTORY\n" -" [--check] [--debug]\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", }; @@ -116,30 +122,30 @@ static const struct option common_options[] = { }; static const struct option append_options[] = { - {"boot", no_argument, NULL, 'b'}, - {"check", no_argument, NULL, 'c'}, - {"config", required_argument, NULL, 'C'}, - {"flags", required_argument, NULL, 'f'}, - {"verify", no_argument, NULL, 'V'}, + {"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[] = { {"check", no_argument, NULL, 'c'}, - {"verify", no_argument, NULL, 'V'}, {"hardlink", no_argument, NULL, 'h'}, {"symlink", no_argument, NULL, 's'}, {"verbose", no_argument, NULL, 'v'}, + {"ref", required_argument, NULL, 'r'}, {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'}, - {"config", required_argument, NULL, 'C'}, - {"flags", required_argument, NULL, 'f'}, - {"norpfix", no_argument, NULL, 'n'}, - {"verify", no_argument, NULL, 'V'}, - {"verbose", no_argument, NULL, 'v'}, + {"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[] = { @@ -148,20 +154,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}, }; @@ -173,6 +179,7 @@ static const struct option join_options[] = { static const struct option mount_options[] = { {"check", no_argument, NULL, 'c'}, {"debug", no_argument, NULL, 'd'}, + {"streams-interface", required_argument, NULL, 's'}, {NULL, 0, NULL, 0}, }; @@ -274,8 +281,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) @@ -289,6 +294,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; @@ -300,8 +344,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) { @@ -313,13 +361,16 @@ static int imagex_append(int argc, const char **argv) write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; case 'C': - /*config = optarg;*/ + config_file = optarg; break; case 'f': flags_element = optarg; break; - case 'V': - /* verify */ + 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); @@ -337,16 +388,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; } @@ -355,9 +446,8 @@ done: static int imagex_apply(int argc, const char **argv) { int c; - int link_type = WIM_LINK_TYPE_NONE; - int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS; - bool verbose = false; + int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS | + WIMLIB_OPEN_FLAG_SPLIT_OK; int image; int num_images; WIMStruct *w; @@ -365,23 +455,29 @@ 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; + + const char *swm_glob = NULL; + WIMStruct **additional_swms = NULL; + size_t num_additional_swms = 0; + glob_t globbuf; for_opt(c, apply_options) { switch (c) { case 'c': open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; break; - case 'V': - /* verify */ - break; case 'h': - link_type = WIM_LINK_TYPE_HARD; + extract_flags |= WIMLIB_EXTRACT_FLAG_HARDLINK; break; case 's': - link_type = WIM_LINK_TYPE_SYMBOLIC; + extract_flags |= WIMLIB_EXTRACT_FLAG_SYMLINK; break; case 'v': - verbose = true; + extract_flags |= WIMLIB_EXTRACT_FLAG_VERBOSE; + break; + case 'r': + swm_glob = optarg; break; default: usage(APPLY); @@ -411,7 +507,7 @@ static int imagex_apply(int argc, const char **argv) image = wimlib_resolve_image(w, image_num_or_name); ret = verify_image_exists(image); if (ret != 0) - goto done; + goto out; num_images = wimlib_get_num_images(w); if (argc == 2 && num_images != 1) { @@ -419,16 +515,68 @@ static int imagex_apply(int argc, const char **argv) "(or all)", wimfile, num_images); usage(APPLY); ret = -1; - goto done; + goto out; } - ret = wimlib_set_output_dir(w, dir); - if (ret != 0) - goto done; - wimlib_set_verbose(w, verbose); - wimlib_set_link_type(w, link_type); - ret = wimlib_extract_image(w, image); -done: + + if (swm_glob) { + ret = glob(swm_glob, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf); + if (ret != 0) { + imagex_error_with_errno("Failed to process glob " + "\"%s\"", swm_glob); + ret = -1; + goto out; + } + num_additional_swms = globbuf.gl_pathc; + additional_swms = calloc(num_additional_swms, sizeof(additional_swms[0])); + if (!additional_swms) { + imagex_error("Out of memory"); + ret = -1; + goto out; + } + size_t offset = 0; + for (size_t i = 0; i < num_additional_swms; i++) { + if (strcmp(globbuf.gl_pathv[i], wimfile) == 0) { + offset++; + continue; + } + ret = wimlib_open_wim(globbuf.gl_pathv[i], + open_flags | WIMLIB_OPEN_FLAG_SPLIT_OK, + &additional_swms[i - offset]); + if (ret != 0) + goto out; + } + num_additional_swms -= offset; + } + +#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("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, + additional_swms, + num_additional_swms); + goto out; + } + } else { + if (errno != ENOENT) + imagex_error_with_errno("Failed to stat `%s'", dir); + } +#endif + + ret = wimlib_extract_image(w, image, dir, extract_flags, + additional_swms, num_additional_swms); +out: wimlib_free(w); + if (additional_swms) + for (size_t i = 0; i < num_additional_swms; i++) + wimlib_free(additional_swms[i]); return ret; } @@ -439,14 +587,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; - const char *flags_element = NULL; - bool verbose = false; + 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) { @@ -457,25 +608,23 @@ 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) return -1; break; - case 'C': - /*config = optarg;*/ - break; case 'f': flags_element = optarg; break; - case 'n': - /*norpfix = true;*/ - break; case 'v': - verbose = true; + add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_VERBOSE; + write_flags |= WIMLIB_WRITE_FLAG_VERBOSE; break; - case 'V': - /*verify = true;*/ + case 'L': + add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE; break; default: usage(CAPTURE); @@ -494,23 +643,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; - wimlib_set_verbose(w, verbose); +#ifdef WITH_NTFS_3G + struct stat stbuf; - ret = wimlib_add_image(w, dir, name, desc, flags_element, - add_image_flags); + 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; } @@ -646,7 +833,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; @@ -701,7 +888,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; @@ -715,6 +902,7 @@ static int imagex_export(int argc, const char **argv) imagex_error("Cannot specify a compression type that is " "not the same as that used in the " "destination WIM"); + ret = -1; goto done; } compression_type = wimlib_get_compression_type(dest_w); @@ -1072,19 +1260,26 @@ static int imagex_mount_rw_or_ro(int argc, const char **argv) case 'd': mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG; break; + case 's': + if (strcasecmp(optarg, "none") == 0) + mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE; + else if (strcasecmp(optarg, "xattr") == 0) + mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR; + else if (strcasecmp(optarg, "windows") == 0) + mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS; + else { + imagex_error("Unknown stream interface \"%s\"", optarg); + goto mount_usage; + } + break; default: - usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) - ? MOUNTRW : MOUNT); - return -1; + goto mount_usage; } } argc -= optind; argv += optind; - if (argc != 2 && argc != 3) { - usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) - ? MOUNTRW : MOUNT); - return -1; - } + if (argc != 2 && argc != 3) + goto mount_usage; wimfile = argv[0]; @@ -1122,6 +1317,10 @@ static int imagex_mount_rw_or_ro(int argc, const char **argv) done: wimlib_free(w); return ret; +mount_usage: + usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) + ? MOUNTRW : MOUNT); + return -1; } /* Split a WIM into a spanned set */ @@ -1265,6 +1464,8 @@ int main(int argc, const char **argv) imagex_error("Exiting with error code %d:\n" " %s.", ret, wimlib_get_error_string(ret)); + if (ret == WIMLIB_ERR_NTFS_3G) + imagex_error_with_errno("errno"); } return ret; } @@ -1274,3 +1475,10 @@ int main(int argc, const char **argv) usage_all(); return 1; } +/*#ifndef WITH_NTFS_3G*/ + /*ERROR("wimlib was not compiled with support for NTFS-3g, so we cannot extract");*/ + /*ERROR("a WIM to a NTFS filesystem while preserving NTFS-specific metadata.");*/ + /*ERROR("Please apply the WIM to a directory rather than a block device, ");*/ + /*ERROR("and without the NTFS flag; or compile in support for NTFS-3g.");*/ + /*return WIMLIB_ERR_UNSUPPORTED;*/ +/*#endif*/