]> wimlib.net Git - wimlib/blobdiff - src/export_image.c
Refactor some of the dentry, inode, and lookup table code
[wimlib] / src / export_image.c
index 6d38855db04e9d8c4b9d403e56e9cf05b6b89bbf..56725e668bb77b113a4bf67fdd6b3c49476e051a 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*
- * Copyright (C) 2012 Eric Biggers
+ * Copyright (C) 2012, 2013 Eric Biggers
  *
  * This file is part of wimlib, a library for working with WIM files.
  *
  * along with wimlib; if not, see http://www.gnu.org/licenses/.
  */
 
-#include "wimlib_internal.h"
-#include "dentry.h"
-#include "lookup_table.h"
-#include "xml.h"
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "wimlib.h"
+#include "wimlib/dentry.h"
+#include "wimlib/error.h"
+#include "wimlib/inode.h"
+#include "wimlib/lookup_table.h"
+#include "wimlib/metadata.h"
+#include "wimlib/xml.h"
+#include <stdlib.h>
+
+static int
+inode_export_streams(struct wim_inode *inode,
+                    const struct wim_lookup_table *src_lookup_table,
+                    struct wim_lookup_table *dest_lookup_table)
+{
+       unsigned i;
+       const u8 *hash;
+       struct wim_lookup_table_entry *src_lte, *dest_lte;
 
-struct wim_pair {
-       WIMStruct *src_wim;
-       WIMStruct *dest_wim;
-       struct list_head lte_list_head;
-};
+       inode_unresolve_streams(inode);
+       for (i = 0; i <= inode->i_num_ads; i++) {
 
-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->staging_list, lte_list_head);
-                       }
+               /* Retrieve SHA1 message digest of stream to export.  */
+               hash = inode_stream_hash(inode, i);
+               if (is_zero_hash(hash))  /* Empty stream?  */
+                       continue;
+
+               /* Search for the stream (via SHA1 message digest) in the
+                * destination WIM.  */
+               dest_lte = lookup_stream(dest_lookup_table, hash);
+               if (!dest_lte) {
+                       /* Stream not yet present in destination WIM.  Search
+                        * for it in the source WIM, then export it into the
+                        * destination WIM.  */
+                       src_lte = lookup_stream(src_lookup_table, hash);
+                       if (!src_lte)
+                               return stream_not_found_error(inode, hash);
+
+                       dest_lte = clone_lookup_table_entry(src_lte);
+                       if (!dest_lte)
+                               return WIMLIB_ERR_NOMEM;
+                       dest_lte->refcnt = 0;
+                       dest_lte->out_refcnt = 0;
+                       lookup_table_insert(dest_lookup_table, dest_lte);
                }
+
+               /* Stream is present in destination WIM (either pre-existing,
+                * already exported, or just exported above).  Increment its
+                * reference count appropriately.   Note: we use 'refcnt' for
+                * the raw reference count, but 'out_refcnt' for references
+                * arising just from the export operation; this is used to roll
+                * back a failed export if needed.  */
+               dest_lte->refcnt += inode->i_nlink;
+               dest_lte->out_refcnt += inode->i_nlink;
        }
        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
- * with the dentry, if there is already a lookup table entry for that stream in
- * the lookup table of the destination WIM file, its reference count is
- * incrementej.  Otherwise, a new lookup table entry is created that points back
- * to the stream in the source WIM file (through the @hash field combined with
- * the @wim field of the lookup table entry.)
- */
-static int add_lte_to_dest_wim(struct dentry *dentry, void *arg)
+static int
+lte_unexport(struct wim_lookup_table_entry *lte, void *_lookup_table)
 {
-       WIMStruct *src_wim, *dest_wim;
-       struct inode *inode;
+       struct wim_lookup_table *lookup_table = _lookup_table;
 
-       src_wim = ((struct wim_pair*)arg)->src_wim;
-       dest_wim = ((struct wim_pair*)arg)->dest_wim;
-       inode = dentry->d_inode;
+       lte->refcnt -= lte->out_refcnt;
+       if (lte->refcnt == 0) {
+               lookup_table_unlink(lookup_table, lte);
+               free_lookup_table_entry(lte);
+       }
+       return 0;
+}
 
-       wimlib_assert(!inode->resolved);
+/* API function documented in wimlib.h  */
+WIMLIBAPI int
+wimlib_export_image(WIMStruct *src_wim,
+                   int src_image,
+                   WIMStruct *dest_wim,
+                   const tchar *dest_name,
+                   const tchar *dest_description,
+                   int export_flags,
+                   wimlib_progress_func_t progress_func)
+{
+       int ret;
+       int start_image;
+       int end_image;
+       int image;
+       u32 orig_dest_boot_idx;
+       u32 orig_dest_image_count;
+
+       /* Check for sane parameters.  */
+       if (src_wim == NULL || dest_wim == NULL)
+               return WIMLIB_ERR_INVALID_PARAM;
+
+       if (!wim_has_metadata(dest_wim))
+               return WIMLIB_ERR_METADATA_NOT_FOUND;
+
+       /* Destination WIM must be writable.  */
+       ret = can_modify_wim(dest_wim);
+       if (ret)
+               return ret;
 
-       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_image == WIMLIB_ALL_IMAGES) {
+               /* Multi-image export.  */
+               if ((!(export_flags & WIMLIB_EXPORT_FLAG_NO_NAMES) &&
+                       dest_name) ||
+                   (!(export_flags & WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS) &&
+                       dest_description))
+               {
+                       ERROR("Image name or image description was "
+                             "specified, but we are exporting "
+                             "multiple images");
+                       return WIMLIB_ERR_INVALID_PARAM;
+               }
+               start_image = 1;
+               end_image = src_wim->hdr.image_count;
+       } else {
+               start_image = src_image;
+               end_image = src_image;
+       }
 
