From: Eric Biggers Date: Sun, 21 Oct 2012 20:56:45 +0000 (-0500) Subject: wimlib_export() updates X-Git-Tag: v1.0.4~45 X-Git-Url: https://wimlib.net/git/?p=wimlib;a=commitdiff_plain;h=5046b052366414f09940041ad79244c320d24751 wimlib_export() updates - Roll back changes if the function fails. - Allow exporting an image that was just added with wimlib_add_image(). --- diff --git a/src/lookup_table.c b/src/lookup_table.c index 57bbc0c6..941ad813 100644 --- a/src/lookup_table.c +++ b/src/lookup_table.c @@ -73,6 +73,61 @@ struct lookup_table_entry *new_lookup_table_entry() return lte; } +struct lookup_table_entry * +clone_lookup_table_entry(const struct lookup_table_entry *old) +{ + struct lookup_table_entry *new; + + new = MALLOC(sizeof(*new)); + if (!new) + return NULL; + + memcpy(new, old, sizeof(*old)); + new->extracted_file = NULL; + switch (new->resource_location) { + case RESOURCE_IN_STAGING_FILE: + case RESOURCE_IN_FILE_ON_DISK: + wimlib_assert((void*)&old->file_on_disk == + (void*)&old->staging_file_name); + new->staging_file_name = STRDUP(old->staging_file_name); + if (!new->staging_file_name) + goto out_free; + break; + case RESOURCE_IN_ATTACHED_BUFFER: + new->attached_buffer = MALLOC(wim_resource_size(old)); + if (!new->attached_buffer) + goto out_free; + memcpy(new->attached_buffer, old->attached_buffer, + wim_resource_size(old)); + break; + case RESOURCE_IN_NTFS_VOLUME: + if (old->ntfs_loc) { + struct ntfs_location *loc; + loc = MALLOC(sizeof(*loc)); + if (!loc) + goto out_free; + memcpy(loc, old->ntfs_loc, sizeof(*loc)); + loc->path_utf8 = NULL; + loc->stream_name_utf16 = NULL; + new->ntfs_loc = loc; + loc->path_utf8 = STRDUP(old->ntfs_loc->path_utf8); + if (!loc->path_utf8) + goto out_free; + loc->stream_name_utf16 = MALLOC(loc->stream_name_utf16_num_chars * 2); + if (!loc->stream_name_utf16) + goto out_free; + memcpy(loc->stream_name_utf16, + old->ntfs_loc->stream_name_utf16, + loc->stream_name_utf16_num_chars * 2); + } + break; + } + return new; +out_free: + free_lookup_table_entry(new); + return NULL; +} + void free_lookup_table_entry(struct lookup_table_entry *lte) { if (lte) { @@ -522,6 +577,8 @@ static void inode_resolve_ltes(struct inode *inode, struct lookup_table *table) { struct lookup_table_entry *lte; + wimlib_assert(!inode->resolved); + /* Resolve the default file stream */ lte = __lookup_resource(table, inode->hash); inode->lte = lte; @@ -535,6 +592,24 @@ static void inode_resolve_ltes(struct inode *inode, struct lookup_table *table) } } +static void inode_unresolve_ltes(struct inode *inode) +{ + wimlib_assert(inode->resolved); + if (inode->lte) + copy_hash(inode->hash, inode->lte->hash); + else + zero_out_hash(inode->hash); + + for (u16 i = 0; i < inode->num_ads; i++) { + if (inode->ads_entries[i].lte) + copy_hash(inode->ads_entries[i].hash, + inode->ads_entries[i].lte->hash); + else + zero_out_hash(inode->ads_entries[i].hash); + } + inode->resolved = false; +} + /* Resolve a dentry's lookup table entries * * This replaces the SHA1 hash fields (which are used to lookup an entry in the @@ -551,6 +626,13 @@ int dentry_resolve_ltes(struct dentry *dentry, void *table) return 0; } +int dentry_unresolve_ltes(struct dentry *dentry, void *ignore) +{ + if (dentry->d_inode->resolved) + inode_unresolve_ltes(dentry->d_inode); + return 0; +} + /* Return the lookup table entry for the unnamed data stream of an inode, or * NULL if there is none. * diff --git a/src/lookup_table.h b/src/lookup_table.h index 5e4d1f58..524e17e6 100644 --- a/src/lookup_table.h +++ b/src/lookup_table.h @@ -50,7 +50,10 @@ struct ntfs_location { struct lookup_table_entry { /* List of lookup table entries in this hash bucket */ - struct hlist_node hash_list; + union { + struct hlist_node hash_list; + struct list_head list; + }; /* 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 @@ -236,6 +239,9 @@ static inline void lookup_table_unlink(struct lookup_table *table, extern struct lookup_table_entry *new_lookup_table_entry(); +extern struct lookup_table_entry * +clone_lookup_table_entry(const struct lookup_table_entry *lte); + extern int for_lookup_table_entry(struct lookup_table *table, int (*visitor)(struct lookup_table_entry *, void *), void *arg); @@ -268,6 +274,7 @@ extern int write_lookup_table_entry(struct lookup_table_entry *lte, void *__out) extern void free_lookup_table_entry(struct lookup_table_entry *lte); extern int dentry_resolve_ltes(struct dentry *dentry, void *__table); +extern int dentry_unresolve_ltes(struct dentry *dentry, void *ignore); /* Writes the lookup table to the output file. */ static inline int write_lookup_table(struct lookup_table *table, FILE *out) diff --git a/src/modify.c b/src/modify.c index f6886bbe..3e7e1c56 100644 --- a/src/modify.c +++ b/src/modify.c @@ -236,8 +236,42 @@ out: struct wim_pair { WIMStruct *src_wim; WIMStruct *dest_wim; + struct list_head lte_list_head; }; +static int allocate_lte_if_needed(struct dentry *dentry, void *arg) +{ + const WIMStruct *src_wim, *dest_wim; + struct list_head *lte_list_head; + struct inode *inode; + + src_wim = ((struct wim_pair*)arg)->src_wim; + dest_wim = ((struct wim_pair*)arg)->dest_wim; + lte_list_head = &((struct wim_pair*)arg)->lte_list_head; + inode = dentry->d_inode; + + wimlib_assert(!inode->resolved); + + for (unsigned i = 0; i <= inode->num_ads; i++) { + struct lookup_table_entry *src_lte, *dest_lte; + src_lte = inode_stream_lte_unresolved(inode, i, + src_wim->lookup_table); + + if (src_lte && ++src_lte->out_refcnt == 1) { + dest_lte = inode_stream_lte_unresolved(inode, i, + dest_wim->lookup_table); + + if (!dest_lte) { + dest_lte = clone_lookup_table_entry(src_lte); + if (!dest_lte) + return WIMLIB_ERR_NOMEM; + list_add_tail(&dest_lte->list, lte_list_head); + } + } + } + return 0; +} + /* * This function takes in a dentry that was previously located only in image(s) * in @src_wim, but now is being added to @dest_wim. For each stream associated @@ -271,12 +305,19 @@ static int add_lte_to_dest_wim(struct dentry *dentry, void *arg) if (dest_lte) { dest_lte->refcnt++; } else { - dest_lte = MALLOC(sizeof(struct lookup_table_entry)); - if (!dest_lte) - return WIMLIB_ERR_NOMEM; - memcpy(dest_lte, src_lte, sizeof(struct lookup_table_entry)); + struct list_head *lte_list_head; + struct list_head *next; + + lte_list_head = &((struct wim_pair*)arg)->lte_list_head; + wimlib_assert(!list_empty(lte_list_head)); + + next = lte_list_head->next; + list_del(next); + dest_lte = container_of(next, struct lookup_table_entry, list); dest_lte->part_number = 1; dest_lte->refcnt = 1; + wimlib_assert(hashes_equal(dest_lte->hash, src_lte->hash)); + lookup_table_insert(dest_wim->lookup_table, dest_lte); } } @@ -290,6 +331,9 @@ static int add_lte_to_dest_wim(struct dentry *dentry, void *arg) * * Does not update the XML data. * + * On failure, WIMLIB_ERR_NOMEM is returned and no changes are made. Otherwise, + * 0 is returned and the image metadata array of @w is modified. + * * @w: The WIMStruct for the WIM file. * @root_dentry: The root of the directory tree for the image. * @sd: The security data for the image. @@ -463,32 +507,56 @@ WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, 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 in an indeterminate state with the only permitted - * operation being wimlib_free(). */ - root = wim_root_dentry(src_wim); - sd = wim_security_data(src_wim); - for_dentry_in_tree(root, increment_dentry_refcnt, NULL); + /* Pre-allocate the new lookup table entries that will be needed. This + * way, it's not possible to run out of memory part-way through + * modifying the lookup table of the destination WIM. */ wims.src_wim = src_wim; wims.dest_wim = dest_wim; - ret = for_dentry_in_tree(root, add_lte_to_dest_wim, &wims); + INIT_LIST_HEAD(&wims.lte_list_head); + for_lookup_table_entry(src_wim->lookup_table, lte_zero_out_refcnt, NULL); + root = wim_root_dentry(src_wim); + for_dentry_in_tree(root, dentry_unresolve_ltes, NULL); + ret = for_dentry_in_tree(root, allocate_lte_if_needed, &wims); if (ret != 0) - goto out; + goto out_free_ltes; + + ret = xml_export_image(src_wim->wim_info, src_image, + &dest_wim->wim_info, dest_name, dest_description); + if (ret != 0) + goto out_free_ltes; + + sd = wim_security_data(src_wim); ret = add_new_dentry_tree(dest_wim, root, sd); if (ret != 0) - goto out; + goto out_xml_delete_image; + + + /* All memory allocations have been taken care of, so it's no longer + * possible for this function to fail. Go ahead and increment the + * reference counts of the dentry tree and security data, then update + * the lookup table of the destination WIM and the boot index, if + * needed. */ + for_dentry_in_tree(root, increment_dentry_refcnt, NULL); sd->refcnt++; + for_dentry_in_tree(root, add_lte_to_dest_wim, &wims); + wimlib_assert(list_empty(&wims.lte_list_head)); if (flags & WIMLIB_EXPORT_FLAG_BOOT) { DEBUG("Setting boot_idx to %d", dest_wim->hdr.image_count); dest_wim->hdr.boot_idx = dest_wim->hdr.image_count; } + ret = 0; + goto out; + +out_xml_delete_image: + xml_delete_image(&dest_wim->wim_info, dest_wim->hdr.image_count); +out_free_ltes: + { + struct lookup_table_entry *lte, *tmp; + list_for_each_entry_safe(lte, tmp, &wims.lte_list_head, list) + free_lookup_table_entry(lte); + } - 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); diff --git a/src/mount.c b/src/mount.c index 44dece2f..478d1f64 100644 --- a/src/mount.c +++ b/src/mount.c @@ -1738,6 +1738,12 @@ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir, DEBUG("Selected image %d", image); + if (imd->root_dentry->refcnt != 1) { + ERROR("Cannot mount image that was just exported with " + "wimlib_export()"); + return WIMLIB_ERR_INVALID_PARAM; + } + next_ino = assign_inode_numbers(&imd->inode_list); DEBUG("(next_ino = %"PRIu64")", next_ino); diff --git a/src/wimlib.h b/src/wimlib.h index 00e81e90..98651deb 100644 --- a/src/wimlib.h +++ b/src/wimlib.h @@ -388,7 +388,8 @@ enum wimlib_error_code { * NULL, a default string is used. Please see the manual page for * imagex capture for more information. * @param config_len - * Length of the string @a config in bytes. + * Length of the string @a config in bytes. Ignored if @a config is @c + * NULL. * * @param flags * Bitwise OR of flags prefixed with WIMLIB_ADD_IMAGE_FLAG. If @@ -511,7 +512,28 @@ extern int wimlib_create_new_wim(int ctype, WIMStruct **wim_ret); extern int wimlib_delete_image(WIMStruct *wim, int image); /** - * Copies an image, or all the images, from a WIM file, into another WIM file. + * Exports an image, or all the images, from a WIM file, into another WIM file. + * + * The destination image is made to share the same dentry tree and security data + * structure as the source image. This places some restrictions on additional + * functions that may be called. wimlib_mount() may not be called on either the + * source image or the destination image without an intervening call to a + * function that un-shares the images, such as wimlib_free() on @a dest_wim, or + * wimlib_delete_image() on either the source or destination image. + * Furthermore, you may not call wimlib_free() or wimlib_overwrite() on @a + * src_wim before any calls to functions such as wimlib_write() on @a dest_wim + * because @a dest_wim will have references back to @a src_wim. + * + * Previous versions of this function left @a dest_wim in an indeterminate state + * on failure. This is no longer the case; all changes to @a dest_wim made by + * this function are rolled back on failure. + * + * Previous versions of this function did not allow exporting an image that had + * been added by wimlib_add_image(). This is no longer the case; you may now + * export an image regardless of how it was added. + * + * Regardless of whether this function succeeds or fails, no user-visible + * changes are made to @a src_wim. * * @param src_wim * Pointer to the ::WIMStruct for a stand-alone WIM or part 1 of a split @@ -552,8 +574,7 @@ extern int wimlib_delete_image(WIMStruct *wim, int image); * 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, @a dest_wim is left in an - * indeterminate state and should be freed with wimlib_free(). + * @return 0 on success; nonzero on error. * @retval ::WIMLIB_ERR_DECOMPRESSION * Could not decompress the metadata resource for @a src_image * in @a src_wim @@ -869,9 +890,10 @@ extern int wimlib_join(const char **swms, unsigned num_swms, * directory. * * wimlib_mount() currently cannot be used with multiple ::WIMStruct's without - * intervening wimlib_unmount()s. If there was a way to have libfuse pass a - * pointer to user data to each FUSE callback, then this would be possible, but - * there doesn't seem to be a way to do this currently. + * intervening wimlib_unmount()s. + * + * wimlib_mount() cannot be used on an image that was exported with + * wimlib_export() while the dentry trees for both images are still in memory. * * @param wim * Pointer to the ::WIMStruct for the WIM file to be mounted. @@ -909,6 +931,9 @@ extern int wimlib_join(const char **swms, unsigned num_swms, * invalid. * @retval ::WIMLIB_ERR_INVALID_IMAGE * @a image does not specify an existing, single image in @a wim. + * @retval ::WIMLIB_ERR_INVALID_PARAM + * @a image is shared among multiple ::WIMStruct's as a result of a call to + * wimlib_export(). * @retval ::WIMLIB_ERR_INVALID_RESOURCE_SIZE * The metadata resource for @a image in @a wim is invalid. * @retval ::WIMLIB_ERR_INVALID_SECURITY_DATA diff --git a/src/xml.c b/src/xml.c index c3772837..ff792520 100644 --- a/src/xml.c +++ b/src/xml.c @@ -866,7 +866,11 @@ static int clone_image_info(const struct image_info *old, struct image_info *new * * @dest_image_name and @dest_image_description are ignored if they are NULL; * otherwise, they are used to override the image name and/or image description - * from the XML data in the source WIM file. */ + * from the XML data in the source WIM file. + * + * On failure, WIMLIB_ERR_NOMEM is returned and no changes are made. Otherwise, + * 0 is returned and the WIM information at *new_wim_info_p is modified. + */ int xml_export_image(const struct wim_info *old_wim_info, int image, struct wim_info **new_wim_info_p,