/*
* Copyright (C) 2012, 2013, 2014 Eric Biggers
*
- * This file is part of wimlib, a library for working with WIM files.
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
*
- * wimlib is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- * A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
- * You should have received a copy of the GNU General Public License
- * along with wimlib; if not, see http://www.gnu.org/licenses/.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this file; if not, see http://www.gnu.org/licenses/.
*/
/*
# include "config.h"
#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
#include "wimlib/apply.h"
+#include "wimlib/assert.h"
#include "wimlib/dentry.h"
#include "wimlib/encoding.h"
#include "wimlib/endianness.h"
#include "wimlib/wildcard.h"
#include "wimlib/wim.h"
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
#define WIMLIB_EXTRACT_FLAG_FROM_PIPE 0x80000000
#define WIMLIB_EXTRACT_FLAG_IMAGEMODE 0x40000000
WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE | \
WIMLIB_EXTRACT_FLAG_WIMBOOT)
+/* Send WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE or
+ * WIMLIB_PROGRESS_MSG_EXTRACT_METADATA. */
+int
+do_file_extract_progress(struct apply_ctx *ctx, enum wimlib_progress_msg msg)
+{
+ ctx->count_until_file_progress = 500; /* Arbitrary value to limit calls */
+ return extract_progress(ctx, msg);
+}
+
+static int
+start_file_phase(struct apply_ctx *ctx, uint64_t end_file_count, enum wimlib_progress_msg msg)
+{
+ ctx->progress.extract.current_file_count = 0;
+ ctx->progress.extract.end_file_count = end_file_count;
+ return do_file_extract_progress(ctx, msg);
+}
+
+int
+start_file_structure_phase(struct apply_ctx *ctx, uint64_t end_file_count)
+{
+ return start_file_phase(ctx, end_file_count, WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE);
+}
+
+int
+start_file_metadata_phase(struct apply_ctx *ctx, uint64_t end_file_count)
+{
+ return start_file_phase(ctx, end_file_count, WIMLIB_PROGRESS_MSG_EXTRACT_METADATA);
+}
+
+static int
+end_file_phase(struct apply_ctx *ctx, enum wimlib_progress_msg msg)
+{
+ ctx->progress.extract.current_file_count = ctx->progress.extract.end_file_count;
+ return do_file_extract_progress(ctx, msg);
+}
+
+int
+end_file_structure_phase(struct apply_ctx *ctx)
+{
+ return end_file_phase(ctx, WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE);
+}
+
+int
+end_file_metadata_phase(struct apply_ctx *ctx)
+{
+ return end_file_phase(ctx, WIMLIB_PROGRESS_MSG_EXTRACT_METADATA);
+}
+
/* Check whether the extraction of a dentry should be skipped completely. */
static bool
dentry_is_supported(struct wim_dentry *dentry,
struct wim_inode *inode = dentry->d_inode;
if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
- return supported_features->reparse_points ||
- (inode_is_symlink(inode) &&
- supported_features->symlink_reparse_points);
+ if (!(supported_features->reparse_points ||
+ (inode_is_symlink(inode) &&
+ supported_features->symlink_reparse_points)))
+ return false;
}
+
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;
+ if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
+ if (!supported_features->encrypted_directories)
+ return false;
+ } else {
+ if (!supported_features->encrypted_files)
+ return false;
+ }
}
+
return true;
}
lte_unbind_wim_resource_spec(found_lte);
lte_bind_wim_resource_spec(needed_lte, rspec);
- ret = (*cbs->begin_stream)(needed_lte, 0,
+ ret = (*cbs->begin_stream)(needed_lte,
cbs->begin_stream_ctx);
if (ret) {
lte_unbind_wim_resource_spec(needed_lte);
}
ret = 0;
out:
- if (found_lte->resource_location != RESOURCE_IN_WIM)
+ if (found_lte && found_lte->resource_location != RESOURCE_IN_WIM)
FREE(rspec);
free_lookup_table_entry(found_lte);
return ret;
}
static int
-begin_extract_stream_wrapper(struct wim_lookup_table_entry *lte,
- u32 flags, void *_ctx)
+begin_extract_stream_wrapper(struct wim_lookup_table_entry *lte, void *_ctx)
{
struct apply_ctx *ctx = _ctx;
ctx->cur_stream = lte;
+ ctx->cur_stream_offset = 0;
if (unlikely(lte->out_refcnt > MAX_OPEN_STREAMS))
return create_temporary_file(&ctx->tmpfile_fd, &ctx->tmpfile_name);
else
- return (*ctx->saved_cbs->begin_stream)(lte, flags,
- ctx->saved_cbs->begin_stream_ctx);
+ return (*ctx->saved_cbs->begin_stream)(lte, ctx->saved_cbs->begin_stream_ctx);
}
static int
union wimlib_progress_info *progress = &ctx->progress;
int ret;
+ ctx->cur_stream_offset += size;
+
if (likely(ctx->supported_features.hard_links)) {
progress->extract.completed_bytes +=
(u64)size * ctx->cur_stream->out_refcnt;
+ if (ctx->cur_stream_offset == ctx->cur_stream->size)
+ progress->extract.completed_streams += ctx->cur_stream->out_refcnt;
} else {
const struct stream_owner *owners = stream_owners(ctx->cur_stream);
for (u32 i = 0; i < ctx->cur_stream->out_refcnt; i++) {
d_extraction_alias_node)
{
progress->extract.completed_bytes += size;
+ if (ctx->cur_stream_offset == ctx->cur_stream->size)
+ progress->extract.completed_streams++;
}
}
}
}
static int
-extract_from_tmpfile(const tchar *tmpfile_name,
- struct wim_lookup_table_entry *orig_lte,
- struct apply_ctx *ctx)
+extract_from_tmpfile(const tchar *tmpfile_name, struct apply_ctx *ctx)
{
struct wim_lookup_table_entry tmpfile_lte;
- const struct stream_owner *owners = stream_owners(orig_lte);
+ struct wim_lookup_table_entry *orig_lte = ctx->cur_stream;
+ const struct read_stream_list_callbacks *cbs = ctx->saved_cbs;
int ret;
+ const u32 orig_refcnt = orig_lte->out_refcnt;
+
+ BUILD_BUG_ON(MAX_OPEN_STREAMS < ARRAY_LEN(orig_lte->inline_stream_owners));
+
+ struct stream_owner *owners = orig_lte->stream_owners;
/* Copy the stream's data from the temporary file to each of its
* destinations.
memcpy(&tmpfile_lte, orig_lte, sizeof(struct wim_lookup_table_entry));
tmpfile_lte.resource_location = RESOURCE_IN_FILE_ON_DISK;
tmpfile_lte.file_on_disk = ctx->tmpfile_name;
- tmpfile_lte.out_refcnt = 1;
+ ret = 0;
+ for (u32 i = 0; i < orig_refcnt; i++) {
- for (u32 i = 0; i < orig_lte->out_refcnt; i++) {
- tmpfile_lte.inline_stream_owners[0] = owners[i];
- ret = read_full_stream_with_cbs(&tmpfile_lte, ctx->saved_cbs);
+ /* Note: it usually doesn't matter whether we pass the original
+ * stream entry to callbacks provided by the extraction backend
+ * as opposed to the tmpfile stream entry, since they shouldn't
+ * actually read data from the stream other than through the
+ * read_stream_prefix() call below. But for
+ * WIMLIB_EXTRACT_FLAG_WIMBOOT mode on Windows it does matter
+ * because it needs the original stream location in order to
+ * create the external backing reference. */
+
+ orig_lte->out_refcnt = 1;
+ orig_lte->inline_stream_owners[0] = owners[i];
+
+ ret = (*cbs->begin_stream)(orig_lte, cbs->begin_stream_ctx);
if (ret)
- return ret;
+ break;
+
+ /* Extra SHA-1 isn't necessary here, but it shouldn't hurt as
+ * this case is very rare anyway. */
+ ret = extract_stream(&tmpfile_lte, tmpfile_lte.size,
+ cbs->consume_chunk,
+ cbs->consume_chunk_ctx);
+
+ ret = (*cbs->end_stream)(orig_lte, ret, cbs->end_stream_ctx);
+ if (ret)
+ break;
}
- return 0;
+ FREE(owners);
+ orig_lte->out_refcnt = 0;
+ return ret;
}
static int
if (unlikely(filedes_valid(&ctx->tmpfile_fd))) {
filedes_close(&ctx->tmpfile_fd);
if (!status)
- status = extract_from_tmpfile(ctx->tmpfile_name,
- stream, ctx);
+ status = extract_from_tmpfile(ctx->tmpfile_name, ctx);
filedes_invalidate(&ctx->tmpfile_fd);
tunlink(ctx->tmpfile_name);
FREE(ctx->tmpfile_name);
inode = dentry->d_inode;
dentry_reset_extraction_list_node(dentry);
inode->i_visited = 0;
+ inode->i_can_externally_back = 0;
if ((void *)dentry->d_extraction_name != (void *)dentry->file_name)
FREE(dentry->d_extraction_name);
dentry->d_extraction_name = NULL;
{
int ret;
- if (!dentry_is_supported(dentry, &ctx->supported_features))
+ if (unlikely(!dentry_is_supported(dentry, &ctx->supported_features)))
goto skip_dentry;
if (dentry_is_root(dentry))
}
static int
-ref_stream(struct wim_lookup_table_entry *lte, u32 stream_idx,
+ref_stream(struct wim_lookup_table_entry *lte, unsigned stream_idx,
struct wim_dentry *dentry, struct apply_ctx *ctx)
{
struct wim_inode *inode = dentry->d_inode;
return 0;
ctx->progress.extract.total_bytes += lte->size;
- ctx->progress.extract.num_streams++;
+ ctx->progress.extract.total_streams++;
if (inode->i_visited)
return 0;
}
static int
-dentry_ref_streams(struct wim_dentry *dentry, struct apply_ctx *ctx)
+ref_unnamed_stream(struct wim_dentry *dentry, struct apply_ctx *ctx)
{
struct wim_inode *inode = dentry->d_inode;
int ret;
+ unsigned stream_idx;
+ struct wim_lookup_table_entry *stream;
- /* The unnamed data stream will always be extracted, except in an
- * unlikely case. */
- if (!inode_is_encrypted_directory(inode)) {
- u16 stream_idx;
- struct wim_lookup_table_entry *stream;
-
- stream = inode_unnamed_stream_resolved(inode, &stream_idx);
- ret = ref_stream(stream, stream_idx, dentry, ctx);
- if (ret)
- return ret;
+ if (unlikely(ctx->apply_ops->will_externally_back)) {
+ ret = (*ctx->apply_ops->will_externally_back)(dentry, ctx);
+ if (ret >= 0) {
+ if (ret) /* Error */
+ return ret;
+ /* Will externally back */
+ return 0;
+ }
+ /* Won't externally back */
}
+ stream = inode_unnamed_stream_resolved(inode, &stream_idx);
+ return ref_stream(stream, stream_idx, dentry, ctx);
+}
+
+static int
+dentry_ref_streams(struct wim_dentry *dentry, struct apply_ctx *ctx)
+{
+ struct wim_inode *inode = dentry->d_inode;
+ int ret;
+
+ /* The unnamed data stream will almost always be extracted, but there
+ * exist cases in which it won't be. */
+ ret = ref_unnamed_stream(dentry, ctx);
+ if (ret)
+ return ret;
+
/* Named data streams will be extracted only if supported in the current
* extraction mode and volume, and to avoid complications, if not doing
* a linked extraction. */
if (ctx->supported_features.named_data_streams) {
- for (u16 i = 0; i < inode->i_num_ads; i++) {
- if (!ads_entry_is_named_stream(&inode->i_ads_entries[i]))
+ for (unsigned i = 0; i < inode->i_num_ads; i++) {
+ if (!inode->i_ads_entries[i].stream_name_nbytes)
continue;
ret = ref_stream(inode->i_ads_entries[i].lte, i + 1,
dentry, ctx);
}
INIT_LIST_HEAD(&ctx->stream_list);
filedes_invalidate(&ctx->tmpfile_fd);
+ ctx->apply_ops = ops;
ret = (*ops->get_supported_features)(target, &ctx->supported_features);
if (ret)
if (ret)
goto out_cleanup;
+ if (unlikely(list_empty(&dentry_list))) {
+ WARNING("There is nothing to extract!");
+ goto out_cleanup;
+ }
+
ret = dentry_list_resolve_streams(&dentry_list, ctx);
if (ret)
goto out_cleanup;
+ dentry_list_build_inode_alias_lists(&dentry_list);
+
ret = dentry_list_ref_streams(&dentry_list, ctx);
if (ret)
goto out_cleanup;
- dentry_list_build_inode_alias_lists(&dentry_list);
-
if (extract_flags & WIMLIB_EXTRACT_FLAG_FROM_PIPE) {
/* When extracting from a pipe, the number of bytes of data to
* extract can't be determined in the normal way (examining the
static int
mkdir_if_needed(const tchar *target)
{
- struct stat stbuf;
- if (tstat(target, &stbuf)) {
- if (errno == ENOENT) {
- if (tmkdir(target, 0755)) {
- ERROR_WITH_ERRNO("Failed to create directory "
- "\"%"TS"\"", target);
- return WIMLIB_ERR_MKDIR;
- }
- } else {
- ERROR_WITH_ERRNO("Failed to stat \"%"TS"\"", target);
- return WIMLIB_ERR_STAT;
- }
- } else if (!S_ISDIR(stbuf.st_mode)) {
- ERROR("\"%"TS"\" is not a directory", target);
- return WIMLIB_ERR_NOTDIR;
- }
- return 0;
+ if (!tmkdir(target, 0755))
+ return 0;
+
+ if (errno == EEXIST)
+ return 0;
+
+#ifdef __WIN32__
+ /* _wmkdir() fails with EACCES if called on a drive root directory. */
+ if (errno == EACCES)
+ return 0;
+#endif
+
+ ERROR_WITH_ERRNO("Failed to create directory \"%"TS"\"", target);
+ return WIMLIB_ERR_MKDIR;
}
/* Make sure the extraction flags make sense, and update them if needed. */
}
#endif
-#ifndef __WIN32__
if (extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT) {
+#ifdef __WIN32__
+ if (!wim->filename)
+ return WIMLIB_ERR_NO_FILENAME;
+#else
ERROR("WIMBoot extraction is only supported on Windows!");
return WIMLIB_ERR_UNSUPPORTED;
- }
#endif
+ }
+
if ((extract_flags & (WIMLIB_EXTRACT_FLAG_RPFIX |
WIMLIB_EXTRACT_FLAG_NORPFIX |
if (i == image) {
/* Metadata resource is for the image being extracted.
* Parse it and save the metadata in memory. */
- ret = read_metadata_resource(pwm, imd);
+ ret = read_metadata_resource(imd);
if (ret)
goto out_wimlib_free;
imd->modified = 1;