+/* Set the reparse point @rpbuf of length @rpbuflen on the extracted file
+ * corresponding to the WIM dentry @dentry. */
+static int
+do_set_reparse_point(const struct wim_dentry *dentry,
+ const struct reparse_buffer_disk *rpbuf, u16 rpbuflen,
+ struct win32_apply_ctx *ctx)
+{
+ NTSTATUS status;
+ HANDLE h;
+
+ status = create_file(&h, GENERIC_WRITE, NULL,
+ 0, FILE_OPEN, 0, dentry, ctx);
+ if (!NT_SUCCESS(status))
+ goto fail;
+
+ status = winnt_fsctl(h, FSCTL_SET_REPARSE_POINT,
+ rpbuf, rpbuflen, NULL, 0, NULL);
+ NtClose(h);
+
+ if (NT_SUCCESS(status))
+ return 0;
+
+ /* On Windows, by default only the Administrator can create symbolic
+ * links for some reason. By default we just issue a warning if this
+ * appears to be the problem. Use WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS
+ * to get a hard error. */
+ if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS)
+ && (status == STATUS_PRIVILEGE_NOT_HELD ||
+ status == STATUS_ACCESS_DENIED)
+ && (dentry->d_inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
+ dentry->d_inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT))
+ {
+ WARNING("Can't create symbolic link \"%ls\"! \n"
+ " (Need Administrator rights, or at least "
+ "the\n"
+ " SeCreateSymbolicLink privilege.)",
+ current_path(ctx));
+ return 0;
+ }
+
+fail:
+ winnt_error(status, L"Can't set reparse data on \"%ls\"",
+ current_path(ctx));
+ return WIMLIB_ERR_SET_REPARSE_DATA;
+}
+
+/*
+ * Create empty named data streams and potentially a reparse point for the
+ * specified file, if any.
+ *
+ * Since these won't have blob descriptors, they won't show up in the call to
+ * extract_blob_list(). Hence the need for the special case.
+ */
+static int
+create_empty_streams(const struct wim_dentry *dentry,
+ struct win32_apply_ctx *ctx)
+{
+ const struct wim_inode *inode = dentry->d_inode;
+ int ret;
+
+ for (unsigned i = 0; i < inode->i_num_streams; i++) {
+ const struct wim_inode_stream *strm = &inode->i_streams[i];
+
+ if (stream_blob_resolved(strm) != NULL)
+ continue;
+
+ if (strm->stream_type == STREAM_TYPE_REPARSE_POINT &&
+ ctx->common.supported_features.reparse_points)
+ {
+ u8 buf[REPARSE_DATA_OFFSET] _aligned_attribute(8);
+ struct reparse_buffer_disk *rpbuf =
+ (struct reparse_buffer_disk *)buf;
+ complete_reparse_point(rpbuf, inode, 0);
+ ret = do_set_reparse_point(dentry, rpbuf,
+ REPARSE_DATA_OFFSET, ctx);
+ if (ret)
+ return ret;
+ } else if (stream_is_named_data_stream(strm) &&
+ ctx->common.supported_features.named_data_streams)
+ {
+ HANDLE h;
+
+ build_extraction_path_with_ads(dentry, ctx,
+ strm->stream_name,
+ utf16le_len_chars(strm->stream_name));
+ /*
+ * Note: do not request any permissions on the handle.
+ * Otherwise, we may encounter a Windows bug where the
+ * parent directory DACL denies read access to the new
+ * named data stream, even when using backup semantics!
+ */
+ ret = supersede_file_or_stream(ctx, 0, &h);
+
+ build_extraction_path(dentry, ctx);
+
+ if (ret)
+ return ret;
+ NtClose(h);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Creates the directory named by @dentry, or uses an existing directory at that
+ * location. If necessary, sets the short name and/or fixes compression and
+ * encryption attributes.
+ *
+ * Returns 0, WIMLIB_ERR_MKDIR, or WIMLIB_ERR_SET_SHORT_NAME.
+ */
+static int
+create_directory(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx)
+{
+ DWORD perms;
+ NTSTATUS status;
+ HANDLE h;
+ int ret;
+
+ /* DELETE is needed for set_short_name(); GENERIC_READ and GENERIC_WRITE
+ * are needed for adjust_compression_attribute(). */
+ perms = GENERIC_READ | GENERIC_WRITE;
+ if (!dentry_is_root(dentry))
+ perms |= DELETE;
+
+ /* FILE_ATTRIBUTE_SYSTEM is needed to ensure that
+ * FILE_ATTRIBUTE_ENCRYPTED doesn't get set before we want it to be. */
+ status = create_file(&h, perms, NULL, FILE_ATTRIBUTE_SYSTEM,
+ FILE_OPEN_IF, FILE_DIRECTORY_FILE, dentry, ctx);
+ if (unlikely(!NT_SUCCESS(status))) {
+ const wchar_t *path = current_path(ctx);
+ winnt_error(status, L"Can't create directory \"%ls\"", path);
+
+ /* Check for known issue with WindowsApps directory. */
+ if (status == STATUS_ACCESS_DENIED &&
+ (wcsstr(path, L"\\WindowsApps\\") ||
+ wcsstr(path, L"\\InfusedApps\\"))) {
+ ERROR(
+"You seem to be trying to extract files to the WindowsApps directory.\n"
+" Windows 8.1 and later use new file permissions in this directory that\n"
+" cannot be overridden, even by backup/restore programs. To extract your\n"
+" files anyway, you need to choose a different target directory, delete\n"
+" the WindowsApps directory entirely, reformat the volume, do the\n"
+" extraction from a non-broken operating system such as Windows 7 or\n"
+" Linux, or wait for Microsoft to fix the design flaw in their operating\n"
+" system. This is *not* a bug in wimlib. See this thread for more\n"
+" information: https://wimlib.net/forums/viewtopic.php?f=1&t=261");
+ }
+ return WIMLIB_ERR_MKDIR;
+ }
+
+ if (ctx->iosb.Information == FILE_OPENED) {
+ /* If we opened an existing directory, try to clear its file
+ * attributes. As far as I know, this only actually makes a
+ * difference in the case where a FILE_ATTRIBUTE_READONLY
+ * directory has a named data stream which needs to be
+ * extracted. You cannot create a named data stream of such a
+ * directory, even though this contradicts Microsoft's
+ * documentation for FILE_ATTRIBUTE_READONLY which states it is
+ * not honored for directories! */
+ if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)) {
+ FILE_BASIC_INFORMATION basic_info =
+ { .FileAttributes = FILE_ATTRIBUTE_NORMAL };
+ NtSetInformationFile(h, &ctx->iosb, &basic_info,
+ sizeof(basic_info),
+ FileBasicInformation);
+ }
+ }
+
+ if (!dentry_is_root(dentry)) {
+ ret = set_short_name(h, dentry, ctx);
+ if (ret)
+ goto out;
+ }
+
+ ret = adjust_compression_attribute(h, dentry, ctx);
+out:
+ NtClose(h);
+ return ret;
+}
+
+/*
+ * Create all the directories being extracted, other than the target directory
+ * itself.
+ *
+ * Note: we don't honor directory hard links. However, we don't allow them to
+ * exist in WIM images anyway (see inode_fixup.c).
+ */
+static int
+create_directories(struct list_head *dentry_list,
+ struct win32_apply_ctx *ctx)
+{
+ const struct wim_dentry *dentry;
+ int ret;
+
+ list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
+
+ if (!(dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY))
+ continue;
+
+ /* Note: Here we include files with
+ * FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_REPARSE_POINT, but we
+ * wait until later to actually set the reparse data. */
+
+ ret = create_directory(dentry, ctx);
+
+ if (!ret)
+ ret = create_empty_streams(dentry, ctx);
+
+ ret = check_apply_error(dentry, ctx, ret);
+ if (ret)
+ return ret;
+
+ ret = report_file_created(&ctx->common);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * Creates the nondirectory file named by @dentry.
+ *
+ * On success, returns an open handle to the file in @h_ret, with GENERIC_READ,
+ * GENERIC_WRITE, and DELETE access. Also, the path to the file will be saved
+ * in ctx->pathbuf. On failure, returns an error code.
+ */
+static int
+create_nondirectory_inode(HANDLE *h_ret, const struct wim_dentry *dentry,
+ struct win32_apply_ctx *ctx)
+{
+ int ret;
+ HANDLE h;
+
+ build_extraction_path(dentry, ctx);
+
+ ret = supersede_file_or_stream(ctx,
+ GENERIC_READ | GENERIC_WRITE | DELETE,
+ &h);
+ if (ret)
+ goto out;
+
+ ret = adjust_compression_attribute(h, dentry, ctx);
+ if (ret)
+ goto out_close;
+
+ if (need_sparse_flag(dentry->d_inode, ctx)) {
+ ret = set_sparse_flag(h, ctx);
+ if (ret)
+ goto out_close;
+ }
+
+ ret = create_empty_streams(dentry, ctx);
+ if (ret)
+ goto out_close;
+
+ *h_ret = h;
+ return 0;
+
+out_close:
+ NtClose(h);
+out:
+ return ret;
+}
+
+/* Creates a hard link at the location named by @dentry to the file represented
+ * by the open handle @h. Or, if the target volume does not support hard links,
+ * create a separate file instead. */
+static int
+create_link(HANDLE h, const struct wim_dentry *dentry,
+ struct win32_apply_ctx *ctx)
+{
+ if (ctx->common.supported_features.hard_links) {
+
+ build_extraction_path(dentry, ctx);
+
+ size_t bufsize = offsetof(FILE_LINK_INFORMATION, FileName) +
+ ctx->pathbuf.Length + sizeof(wchar_t);
+ u8 buf[bufsize] _aligned_attribute(8);
+ FILE_LINK_INFORMATION *info = (FILE_LINK_INFORMATION *)buf;
+ NTSTATUS status;
+
+ info->ReplaceIfExists = TRUE;
+ info->RootDirectory = ctx->attr.RootDirectory;
+ info->FileNameLength = ctx->pathbuf.Length;
+ memcpy(info->FileName, ctx->pathbuf.Buffer, ctx->pathbuf.Length);
+ info->FileName[info->FileNameLength / 2] = L'\0';
+
+ /* Note: the null terminator isn't actually necessary,
+ * but if you don't add the extra character, you get
+ * STATUS_INFO_LENGTH_MISMATCH when FileNameLength
+ * happens to be 2 */
+
+ status = NtSetInformationFile(h, &ctx->iosb, info, bufsize,
+ FileLinkInformation);
+ if (NT_SUCCESS(status))
+ return 0;
+ winnt_error(status, L"Failed to create link \"%ls\"",
+ current_path(ctx));
+ return WIMLIB_ERR_LINK;
+ } else {
+ HANDLE h2;
+ int ret;
+
+ ret = create_nondirectory_inode(&h2, dentry, ctx);
+ if (ret)
+ return ret;
+
+ NtClose(h2);
+ return 0;
+ }
+}
+
+/* Given an inode (represented by the open handle @h) for which one link has
+ * been created (named by @first_dentry), create the other links.
+ *
+ * Or, if the target volume does not support hard links, create separate files.
+ *
+ * Note: This uses ctx->pathbuf and does not reset it.
+ */
+static int
+create_links(HANDLE h, const struct wim_dentry *first_dentry,
+ struct win32_apply_ctx *ctx)
+{
+ const struct wim_inode *inode = first_dentry->d_inode;
+ const struct wim_dentry *dentry;
+ int ret;
+
+ inode_for_each_extraction_alias(dentry, inode) {
+ if (dentry != first_dentry) {
+ ret = create_link(h, dentry, ctx);
+ if (ret)
+ return ret;
+ }
+ }
+ return 0;
+}
+
+/* Create a nondirectory file, including all links. */
+static int
+create_nondirectory(struct wim_inode *inode, struct win32_apply_ctx *ctx)
+{
+ struct wim_dentry *first_dentry;
+ HANDLE h;
+ int ret;
+
+ first_dentry = first_extraction_alias(inode);
+
+ /* Create first link. */
+ ret = create_nondirectory_inode(&h, first_dentry, ctx);
+ if (ret)
+ return ret;
+
+ /* Set short name. */
+ ret = set_short_name(h, first_dentry, ctx);
+
+ /* Create additional links, OR if hard links are not supported just
+ * create more files. */
+ if (!ret)
+ ret = create_links(h, first_dentry, ctx);
+
+ /* "WIMBoot" extraction: set external backing by the WIM file if needed. */
+ if (!ret && unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT))
+ ret = set_backed_from_wim(h, inode, ctx);
+
+ NtClose(h);
+ return ret;
+}
+
+/* Create all the nondirectory files being extracted, including all aliases
+ * (hard links). */
+static int
+create_nondirectories(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
+{
+ struct wim_dentry *dentry;
+ struct wim_inode *inode;
+ int ret;
+
+ list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
+ inode = dentry->d_inode;
+ if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+ /* Call create_nondirectory() only once per inode */
+ if (dentry == inode_first_extraction_dentry(inode)) {
+ ret = create_nondirectory(inode, ctx);
+ ret = check_apply_error(dentry, ctx, ret);
+ if (ret)
+ return ret;
+ }
+ ret = report_file_created(&ctx->common);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static void
+close_handles(struct win32_apply_ctx *ctx)
+{
+ for (unsigned i = 0; i < ctx->num_open_handles; i++)
+ NtClose(ctx->open_handles[i]);
+}
+
+/* Prepare to read the next blob, which has size @blob_size, into an in-memory
+ * buffer. */
+static bool
+prepare_data_buffer(struct win32_apply_ctx *ctx, u64 blob_size)
+{
+ if (blob_size > ctx->data_buffer_size) {
+ /* Larger buffer needed. */
+ void *new_buffer;
+ if ((size_t)blob_size != blob_size)
+ return false;
+ new_buffer = REALLOC(ctx->data_buffer, blob_size);
+ if (!new_buffer)
+ return false;
+ ctx->data_buffer = new_buffer;
+ ctx->data_buffer_size = blob_size;
+ }
+ /* On the first call this changes data_buffer_ptr from NULL, which tells
+ * extract_chunk() that the data buffer needs to be filled while reading
+ * the stream data. */
+ ctx->data_buffer_ptr = ctx->data_buffer;
+ return true;
+}
+
+static int
+begin_extract_blob_instance(const struct blob_descriptor *blob,
+ struct wim_dentry *dentry,
+ const struct wim_inode_stream *strm,
+ struct win32_apply_ctx *ctx)
+{
+ HANDLE h;
+ NTSTATUS status;
+
+ if (unlikely(strm->stream_type == STREAM_TYPE_REPARSE_POINT)) {
+ /* We can't write the reparse point 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, blob->size))
+ return WIMLIB_ERR_NOMEM;
+ list_add_tail(&dentry->d_tmp_list, &ctx->reparse_dentries);
+ return 0;
+ }
+
+ if (unlikely(strm->stream_type == STREAM_TYPE_EFSRPC_RAW_DATA)) {
+ /* We can't write encrypted files directly; we must use
+ * WriteEncryptedFileRaw(), which requires providing the data
+ * through a callback function. This can't easily be combined
+ * with our own callback-based approach.
+ *
+ * The current workaround is to simply read the blob into memory
+ * and write the encrypted file from that.
+ *
+ * TODO: This isn't sufficient for extremely large encrypted
+ * files. Perhaps we should create an extra thread to write
+ * such files... */
+ if (!prepare_data_buffer(ctx, blob->size))
+ return WIMLIB_ERR_NOMEM;
+ list_add_tail(&dentry->d_tmp_list, &ctx->encrypted_dentries);
+ return 0;
+ }
+
+ /* It's a data stream (may be unnamed or named). */
+ wimlib_assert(strm->stream_type == STREAM_TYPE_DATA);
+
+ if (ctx->num_open_handles == MAX_OPEN_FILES) {
+ /* XXX: Fix this. But because of the checks in
+ * extract_blob_list(), this can now only happen on a filesystem
+ * that does not support hard links. */
+ ERROR("Can't extract data: too many open files!");
+ return WIMLIB_ERR_UNSUPPORTED;
+ }
+
+
+ if (unlikely(stream_is_named(strm))) {
+ build_extraction_path_with_ads(dentry, ctx,
+ strm->stream_name,
+ utf16le_len_chars(strm->stream_name));
+ } else {
+ build_extraction_path(dentry, ctx);
+ }
+
+
+ /* Open a new handle */
+ status = do_create_file(&h,
+ FILE_WRITE_DATA | SYNCHRONIZE,
+ NULL, 0, FILE_OPEN_IF,
+ FILE_SEQUENTIAL_ONLY |
+ FILE_SYNCHRONOUS_IO_NONALERT,
+ ctx);
+ if (!NT_SUCCESS(status)) {
+ winnt_error(status, L"Can't open \"%ls\" for writing",
+ current_path(ctx));
+ return WIMLIB_ERR_OPEN;
+ }
+
+ ctx->is_sparse_stream[ctx->num_open_handles] = false;
+ if (need_sparse_flag(dentry->d_inode, ctx)) {
+ /* If the stream is unnamed, then the sparse flag was already
+ * set when the file was created. But if the stream is named,
+ * then we need to set the sparse flag here. */
+ if (unlikely(stream_is_named(strm))) {
+ int ret = set_sparse_flag(h, ctx);
+ if (ret) {
+ NtClose(h);
+ return ret;
+ }
+ }
+ ctx->is_sparse_stream[ctx->num_open_handles] = true;
+ ctx->any_sparse_streams = true;
+ } else {
+ /* Allocate space for the data. */
+ FILE_ALLOCATION_INFORMATION info =
+ { .AllocationSize = { .QuadPart = blob->size }};
+ NtSetInformationFile(h, &ctx->iosb, &info, sizeof(info),
+ FileAllocationInformation);
+ }
+ ctx->open_handles[ctx->num_open_handles++] = h;
+ return 0;
+}
+
+/* Given a Windows NT namespace path, such as \??\e:\Windows\System32, return a
+ * pointer to the suffix of the path that begins with the device directly, such
+ * as e:\Windows\System32. */
+static const wchar_t *
+skip_nt_toplevel_component(const wchar_t *path, size_t path_nchars)
+{
+ static const wchar_t * const dirs[] = {
+ L"\\??\\",
+ L"\\DosDevices\\",
+ L"\\Device\\",
+ };
+ const wchar_t * const end = path + path_nchars;
+
+ for (size_t i = 0; i < ARRAY_LEN(dirs); i++) {
+ size_t len = wcslen(dirs[i]);
+ if (len <= (end - path) && !wmemcmp(path, dirs[i], len)) {
+ path += len;
+ while (path != end && *path == L'\\')
+ path++;
+ return path;
+ }
+ }
+ return path;
+}
+
+/*
+ * Given a Windows NT namespace path, such as \??\e:\Windows\System32, return a
+ * pointer to the suffix of the path that is device-relative but possibly with
+ * leading slashes, such as \Windows\System32.
+ *
+ * The path has an explicit length and is not necessarily null terminated.
+ */
+static const wchar_t *
+get_device_relative_path(const wchar_t *path, size_t path_nchars)
+{
+ const wchar_t * const orig_path = path;
+ const wchar_t * const end = path + path_nchars;
+
+ path = skip_nt_toplevel_component(path, path_nchars);
+ if (path == orig_path)
+ return orig_path;
+
+ while (path != end && *path != L'\\')
+ path++;
+
+ return path;
+}
+
+/*
+ * Given a reparse point buffer for an inode for which the absolute link target
+ * was relativized when it was archived, de-relative the link target to be
+ * consistent with the actual extraction location.
+ */
+static void
+try_rpfix(struct reparse_buffer_disk *rpbuf, u16 *rpbuflen_p,
+ struct win32_apply_ctx *ctx)
+{
+ struct link_reparse_point link;
+ size_t orig_subst_name_nchars;
+ const wchar_t *relpath;
+ size_t relpath_nchars;
+ size_t target_ntpath_nchars;
+ size_t fixed_subst_name_nchars;
+ const wchar_t *fixed_print_name;
+ size_t fixed_print_name_nchars;
+
+ /* Do nothing if the reparse data is invalid. */
+ if (parse_link_reparse_point(rpbuf, *rpbuflen_p, &link))
+ return;
+
+ /* Do nothing if the reparse point is a relative symbolic link. */
+ if (link_is_relative_symlink(&link))
+ return;
+
+ /* Build the new substitute name from the NT namespace path to the
+ * target directory, then a path separator, then the "device relative"
+ * part of the old substitute name. */
+
+ orig_subst_name_nchars = link.substitute_name_nbytes / sizeof(wchar_t);
+
+ relpath = get_device_relative_path(link.substitute_name,
+ orig_subst_name_nchars);
+ relpath_nchars = orig_subst_name_nchars -
+ (relpath - link.substitute_name);
+
+ target_ntpath_nchars = ctx->target_ntpath.Length / sizeof(wchar_t);
+
+ /* If the target directory is a filesystem root, such as \??\C:\, then
+ * it already will have a trailing slash. Don't include this slash if
+ * we are already adding slashes via 'relpath'. This prevents an extra
+ * slash from being generated each time the link is extracted. And
+ * unlike on UNIX, the number of slashes in paths on Windows can be
+ * significant; Windows won't understand the link target if it contains
+ * too many slashes. */
+ if (target_ntpath_nchars > 0 && relpath_nchars > 0 &&
+ ctx->target_ntpath.Buffer[target_ntpath_nchars - 1] == L'\\')
+ target_ntpath_nchars--;
+
+ /* Also remove extra slashes from the beginning of 'relpath'. Normally
+ * this isn't needed, but this is here to make the extra slash(es) added
+ * by wimlib pre-v1.9.1 get removed automatically. */
+ while (relpath_nchars >= 2 &&
+ relpath[0] == L'\\' && relpath[1] == L'\\') {
+ relpath++;
+ relpath_nchars--;
+ }
+
+ fixed_subst_name_nchars = target_ntpath_nchars + relpath_nchars;
+
+ wchar_t fixed_subst_name[fixed_subst_name_nchars];
+
+ wmemcpy(fixed_subst_name, ctx->target_ntpath.Buffer, target_ntpath_nchars);
+ wmemcpy(&fixed_subst_name[target_ntpath_nchars], relpath, relpath_nchars);
+ /* Doesn't need to be null-terminated. */
+
+ /* Print name should be Win32, but not all NT names can even be
+ * translated to Win32 names. But we can at least delete the top-level
+ * directory, such as \??\, and this will have the expected result in
+ * the usual case. */
+ fixed_print_name = skip_nt_toplevel_component(fixed_subst_name,
+ fixed_subst_name_nchars);
+ fixed_print_name_nchars = fixed_subst_name_nchars - (fixed_print_name -
+ fixed_subst_name);
+
+ link.substitute_name = fixed_subst_name;
+ link.substitute_name_nbytes = fixed_subst_name_nchars * sizeof(wchar_t);
+ link.print_name = (wchar_t *)fixed_print_name;
+ link.print_name_nbytes = fixed_print_name_nchars * sizeof(wchar_t);
+ make_link_reparse_point(&link, rpbuf, rpbuflen_p);
+}
+
+/* Sets the reparse point on the specified file. This handles "fixing" the
+ * targets of absolute symbolic links and junctions if WIMLIB_EXTRACT_FLAG_RPFIX
+ * was specified. */
+static int
+set_reparse_point(const struct wim_dentry *dentry,
+ const struct reparse_buffer_disk *rpbuf, u16 rpbuflen,
+ struct win32_apply_ctx *ctx)
+{
+ if ((ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX)
+ && !(dentry->d_inode->i_rp_flags & WIM_RP_FLAG_NOT_FIXED))
+ {
+ memcpy(&ctx->rpfixbuf, rpbuf, rpbuflen);
+ try_rpfix(&ctx->rpfixbuf, &rpbuflen, ctx);
+ rpbuf = &ctx->rpfixbuf;
+ }
+ return do_set_reparse_point(dentry, rpbuf, rpbuflen, ctx);
+
+}
+
+/* Import the next block of raw encrypted data */
+static DWORD WINAPI
+import_encrypted_data(PBYTE pbData, PVOID pvCallbackContext, PULONG Length)
+{
+ struct win32_apply_ctx *ctx = pvCallbackContext;
+ ULONG copy_len;
+
+ copy_len = min(ctx->encrypted_size - ctx->encrypted_offset, *Length);
+ memcpy(pbData, &ctx->data_buffer[ctx->encrypted_offset], copy_len);
+ ctx->encrypted_offset += copy_len;
+ *Length = copy_len;
+ return ERROR_SUCCESS;
+}
+
+/*
+ * 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.
+ *
+ * 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);
+
+ 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);