#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.total_bytes += lte->size;
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++;
}
- if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_SEQUENTIAL) {
+ if (!(ctx->extract_flags & WIMLIB_EXTRACT_FLAG_FILE_ORDER)) {
struct wim_dentry **lte_dentries;
/* Append dentry to this stream's array of dentries referencing
lte_dentries = REALLOC(prev_lte_dentries,
alloc_lte_dentries *
sizeof(lte_dentries[0]));
- if (!lte_dentries)
+ if (lte_dentries == NULL)
return WIMLIB_ERR_NOMEM;
if (prev_lte_dentries == NULL) {
memcpy(lte_dentries,
* 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)) {
wimlib_progress_func_t progress_func = ctx->progress_func;
union wimlib_progress_info *progress = &ctx->progress;
- progress->extract.completed_bytes += wim_resource_size(lte);
+ progress->extract.completed_bytes += lte->size;
if (progress_func &&
progress->extract.completed_bytes >= ctx->next_progress)
{
} 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);
* This function operates slightly differently depending on whether @lte_spec is
* NULL or not. When @lte_spec is NULL, the behavior is to extract the default
* file contents (unnamed stream), and, if named data streams are supported in
- * the extract mode and volume, any named data streams. When @lte_spec is NULL,
- * the behavior is to extract only all copies of the stream @lte_spec, and in
- * addition use @lte_spec to set the reparse data or create the symbolic link if
- * appropriate.
+ * the extract mode and volume, any named data streams. When @lte_spec is not
+ * NULL, the behavior is to extract only all copies of the stream @lte_spec, and
+ * in addition use @lte_spec to set the reparse data or create the symbolic link
+ * if appropriate.
*
* @path
* Path to file to extract (as can be passed to apply_operations
{
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->in_extraction_tree)
{
DEBUG("Creating %"TS" before %"TS" "
"to guarantee correct DOS name extraction",
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
+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. The WIM
* may not be read sequentially by this function. */
static int
dentry_extract(struct wim_dentry *dentry, void *_ctx)
return extract_streams(path, ctx, dentry, NULL, NULL);
}
+/* Creates a temporary file opened for writing. The open file descriptor is
+ * returned in @fd_ret and its name is returned in @name_ret (dynamically
+ * allocated). */
+static int
+create_temporary_file(struct filedes *fd_ret, tchar **name_ret)
+{
+ tchar *name;
+ int raw_fd;
+
+retry:
+ name = ttempnam(NULL, T("wimlib"));
+ if (name == NULL) {
+ ERROR_WITH_ERRNO("Failed to create temporary filename");
+ return WIMLIB_ERR_NOMEM;
+ }
+
+ raw_fd = topen(name, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0600);
+
+ if (raw_fd < 0) {
+ if (errno == EEXIST) {
+ FREE(name);
+ goto retry;
+ }
+ ERROR_WITH_ERRNO("Failed to open temporary file \"%"TS"\"", name);
+ FREE(name);
+ return WIMLIB_ERR_OPEN;
+ }
+
+ filedes_init(fd_ret, raw_fd);
+ *name_ret = name;
+ return 0;
+}
+
/* Extract all instances of the stream @lte that are being extracted in this
- * call of extract_tree(). @can_seek specifies whether the WIM file descriptor
- * is seekable or not (e.g. is a pipe). If not and the stream needs to be
- * extracted multiple times, it is extracted to a temporary file first.
- *
- * This is intended for use with sequential extraction of a WIM image
- * (WIMLIB_EXTRACT_FLAG_SEQUENTIAL specified). */
+ * call of extract_tree(), but actually read the stream data from @lte_override.
+ */
static int
extract_stream_instances(struct wim_lookup_table_entry *lte,
- struct apply_ctx *ctx, bool can_seek)
+ struct wim_lookup_table_entry *lte_override,
+ struct apply_ctx *ctx)
{
struct wim_dentry **lte_dentries;
- struct wim_lookup_table_entry *lte_tmp = NULL;
- struct wim_lookup_table_entry *lte_override;
- tchar *stream_tmp_filename = NULL;
tchar path[ctx->ops->path_max];
- unsigned i;
+ size_t i;
int ret;
if (lte->out_refcnt <= ARRAY_LEN(lte->inline_lte_dentries))
else
lte_dentries = lte->lte_dentries;
- if (likely(can_seek || lte->out_refcnt < 2)) {
- lte_override = lte;
- } else {
- /* Need to extract stream to temporary file. */
- struct filedes fd;
- int raw_fd;
-
- stream_tmp_filename = ttempnam(NULL, T("wimlib"));
- if (!stream_tmp_filename) {
- ERROR_WITH_ERRNO("Failed to create temporary filename");
- ret = WIMLIB_ERR_OPEN;
- goto out;
- }
-
- lte_tmp = memdup(lte, sizeof(struct wim_lookup_table_entry));
- if (!lte_tmp) {
- ret = WIMLIB_ERR_NOMEM;
- goto out_free_stream_tmp_filename;
- }
- lte_tmp->resource_location = RESOURCE_IN_FILE_ON_DISK;
- lte_tmp->file_on_disk = stream_tmp_filename;
- lte_override = lte_tmp;
-
- raw_fd = topen(stream_tmp_filename,
- O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600);
- if (raw_fd < 0) {
- ERROR_WITH_ERRNO("Failed to open temporary file");
- ret = WIMLIB_ERR_OPEN;
- goto out_free_lte_tmp;
- }
- filedes_init(&fd, raw_fd);
- ret = extract_wim_resource_to_fd(lte, &fd,
- wim_resource_size(lte));
- if (filedes_close(&fd) && !ret)
- ret = WIMLIB_ERR_WRITE;
- if (ret)
- goto out_unlink_stream_tmp_file;
- }
-
- /* Extract all instances of the stream, reading either from the stream
- * in the WIM file or from the temporary file containing the stream.
- * dentry->tmp_flag is used to ensure that each dentry is processed only
- * once regardless of how many times this stream appears in the streams
- * of the corresponding inode. */
for (i = 0; i < lte->out_refcnt; i++) {
struct wim_dentry *dentry = lte_dentries[i];
continue;
if (!build_extraction_path(path, dentry, ctx))
continue;
- ret = extract_streams(path, ctx, dentry,
- lte, lte_override);
+ ret = extract_streams(path, ctx, dentry, lte, lte_override);
if (ret)
goto out_clear_tmp_flags;
dentry->tmp_flag = 1;
out_clear_tmp_flags:
for (i = 0; i < lte->out_refcnt; i++)
lte_dentries[i]->tmp_flag = 0;
-out_unlink_stream_tmp_file:
- if (stream_tmp_filename)
- tunlink(stream_tmp_filename);
-out_free_lte_tmp:
- FREE(lte_tmp);
-out_free_stream_tmp_filename:
- FREE(stream_tmp_filename);
-out:
+ return ret;
+}
+
+/* Determine whether the specified stream needs to be extracted to a temporary
+ * file or not.
+ *
+ * @lte->out_refcnt specifies the number of instances of this stream that must
+ * be extracted.
+ *
+ * @is_partial_res is %true if this stream is just one of multiple in a single
+ * WIM resource being extracted. */
+static bool
+need_tmpfile_to_extract(struct wim_lookup_table_entry *lte,
+ bool is_partial_res)
+{
+ /* Temporary file is always required when reading a partial resource,
+ * since in that case we retrieve all the contained streams in one pass.
+ * */
+ if (is_partial_res)
+ return true;
+
+ /* Otherwise we don't need a temporary file if only a single instance of
+ * the stream is needed. */
+ if (lte->out_refcnt == 1)
+ return false;
+
+ wimlib_assert(lte->out_refcnt >= 2);
+
+ /* We also don't need a temporary file if random access to the stream is
+ * allowed. */
+ if (lte->resource_location != RESOURCE_IN_WIM ||
+ filedes_is_seekable(<e->rspec->wim->in_fd))
+ return false;
+
+ return true;
+}
+
+static int
+begin_extract_stream_to_tmpfile(struct wim_lookup_table_entry *lte,
+ bool is_partial_res,
+ void *_ctx)
+{
+ struct apply_ctx *ctx = _ctx;
+ int ret;
+
+ if (!need_tmpfile_to_extract(lte, is_partial_res)) {
+ DEBUG("Temporary file not needed "
+ "for stream (size=%"PRIu64")", lte->size);
+ ret = extract_stream_instances(lte, lte, ctx);
+ if (ret)
+ return ret;
+
+ return BEGIN_STREAM_STATUS_SKIP_STREAM;
+ }
+
+ DEBUG("Temporary file needed for stream (size=%"PRIu64")", lte->size);
+ return create_temporary_file(&ctx->tmpfile_fd, &ctx->tmpfile_name);
+}
+
+static int
+end_extract_stream_to_tmpfile(struct wim_lookup_table_entry *lte,
+ int status, void *_ctx)
+{
+ struct apply_ctx *ctx = _ctx;
+ struct wim_lookup_table_entry lte_override;
+ int ret;
+ int errno_save = errno;
+
+ ret = filedes_close(&ctx->tmpfile_fd);
+
+ if (status) {
+ ret = status;
+ errno = errno_save;
+ goto out_delete_tmpfile;
+ }
+
+ if (ret) {
+ ERROR_WITH_ERRNO("Error writing temporary file %"TS, ctx->tmpfile_name);
+ ret = WIMLIB_ERR_WRITE;
+ goto out_delete_tmpfile;
+ }
+
+ /* Now that a full stream has been extracted to a temporary file,
+ * extract all instances of it to the actual target. */
+
+ memcpy(<e_override, lte, sizeof(struct wim_lookup_table_entry));
+ lte_override.resource_location = RESOURCE_IN_FILE_ON_DISK;
+ lte_override.file_on_disk = ctx->tmpfile_name;
+
+ ret = extract_stream_instances(lte, <e_override, ctx);
+
+out_delete_tmpfile:
+ errno_save = errno;
+ tunlink(ctx->tmpfile_name);
+ FREE(ctx->tmpfile_name);
+ errno = errno_save;
return ret;
}
/* Extracts a list of streams (ctx.stream_list), assuming that the directory
* structure and empty files were already created. This relies on the
* per-`struct wim_lookup_table_entry' list of dentries that reference each
- * stream that was constructed earlier. Streams are extracted exactly in the
- * order of the stream list; however, unless the WIM's file descriptor is
- * detected to be non-seekable, streams may be read from the WIM file more than
- * one time if multiple copies need to be extracted. */
+ * stream that was constructed earlier. */
static int
extract_stream_list(struct apply_ctx *ctx)
{
- struct wim_lookup_table_entry *lte;
- bool can_seek;
- int ret;
+ if (!(ctx->extract_flags & WIMLIB_EXTRACT_FLAG_FILE_ORDER)) {
+ /* Sequential extraction: read the streams in the order in which
+ * they appear in the WIM file. */
+ struct read_stream_list_callbacks cbs = {
+ .begin_stream = begin_extract_stream_to_tmpfile,
+ .begin_stream_ctx = ctx,
+ .consume_chunk = extract_chunk_to_fd,
+ .consume_chunk_ctx = &ctx->tmpfile_fd,
+ .end_stream = end_extract_stream_to_tmpfile,
+ .end_stream_ctx = ctx,
+ };
+ return read_stream_list(&ctx->stream_list,
+ offsetof(struct wim_lookup_table_entry, extraction_list),
+ &cbs, VERIFY_STREAM_HASHES);
+ } else {
+ /* Extract the streams in unsorted order. */
+ struct wim_lookup_table_entry *lte;
+ int ret;
- can_seek = (lseek(ctx->wim->in_fd.fd, 0, SEEK_CUR) != -1);
- list_for_each_entry(lte, &ctx->stream_list, extraction_list) {
- ret = extract_stream_instances(lte, ctx, can_seek);
- if (ret)
- return ret;
+ list_for_each_entry(lte, &ctx->stream_list, extraction_list) {
+ ret = extract_stream_instances(lte, lte, ctx);
+ if (ret)
+ return ret;
+ }
+ return 0;
}
- return 0;
}
#define PWM_ALLOW_WIM_HDR 0x00001
/* Read the header from a stream in a pipable WIM. */
static int
read_pwm_stream_header(WIMStruct *pwm, struct wim_lookup_table_entry *lte,
+ struct wim_resource_spec *rspec,
int flags, struct wim_header_disk *hdr_ret)
{
union {
struct pwm_stream_hdr stream_hdr;
struct wim_header_disk pwm_hdr;
} buf;
+ struct wim_reshdr reshdr;
int ret;
ret = full_read(&pwm->in_fd, &buf.stream_hdr, sizeof(buf.stream_hdr));
return 0;
}
- if (buf.stream_hdr.magic != PWM_STREAM_MAGIC) {
+ if (le64_to_cpu(buf.stream_hdr.magic) != PWM_STREAM_MAGIC) {
ERROR("Data read on pipe is invalid (expected stream header).");
return WIMLIB_ERR_INVALID_PIPABLE_WIM;
}
- lte->resource_entry.original_size = le64_to_cpu(buf.stream_hdr.uncompressed_size);
copy_hash(lte->hash, buf.stream_hdr.hash);
- lte->resource_entry.flags = le32_to_cpu(buf.stream_hdr.flags);
- lte->resource_entry.offset = pwm->in_fd.offset;
- lte->resource_location = RESOURCE_IN_WIM;
- lte->wim = pwm;
- if (lte->resource_entry.flags & WIM_RESHDR_FLAG_COMPRESSED) {
- lte->compression_type = pwm->compression_type;
- lte->resource_entry.size = 0;
- } else {
- lte->compression_type = WIMLIB_COMPRESSION_TYPE_NONE;
- lte->resource_entry.size = lte->resource_entry.original_size;
- }
- lte->is_pipable = 1;
+
+ reshdr.size_in_wim = 0;
+ reshdr.flags = le32_to_cpu(buf.stream_hdr.flags);
+ reshdr.offset_in_wim = pwm->in_fd.offset;
+ reshdr.uncompressed_size = le64_to_cpu(buf.stream_hdr.uncompressed_size);
+ wim_res_hdr_to_spec(&reshdr, pwm, rspec);
+ lte_bind_wim_resource_spec(lte, rspec);
+ lte->flags = rspec->flags;
+ lte->size = rspec->uncompressed_size;
+ lte->offset_in_res = 0;
return 0;
read_error:
return ret;
}
-/* Skip over an unneeded stream in a pipable WIM being read from a pipe. */
-static int
-skip_pwm_stream(struct wim_lookup_table_entry *lte)
-{
- return read_partial_wim_resource(lte, wim_resource_size(lte),
- NULL, NULL,
- WIMLIB_READ_RESOURCE_FLAG_SEEK_ONLY,
- 0);
-}
-
static int
extract_streams_from_pipe(struct apply_ctx *ctx)
{
struct wim_lookup_table_entry *found_lte;
+ struct wim_resource_spec *rspec;
struct wim_lookup_table_entry *needed_lte;
struct wim_lookup_table *lookup_table;
struct wim_header_disk pwm_hdr;
ret = WIMLIB_ERR_NOMEM;
found_lte = new_lookup_table_entry();
- if (!found_lte)
+ if (found_lte == NULL)
goto out;
+ rspec = MALLOC(sizeof(struct wim_resource_spec));
+ if (rspec == NULL)
+ goto out_free_found_lte;
+
lookup_table = ctx->wim->lookup_table;
pwm_flags = PWM_ALLOW_WIM_HDR;
if ((ctx->extract_flags & WIMLIB_EXTRACT_FLAG_RESUME))
ctx->progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN,
&ctx->progress);
while (ctx->num_streams_remaining) {
- ret = read_pwm_stream_header(ctx->wim, found_lte, pwm_flags,
- &pwm_hdr);
+ if (found_lte->resource_location != RESOURCE_NONEXISTENT)
+ lte_unbind_wim_resource_spec(found_lte);
+ ret = read_pwm_stream_header(ctx->wim, found_lte, rspec,
+ pwm_flags, &pwm_hdr);
if (ret) {
if (ret == WIMLIB_ERR_UNEXPECTED_END_OF_FILE &&
(ctx->extract_flags & WIMLIB_EXTRACT_FLAG_RESUME))
}
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))
+ && !(found_lte->flags & WIM_RESHDR_FLAG_METADATA)
+ && (needed_lte = lookup_resource(lookup_table, found_lte->hash))
&& (needed_lte->out_refcnt))
{
- copy_resource_entry(&needed_lte->resource_entry,
- &found_lte->resource_entry);
- needed_lte->resource_location = found_lte->resource_location;
- needed_lte->wim = found_lte->wim;
- needed_lte->compression_type = found_lte->compression_type;
- needed_lte->is_pipable = found_lte->is_pipable;
-
- ret = extract_stream_instances(needed_lte, ctx, false);
+ tchar *tmpfile_name = NULL;
+ struct wim_lookup_table_entry *lte_override;
+ struct wim_lookup_table_entry tmpfile_lte;
+
+ needed_lte->offset_in_res = found_lte->offset_in_res;
+ needed_lte->flags = found_lte->flags;
+ needed_lte->size = found_lte->size;
+
+ lte_unbind_wim_resource_spec(found_lte);
+ lte_bind_wim_resource_spec(needed_lte, rspec);
+
+ if (needed_lte->out_refcnt > 1) {
+
+ struct filedes tmpfile_fd;
+
+ /* Extract stream to temporary file. */
+ ret = create_temporary_file(&tmpfile_fd, &tmpfile_name);
+ if (ret)
+ goto out_free_found_lte;
+
+ ret = extract_stream_to_fd(needed_lte, &tmpfile_fd,
+ needed_lte->size);
+ if (ret) {
+ filedes_close(&tmpfile_fd);
+ goto delete_tmpfile;
+ }
+
+ if (filedes_close(&tmpfile_fd)) {
+ ERROR_WITH_ERRNO("Error writing to temporary "
+ "file \"%"TS"\"", tmpfile_name);
+ ret = WIMLIB_ERR_WRITE;
+ goto delete_tmpfile;
+ }
+ memcpy(&tmpfile_lte, needed_lte,
+ sizeof(struct wim_lookup_table_entry));
+ tmpfile_lte.resource_location = RESOURCE_IN_FILE_ON_DISK;
+ tmpfile_lte.file_on_disk = tmpfile_name;
+ lte_override = &tmpfile_lte;
+ } else {
+ lte_override = needed_lte;
+ }
+
+ ret = extract_stream_instances(needed_lte, lte_override, ctx);
+ delete_tmpfile:
+ lte_unbind_wim_resource_spec(needed_lte);
+ if (tmpfile_name) {
+ tunlink(tmpfile_name);
+ FREE(tmpfile_name);
+ }
if (ret)
goto out_free_found_lte;
ctx->num_streams_remaining--;
} else if (found_lte->resource_location != RESOURCE_NONEXISTENT) {
- ret = skip_pwm_stream(found_lte);
+ ret = skip_wim_stream(found_lte);
if (ret)
goto out_free_found_lte;
} else {
}
ret = 0;
out_free_found_lte:
+ if (found_lte->resource_location != RESOURCE_IN_WIM)
+ FREE(rspec);
free_lookup_table_entry(found_lte);
out:
return ret;
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);
}
if (lte) {
struct filedes _stdout;
filedes_init(&_stdout, STDOUT_FILENO);
- ret = extract_wim_resource_to_fd(lte, &_stdout,
- wim_resource_size(lte));
+ ret = extract_stream_to_fd(lte, &_stdout, lte->size);
}
}
return ret;
struct apply_ctx *ctx = _args;
int ret;
+ dentry->in_extraction_tree = 1;
+
if (dentry == ctx->extract_root || dentry->extraction_skipped)
return 0;
{
struct wim_inode *inode = dentry->d_inode;
+ dentry->in_extraction_tree = 0;
dentry->extraction_skipped = 0;
dentry->was_hardlinked = 0;
dentry->skeleton_extracted = 0;
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 to \"%"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. */
goto out_teardown_stream_list;
}
- /* If a sequential extraction was specified, sort the streams to be
- * extracted by their position in the WIM file so that the WIM file can
- * be read sequentially. */
- if ((extract_flags & (WIMLIB_EXTRACT_FLAG_SEQUENTIAL |
- WIMLIB_EXTRACT_FLAG_FROM_PIPE))
- == WIMLIB_EXTRACT_FLAG_SEQUENTIAL)
- {
- ret = sort_stream_list_by_sequential_order(
- &ctx.stream_list,
- offsetof(struct wim_lookup_table_entry,
- extraction_list));
- if (ret)
- goto out_teardown_stream_list;
- }
-
if (ctx.ops->realpath_works_on_nonexisting_files &&
((extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX) ||
ctx.ops->requires_realtarget_in_paths))
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)) {
+ if (!(extract_flags & WIMLIB_EXTRACT_FLAG_FILE_ORDER)) {
/* Sequential extraction requested, so two passes are needed
* (one for directory structure, one for streams.) */
if (progress_func)
&ctx.progress);
}
+ do_extract_warnings(&ctx);
+
ret = 0;
out_free_realtarget:
FREE(ctx.realtarget);
out_teardown_stream_list:
/* Free memory allocated as part of the mapping from each
* wim_lookup_table_entry to the dentries that reference it. */
- if (ctx.extract_flags & WIMLIB_EXTRACT_FLAG_SEQUENTIAL)
+ if (!(ctx.extract_flags & WIMLIB_EXTRACT_FLAG_FILE_ORDER))
list_for_each_entry(lte, &ctx.stream_list, extraction_list)
if (lte->out_refcnt > ARRAY_LEN(lte->inline_lte_dentries))
FREE(lte->lte_dentries);
* corresponding file or symbolic link data. This needs to be handled
* better. */
if ((extract_flags & (WIMLIB_EXTRACT_FLAG_UNIX_DATA |
- WIMLIB_EXTRACT_FLAG_SEQUENTIAL))
- == (WIMLIB_EXTRACT_FLAG_UNIX_DATA |
- WIMLIB_EXTRACT_FLAG_SEQUENTIAL))
+ WIMLIB_EXTRACT_FLAG_FILE_ORDER))
+ == WIMLIB_EXTRACT_FLAG_UNIX_DATA)
{
if (extract_flags & WIMLIB_EXTRACT_FLAG_FROM_PIPE) {
WARNING("Setting UNIX file/owner group may "
" symbolic links "
"when applying from a pipe.");
} else {
- extract_flags &= ~WIMLIB_EXTRACT_FLAG_SEQUENTIAL;
+ extract_flags |= WIMLIB_EXTRACT_FLAG_FILE_ORDER;
WARNING("Disabling sequential extraction for "
"UNIX data mode");
}
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 ret;
tchar *target_copy = canonicalize_fs_path(target);
- if (!target_copy)
+ if (target_copy == NULL)
return WIMLIB_ERR_NOMEM;
struct wimlib_extract_command cmd = {
.wim_source_path = T(""),
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;
}
if (extract_flags & WIMLIB_EXTRACT_FLAG_TO_STDOUT)
return WIMLIB_ERR_INVALID_PARAM;
- extract_flags |= WIMLIB_EXTRACT_FLAG_SEQUENTIAL;
-
/* Read the WIM header from the pipe and get a WIMStruct to represent
* the pipable WIM. Caveats: Unlike getting a WIMStruct with
* wimlib_open_wim(), getting a WIMStruct in this way will result in
* an empty lookup table, no XML data read, and no filename set. */
ret = open_wim_as_WIMStruct(&pipe_fd,
- WIMLIB_OPEN_FLAG_FROM_PIPE |
- WIMLIB_OPEN_FLAG_SPLIT_OK,
+ WIMLIB_OPEN_FLAG_FROM_PIPE,
&pwm, progress_func);
if (ret)
return ret;
* WIMs.) */
{
struct wim_lookup_table_entry xml_lte;
- ret = read_pwm_stream_header(pwm, &xml_lte, 0, NULL);
+ struct wim_resource_spec xml_rspec;
+ ret = read_pwm_stream_header(pwm, &xml_lte, &xml_rspec, 0, NULL);
if (ret)
goto out_wimlib_free;
- if (!(xml_lte.resource_entry.flags & WIM_RESHDR_FLAG_METADATA))
+ if (!(xml_lte.flags & WIM_RESHDR_FLAG_METADATA))
{
ERROR("Expected XML data, but found non-metadata "
"stream.");
goto out_wimlib_free;
}
- copy_resource_entry(&pwm->hdr.xml_res_entry,
- &xml_lte.resource_entry);
+ wim_res_spec_to_hdr(&xml_rspec, &pwm->hdr.xml_data_reshdr);
ret = read_wim_xml_data(pwm);
if (ret)
goto out_wimlib_free;
+
if (wim_info_get_num_images(pwm->wim_info) != pwm->hdr.image_count) {
ERROR("Image count in XML data is not the same as in WIM header.");
ret = WIMLIB_ERR_XML;
for (i = 1; i <= pwm->hdr.image_count; i++) {
struct wim_lookup_table_entry *metadata_lte;
struct wim_image_metadata *imd;
+ struct wim_resource_spec *metadata_rspec;
metadata_lte = new_lookup_table_entry();
- if (!metadata_lte) {
+ if (metadata_lte == NULL) {
ret = WIMLIB_ERR_NOMEM;
goto out_wimlib_free;
}
+ metadata_rspec = MALLOC(sizeof(struct wim_resource_spec));
+ if (metadata_rspec == NULL) {
+ ret = WIMLIB_ERR_NOMEM;
+ free_lookup_table_entry(metadata_lte);
+ goto out_wimlib_free;
+ }
- ret = read_pwm_stream_header(pwm, metadata_lte, 0, NULL);
+ ret = read_pwm_stream_header(pwm, metadata_lte, metadata_rspec, 0, NULL);
imd = pwm->image_metadata[i - 1];
imd->metadata_lte = metadata_lte;
- if (ret)
+ if (ret) {
+ FREE(metadata_rspec);
goto out_wimlib_free;
+ }
- if (!(metadata_lte->resource_entry.flags &
- WIM_RESHDR_FLAG_METADATA))
- {
+ if (!(metadata_lte->flags & WIM_RESHDR_FLAG_METADATA)) {
ERROR("Expected metadata resource, but found "
"non-metadata stream.");
ret = WIMLIB_ERR_INVALID_PIPABLE_WIM;
} else {
/* Metadata resource is not for the image being
* extracted. Skip over it. */
- ret = skip_pwm_stream(metadata_lte);
+ ret = skip_wim_stream(metadata_lte);
if (ret)
goto out_wimlib_free;
}
/* 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);
}