+ FILE_BASIC_INFORMATION info;
+ NTSTATUS status;
+ USHORT compression_state;
+
+ /* Get current attributes */
+ status = (*func_NtQueryInformationFile)(h, &ctx->iosb,
+ &info, sizeof(info),
+ FileBasicInformation);
+ if (NT_SUCCESS(status) &&
+ compressed == !!(info.FileAttributes & FILE_ATTRIBUTE_COMPRESSED))
+ {
+ /* Nothing needs to be done. */
+ return 0;
+ }
+
+ /* Set the new compression state */
+
+ if (compressed)
+ compression_state = COMPRESSION_FORMAT_DEFAULT;
+ else
+ compression_state = COMPRESSION_FORMAT_NONE;
+
+ status = (*func_NtFsControlFile)(h,
+ NULL,
+ NULL,
+ NULL,
+ &ctx->iosb,
+ FSCTL_SET_COMPRESSION,
+ &compression_state,
+ sizeof(USHORT),
+ NULL,
+ 0);
+ if (NT_SUCCESS(status))
+ return 0;
+
+ set_errno_from_nt_status(status);
+ ERROR_WITH_ERRNO("Can't %s compression attribute on \"%ls\" "
+ "(status=0x%08"PRIx32")",
+ (compressed ? "set" : "clear"),
+ current_path(ctx), status);
+ return WIMLIB_ERR_SET_ATTRIBUTES;
+}
+
+/*
+ * Clear FILE_ATTRIBUTE_ENCRYPTED if the file or directory is not supposed to be
+ * encrypted.
+ *
+ * You can provide FILE_ATTRIBUTE_ENCRYPTED to NtCreateFile() to set it on the
+ * created file. However, the file or directory will otherwise default to the
+ * encryption state of the parent directory. This function works around this
+ * limitation by using DecryptFile() to remove FILE_ATTRIBUTE_ENCRYPTED on files
+ * (and directories) that are not supposed to have it set.
+ *
+ * Regardless of whether it succeeds or fails, this function may close the
+ * handle to the file. If it does, it sets it to NULL.
+ */
+static int
+maybe_clear_encryption_attribute(HANDLE *h_ret, const struct wim_dentry *dentry,
+ struct win32_apply_ctx *ctx)
+{
+ if (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)
+ return 0;
+
+ if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)
+ return 0;
+
+ if (!ctx->common.supported_features.encrypted_files)
+ return 0;
+
+ FILE_BASIC_INFORMATION info;
+ NTSTATUS status;
+ BOOL bret;
+
+ /* Get current attributes */
+ status = (*func_NtQueryInformationFile)(*h_ret, &ctx->iosb,
+ &info, sizeof(info),
+ FileBasicInformation);
+ if (NT_SUCCESS(status) &&
+ !(info.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED))
+ {
+ /* Nothing needs to be done. */
+ return 0;
+ }
+
+ /* Set the new encryption state */
+
+ /* Due to Windows' crappy file encryption APIs, we need to close the
+ * handle to the file so we don't get ERROR_SHARING_VIOLATION. We also
+ * hack together a Win32 path, although we will use the \\?\ prefix so
+ * it will actually be a NT path in disguise... */
+ (*func_NtClose)(*h_ret);
+ *h_ret = NULL;
+
+ build_win32_extraction_path(dentry, ctx);
+
+ bret = DecryptFile(ctx->pathbuf.Buffer, 0);
+
+ /* Restore the NT namespace path */
+ build_extraction_path(dentry, ctx);
+
+ if (!bret) {
+ DWORD err = GetLastError();
+ set_errno_from_win32_error(err);
+ ERROR_WITH_ERRNO("Can't decrypt file \"%ls\" (err=%"PRIu32")",
+ current_path(ctx), (u32)err);
+ return WIMLIB_ERR_SET_ATTRIBUTES;
+ }
+ return 0;
+}
+
+/* Set the short name on the open file @h which has been created at the location
+ * indicated by @dentry.
+ *
+ * Note that this may add, change, or remove the short name.
+ *
+ * @h must be opened with DELETE access.
+ *
+ * Returns 0 or WIMLIB_ERR_SET_SHORT_NAME. The latter only happens in
+ * STRICT_SHORT_NAMES mode.
+ */
+static int
+set_short_name(HANDLE h, const struct wim_dentry *dentry,
+ struct win32_apply_ctx *ctx)
+{
+ size_t bufsize = offsetof(FILE_NAME_INFORMATION, FileName) +
+ dentry->short_name_nbytes;
+ u8 buf[bufsize] _aligned_attribute(8);
+ FILE_NAME_INFORMATION *info = (FILE_NAME_INFORMATION *)buf;
+ NTSTATUS status;
+
+ info->FileNameLength = dentry->short_name_nbytes;
+ memcpy(info->FileName, dentry->short_name, dentry->short_name_nbytes);
+
+ status = (*func_NtSetInformationFile)(h, &ctx->iosb, info, bufsize,
+ FileShortNameInformation);
+ if (NT_SUCCESS(status))
+ return 0;
+
+ /* By default, failure to set short names is not an error (since short
+ * names aren't too important anymore...). */
+ if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES))
+ return 0;
+
+ if (status == STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME) {
+ if (dentry->short_name_nbytes == 0)
+ return 0;
+ ERROR("Can't extract short name when short "
+ "names are not enabled on the volume!");
+ } else {
+ ERROR("Can't set short name on \"%ls\" (status=0x%08"PRIx32")",
+ current_path(ctx), (u32)status);
+ }
+ return WIMLIB_ERR_SET_SHORT_NAME;
+}
+
+/*
+ * A wrapper around NtCreateFile() to make it slightly more usable...
+ * This uses the path currently constructed in ctx->pathbuf.
+ *
+ * Also, we always specify FILE_OPEN_FOR_BACKUP_INTENT and
+ * FILE_OPEN_REPARSE_POINT.
+ */
+static NTSTATUS
+do_create_file(PHANDLE FileHandle,
+ ACCESS_MASK DesiredAccess,
+ PLARGE_INTEGER AllocationSize,
+ ULONG FileAttributes,
+ ULONG CreateDisposition,
+ ULONG CreateOptions,
+ struct win32_apply_ctx *ctx)
+{
+ return (*func_NtCreateFile)(FileHandle,
+ DesiredAccess,
+ &ctx->attr,
+ &ctx->iosb,
+ AllocationSize,
+ FileAttributes,
+ FILE_SHARE_VALID_FLAGS,
+ CreateDisposition,
+ CreateOptions |
+ FILE_OPEN_FOR_BACKUP_INTENT |
+ FILE_OPEN_REPARSE_POINT,
+ NULL,
+ 0);
+}
+
+/* Like do_create_file(), but builds the extraction path of the @dentry first.
+ */
+static NTSTATUS
+create_file(PHANDLE FileHandle,
+ ACCESS_MASK DesiredAccess,
+ PLARGE_INTEGER AllocationSize,
+ ULONG FileAttributes,
+ ULONG CreateDisposition,
+ ULONG CreateOptions,
+ const struct wim_dentry *dentry,
+ struct win32_apply_ctx *ctx)
+{
+ build_extraction_path(dentry, ctx);
+ return do_create_file(FileHandle,
+ DesiredAccess,
+ AllocationSize,
+ FileAttributes,
+ CreateDisposition,
+ CreateOptions,
+ ctx);
+}
+
+/* Create empty named data streams.
+ *
+ * Since these won't have 'struct wim_lookup_table_entry's, they won't show up
+ * in the call to extract_stream_list(). Hence the need for the special case.
+ */
+static int
+create_any_empty_ads(const struct wim_dentry *dentry,
+ struct win32_apply_ctx *ctx)
+{
+ const struct wim_inode *inode = dentry->d_inode;
+ LARGE_INTEGER allocation_size;
+ bool path_modified = false;
+ int ret = 0;
+
+ if (!ctx->common.supported_features.named_data_streams)
+ return 0;