Windows: Use ntdll unconditionally
authorEric Biggers <ebiggers3@gmail.com>
Tue, 13 May 2014 18:59:48 +0000 (13:59 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Tue, 13 May 2014 19:15:45 +0000 (14:15 -0500)
The non-ntdll code isn't being tested, and there are known problems with
it.  For now, just rely on ntdll unconditionally.

configure.ac
include/wimlib/win32_common.h
src/ntfs-3g_capture.c
src/win32_apply.c
src/win32_capture.c
src/win32_common.c

index 3f7f2b8..f4b143d 100644 (file)
@@ -206,25 +206,6 @@ case "$host" in
                ;;
 esac
 
-AC_ARG_WITH([ntdll],
-           AS_HELP_STRING([--without-ntdll], [Windows only:  do not try to
-                           use functions from ntdll.  By default, in some parts
-                           of its code, wimlib uses Windows NT system calls to
-                           work around flaws in the Win32 API.  This support
-                           can be omitted if needed, but some annoying bugs
-                           will surface (e.g.  permission denied errors even
-                                         when running as the Administrator).]),
-       [WITH_NTDLL=$withval],
-       [WITH_NTDLL=yes])
-
-if test "x$WINDOWS_NATIVE_BUILD" = "xyes"; then
-       if test "x$WITH_NTDLL" = "xyes"; then
-               WINDOWS_LDADD="$WINDOWS_LDADD"
-               AC_DEFINE([WITH_NTDLL], [1], [Define to 1 to try to use ntdll
-                                               functions])
-       fi
-fi
-
 AC_SUBST([VISIBILITY_CFLAGS], [$VISIBILITY_CFLAGS])
 AC_SUBST([WINDOWS_LDFLAGS], [$WINDOWS_LDFLAGS])
 AC_SUBST([WINDOWS_LDADD], [$WINDOWS_LDADD])
index fa10469..5e108ec 100644 (file)
@@ -9,10 +9,8 @@
 #include "wimlib/types.h"
 #include "wimlib/win32.h"
 
-#ifdef WITH_NTDLL
-#  include <ntstatus.h>
-#  include <winternl.h>
-#endif
+#include <ntstatus.h>
+#include <winternl.h>
 
 extern void
 set_errno_from_GetLastError(void);
@@ -20,10 +18,8 @@ set_errno_from_GetLastError(void);
 extern void
 set_errno_from_win32_error(DWORD err);
 
-#ifdef WITH_NTDLL
 extern void
 set_errno_from_nt_status(NTSTATUS status);
-#endif
 
 extern bool
 win32_path_is_root_of_drive(const wchar_t *path);
@@ -52,8 +48,6 @@ extern BOOL (WINAPI *win32func_CreateSymbolicLinkW)(const wchar_t *lpSymlinkFile
 
 /* ntdll functions  */
 
-#ifdef WITH_NTDLL
-
 extern NTSTATUS (WINAPI *func_NtQueryInformationFile)(HANDLE FileHandle,
                                                      PIO_STATUS_BLOCK IoStatusBlock,
                                                      PVOID FileInformation,
@@ -87,8 +81,6 @@ extern DWORD (WINAPI *func_RtlNtStatusToDosError)(NTSTATUS status);
 
 extern NTSTATUS (WINAPI *func_RtlCreateSystemVolumeInformationFolder)
                        (PCUNICODE_STRING VolumeRootPath);
-#endif
-
 
 
 extern bool
index 228a68d..48bbc48 100644 (file)
@@ -394,12 +394,10 @@ set_dentry_dos_name(struct wim_dentry *dentry, const struct dos_name_map *map)
        if (dentry->is_win32_name) {
                node = lookup_dos_name(map, dentry->d_inode->i_ino);
                if (node) {
-                       dentry->short_name = MALLOC(node->name_nbytes + 2);
+                       dentry->short_name = utf16le_dupz(node->dos_node,
+                                                         node->name_nbytes);
                        if (!dentry->short_name)
                                return WIMLIB_ERR_NOMEM;
-                       memcpy(dentry->short_name, node->dos_name,
-                              node->name_nbytes);
-                       dentry->short_name[node->name_nbytes / 2] = 0;
                        dentry->short_name_nbytes = node->name_nbytes;
                        DEBUG("Assigned DOS name to ino %"PRIu64,
                              dentry->d_inode->i_ino);
index 56f82b6..ac0bed0 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*
- * Copyright (C) 2013 Eric Biggers
+ * Copyright (C) 2013, 2014 Eric Biggers
  *
  * This file is part of wimlib, a library for working with WIM files.
  *
@@ -730,23 +730,6 @@ error:
        return WIMLIB_ERR_WRITE; /* XXX: need better error code */
 }
 
-static DWORD
-do_win32_set_security_descriptor(HANDLE h, const wchar_t *path,
-                                SECURITY_INFORMATION info,
-                                PSECURITY_DESCRIPTOR desc)
-{
-#ifdef WITH_NTDLL
-       if (func_NtSetSecurityObject) {
-               return (*func_RtlNtStatusToDosError)(
-                               (*func_NtSetSecurityObject)(h, info, desc));
-       }
-#endif
-       if (SetFileSecurity(path, info, desc))
-               return ERROR_SUCCESS;
-       else
-               return GetLastError();
-}
-
 /*
  * Set an arbitrary security descriptor on an arbitrary file (or directory),
  * working around bugs and design flaws in the Windows operating system.
@@ -761,7 +744,9 @@ win32_set_security_descriptor(const wchar_t *path, const u8 *desc,
                              size_t desc_size, struct apply_ctx *ctx)
 {
        SECURITY_INFORMATION info;
+       DWORD dwDesiredAccess;
        HANDLE h;
+       DWORD status;
        int ret;
 
        /* We really just want to set entire the security descriptor as-is, but
@@ -773,8 +758,6 @@ win32_set_security_descriptor(const wchar_t *path, const u8 *desc,
        info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
               DACL_SECURITY_INFORMATION  | SACL_SECURITY_INFORMATION;
 
-       h = INVALID_HANDLE_VALUE;
-
        /* Prefer NtSetSecurityObject() to SetFileSecurity().  SetFileSecurity()
         * itself necessarily uses NtSetSecurityObject() as the latter is the
         * underlying system call for setting security information, but
@@ -784,83 +767,69 @@ win32_set_security_descriptor(const wchar_t *path, const u8 *desc,
         * Administrator can have access denied.  (Of course, this not mentioned
         * in the MS "documentation".)  */
 
-#ifdef WITH_NTDLL
-       if (func_NtSetSecurityObject) {
-               DWORD dwDesiredAccess;
-
-               /* Open a handle for NtSetSecurityObject() with as many relevant
-                * access rights as possible.
-                *
-                * We don't know which rights will be actually granted.  It
-                * could be less than what is needed to actually assign the full
-                * security descriptor, especially if the process is running as
-                * a non-Administrator.  However, by default we just do the best
-                * we can, unless WIMLIB_EXTRACT_FLAG_STRICT_ACLS has been
-                * enabled.  The MAXIMUM_ALLOWED access right is seemingly
-                * designed for this use case; however, it does not work
-                * properly in all cases: it can cause CreateFile() to fail with
-                * ERROR_ACCESS_DENIED, even though by definition
-                * MAXIMUM_ALLOWED access only requests access rights that are
-                * *not* denied.  (Needless to say, MS does not document this
-                * bug.)  */
-
-               dwDesiredAccess = WRITE_DAC |
-                                 WRITE_OWNER |
-                                 ACCESS_SYSTEM_SECURITY;
-               for (;;) {
-                       DWORD err;
-
-                       h = win32_open_existing_file(path, dwDesiredAccess);
-                       if (h != INVALID_HANDLE_VALUE)
-                               break;
-                       err = GetLastError();
-                       if (err == ERROR_ACCESS_DENIED ||
-                           err == ERROR_PRIVILEGE_NOT_HELD)
-                       {
-                               /* Don't increment partial_security_descriptors
-                                * here or check WIMLIB_EXTRACT_FLAG_STRICT_ACLS
-                                * here.  It will be done later if needed; here
-                                * we are just trying to get as many relevant
-                                * access rights as possible.  */
-                               if (dwDesiredAccess & ACCESS_SYSTEM_SECURITY) {
-                                       dwDesiredAccess &= ~ACCESS_SYSTEM_SECURITY;
-                                       continue;
-                               }
-                               if (dwDesiredAccess & WRITE_DAC) {
-                                       dwDesiredAccess &= ~WRITE_DAC;
-                                       continue;
-                               }
-                               if (dwDesiredAccess & WRITE_OWNER) {
-                                       dwDesiredAccess &= ~WRITE_OWNER;
-                                       continue;
-                               }
+       /* Open a handle for NtSetSecurityObject() with as many relevant
+        * access rights as possible.
+        *
+        * We don't know which rights will be actually granted.  It
+        * could be less than what is needed to actually assign the full
+        * security descriptor, especially if the process is running as
+        * a non-Administrator.  However, by default we just do the best
+        * we can, unless WIMLIB_EXTRACT_FLAG_STRICT_ACLS has been
+        * enabled.  The MAXIMUM_ALLOWED access right is seemingly
+        * designed for this use case; however, it does not work
+        * properly in all cases: it can cause CreateFile() to fail with
+        * ERROR_ACCESS_DENIED, even though by definition
+        * MAXIMUM_ALLOWED access only requests access rights that are
+        * *not* denied.  (Needless to say, MS does not document this
+        * bug.)  */
+
+       dwDesiredAccess = WRITE_DAC | WRITE_OWNER | ACCESS_SYSTEM_SECURITY;
+       while ((h = win32_open_existing_file(path,
+                                            dwDesiredAccess)) == INVALID_HANDLE_VALUE)
+       {
+               DWORD err;
+
+               err = GetLastError();
+               if (err == ERROR_ACCESS_DENIED ||
+                   err == ERROR_PRIVILEGE_NOT_HELD)
+               {
+                       /* Don't increment partial_security_descriptors
+                        * here or check WIMLIB_EXTRACT_FLAG_STRICT_ACLS
+                        * here.  It will be done later if needed; here
+                        * we are just trying to get as many relevant
+                        * access rights as possible.  */
+                       if (dwDesiredAccess & ACCESS_SYSTEM_SECURITY) {
+                               dwDesiredAccess &= ~ACCESS_SYSTEM_SECURITY;
+                               continue;
+                       }
+                       if (dwDesiredAccess & WRITE_DAC) {
+                               dwDesiredAccess &= ~WRITE_DAC;
+                               continue;
+                       }
+                       if (dwDesiredAccess & WRITE_OWNER) {
+                               dwDesiredAccess &= ~WRITE_OWNER;
+                               continue;
                        }
-                       /* Other error, or couldn't open the file even with no
-                        * access rights specified.  Something else must be
-                        * wrong.  */
-                       set_errno_from_win32_error(err);
-                       return WIMLIB_ERR_SET_SECURITY;
                }
+               /* Other error, or couldn't open the file even with no
+                * access rights specified.  Something else must be
+                * wrong.  */
+               set_errno_from_win32_error(err);
+               return WIMLIB_ERR_SET_SECURITY;
        }
-#endif
 
        /* Try setting the security descriptor.  */
-       for (;;) {
-               DWORD err;
-
-               err = do_win32_set_security_descriptor(h, path, info,
-                                                      (PSECURITY_DESCRIPTOR)desc);
-               if (err == ERROR_SUCCESS) {
-                       ret = 0;
-                       break;
-               }
-
+       ret = 0;
+       while (!(NT_SUCCESS(status = (*func_NtSetSecurityObject)(h,
+                                                                info,
+                                                                (PSECURITY_DESCRIPTOR)desc))))
+       {
                /* Failed to set the requested parts of the security descriptor.
                 * If the error was permissions-related, try to set fewer parts
                 * of the security descriptor, unless
                 * WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled.  */
-               if ((err == ERROR_PRIVILEGE_NOT_HELD ||
-                    err == ERROR_ACCESS_DENIED) &&
+               if ((status == STATUS_PRIVILEGE_NOT_HELD ||
+                    status == STATUS_ACCESS_DENIED) &&
                    !(ctx->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS))
                {
                        if (info & SACL_SECURITY_INFORMATION) {
@@ -884,16 +853,13 @@ win32_set_security_descriptor(const wchar_t *path, const u8 *desc,
                 * security descriptor could not be set.  */
                if (!(info & SACL_SECURITY_INFORMATION))
                        ctx->partial_security_descriptors--;
-               set_errno_from_win32_error(err);
+               set_errno_from_nt_status(status);
                ret = WIMLIB_ERR_SET_SECURITY;
                break;
        }
 
        /* Close handle opened for NtSetSecurityObject().  */
-#ifdef WITH_NTDLL
-       if (func_NtSetSecurityObject)
-               CloseHandle(h);
-#endif
+       CloseHandle(h);
        return ret;
 }
 
index feac764..50191e6 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*
- * Copyright (C) 2013 Eric Biggers
+ * Copyright (C) 2013, 2014 Eric Biggers
  *
  * This file is part of wimlib, a library for working with WIM files.
  *
@@ -31,6 +31,7 @@
 
 #include "wimlib/capture.h"
 #include "wimlib/dentry.h"
+#include "wimlib/encoding.h"
 #include "wimlib/endianness.h"
 #include "wimlib/error.h"
 #include "wimlib/lookup_table.h"
@@ -48,13 +49,6 @@ struct win32_capture_state {
 };
 
 
-static const wchar_t *capture_access_denied_msg =
-L"         If you are not running this program as the administrator, you may\n"
- "         need to do so, so that all data and metadata can be backed up.\n"
- "         Otherwise, there may be no way to access the desired data or\n"
- "         metadata without taking ownership of the file or directory.\n"
- ;
-
 int
 read_win32_file_prefix(const struct wim_lookup_table_entry *lte,
                       u64 size,
@@ -92,7 +86,6 @@ read_win32_file_prefix(const struct wim_lookup_table_entry *lte,
                if (ret)
                        break;
        }
-out_close_handle:
        CloseHandle(hFile);
        return ret;
 }
@@ -136,7 +129,7 @@ read_win32_encrypted_file_prefix(const struct wim_lookup_table_entry *lte,
        void *file_ctx;
        int ret;
 
-       DEBUG("Reading %"PRIu64" bytes from encryted file \"%ls\"",
+       DEBUG("Reading %"PRIu64" bytes from encrypted file \"%ls\"",
              size, lte->file_on_disk);
 
        export_ctx.read_prefix_cb = cb;
@@ -187,113 +180,28 @@ FILETIME_to_u64(const FILETIME *ft)
  * short names are essentially obsolete anyway.
  */
 static int
-win32_get_short_name(HANDLE hFile, const wchar_t *path, struct wim_dentry *dentry)
+win32_get_short_name(HANDLE hFile, struct wim_dentry *dentry)
 {
 
        /* It's not any harder to just make the NtQueryInformationFile() system
         * call ourselves, and it saves a dumb call to FindFirstFile() which of
         * course has to create its own handle.  */
-#ifdef WITH_NTDLL
-       if (func_NtQueryInformationFile) {
-               NTSTATUS status;
-               IO_STATUS_BLOCK io_status;
-               u8 buf[128] _aligned_attribute(8);
-               const FILE_NAME_INFORMATION *info;
-
-               status = (*func_NtQueryInformationFile)(hFile, &io_status, buf, sizeof(buf),
-                                                       FileAlternateNameInformation);
-               info = (const FILE_NAME_INFORMATION*)buf;
-               if (status == STATUS_SUCCESS && info->FileNameLength != 0) {
-                       dentry->short_name = MALLOC(info->FileNameLength + 2);
-                       if (!dentry->short_name)
-                               return WIMLIB_ERR_NOMEM;
-                       memcpy(dentry->short_name, info->FileName,
-                              info->FileNameLength);
-                       dentry->short_name[info->FileNameLength / 2] = L'\0';
-                       dentry->short_name_nbytes = info->FileNameLength;
-               }
-               return 0;
-       }
-#endif
-
-       WIN32_FIND_DATAW dat;
-       HANDLE hFind;
-       int ret = 0;
-
-       hFind = FindFirstFile(path, &dat);
-       if (hFind != INVALID_HANDLE_VALUE) {
-               if (dat.cAlternateFileName[0] != L'\0') {
-                       DEBUG("\"%ls\": short name \"%ls\"", path, dat.cAlternateFileName);
-                       size_t short_name_nbytes = wcslen(dat.cAlternateFileName) *
-                                                  sizeof(wchar_t);
-                       size_t n = short_name_nbytes + sizeof(wchar_t);
-                       dentry->short_name = MALLOC(n);
-                       if (dentry->short_name) {
-                               memcpy(dentry->short_name, dat.cAlternateFileName, n);
-                               dentry->short_name_nbytes = short_name_nbytes;
-                       } else {
-                               ret = WIMLIB_ERR_NOMEM;
-                       }
-               }
-               FindClose(hFind);
-       }
-       return ret;
-}
-
-/*
- * win32_query_security_descriptor() - Query a file's security descriptor
- *
- * We need the file's security descriptor in SECURITY_DESCRIPTOR_RELATIVE
- * format, and we currently have a handle opened with as many relevant
- * permissions as possible.  At this point, on Windows there are a number of
- * options for reading a file's security descriptor:
- *
- * GetFileSecurity():  This takes in a path and returns the
- * SECURITY_DESCRIPTOR_RELATIVE.  Problem: this uses an internal handle, not
- * ours, and the handle created internally doesn't specify
- * FILE_FLAG_BACKUP_SEMANTICS.  Therefore there can be access denied errors on
- * some files and directories, even when running as the Administrator.
- *
- * GetSecurityInfo():  This takes in a handle and returns the security
- * descriptor split into a bunch of different parts.  This should work, but it's
- * dumb because we have to put the security descriptor back together again.
- *
- * BackupRead():  This can read the security descriptor, but this is a
- * difficult-to-use API, probably only works as the Administrator, and the
- * format of the returned data is not well documented.
- *
- * NtQuerySecurityObject():  This is exactly what we need, as it takes in a
- * handle and returns the security descriptor in SECURITY_DESCRIPTOR_RELATIVE
- * format.  Only problem is that it's a ntdll function and therefore not
- * officially part of the Win32 API.  Oh well.
- */
-static DWORD
-win32_query_security_descriptor(HANDLE hFile, const wchar_t *path,
-                               SECURITY_INFORMATION requestedInformation,
-                               SECURITY_DESCRIPTOR *buf,
-                               DWORD bufsize, DWORD *lengthNeeded)
-{
-#ifdef WITH_NTDLL
-       if (func_NtQuerySecurityObject) {
-               NTSTATUS status;
-
-               status = (*func_NtQuerySecurityObject)(hFile,
-                                                      requestedInformation, buf,
-                                                      bufsize, lengthNeeded);
-               /* Since it queries an already-open handle, NtQuerySecurityObject()
-                * apparently returns STATUS_ACCESS_DENIED rather than
-                * STATUS_PRIVILEGE_NOT_HELD.  */
-               if (status == STATUS_ACCESS_DENIED)
-                       return ERROR_PRIVILEGE_NOT_HELD;
-               else
-                       return (*func_RtlNtStatusToDosError)(status);
+       NTSTATUS status;
+       IO_STATUS_BLOCK io_status;
+       u8 buf[128] _aligned_attribute(8);
+       const FILE_NAME_INFORMATION *info;
+
+       status = (*func_NtQueryInformationFile)(hFile, &io_status, buf, sizeof(buf),
+                                               FileAlternateNameInformation);
+       info = (const FILE_NAME_INFORMATION*)buf;
+       if (NT_SUCCESS(status) && info->FileNameLength != 0) {
+               dentry->short_name = utf16le_dupz(info->FileName,
+                                                 info->FileNameLength);
+               if (!dentry->short_name)
+                       return WIMLIB_ERR_NOMEM;
+               dentry->short_name_nbytes = info->FileNameLength;
        }
-#endif
-       if (GetFileSecurity(path, requestedInformation, buf,
-                           bufsize, lengthNeeded))
-               return ERROR_SUCCESS;
-       else
-               return GetLastError();
+       return 0;
 }
 
 static int
@@ -309,7 +217,7 @@ win32_get_security_descriptor(HANDLE hFile,
        u8 *buf;
        size_t bufsize;
        DWORD lenNeeded;
-       DWORD err;
+       NTSTATUS status;
        int ret;
 
        requestedInformation = DACL_SECURITY_INFORMATION |
@@ -318,46 +226,67 @@ win32_get_security_descriptor(HANDLE hFile,
                               GROUP_SECURITY_INFORMATION;
        buf = _buf;
        bufsize = sizeof(_buf);
-       for (;;) {
-               err = win32_query_security_descriptor(hFile, path,
-                                                     requestedInformation,
-                                                     (SECURITY_DESCRIPTOR*)buf,
-                                                     bufsize, &lenNeeded);
-               switch (err) {
-               case ERROR_SUCCESS:
-                       goto have_descriptor;
-               case ERROR_INSUFFICIENT_BUFFER:
+
+       /*
+        * We need the file's security descriptor in SECURITY_DESCRIPTOR_RELATIVE
+        * format, and we currently have a handle opened with as many relevant
+        * permissions as possible.  At this point, on Windows there are a number of
+        * options for reading a file's security descriptor:
+        *
+        * GetFileSecurity():  This takes in a path and returns the
+        * SECURITY_DESCRIPTOR_RELATIVE.  Problem: this uses an internal handle, not
+        * ours, and the handle created internally doesn't specify
+        * FILE_FLAG_BACKUP_SEMANTICS.  Therefore there can be access denied errors on
+        * some files and directories, even when running as the Administrator.
+        *
+        * GetSecurityInfo():  This takes in a handle and returns the security
+        * descriptor split into a bunch of different parts.  This should work, but it's
+        * dumb because we have to put the security descriptor back together again.
+        *
+        * BackupRead():  This can read the security descriptor, but this is a
+        * difficult-to-use API, probably only works as the Administrator, and the
+        * format of the returned data is not well documented.
+        *
+        * NtQuerySecurityObject():  This is exactly what we need, as it takes in a
+        * handle and returns the security descriptor in SECURITY_DESCRIPTOR_RELATIVE
+        * format.  Only problem is that it's a ntdll function and therefore not
+        * officially part of the Win32 API.  Oh well.
+        */
+       while (!(NT_SUCCESS(status = (*func_NtQuerySecurityObject)(hFile,
+                                                                  requestedInformation,
+                                                                  (PSECURITY_DESCRIPTOR)buf,
+                                                                  bufsize,
+                                                                  &lenNeeded))))
+       {
+               switch (status) {
+               case STATUS_BUFFER_OVERFLOW:
                        wimlib_assert(buf == _buf);
                        buf = MALLOC(lenNeeded);
                        if (!buf)
                                return WIMLIB_ERR_NOMEM;
                        bufsize = lenNeeded;
                        break;
-               case ERROR_PRIVILEGE_NOT_HELD:
-                       if (add_flags & WIMLIB_ADD_FLAG_STRICT_ACLS)
-                               goto fail;
+               case STATUS_PRIVILEGE_NOT_HELD:
+               case STATUS_ACCESS_DENIED:
+                       if (add_flags & WIMLIB_ADD_FLAG_STRICT_ACLS) {
+               default:
+                               set_errno_from_nt_status(status);
+                               ERROR_WITH_ERRNO("\"%ls\": Failed to "
+                                                "read security descriptor", path);
+                               ret = WIMLIB_ERR_READ;
+                               goto out_free_buf;
+                       }
                        if (requestedInformation & SACL_SECURITY_INFORMATION) {
                                state->num_get_sacl_priv_notheld++;
                                requestedInformation &= ~SACL_SECURITY_INFORMATION;
                                break;
                        }
-                       /* Fall through */
-               case ERROR_ACCESS_DENIED:
-                       if (add_flags & WIMLIB_ADD_FLAG_STRICT_ACLS)
-                               goto fail;
                        state->num_get_sd_access_denied++;
                        ret = 0;
                        goto out_free_buf;
-               default:
-               fail:
-                       set_errno_from_win32_error(err);
-                       ERROR_WITH_ERRNO("Failed to read security descriptor of \"%ls\"", path);
-                       ret = WIMLIB_ERR_READ;
-                       goto out_free_buf;
                }
        }
 
-have_descriptor:
        inode->i_security_id = sd_set_add_sd(sd_set, buf, lenNeeded);
        if (inode->i_security_id < 0)
                ret = WIMLIB_ERR_NOMEM;
@@ -393,48 +322,23 @@ win32_recurse_directory(HANDLE hDir,
        DEBUG("Recurse to directory \"%ls\"", dir_path);
 
        /* Using NtQueryDirectoryFile() we can re-use the same open handle,
-        * which we opened with FILE_FLAG_BACKUP_SEMANTICS (probably not the
-        * case for the FindFirstFile() API; it's not documented).  */
-#ifdef WITH_NTDLL
-       if (!func_NtQueryDirectoryFile)
-               goto use_FindFirstFile;
+        * which we opened with FILE_FLAG_BACKUP_SEMANTICS.  */
 
        NTSTATUS status;
        IO_STATUS_BLOCK io_status;
        const size_t bufsize = 8192;
-       u8 *buf;
-       BOOL restartScan = TRUE;
-       const FILE_NAMES_INFORMATION *info;
+       void *buf;
 
        buf = MALLOC(bufsize);
        if (!buf)
                return WIMLIB_ERR_NOMEM;
-       for (;;) {
-               status = (*func_NtQueryDirectoryFile)(hDir, NULL, NULL, NULL,
-                                                     &io_status, buf, bufsize,
-                                                     FileNamesInformation,
-                                                     FALSE, NULL, restartScan);
-               restartScan = FALSE;
-               if (status != STATUS_SUCCESS) {
-                       if (status == STATUS_NO_MORE_FILES ||
-                           status == STATUS_NO_MORE_ENTRIES ||
-                           status == STATUS_NO_MORE_MATCHES) {
-                               ret = 0;
-                       } else if (status == STATUS_NOT_IMPLEMENTED ||
-                                  status == STATUS_NOT_SUPPORTED ||
-                                  status == STATUS_INVALID_INFO_CLASS) {
-                               FREE(buf);
-                               goto use_FindFirstFile;
-                       } else {
-                               set_errno_from_nt_status(status);
-                               ERROR_WITH_ERRNO("Failed to read directory "
-                                                "\"%ls\"", dir_path);
-                               ret = WIMLIB_ERR_READ;
-                       }
-                       goto out_free_buf;
-               }
-               wimlib_assert(io_status.Information != 0);
-               info = (const FILE_NAMES_INFORMATION*)buf;
+
+       while (NT_SUCCESS(status = (*func_NtQueryDirectoryFile)(hDir, NULL, NULL, NULL,
+                                                               &io_status, buf, bufsize,
+                                                               FileNamesInformation,
+                                                               FALSE, NULL, FALSE)))
+       {
+               const FILE_NAMES_INFORMATION *info = buf;
                for (;;) {
                        if (!(info->FileNameLength == 2 && info->FileName[0] == L'.') &&
                            !(info->FileNameLength == 4 && info->FileName[0] == L'.' &&
@@ -466,80 +370,18 @@ win32_recurse_directory(HANDLE hDir,
                        }
                        if (info->NextEntryOffset == 0)
                                break;
-                       info = (const FILE_NAMES_INFORMATION*)
-                                       ((const u8*)info + info->NextEntryOffset);
+                       info = (const FILE_NAMES_INFORMATION *)
+                                       ((const u8 *)info + info->NextEntryOffset);
                }
        }
-out_free_buf:
-       FREE(buf);
-       return ret;
-#endif
 
-use_FindFirstFile:
-       ;
-       WIN32_FIND_DATAW dat;
-       HANDLE hFind;
-       DWORD err;
-
-       /* Begin reading the directory by calling FindFirstFileW.  Unlike UNIX
-        * opendir(), FindFirstFileW has file globbing built into it.  But this
-        * isn't what we actually want, so just add a dummy glob to get all
-        * entries. */
-       dir_path[dir_path_num_chars] = OS_PREFERRED_PATH_SEPARATOR;
-       dir_path[dir_path_num_chars + 1] = L'*';
-       dir_path[dir_path_num_chars + 2] = L'\0';
-       hFind = FindFirstFile(dir_path, &dat);
-       dir_path[dir_path_num_chars] = L'\0';
-
-       if (hFind == INVALID_HANDLE_VALUE) {
-               err = GetLastError();
-               if (err == ERROR_FILE_NOT_FOUND) {
-                       return 0;
-               } else {
-                       set_errno_from_win32_error(err);
-                       ERROR_WITH_ERRNO("Failed to read directory \"%ls\"",
-                                        dir_path);
-                       return WIMLIB_ERR_READ;
-               }
-       }
-       ret = 0;
-       do {
-               /* Skip . and .. entries */
-               if (dat.cFileName[0] == L'.' &&
-                   (dat.cFileName[1] == L'\0' ||
-                    (dat.cFileName[1] == L'.' &&
-                     dat.cFileName[2] == L'\0')))
-                       continue;
-               size_t filename_len = wcslen(dat.cFileName);
-
-               dir_path[dir_path_num_chars] = OS_PREFERRED_PATH_SEPARATOR;
-               wmemcpy(dir_path + dir_path_num_chars + 1,
-                       dat.cFileName,
-                       filename_len + 1);
-
-               struct wim_dentry *child;
-               size_t path_len = dir_path_num_chars + 1 + filename_len;
-               ret = win32_build_dentry_tree_recursive(&child,
-                                                       dir_path,
-                                                       path_len,
-                                                       params,
-                                                       state,
-                                                       vol_flags);
-               dir_path[dir_path_num_chars] = L'\0';
-               if (ret)
-                       goto out_find_close;
-               if (child)
-                       dentry_add_child(root, child);
-       } while (FindNextFile(hFind, &dat));
-       err = GetLastError();
-       if (err != ERROR_NO_MORE_FILES) {
-               set_errno_from_win32_error(err);
+       if (status != STATUS_NO_MORE_FILES) {
+               set_errno_from_nt_status(status);
                ERROR_WITH_ERRNO("Failed to read directory \"%ls\"", dir_path);
-               if (ret == 0)
-                       ret = WIMLIB_ERR_READ;
+               ret = WIMLIB_ERR_READ;
        }
-out_find_close:
-       FindClose(hFind);
+out_free_buf:
+       FREE(buf);
        return ret;
 }
 
@@ -932,8 +774,8 @@ out_invalid_stream_name:
 
 /* Load information about the streams of an open file into a WIM inode.
  *
- * By default, we use the NtQueryInformationFile() system call instead of
- * FindFirstStream() and FindNextStream().  This is done for two reasons:
+ * We use the NtQueryInformationFile() system call instead of FindFirstStream()
+ * and FindNextStream().  This is done for two reasons:
  *
  * - FindFirstStream() opens its own handle to the file or directory and
  *   apparently does so without specifying FILE_FLAG_BACKUP_SEMANTICS, thereby
@@ -952,59 +794,55 @@ win32_capture_streams(HANDLE *hFile_p,
                      u64 file_size,
                      unsigned vol_flags)
 {
-       WIN32_FIND_STREAM_DATA dat;
        int ret;
-#ifdef WITH_NTDLL
        u8 _buf[8192] _aligned_attribute(8);
        u8 *buf;
        size_t bufsize;
        IO_STATUS_BLOCK io_status;
        NTSTATUS status;
        const FILE_STREAM_INFORMATION *info;
-#endif
-       HANDLE hFind;
-       DWORD err;
 
        DEBUG("Capturing streams from \"%ls\"", path);
 
-       if (!(vol_flags & FILE_NAMED_STREAMS))
-               goto unnamed_only;
-
-#ifdef WITH_NTDLL
-       if (!func_NtQueryInformationFile)
-               goto use_FindFirstStream;
-
        buf = _buf;
        bufsize = sizeof(_buf);
 
-       /* Get a buffer containing the stream information.  */
-       for (;;) {
-               status = (*func_NtQueryInformationFile)(*hFile_p, &io_status,
-                                                       buf, bufsize,
-                                                       FileStreamInformation);
-               if (status == STATUS_SUCCESS) {
-                       break;
-               } else if (status == STATUS_BUFFER_OVERFLOW) {
-                       u8 *newbuf;
+       if (!(vol_flags & FILE_NAMED_STREAMS))
+               goto unnamed_only;
 
-                       bufsize *= 2;
-                       if (buf == _buf)
-                               newbuf = MALLOC(bufsize);
-                       else
-                               newbuf = REALLOC(buf, bufsize);
+       /* Get a buffer containing the stream information.  */
+       while (!NT_SUCCESS(status = (*func_NtQueryInformationFile)(*hFile_p,
+                                                                  &io_status,
+                                                                  buf,
+                                                                  bufsize,
+                                                                  FileStreamInformation)))
+       {
 
-                       if (!newbuf) {
-                               ret = WIMLIB_ERR_NOMEM;
-                               goto out_free_buf;
+               switch (status) {
+               case STATUS_BUFFER_OVERFLOW:
+                       {
+                               u8 *newbuf;
+
+                               bufsize *= 2;
+                               if (buf == _buf)
+                                       newbuf = MALLOC(bufsize);
+                               else
+                                       newbuf = REALLOC(buf, bufsize);
+                               if (!newbuf) {
+                                       ret = WIMLIB_ERR_NOMEM;
+                                       goto out_free_buf;
+                               }
+                               buf = newbuf;
                        }
-                       buf = newbuf;
-               } else if (status == STATUS_NOT_IMPLEMENTED ||
-                          status == STATUS_NOT_SUPPORTED ||
-                          status == STATUS_INVALID_INFO_CLASS) {
-                       goto use_FindFirstStream;
-               } else {
+                       break;
+               case STATUS_NOT_IMPLEMENTED:
+               case STATUS_NOT_SUPPORTED:
+               case STATUS_INVALID_INFO_CLASS:
+                       goto unnamed_only;
+               default:
                        set_errno_from_nt_status(status);
-                       ERROR_WITH_ERRNO("Failed to read streams of %ls", path);
+                       ERROR_WITH_ERRNO("\"%ls\": Failed to query "
+                                        "stream information", path);
                        ret = WIMLIB_ERR_READ;
                        goto out_free_buf;
                }
@@ -1025,8 +863,10 @@ win32_capture_streams(HANDLE *hFile_p,
        }
 
        /* Parse one or more stream information structures.  */
-       info = (const FILE_STREAM_INFORMATION*)buf;
+       info = (const FILE_STREAM_INFORMATION *)buf;
        for (;;) {
+               WIN32_FIND_STREAM_DATA dat;
+
                if (info->StreamNameLength <= sizeof(dat.cStreamName) - 2) {
                        dat.StreamSize = info->StreamSize;
                        memcpy(dat.cStreamName, info->StreamName, info->StreamNameLength);
@@ -1040,85 +880,39 @@ win32_capture_streams(HANDLE *hFile_p,
                }
                if (info->NextEntryOffset == 0) {
                        /* No more stream information.  */
-                       ret = 0;
                        break;
                }
                /* Advance to next stream information.  */
-               info = (const FILE_STREAM_INFORMATION*)
-                               ((const u8*)info + info->NextEntryOffset);
-       }
-out_free_buf:
-       /* Free buffer if allocated on heap.  */
-       if (buf != _buf)
-               FREE(buf);
-       return ret;
-#endif /* WITH_NTDLL */
-
-use_FindFirstStream:
-       if (win32func_FindFirstStreamW == NULL)
-               goto unnamed_only;
-       hFind = win32func_FindFirstStreamW(path, FindStreamInfoStandard, &dat, 0);
-       if (hFind == INVALID_HANDLE_VALUE) {
-               err = GetLastError();
-               if (err == ERROR_CALL_NOT_IMPLEMENTED ||
-                   err == ERROR_NOT_SUPPORTED ||
-                   err == ERROR_INVALID_FUNCTION ||
-                   err == ERROR_INVALID_PARAMETER)
-                       goto unnamed_only;
-
-               /* Seems legal for this to return ERROR_HANDLE_EOF on reparse
-                * points and directories */
-               if ((inode->i_attributes &
-                   (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))
-                   && err == ERROR_HANDLE_EOF)
-               {
-                       DEBUG("ERROR_HANDLE_EOF (ok)");
-                       return 0;
-               } else {
-                       if (err == ERROR_ACCESS_DENIED) {
-                               WARNING("Failed to look up data streams "
-                                       "of \"%ls\": Access denied!\n%ls",
-                                       path, capture_access_denied_msg);
-                               return 0;
-                       } else {
-                               set_errno_from_win32_error(err);
-                               ERROR_WITH_ERRNO("Failed to look up data streams "
-                                                "of \"%ls\"", path);
-                               return WIMLIB_ERR_READ;
-                       }
-               }
-       }
-       do {
-               ret = win32_capture_stream(path,
-                                          path_num_chars,
-                                          inode, unhashed_streams,
-                                          &dat);
-               if (ret)
-                       goto out_find_close;
-       } while (win32func_FindNextStreamW(hFind, &dat));
-       err = GetLastError();
-       if (err != ERROR_HANDLE_EOF) {
-               set_errno_from_win32_error(err);
-               ERROR_WITH_ERRNO("Error reading data streams from "
-                                "\"%ls\"", path);
-               ret = WIMLIB_ERR_READ;
+               info = (const FILE_STREAM_INFORMATION *)
+                               ((const u8 *)info + info->NextEntryOffset);
        }
-out_find_close:
-       FindClose(hFind);
-       return ret;
+       ret = 0;
+       goto out_free_buf;
 
 unnamed_only:
-       /* FindFirstStream() API is not available, or the volume does not
-        * support named streams.  Only capture the unnamed data stream. */
+       /* The volume does not support named streams.  Only capture the unnamed
+        * data stream. */
        DEBUG("Only capturing unnamed data stream");
        if (inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
                                   FILE_ATTRIBUTE_REPARSE_POINT))
-               return 0;
+       {
+               ret = 0;
+               goto out_free_buf;
+       }
 
-       wcscpy(dat.cStreamName, L"::$DATA");
-       dat.StreamSize.QuadPart = file_size;
-       return win32_capture_stream(path, path_num_chars,
-                                   inode, unhashed_streams, &dat);
+       {
+               WIN32_FIND_STREAM_DATA dat;
+
+               wcscpy(dat.cStreamName, L"::$DATA");
+               dat.StreamSize.QuadPart = file_size;
+               ret = win32_capture_stream(path, path_num_chars,
+                                          inode, unhashed_streams, &dat);
+       }
+out_free_buf:
+       /* Free buffer if allocated on heap.  */
+       if (buf != _buf)
+               FREE(buf);
+       return ret;
 }
 
 static int
@@ -1227,7 +1021,7 @@ again:
        if (ret)
                goto out;
 
-       ret = win32_get_short_name(hFile, path, root);
+       ret = win32_get_short_name(hFile, root);
        if (ret)
                goto out;
 
@@ -1361,16 +1155,6 @@ win32_build_dentry_tree(struct wim_dentry **root_ret,
        unsigned vol_flags;
        DWORD dret;
 
-       if (!win32func_FindFirstStreamW
-#ifdef WITH_NTDLL
-           && !func_NtQueryInformationFile
-#endif
-          )
-       {
-               WARNING("Running on Windows XP or earlier; "
-                       "alternate data streams will not be captured.");
-       }
-
        path_nchars = wcslen(root_disk_path);
        if (path_nchars > WINDOWS_NT_MAX_PATH)
                return WIMLIB_ERR_INVALID_PARAM;
@@ -1427,7 +1211,6 @@ win32_build_dentry_tree(struct wim_dentry **root_ret,
        ret = win32_build_dentry_tree_recursive(root_ret, path,
                                                path_nchars, params,
                                                &state, vol_flags);
-out_free_path:
        FREE(path);
        if (ret == 0)
                win32_do_capture_warnings(root_disk_path, &state, params->add_flags);
index 67d208d..d6f6888 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*
- * Copyright (C) 2013 Eric Biggers
+ * Copyright (C) 2013, 2014 Eric Biggers
  *
  * This file is part of wimlib, a library for working with WIM files.
  *
@@ -29,9 +29,7 @@
 
 #include <errno.h>
 
-#ifdef WITH_NTDLL
-#  include <winternl.h>
-#endif
+#include <winternl.h>
 
 #include "wimlib/win32_common.h"
 #include "wimlib/assert.h"
@@ -320,13 +318,11 @@ set_errno_from_GetLastError(void)
        set_errno_from_win32_error(GetLastError());
 }
 
-#ifdef WITH_NTDLL
 void
 set_errno_from_nt_status(NTSTATUS status)
 {
        set_errno_from_win32_error((*func_RtlNtStatusToDosError)(status));
 }
-#endif
 
 /* Given a Windows-style path, return the number of characters of the prefix
  * that specify the path to the root directory of a drive, or return 0 if the
@@ -512,26 +508,14 @@ win32_open_existing_file(const wchar_t *path, DWORD dwDesiredAccess)
                          NULL /* hTemplateFile */);
 }
 
-/* Pointers to functions that are not available on all targetted versions of
- * Windows (XP and later).  NOTE: The WINAPI annotations seem to be important; I
- * assume it specifies a certain calling convention. */
-
-/* Vista and later */
-HANDLE (WINAPI *win32func_FindFirstStreamW)(LPCWSTR lpFileName,
-                                           STREAM_INFO_LEVELS InfoLevel,
-                                           LPVOID lpFindStreamData,
-                                           DWORD dwFlags) = NULL;
-
-/* Vista and later */
-BOOL (WINAPI *win32func_FindNextStreamW)(HANDLE hFindStream,
-                                        LPVOID lpFindStreamData) = NULL;
+/* Pointers to dynamically loaded functions  */
 
-/* Vista and later */
+/* kernel32.dll:  Vista and later */
 BOOL (WINAPI *win32func_CreateSymbolicLinkW)(const wchar_t *lpSymlinkFileName,
                                             const wchar_t *lpTargetFileName,
-                                            DWORD dwFlags) = NULL;
+                                            DWORD dwFlags);
 
-#ifdef WITH_NTDLL
+/* ntdll.dll  */
 
 DWORD (WINAPI *func_RtlNtStatusToDosError)(NTSTATUS status);
 
@@ -566,17 +550,13 @@ NTSTATUS (WINAPI *func_NtSetSecurityObject)(HANDLE Handle,
 NTSTATUS (WINAPI *func_RtlCreateSystemVolumeInformationFolder)
                (PCUNICODE_STRING VolumeRootPath);
 
-#endif /* WITH_NTDLL */
-
 static OSVERSIONINFO windows_version_info = {
        .dwOSVersionInfoSize = sizeof(OSVERSIONINFO),
 };
 
 static HMODULE hKernel32 = NULL;
 
-#ifdef WITH_NTDLL
 static HMODULE hNtdll = NULL;
-#endif
 
 static bool acquired_privileges = false;
 
@@ -588,6 +568,20 @@ windows_version_is_at_least(unsigned major, unsigned minor)
                 windows_version_info.dwMinorVersion >= minor);
 }
 
+#define NTDLL_SYM(name) { (void **)&func_##name, #name }
+static const struct ntdll_sym {
+       void **func_ptr;
+       const char *name;
+} ntdll_syms[] = {
+       NTDLL_SYM(RtlNtStatusToDosError),
+       NTDLL_SYM(NtQueryInformationFile),
+       NTDLL_SYM(NtQuerySecurityObject),
+       NTDLL_SYM(NtQueryDirectoryFile),
+       NTDLL_SYM(NtSetSecurityObject),
+       {NULL, NULL},
+};
+#undef NTDLL_SYM
+
 /* One-time initialization for Windows capture/apply code.  */
 int
 win32_global_init(int init_flags)
@@ -611,55 +605,29 @@ win32_global_init(int init_flags)
                hKernel32 = LoadLibrary(L"Kernel32.dll");
 
        if (hKernel32) {
-               win32func_FindFirstStreamW = (void*)GetProcAddress(hKernel32,
-                                                                  "FindFirstStreamW");
-               if (win32func_FindFirstStreamW) {
-                       win32func_FindNextStreamW = (void*)GetProcAddress(hKernel32,
-                                                                         "FindNextStreamW");
-                       if (!win32func_FindNextStreamW)
-                               win32func_FindFirstStreamW = NULL;
-               }
-               win32func_CreateSymbolicLinkW = (void*)GetProcAddress(hKernel32,
-                                                                     "CreateSymbolicLinkW");
+               win32func_CreateSymbolicLinkW =
+                       (void *)GetProcAddress(hKernel32, "CreateSymbolicLinkW");
        }
 
-#ifdef WITH_NTDLL
        if (hNtdll == NULL)
                hNtdll = LoadLibrary(L"ntdll.dll");
 
-       if (hNtdll) {
-               func_RtlNtStatusToDosError  =
-                       (void*)GetProcAddress(hNtdll, "RtlNtStatusToDosError");
-               if (func_RtlNtStatusToDosError) {
-
-                       func_NtQuerySecurityObject  =
-                               (void*)GetProcAddress(hNtdll, "NtQuerySecurityObject");
-
-                       func_NtQueryDirectoryFile   =
-                               (void*)GetProcAddress(hNtdll, "NtQueryDirectoryFile");
-
-                       func_NtQueryInformationFile =
-                               (void*)GetProcAddress(hNtdll, "NtQueryInformationFile");
+       if (!hNtdll) {
+               ERROR("ntdll.dll could not be loaded!");
+               return WIMLIB_ERR_UNSUPPORTED;
+       }
 
-                       func_NtSetSecurityObject    =
-                               (void*)GetProcAddress(hNtdll, "NtSetSecurityObject");
-                       func_RtlCreateSystemVolumeInformationFolder =
-                               (void*)GetProcAddress(hNtdll, "RtlCreateSystemVolumeInformationFolder");
+       for (const struct ntdll_sym *sym = ntdll_syms; sym->name; sym++) {
+               void *addr = (void *)GetProcAddress(hNtdll, sym->name);
+               if (!addr) {
+                       ERROR("Can't find %s() in ntdll.dll", sym->name);
+                       return WIMLIB_ERR_UNSUPPORTED;
                }
+               *(sym->func_ptr) = addr;
        }
 
-       DEBUG("FindFirstStreamW       @ %p", win32func_FindFirstStreamW);
-       DEBUG("FindNextStreamW        @ %p", win32func_FindNextStreamW);
-       DEBUG("CreateSymbolicLinkW    @ %p", win32func_CreateSymbolicLinkW);
-       DEBUG("RtlNtStatusToDosError  @ %p", func_RtlNtStatusToDosError);
-       DEBUG("NtQuerySecurityObject  @ %p", func_NtQuerySecurityObject);
-       DEBUG("NtQueryDirectoryFile   @ %p", func_NtQueryDirectoryFile);
-       DEBUG("NtQueryInformationFile @ %p", func_NtQueryInformationFile);
-       DEBUG("NtSetSecurityObject    @ %p", func_NtSetSecurityObject);
-       DEBUG("RtlCreateSystemVolumeInformationFolder    @ %p",
-             func_RtlCreateSystemVolumeInformationFolder);
-#endif
-
+       func_RtlCreateSystemVolumeInformationFolder =
+               (void *)GetProcAddress(hNtdll, "RtlCreateSystemVolumeInformationFolder");
        return 0;
 
 insufficient_privileges:
@@ -674,14 +642,16 @@ win32_global_cleanup(void)
                win32_release_capture_and_apply_privileges();
        if (hKernel32 != NULL) {
                FreeLibrary(hKernel32);
+               win32func_CreateSymbolicLinkW = NULL;
                hKernel32 = NULL;
        }
-#ifdef WITH_NTDLL
        if (hNtdll != NULL) {
                FreeLibrary(hNtdll);
+               for (const struct ntdll_sym *sym = ntdll_syms; sym->name; sym++)
+                       *(sym->func_ptr) = NULL;
+               func_RtlCreateSystemVolumeInformationFolder = NULL;
                hNtdll = NULL;
        }
-#endif
 }
 
 #endif /* __WIN32__ */