+ FreeLibrary(ntdll_handle);
+ ntdll_handle = NULL;
+}
+
+/*
+ * Translates a Win32-namespace path into an NT-namespace path.
+ *
+ * On success, returns 0. The NT-namespace path will be stored in the
+ * UNICODE_STRING structure pointed to by nt_path. nt_path->Buffer will be set
+ * to a new buffer that must later be freed with HeapFree(). (Really
+ * RtlHeapFree(), but HeapFree() seems to be the same thing.)
+ *
+ * On failure, returns WIMLIB_ERR_NOMEM or WIMLIB_ERR_INVALID_PARAM.
+ */
+int
+win32_path_to_nt_path(const wchar_t *win32_path, UNICODE_STRING *nt_path)
+{
+ NTSTATUS status;
+
+ if (func_RtlDosPathNameToNtPathName_U_WithStatus) {
+ status = (*func_RtlDosPathNameToNtPathName_U_WithStatus)(win32_path,
+ nt_path,
+ NULL, NULL);
+ } else {
+ if (RtlDosPathNameToNtPathName_U(win32_path, nt_path, NULL, NULL))
+ status = STATUS_SUCCESS;
+ else
+ status = STATUS_NO_MEMORY;
+ }
+
+ if (likely(NT_SUCCESS(status)))
+ return 0;
+
+ if (status == STATUS_NO_MEMORY)
+ return WIMLIB_ERR_NOMEM;
+
+ winnt_error(status, L"\"%ls\": invalid path name", win32_path);
+ return WIMLIB_ERR_INVALID_PARAM;
+}
+
+int
+win32_get_drive_path(const wchar_t *file_path, wchar_t drive_path[7])
+{
+ tchar *file_abspath;
+
+ file_abspath = realpath(file_path, NULL);
+ if (!file_abspath)
+ return WIMLIB_ERR_NOMEM;
+
+ if (file_abspath[0] == L'\0' || file_abspath[1] != L':') {
+ ERROR("\"%ls\": Path format not recognized", file_abspath);
+ FREE(file_abspath);
+ return WIMLIB_ERR_UNSUPPORTED;
+ }
+
+ wsprintf(drive_path, L"\\\\.\\%lc:", file_abspath[0]);
+ FREE(file_abspath);
+ return 0;
+}
+
+/* Try to attach an instance of the Windows Overlay Filesystem filter driver to
+ * the specified drive (such as C:) */
+bool
+win32_try_to_attach_wof(const wchar_t *drive)
+{
+ HMODULE fltlib;
+ bool retval = false;
+
+ /* Use FilterAttach() from Fltlib.dll. */
+
+ fltlib = LoadLibrary(L"Fltlib.dll");
+
+ if (!fltlib) {
+ WARNING("Failed to load Fltlib.dll");
+ return retval;
+ }
+
+ HRESULT (WINAPI *func_FilterAttach)(LPCWSTR lpFilterName,
+ LPCWSTR lpVolumeName,
+ LPCWSTR lpInstanceName,
+ DWORD dwCreatedInstanceNameLength,
+ LPWSTR lpCreatedInstanceName);
+
+ func_FilterAttach = (void *)GetProcAddress(fltlib, "FilterAttach");
+
+ if (func_FilterAttach) {
+ HRESULT res;
+
+ res = (*func_FilterAttach)(L"wof", drive, NULL, 0, NULL);
+
+ if (res != S_OK)
+ res = (*func_FilterAttach)(L"wofadk", drive, NULL, 0, NULL);
+
+ if (res == S_OK)
+ retval = true;
+ } else {
+ WARNING("FilterAttach() does not exist in Fltlib.dll");
+ }
+
+ FreeLibrary(fltlib);
+
+ return retval;
+}
+
+
+static void
+windows_msg(u32 code, const wchar_t *format, va_list va,
+ bool is_ntstatus, bool is_error)
+{
+ wchar_t _buf[STACK_MAX / 8];
+ wchar_t *buf = _buf;
+ size_t buflen = ARRAY_LEN(_buf);
+ size_t ret;
+ size_t n;
+
+retry:
+ n = vsnwprintf(buf, buflen, format, va);
+
+ if (n >= buflen)
+ goto realloc;
+
+ n += snwprintf(&buf[n], buflen - n,
+ (is_ntstatus ?
+ L" (status=%08"PRIx32"): " :
+ L" (err=%"PRIu32"): "),
+ code);
+
+ if (n >= buflen)
+ goto realloc;
+
+ ret = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS |
+ (is_ntstatus ? FORMAT_MESSAGE_FROM_HMODULE : 0),
+ (is_ntstatus ? ntdll_handle : NULL),
+ code,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ &buf[n],
+ buflen - n,
+ NULL);
+ n += ret;
+
+ if (n >= buflen || (ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER))
+ goto realloc;
+
+ if (buf[n - 1] == L'\n')
+ buf[--n] = L'\0';
+ if (buf[n - 1] == L'\r')
+ buf[--n] = L'\0';
+ if (buf[n - 1] == L'.')
+ buf[--n] = L'\0';
+
+ if (is_error)
+ ERROR("%ls", buf);
+ else
+ WARNING("%ls", buf);
+ if (buf != _buf)
+ FREE(buf);
+ return;
+
+realloc:
+ if (buf != _buf)
+ FREE(buf);
+ buflen *= 2;
+ buf = MALLOC(buflen * sizeof(buf[0]));
+ if (buf)
+ goto retry;
+ ERROR("Ran out of memory while building error message!!!");
+}
+
+void
+win32_warning(DWORD err, const wchar_t *format, ...)
+{
+ va_list va;
+
+ va_start(va, format);
+ windows_msg(err, format, va, false, false);
+ va_end(va);
+}
+
+void
+win32_error(DWORD err, const wchar_t *format, ...)
+{
+ va_list va;
+
+ va_start(va, format);
+ windows_msg(err, format, va, false, true);
+ va_end(va);
+}
+
+void
+winnt_warning(NTSTATUS status, const wchar_t *format, ...)
+{
+ va_list va;
+
+ va_start(va, format);
+ windows_msg(status, format, va, true, false);
+ va_end(va);
+}
+
+void
+winnt_error(NTSTATUS status, const wchar_t *format, ...)
+{
+ va_list va;
+
+ va_start(va, format);
+ windows_msg(status, format, va, true, true);
+ va_end(va);
+}
+
+/*
+ * Synchronously execute a filesystem control method. This is a wrapper around
+ * NtFsControlFile() that handles STATUS_PENDING. Note that SYNCHRONIZE
+ * permission is, in general, required on the handle.
+ */
+NTSTATUS
+winnt_fsctl(HANDLE h, u32 code, const void *in, u32 in_size,
+ void *out, u32 out_size_avail, u32 *actual_out_size_ret)
+{
+ IO_STATUS_BLOCK iosb;
+ NTSTATUS status;
+
+ status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, code,
+ (void *)in, in_size, out, out_size_avail);
+ if (status == STATUS_PENDING) {
+ /* Beware: this case is often encountered with remote
+ * filesystems, but rarely with local filesystems. */
+
+ status = NtWaitForSingleObject(h, FALSE, NULL);
+ if (NT_SUCCESS(status)) {
+ status = iosb.Status;
+ } else {
+ /* We shouldn't be issuing ioctls on a handle to which
+ * we don't have SYNCHRONIZE access. Otherwise we have
+ * no way to wait for them to complete. */
+ wimlib_assert(status != STATUS_ACCESS_DENIED);
+ }
+ }
+
+ if (NT_SUCCESS(status) && actual_out_size_ret != NULL)
+ *actual_out_size_ret = (u32)iosb.Information;
+
+ return status;