/*
* 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__
# include "config.h"
#endif
+#include <errno.h>
+
#include "wimlib/win32_common.h"
+#include "wimlib/assert.h"
#include "wimlib/capture.h"
#include "wimlib/dentry.h"
#include "wimlib/encoding.h"
#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;
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;
}
/*
- * 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
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);
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
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);
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)
{
}
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];
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;
}
/* 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
*/
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;
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, <e->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)
sizeof(wchar_t));
if (!ads_entry)
return WIMLIB_ERR_NOMEM;
+ } 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;
}
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;
stream_id = 0;
inode->i_lte = lte;
}
+ lte->file_inode = inode;
add_unhashed_stream(lte, inode, stream_id, unhashed_streams);
return 0;
}
* 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)
{
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,
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 (;;) {
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)
{
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;
- if (should_exclude_path(full_path + params->capture_root_nchars,
- full_path_nchars - params->capture_root_nchars,
- params->config))
+ 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. */
+ 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);
- ret = WIMLIB_ERR_OPEN;
+ goto out;
+ }
+ if (status == STATUS_ACCESS_DENIED &&
+ (requestedPerms & FILE_READ_DATA)) {
+ /* This happens on encrypted files. */
+ requestedPerms &= ~FILE_READ_DATA;
+ goto retry_open;
}
- /* XXX: Provide option to exclude files that fail with
- * STATUS_SHARING_VIOLATION? */
+
+ if (status == STATUS_FVE_LOCKED_VOLUME) {
+ ERROR("\"%ls\": Can't open file "
+ "(encrypted volume has not been unlocked)",
+ printable_path(full_path));
+ ret = WIMLIB_ERR_FVE_LOCKED_VOLUME;
+ goto out;
+ }
+
+ 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;
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. */
* 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)),
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;
/* 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,
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,
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 (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;
}
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;
int ret;
{
ERROR("\"%ls\": unrecognized path format", root_disk_path);
ret = WIMLIB_ERR_INVALID_PARAM;
- goto out_free_path;
- }
-
- ntpath_nchars = ntpath.Length / sizeof(wchar_t);
- wmemcpy(path, ntpath.Buffer, ntpath_nchars);
- path[ntpath_nchars] = L'\0';
-
- params->capture_root_nchars = ntpath_nchars;
- if (path[ntpath_nchars - 1] == L'\\')
- params->capture_root_nchars--;
+ } else {
+ ntpath_nchars = ntpath.Length / sizeof(wchar_t);
+ wmemcpy(path, ntpath.Buffer, ntpath_nchars);
+ path[ntpath_nchars] = L'\0';
+ 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));