+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));
+ ret = supersede_file_or_stream(ctx, &h);
+
+ build_extraction_path(dentry, ctx);
+
+ if (ret)
+ return ret;
+ (*func_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 (!NT_SUCCESS(status)) {
+ winnt_error(status, L"Can't create directory \"%ls\"",
+ current_path(ctx));
+ 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! */
+ FILE_BASIC_INFORMATION basic_info = { .FileAttributes = FILE_ATTRIBUTE_NORMAL };
+ (*func_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:
+ (*func_NtClose)(h);