#include "wimlib/error.h"
#include "wimlib/lookup_table.h"
#include "wimlib/metadata.h"
+#include "wimlib/pathlist.h"
#include "wimlib/paths.h"
#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
#include "wimlib/xml.h"
+#include "wimlib/wildcard.h"
#include "wimlib/wim.h"
#include <errno.h>
#define WIMLIB_EXTRACT_FLAG_MULTI_IMAGE 0x80000000
#define WIMLIB_EXTRACT_FLAG_FROM_PIPE 0x40000000
-#define WIMLIB_EXTRACT_MASK_PUBLIC 0x3fffffff
+#define WIMLIB_EXTRACT_FLAG_PATHMODE 0x20000000
+#define WIMLIB_EXTRACT_MASK_PUBLIC 0x1fffffff
/* Given a WIM dentry in the tree to be extracted, resolve all streams in the
* corresponding inode and set 'out_refcnt' in each to 0. */
* "resolve" the inode's streams anyway by allocating new entries. */
if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_FROM_PIPE)
force = true;
- ret = inode_resolve_ltes(inode, ctx->wim->lookup_table, force);
+ ret = inode_resolve_streams(inode, ctx->wim->lookup_table, force);
if (ret)
return ret;
for (unsigned i = 0; i <= inode->i_num_ads; i++) {
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)
{
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, &inode->extract_cookie);
if (ret) {
* 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
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(file_spec, lte, ctx);
- else
- ret = ctx->ops->extract_unnamed_stream(file_spec, 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)
* 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 (attributes == 0)
attributes = FILE_ATTRIBUTE_NORMAL;
- ret = ctx->ops->set_file_attributes(path, attributes, ctx);
+ 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++;
}
}
}
}
/* 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
+ && !other_dentry->extraction_skipped)
{
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_stream(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) {
+ lte_unbind_wim_resource_spec(needed_lte);
+ goto out_free_found_lte;
+ }
+
+ ret = extract_full_stream_to_fd(needed_lte,
+ &tmpfile_fd);
+ 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_full_stream_to_fd(lte, &_stdout);
}
}
return ret;
goto skip_dentry;
}
-#ifdef __WIN32__
if (!ctx->ops->supports_case_sensitive_filenames)
{
struct wim_dentry *other;
list_for_each_entry(other, &dentry->case_insensitive_conflict_list,
case_insensitive_conflict_list)
{
- if (ctx->extract_flags &
- WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS) {
- WARNING("\"%"TS"\" has the same "
- "case-insensitive name as "
- "\"%"TS"\"; extracting "
- "dummy name instead",
- dentry_full_path(dentry),
- dentry_full_path(other));
- goto out_replace;
- } else {
- WARNING("Not extracting \"%"TS"\": "
- "has same case-insensitive "
- "name as \"%"TS"\"",
- dentry_full_path(dentry),
- dentry_full_path(other));
- goto skip_dentry;
+ if (!other->extraction_skipped) {
+ if (ctx->extract_flags &
+ WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS) {
+ WARNING("\"%"TS"\" has the same "
+ "case-insensitive name as "
+ "\"%"TS"\"; extracting "
+ "dummy name instead",
+ dentry_full_path(dentry),
+ dentry_full_path(other));
+ goto out_replace;
+ } else {
+ WARNING("Not extracting \"%"TS"\": "
+ "has same case-insensitive "
+ "name as \"%"TS"\"",
+ dentry_full_path(dentry),
+ dentry_full_path(other));
+ goto skip_dentry;
+ }
}
}
}
-#else /* __WIN32__ */
- wimlib_assert(ctx->ops->supports_case_sensitive_filenames);
-#endif /* !__WIN32__ */
if (file_name_valid(dentry->file_name, dentry->file_name_nbytes / 2, false)) {
-#ifdef __WIN32__
+#if TCHAR_IS_UTF16LE
dentry->extraction_name = dentry->file_name;
dentry->extraction_name_nchars = dentry->file_name_nbytes / 2;
return 0;
tchar *tchar_name;
size_t tchar_nchars;
- #ifdef __WIN32__
+ #if TCHAR_IS_UTF16LE
tchar_name = utf16_name_copy;
tchar_nchars = dentry->file_name_nbytes / 2;
#else
fixed_name_num_chars += tsprintf(fixed_name + tchar_nchars,
T(" (invalid filename #%lu)"),
++ctx->invalid_sequence);
- #ifndef __WIN32__
+ #if !TCHAR_IS_UTF16LE
FREE(tchar_name);
#endif
dentry->extraction_name = memdup(fixed_name,
{
struct wim_inode *inode = dentry->d_inode;
+ dentry->in_extraction_tree = 0;
dentry->extraction_skipped = 0;
dentry->was_hardlinked = 0;
dentry->skeleton_extracted = 0;
struct wim_features *features = _features;
struct wim_inode *inode = dentry->d_inode;
+ if (dentry->extraction_skipped)
+ return 0;
+
if (inode->i_attributes & FILE_ATTRIBUTE_ARCHIVE)
features->archive_files++;
if (inode->i_attributes & FILE_ATTRIBUTE_HIDDEN)
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
const struct wim_features *supported_features,
int extract_flags,
const struct apply_operations *ops,
- const tchar *wim_source_path)
+ const tchar *wim_source_path,
+ bool warn)
{
const tchar *loc;
const tchar *mode = T("this extraction mode");
else
loc = wim_source_path;
- /* We're an archive program, so theoretically we can do what we want
- * with FILE_ATTRIBUTE_ARCHIVE (which is a dumb flag anyway). Don't
- * bother the user about it. */
+ if (warn) {
+ /* We're an archive program, so theoretically we can do what we want
+ * with FILE_ATTRIBUTE_ARCHIVE (which is a dumb flag anyway). Don't
+ * bother the user about it. */
#if 0
- if (required_features->archive_files && !supported_features->archive_files)
- {
- WARNING(
- "%lu files in %"TS" are marked as archived, but this attribute\n"
-" is not supported in %"TS".",
- required_features->archive_files, loc, mode);
- }
+ if (required_features->archive_files && !supported_features->archive_files)
+ {
+ WARNING(
+ "%lu files in %"TS" are marked as archived, but this attribute\n"
+ " is not supported in %"TS".",
+ required_features->archive_files, loc, mode);
+ }
#endif
- if (required_features->hidden_files && !supported_features->hidden_files)
- {
- WARNING(
- "%lu files in %"TS" are marked as hidden, but this\n"
-" attribute is not supported in %"TS".",
- required_features->hidden_files, loc, mode);
- }
+ if (required_features->hidden_files && !supported_features->hidden_files)
+ {
+ WARNING(
+ "%lu files in %"TS" are marked as hidden, but this\n"
+ " attribute is not supported in %"TS".",
+ required_features->hidden_files, loc, mode);
+ }
- if (required_features->system_files && !supported_features->system_files)
- {
- WARNING(
- "%lu files in %"TS" are marked as system files,\n"
-" but this attribute is not supported in %"TS".",
- required_features->system_files, loc, mode);
- }
+ if (required_features->system_files && !supported_features->system_files)
+ {
+ WARNING(
+ "%lu files in %"TS" are marked as system files,\n"
+ " but this attribute is not supported in %"TS".",
+ required_features->system_files, loc, mode);
+ }
- if (required_features->compressed_files && !supported_features->compressed_files)
- {
- WARNING(
- "%lu files in %"TS" are marked as being transparently\n"
-" compressed, but transparent compression is not supported in\n"
-" %"TS". These files will be extracted as uncompressed.",
- required_features->compressed_files, loc, mode);
- }
+ if (required_features->compressed_files && !supported_features->compressed_files)
+ {
+ WARNING(
+ "%lu files in %"TS" are marked as being transparently\n"
+ " compressed, but transparent compression is not supported in\n"
+ " %"TS". These files will be extracted as uncompressed.",
+ required_features->compressed_files, loc, mode);
+ }
- if (required_features->encrypted_files && !supported_features->encrypted_files)
- {
- WARNING(
- "%lu files in %"TS" are marked as being encrypted,\n"
-" but encryption is not supported in %"TS". These files\n"
-" will not be extracted.",
- required_features->encrypted_files, loc, mode);
- }
+ if (required_features->encrypted_files && !supported_features->encrypted_files)
+ {
+ WARNING(
+ "%lu files in %"TS" are marked as being encrypted,\n"
+ " but encryption is not supported in %"TS". These files\n"
+ " 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->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)
- {
- WARNING(
- "%lu files in %"TS" are marked as not content indexed,\n"
-" but this attribute is not supported in %"TS".",
- required_features->not_context_indexed_files, loc, mode);
- }
+ if (required_features->not_context_indexed_files &&
+ !supported_features->not_context_indexed_files)
+ {
+ WARNING(
+ "%lu files in %"TS" are marked as not content indexed,\n"
+ " but this attribute is not supported in %"TS".",
+ required_features->not_context_indexed_files, loc, mode);
+ }
- if (required_features->sparse_files && !supported_features->sparse_files)
- {
- WARNING(
- "%lu files in %"TS" are marked as sparse, but creating\n"
-" sparse files is not supported in %"TS". These files\n"
-" will be extracted as non-sparse.",
- required_features->sparse_files, loc, mode);
- }
+ if (required_features->sparse_files && !supported_features->sparse_files)
+ {
+ WARNING(
+ "%lu files in %"TS" are marked as sparse, but creating\n"
+ " sparse files is not supported in %"TS". These files\n"
+ " will be extracted as non-sparse.",
+ required_features->sparse_files, loc, mode);
+ }
- if (required_features->named_data_streams &&
- !supported_features->named_data_streams)
- {
- WARNING(
- "%lu files in %"TS" contain one or more alternate (named)\n"
-" data streams, which are not supported in %"TS".\n"
-" Alternate data streams will NOT be extracted.",
- required_features->named_data_streams, loc, mode);
- }
+ if (required_features->named_data_streams &&
+ !supported_features->named_data_streams)
+ {
+ WARNING(
+ "%lu files in %"TS" contain one or more alternate (named)\n"
+ " data streams, which are not supported in %"TS".\n"
+ " Alternate data streams will NOT be extracted.",
+ required_features->named_data_streams, loc, mode);
+ }
- if (unlikely(extract_flags & (WIMLIB_EXTRACT_FLAG_HARDLINK |
- WIMLIB_EXTRACT_FLAG_SYMLINK)) &&
- required_features->named_data_streams &&
- supported_features->named_data_streams)
- {
- WARNING(
- "%lu files in %"TS" contain one or more alternate (named)\n"
-" data streams, which are not supported in linked extraction mode.\n"
-" Alternate data streams will NOT be extracted.",
- required_features->named_data_streams, loc);
- }
+ if (unlikely(extract_flags & (WIMLIB_EXTRACT_FLAG_HARDLINK |
+ WIMLIB_EXTRACT_FLAG_SYMLINK)) &&
+ required_features->named_data_streams &&
+ supported_features->named_data_streams)
+ {
+ WARNING(
+ "%lu files in %"TS" contain one or more alternate (named)\n"
+ " data streams, which are not supported in linked extraction mode.\n"
+ " Alternate data streams will NOT be extracted.",
+ required_features->named_data_streams, loc);
+ }
- if (required_features->hard_links && !supported_features->hard_links)
- {
- WARNING(
- "%lu files in %"TS" are hard links, but hard links are\n"
-" not supported in %"TS". Hard links will be extracted as\n"
-" duplicate copies of the linked files.",
- required_features->hard_links, loc, mode);
- }
+ if (required_features->hard_links && !supported_features->hard_links)
+ {
+ WARNING(
+ "%lu files in %"TS" are hard links, but hard links are\n"
+ " not supported in %"TS". Hard links will be extracted as\n"
+ " duplicate copies of the linked files.",
+ required_features->hard_links, loc, mode);
+ }
- if (required_features->reparse_points && !supported_features->reparse_points)
- {
- if (supported_features->symlink_reparse_points) {
- if (required_features->other_reparse_points) {
+ if (required_features->reparse_points && !supported_features->reparse_points)
+ {
+ if (supported_features->symlink_reparse_points) {
+ if (required_features->other_reparse_points) {
+ WARNING(
+ "%lu files in %"TS" are reparse points that are neither\n"
+ " symbolic links nor junction points and are not supported in\n"
+ " %"TS". These reparse points will not be extracted.",
+ required_features->other_reparse_points, loc,
+ mode);
+ }
+ } else {
WARNING(
- "%lu files in %"TS" are reparse points that are neither\n"
-" symbolic links nor junction points and are not supported in\n"
-" %"TS". These reparse points will not be extracted.",
- required_features->other_reparse_points, loc,
- mode);
+ "%lu files in %"TS" are reparse points, which are\n"
+ " not supported in %"TS" and will not be extracted.",
+ required_features->reparse_points, loc, mode);
}
- } else {
- WARNING(
- "%lu files in %"TS" are reparse points, which are\n"
-" not supported in %"TS" and will not be extracted.",
- required_features->reparse_points, loc, mode);
}
- }
- if (required_features->security_descriptors &&
- !supported_features->security_descriptors)
- {
- WARNING(
- "%lu files in %"TS" have Windows NT security descriptors,\n"
-" but extracting security descriptors is not supported in\n"
-" %"TS". No security descriptors will be extracted.",
- required_features->security_descriptors, loc, mode);
- }
+ if (required_features->security_descriptors &&
+ !supported_features->security_descriptors)
+ {
+ WARNING(
+ "%lu files in %"TS" have Windows NT security descriptors,\n"
+ " but extracting security descriptors is not supported in\n"
+ " %"TS". No security descriptors will be extracted.",
+ required_features->security_descriptors, loc, mode);
+ }
- if (required_features->short_names && !supported_features->short_names)
- {
- WARNING(
- "%lu files in %"TS" have short (DOS) names, but\n"
-" extracting short names is not supported in %"TS".\n"
-" Short names will not be extracted.\n",
- required_features->short_names, loc, mode);
+ if (required_features->short_names && !supported_features->short_names)
+ {
+ WARNING(
+ "%lu files in %"TS" have short (DOS) names, but\n"
+ " extracting short names is not supported in %"TS".\n"
+ " Short names will not be extracted.\n",
+ required_features->short_names, loc, mode);
+ }
}
if ((extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) &&
return 0;
}
-/*
- * extract_tree - Extract a file or directory tree from the currently selected
- * WIM image.
- *
- * @wim: WIMStruct for the WIM file, with the desired image selected
- * (as wim->current_image).
- *
- * @wim_source_path:
- * "Canonical" (i.e. no leading or trailing slashes, path
- * separators WIM_PATH_SEPARATOR) path inside the WIM image to
- * extract. An empty string means the full image.
- *
- * @target:
- * Filesystem path to extract the file or directory tree to.
- * (Or, with WIMLIB_EXTRACT_FLAG_NTFS: the name of a NTFS volume.)
- *
- * @extract_flags:
- * WIMLIB_EXTRACT_FLAG_*. Also, the private flag
- * WIMLIB_EXTRACT_FLAG_MULTI_IMAGE will be set if this is being
- * called through wimlib_extract_image() with WIMLIB_ALL_IMAGES as
- * the image.
- *
- * @progress_func:
- * If non-NULL, progress function for the extraction. The messages
- * that may be sent in this function are:
- *
- * WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN or
- * WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN;
- * WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN;
- * WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END;
- * WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY;
- * WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS;
- * WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS;
- * WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END or
- * WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END.
- *
- * Returns 0 on success; a positive WIMLIB_ERR_* code on failure.
- */
+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
+}
+
static int
-extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target,
- int extract_flags, wimlib_progress_func_t progress_func)
+dentry_set_skipped(struct wim_dentry *dentry, void *_ignore)
+{
+ dentry->in_extraction_tree = 1;
+ dentry->extraction_skipped = 1;
+ return 0;
+}
+
+static int
+dentry_set_not_skipped(struct wim_dentry *dentry, void *_ignore)
+{
+ dentry->in_extraction_tree = 1;
+ dentry->extraction_skipped = 0;
+ return 0;
+}
+
+static int
+extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees,
+ const tchar *wim_source_path, const tchar *target,
+ int extract_flags, wimlib_progress_func_t progress_func)
{
- struct wim_dentry *root;
struct wim_features required_features;
struct apply_ctx ctx;
int ret;
struct wim_lookup_table_entry *lte;
+ wimlib_assert(num_trees != 0);
+ wimlib_assert(target != NULL);
+
+ if (num_trees > 1)
+ wimlib_assert((extract_flags & WIMLIB_EXTRACT_FLAG_PATHMODE));
+
/* Start initializing the apply_ctx. */
memset(&ctx, 0, sizeof(struct apply_ctx));
ctx.wim = wim;
}
INIT_LIST_HEAD(&ctx.stream_list);
- /* Translate the path to extract into the corresponding
- * `struct wim_dentry', which will be the root of the
- * "dentry tree" to extract. */
- root = get_dentry(wim, wim_source_path);
- if (!root) {
- ERROR("Path \"%"TS"\" does not exist in WIM image %d",
- wim_source_path, wim->current_image);
- ret = WIMLIB_ERR_PATH_DOES_NOT_EXIST;
- goto out;
+ if (extract_flags & WIMLIB_EXTRACT_FLAG_PATHMODE) {
+ /* Path mode --- there may be multiple trees, and targets are
+ * set relative to the root of the image.
+ *
+ * Consider all dentries to be in the extraction tree, but
+ * assume all to be skipped unless in one of the subtrees being
+ * extracted or one of the ancestors of the subtrees up to the
+ * image root. */
+ ctx.extract_root = wim_root_dentry(wim);
+ for_dentry_in_tree(ctx.extract_root, dentry_set_skipped, NULL);
+
+ for (size_t i = 0; i < num_trees; i++) {
+ struct wim_dentry *d;
+
+ for_dentry_in_tree(trees[i], dentry_set_not_skipped, NULL);
+ d = trees[i];
+ while (d != ctx.extract_root) {
+ d = d->parent;
+ dentry_set_not_skipped(d, NULL);
+ }
+ }
+ } else {
+ ctx.extract_root = trees[0];
+ for_dentry_in_tree(ctx.extract_root, dentry_set_not_skipped, NULL);
}
- ctx.extract_root = root;
-
/* Select the appropriate apply_operations based on the
* platform and extract_flags. */
#ifdef __WIN32__
*/
ret = ctx.ops->start_extract(target, &ctx);
if (ret)
- goto out;
+ goto out_dentry_reset_needs_extraction;
- dentry_tree_get_features(root, &required_features);
+ /* Get and check the features required to extract the dentry tree. */
+ dentry_tree_get_features(ctx.extract_root, &required_features);
ret = do_feature_check(&required_features, &ctx.supported_features,
- extract_flags, ctx.ops, wim_source_path);
+ extract_flags, ctx.ops,
+ wim_source_path,
+ !(extract_flags & WIMLIB_EXTRACT_FLAG_PATHMODE));
if (ret)
goto out_finish_or_abort_extract;
/* Calculate the actual filename component of each extracted dentry. In
* the process, set the dentry->extraction_skipped flag on dentries that
- * are being skipped for some reason (e.g. invalid filename). */
- ret = for_dentry_in_tree(root, dentry_calculate_extraction_path, &ctx);
+ * are being skipped because of filename or supported features problems. */
+ ret = for_dentry_in_tree(ctx.extract_root,
+ dentry_calculate_extraction_path, &ctx);
if (ret)
goto out_dentry_reset_needs_extraction;
* directory tree. (If not, extract_dentry_to_stdout() will
* return an error.) */
if (extract_flags & WIMLIB_EXTRACT_FLAG_TO_STDOUT) {
- ret = extract_dentry_to_stdout(root);
+ ret = 0;
+ for (size_t i = 0; i < num_trees; i++) {
+ ret = extract_dentry_to_stdout(trees[i]);
+ if (ret)
+ break;
+ }
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))
}
if (progress_func) {
- progress_func(*wim_source_path ? WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN :
- WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN,
- &ctx.progress);
+ int msg;
+ if (*wim_source_path || (extract_flags & WIMLIB_EXTRACT_FLAG_PATHMODE))
+ msg = WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN;
+ else
+ msg = WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN;
+ progress_func(msg, &ctx.progress);
}
if (!ctx.root_dentry_is_special)
{
tchar path[ctx.ops->path_max];
- if (build_extraction_path(path, root, &ctx))
+ if (build_extraction_path(path, ctx.extract_root, &ctx))
{
- ret = extract_inode(path, &ctx, root->d_inode);
+ ret = extract_inode(path, &ctx, ctx.extract_root->d_inode);
if (ret)
goto out_free_realtarget;
}
ctx.realtarget_nchars = tstrlen(ctx.realtarget);
}
+ if (ctx.ops->requires_short_name_reordering) {
+ ret = for_dentry_in_tree(ctx.extract_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);
if (!(extract_flags & WIMLIB_EXTRACT_FLAG_RESUME)) {
- ret = for_dentry_in_tree(root, dentry_extract_skeleton, &ctx);
+ ret = for_dentry_in_tree(ctx.extract_root, dentry_extract_skeleton, &ctx);
if (ret)
goto out_free_realtarget;
}
if (progress_func)
progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN,
&ctx.progress);
- ret = for_dentry_in_tree(root, dentry_extract, &ctx);
+ ret = for_dentry_in_tree(ctx.extract_root, dentry_extract, &ctx);
if (ret)
goto out_free_realtarget;
if (progress_func)
if (progress_func)
progress_func(WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS,
&ctx.progress);
- ret = for_dentry_in_tree_depth(root, dentry_extract_final, &ctx);
+ ret = for_dentry_in_tree_depth(ctx.extract_root, dentry_extract_final, &ctx);
if (ret)
goto out_free_realtarget;
if (progress_func) {
- progress_func(*wim_source_path ? WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END :
- WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END,
- &ctx.progress);
+ int msg;
+ if (*wim_source_path || (extract_flags & WIMLIB_EXTRACT_FLAG_PATHMODE))
+ msg = WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END;
+ else
+ msg = WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END;
+ progress_func(msg, &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);
-out_dentry_reset_needs_extraction:
- for_dentry_in_tree(root, dentry_reset_needs_extraction, NULL);
out_finish_or_abort_extract:
if (ret) {
if (ctx.ops->abort_extract)
if (ctx.ops->finish_extract)
ret = ctx.ops->finish_extract(&ctx);
}
-out:
+out_dentry_reset_needs_extraction:
+ for_dentry_in_tree(ctx.extract_root, dentry_reset_needs_extraction, NULL);
return ret;
}
-/* Validates a single wimlib_extract_command, mostly checking to make sure the
- * extract flags make sense. */
+/*
+ * extract_tree - Extract a file or directory tree from the currently selected
+ * WIM image.
+ *
+ * @wim: WIMStruct for the WIM file, with the desired image selected
+ * (as wim->current_image).
+ *
+ * @wim_source_path:
+ * "Canonical" (i.e. no leading or trailing slashes, path
+ * separators WIM_PATH_SEPARATOR) path inside the WIM image to
+ * extract. An empty string means the full image.
+ *
+ * @target:
+ * Filesystem path to extract the file or directory tree to.
+ * (Or, with WIMLIB_EXTRACT_FLAG_NTFS: the name of a NTFS volume.)
+ *
+ * @extract_flags:
+ * WIMLIB_EXTRACT_FLAG_*. Also, the private flag
+ * WIMLIB_EXTRACT_FLAG_MULTI_IMAGE will be set if this is being
+ * called through wimlib_extract_image() with WIMLIB_ALL_IMAGES as
+ * the image.
+ *
+ * @progress_func:
+ * If non-NULL, progress function for the extraction. The messages
+ * that may be sent in this function are:
+ *
+ * WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN or
+ * WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN;
+ * WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN;
+ * WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END;
+ * WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY;
+ * WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS;
+ * WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS;
+ * WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END or
+ * WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END.
+ *
+ * Returns 0 on success; a positive WIMLIB_ERR_* code on failure.
+ */
static int
-check_extract_command(struct wimlib_extract_command *cmd, int wim_header_flags)
+extract_tree(WIMStruct *wim, const tchar *wim_source_path,
+ const tchar *target, int extract_flags,
+ wimlib_progress_func_t progress_func)
{
- int extract_flags;
+ struct wim_dentry *root;
- /* Empty destination path? */
- if (cmd->fs_dest_path[0] == T('\0'))
- return WIMLIB_ERR_INVALID_PARAM;
+ root = get_dentry(wim, wim_source_path, WIMLIB_CASE_PLATFORM_DEFAULT);
- extract_flags = cmd->extract_flags;
+ if (root == NULL) {
+ ERROR("Path \"%"TS"\" does not exist in WIM image %d",
+ wim_source_path, wim->current_image);
+ return WIMLIB_ERR_PATH_DOES_NOT_EXIST;
+ }
+ return extract_trees(wim, &root, 1, wim_source_path,
+ target, extract_flags, progress_func);
+}
+/* Make sure the extraction flags make sense, and update them if needed. */
+static int
+check_extract_flags(int extract_flags,
+ const u32 wim_header_flags,
+ int *updated_extract_flags_ret)
+{
/* Check for invalid flag combinations */
if ((extract_flags &
(WIMLIB_EXTRACT_FLAG_SYMLINK |
WIMLIB_EXTRACT_FLAG_FROM_PIPE)) == WIMLIB_EXTRACT_FLAG_RESUME)
return WIMLIB_ERR_INVALID_PARAM;
- if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
#ifndef WITH_NTFS_3G
+ if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
ERROR("wimlib was compiled without support for NTFS-3g, so\n"
" we cannot apply a WIM image directly to a NTFS volume.");
return WIMLIB_ERR_UNSUPPORTED;
-#endif
}
+#endif
if ((extract_flags & (WIMLIB_EXTRACT_FLAG_RPFIX |
WIMLIB_EXTRACT_FLAG_NORPFIX)) == 0)
* 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");
}
}
- cmd->extract_flags = extract_flags;
+ if (updated_extract_flags_ret)
+ *updated_extract_flags_ret = extract_flags;
return 0;
}
/* Check for problems with the extraction commands */
for (size_t i = 0; i < num_cmds; i++) {
- ret = check_extract_command(&cmds[i], wim->hdr.flags);
+
+ if (cmds[i].fs_dest_path[0] == T('\0'))
+ return WIMLIB_ERR_INVALID_PARAM;
+
+ if (cmds[i].extract_flags & WIMLIB_EXTRACT_FLAG_GLOB_PATHS)
+ return WIMLIB_ERR_INVALID_PARAM;
+
+ ret = check_extract_flags(cmds[i].extract_flags,
+ wim->hdr.flags,
+ &cmds[i].extract_flags);
if (ret)
return ret;
if (cmds[i].extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
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);
}
+
+/* API function documented in wimlib.h */
+WIMLIBAPI int
+wimlib_extract_pathlist(WIMStruct *wim, int image,
+ const tchar *target,
+ const tchar *path_list_file,
+ int extract_flags,
+ wimlib_progress_func_t progress_func)
+{
+ int ret;
+ tchar **paths;
+ size_t num_paths;
+ void *mem;
+
+ ret = read_path_list_file(path_list_file, &paths, &num_paths, &mem);
+ if (ret) {
+ ERROR("Failed to read path list file \"%"TS"\"",
+ path_list_file);
+ return ret;
+ }
+
+ ret = wimlib_extract_paths(wim, image, target,
+ (const tchar * const *)paths, num_paths,
+ extract_flags, progress_func);
+ FREE(paths);
+ FREE(mem);
+ return ret;
+}
+
+struct append_dentry_ctx {
+ struct wim_dentry **dentries;
+ size_t num_dentries;
+ size_t num_alloc_dentries;
+};
+
+static int
+append_dentry_cb(struct wim_dentry *dentry, void *_ctx)
+{
+ struct append_dentry_ctx *ctx = _ctx;
+
+ if (ctx->num_dentries == ctx->num_alloc_dentries) {
+ struct wim_dentry **new_dentries;
+ size_t new_length;
+
+ new_length = max(ctx->num_alloc_dentries + 8,
+ ctx->num_alloc_dentries * 3 / 2);
+ new_dentries = REALLOC(ctx->dentries,
+ new_length * sizeof(ctx->dentries[0]));
+ if (new_dentries == NULL)
+ return WIMLIB_ERR_NOMEM;
+ ctx->dentries = new_dentries;
+ ctx->num_alloc_dentries = new_length;
+ }
+ ctx->dentries[ctx->num_dentries++] = dentry;
+ return 0;
+}
+
+/* API function documented in wimlib.h */
+WIMLIBAPI int
+wimlib_extract_paths(WIMStruct *wim,
+ int image,
+ const tchar *target,
+ const tchar * const *paths,
+ size_t num_paths,
+ int extract_flags,
+ wimlib_progress_func_t progress_func)
+{
+ int ret;
+ struct wim_dentry **trees;
+ size_t num_trees;
+
+ extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
+
+ if (wim == NULL || target == NULL || target[0] == T('\0') ||
+ (num_paths != 0 && paths == NULL))
+ return WIMLIB_ERR_INVALID_PARAM;
+
+ ret = check_extract_flags(extract_flags, wim->hdr.flags,
+ &extract_flags);
+ if (ret)
+ return ret;
+
+ ret = select_wim_image(wim, image);
+ if (ret)
+ return ret;
+
+ if (extract_flags & WIMLIB_EXTRACT_FLAG_GLOB_PATHS) {
+
+ struct append_dentry_ctx append_dentry_ctx = {
+ .dentries = NULL,
+ .num_dentries = 0,
+ .num_alloc_dentries = 0,
+ };
+
+ u32 wildcard_flags = 0;
+
+ if (extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_GLOB)
+ wildcard_flags |= WILDCARD_FLAG_ERROR_IF_NO_MATCH;
+ else
+ wildcard_flags |= WILDCARD_FLAG_WARN_IF_NO_MATCH;
+
+ if (default_ignore_case)
+ wildcard_flags |= WILDCARD_FLAG_CASE_INSENSITIVE;
+
+ for (size_t i = 0; i < num_paths; i++) {
+ tchar *path = canonicalize_wim_path(paths[i]);
+ if (path == NULL) {
+ ret = WIMLIB_ERR_NOMEM;
+ trees = append_dentry_ctx.dentries;
+ goto out_free_trees;
+ }
+ ret = expand_wildcard(wim, path,
+ append_dentry_cb,
+ &append_dentry_ctx,
+ wildcard_flags);
+ FREE(path);
+ if (ret) {
+ trees = append_dentry_ctx.dentries;
+ goto out_free_trees;
+ }
+ }
+ trees = append_dentry_ctx.dentries;
+ num_trees = append_dentry_ctx.num_dentries;
+ } else {
+ trees = MALLOC(num_paths * sizeof(trees[0]));
+ if (trees == NULL)
+ return WIMLIB_ERR_NOMEM;
+
+ for (size_t i = 0; i < num_paths; i++) {
+
+ tchar *path = canonicalize_wim_path(paths[i]);
+ if (path == NULL) {
+ ret = WIMLIB_ERR_NOMEM;
+ goto out_free_trees;
+ }
+
+ trees[i] = get_dentry(wim, path,
+ WIMLIB_CASE_PLATFORM_DEFAULT);
+ FREE(path);
+ if (trees[i] == NULL) {
+ ERROR("Path \"%"TS"\" does not exist "
+ "in WIM image %d",
+ paths[i], wim->current_image);
+ ret = WIMLIB_ERR_PATH_DOES_NOT_EXIST;
+ goto out_free_trees;
+ }
+ }
+ num_trees = num_paths;
+ }
+
+ if (num_trees == 0) {
+ ret = 0;
+ goto out_free_trees;
+ }
+
+ ret = extract_trees(wim, trees, num_trees,
+ T(""), target,
+ ((extract_flags &
+ ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS)
+ | WIMLIB_EXTRACT_FLAG_PATHMODE),
+ progress_func);
+
+ if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
+ WIMLIB_EXTRACT_FLAG_HARDLINK))
+ {
+ for_lookup_table_entry(wim->lookup_table,
+ lte_free_extracted_file,
+ NULL);
+ }
+out_free_trees:
+ FREE(trees);
+ return ret;
+}