-               if (!src_lte) /* Empty or nonexistent stream. */
-                       continue;
+       /* Stream checksums must be known before proceeding.  */
+       ret = wim_checksum_unhashed_streams(src_wim);
+       if (ret)
+               return ret;
+       ret = wim_checksum_unhashed_streams(dest_wim);
+       if (ret)
+               return ret;
 
-               dest_lte = inode_stream_lte_unresolved(inode, i,
-                                                      dest_wim->lookup_table);
-               if (dest_lte) {
-                       dest_lte->refcnt++;
-               } else {
-                       struct list_head *lte_list_head;
-                       struct list_head *next;
+       /* Zero 'out_refcnt' in all lookup table entries in the destination WIM;
+        * this tracks the number of references found from the source WIM
+        * image(s).  */
+       for_lookup_table_entry(dest_wim->lookup_table, lte_zero_out_refcnt,
+                              NULL);
 
-                       lte_list_head = &((struct wim_pair*)arg)->lte_list_head;
-                       wimlib_assert(!list_empty(lte_list_head));
+       /* Save the original count of images in the destination WIM and the boot
+        * index (used if rollback necessary).  */
+       orig_dest_image_count = dest_wim->hdr.image_count;
+       orig_dest_boot_idx = dest_wim->hdr.boot_idx;
 
-                       next = lte_list_head->next;
-                       list_del(next);
-                       dest_lte = container_of(next, struct lookup_table_entry,
-                                               staging_list);
-                       dest_lte->part_number = 1;
-                       dest_lte->refcnt = 1;
-                       wimlib_assert(hashes_equal(dest_lte->hash, src_lte->hash));
+       /* Export each requested image.  */
+       for (image = start_image; image <= end_image; image++) {
+               const tchar *next_dest_name, *next_dest_description;
+               struct wim_image_metadata *src_imd;
+               struct wim_inode *inode;
 
-                       lookup_table_insert(dest_wim->lookup_table, dest_lte);
-               }
-       }
-       return 0;
-}
+               DEBUG("Exporting image %d from \"%"TS"\"",
+                     image, src_wim->filename);
 
