]> wimlib.net Git - wimlib/blobdiff - src/win32_capture.c
Rename add_image_params => capture_params
[wimlib] / src / win32_capture.c
index 27b3794fd436073d23d120800272ec1f50623941..4adf3364cb8f5ae778f9bca4f4e787fe1fc07162 100644 (file)
@@ -7,20 +7,18 @@
 /*
  * Copyright (C) 2013, 2014 Eric Biggers
  *
- * This file is part of wimlib, a library for working with WIM files.
+ * 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
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
  *
- * wimlib is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- * A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
  * details.
  *
- * You should have received a copy of the GNU General Public License
- * along with wimlib; if not, see http://www.gnu.org/licenses/.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this file; if not, see http://www.gnu.org/licenses/.
  */
 
 #ifdef __WIN32__
@@ -224,7 +222,7 @@ read_win32_encrypted_file_prefix(const struct wim_lookup_table_entry *lte,
                        ret = WIMLIB_ERR_READ;
        } else if (export_ctx.bytes_remaining != 0) {
                ERROR("Only could read %"PRIu64" of %"PRIu64" bytes from "
-                     "encryted file \"%ls\"",
+                     "encrypted file \"%ls\"",
                      size - export_ctx.bytes_remaining, size,
                      printable_path(lte->file_on_disk));
                ret = WIMLIB_ERR_READ;
@@ -263,7 +261,7 @@ winnt_get_short_name(HANDLE h, struct wim_dentry *dentry)
 }
 
 /*
- * Load the security descriptor of a file into the corresponding inode, and the
+ * Load the security descriptor of a file into the corresponding inode and the
  * WIM image's security descriptor set.
  */
 static NTSTATUS
