+ /* 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;
+
+ ctx->attr.Length = sizeof(ctx->attr);
+ ctx->attr.ObjectName = &ctx->target_ntpath;
+
+ status = (*func_NtCreateFile)(&ctx->h_target,
+ FILE_TRAVERSE,
+ &ctx->attr,
+ &ctx->iosb,
+ NULL,
+ 0,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_OPEN_IF,
+ FILE_DIRECTORY_FILE |
+ FILE_OPEN_REPARSE_POINT |
+ 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\" "
+ "(status=0x%08"PRIx32")",
+ ctx->common.target, (u32)status);
+ return WIMLIB_ERR_OPENDIR;
+ }
+
+ path_max = compute_path_max(dentry_list);
+
+ /* Add some extra for building Win32 paths for the file encryption APIs
+ * ... */
+ path_max += 2 + (ctx->target_ntpath.Length / sizeof(wchar_t));
+
+ ctx->pathbuf.MaximumLength = path_max * sizeof(wchar_t);
+ ctx->pathbuf.Buffer = MALLOC(ctx->pathbuf.MaximumLength);
+ 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)
+ return WIMLIB_ERR_NOMEM;
+
+ return 0;
+}
+
+/* When creating an inode that will have a short (DOS) name, we create it using
+ * the long name associated with the short name. This ensures that the short
+ * name gets associated with the correct long name. */
+static struct wim_dentry *
+first_extraction_alias(const struct wim_inode *inode)
+{
+ struct list_head *next = inode->i_extraction_aliases.next;
+ struct wim_dentry *dentry;
+
+ do {
+ dentry = list_entry(next, struct wim_dentry,
+ d_extraction_alias_node);
+ if (dentry_has_short_name(dentry))
+ break;
+ next = next->next;
+ } while (next != &inode->i_extraction_aliases);
+ return dentry;
+}
+
+/*
+ * Set or clear FILE_ATTRIBUTE_COMPRESSED if the inherited value is different
+ * from the desired value.
+ *
+ * Note that you can NOT override the inherited value of
+ * FILE_ATTRIBUTE_COMPRESSED directly with NtCreateFile().
+ */
+static int
+adjust_compression_attribute(HANDLE h, const struct wim_dentry *dentry,
+ struct win32_apply_ctx *ctx)
+{
+ const bool compressed = (dentry->d_inode->i_attributes &
+ FILE_ATTRIBUTE_COMPRESSED);
+
+ if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)
+ return 0;
+
+ if (!ctx->common.supported_features.compressed_files)
+ return 0;
+
+ 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_ptr, 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_ptr, &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_ptr);
+ *h_ptr = 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;
+}
+
+/* Try to enable short name support on the target volume. If successful, return
+ * true. If unsuccessful, issue a warning and return false. */
+static bool
+try_to_enable_short_names(const wchar_t *volume)
+{
+ HANDLE h;
+ FILE_FS_PERSISTENT_VOLUME_INFORMATION info;
+ BOOL bret;
+ DWORD bytesReturned;
+
+ h = CreateFile(volume, GENERIC_WRITE,
+ FILE_SHARE_VALID_FLAGS, NULL, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ if (h == INVALID_HANDLE_VALUE)
+ goto fail;
+
+ info.VolumeFlags = 0;
+ info.FlagMask = PERSISTENT_VOLUME_STATE_SHORT_NAME_CREATION_DISABLED;
+ info.Version = 1;
+ info.Reserved = 0;
+
+ bret = DeviceIoControl(h, FSCTL_SET_PERSISTENT_VOLUME_STATE,
+ &info, sizeof(info), NULL, 0,
+ &bytesReturned, NULL);
+
+ CloseHandle(h);
+
+ if (!bret)
+ goto fail;
+ return true;
+
+fail:
+ WARNING("Failed to enable short name support on %ls "
+ "(err=%"PRIu32")", volume + 4, (u32)GetLastError());
+ return false;
+}
+
+/* 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)
+{
+
+ if (!ctx->common.supported_features.short_names)
+ return 0;
+
+ /*
+ * Note: The size of the FILE_NAME_INFORMATION buffer must be such that
+ * FileName contains at least 2 wide characters (4 bytes). Otherwise,
+ * NtSetInformationFile() will return STATUS_INFO_LENGTH_MISMATCH. This
+ * is despite the fact that FileNameLength can validly be 0 or 2 bytes,
+ * with the former case being removing the existing short name if
+ * present, rather than setting one.
+ *
+ * The null terminator is seemingly optional, but to be safe we include
+ * space for it and zero all unused space.
+ */
+
+ size_t bufsize = offsetof(FILE_NAME_INFORMATION, FileName) +
+ max(dentry->short_name_nbytes, sizeof(wchar_t)) +
+ sizeof(wchar_t);
+ u8 buf[bufsize] _aligned_attribute(8);
+ FILE_NAME_INFORMATION *info = (FILE_NAME_INFORMATION *)buf;
+ NTSTATUS status;
+
+ memset(buf, 0, bufsize);
+
+ info->FileNameLength = dentry->short_name_nbytes;
+ memcpy(info->FileName, dentry->short_name, dentry->short_name_nbytes);
+
+retry:
+ status = (*func_NtSetInformationFile)(h, &ctx->iosb, info, bufsize,
+ FileShortNameInformation);
+ if (NT_SUCCESS(status))
+ return 0;
+
+ if (status == STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME) {
+ if (dentry->short_name_nbytes == 0)
+ return 0;
+ if (!ctx->tried_to_enable_short_names) {
+ wchar_t volume[7];
+ int ret;
+
+ ctx->tried_to_enable_short_names = true;
+
+ ret = win32_get_drive_path(ctx->common.target,
+ volume);
+ if (ret)
+ return ret;
+ if (try_to_enable_short_names(volume))
+ goto retry;