X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fexport_image.c;h=bfc36bde6237c9b39b87d9f5485366d60ff204c2;hp=6d38855db04e9d8c4b9d403e56e9cf05b6b89bbf;hb=2b14c2bfd6d0a7a17433dd8529cfc8b7d969c4b0;hpb=d364e4a9e3a18a144b35c47ce213e09087978d1b diff --git a/src/export_image.c b/src/export_image.c index 6d38855d..bfc36bde 100644 --- a/src/export_image.c +++ b/src/export_image.c @@ -3,289 +3,279 @@ */ /* - * Copyright (C) 2012 Eric Biggers + * Copyright (C) 2012, 2013, 2014 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/. */ -#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/error.h" +#include "wimlib/inode.h" +#include "wimlib/lookup_table.h" +#include "wimlib/metadata.h" +#include "wimlib/xml.h" -struct wim_pair { - WIMStruct *src_wim; - WIMStruct *dest_wim; - struct list_head lte_list_head; -}; +static int +lte_set_not_exported(struct wim_lookup_table_entry *lte, void *_ignore) +{ + lte->out_refcnt = 0; + lte->was_exported = 0; + return 0; +} -static int allocate_lte_if_needed(struct dentry *dentry, void *arg) +static int +lte_rollback_export(struct wim_lookup_table_entry *lte, void *_lookup_table) { - const WIMStruct *src_wim, *dest_wim; - struct list_head *lte_list_head; - 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; - lte_list_head = &((struct wim_pair*)arg)->lte_list_head; - inode = dentry->d_inode; + lte->refcnt -= lte->out_refcnt; + if (lte->was_exported) { + lookup_table_unlink(lookup_table, lte); + free_lookup_table_entry(lte); + } + return 0; +} - wimlib_assert(!inode->resolved); +static int +inode_export_streams(struct wim_inode *inode, + struct wim_lookup_table *src_lookup_table, + struct wim_lookup_table *dest_lookup_table, + bool gift) +{ + unsigned i; + const u8 *hash; + struct wim_lookup_table_entry *src_lte, *dest_lte; - 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); + inode_unresolve_streams(inode); + for (i = 0; i <= inode->i_num_ads; i++) { - if (src_lte && ++src_lte->out_refcnt == 1) { - dest_lte = inode_stream_lte_unresolved(inode, i, - dest_wim->lookup_table); + /* Retrieve SHA1 message digest of stream to export. */ + hash = inode_stream_hash(inode, i); + if (is_zero_hash(hash)) /* Empty stream? */ + continue; - if (!dest_lte) { + /* 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); + + if (gift) { + dest_lte = src_lte; + lookup_table_unlink(src_lookup_table, src_lte); + } else { 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); } + dest_lte->refcnt = 0; + dest_lte->out_refcnt = 0; + dest_lte->was_exported = 1; + 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) +/* 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) { - 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; + int ret; + int start_src_image; + int end_src_image; + int orig_dest_image_count; + int image; + bool all_images = (src_image == WIMLIB_ALL_IMAGES); + + /* 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_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; + + 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; - wimlib_assert(!inode->resolved); + /* 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; - 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); + /* Enable rollbacks */ + for_lookup_table_entry(dest_wim->lookup_table, lte_set_not_exported, NULL); - if (!src_lte) /* Empty or nonexistent stream. */ - continue; + /* 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 = T(""); + else if (dest_name) + next_dest_name = dest_name; + else + next_dest_name = wimlib_get_image_name(src_wim, src_image); + + if (export_flags & WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS) + next_dest_description = T(""); + 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; + } - 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)); - - 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); + /* Load metadata for source image into memory. */ + ret = select_wim_image(src_wim, src_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, + export_flags & WIMLIB_EXPORT_FLAG_GIFT); + if (ret) + goto out_rollback; } - } - 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) -{ - 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; - } + /* Export XML information into the destination WIM. */ + ret = xml_export_image(src_wim->wim_info, src_image, + &dest_wim->wim_info, next_dest_name, + next_dest_description); + if (ret) + goto out_rollback; - 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; - } - } + /* 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 (!dest_name) { - dest_name = wimlib_get_image_name(src_wim, src_image); - DEBUG("Using name `%s' for source image %d", - dest_name, src_image); - } + /* Lock the metadata into memory. XXX: need better solution for + * this. */ + src_imd->modified = 1; - 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); } - DEBUG("Exporting image %d from `%s'", src_image, src_wim->filename); + /* Image export complete. Finish by setting any needed special metadata + * on the destination WIM. */ - 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; - } + if (src_wim->hdr.flags & WIM_HDR_FLAG_RP_FIX) + dest_wim->hdr.flags |= WIM_HDR_FLAG_RP_FIX; - ret = verify_swm_set(src_wim, additional_swms, num_additional_swms); - if (ret != 0) - return ret; + 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_WIMBOOT) + wim_info_set_wimboot(dest_wim->wim_info, dst_image, true); - 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; + if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) && + (!all_images || src_image == src_wim->hdr.boot_idx)) + dest_wim->hdr.boot_idx = dst_image; } - 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_GIFT) { + free_lookup_table(src_wim->lookup_table); + src_wim->lookup_table = NULL; } + 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_rollback_export, + dest_wim->lookup_table); return ret; }