]> wimlib.net Git - wimlib/blobdiff - src/win32_capture.c
win32_get_security_descriptor(): Use errno
[wimlib] / src / win32_capture.c
index 986a4d1b7593da2492294ce5168cb6dcedf6e708..308c0677ccb90b02c0a3b6f7eb31a51e7bc9558a 100644 (file)
 #include "wimlib/paths.h"
 #include "wimlib/reparse.h"
 
-#ifdef WITH_NTDLL
-#  include <winternl.h>
-#  include <ntstatus.h>
-
-NTSTATUS WINAPI
-NtQuerySecurityObject(HANDLE handle,
-                     SECURITY_INFORMATION SecurityInformation,
-                     PSECURITY_DESCRIPTOR SecurityDescriptor,
-                     ULONG Length,
-                     PULONG LengthNeeded);
-NTSTATUS WINAPI
-NtQueryDirectoryFile(HANDLE FileHandle,
-                    HANDLE Event,
-                    PIO_APC_ROUTINE ApcRoutine,
-                    PVOID ApcContext,
-                    PIO_STATUS_BLOCK IoStatusBlock,
-                    PVOID FileInformation,
-                    ULONG Length,
-                    FILE_INFORMATION_CLASS FileInformationClass,
-                    BOOLEAN ReturnSingleEntry,
-                    PUNICODE_STRING FileName,
-                    BOOLEAN RestartScan);
-#endif
-
 #define MAX_GET_SD_ACCESS_DENIED_WARNINGS 1
 #define MAX_GET_SACL_PRIV_NOTHELD_WARNINGS 1
 #define MAX_CAPTURE_LONG_PATH_WARNINGS 5
