]> wimlib.net Git - wimlib/blobdiff - src/win32_capture.c
A few cleanups and fixes from recent changes
[wimlib] / src / win32_capture.c
index 4adf3364cb8f5ae778f9bca4f4e787fe1fc07162..7196fe22dea8a38837710a8733f1733d8198d975 100644 (file)
@@ -5,7 +5,7 @@
  */
 
 /*
- * Copyright (C) 2013, 2014 Eric Biggers
+ * Copyright (C) 2013, 2014, 2015 Eric Biggers
  *
  * This file is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
@@ -29,6 +29,7 @@
 
 #include "wimlib/win32_common.h"
 
+#include "wimlib/assert.h"
 #include "wimlib/capture.h"
 #include "wimlib/dentry.h"
 #include "wimlib/encoding.h"
@@ -38,8 +39,6 @@
 #include "wimlib/paths.h"
 #include "wimlib/reparse.h"
 
-#include <errno.h>
-
 struct winnt_scan_stats {
        unsigned long num_get_sd_access_denied;
        unsigned long num_get_sacl_priv_notheld;
@@ -122,10 +121,8 @@ read_winnt_file_prefix(const struct wim_lookup_table_entry *lte, u64 size,
        status = winnt_openat(NULL, path, wcslen(path),
                              FILE_READ_DATA | SYNCHRONIZE, &h);
        if (!NT_SUCCESS(status)) {
-               set_errno_from_nt_status(status);
-               ERROR_WITH_ERRNO("\"%ls\": Can't open for reading "
-                                "(status=0x%08"PRIx32")",
-                                printable_path(path), (u32)status);
+               winnt_error(status, L"\"%ls\": Can't open for reading",
+                           printable_path(path));
                return WIMLIB_ERR_OPEN;
        }
 
@@ -141,10 +138,8 @@ read_winnt_file_prefix(const struct wim_lookup_table_entry *lte, u64 size,
                status = (*func_NtReadFile)(h, NULL, NULL, NULL,
                                            &iosb, buf, count, NULL, NULL);
                if (!NT_SUCCESS(status)) {
-                       set_errno_from_nt_status(status);
-                       ERROR_WITH_ERRNO("\"%ls\": Error reading data "
-                                        "(status=0x%08"PRIx32")",
-                                        printable_path(path), (u32)status);
+                       winnt_error(status, L"\"%ls\": Error reading data",
+                                   printable_path(path));
                        ret = WIMLIB_ERR_READ;
                        break;
                }
@@ -180,8 +175,8 @@ win32_encrypted_export_cb(unsigned char *data, void *_ctx, unsigned long len)
        ret = (*ctx->read_prefix_cb)(data, bytes_to_consume, ctx->read_prefix_ctx);
        if (ret) {
                ctx->wimlib_err_code = ret;
-               /* Shouldn't matter what error code is returned here, as long as
-                * it isn't ERROR_SUCCESS.  */
+               /* It doesn't matter what error code is returned here, as long
+                * as it isn't ERROR_SUCCESS.  */
                return ERROR_READ_FAULT;
        }
        ctx->bytes_remaining -= bytes_to_consume;