@@ -278,10 +276,27 @@ winnt_get_security_descriptor(HANDLE h, struct wim_inode *inode,
        ULONG len_needed;
        NTSTATUS status;
 
-       requestedInformation = DACL_SECURITY_INFORMATION |
+       /*
+        * LABEL_SECURITY_INFORMATION is needed on Windows Vista and 7 because
+        * Microsoft decided to add mandatory integrity labels to the SACL but
+        * not have them returned by SACL_SECURITY_INFORMATION.
+        *
+        * BACKUP_SECURITY_INFORMATION is needed on Windows 8 because Microsoft
+        * decided to add even more stuff to the SACL and still not have it
+        * returned by SACL_SECURITY_INFORMATION; but they did remember that
+        * backup applications exist and simply want to read the stupid thing
+        * once and for all, so they added a flag to read the entire security
+        * descriptor.
+        *
+        * Older versions of Windows tolerate these new flags being passed in.
+        */
+       requestedInformation = OWNER_SECURITY_INFORMATION |
+                              GROUP_SECURITY_INFORMATION |
+                              DACL_SECURITY_INFORMATION |
                               SACL_SECURITY_INFORMATION |
-                              OWNER_SECURITY_INFORMATION |
-                              GROUP_SECURITY_INFORMATION;
+                              LABEL_SECURITY_INFORMATION |
+                              BACKUP_SECURITY_INFORMATION;
+
        buf = _buf;
        bufsize = sizeof(_buf);
 
@@ -339,7 +354,9 @@ winnt_get_security_descriptor(HANDLE h, struct wim_inode *inode,
                        if (requestedInformation & SACL_SECURITY_INFORMATION) {
                                /* Try again without the SACL.  */
                                stats->num_get_sacl_priv_notheld++;
-                               requestedInformation &= ~SACL_SECURITY_INFORMATION;
+                               requestedInformation &= ~(SACL_SECURITY_INFORMATION |
+                                                         LABEL_SECURITY_INFORMATION |
+                                                         BACKUP_SECURITY_INFORMATION);
                                break;
                        }
                        /* Fake success (useful when capturing as
@@ -368,7 +385,7 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                                  size_t full_path_nchars,
                                  const wchar_t *filename,
                                  size_t filename_nchars,
-                                 struct add_image_params *params,
+                                 struct capture_params *params,
                                  struct winnt_scan_stats *stats,
                                  u32 vol_flags);
 
@@ -377,7 +394,7 @@ winnt_recurse_directory(HANDLE h,
                        wchar_t *full_path,
                        size_t full_path_nchars,
                        struct wim_dentry *parent,
-                       struct add_image_params *params,
+                       struct capture_params *params,
                        struct winnt_scan_stats *stats,
                        u32 vol_flags)
 {
@@ -406,11 +423,18 @@ winnt_recurse_directory(HANDLE h,
                                                           info->FileName[1] == L'.'))
                        {
                                wchar_t *p;
+                               wchar_t *filename;
                                struct wim_dentry *child;
 
                                p = full_path + full_path_nchars;
-                               *p++ = L'\\';
-                               p = wmempcpy(p, info->FileName,
+                               /* Only add a backslash if we don't already have
+                                * one.  This prevents a duplicate backslash
+                                * from being added when the path to the capture
+                                * dir had a trailing backslash.  */
+                               if (*(p - 1) != L'\\')
+                                       *p++ = L'\\';
+                               filename = p;
+                               p = wmempcpy(filename, info->FileName,
                                             info->FileNameLength / 2);
                                *p = '\0';
 
@@ -419,7 +443,7 @@ winnt_recurse_directory(HANDLE h,
                                                        h,
                                                        full_path,
                                                        p - full_path,
-                                                       full_path + full_path_nchars + 1,
+                                                       filename,
                                                        info->FileNameLength / 2,
                                                        params,
                                                        stats,
@@ -453,9 +477,6 @@ out_free_buf:
 
 /* Reparse point fixup status code  */
 enum rp_status {
-       /* Reparse point should be excluded from capture  */
-       RP_EXCLUDED     = -0,
-
        /* Reparse point will be captured literally (no fixup)  */
        RP_NOT_FIXED    = -1,
 
@@ -589,9 +610,8 @@ out_close_root_dir:
 }
 
 static int
-winnt_rpfix_progress(struct add_image_params *params, const wchar_t *path,
-                    const struct reparse_data *rpdata,
-                    enum wimlib_progress_msg msg)
+winnt_rpfix_progress(struct capture_params *params, const wchar_t *path,
+                    const struct reparse_data *rpdata, int scan_status)
 {
        size_t print_name_nchars = rpdata->print_name_nbytes / sizeof(wchar_t);
        wchar_t print_name0[print_name_nchars + 1];
@@ -601,13 +621,13 @@ winnt_rpfix_progress(struct add_image_params *params, const wchar_t *path,
 
        params->progress.scan.cur_path = printable_path(path);
        params->progress.scan.symlink_target = print_name0;
-       return do_capture_progress(params, msg, NULL);
+       return do_capture_progress(params, scan_status, NULL);
 }
 
 static int
 winnt_try_rpfix(u8 *rpbuf, u16 *rpbuflen_p,
                u64 capture_root_ino, u64 capture_root_dev,
-               const wchar_t *path, struct add_image_params *params)
+               const wchar_t *path, struct capture_params *params)
 {
        struct reparse_data rpdata;
        const wchar_t *rel_target;
@@ -650,14 +670,13 @@ winnt_try_rpfix(u8 *rpbuf, u16 *rpbuflen_p,
                                                    capture_root_ino,
                                                    capture_root_dev);
        if (!rel_target) {
-               /* Target points outside of the tree being captured.  Exclude
-                * this reparse point from the capture (but inform the library
-                * user).  */
+               /* Target points outside of the tree being captured.  Don't
+                * adjust it.  */
                ret = winnt_rpfix_progress(params, path, &rpdata,
-                                          WIMLIB_SCAN_DENTRY_EXCLUDED_SYMLINK);
+                                          WIMLIB_SCAN_DENTRY_NOT_FIXED_SYMLINK);
                if (ret)
                        return ret;
-               return RP_EXCLUDED;
+               return RP_NOT_FIXED;
        }
 
        if (rel_target == rpdata.substitute_name) {
@@ -667,7 +686,7 @@ winnt_try_rpfix(u8 *rpbuf, u16 *rpbuflen_p,
        }
 
        /* We have an absolute target pointing within the directory being
-        * captured, @rel_target is the suffix of the link target that is the
+        * captured. @rel_target is the suffix of the link target that is the
         * part relative to the directory being captured.
         *
         * We will cut off the prefix before this part (which is the path to the
@@ -722,12 +741,12 @@ winnt_try_rpfix(u8 *rpbuf, u16 *rpbuflen_p,
  *     On success, the length of the reparse point buffer in bytes is written
  *     to this location.
  *
- * On success, returns a nonpositive `enum rp_status' value.
+ * On success, returns a negative `enum rp_status' value.
  * On failure, returns a positive error code.
  */
 static int
 winnt_get_reparse_data(HANDLE h, const wchar_t *path,
-                      struct add_image_params *params,
+                      struct capture_params *params,
                       u8 *rpbuf, u16 *rpbuflen_ret)
 {
        DWORD bytes_returned;
@@ -885,6 +904,9 @@ 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  */
+               return 0;
        } else {
                ads_entry = NULL;
        }
@@ -938,6 +960,7 @@ winnt_scan_stream(const wchar_t *path, size_t path_nchars,
                stream_id = 0;
                inode->i_lte = lte;
        }
+       lte->file_inode = inode;
        add_unhashed_stream(lte, inode, stream_id, unhashed_streams);
        return 0;
 }
@@ -1077,7 +1100,7 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                                  size_t full_path_nchars,
                                  const wchar_t *filename,
                                  size_t filename_nchars,
-                                 struct add_image_params *params,
+                                 struct capture_params *params,
                                  struct winnt_scan_stats *stats,
                                  u32 vol_flags)
 {
@@ -1091,13 +1114,11 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
        u16 rpbuflen;
        u16 not_rpfixed;
 
-       if (should_exclude_path(full_path + params->capture_root_nchars,
-                               full_path_nchars - params->capture_root_nchars,
-                               params->config))
-       {
-               ret = 0;
+       ret = try_exclude(full_path, full_path_nchars, params);
+       if (ret < 0) /* Excluded? */
                goto out_progress;
-       }
+       if (ret > 0) /* Error? */
+               goto out;
 
        /* Open the file.  */
        status = winnt_openat(cur_dir,
@@ -1110,11 +1131,22 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                                        SYNCHRONIZE,
                              &h);
        if (unlikely(!NT_SUCCESS(status))) {
-               set_errno_from_nt_status(status);
-               ERROR_WITH_ERRNO("\"%ls\": Can't open file "
-                                "(status=0x%08"PRIx32")",
-                                printable_path(full_path), (u32)status);
-               ret = WIMLIB_ERR_OPEN;
+               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;
+               }
+               /* XXX: Provide option to exclude files that fail with
+                * STATUS_SHARING_VIOLATION?  */
                goto out;
        }
 
@@ -1204,9 +1236,6 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                ret = winnt_get_reparse_data(h, full_path, params,
                                             rpbuf, &rpbuflen);
                switch (ret) {
-               case RP_EXCLUDED:
-                       ret = 0;
-                       goto out;
                case RP_FIXED:
                        not_rpfixed = 0;
                        break;
@@ -1225,13 +1254,17 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
         * However, we need to explicitly check for directories and files with
         * only 1 link and refuse to hard link them.  This is because Windows
         * has a bug where it can return duplicate File IDs for files and
-        * directories on the FAT filesystem. */
+        * directories on the FAT filesystem.
+        *
+        * Since we don't follow mount points on Windows, we don't need to query
+        * the volume ID per-file.  Just once, for the root, is enough.  But we
+        * can't simply pass 0, because then there could be inode collisions
+        * among multiple calls to win32_build_dentry_tree() that are scanning
+        * files on different volumes.  */
        ret = inode_table_new_dentry(params->inode_table,
                                     filename,
                                     file_info.InternalInformation.IndexNumber.QuadPart,
-                                    0, /* We don't follow mount points, so we
-                                          currently don't need to get the
-                                          volume ID / device number.  */
+                                    params->capture_root_dev,
                                     (file_info.StandardInformation.NumberOfLinks <= 1 ||
                                        (file_info.BasicInformation.FileAttributes &
                                         FILE_ATTRIBUTE_DIRECTORY)),
@@ -1256,7 +1289,6 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
        if (inode->i_nlink > 1) {
                /* Shared inode (hard link); skip reading per-inode information.
                 */
-               ret = 0;
                goto out_progress;
        }
 
@@ -1277,7 +1309,7 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                if (!NT_SUCCESS(status)) {
                        set_errno_from_nt_status(status);
                        ERROR_WITH_ERRNO("\"%ls\": Can't read security "
-                                        "descriptor (status=0x%08"PRIu32")",
+                                        "descriptor (status=0x%08"PRIx32")",
                                         printable_path(full_path),
                                         (u32)status);
                        ret = WIMLIB_ERR_STAT;
@@ -1351,10 +1383,12 @@ out_progress:
 out:
        if (likely(h != INVALID_HANDLE_VALUE))
                (*func_NtClose)(h);
-       if (likely(ret == 0))
-               *root_ret = root;
-       else
+       if (unlikely(ret)) {
                free_dentry_tree(root, params->lookup_table);
+               root = NULL;
+               ret = report_capture_error(params, ret, full_path);
+       }
+       *root_ret = root;
        return ret;
 }
 
@@ -1386,13 +1420,13 @@ winnt_do_scan_warnings(const wchar_t *path, const struct winnt_scan_stats *stats
 int
 win32_build_dentry_tree(struct wim_dentry **root_ret,
                        const wchar_t *root_disk_path,
-                       struct add_image_params *params)
+                       struct capture_params *params)
 {
        wchar_t *path;
-       DWORD dret;
-       size_t path_nchars;
        int ret;
+       UNICODE_STRING ntpath;
        struct winnt_scan_stats stats;
+       size_t ntpath_nchars;
 
        /* WARNING: There is no check for overflow later when this buffer is
         * being used!  But it's as long as the maximum path length understood
@@ -1401,33 +1435,36 @@ win32_build_dentry_tree(struct wim_dentry **root_ret,
        if (!path)
                return WIMLIB_ERR_NOMEM;
 
-       /* Translate into full path.  */
-       dret = GetFullPathName(root_disk_path, WINDOWS_NT_MAX_PATH - 3,
-                              &path[4], NULL);
-
-       if (unlikely(dret == 0 || dret >= WINDOWS_NT_MAX_PATH - 3)) {
-               ERROR("Can't get full path name for \"%ls\"", root_disk_path);
-               return WIMLIB_ERR_UNSUPPORTED;
-       }
-
-       /* Add \??\ prefix to form the NT namespace path.  */
-       wmemcpy(path, L"\\??\\", 4);
-       path_nchars = dret + 4;
+       ret = win32_path_to_nt_path(root_disk_path, &ntpath);
+       if (ret)
+               goto out_free_path;
 
-       /* Strip trailing slashes.  If we don't do this, we may create a path
-       * with multiple consecutive backslashes, which for some reason causes
-       * Windows to report that the file cannot be found.  */
-       while (unlikely(path[path_nchars - 1] == L'\\' &&
-                       path[path_nchars - 2] != L':'))
-               path[--path_nchars] = L'\0';
+       if (ntpath.Length < 4 * sizeof(wchar_t) ||
+           ntpath.Length > WINDOWS_NT_MAX_PATH * sizeof(wchar_t) ||
+           wmemcmp(ntpath.Buffer, L"\\??\\", 4))
+       {
+               ERROR("\"%ls\": unrecognized path format", root_disk_path);
+               ret = WIMLIB_ERR_INVALID_PARAM;
+       } else {
+               ntpath_nchars = ntpath.Length / sizeof(wchar_t);
+               wmemcpy(path, ntpath.Buffer, ntpath_nchars);
+               path[ntpath_nchars] = L'\0';
 
-       params->capture_root_nchars = path_nchars;
+               params->capture_root_nchars = ntpath_nchars;
+               if (path[ntpath_nchars - 1] == L'\\')
+                       params->capture_root_nchars--;
+               ret = 0;
+       }
+       HeapFree(GetProcessHeap(), 0, ntpath.Buffer);
+       if (ret)
+               goto out_free_path;
 
        memset(&stats, 0, sizeof(stats));
 
        ret = winnt_build_dentry_tree_recursive(root_ret, NULL,
-                                               path, path_nchars, L"", 0,
-                                               params, &stats, 0);
+                                               path, ntpath_nchars,
+                                               L"", 0, params, &stats, 0);
+out_free_path:
        FREE(path);
        if (ret == 0)
                winnt_do_scan_warnings(root_disk_path, &stats);