From: Eric Biggers Date: Sat, 1 Sep 2012 03:04:46 +0000 (-0500) Subject: Export from split WIM X-Git-Tag: v1.0.0~21 X-Git-Url: https://wimlib.net/git/?p=wimlib;a=commitdiff_plain;h=6f841e85af6215e88bce12a34a00548664fba6ea;hp=1cfb17b5e36ad9b6148a14a5bcc8a68689ec10ec Export from split WIM --- diff --git a/doc/imagex-export.1.in b/doc/imagex-export.1.in index d455e9d1..e80a5d13 100644 --- a/doc/imagex-export.1.in +++ b/doc/imagex-export.1.in @@ -29,6 +29,10 @@ If given, \fIDEST_IMAGE_DESCRIPTION\fR specifies the description to give the image being exported to \fIDEST_WIMFILE\fR. The default is its description in \fISRC_WIMFILE\fR. +\fBimagex export\fR supports exporting images from stand-alone WIMs as well as +from split WIMs. However, you cannot export an image to a split WIM. See +\fBSPLIT WIMS\fR. + .SH OPTIONS .TP 6 \fB--boot\fR @@ -56,16 +60,50 @@ compression type must be the same as that of \fIDEST_WIMFILE\fR. You may also specify the actual names of the compression algorithms, "XPRESS" and "LZX", instead of "fast" and "maximum", respectively. -.SH NOTES +.TP +\fB--ref\fR="\fIGLOB\fR" +File glob of additional split WIM parts that are part of the split WIM being +exported. See \fBSPLIT_WIMS\fR. + +.SH SPLIT WIMS + +You may use \fBimagex export\fR to export images from a split WIM. The +\fISRC_WIMFILE\fR argument is used to specify the first part of the split WIM, and +the \fB--refs\fR="\fIGLOB\fR" option is used to provide a shell-style file glob +that specifies the additional parts of the split WIM. \fIGLOB\fR is expected to +be a single string on the command line, so \fIGLOB\fR must be quoted so that it +is protected against shell expansion. \fIGLOB\fR must expand to all parts of +the split WIM, except optionally the first part which may either omitted or +included in the glob (but the first part MUST be specified as \fISRC_WIMFILE\fR as +well). + +Here's an example. The names for the split WIMs usually go something like: + +.RS +.PP +.nf +mywim.swm +mywim2.swm +mywim3.swm +mywim4.swm +mywim5.swm +\. ... etc. +.RE -\fBimagex export\fR does not yet support split WIMs. +To export the first image of this split WIM to a new or existing WIM file +"other.wim", run: +.PP +.RS +imagex export mywim.swm 1 other.wim --ref="mywim*.swm" +.RE +.PP .SH EXAMPLES -Export the second image of 'boot.wim' to the new WIM file 'image2.wim', and +Export the second image of 'boot.wim' to the new WIM file 'new.wim', and change the compression type to maximum, if it wasn't maximum already: .RS .PP -image export boot.wim 2 image2.wim --compress=maximum +image export boot.wim 2 new.wim --compress=maximum .RE .PP diff --git a/programs/imagex.c b/programs/imagex.c index 07381a9b..fac692f0 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -81,7 +81,7 @@ static const char *usage_strings[] = { " 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]\n", +" [--compress=TYPE] [--ref=\"GLOB\"]\n", [INFO] = " imagex info WIMFILE [IMAGE_NUM | IMAGE_NAME] [NEW_NAME]\n" " [NEW_DESC] [--boot] [--check] [--header] [--lookup-table]\n" @@ -143,6 +143,7 @@ static const struct option export_options[] = { {"boot", no_argument, NULL, 'b'}, {"check", no_argument, NULL, 'c'}, {"compress", required_argument, NULL, 'x'}, + {"ref", required_argument, NULL, 'r'}, {NULL, 0, NULL, 0}, }; @@ -851,6 +852,9 @@ static int imagex_export(int argc, const char **argv) int image; struct stat stbuf; bool wim_is_new; + const char *swm_glob = NULL; + WIMStruct **additional_swms = NULL; + unsigned num_additional_swms = 0; for_opt(c, export_options) { switch (c) { @@ -867,6 +871,9 @@ static int imagex_export(int argc, const char **argv) return -1; compression_type_specified = true; break; + case 'r': + swm_glob = optarg; + break; default: usage(EXPORT); return -1; @@ -883,7 +890,8 @@ static int imagex_export(int argc, const char **argv) 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_w); + ret = wimlib_open_wim(src_wimfile, + open_flags | WIMLIB_OPEN_FLAG_SPLIT_OK, &src_w); if (ret != 0) return ret; @@ -896,11 +904,11 @@ static int imagex_export(int argc, const char **argv) if (!S_ISREG(stbuf.st_mode) && !S_ISLNK(stbuf.st_mode)) { imagex_error("`%s' is not a regular file", dest_wimfile); - goto done; + goto out; } ret = wimlib_open_wim(dest_wimfile, open_flags, &dest_w); if (ret != 0) - goto done; + goto out; if (compression_type_specified && compression_type != wimlib_get_compression_type(dest_w)) { @@ -908,7 +916,7 @@ static int imagex_export(int argc, const char **argv) "not the same as that used in the " "destination WIM"); ret = -1; - goto done; + goto out; } compression_type = wimlib_get_compression_type(dest_w); } else { @@ -917,23 +925,32 @@ static int imagex_export(int argc, const char **argv) if (errno == ENOENT) { ret = wimlib_create_new_wim(compression_type, &dest_w); if (ret != 0) - goto done; + goto out; } else { imagex_error_with_errno("Cannot stat file `%s'", dest_wimfile); - goto done; + goto out; } } image = wimlib_resolve_image(src_w, src_image_num_or_name); ret = verify_image_exists(image); if (ret != 0) - goto done; + goto out; + + if (swm_glob) { + ret = open_swms_from_glob(swm_glob, src_wimfile, open_flags, + &additional_swms, + &num_additional_swms); + if (ret != 0) + goto out; + } ret = wimlib_export_image(src_w, image, dest_w, dest_name, dest_desc, - export_flags); + export_flags, additional_swms, + num_additional_swms); if (ret != 0) - goto done; + goto out; if (wim_is_new) @@ -941,9 +958,12 @@ static int imagex_export(int argc, const char **argv) write_flags); else ret = wimlib_overwrite(dest_w, write_flags); -done: +out: wimlib_free(src_w); wimlib_free(dest_w); + if (additional_swms) + for (unsigned i = 0; i < num_additional_swms; i++) + wimlib_free(additional_swms[i]); return ret; } diff --git a/src/join.c b/src/join.c index 07cbfa97..a1639e36 100644 --- a/src/join.c +++ b/src/join.c @@ -138,6 +138,21 @@ int verify_swm_set(WIMStruct *w, WIMStruct **additional_swms, return 0; } +/* + * Joins lookup tables from the parts of a split WIM. + * + * @w specifies the first part, while @additional_swms and @num_additional_swms + * specify an array of points to the WIMStruct's for additional split WIM parts. + * + * On success, 0 is returned on a pointer to the joined lookup table is returned + * in @table_ret. + * + * The reason we join the lookup tables is so: + * - We only have to search one lookup table to find the location of a + * resource in the entire split WIM. + * - Each lookup table entry will have a pointer to its split WIM part (and + * a part number field, although we don't really use it). + */ int new_joined_lookup_table(WIMStruct *w, WIMStruct **additional_swms, unsigned num_additional_swms, diff --git a/src/lookup_table.h b/src/lookup_table.h index f0ee25b7..6fc5ae50 100644 --- a/src/lookup_table.h +++ b/src/lookup_table.h @@ -41,55 +41,97 @@ struct ntfs_location { * * It is used to find data streams for files in the WIM. * - * The lookup_table_entry for a given dentry in the WIM is found using the SHA1 - * message digest field. + * Metadata resources and reparse point data buffers will also have lookup table + * entries associated with the data. + * + * The lookup_table_entry for a given dentry or alternate stream entry in the + * WIM is found using the SHA1 message digest field. */ struct lookup_table_entry { /* List of lookup table entries in this hash bucket */ struct hlist_node hash_list; - /* @resource_entry is read from the lookup table in the WIM - * file; it says where to find the file resource in the WIM - * file, and whether it is compressed or not. */ + /* Location and size of the stream in the WIM, whether it is compressed + * or not, and whether it's a metadata resource or not. This is an + * on-disk field. */ struct resource_entry resource_entry; - /* Currently ignored; set to 1 in new lookup table entries. */ + /* Specifies which part of the split WIM the resource is located in. + * This is on on-disk field. + * + * In stand-alone WIMs, this must be 1. + * + * In split WIMs, every split WIM part has its own lookup table, and in + * read_lookup_table() it's currently expected that the part number of + * each lookup table entry in a split WIM part's lookup table is the + * same as the part number of that split WIM part. So this makes this + * field redundant since we store a pointer to the corresponding + * WIMStruct in the lookup table entry anyway. + */ u16 part_number; - /* If %true, this lookup table entry corresponds to a symbolic link - * reparse buffer. @symlink_reparse_data_buf will give the target of - * the symbolic link. */ + /* An enumerated type that identifies where the stream corresponding to + * this lookup table entry is actually located. + * + * Obviously if we open a WIM and read its lookup table, the location is + * set to RESOURCE_IN_WIM since all the streams will initially be + * located in the WIM. However, to deal with problems such as image + * capture and image mount, we allow the actual location of the stream + * to be somewhere else, such as an external file. + */ enum { + /* The lookup table entry does not correspond to a stream (this + * state should exist only temporarily) */ RESOURCE_NONEXISTENT = 0, + + /* The stream resource is located in a WIM file. The WIMStruct + * for the WIM file will be pointed to by the @wim member. */ RESOURCE_IN_WIM, + + /* The stream resource is located in an external file. The + * name of the file will be provided by @file_on_disk member. + * In addition, if @file_on_disk_fp is not NULL, it will be an + * open FILE * to the file. */ RESOURCE_IN_FILE_ON_DISK, + + /* The stream resource is located in an external file in the + * staging directory for a read-write mount. */ RESOURCE_IN_STAGING_FILE, + + /* The stream resource is directly attached in an in-memory + * buffer pointed to by @attached_buffer. */ RESOURCE_IN_ATTACHED_BUFFER, + + /* The stream resource is located in an NTFS volume. It is + * identified by volume, filename, data stream name, and by + * whether it is a reparse point or not. @ntfs_loc points to a + * structure containing this information. */ RESOURCE_IN_NTFS_VOLUME, } resource_location; - /* Number of times this lookup table entry is referenced by dentries. */ + /* (On-disk field) + * Number of times this lookup table entry is referenced by dentries. + * Unfortunately, this field is not always set correctly in Microsoft's + * WIMs, so we have no choice but to fix it if more references to the + * lookup table entry are found than stated here. */ u32 refcnt; union { - /* SHA1 hash of the file resource pointed to by this lookup - * table entry */ + /* (On-disk field) SHA1 message digest of the stream referenced + * by this lookup table entry */ u8 hash[SHA1_HASH_SIZE]; - /* First 4 or 8 bytes of the SHA1 hash, used for inserting the - * entry into the hash table. Since the SHA1 hashes can be - * considered random, we don't really need the full 20 byte hash - * just to insert the entry in a hash table. */ + /* First 4 or 8 bytes of the SHA1 message digest, used for + * inserting the entry into the hash table. Since the SHA1 + * message digest can be considered random, we don't really need + * the full 20 byte hash just to insert the entry in a hash + * table. */ size_t hash_short; }; - /* If @file_on_disk != NULL, the file resource indicated by this lookup - * table entry is not in the WIM file, but rather a file on disk; this - * occurs for files that are added to the WIM. In that case, - * file_on_disk is the name of the file in the outside filesystem. - * It will not be compressed, and its size will be given by - * resource_entry.size and resource_entry.original_size. */ + /* Pointers to somewhere where the stream is actually located. See the + * comments for the @resource_location field above. */ union { WIMStruct *wim; char *file_on_disk; @@ -100,14 +142,25 @@ struct lookup_table_entry { #endif }; union { + /* Temporary field for creating a singly linked list. Shouldn't + * really be here */ struct lookup_table_entry *next_lte_in_swm; + + /* @file_on_disk_fp and @attr are both used to cache file/stream + * handles so we don't have re-open them on every read */ FILE *file_on_disk_fp; #ifdef WITH_NTFS_3G struct _ntfs_attr *attr; #endif }; #ifdef WITH_FUSE - /* File descriptors table for this data stream */ + /* File descriptors table for this data stream. This is used if the WIM + * is mounted. Basically, each time a file is open()ed, a new file + * descriptor is added here, and each time a file is close()ed, the file + * descriptor is gotten rid of. If the stream is opened for writing, it + * will be extracted to the staging directory and there will be an + * actual native file descriptor associated with each "wimlib file + * descriptor". */ u16 num_opened_fds; u16 num_allocated_fds; struct wimlib_fd **fds; @@ -115,22 +168,31 @@ struct lookup_table_entry { /* When a WIM file is written, out_refcnt starts at 0 and is incremented * whenever the file resource pointed to by this lookup table entry - * needs to be written. Naturally, the file resource only need to be - * written when out_refcnt is 0. Incrementing it further is needed to - * find the correct reference count to write to the lookup table in the - * output file, which may be less than the regular refcnt if not all - * images in the WIM file are written. - * - * output_resource_entry is the struct resource_entry for the position of the - * file resource when written to the output file. */ + * needs to be written. The file resource only need to be written when + * out_refcnt is nonzero, since otherwise it is not referenced by any + * dentries. */ u32 out_refcnt; + union { + /* When a WIM file is written, @output_resource_entry is filled + * in with the resource entry for the output WIM. This will not + * necessarily be the same as the @resource_entry since: + * - The stream may have a different offset in the new WIM + * - The stream may have a different compressed size in the + * new WIM if the compression type changed + */ struct resource_entry output_resource_entry; + + /* This field is used for the special hardlink or symlink image + * application mode. In these mode, all identical files are + * linked together, and @extracted_file will be set to the + * filename of the first extracted file containing this stream. + * */ char *extracted_file; }; /* Circular linked list of streams that share the same lookup table - * entry + * entry. * * This list of streams may include streams from different hard link * sets that happen to be the same. */ diff --git a/src/modify.c b/src/modify.c index 5223b819..8519881d 100644 --- a/src/modify.c +++ b/src/modify.c @@ -267,16 +267,12 @@ static int add_lte_to_dest_wim(struct dentry *dentry, void *arg) if (dest_lte) { dest_lte->refcnt++; } else { - dest_lte = new_lookup_table_entry(); + dest_lte = MALLOC(sizeof(struct lookup_table_entry)); if (!dest_lte) return WIMLIB_ERR_NOMEM; - dest_lte->resource_location = RESOURCE_IN_WIM; - dest_lte->wim = src_wim; - memcpy(&dest_lte->resource_entry, - &src_lte->resource_entry, - sizeof(struct resource_entry)); - copy_hash(dest_lte->hash, - dentry_stream_hash_unresolved(dentry, i)); + memcpy(dest_lte, src_lte, sizeof(struct lookup_table_entry)); + dest_lte->part_number = 1; + dest_lte->refcnt = 1; lookup_table_insert(dest_wim->lookup_table, dest_lte); } } @@ -356,19 +352,22 @@ WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, WIMStruct *dest_wim, const char *dest_name, const char *dest_description, - int flags) + int flags, + WIMStruct **additional_swms, + unsigned num_additional_swms) { int i; int ret; struct dentry *root; struct wim_pair wims; struct wim_security_data *sd; + struct lookup_table *joined_tab, *src_wim_tab_save; if (!src_wim || !dest_wim) return WIMLIB_ERR_INVALID_PARAM; - if (src_wim->hdr.total_parts != 1 || src_wim->hdr.total_parts != 1) { - ERROR("Exporting an image to or from a split WIM is " + if (dest_wim->hdr.total_parts != 1) { + ERROR("Exporting an image to a split WIM is " "unsupported"); return WIMLIB_ERR_SPLIT_UNSUPPORTED; } @@ -406,7 +405,9 @@ WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, ret = wimlib_export_image(src_wim, i, dest_wim, NULL, dest_description, - export_flags); + export_flags, + additional_swms, + num_additional_swms); if (ret != 0) return ret; } @@ -416,13 +417,6 @@ WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, } } - ret = wimlib_select_image(src_wim, src_image); - if (ret != 0) { - ERROR("Could not select image %d from the WIM `%s' " - "to export it", src_image, src_wim->filename); - return ret; - } - if (!dest_name) { dest_name = wimlib_get_image_name(src_wim, src_image); DEBUG("Using name `%s' for source image %d", @@ -437,12 +431,32 @@ WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, return WIMLIB_ERR_IMAGE_NAME_COLLISION; } + ret = verify_swm_set(src_wim, additional_swms, num_additional_swms); + if (ret != 0) + return ret; + + if (num_additional_swms) { + ret = new_joined_lookup_table(src_wim, additional_swms, + num_additional_swms, + &joined_tab); + if (ret != 0) + return ret; + src_wim_tab_save = src_wim->lookup_table; + src_wim->lookup_table = joined_tab; + } + + ret = wimlib_select_image(src_wim, src_image); + if (ret != 0) { + ERROR("Could not select image %d from the WIM `%s' " + "to export it", src_image, src_wim->filename); + goto out; + } /* Cleaning up here on failure would be hard. For example, we could * fail to allocate memory in add_lte_to_dest_wim(), * leaving the lookup table entries in the destination WIM in an * inconsistent state. Until these issues can be resolved, - * wimlib_export_image() is documented as leaving dest_wim is an + * wimlib_export_image() is documented as leaving dest_wim in an * indeterminate state. */ root = wim_root_dentry(src_wim); sd = wim_security_data(src_wim); @@ -451,10 +465,10 @@ WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, wims.dest_wim = dest_wim; ret = for_dentry_in_tree(root, add_lte_to_dest_wim, &wims); if (ret != 0) - return ret; + goto out; ret = add_new_dentry_tree(dest_wim, root, sd); if (ret != 0) - return ret; + goto out; sd->refcnt++; if (flags & WIMLIB_EXPORT_FLAG_BOOT) { @@ -462,8 +476,14 @@ WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, dest_wim->hdr.boot_idx = dest_wim->hdr.image_count; } - return xml_export_image(src_wim->wim_info, src_image, &dest_wim->wim_info, - dest_name, dest_description); + ret = xml_export_image(src_wim->wim_info, src_image, &dest_wim->wim_info, + dest_name, dest_description); +out: + if (num_additional_swms) { + free_lookup_table(src_wim->lookup_table); + src_wim->lookup_table = src_wim_tab_save; + } + return ret; } /* diff --git a/src/wimlib.h b/src/wimlib.h index 94a0f9d9..05681b3a 100644 --- a/src/wimlib.h +++ b/src/wimlib.h @@ -514,8 +514,8 @@ extern int wimlib_delete_image(WIMStruct *wim, int image); * Copies an image, or all the images, from a WIM file, into another WIM file. * * @param src_wim - * Pointer to the ::WIMStruct for a WIM file that contains the image(s) - * being exported. + * Pointer to the ::WIMStruct for a stand-alone WIM or part 1 of a split + * WIM that contains the image(s) being exported. * @param src_image * The image to export from @a src_wim. Can be the number of an image, or * ::WIM_ALL_IMAGES to export all images. @@ -541,6 +541,16 @@ extern int wimlib_delete_image(WIMStruct *wim, int image); * ::WIMLIB_EXPORT_FLAG_BOOT is valid only if one of the exported images is * currently marked as bootable in @a src_wim; if that is the case, then * that image is marked as bootable in the destination WIM. + * @param additional_swms + * Array of pointers to the ::WIMStruct for each additional part in the + * split WIM. Ignored if @a num_additional_swms is 0. The pointers do not + * need to be in any particular order, but they must include all parts of + * the split WIM other than the first part, which must be provided in the + * @a wim parameter. + * @param num_additional_swms + * Number of additional WIM parts provided in the @a additional_swms array. + * This number should be one less than the total number of parts in the + * split WIM. Set to 0 if the WIM is a standalone WIM. * * @return 0 on success; nonzero on error. On error, @dest_wim is left in an * indeterminate state and should be freed with wimlib_free(). @@ -570,13 +580,20 @@ extern int wimlib_delete_image(WIMStruct *wim, int image); * Failed to allocate needed memory. * @retval ::WIMLIB_ERR_READ * Could not read the metadata resource for @a src_image from @a src_wim. + * @retval ::WIMLIB_ERR_SPLIT_INVALID + * The source WIM is a split WIM, but the parts specified do not form a + * complete split WIM because they do not include all the parts of the + * original WIM, there are duplicate parts, or not all the parts have the + * same GUID and compression type. * @retval ::WIMLIB_ERR_SPLIT_UNSUPPORTED - * @a src_wim or @a dest_wim is part of a split WIM. Exporting an image - * from or to a split WIM is unsupported. + * @a dest_wim is part of a split WIM. Exporting an image to a split WIM + * is unsupported. */ extern int wimlib_export_image(WIMStruct *src_wim, int src_image, WIMStruct *dest_wim, const char *dest_name, - const char *dest_description, int flags); + const char *dest_description, int flags, + WIMStruct **additional_swms, + unsigned num_additional_swms); /** * Extracts an image, or all images, from a standalone or split WIM file.