/*
- * update_image.c - Update a WIM image.
+ * update_image.c - see description below
*/
/*
* Copyright (C) 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/.
+ */
+
+/*
+ * This file contains the implementation of wimlib_update_image(), which is one
+ * of the two ways by which library users can make changes to a WIM image. (The
+ * other way is by mounting an image read-write.) wimlib_update_image() is also
+ * used in the implementation of wimlib_add_image(), since "create a WIM image
+ * from this directory tree" is equivalent to "create an empty WIM image, then
+ * update it to add this directory tree as the root".
+ *
+ * wimlib_update_image() processes a list of commands passed to it. Currently,
+ * the following types of commands are supported:
+ *
+ * - Add a directory tree from an external source (filesystem or NTFS volume).
+ * This can be used to add new files or to replace existing files.
+ * - Delete a file or directory tree.
+ * - Rename a file or directory tree.
+ *
+ * Not supported are creating links to existing files or changing metadata of
+ * existing files.
+ *
+ * wimlib_update_image() is atomic. If it cannot complete successfully, then
+ * all changes are rolled back and the WIMStruct is left unchanged. Rollback is
+ * implemented by breaking the commands into primitive operations such as "link
+ * this dentry tree here" which can be undone by doing the opposite operations
+ * in reverse order.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "wimlib/alloca.h"
+#include "wimlib/assert.h"
+#include "wimlib/blob_table.h"
#include "wimlib/capture.h"
#include "wimlib/dentry.h"
#include "wimlib/encoding.h"
+#include "wimlib/endianness.h"
#include "wimlib/error.h"
-#include "wimlib/lookup_table.h"
#include "wimlib/metadata.h"
-#ifdef WITH_NTFS_3G
-# include "wimlib/ntfs_3g.h" /* for do_ntfs_umount() */
-#endif
#include "wimlib/paths.h"
#include "wimlib/progress.h"
#include "wimlib/xml.h"
-#include <errno.h>
-#include <sys/stat.h>
-#include <stdlib.h>
-
-#ifdef HAVE_ALLOCA_H
-# include <alloca.h>
-#endif
-
/* Saved specification of a "primitive" update operation that was performed. */
struct update_primitive {
enum {
/* Location of the WIM image's root pointer. */
struct wim_dentry **root_p;
- /* Pointer to the lookup table of the WIM (may needed for rollback) */
- struct wim_lookup_table *lookup_table;
+ /* Pointer to the blob table of the WIM (may needed for rollback) */
+ struct blob_table *blob_table;
/* List of dentries that are currently unlinked from the WIM image.
* These must be freed when no longer needed for commit or rollback. */
* commands. */
static struct update_command_journal *
new_update_command_journal(size_t num_cmds, struct wim_dentry **root_p,
- struct wim_lookup_table *lookup_table)
+ struct blob_table *blob_table)
{
struct update_command_journal *j;
j->num_cmds = num_cmds;
j->cur_cmd = 0;
j->root_p = root_p;
- j->lookup_table = lookup_table;
+ j->blob_table = blob_table;
INIT_LIST_HEAD(&j->orphans);
for (size_t i = 0; i < num_cmds; i++)
init_update_primitive_list(&j->cmd_prims[i]);
orphan = list_first_entry(&j->orphans,
struct wim_dentry, tmp_list);
list_del(&orphan->tmp_list);
- free_dentry_tree(orphan, j->lookup_table);
+ free_dentry_tree(orphan, j->blob_table);
}
for (size_t i = 0; i < j->num_cmds; i++)
FREE(*name_ptr);
if (old_name) {
*name_ptr = old_name;
- *name_nbytes_ptr = utf16le_strlen(old_name);
+ *name_nbytes_ptr = utf16le_len_bytes(old_name);
} else {
*name_ptr = NULL;
*name_nbytes_ptr = 0;
return ret;
}
}
- free_dentry(branch);
+ free_dentry_tree(branch, j->blob_table);
return 0;
} else if (add_flags & WIMLIB_ADD_FLAG_NO_REPLACE) {
/* Can't replace nondirectory file */
out_free_target:
tstr_put_utf16le(target);
out_free_branch:
- free_dentry_tree(branch, j->lookup_table);
+ free_dentry_tree(branch, j->blob_table);
out:
return ret;
}
const struct wimlib_update_command *add_cmd,
struct wim_inode_table *inode_table,
struct wim_sd_set *sd_set,
- struct list_head *unhashed_streams)
+ struct list_head *unhashed_blobs)
{
int ret;
int add_flags;
tchar *fs_source_path;
tchar *wim_target_path;
const tchar *config_file;
- struct add_image_params params;
+ struct capture_params params;
struct capture_config config;
capture_tree_t capture_tree = platform_default_capture_tree;
-#ifdef WITH_NTFS_3G
- struct _ntfs_volume *ntfs_vol = NULL;
-#endif
- void *extra_arg = NULL;
struct wim_dentry *branch;
add_flags = add_cmd->add.add_flags;
memset(¶ms, 0, sizeof(params));
#ifdef WITH_NTFS_3G
- if (add_flags & WIMLIB_ADD_FLAG_NTFS) {
- capture_tree = build_dentry_tree_ntfs;
- extra_arg = &ntfs_vol;
- if (wim_get_current_image_metadata(wim)->ntfs_vol != NULL) {
- ERROR("NTFS volume already set");
- ret = WIMLIB_ERR_INVALID_PARAM;
- goto out;
- }
- }
+ if (add_flags & WIMLIB_ADD_FLAG_NTFS)
+ capture_tree = ntfs_3g_build_dentry_tree;
#endif
ret = get_capture_config(config_file, &config,
if (ret)
goto out;
- params.lookup_table = wim->lookup_table;
- params.unhashed_streams = unhashed_streams;
+ params.blob_table = wim->blob_table;
+ params.unhashed_blobs = unhashed_blobs;
params.inode_table = inode_table;
params.sd_set = sd_set;
params.config = &config;
params.add_flags = add_flags;
- params.extra_arg = extra_arg;
params.progfunc = wim->progfunc;
params.progctx = wim->progctx;
ret = call_progress(params.progfunc, WIMLIB_PROGRESS_MSG_SCAN_END,
¶ms.progress, params.progctx);
if (ret) {
- free_dentry_tree(branch, wim->lookup_table);
- goto out_cleanup_after_capture;
+ free_dentry_tree(branch, wim->blob_table);
+ goto out_destroy_config;
}
if (WIMLIB_IS_WIM_ROOT_PATH(wim_target_path) &&
{
ERROR("\"%"TS"\" is not a directory!", fs_source_path);
ret = WIMLIB_ERR_NOTDIR;
- free_dentry_tree(branch, wim->lookup_table);
- goto out_cleanup_after_capture;
+ free_dentry_tree(branch, wim->blob_table);
+ goto out_destroy_config;
}
ret = attach_branch(branch, wim_target_path, j,
add_flags, params.progfunc, params.progctx);
if (ret)
- goto out_cleanup_after_capture;
+ goto out_destroy_config;
if (config_file && (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) &&
WIMLIB_IS_WIM_ROOT_PATH(wim_target_path))
* /Windows/System32/WimBootCompress.ini in the WIM image. */
ret = platform_default_capture_tree(&branch, config_file, ¶ms);
if (ret)
- goto out_cleanup_after_capture;
+ goto out_destroy_config;
ret = attach_branch(branch, wimboot_cfgfile, j, 0, NULL, NULL);
if (ret)
- goto out_cleanup_after_capture;
+ goto out_destroy_config;
}
-#ifdef WITH_NTFS_3G
- wim_get_current_image_metadata(wim)->ntfs_vol = ntfs_vol;
-#endif
- if (add_flags & WIMLIB_ADD_FLAG_RPFIX)
- wim->hdr.flags |= WIM_HDR_FLAG_RP_FIX;
ret = 0;
- goto out_destroy_config;
-out_cleanup_after_capture:
-#ifdef WITH_NTFS_3G
- if (ntfs_vol)
- do_ntfs_umount(ntfs_vol);
-#endif
out_destroy_config:
destroy_capture_config(&config);
out:
return -ENOMEM;
if (dst) {
unlink_dentry(dst);
- free_dentry_tree(dst, wim->lookup_table);
+ free_dentry_tree(dst, wim->blob_table);
}
unlink_dentry(src);
dentry_add_child(parent_of_dst, src);
{
struct wim_inode_table *inode_table;
struct wim_sd_set *sd_set;
- struct list_head unhashed_streams;
+ struct list_head unhashed_blobs;
struct update_command_journal *j;
union wimlib_progress_info info;
int ret;
if (ret)
goto out_destroy_inode_table;
- INIT_LIST_HEAD(&unhashed_streams);
+ INIT_LIST_HEAD(&unhashed_blobs);
} else {
inode_table = NULL;
sd_set = NULL;
*/
j = new_update_command_journal(num_cmds,
&wim_get_current_image_metadata(wim)->root_dentry,
- wim->lookup_table);
+ wim->blob_table);
if (!j) {
ret = WIMLIB_ERR_NOMEM;
goto out_destroy_sd_set;
goto rollback;
}
- ret = WIMLIB_ERR_INVALID_PARAM;
switch (cmds[i].op) {
case WIMLIB_UPDATE_OP_ADD:
ret = execute_add_command(j, wim, &cmds[i], inode_table,
- sd_set, &unhashed_streams);
+ sd_set, &unhashed_blobs);
break;
case WIMLIB_UPDATE_OP_DELETE:
ret = execute_delete_command(j, wim, &cmds[i]);
imd = wim_get_current_image_metadata(wim);
- list_splice_tail(&unhashed_streams, &imd->unhashed_streams);
+ list_splice_tail(&unhashed_blobs, &imd->unhashed_blobs);
inode_table_prepare_inode_list(inode_table, &imd->inode_list);
}
goto out_destroy_sd_set;
WIMLIB_ADD_FLAG_NO_UNSUPPORTED_EXCLUDE |
WIMLIB_ADD_FLAG_WINCONFIG |
WIMLIB_ADD_FLAG_WIMBOOT |
- WIMLIB_ADD_FLAG_NO_REPLACE))
+ WIMLIB_ADD_FLAG_NO_REPLACE |
+ WIMLIB_ADD_FLAG_TEST_FILE_EXCLUSION))
return WIMLIB_ERR_INVALID_PARAM;
bool is_entire_image = WIMLIB_IS_WIM_ROOT_PATH(cmd->add.wim_target_path);
#ifndef WITH_NTFS_3G
if (add_flags & WIMLIB_ADD_FLAG_NTFS) {
- ERROR("wimlib was compiled without support for NTFS-3g, so\n"
- " we cannot capture a WIM image directly "
- "from an NTFS volume");
+ ERROR("NTFS-3g capture mode is unsupported because wimlib "
+ "was compiled --without-ntfs-3g");
return WIMLIB_ERR_UNSUPPORTED;
}
#endif
}
if (!is_entire_image) {
- if (add_flags & WIMLIB_ADD_FLAG_NTFS) {
- ERROR("Cannot add directly from an NTFS volume "
- "when not capturing a full image!");
- return WIMLIB_ERR_INVALID_PARAM;
- }
-
if (add_flags & WIMLIB_ADD_FLAG_RPFIX) {
ERROR("Cannot do reparse point fixups when "
"not capturing a full image!");
DEBUG("Updating image %d with %zu commands", image, num_cmds);
- if (have_command_type(cmds, num_cmds, WIMLIB_UPDATE_OP_DELETE))
- ret = can_delete_from_wim(wim);
- else
- ret = can_modify_wim(wim);
-
- if (ret)
- goto out;
-
/* Load the metadata for the image to modify (if not loaded already) */
ret = select_wim_image(wim, image);
if (ret)
* directories, may have changed. Call xml_update_image_info() to
* recalculate these statistics. */
xml_update_image_info(wim, image);
+
+ for (size_t i = 0; i < num_cmds; i++)
+ if (cmds_copy[i].op == WIMLIB_UPDATE_OP_ADD &&
+ cmds_copy[i].add.add_flags & WIMLIB_ADD_FLAG_RPFIX)
+ wim->hdr.flags |= WIM_HDR_FLAG_RP_FIX;
out_free_cmds_copy:
free_update_commands(cmds_copy, num_cmds);
out:
return ret;
}
-static int
-update1(WIMStruct *wim, int image, const struct wimlib_update_command *cmd)
-{
- return wimlib_update_image(wim, image, cmd, 1, 0);
-}
-
WIMLIBAPI int
wimlib_delete_path(WIMStruct *wim, int image,
const tchar *path, int delete_flags)
cmd.delete_.wim_path = (tchar *)path;
cmd.delete_.delete_flags = delete_flags;
- return update1(wim, image, &cmd);
+ return wimlib_update_image(wim, image, &cmd, 1, 0);
}
WIMLIBAPI int
cmd.rename.wim_target_path = (tchar *)dest_path;
cmd.rename.rename_flags = 0;
- return update1(wim, image, &cmd);
+ return wimlib_update_image(wim, image, &cmd, 1, 0);
}
WIMLIBAPI int
cmd.add.add_flags = add_flags;
cmd.add.config_file = NULL;
- return update1(wim, image, &cmd);
+ return wimlib_update_image(wim, image, &cmd, 1, 0);
}