#include "wimlib/win32_common.h"
#include "wimlib/apply.h"
+#include "wimlib/assert.h"
#include "wimlib/capture.h" /* for mangle_pat() and match_pattern_list() */
#include "wimlib/dentry.h"
#include "wimlib/error.h"
return ctx->print_buffer;
}
-/*
- * Ensures the target directory exists and opens a handle to it, in preparation
- * of using paths relative to it.
- */
+/* Open handle to the target directory if it is not already open. If the target
+ * directory does not exist, this creates it. */
static int
-prepare_target(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
+open_target_directory(struct win32_apply_ctx *ctx)
{
- int ret;
NTSTATUS status;
- size_t path_max;
-
- /* Open handle to the target directory (possibly creating it). */
- ret = win32_path_to_nt_path(ctx->common.target, &ctx->target_ntpath);
- if (ret)
- return ret;
+ if (ctx->h_target)
+ return 0;
ctx->attr.Length = sizeof(ctx->attr);
+ ctx->attr.RootDirectory = NULL;
ctx->attr.ObjectName = &ctx->target_ntpath;
-
status = (*func_NtCreateFile)(&ctx->h_target,
FILE_TRAVERSE,
&ctx->attr,
FILE_OPEN_FOR_BACKUP_INTENT,
NULL,
0);
-
if (!NT_SUCCESS(status)) {
set_errno_from_nt_status(status);
ERROR_WITH_ERRNO("Can't open or create directory \"%ls\" "
ctx->common.target, (u32)status);
return WIMLIB_ERR_OPENDIR;
}
+ ctx->attr.RootDirectory = ctx->h_target;
+ ctx->attr.ObjectName = &ctx->pathbuf;
+ return 0;
+}
- path_max = compute_path_max(dentry_list);
+static void
+close_target_directory(struct win32_apply_ctx *ctx)
+{
+ if (ctx->h_target) {
+ (*func_NtClose)(ctx->h_target);
+ ctx->h_target = NULL;
+ ctx->attr.RootDirectory = NULL;
+ }
+}
+
+/*
+ * Ensures the target directory exists and opens a handle to it, in preparation
+ * of using paths relative to it.
+ */
+static int
+prepare_target(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
+{
+ int ret;
+ size_t path_max;
+ ret = win32_path_to_nt_path(ctx->common.target, &ctx->target_ntpath);
+ if (ret)
+ return ret;
+
+ ret = open_target_directory(ctx);
+ if (ret)
+ return ret;
+
+ path_max = compute_path_max(dentry_list);
/* Add some extra for building Win32 paths for the file encryption APIs,
* and ensure we have at least enough to potentially use a 8.3 name for
* the last component. */
if (!ctx->pathbuf.Buffer)
return WIMLIB_ERR_NOMEM;
- ctx->attr.RootDirectory = ctx->h_target;
- ctx->attr.ObjectName = &ctx->pathbuf;
-
ctx->print_buffer = MALLOC((ctx->common.target_nchars + 1 + path_max + 1) *
sizeof(wchar_t));
if (!ctx->print_buffer)
HANDLE h;
NTSTATUS status;
int ret;
- ULONG attrib;
-
- /* Special attributes:
- *
- * Use FILE_ATTRIBUTE_ENCRYPTED if the directory needs to have it set.
- * This doesn't work for FILE_ATTRIBUTE_COMPRESSED (unfortunately).
- *
- * Don't specify FILE_ATTRIBUTE_DIRECTORY; it gets set anyway as a
- * result of the FILE_DIRECTORY_FILE option. */
- attrib = (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED);
/* DELETE is needed for set_short_name().
* GENERIC_READ and GENERIC_WRITE are needed for
* adjust_compression_attribute(). */
status = create_file(&h, GENERIC_READ | GENERIC_WRITE | DELETE, NULL,
- attrib, FILE_OPEN_IF, FILE_DIRECTORY_FILE,
+ 0, FILE_OPEN_IF, FILE_DIRECTORY_FILE,
dentry, ctx);
if (!NT_SUCCESS(status)) {
set_errno_from_nt_status(status);
build_extraction_path(dentry, ctx);
}
- /* Reparse point? */
- if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)
- && (stream_name_nchars == 0))
- {
- if (!ctx->common.supported_features.reparse_points)
- return 0;
-
- /* We can't write the reparse stream directly; we must set it
- * with FSCTL_SET_REPARSE_POINT, which requires that all the
- * data be available. So, stage the data in a buffer. */
-
- if (!prepare_data_buffer(ctx, stream->size))
- return WIMLIB_ERR_NOMEM;
- list_add_tail(&dentry->tmp_list, &ctx->reparse_dentries);
- return 0;
- }
/* Encrypted file? */
if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)
return 0;
}
+ /* Reparse point?
+ *
+ * Note: FILE_ATTRIBUTE_REPARSE_POINT is tested *after*
+ * FILE_ATTRIBUTE_ENCRYPTED since the WIM format does not store both EFS
+ * data and reparse data for the same file, and the EFS data takes
+ * precedence. */
+ if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)
+ && (stream_name_nchars == 0))
+ {
+ if (!ctx->common.supported_features.reparse_points)
+ return 0;
+
+ /* We can't write the reparse stream directly; we must set it
+ * with FSCTL_SET_REPARSE_POINT, which requires that all the
+ * data be available. So, stage the data in a buffer. */
+
+ if (!prepare_data_buffer(ctx, stream->size))
+ return WIMLIB_ERR_NOMEM;
+ list_add_tail(&dentry->tmp_list, &ctx->reparse_dentries);
+ return 0;
+ }
+
if (ctx->num_open_handles == MAX_OPEN_STREAMS) {
/* XXX: Fix this. But because of the checks in
* extract_stream_list(), this can now only happen on a
return ERROR_SUCCESS;
}
-/* Write the raw encrypted data to the already-created file corresponding to
- * @dentry.
+/*
+ * Write the raw encrypted data to the already-created file (or directory)
+ * corresponding to @dentry.
*
* The raw encrypted data is provided in ctx->data_buffer, and its size is
- * ctx->encrypted_size. */
+ * ctx->encrypted_size.
+ *
+ * This function may close the target directory, in which case the caller needs
+ * to re-open it if needed.
+ */
static int
extract_encrypted_file(const struct wim_dentry *dentry,
struct win32_apply_ctx *ctx)
{
void *rawctx;
DWORD err;
+ ULONG flags;
+ bool retried;
/* Temporarily build a Win32 path for OpenEncryptedFileRaw() */
build_win32_extraction_path(dentry, ctx);
- err = OpenEncryptedFileRaw(ctx->pathbuf.Buffer,
- CREATE_FOR_IMPORT, &rawctx);
+ flags = CREATE_FOR_IMPORT | OVERWRITE_HIDDEN;
+ if (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
+ flags |= CREATE_FOR_DIR;
+
+ retried = false;
+retry:
+ err = OpenEncryptedFileRaw(ctx->pathbuf.Buffer, flags, &rawctx);
+ if (err == ERROR_SHARING_VIOLATION && !retried) {
+ /* This can be caused by the handle we have open to the target
+ * directory. Try closing it temporarily. */
+ close_target_directory(ctx);
+ retried = true;
+ goto retry;
+ }
/* Restore the NT namespace path */
build_extraction_path(dentry, ctx);
ret = check_apply_error(dentry, ctx, ret);
if (ret)
return ret;
+ /* Re-open the target directory if needed. */
+ ret = open_target_directory(ctx);
+ if (ret)
+ return ret;
}
}
do_warnings(ctx);
out:
- if (ctx->h_target)
- (*func_NtClose)(ctx->h_target);
+ close_target_directory(ctx);
if (ctx->target_ntpath.Buffer)
HeapFree(GetProcessHeap(), 0, ctx->target_ntpath.Buffer);
FREE(ctx->pathbuf.Buffer);