*/
/*
- * 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/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_ltes(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_resource(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_resource(src_lookup_table, hash);
+ if (!src_lte)
+ return resource_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;
-
- src_wim = ((struct wim_pair*)arg)->src_wim;
- dest_wim = ((struct wim_pair*)arg)->dest_wim;
- 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) /* Empty or nonexistent stream. */
- continue;
-
- 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;
-
- lte_list_head = &((struct wim_pair*)arg)->lte_list_head;
- wimlib_assert(!list_empty(lte_list_head));
+ struct wim_lookup_table *lookup_table = _lookup_table;
- 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));
-
- lookup_table_insert(dest_wim->lookup_table, dest_lte);
- }
+ lte->refcnt -= lte->out_refcnt;
+ if (lte->refcnt == 0) {
+ lookup_table_unlink(lookup_table, lte);
+ free_lookup_table_entry(lte);
}
return 0;
}
-/*
- * 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)
+/* 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 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;
- }
+ 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(src_wim) || !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;
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;
- } else {
- return 0;
+ /* 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 (!dest_name) {
- dest_name = wimlib_get_image_name(src_wim, src_image);
- DEBUG("Using name `%s' for source image %d",
- dest_name, src_image);
- }
+ /* 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;
- 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);
- }
+ /* 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);
- DEBUG("Exporting image %d from `%s'", src_image, src_wim->filename);
+ /* 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;
- 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;
- }
+ /* 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;
- ret = verify_swm_set(src_wim, additional_swms, num_additional_swms);
- if (ret != 0)
- return ret;
+ DEBUG("Exporting image %d from \"%"TS"\"",
+ image, src_wim->filename);
- 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;
- }
+ /* Determine destination image name and description. */
- 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;
- }
+ if (export_flags & WIMLIB_EXPORT_FLAG_NO_NAMES) {
+ next_dest_name = NULL;
+ } else if (dest_name) {
+ next_dest_name = dest_name;
+ } else {
+ next_dest_name = wimlib_get_image_name(src_wim,
+ image);
+ }
- /* 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) {
- DEBUG("Setting boot_idx to %d", dest_wim->hdr.image_count);
- wimlib_set_boot_idx(dest_wim, dest_wim->hdr.image_count);
- }
- ret = 0;
- goto out;
+ DEBUG("Using name \"%"TS"\"", next_dest_name);
-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, staging_list)
- free_lookup_table_entry(lte);
- }
+ 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);
+ }
-out:
- if (num_additional_swms) {
- free_lookup_table(src_wim->lookup_table);
- src_wim->lookup_table = src_wim_tab_save;
- }
- return ret;
-}
+ DEBUG("Using description \"%"TS"\"", next_dest_description);
-static int image_run_full_verifications(WIMStruct *w)
-{
- return for_dentry_in_tree(wim_root_dentry(w), verify_dentry, w);
-}
+ /* 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;
+ }
+
+ /* 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;
+ }
+
+ /* 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;
+ }
-static int lte_fix_refcnt(struct lookup_table_entry *lte, void *ctr)
-{
- if (lte->refcnt != lte->real_refcnt) {
- WARNING("The following lookup table entry has a reference "
- "count of %u, but", lte->refcnt);
- WARNING("We found %u references to it",
- lte->real_refcnt);
- print_lookup_table_entry(lte);
- lte->refcnt = lte->real_refcnt;
- ++*(unsigned long *)ctr;
}
+ /* 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;
-}
-
-/* Ideally this would be unnecessary... however, the WIMs for Windows 8 are
- * screwed up because some lookup table entries are referenced more times than
- * their stated reference counts. So theoretically, if we delete all the
- * references to a stream and then remove it, it might still be referenced
- * somewhere else, making a file be missing from the WIM... So, work around this
- * problem by looking at ALL the images to re-calculate the reference count of
- * EVERY lookup table entry. This only absolutely has to be done before an image
- * is deleted or before an image is mounted read-write. */
-int wim_run_full_verifications(WIMStruct *w)
-{
- int ret;
- for_lookup_table_entry(w->lookup_table, lte_zero_real_refcnt, NULL);
- w->all_images_verified = true;
- w->full_verification_in_progress = true;
- ret = for_image(w, WIMLIB_ALL_IMAGES, image_run_full_verifications);
- w->full_verification_in_progress = false;
- if (ret == 0) {
- unsigned long num_ltes_with_bogus_refcnt = 0;
- for (int i = 0; i < w->hdr.image_count; i++)
- w->image_metadata[i].metadata_lte->real_refcnt++;
- for_lookup_table_entry(w->lookup_table, lte_fix_refcnt,
- &num_ltes_with_bogus_refcnt);
- if (num_ltes_with_bogus_refcnt != 0) {
- WARNING("A total of %lu entries in the WIM's stream "
- "lookup table had to have\n"
- " their reference counts fixed.",
- num_ltes_with_bogus_refcnt);
- }
- } else {
- w->all_images_verified = false;
+out_rollback:
+ while ((image = wim_info_get_num_images(dest_wim->wim_info))
+ > orig_dest_image_count)
+ {
+ xml_delete_image(&dest_wim->wim_info, image);
+ }
+ 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;
}