+#include "wimlib/metadata.h"
+#include "wimlib/reparse.h"
+#include "wimlib/textfile.h"
+#include "wimlib/xml.h"
+#include "wimlib/wimboot.h"
+
+/* TODO: Add workaround for when a stream needs to be extracted to more places
+ * than this */
+#define MAX_OPEN_HANDLES 32768
+
+struct win32_apply_ctx {
+
+ /* Extract flags, the pointer to the WIMStruct, etc. */
+ struct apply_ctx common;
+
+ /* WIMBoot information, only filled in if WIMLIB_EXTRACT_FLAG_WIMBOOT
+ * was provided */
+ struct {
+ u64 data_source_id;
+ struct string_set *prepopulate_pats;
+ void *mem_prepopulate_pats;
+ u8 wim_lookup_table_hash[SHA1_HASH_SIZE];
+ bool wof_running;
+ } wimboot;
+
+ /* Open handle to the target directory */
+ HANDLE h_target;
+
+ /* NT namespace path to the target directory (buffer allocated) */
+ UNICODE_STRING target_ntpath;
+
+ /* Temporary buffer for building paths (buffer allocated) */
+ UNICODE_STRING pathbuf;
+
+ /* Object attributes to reuse for opening files in the target directory.
+ * (attr.ObjectName == &pathbuf) and (attr.RootDirectory == h_target).
+ */
+ OBJECT_ATTRIBUTES attr;
+
+ /* Temporary I/O status block for system calls */
+ IO_STATUS_BLOCK iosb;
+
+ /* Allocated buffer for creating "printable" paths from our
+ * target-relative NT paths */
+ wchar_t *print_buffer;
+
+ /* Allocated buffer for reading stream data when it cannot be extracted
+ * directly */
+ u8 *data_buffer;
+
+ /* Pointer to the next byte in @data_buffer to fill */
+ u8 *data_buffer_ptr;
+
+ /* Size allocated in @data_buffer */
+ size_t data_buffer_size;
+
+ /* Current offset in the raw encrypted file being written */
+ size_t encrypted_offset;
+
+ /* Current size of the raw encrypted file being written */
+ size_t encrypted_size;
+
+ /* Temporary buffer for reparse data */
+ struct reparse_buffer_disk rpbuf;
+
+ /* Temporary buffer for reparse data of "fixed" absolute symbolic links
+ * and junction */
+ struct reparse_buffer_disk rpfixbuf;
+
+ /* Array of open handles to filesystem streams currently being written
+ */
+ HANDLE open_handles[MAX_OPEN_HANDLES];
+
+ /* Number of handles in @open_handles currently open (filled in from the
+ * beginning of the array) */
+ unsigned num_open_handles;
+
+ /* List of dentries, joined by @tmp_list, that need to have reparse data
+ * extracted as soon as the whole stream has been read into
+ * @data_buffer. */
+ struct list_head reparse_dentries;
+
+ /* List of dentries, joined by @tmp_list, that need to have raw
+ * encrypted data extracted as soon as the whole stream has been read
+ * into @data_buffer. */
+ struct list_head encrypted_dentries;
+
+ /* Number of files for which we didn't have permission to set the full
+ * security descriptor. */
+ unsigned long partial_security_descriptors;
+
+ /* Number of files for which we didn't have permission to set any part
+ * of the security descriptor. */
+ unsigned long no_security_descriptors;
+};
+
+/* Get the drive letter from a Windows path, or return the null character if the
+ * path is relative. */
+static wchar_t
+get_drive_letter(const wchar_t *path)
+{
+ /* Skip \\?\ prefix */
+ if (!wcsncmp(path, L"\\\\?\\", 4))
+ path += 4;
+
+ /* Return drive letter if valid */
+ if (((path[0] >= L'a' && path[0] <= L'z') ||
+ (path[0] >= L'A' && path[0] <= L'Z')) && path[1] == L':')
+ return path[0];
+
+ return L'\0';
+}
+
+static void
+get_vol_flags(const wchar_t *target, DWORD *vol_flags_ret,
+ bool *short_names_supported_ret)
+{
+ wchar_t filesystem_name[MAX_PATH + 1];
+ wchar_t drive[4];
+ wchar_t *volume = NULL;
+
+ *vol_flags_ret = 0;
+ *short_names_supported_ret = false;
+
+ drive[0] = get_drive_letter(target);
+ if (drive[0]) {
+ drive[1] = L':';
+ drive[2] = L'\\';
+ drive[3] = L'\0';
+ volume = drive;
+ }
+
+ if (!GetVolumeInformation(volume, NULL, 0, NULL, NULL,
+ vol_flags_ret, filesystem_name,
+ ARRAY_LEN(filesystem_name)))
+ {
+ DWORD err = GetLastError();
+ set_errno_from_win32_error(err);
+ WARNING_WITH_ERRNO("Failed to get volume information for "
+ "\"%ls\" (err=%"PRIu32")",
+ target, (u32)err);
+ return;
+ }
+
+ if (wcsstr(filesystem_name, L"NTFS")) {
+ /* FILE_SUPPORTS_HARD_LINKS is only supported on Windows 7 and
+ * later. Force it on anyway if filesystem is NTFS. */
+ *vol_flags_ret |= FILE_SUPPORTS_HARD_LINKS;
+
+ /* There's no volume flag for short names, but according to the
+ * MS documentation they are only user-settable on NTFS. */
+ *short_names_supported_ret = true;
+ }
+}