@@ -197,29 +192,33 @@ read_win32_encrypted_file_prefix(const struct wim_lookup_table_entry *lte,
        DWORD err;
        void *file_ctx;
        int ret;
+       DWORD flags = 0;
+
+       if (lte->file_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
+               flags |= CREATE_FOR_DIR;
 
        export_ctx.read_prefix_cb = cb;
        export_ctx.read_prefix_ctx = cb_ctx;
        export_ctx.wimlib_err_code = 0;
        export_ctx.bytes_remaining = size;
 
-       err = OpenEncryptedFileRaw(lte->file_on_disk, 0, &file_ctx);
+       err = OpenEncryptedFileRaw(lte->file_on_disk, flags, &file_ctx);
        if (err != ERROR_SUCCESS) {
-               set_errno_from_win32_error(err);
-               ERROR_WITH_ERRNO("Failed to open encrypted file \"%ls\" "
-                                "for raw read",
-                                printable_path(lte->file_on_disk));
+               win32_error(err,
+                           L"Failed to open encrypted file \"%ls\" for raw read",
+                           printable_path(lte->file_on_disk));
                return WIMLIB_ERR_OPEN;
        }
        err = ReadEncryptedFileRaw(win32_encrypted_export_cb,
                                   &export_ctx, file_ctx);
        if (err != ERROR_SUCCESS) {
-               set_errno_from_win32_error(err);
-               ERROR_WITH_ERRNO("Failed to read encrypted file \"%ls\"",
-                                printable_path(lte->file_on_disk));
                ret = export_ctx.wimlib_err_code;
-               if (ret == 0)
+               if (ret == 0) {
+                       win32_error(err,
+                                   L"Failed to read encrypted file \"%ls\"",
+                                   printable_path(lte->file_on_disk));
                        ret = WIMLIB_ERR_READ;
+               }
        } else if (export_ctx.bytes_remaining != 0) {
                ERROR("Only could read %"PRIu64" of %"PRIu64" bytes from "
                      "encrypted file \"%ls\"",
@@ -464,10 +463,8 @@ winnt_recurse_directory(HANDLE h,
        }
 
        if (unlikely(status != STATUS_NO_MORE_FILES)) {
-               set_errno_from_nt_status(status);
-               ERROR_WITH_ERRNO("\"%ls\": Can't read directory "
-                                "(status=0x%08"PRIx32")",
-                                printable_path(full_path), (u32)status);
+               winnt_error(status, L"\"%ls\": Can't read directory",
+                           printable_path(full_path));
                ret = WIMLIB_ERR_READ;
        }
 out_free_buf:
@@ -758,12 +755,14 @@ winnt_get_reparse_data(HANDLE h, const wchar_t *path,
                             NULL, 0, rpbuf, REPARSE_POINT_MAX_SIZE,
                             &bytes_returned, NULL))
        {
-               set_errno_from_GetLastError();
+               win32_error(GetLastError(), L"\"%ls\": Can't get reparse data",
+                           printable_path(path));
                return WIMLIB_ERR_READ;
        }
 
        if (unlikely(bytes_returned < 8)) {
-               errno = EINVAL;
+               ERROR("\"%ls\": Reparse point data is invalid",
+                     printable_path(path));
                return WIMLIB_ERR_INVALID_REPARSE_DATA;
        }
 
@@ -792,26 +791,30 @@ win32_tally_encrypted_size_cb(unsigned char *_data, void *_size_ret,
 }
 
 static int
-win32_get_encrypted_file_size(const wchar_t *path, u64 *size_ret)
+win32_get_encrypted_file_size(const wchar_t *path, bool is_dir, u64 *size_ret)
 {
        DWORD err;
        void *file_ctx;
        int ret;
+       DWORD flags = 0;
+
+       if (is_dir)
+               flags |= CREATE_FOR_DIR;
 
-       err = OpenEncryptedFileRaw(path, 0, &file_ctx);
+       err = OpenEncryptedFileRaw(path, flags, &file_ctx);
        if (err != ERROR_SUCCESS) {
-               set_errno_from_win32_error(err);
-               ERROR_WITH_ERRNO("Failed to open encrypted file \"%ls\" "
-                                "for raw read", printable_path(path));
+               win32_error(err,
+                           L"Failed to open encrypted file \"%ls\" for raw read",
+                           printable_path(path));
                return WIMLIB_ERR_OPEN;
        }
        *size_ret = 0;
        err = ReadEncryptedFileRaw(win32_tally_encrypted_size_cb,
                                   size_ret, file_ctx);
        if (err != ERROR_SUCCESS) {
-               set_errno_from_win32_error(err);
-               ERROR_WITH_ERRNO("Failed to read raw encrypted data from "
-                                "\"%ls\"", printable_path(path));
+               win32_error(err,
+                           L"Failed to read raw encrypted data from \"%ls\"",
+                           printable_path(path));
                ret = WIMLIB_ERR_READ;
        } else {
                ret = 0;
@@ -820,6 +823,41 @@ win32_get_encrypted_file_size(const wchar_t *path, u64 *size_ret)
        return ret;
 }
 
+static int
+winnt_load_encrypted_stream_info(struct wim_inode *inode, const wchar_t *nt_path,
+                                struct list_head *unhashed_streams)
+{
+       struct wim_lookup_table_entry *lte = new_lookup_table_entry();
+       int ret;
+
+       if (unlikely(!lte))
+               return WIMLIB_ERR_NOMEM;
+
+       lte->file_on_disk = WCSDUP(nt_path);
+       if (unlikely(!lte->file_on_disk)) {
+               free_lookup_table_entry(lte);
+               return WIMLIB_ERR_NOMEM;
+       }
+       lte->resource_location = RESOURCE_WIN32_ENCRYPTED;
+
+       /* OpenEncryptedFileRaw() expects a Win32 name.  */
+       wimlib_assert(!wmemcmp(lte->file_on_disk, L"\\??\\", 4));
+       lte->file_on_disk[1] = L'\\';
+
+       ret = win32_get_encrypted_file_size(lte->file_on_disk,
+                                           (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY),
+                                           &lte->size);
+       if (unlikely(ret)) {
+               free_lookup_table_entry(lte);
+               return ret;
+       }
+
+       lte->file_inode = inode;
+       add_unhashed_stream(lte, inode, 0, unhashed_streams);
+       inode->i_lte = lte;
+       return 0;
+}
+
 static bool
 get_data_stream_name(const wchar_t *raw_stream_name, size_t raw_stream_name_nchars,
                     const wchar_t **stream_name_ret, size_t *stream_name_nchars_ret)
@@ -904,8 +942,11 @@ winnt_scan_stream(const wchar_t *path, size_t path_nchars,
                                                        sizeof(wchar_t));
                if (!ads_entry)
                        return WIMLIB_ERR_NOMEM;
-       } else if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
-               /* Ignore unnamed data stream of reparse point  */
+       } else if (inode->i_attributes & (FILE_ATTRIBUTE_REPARSE_POINT |
+                                         FILE_ATTRIBUTE_ENCRYPTED))
+       {
+               /* Ignore unnamed data stream of reparse point or encrypted file
+                */
                return 0;
        } else {
                ads_entry = NULL;
@@ -932,27 +973,6 @@ winnt_scan_stream(const wchar_t *path, size_t path_nchars,
        lte->file_on_disk = stream_path;
        lte->resource_location = RESOURCE_IN_WINNT_FILE_ON_DISK;
        lte->size = stream_size;
-       if ((inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) && !ads_entry) {
-               /* Special case for encrypted file.  */
-
-               /* OpenEncryptedFileRaw() expects Win32 name, not NT name.
-                * Change \??\ into \\?\  */
-               lte->file_on_disk[1] = L'\\';
-               wimlib_assert(!wmemcmp(lte->file_on_disk, L"\\\\?\\", 4));
-
-               u64 encrypted_size;
-               int ret;
-
-               ret = win32_get_encrypted_file_size(lte->file_on_disk,
-                                                   &encrypted_size);
-               if (ret) {
-                       free_lookup_table_entry(lte);
-                       return ret;
-               }
-               lte->size = encrypted_size;
-               lte->resource_location = RESOURCE_WIN32_ENCRYPTED;
-       }
-
        if (ads_entry) {
                stream_id = ads_entry->stream_id;
                ads_entry->lte = lte;
@@ -980,7 +1000,7 @@ winnt_scan_stream(const wchar_t *path, size_t path_nchars,
  *   already present in Windows XP.
  */
 static int
-winnt_scan_streams(HANDLE *hFile_p, const wchar_t *path, size_t path_nchars,
+winnt_scan_streams(HANDLE h, const wchar_t *path, size_t path_nchars,
                   struct wim_inode *inode, struct list_head *unhashed_streams,
                   u64 file_size, u32 vol_flags)
 {
@@ -999,7 +1019,7 @@ winnt_scan_streams(HANDLE *hFile_p, const wchar_t *path, size_t path_nchars,
                goto unnamed_only;
 
        /* Get a buffer containing the stream information.  */
-       while (!NT_SUCCESS(status = (*func_NtQueryInformationFile)(*hFile_p,
+       while (!NT_SUCCESS(status = (*func_NtQueryInformationFile)(h,
                                                                   &iosb,
                                                                   buf,
                                                                   bufsize,
@@ -1028,10 +1048,9 @@ winnt_scan_streams(HANDLE *hFile_p, const wchar_t *path, size_t path_nchars,
                case STATUS_INVALID_INFO_CLASS:
                        goto unnamed_only;
                default:
-                       set_errno_from_nt_status(status);
-                       ERROR_WITH_ERRNO("\"%ls\": Failed to query stream "
-                                        "information (status=0x%08"PRIx32")",
-                                        printable_path(path), (u32)status);
+                       winnt_error(status,
+                                   L"\"%ls\": Failed to query stream information",
+                                   printable_path(path));
                        ret = WIMLIB_ERR_READ;
                        goto out_free_buf;
                }
@@ -1043,14 +1062,6 @@ winnt_scan_streams(HANDLE *hFile_p, const wchar_t *path, size_t path_nchars,
                goto out_free_buf;
        }
 
-       if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)) {
-               /* OpenEncryptedFileRaw() seems to fail with
-                * ERROR_SHARING_VIOLATION if there are any handles opened to
-                * the file.  */
-               (*func_NtClose)(*hFile_p);
-               *hFile_p = INVALID_HANDLE_VALUE;
-       }
-
        /* Parse one or more stream information structures.  */
        info = (const FILE_STREAM_INFORMATION *)buf;
        for (;;) {
@@ -1106,13 +1117,14 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
 {
        struct wim_dentry *root = NULL;
        struct wim_inode *inode = NULL;
-       HANDLE h = INVALID_HANDLE_VALUE;
+       HANDLE h = NULL;
        int ret;
        NTSTATUS status;
        FILE_ALL_INFORMATION file_info;
        u8 *rpbuf;
        u16 rpbuflen;
        u16 not_rpfixed;
+       ACCESS_MASK requestedPerms;
 
        ret = try_exclude(full_path, full_path_nchars, params);
        if (ret < 0) /* Excluded? */
@@ -1121,32 +1133,37 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                goto out;
 
        /* Open the file.  */
+       requestedPerms = FILE_READ_DATA |
+                        FILE_READ_ATTRIBUTES |
+                        READ_CONTROL |
+                        ACCESS_SYSTEM_SECURITY |
+                        SYNCHRONIZE;
+retry_open:
        status = winnt_openat(cur_dir,
                              (cur_dir ? filename : full_path),
                              (cur_dir ? filename_nchars : full_path_nchars),
-                             FILE_READ_DATA |
-                                       FILE_READ_ATTRIBUTES |
-                                       READ_CONTROL |
-                                       ACCESS_SYSTEM_SECURITY |
-                                       SYNCHRONIZE,
+                             requestedPerms,
                              &h);
        if (unlikely(!NT_SUCCESS(status))) {
                if (status == STATUS_DELETE_PENDING) {
                        WARNING("\"%ls\": Deletion pending; skipping file",
                                printable_path(full_path));
                        ret = 0;
-               } else {
-                       set_errno_from_nt_status(status);
-                       ERROR_WITH_ERRNO("\"%ls\": Can't open file "
-                                        "(status=0x%08"PRIx32")",
-                                        printable_path(full_path), (u32)status);
-                       if (status == STATUS_FVE_LOCKED_VOLUME)
-                               ret = WIMLIB_ERR_FVE_LOCKED_VOLUME;
-                       else
-                               ret = WIMLIB_ERR_OPEN;
+                       goto out;
                }
-               /* XXX: Provide option to exclude files that fail with
-                * STATUS_SHARING_VIOLATION?  */
+               if (status == STATUS_ACCESS_DENIED &&
+                   (requestedPerms & FILE_READ_DATA)) {
+                       /* This happens on encrypted files.  */
+                       requestedPerms &= ~FILE_READ_DATA;
+                       goto retry_open;
+               }
+
+               winnt_error(status, L"\"%ls\": Can't open file",
+                           printable_path(full_path));
+               if (status == STATUS_FVE_LOCKED_VOLUME)
+                       ret = WIMLIB_ERR_FVE_LOCKED_VOLUME;
+               else
+                       ret = WIMLIB_ERR_OPEN;
                goto out;
        }
 
@@ -1162,15 +1179,23 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                if (unlikely(!NT_SUCCESS(status) &&
                             status != STATUS_BUFFER_OVERFLOW))
                {
-                       set_errno_from_nt_status(status);
-                       ERROR_WITH_ERRNO("\"%ls\": Can't get file information "
-                                        "(status=0x%08"PRIx32")",
-                                        printable_path(full_path), (u32)status);
+                       winnt_error(status,
+                                   L"\"%ls\": Can't get file information",
+                                   printable_path(full_path));
                        ret = WIMLIB_ERR_STAT;
                        goto out;
                }
        }
 
+       if (unlikely(!(requestedPerms & FILE_READ_DATA)) &&
+           !(file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED))
+       {
+               ERROR("\"%ls\": Permission to read data was denied",
+                     printable_path(full_path));
+               ret = WIMLIB_ERR_OPEN;
+               goto out;
+       }
+
        if (unlikely(!cur_dir)) {
 
                /* Root of tree being captured; get volume information.  */
@@ -1193,11 +1218,9 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                {
                        vol_flags = attr_info.FileSystemAttributes;
                } else {
-                       set_errno_from_nt_status(status);
-                       WARNING_WITH_ERRNO("\"%ls\": Can't get volume attributes "
-                                          "(status=0x%08"PRIx32")",
-                                          printable_path(full_path),
-                                          (u32)status);
+                       winnt_warning(status,
+                                     L"\"%ls\": Can't get volume attributes",
+                                     printable_path(full_path));
                        vol_flags = 0;
                }
 
@@ -1219,11 +1242,8 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                {
                        params->capture_root_dev = vol_info.VolumeSerialNumber;
                } else {
-                       set_errno_from_nt_status(status);
-                       WARNING_WITH_ERRNO("\"%ls\": Can't get volume ID "
-                                          "(status=0x%08"PRIx32")",
-                                          printable_path(full_path),
-                                          (u32)status);
+                       winnt_warning(status, L"\"%ls\": Can't get volume ID",
+                                     printable_path(full_path));
                        params->capture_root_dev = 0;
                }
        }
@@ -1243,8 +1263,6 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                        not_rpfixed = 1;
                        break;
                default:
-                       ERROR_WITH_ERRNO("\"%ls\": Can't get reparse data",
-                                        printable_path(full_path));
                        goto out;
                }
        }
@@ -1307,11 +1325,9 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                                                       params->sd_set, stats,
                                                       params->add_flags);
                if (!NT_SUCCESS(status)) {
-                       set_errno_from_nt_status(status);
-                       ERROR_WITH_ERRNO("\"%ls\": Can't read security "
-                                        "descriptor (status=0x%08"PRIx32")",
-                                        printable_path(full_path),
-                                        (u32)status);
+                       winnt_error(status,
+                                   L"\"%ls\": Can't read security descriptor",
+                                   printable_path(full_path));
                        ret = WIMLIB_ERR_STAT;
                        goto out;
                }
@@ -1319,7 +1335,7 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
 
        /* Load information about the unnamed data stream and any named data
         * streams.  */
-       ret = winnt_scan_streams(&h,
+       ret = winnt_scan_streams(h,
                                 full_path,
                                 full_path_nchars,
                                 inode,
@@ -1329,21 +1345,42 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
        if (ret)
                goto out;
 
-       if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
-
-               /* Reparse point: set the reparse data (already read).  */
-
-               inode->i_not_rpfixed = not_rpfixed;
-               inode->i_reparse_tag = le32_to_cpu(*(le32*)rpbuf);
-               ret = inode_set_unnamed_stream(inode, rpbuf + 8, rpbuflen - 8,
-                                              params->lookup_table);
+       if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)) {
+               /* Load information about the raw encrypted data.  This is
+                * needed for any directory or non-directory that has
+                * FILE_ATTRIBUTE_ENCRYPTED set.
+                *
+                * Note: since OpenEncryptedFileRaw() fails with
+                * ERROR_SHARING_VIOLATION if there are any open handles to the
+                * file, we have to close the file and re-open it later if
+                * needed.  */
+               (*func_NtClose)(h);
+               h = NULL;
+               ret = winnt_load_encrypted_stream_info(inode, full_path,
+                                                      params->unhashed_streams);
                if (ret)
                        goto out;
+       }
+
+       if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
+               if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)) {
+                       WARNING("Ignoring reparse data of encrypted reparse point file \"%ls\"",
+                               printable_path(full_path));
+               } else {
+                       /* Reparse point: set the reparse data (already read).  */
+
+                       inode->i_not_rpfixed = not_rpfixed;
+                       inode->i_reparse_tag = le32_to_cpu(*(le32*)rpbuf);
+                       ret = inode_set_unnamed_stream(inode, rpbuf + 8, rpbuflen - 8,
+                                                      params->lookup_table);
+                       if (ret)
+                               goto out;
+               }
        } else if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
 
                /* Directory: recurse to children.  */
 
-               if (unlikely(h == INVALID_HANDLE_VALUE)) {
+               if (unlikely(!h)) {
                        /* Re-open handle that was closed to read raw encrypted
                         * data.  */
                        status = winnt_openat(cur_dir,
@@ -1354,11 +1391,9 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                                              FILE_LIST_DIRECTORY | SYNCHRONIZE,
                                              &h);
                        if (!NT_SUCCESS(status)) {
-                               set_errno_from_nt_status(status);
-                               ERROR_WITH_ERRNO("\"%ls\": Can't re-open file "
-                                                "(status=0x%08"PRIx32")",
-                                                printable_path(full_path),
-                                                (u32)status);
+                               winnt_error(status,
+                                           L"\"%ls\": Can't re-open file",
+                                           printable_path(full_path));
                                ret = WIMLIB_ERR_OPEN;
                                goto out;
                        }
@@ -1381,7 +1416,7 @@ out_progress:
        else
                ret = do_capture_progress(params, WIMLIB_SCAN_DENTRY_EXCLUDED, NULL);
 out:
-       if (likely(h != INVALID_HANDLE_VALUE))
+       if (likely(h))
                (*func_NtClose)(h);
        if (unlikely(ret)) {
                free_dentry_tree(root, params->lookup_table);