-/*
- * Copies an image, or all the images, from a WIM file, into another WIM file.
- */
-WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim,
-                                 int src_image,
-                                 WIMStruct *dest_wim,
-                                 const char *dest_name,
-                                 const char *dest_description,
-                                 int export_flags,
-                                 WIMStruct **additional_swms,
-                                 unsigned num_additional_swms,
-                                 wimlib_progress_func_t progress_func)
-{
-       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 (dest_wim->hdr.total_parts != 1) {
-               ERROR("Exporting an image to a split WIM is "
-                     "unsupported");
-               return WIMLIB_ERR_SPLIT_UNSUPPORTED;
-       }
+               /* Determine destination image name and description.  */
 
-       if (src_image == WIMLIB_ALL_IMAGES) {
-               if (src_wim->hdr.image_count > 1) {
-
-                       /* multi-image export. */
-
-                       if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
-                             (src_wim->hdr.boot_idx == 0))
-                       {
-                               /* Specifying the boot flag on a multi-image
-                                * source WIM makes the boot index default to
-                                * the bootable image in the source WIM.  It is
-                                * an error if there is no such bootable image.
-                                * */
-                               ERROR("Cannot specify `boot' flag when "
-                                     "exporting multiple images from a WIM "
-                                     "with no bootable images");
-                               return WIMLIB_ERR_INVALID_PARAM;
-                       }
-                       if (dest_name || dest_description) {
-                               ERROR("Image name or image description was "
-                                     "specified, but we are exporting "
-                                     "multiple images");
-                               return WIMLIB_ERR_INVALID_PARAM;
-                       }
-                       for (i = 1; i <= src_wim->hdr.image_count; i++) {
-                               int new_flags = export_flags;
-
-                               if (i != src_wim->hdr.boot_idx)
-                                       new_flags &= ~WIMLIB_EXPORT_FLAG_BOOT;
-
-                               ret = wimlib_export_image(src_wim, i, dest_wim,
-                                                         NULL, NULL,
-                                                         new_flags,
-                                                         additional_swms,
-                                                         num_additional_swms,
-                                                         progress_func);
-                               if (ret != 0)
-                                       return ret;
-                       }
-                       return 0;
-               } else if (src_wim->hdr.image_count == 1) {
-                       src_image = 1;
+               if (export_flags & WIMLIB_EXPORT_FLAG_NO_NAMES) {
+                       next_dest_name = NULL;
+               } else if (dest_name) {
+                       next_dest_name = dest_name;
                } else {
-                       return 0;
+                       next_dest_name = wimlib_get_image_name(src_wim,
+                                                              image);
                }
-       }
 
-       if (!dest_name) {
-               dest_name = wimlib_get_image_name(src_wim, src_image);
-               DEBUG("Using name `%s' for source image %d",
-                     dest_name, src_image);
-       }
+               DEBUG("Using name \"%"TS"\"", next_dest_name);
 
-       if (!dest_description) {
-               dest_description = wimlib_get_image_description(src_wim,
-                                                               src_image);
-               DEBUG("Using description `%s' for source image %d",
-                     dest_description, src_image);
-       }
+               if (export_flags & WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS) {
+                       next_dest_description = NULL;
+               } if (dest_description) {
+                       next_dest_description = dest_description;
+               } else {
+                       next_dest_description = wimlib_get_image_description(
+                                                       src_wim, image);
+               }
 
-       DEBUG("Exporting image %d from `%s'", src_image, src_wim->filename);
+               DEBUG("Using description \"%"TS"\"", next_dest_description);
 
-       if (wimlib_image_name_in_use(dest_wim, dest_name)) {
-               ERROR("There is already an image named `%s' in the "
-                     "destination WIM", dest_name);
-               return WIMLIB_ERR_IMAGE_NAME_COLLISION;
-       }
+               /* Check for name conflict.  */
+               if (wimlib_image_name_in_use(dest_wim, next_dest_name)) {
+                       ERROR("There is already an image named \"%"TS"\" "
+                             "in the destination WIM", next_dest_name);
+                       ret = WIMLIB_ERR_IMAGE_NAME_COLLISION;
+                       goto out_rollback;
+               }
 
-       ret = verify_swm_set(src_wim, additional_swms, num_additional_swms);
-       if (ret != 0)
-               return ret;
+               /* Load metadata for source image into memory.  */
+               ret = select_wim_image(src_wim, image);
+               if (ret)
+                       goto out_rollback;
+
+               src_imd = wim_get_current_image_metadata(src_wim);
+
+               /* Iterate through inodes in the source image and export their
+                * streams into the destination WIM.  */
+               image_for_each_inode(inode, src_imd) {
+                       ret = inode_export_streams(inode,
+                                                  src_wim->lookup_table,
+                                                  dest_wim->lookup_table);
+                       if (ret)
+                               goto out_rollback;
+               }
 
-       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;
-       }
+               /* Export XML information into the destination WIM.  */
+               ret = xml_export_image(src_wim->wim_info, image,
+                                      &dest_wim->wim_info, next_dest_name,
+                                      next_dest_description);
+               if (ret)
+                       goto out_rollback;
+
+               /* Reference the source image metadata from the destination WIM.
+                */
+               ret = append_image_metadata(dest_wim, src_imd);
+               if (ret)
+                       goto out_rollback;
+               src_imd->refcnt++;
+
+               /* Lock the metadata into memory.  XXX: need better solution for
+                * this.  */
+               src_imd->modified = 1;
+
+               /* Set boot index in destination WIM.  */
+               if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
+                   (src_image != WIMLIB_ALL_IMAGES ||
+                    image == src_wim->hdr.boot_idx))
+               {
+                       DEBUG("Marking destination image %u as bootable.",
+                             dest_wim->hdr.image_count);
+                       dest_wim->hdr.boot_idx = dest_wim->hdr.image_count;
+               }
 
-       ret = select_wim_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;
        }
+       /* Set the reparse point fixup flag on the destination WIM if the flag
+        * is set on the source WIM. */
+       if (src_wim->hdr.flags & WIM_HDR_FLAG_RP_FIX)
+               dest_wim->hdr.flags |= WIM_HDR_FLAG_RP_FIX;
+       DEBUG("Export operation successful.");
+       return 0;
 
-       /* 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;
-       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_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_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 (export_flags & WIMLIB_EXPORT_FLAG_BOOT)
-               wimlib_set_boot_idx(dest_wim, 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:
+out_rollback:
+       while ((image = wim_info_get_num_images(dest_wim->wim_info))
+              > orig_dest_image_count)
        {
-               struct lookup_table_entry *lte, *tmp;
-               list_for_each_entry_safe(lte, tmp, &wims.lte_list_head, staging_list)
-                       free_lookup_table_entry(lte);
+               xml_delete_image(&dest_wim->wim_info, image);
        }
-
-out:
-       if (num_additional_swms) {
-               free_lookup_table(src_wim->lookup_table);
-               src_wim->lookup_table = src_wim_tab_save;
+       while (dest_wim->hdr.image_count > orig_dest_image_count)
+       {
+               put_image_metadata(dest_wim->image_metadata[
+                                       --dest_wim->hdr.image_count], NULL);
        }
+       for_lookup_table_entry(dest_wim->lookup_table, lte_unexport,
+                              dest_wim->lookup_table);
+       dest_wim->hdr.boot_idx = orig_dest_boot_idx;
        return ret;
 }