@@ -87,7 +63,6 @@ read_win32_file_prefix(const struct wim_lookup_table_entry *lte,
 {
        int ret = 0;
        void *out_buf;
-       DWORD err;
        u64 bytes_remaining;
 
        HANDLE hFile = win32_open_existing_file(lte->file_on_disk,
@@ -217,7 +192,7 @@ read_win32_encrypted_file_prefix(const struct wim_lookup_table_entry *lte,
        export_ctx.buf_filled = 0;
        export_ctx.bytes_remaining = size;
 
-       err = OpenEncryptedFileRawW(lte->file_on_disk, 0, &file_ctx);
+       err = OpenEncryptedFileRaw(lte->file_on_disk, 0, &file_ctx);
        if (err != ERROR_SUCCESS) {
                set_errno_from_win32_error(err);
                ERROR_WITH_ERRNO("Failed to open encrypted file \"%ls\" "
@@ -270,25 +245,28 @@ win32_get_short_name(HANDLE hFile, const wchar_t *path, struct wim_dentry *dentr
         * call ourselves, and it saves a dumb call to FindFirstFile() which of
         * course has to create its own handle.  */
 #ifdef WITH_NTDLL
-       NTSTATUS status;
-       IO_STATUS_BLOCK io_status;
-       u8 buf[128] _aligned_attribute(8);
-       const FILE_NAME_INFORMATION *info;
-
-       status = 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;
+       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;
        }
-       return 0;
-#else
+#endif
+
        WIN32_FIND_DATAW dat;
        HANDLE hFind;
        int ret = 0;
@@ -311,7 +289,6 @@ win32_get_short_name(HANDLE hFile, const wchar_t *path, struct wim_dentry *dentr
                FindClose(hFind);
        }
        return ret;
-#endif
 }
 
 /*
@@ -348,24 +325,26 @@ win32_query_security_descriptor(HANDLE hFile, const wchar_t *path,
                                DWORD bufsize, DWORD *lengthNeeded)
 {
 #ifdef WITH_NTDLL
-       NTSTATUS status;
-
-       status = 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 RtlNtStatusToDosError(status);
-#else
+       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);
+       }
+#endif
        if (GetFileSecurity(path, requestedInformation, buf,
                            bufsize, lengthNeeded))
                return ERROR_SUCCESS;
        else
                return GetLastError();
-#endif
 }
 
 static int
@@ -423,7 +402,7 @@ win32_get_security_descriptor(HANDLE hFile,
                default:
                fail:
                        set_errno_from_win32_error(err);
-                       ERROR("Failed to read security descriptor of \"%ls\"", path);
+                       ERROR_WITH_ERRNO("Failed to read security descriptor of \"%ls\"", path);
                        ret = WIMLIB_ERR_READ;
                        goto out_free_buf;
                }
@@ -468,6 +447,9 @@ win32_recurse_directory(HANDLE hDir,
         * 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;
+
        NTSTATUS status;
        IO_STATUS_BLOCK io_status;
        const size_t bufsize = 8192;
@@ -479,16 +461,21 @@ win32_recurse_directory(HANDLE hDir,
        if (!buf)
                return WIMLIB_ERR_NOMEM;
        for (;;) {
-               status = NtQueryDirectoryFile(hDir, NULL, NULL, NULL,
-                                             &io_status, buf, bufsize,
-                                             FileNamesInformation,
-                                             FALSE, NULL, restartScan);
+               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 "
@@ -537,7 +524,10 @@ win32_recurse_directory(HANDLE hDir,
 out_free_buf:
        FREE(buf);
        return ret;
-#else
+#endif
+
+use_FindFirstFile:
+       ;
        WIN32_FIND_DATAW dat;
        HANDLE hFind;
        DWORD err;
@@ -549,7 +539,7 @@ out_free_buf:
        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 = FindFirstFileW(dir_path, &dat);
+       hFind = FindFirstFile(dir_path, &dat);
        dir_path[dir_path_num_chars] = L'\0';
 
        if (hFind == INVALID_HANDLE_VALUE) {
@@ -591,7 +581,7 @@ out_free_buf:
                        goto out_find_close;
                if (child)
                        dentry_add_child(root, child);
-       } while (FindNextFileW(hFind, &dat));
+       } while (FindNextFile(hFind, &dat));
        err = GetLastError();
        if (err != ERROR_NO_MORE_FILES) {
                set_errno_from_win32_error(err);
@@ -602,7 +592,6 @@ out_free_buf:
 out_find_close:
        FindClose(hFind);
        return ret;
-#endif
 }
 
 /* Reparse point fixup status code */
@@ -809,10 +798,10 @@ win32_get_reparse_data(HANDLE hFile, const wchar_t *path,
 }
 
 static DWORD WINAPI
-win32_tally_encrypted_size_cb(unsigned char *_data, void *_ctx,
+win32_tally_encrypted_size_cb(unsigned char *_data, void *_size_ret,
                              unsigned long len)
 {
-       *(u64*)_ctx += len;
+       *(u64*)_size_ret += len;
        return ERROR_SUCCESS;
 }
 
@@ -823,14 +812,14 @@ win32_get_encrypted_file_size(const wchar_t *path, u64 *size_ret)
        void *file_ctx;
        int ret;
 
-       *size_ret = 0;
-       err = OpenEncryptedFileRawW(path, 0, &file_ctx);
+       err = OpenEncryptedFileRaw(path, 0, &file_ctx);
        if (err != ERROR_SUCCESS) {
                set_errno_from_win32_error(err);
                ERROR_WITH_ERRNO("Failed to open encrypted file \"%ls\" "
                                 "for raw read", path);
                return WIMLIB_ERR_OPEN;
        }
+       *size_ret = 0;
        err = ReadEncryptedFileRaw(win32_tally_encrypted_size_cb,
                                   size_ret, file_ctx);
        if (err != ERROR_SUCCESS) {
@@ -946,7 +935,7 @@ win32_capture_stream(const wchar_t *path,
        spath_buf_nbytes = (spath_nchars + 1) * sizeof(wchar_t);
        spath = MALLOC(spath_buf_nbytes);
 
-       swprintf(spath, L"%ls%ls%ls%ls",
+       tsprintf(spath, L"%ls%ls%ls%ls",
                 relpath_prefix, path, colonchar, stream_name);
 
        /* Make a new wim_lookup_table_entry */
@@ -1003,7 +992,7 @@ out_invalid_stream_name:
  *   already present in Windows XP.
  */
 static int
-win32_capture_streams(HANDLE hFile,
+win32_capture_streams(HANDLE *hFile_p,
                      const wchar_t *path,
                      size_t path_num_chars,
                      struct wim_inode *inode,
@@ -1020,28 +1009,27 @@ win32_capture_streams(HANDLE hFile,
        IO_STATUS_BLOCK io_status;
        NTSTATUS status;
        const FILE_STREAM_INFORMATION *info;
-#else
+#endif
        HANDLE hFind;
        DWORD err;
-#endif
 
        DEBUG("Capturing streams from \"%ls\"", path);
 
        if (!(vol_flags & FILE_NAMED_STREAMS))
                goto unnamed_only;
-#ifndef WITH_NTDLL
-       if (win32func_FindFirstStreamW == NULL)
-               goto unnamed_only;
-#endif
 
 #ifdef WITH_NTDLL
+       if (!func_NtQueryInformationFile)
+               goto use_FindFirstStream;
+
        buf = _buf;
        bufsize = sizeof(_buf);
 
        /* Get a buffer containing the stream information.  */
        for (;;) {
-               status = NtQueryInformationFile(hFile, &io_status, buf, bufsize,
-                                               FileStreamInformation);
+               status = (*func_NtQueryInformationFile)(*hFile_p, &io_status,
+                                                       buf, bufsize,
+                                                       FileStreamInformation);
                if (status == STATUS_SUCCESS) {
                        break;
                } else if (status == STATUS_BUFFER_OVERFLOW) {
@@ -1058,6 +1046,10 @@ win32_capture_streams(HANDLE hFile,
                                goto out_free_buf;
                        }
                        buf = newbuf;
+               } else if (status == STATUS_NOT_IMPLEMENTED ||
+                          status == STATUS_NOT_SUPPORTED ||
+                          status == STATUS_INVALID_INFO_CLASS) {
+                       goto use_FindFirstStream;
                } else {
                        set_errno_from_nt_status(status);
                        ERROR_WITH_ERRNO("Failed to read streams of %ls", path);
@@ -1072,6 +1064,14 @@ win32_capture_streams(HANDLE hFile,
                goto out_free_buf;
        }
 
+       if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) {
+               /* OpenEncryptedFileRaw() seems to fail with
+                * ERROR_SHARING_VIOLATION if there are any handles opened to
+                * the file.  */
+               CloseHandle(*hFile_p);
+               *hFile_p = INVALID_HANDLE_VALUE;
+       }
+
        /* Parse one or more stream information structures.  */
        info = (const FILE_STREAM_INFORMATION*)buf;
        for (;;) {
@@ -1100,12 +1100,18 @@ out_free_buf:
        if (buf != _buf)
                FREE(buf);
        return ret;
+#endif /* WITH_NTDLL */
 
-#else /* 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)
+               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
@@ -1148,25 +1154,19 @@ out_free_buf:
 out_find_close:
        FindClose(hFind);
        return ret;
-#endif /* !WITH_NTDLL */
 
 unnamed_only:
-       /* FindFirstStreamW() API is not available, or the volume does not
+       /* FindFirstStream() API is not available, or 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)))
-       {
-               wcscpy(dat.cStreamName, L"::$DATA");
-               dat.StreamSize.QuadPart = file_size;
-               ret = win32_capture_stream(path,
-                                          path_num_chars,
-                                          inode, lookup_table,
-                                          &dat);
-               if (ret)
-                       return ret;
-       }
-       return ret;
+       if (inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
+                                  FILE_ATTRIBUTE_REPARSE_POINT))
+               return 0;
+
+       wcscpy(dat.cStreamName, L"::$DATA");
+       dat.StreamSize.QuadPart = file_size;
+       return win32_capture_stream(path, path_num_chars,
+                                   inode, lookup_table, &dat);
 }
 
 static int
@@ -1314,7 +1314,7 @@ again:
 
        /* Capture the unnamed data stream (only should be present for regular
         * files) and any alternate data streams. */
-       ret = win32_capture_streams(hFile,
+       ret = win32_capture_streams(&hFile,
                                    path,
                                    path_num_chars,
                                    inode,
@@ -1333,6 +1333,19 @@ again:
                                               params->lookup_table);
        } else if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
                /* Directory (not a reparse point) --- recurse to children */
+
+               if (hFile == INVALID_HANDLE_VALUE) {
+                       /* Re-open handle that was closed to read raw encrypted
+                        * data.  */
+                       hFile = win32_open_existing_file(path, FILE_READ_DATA);
+                       if (hFile == INVALID_HANDLE_VALUE) {
+                               set_errno_from_GetLastError();
+                               ERROR_WITH_ERRNO("Failed to reopen \"%ls\"",
+                                                path);
+                               ret = WIMLIB_ERR_OPEN;
+                               goto out_close_handle;
+                       }
+               }
                ret = win32_recurse_directory(hFile,
                                              path,
                                              path_num_chars,
@@ -1391,12 +1404,15 @@ win32_build_dentry_tree(struct wim_dentry **root_ret,
        DWORD dret;
        bool need_prefix_free = false;
 
-#ifndef WITH_NTDLL
-       if (!win32func_FindFirstStreamW) {
+       if (!win32func_FindFirstStreamW
+#ifdef WITH_NTDLL
+           && !func_NtQueryInformationFile
+#endif
+          )
+       {
                WARNING("Running on Windows XP or earlier; "
                        "alternate data streams will not be captured.");
        }
-#endif
 
        path_nchars = wcslen(root_disk_path);
        if (path_nchars > WINDOWS_NT_MAX_PATH)