wimlib_export() updates
authorEric Biggers <ebiggers3@gmail.com>
Sun, 21 Oct 2012 20:56:45 +0000 (15:56 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Sun, 21 Oct 2012 20:56:45 +0000 (15:56 -0500)
- Roll back changes if the function fails.
- Allow exporting an image that was just added with wimlib_add_image().

src/lookup_table.c
src/lookup_table.h
src/modify.c
src/mount.c
src/wimlib.h
src/xml.c

index 57bbc0c63d9bb1c116f7a6147cd55d1129d1a37b..941ad813e52890b3d1fdc53dc5eaac278e06fcd5 100644 (file)
@@ -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.
  *
index 5e4d1f5836d444dc8bd41ca29db8ad50cc9ea25a..524e17e67ae8ec4510af14158c4682a8e9f98d18 100644 (file)
@@ -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)
index f6886bbe1dcb32d2fb84f90095e29134e4d8211d..3e7e1c5684780f7fdf3a8a79c1f487bfae3bd73b 100644 (file)
@@ -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);
index 44dece2fdcfd3e71d7992dcabe27780b12dfc36d..478d1f643e63505b712f5b8d1dd1dce26316c6c2 100644 (file)
@@ -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);
index 00e81e902f5421ba2ce0b858219da76058fd6c80..98651debd9eea4f2171c95c3a3f1ea1fb3c4c42b 100644 (file)
@@ -388,7 +388,8 @@ enum wimlib_error_code {
  *     NULL, a default string is used.  Please see the manual page for
  *     <b>imagex capture</b> 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
index c377283751395b72392fbd11b5688adcf227a08f..ff7925204017f869fca3b9f5a5169fd80c6b5951 100644 (file)
--- 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,