#include "wimlib/reparse.h"
#include "wimlib/resource.h"
#include "wimlib/security.h"
-#include "wimlib/swm.h"
#ifdef __WIN32__
# include "wimlib/win32.h" /* for realpath() equivalent */
#endif
if (!lte)
return 0;
- if (likely(!is_linked_extraction(ctx)) || (lte->out_refcnt == 0 &&
- lte->extracted_file == NULL))
+ /* Tally the size only for each extraction of the stream (not hard
+ * links). */
+ if (!(dentry->d_inode->i_visited &&
+ ctx->supported_features.hard_links) &&
+ (!is_linked_extraction(ctx) || (lte->out_refcnt == 0 &&
+ lte->extracted_file == NULL)))
{
ctx->progress.extract.total_bytes += wim_resource_size(lte);
ctx->progress.extract.num_streams++;
}
+ /* Add stream to the extraction_list only one time, even if it's going
+ * to be extracted to multiple locations. */
if (lte->out_refcnt == 0) {
list_add_tail(<e->extraction_list, &ctx->stream_list);
ctx->num_streams_remaining++;
* extracted (ctx->stream_list) if not already done so, and also update the
* progress information (ctx->progress) with the stream. Furthermore, if doing
* a sequential extraction, build a mapping from each the stream to the dentries
- * referencing it. */
+ * referencing it.
+ *
+ * This uses the i_visited member of the inodes (assumed to be 0 initially). */
static int
dentry_add_streams_to_extract(struct wim_dentry *dentry, void *_ctx)
{
if (dentry->extraction_skipped)
return 0;
- /* Don't process additional hard links. */
- if (inode->i_visited && ctx->supported_features.hard_links)
- return 0;
-
/* The unnamed data stream will always be extracted, except in an
* unlikely case. */
if (!inode_is_encrypted_directory(inode)) {
} else
#endif /* !__WIN32__ */
if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
- ret = ctx->ops->create_directory(path, ctx);
+ ret = ctx->ops->create_directory(path, ctx, &inode->extract_cookie);
if (ret) {
ERROR_WITH_ERRNO("Failed to create the directory "
"\"%"TS"\"", path);
}
+ } else if ((inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) &&
+ ctx->ops->extract_encrypted_stream_creates_file &&
+ ctx->supported_features.encrypted_files) {
+ ret = ctx->ops->extract_encrypted_stream(
+ path, inode_unnamed_lte_resolved(inode), ctx);
+ if (ret) {
+ ERROR_WITH_ERRNO("Failed to create and extract "
+ "encrypted file \"%"TS"\"", path);
+ }
} else {
- ret = ctx->ops->create_file(path, ctx);
+ ret = ctx->ops->create_file(path, ctx, &inode->extract_cookie);
if (ret) {
ERROR_WITH_ERRNO("Failed to create the file "
"\"%"TS"\"", path);
{
struct wim_inode *inode = dentry->d_inode;
struct wim_lookup_table_entry *lte;
+ file_spec_t file_spec;
int ret;
if (dentry->was_hardlinked)
}
#endif
+ if (ctx->ops->uses_cookies)
+ file_spec.cookie = inode->extract_cookie;
+ else
+ file_spec.path = path;
+
/* Unnamed data stream. */
lte = inode_unnamed_lte_resolved(inode);
if (lte && (!lte_spec || lte == lte_spec)) {
if (!(inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
FILE_ATTRIBUTE_REPARSE_POINT)))
{
- if ((inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) &&
- ctx->supported_features.encrypted_files)
- ret = ctx->ops->extract_encrypted_stream(path, lte, ctx);
- else
- ret = ctx->ops->extract_unnamed_stream(path, lte, ctx);
- if (ret)
- goto error;
+ if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED &&
+ ctx->supported_features.encrypted_files) {
+ if (!ctx->ops->extract_encrypted_stream_creates_file) {
+ ret = ctx->ops->extract_encrypted_stream(
+ path, lte, ctx);
+ if (ret)
+ goto error;
+ }
+ } else {
+ ret = ctx->ops->extract_unnamed_stream(
+ file_spec, lte, ctx);
+ if (ret)
+ goto error;
+ }
update_extract_progress(ctx, lte);
}
else if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)
continue;
if (lte_spec)
lte = lte_override;
- ret = ctx->ops->extract_named_stream(path, entry->stream_name,
+ ret = ctx->ops->extract_named_stream(file_spec, entry->stream_name,
entry->stream_name_nbytes / 2,
lte, ctx);
if (ret)
* extraction mode. */
static int
extract_file_attributes(const tchar *path, struct apply_ctx *ctx,
- struct wim_dentry *dentry)
+ struct wim_dentry *dentry, unsigned pass)
{
int ret;
- if (ctx->ops->set_file_attributes) {
- if (dentry == ctx->extract_root && ctx->root_dentry_is_special)
- return 0;
- ret = ctx->ops->set_file_attributes(path,
- dentry->d_inode->i_attributes,
- ctx);
+ if (ctx->ops->set_file_attributes &&
+ !(dentry == ctx->extract_root && ctx->root_dentry_is_special)) {
+ u32 attributes = dentry->d_inode->i_attributes;
+
+ /* Clear unsupported attributes. */
+ attributes &= ctx->supported_attributes_mask;
+
+ if ((attributes & FILE_ATTRIBUTE_DIRECTORY &&
+ !ctx->supported_features.encrypted_directories) ||
+ (!(attributes & FILE_ATTRIBUTE_DIRECTORY) &&
+ !ctx->supported_features.encrypted_files))
+ {
+ attributes &= ~FILE_ATTRIBUTE_ENCRYPTED;
+ }
+
+ if (attributes == 0)
+ attributes = FILE_ATTRIBUTE_NORMAL;
+
+ ret = ctx->ops->set_file_attributes(path, attributes, ctx, pass);
if (ret) {
ERROR_WITH_ERRNO("Failed to set attributes on "
"\"%"TS"\"", path);
"descriptor on \"%"TS"\"", path);
return ret;
} else {
+ #if 0
if (errno != EACCES) {
WARNING_WITH_ERRNO("Failed to set "
"security descriptor "
"on \"%"TS"\"", path);
}
+ #endif
+ ctx->no_security_descriptors++;
}
}
}
struct wim_inode *inode = dentry->d_inode;
if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
- if (supported_features->reparse_points)
- return true;
- if (supported_features->symlink_reparse_points &&
- inode_is_symlink(inode))
- return true;
- return false;
+ return supported_features->reparse_points ||
+ (inode_is_symlink(inode) &&
+ supported_features->symlink_reparse_points);
+ }
+ if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) {
+ if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
+ return supported_features->encrypted_directories != 0;
+ else
+ return supported_features->encrypted_files != 0;
}
return true;
}
get_num_path_components(const tchar *path, tchar path_separator)
{
unsigned num_components = 0;
+#ifdef __WIN32__
+ /* Ignore drive letter. */
+ if (path[0] != L'\0' && path[1] == L':')
+ path += 2;
+#endif
while (*path) {
while (*path == path_separator)
num_target_path_components--;
}
- p_old = oldpath;
+ p_old = oldpath + ctx->ops->path_prefix_nchars;
+#ifdef __WIN32__
+ if (p_old[0] != L'\0' && p_old[1] == ':')
+ p_old += 2;
+#endif
while (*p_old == ctx->ops->path_separator)
p_old++;
while (--num_target_path_components) {
/* Create empty named data streams. */
if (can_extract_named_data_streams(ctx)) {
for (u16 i = 0; i < inode->i_num_ads; i++) {
+ file_spec_t file_spec;
struct wim_ads_entry *entry = &inode->i_ads_entries[i];
if (!ads_entry_is_named_stream(entry))
continue;
if (entry->lte)
continue;
- ret = ctx->ops->extract_named_stream(path,
+ if (ctx->ops->uses_cookies)
+ file_spec.cookie = inode->extract_cookie;
+ else
+ file_spec.path = path;
+ ret = ctx->ops->extract_named_stream(file_spec,
entry->stream_name,
entry->stream_name_nbytes / 2,
entry->lte, ctx);
}
/* Set file attributes (if supported). */
- ret = extract_file_attributes(path, ctx, dentry);
+ ret = extract_file_attributes(path, ctx, dentry, 0);
if (ret)
return ret;
return 0;
}
+/* This is a wrapper around do_dentry_extract_skeleton() that handles building
+ * the path, doing short name reordering. This is also idempotent; dentries
+ * already processed have skeleton_extracted set and no action is taken. See
+ * apply_operations.requires_short_name_reordering for more details about short
+ * name reordering. */
static int
dentry_extract_skeleton(struct wim_dentry *dentry, void *_ctx)
{
struct wim_dentry *other_dentry;
int ret;
- /* Here we may re-order the extraction of multiple names (hard links)
- * for the same file in the same directory in order to ensure the short
- * (DOS) name is set correctly. A short name is always associated with
- * exactly one long name, and at least on NTFS, only one long name for a
- * file can have a short name associated with it. (More specifically,
- * there can be unlimited names in the POSIX namespace, but only one
- * name can be in the Win32+DOS namespace, or one name in the Win32
- * namespace with a corresponding name in the DOS namespace.) To ensure
- * the short name of a file is associated with the correct long name in
- * a directory, we extract the long name with a corresponding short name
- * before any additional names. This can affect NTFS-3g extraction
- * (which uses ntfs_set_ntfs_dos_name(), which doesn't allow specifying
- * the long name to associate with a short name) and may affect Win32
- * extraction as well (which uses SetFileShortName()). */
-
if (dentry->skeleton_extracted)
return 0;
+
orig_dentry = NULL;
if (ctx->supported_features.short_names
+ && ctx->ops->requires_short_name_reordering
&& !dentry_has_short_name(dentry)
&& !dentry->d_inode->i_dos_name_extracted)
{
inode_for_each_dentry(other_dentry, dentry->d_inode) {
if (dentry_has_short_name(other_dentry)
- && !other_dentry->skeleton_extracted
- && other_dentry->parent == dentry->parent)
+ && !other_dentry->skeleton_extracted)
{
DEBUG("Creating %"TS" before %"TS" "
"to guarantee correct DOS name extraction",
return 0;
}
+static int
+dentry_extract_dir_skeleton(struct wim_dentry *dentry, void *_ctx)
+{
+ if (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
+ return dentry_extract_skeleton(dentry, _ctx);
+ return 0;
+}
+
/* Create a file or directory, then immediately extract all streams. This
* assumes that WIMLIB_EXTRACT_FLAG_SEQUENTIAL is not specified, since the WIM
* may not be read sequentially by this function. */
if ((found_lte->resource_location != RESOURCE_NONEXISTENT)
&& !(found_lte->resource_entry.flags & WIM_RESHDR_FLAG_METADATA)
- && (needed_lte = __lookup_resource(lookup_table, found_lte->hash))
+ && (needed_lte = lookup_resource(lookup_table, found_lte->hash))
&& (needed_lte->out_refcnt))
{
copy_resource_entry(&needed_lte->resource_entry,
if (ret)
return ret;
+ if (ctx->ops->requires_final_set_attributes_pass) {
+ /* Set file attributes (if supported). */
+ ret = extract_file_attributes(path, ctx, dentry, 1);
+ if (ret)
+ return ret;
+ }
+
return extract_timestamps(path, ctx, dentry);
}
features->system_files++;
if (inode->i_attributes & FILE_ATTRIBUTE_COMPRESSED)
features->compressed_files++;
- if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)
- features->encrypted_files++;
+ if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) {
+ if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
+ features->encrypted_directories++;
+ else
+ features->encrypted_files++;
+ }
if (inode->i_attributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
features->not_context_indexed_files++;
if (inode->i_attributes & FILE_ATTRIBUTE_SPARSE_FILE)
return 0;
}
-static int
-dentry_clear_inode_visited(struct wim_dentry *dentry, void *_ignore)
-{
- dentry->d_inode->i_visited = 0;
- return 0;
-}
-
/* Tally the features necessary to extract a dentry tree. */
static void
dentry_tree_get_features(struct wim_dentry *root, struct wim_features *features)
{
memset(features, 0, sizeof(struct wim_features));
for_dentry_in_tree(root, dentry_tally_features, features);
- for_dentry_in_tree(root, dentry_clear_inode_visited, NULL);
+ dentry_tree_clear_inode_visited(root);
+}
+
+static u32
+compute_supported_attributes_mask(const struct wim_features *supported_features)
+{
+ u32 mask = ~(u32)0;
+
+ if (!supported_features->archive_files)
+ mask &= ~FILE_ATTRIBUTE_ARCHIVE;
+
+ if (!supported_features->hidden_files)
+ mask &= ~FILE_ATTRIBUTE_HIDDEN;
+
+ if (!supported_features->system_files)
+ mask &= ~FILE_ATTRIBUTE_SYSTEM;
+
+ if (!supported_features->not_context_indexed_files)
+ mask &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+
+ if (!supported_features->compressed_files)
+ mask &= ~FILE_ATTRIBUTE_COMPRESSED;
+
+ if (!supported_features->sparse_files)
+ mask &= ~FILE_ATTRIBUTE_SPARSE_FILE;
+
+ if (!supported_features->reparse_points)
+ mask &= ~FILE_ATTRIBUTE_REPARSE_POINT;
+
+ return mask;
}
static int
const tchar *wim_source_path)
{
const tchar *loc;
- const tchar *mode = "this extraction mode";
+ const tchar *mode = T("this extraction mode");
if (wim_source_path[0] == '\0')
- loc = "the WIM image";
+ loc = T("the WIM image");
else
loc = wim_source_path;
WARNING(
"%lu files in %"TS" are marked as being encrypted,\n"
" but encryption is not supported in %"TS". These files\n"
-" will be extracted as raw encrypted data instead.",
+" will not be extracted.",
required_features->encrypted_files, loc, mode);
}
+ if (required_features->encrypted_directories &&
+ !supported_features->encrypted_directories)
+ {
+ WARNING(
+ "%lu directories in %"TS" are marked as being encrypted,\n"
+" but encryption is not supported in %"TS".\n"
+" These directories will be extracted as unencrypted.",
+ required_features->encrypted_directories, loc, mode);
+ }
+
if (required_features->not_context_indexed_files &&
!supported_features->not_context_indexed_files)
{
return WIMLIB_ERR_UNSUPPORTED;
}
+ if ((extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS) &&
+ required_features->symlink_reparse_points &&
+ !(supported_features->symlink_reparse_points ||
+ supported_features->reparse_points))
+ {
+ ERROR("Extracting symbolic links is not supported in %"TS, mode);
+ return WIMLIB_ERR_UNSUPPORTED;
+ }
+
if ((extract_flags & WIMLIB_EXTRACT_FLAG_SYMLINK) &&
!supported_features->symlink_reparse_points)
{
return 0;
}
+static void
+do_extract_warnings(struct apply_ctx *ctx)
+{
+ if (ctx->partial_security_descriptors == 0 &&
+ ctx->no_security_descriptors == 0)
+ return;
+
+ WARNING("Extraction of \"%"TS"\" complete, but with one or more warnings:",
+ ctx->target);
+ if (ctx->partial_security_descriptors != 0) {
+ WARNING("- Could only partially set the security descriptor\n"
+ " on %lu files or directories.",
+ ctx->partial_security_descriptors);
+ }
+ if (ctx->no_security_descriptors != 0) {
+ WARNING("- Could not set security descriptor at all\n"
+ " on %lu files or directories.",
+ ctx->no_security_descriptors);
+ }
+#ifdef __WIN32__
+ WARNING("To fully restore all security descriptors, run the program\n"
+ " with Administrator rights.");
+#endif
+}
+
/*
* extract_tree - Extract a file or directory tree from the currently selected
* WIM image.
if (ret)
goto out_finish_or_abort_extract;
+ ctx.supported_attributes_mask =
+ compute_supported_attributes_mask(&ctx.supported_features);
+
/* Figure out whether the root dentry is being extracted to the root of
* a volume and therefore needs to be treated "specially", for example
* not being explicitly created and not having attributes set. */
ctx.realtarget_nchars = tstrlen(ctx.realtarget);
}
+ if (ctx.ops->requires_short_name_reordering) {
+ ret = for_dentry_in_tree(root, dentry_extract_dir_skeleton,
+ &ctx);
+ if (ret)
+ goto out_free_realtarget;
+ }
+
/* Finally, the important part: extract the tree of files. */
if (extract_flags & (WIMLIB_EXTRACT_FLAG_SEQUENTIAL |
WIMLIB_EXTRACT_FLAG_FROM_PIPE)) {
&ctx.progress);
}
+ do_extract_warnings(&ctx);
+
ret = 0;
out_free_realtarget:
FREE(ctx.realtarget);
const struct wimlib_extract_command *cmds,
size_t num_cmds,
int default_extract_flags,
- WIMStruct **additional_swms,
- unsigned num_additional_swms,
wimlib_progress_func_t progress_func)
{
int ret;
default_extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
- ret = verify_swm_set(wim, additional_swms, num_additional_swms);
- if (ret)
- goto out;
-
if (num_cmds == 0)
- goto out;
-
- if (num_additional_swms)
- merge_lookup_tables(wim, additional_swms, num_additional_swms);
+ return 0;
cmds_copy = CALLOC(num_cmds, sizeof(cmds[0]));
- if (!cmds_copy) {
- ret = WIMLIB_ERR_NOMEM;
- goto out_restore_lookup_table;
- }
+ if (!cmds_copy)
+ return WIMLIB_ERR_NOMEM;
for (size_t i = 0; i < num_cmds; i++) {
cmds_copy[i].extract_flags = (default_extract_flags |
FREE(cmds_copy[i].fs_dest_path);
}
FREE(cmds_copy);
-out_restore_lookup_table:
- if (num_additional_swms)
- unmerge_lookup_table(wim);
-out:
return ret;
}
int image,
const tchar *target,
int extract_flags,
- WIMStruct **additional_swms,
- unsigned num_additional_swms,
wimlib_progress_func_t progress_func)
{
int ret;
- if (extract_flags & WIMLIB_EXTRACT_FLAG_FROM_PIPE) {
- wimlib_assert(wim->hdr.part_number == 1);
- wimlib_assert(num_additional_swms == 0);
- } else {
- ret = verify_swm_set(wim, additional_swms, num_additional_swms);
- if (ret)
- return ret;
-
- if (num_additional_swms)
- merge_lookup_tables(wim, additional_swms, num_additional_swms);
- }
-
if (image == WIMLIB_ALL_IMAGES) {
ret = extract_all_images(wim, target, extract_flags,
progress_func);
lte_free_extracted_file,
NULL);
}
- if (num_additional_swms)
- unmerge_lookup_table(wim);
return ret;
}
/* Extract the image. */
extract_flags |= WIMLIB_EXTRACT_FLAG_FROM_PIPE;
ret = do_wimlib_extract_image(pwm, image, target,
- extract_flags, NULL, 0, progress_func);
+ extract_flags, progress_func);
/* Clean up and return. */
out_wimlib_free:
wimlib_free(pwm);
int image,
const tchar *target,
int extract_flags,
- WIMStruct **additional_swms,
- unsigned num_additional_swms,
wimlib_progress_func_t progress_func)
{
extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
return do_wimlib_extract_image(wim, image, target, extract_flags,
- additional_swms, num_additional_swms,
progress_func);
}