*/
/*
- * Copyright (C) 2012, 2013 Eric Biggers
+ * Copyright (C) 2012-2016 Eric Biggers
*
- * This file is part of wimlib, a library for working with WIM files.
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
*
- * wimlib is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- * A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
- * You should have received a copy of the GNU General Public License
- * along with wimlib; if not, see http://www.gnu.org/licenses/.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this file; if not, see http://www.gnu.org/licenses/.
*/
#ifdef HAVE_CONFIG_H
#endif
#include "wimlib.h"
-#include "wimlib/dentry.h"
+#include "wimlib/blob_table.h"
#include "wimlib/error.h"
-#include "wimlib/lookup_table.h"
+#include "wimlib/inode.h"
#include "wimlib/metadata.h"
-#include "wimlib/swm.h"
#include "wimlib/xml.h"
static int
-inode_allocate_needed_ltes(struct wim_inode *inode,
- struct wim_lookup_table *src_lookup_table,
- struct wim_lookup_table *dest_lookup_table,
- struct list_head *lte_list_head)
+blob_set_not_exported(struct blob_descriptor *blob, void *_ignore)
{
- struct wim_lookup_table_entry *src_lte, *dest_lte;
- unsigned i;
+ blob->out_refcnt = 0;
+ blob->was_exported = 0;
+ return 0;
+}
- inode_unresolve_ltes(inode);
- for (i = 0; i <= inode->i_num_ads; i++) {
- src_lte = inode_stream_lte_unresolved(inode, i,
- src_lookup_table);
- if (src_lte && src_lte->out_refcnt == 0) {
- src_lte->out_refcnt = 1;
- dest_lte = inode_stream_lte_unresolved(inode, i,
- dest_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->export_stream_list,
- lte_list_head);
- }
- }
+static int
+blob_rollback_export(struct blob_descriptor *blob, void *_blob_table)
+{
+ struct blob_table *blob_table = _blob_table;
+
+ blob->refcnt -= blob->out_refcnt;
+ if (blob->was_exported) {
+ blob_table_unlink(blob_table, blob);
+ free_blob_descriptor(blob);
}
return 0;
}
-static void
-inode_move_ltes_to_table(struct wim_inode *inode,
- struct wim_lookup_table *src_lookup_table,
- struct wim_lookup_table *dest_lookup_table,
- struct list_head *lte_list_head)
+static int
+inode_export_blobs(struct wim_inode *inode, struct blob_table *src_blob_table,
+ struct blob_table *dest_blob_table, bool gift)
{
- struct wim_lookup_table_entry *src_lte, *dest_lte;
unsigned i;
+ const u8 *hash;
+ struct blob_descriptor *src_blob, *dest_blob;
+
+ for (i = 0; i < inode->i_num_streams; i++) {
+
+ /* Retrieve SHA-1 message digest of blob to export. */
+ hash = stream_hash(&inode->i_streams[i]);
+ if (is_zero_hash(hash)) /* Empty stream? */
+ continue;
+
+ /* Search for the blob (via SHA-1 message digest) in the
+ * destination WIM. */
+ dest_blob = lookup_blob(dest_blob_table, hash);
+ if (!dest_blob) {
+ /* Blob not yet present in destination WIM. Search for
+ * it in the source WIM, then export it into the
+ * destination WIM. */
+ src_blob = stream_blob(&inode->i_streams[i],
+ src_blob_table);
+ if (!src_blob)
+ return blob_not_found_error(inode, hash);
- for (i = 0; i <= inode->i_num_ads; i++) {
- src_lte = inode_stream_lte_unresolved(inode, i, src_lookup_table);
- if (src_lte) {
- dest_lte = inode_stream_lte_unresolved(inode, i,
- dest_lookup_table);
- if (!dest_lte) {
- struct list_head *next;
-
- wimlib_assert(!list_empty(lte_list_head));
- next = lte_list_head->next;
- list_del(next);
- dest_lte = container_of(next,
- struct wim_lookup_table_entry,
- export_stream_list);
- dest_lte->part_number = 1;
- dest_lte->refcnt = 0;
- wimlib_assert(hashes_equal(dest_lte->hash, src_lte->hash));
- lookup_table_insert(dest_lookup_table, dest_lte);
+ if (gift) {
+ dest_blob = src_blob;
+ blob_table_unlink(src_blob_table, src_blob);
+ } else {
+ dest_blob = clone_blob_descriptor(src_blob);
+ if (!dest_blob)
+ return WIMLIB_ERR_NOMEM;
}
- dest_lte->refcnt += inode->i_nlink;
+ dest_blob->refcnt = 0;
+ dest_blob->out_refcnt = 0;
+ dest_blob->was_exported = 1;
+ blob_table_insert(dest_blob_table, dest_blob);
}
+
+ /* Blob 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_blob->refcnt += inode->i_nlink;
+ dest_blob->out_refcnt += inode->i_nlink;
}
+ return 0;
}
/* API function documented in wimlib.h */
WIMStruct *dest_wim,
const tchar *dest_name,
const tchar *dest_description,
- int export_flags,
- WIMStruct **additional_swms,
- unsigned num_additional_swms,
- wimlib_progress_func_t progress_func)
+ int export_flags)
{
int ret;
- struct wim_image_metadata *src_imd;
- struct list_head lte_list_head;
- struct wim_inode *inode;
+ int start_src_image;
+ int end_src_image;
+ int orig_dest_image_count;
+ int image;
+ bool all_images = (src_image == WIMLIB_ALL_IMAGES);
- ret = can_modify_wim(dest_wim);
- if (ret)
- return ret;
+ /* Check for sane parameters. */
+ if (export_flags & ~(WIMLIB_EXPORT_FLAG_BOOT |
+ WIMLIB_EXPORT_FLAG_NO_NAMES |
+ WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS |
+ WIMLIB_EXPORT_FLAG_GIFT |
+ WIMLIB_EXPORT_FLAG_WIMBOOT))
+ return WIMLIB_ERR_INVALID_PARAM;
- 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 (int 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)
- return ret;
- }
- return 0;
- } else if (src_wim->hdr.image_count == 1) {
- src_image = 1;
- } else {
- return 0;
- }
- }
+ if (!src_wim || !dest_wim)
+ return WIMLIB_ERR_INVALID_PARAM;
- if (!dest_name) {
- dest_name = wimlib_get_image_name(src_wim, src_image);
- DEBUG("Using name `%"TS"' for source image %d",
- dest_name, src_image);
- }
+ if (!wim_has_metadata(src_wim) || !wim_has_metadata(dest_wim))
+ return WIMLIB_ERR_METADATA_NOT_FOUND;
- if (!dest_description) {
- dest_description = wimlib_get_image_description(src_wim,
- src_image);
- DEBUG("Using description `%"TS"' for source image %d",
- dest_description, src_image);
+ if (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 and description must be "
+ "left NULL for multi-image export");
+ return WIMLIB_ERR_INVALID_PARAM;
+ }
+ start_src_image = 1;
+ end_src_image = src_wim->hdr.image_count;
+ } else {
+ start_src_image = src_image;
+ end_src_image = src_image;
}
+ orig_dest_image_count = dest_wim->hdr.image_count;
- DEBUG("Exporting image %d from `%"TS"'", src_image, src_wim->filename);
-
- if (wimlib_image_name_in_use(dest_wim, dest_name)) {
- ERROR("There is already an image named `%"TS"' in the "
- "destination WIM", dest_name);
- return WIMLIB_ERR_IMAGE_NAME_COLLISION;
+ /* We don't yet support having a single WIMStruct contain duplicate
+ * 'image_metadata' structures, so we must forbid this from happening.
+ * A duplication is possible if 'src_wim == dest_wim', if the same image
+ * is exported to the same destination WIMStruct multiple times, or if
+ * an image is exported in an A => B => A manner. */
+ for (src_image = start_src_image;
+ src_image <= end_src_image; src_image++)
+ {
+ const struct wim_image_metadata *src_imd =
+ src_wim->image_metadata[src_image - 1];
+ for (int i = 0; i < dest_wim->hdr.image_count; i++)
+ if (dest_wim->image_metadata[i] == src_imd)
+ return WIMLIB_ERR_DUPLICATE_EXPORTED_IMAGE;
}
- ret = verify_swm_set(src_wim, additional_swms, num_additional_swms);
+ /* Blob checksums must be known before proceeding. */
+ ret = wim_checksum_unhashed_blobs(src_wim);
if (ret)
return ret;
-
- ret = wim_checksum_unhashed_streams(src_wim);
- if (ret)
- return ret;
- ret = wim_checksum_unhashed_streams(dest_wim);
+ ret = wim_checksum_unhashed_blobs(dest_wim);
if (ret)
return ret;
- if (num_additional_swms)
- merge_lookup_tables(src_wim, additional_swms, num_additional_swms);
+ /* Enable rollbacks */
+ for_blob_in_table(dest_wim->blob_table, blob_set_not_exported, NULL);
- ret = select_wim_image(src_wim, src_image);
- if (ret) {
- ERROR("Could not select image %d from the WIM `%"TS"' "
- "to export it", src_image, src_wim->filename);
- goto out;
- }
+ /* Export each requested image. */
+ for (src_image = start_src_image;
+ src_image <= end_src_image;
+ src_image++)
+ {
+ const tchar *next_dest_name, *next_dest_description;
+ struct wim_image_metadata *src_imd;
+ struct wim_inode *inode;
+
+ /* Determine destination image name and description. */
+
+ 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, src_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. */
- for_lookup_table_entry(src_wim->lookup_table, lte_zero_out_refcnt, NULL);
- src_imd = wim_get_current_image_metadata(src_wim);
- INIT_LIST_HEAD(<e_list_head);
- image_for_each_inode(inode, src_imd) {
- ret = inode_allocate_needed_ltes(inode,
- src_wim->lookup_table,
- dest_wim->lookup_table,
- <e_list_head);
+ if (export_flags & WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS)
+ next_dest_description = NULL;
+ else if (dest_description)
+ next_dest_description = dest_description;
+ else
+ next_dest_description = wimlib_get_image_description(src_wim, src_image);
+
+ /* 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, src_image);
if (ret)
- goto out_free_ltes;
- }
+ goto out_rollback;
- ret = xml_export_image(src_wim->wim_info, src_image,
- &dest_wim->wim_info, dest_name,
- dest_description);
- if (ret)
- goto out_free_ltes;
+ src_imd = wim_get_current_image_metadata(src_wim);
- ret = append_image_metadata(dest_wim, src_imd);
- if (ret)
- goto out_xml_delete_image;
-
- /* The `struct image_metadata' is now referenced by both the @src_wim
- * and the @dest_wim. */
- src_imd->refcnt++;
- src_imd->modified = 1;
-
- /* All memory allocations have been taken care of, so it's no longer
- * possible for this function to fail. Go ahead and update the lookup
- * table of the destination WIM and the boot index, if needed. */
- image_for_each_inode(inode, src_imd) {
- inode_move_ltes_to_table(inode,
- src_wim->lookup_table,
- dest_wim->lookup_table,
- <e_list_head);
+ /* Iterate through inodes in the source image and export their
+ * blobs into the destination WIM. */
+ image_for_each_inode(inode, src_imd) {
+ ret = inode_export_blobs(inode,
+ src_wim->blob_table,
+ dest_wim->blob_table,
+ export_flags & WIMLIB_EXPORT_FLAG_GIFT);
+ if (ret)
+ goto out_rollback;
+ }
+
+ /* Export XML information into the destination WIM. */
+ ret = xml_export_image(src_wim->xml_info, src_image,
+ dest_wim->xml_info, next_dest_name,
+ next_dest_description,
+ export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT);
+ 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++;
}
- if (export_flags & WIMLIB_EXPORT_FLAG_BOOT)
- dest_wim->hdr.boot_idx = dest_wim->hdr.image_count;
+ /* Image export complete. Finish by setting any needed special metadata
+ * on the destination WIM. */
+
if (src_wim->hdr.flags & WIM_HDR_FLAG_RP_FIX)
- {
- /* Set the reparse point fixup flag on the destination WIM if
- * the flag is set on the source WIM. */
dest_wim->hdr.flags |= WIM_HDR_FLAG_RP_FIX;
+
+ for (src_image = start_src_image;
+ src_image <= end_src_image;
+ src_image++)
+ {
+ int dst_image = orig_dest_image_count + 1 +
+ (src_image - start_src_image);
+
+ if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
+ (!all_images || src_image == src_wim->hdr.boot_idx))
+ dest_wim->hdr.boot_idx = dst_image;
+ }
+
+ return 0;
+
+out_rollback:
+ while ((image = xml_get_image_count(dest_wim->xml_info))
+ > orig_dest_image_count)
+ {
+ xml_delete_image(dest_wim->xml_info, image);
}
- DEBUG("Successfully exported image.");
- ret = 0;
- goto out;
-out_xml_delete_image:
- xml_delete_image(&dest_wim->wim_info, dest_wim->hdr.image_count + 1);
-out_free_ltes:
+ while (dest_wim->hdr.image_count > orig_dest_image_count)
{
- struct wim_lookup_table_entry *lte, *tmp;
- list_for_each_entry_safe(lte, tmp, <e_list_head, export_stream_list)
- free_lookup_table_entry(lte);
+ put_image_metadata(dest_wim->image_metadata[
+ --dest_wim->hdr.image_count]);
}
-out:
- if (num_additional_swms)
- unmerge_lookup_table(src_wim);
+ for_blob_in_table(dest_wim->blob_table, blob_rollback_export,
+ dest_wim->blob_table);
return ret;
}