*/
/*
- * Copyright (C) 2013, 2014 Eric Biggers
+ * Copyright (C) 2013, 2014, 2015 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 "wimlib/win32_common.h"
+#include "wimlib/assert.h"
+#include "wimlib/blob_table.h"
#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"
#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;
}
/* Read the first @size bytes from the file, or named data stream of a file,
- * from which the stream entry @lte was created. */
+ * described by @blob. */
int
-read_winnt_file_prefix(const struct wim_lookup_table_entry *lte, u64 size,
- consume_data_callback_t cb, void *cb_ctx)
+read_winnt_stream_prefix(const struct blob_descriptor *blob, u64 size,
+ consume_data_callback_t cb, void *cb_ctx)
{
const wchar_t *path;
HANDLE h;
int ret;
/* This is an NT namespace path. */
- path = lte->file_on_disk;
+ path = blob->file_on_disk;
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;
}
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;
}
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;
}
int
-read_win32_encrypted_file_prefix(const struct wim_lookup_table_entry *lte,
+read_win32_encrypted_file_prefix(const struct blob_descriptor *blob,
u64 size,
consume_data_callback_t cb, void *cb_ctx)
{
DWORD err;
void *file_ctx;
int ret;
+ DWORD flags = 0;
+
+ if (blob->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(blob->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(blob->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(blob->file_on_disk));
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));
+ printable_path(blob->file_on_disk));
ret = WIMLIB_ERR_READ;
} else {
ret = 0;
}
/*
- * 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)
{
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';
h,
full_path,
p - full_path,
- full_path + full_path_nchars + 1,
+ filename,
info->FileNameLength / 2,
params,
stats,
}
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:
/* 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,
}
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;
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) {
}
/* 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
* 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;
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;
+ if (unlikely(bytes_returned < REPARSE_DATA_OFFSET)) {
+ ERROR("\"%ls\": Reparse point data is invalid",
+ printable_path(path));
return WIMLIB_ERR_INVALID_REPARSE_DATA;
}
}
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;
- err = OpenEncryptedFileRaw(path, 0, &file_ctx);
+ if (is_dir)
+ flags |= CREATE_FOR_DIR;
+
+ 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;
return ret;
}
+static int
+winnt_load_efsrpc_raw_data(struct wim_inode *inode, const wchar_t *nt_path,
+ struct list_head *unhashed_blobs)
+{
+ struct blob_descriptor *blob;
+ struct wim_inode_stream *strm;
+ int ret;
+
+ blob = new_blob_descriptor();
+ if (!blob)
+ goto err_nomem;
+
+ blob->file_on_disk = WCSDUP(nt_path);
+ if (!blob->file_on_disk)
+ goto err_nomem;
+ blob->blob_location = BLOB_WIN32_ENCRYPTED;
+
+ /* OpenEncryptedFileRaw() expects a Win32 name. */
+ wimlib_assert(!wmemcmp(blob->file_on_disk, L"\\??\\", 4));
+ blob->file_on_disk[1] = L'\\';
+
+ blob->file_inode = inode;
+
+ ret = win32_get_encrypted_file_size(blob->file_on_disk,
+ (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY),
+ &blob->size);
+ if (ret)
+ goto err;
+
+ strm = inode_add_stream(inode, STREAM_TYPE_EFSRPC_RAW_DATA,
+ NO_STREAM_NAME, blob);
+ if (!strm)
+ goto err_nomem;
+
+ prepare_unhashed_blob(blob, inode, strm->stream_id, unhashed_blobs);
+ return 0;
+
+err_nomem:
+ ret = WIMLIB_ERR_NOMEM;
+err:
+ free_blob_descriptor(blob);
+ return ret;
+}
+
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)
+get_data_stream_name(wchar_t *raw_stream_name, size_t raw_stream_name_nchars,
+ wchar_t **stream_name_ret, size_t *stream_name_nchars_ret)
{
const wchar_t *sep, *type, *end;
return true;
}
+/* Build the path to the stream. For unnamed streams, this is simply the path
+ * to the file. For named streams, this is the path to the file, followed by a
+ * colon, followed by the stream name. */
static wchar_t *
build_stream_path(const wchar_t *path, size_t path_nchars,
const wchar_t *stream_name, size_t stream_name_nchars)
}
static int
-winnt_scan_stream(const wchar_t *path, size_t path_nchars,
- const wchar_t *raw_stream_name, size_t raw_stream_name_nchars,
- u64 stream_size,
- struct wim_inode *inode, struct list_head *unhashed_streams)
+winnt_scan_data_stream(const wchar_t *path, size_t path_nchars,
+ wchar_t *raw_stream_name, size_t raw_stream_name_nchars,
+ u64 stream_size,
+ struct wim_inode *inode, struct list_head *unhashed_blobs)
{
- const wchar_t *stream_name;
+ wchar_t *stream_name;
size_t stream_name_nchars;
- struct wim_ads_entry *ads_entry;
- wchar_t *stream_path;
- struct wim_lookup_table_entry *lte;
- u32 stream_id;
+ struct blob_descriptor *blob;
+ struct wim_inode_stream *strm;
/* Given the raw stream name (which is something like
- * :streamname:$DATA), extract just the stream name part.
+ * :streamname:$DATA), extract just the stream name part (streamname).
* Ignore any non-$DATA streams. */
if (!get_data_stream_name(raw_stream_name, raw_stream_name_nchars,
&stream_name, &stream_name_nchars))
return 0;
- /* If this is a named stream, allocate an ADS entry for it. */
- if (stream_name_nchars) {
- ads_entry = inode_add_ads_utf16le(inode, stream_name,
- stream_name_nchars *
- sizeof(wchar_t));
- if (!ads_entry)
- return WIMLIB_ERR_NOMEM;
+ stream_name[stream_name_nchars] = L'\0';
+
+ /* If the stream is non-empty, set up a blob descriptor for it. */
+ if (stream_size != 0) {
+ blob = new_blob_descriptor();
+ if (!blob)
+ goto err_nomem;
+ blob->file_on_disk = build_stream_path(path,
+ path_nchars,
+ stream_name,
+ stream_name_nchars);
+ if (!blob->file_on_disk)
+ goto err_nomem;
+ blob->blob_location = BLOB_IN_WINNT_FILE_ON_DISK;
+ blob->size = stream_size;
+ blob->file_inode = inode;
} else {
- ads_entry = NULL;
+ blob = NULL;
}
- /* If the stream is empty, no lookup table entry is needed. */
- if (stream_size == 0)
- return 0;
-
- /* Build the path to the stream. For unnamed streams, this is simply
- * the path to the file. For named streams, this is the path to the
- * file, followed by a colon, followed by the stream name. */
- stream_path = build_stream_path(path, path_nchars,
- stream_name, stream_name_nchars);
- if (!stream_path)
- return WIMLIB_ERR_NOMEM;
-
- /* Set up the lookup table entry for the stream. */
- lte = new_lookup_table_entry();
- if (!lte) {
- FREE(stream_path);
- return WIMLIB_ERR_NOMEM;
- }
- 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;
- }
+ strm = inode_add_stream(inode, STREAM_TYPE_DATA, stream_name, blob);
+ if (!strm)
+ goto err_nomem;
- if (ads_entry) {
- stream_id = ads_entry->stream_id;
- ads_entry->lte = lte;
- } else {
- stream_id = 0;
- inode->i_lte = lte;
- }
- add_unhashed_stream(lte, inode, stream_id, unhashed_streams);
+ prepare_unhashed_blob(blob, inode, strm->stream_id, unhashed_blobs);
return 0;
+
+err_nomem:
+ free_blob_descriptor(blob);
+ return WIMLIB_ERR_NOMEM;
}
/*
- * Load information about the streams of an open file into a WIM inode.
+ * Load information about the data streams of an open file into a WIM inode.
*
* We use the NtQueryInformationFile() system call instead of FindFirstStream()
* and FindNextStream(). This is done for two reasons:
* already present in Windows XP.
*/
static int
-winnt_scan_streams(HANDLE *hFile_p, const wchar_t *path, size_t path_nchars,
- struct wim_inode *inode, struct list_head *unhashed_streams,
- u64 file_size, u32 vol_flags)
+winnt_scan_data_streams(HANDLE h, const wchar_t *path, size_t path_nchars,
+ struct wim_inode *inode, struct list_head *unhashed_blobs,
+ u64 file_size, u32 vol_flags)
{
int ret;
u8 _buf[1024] _aligned_attribute(8);
size_t bufsize;
IO_STATUS_BLOCK iosb;
NTSTATUS status;
- const FILE_STREAM_INFORMATION *info;
+ FILE_STREAM_INFORMATION *info;
buf = _buf;
bufsize = sizeof(_buf);
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,
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;
}
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;
+ info = (FILE_STREAM_INFORMATION *)buf;
for (;;) {
/* Load the stream information. */
- ret = winnt_scan_stream(path, path_nchars,
- info->StreamName,
- info->StreamNameLength / 2,
- info->StreamSize.QuadPart,
- inode, unhashed_streams);
+ ret = winnt_scan_data_stream(path, path_nchars,
+ info->StreamName,
+ info->StreamNameLength / 2,
+ info->StreamSize.QuadPart,
+ inode, unhashed_blobs);
if (ret)
goto out_free_buf;
break;
}
/* Advance to next stream information. */
- info = (const FILE_STREAM_INFORMATION *)
- ((const u8 *)info + info->NextEntryOffset);
+ info = (FILE_STREAM_INFORMATION *)
+ ((u8 *)info + info->NextEntryOffset);
}
ret = 0;
goto out_free_buf;
goto out_free_buf;
}
- ret = winnt_scan_stream(path, path_nchars, L"::$DATA", 7,
- file_size, inode, unhashed_streams);
+ {
+ wchar_t stream_name[] = L"::$DATA";
+ ret = winnt_scan_data_stream(path, path_nchars, stream_name, 7,
+ file_size, inode, unhashed_blobs);
+ }
out_free_buf:
/* Free buffer if allocated on heap. */
if (unlikely(buf != _buf))
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 = 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. */
+ 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))) {
- 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;
+ goto out;
+ }
+ 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;
}
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. */
{
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;
}
{
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;
}
}
- /* If this is a reparse point, read the reparse data. */
- if (unlikely(file_info.BasicInformation.FileAttributes &
- FILE_ATTRIBUTE_REPARSE_POINT))
- {
- rpbuf = alloca(REPARSE_POINT_MAX_SIZE);
- 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;
- case RP_NOT_FIXED:
- not_rpfixed = 1;
- break;
- default:
- ERROR_WITH_ERRNO("\"%ls\": Can't get reparse data",
- printable_path(full_path));
- goto out;
- }
- }
-
/* Create a WIM dentry with an associated inode, which may be shared.
*
* 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 (inode->i_nlink > 1) {
/* Shared inode (hard link); skip reading per-inode information.
*/
- ret = 0;
goto out_progress;
}
inode->i_creation_time = file_info.BasicInformation.CreationTime.QuadPart;
inode->i_last_write_time = file_info.BasicInformation.LastWriteTime.QuadPart;
inode->i_last_access_time = file_info.BasicInformation.LastAccessTime.QuadPart;
- inode->i_resolved = 1;
/* Get the file's security descriptor, unless we are capturing in
* NO_ACLS mode or the volume does not support security descriptors. */
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"PRIu32")",
- 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;
}
}
- /* Load information about the unnamed data stream and any named data
- * streams. */
- ret = winnt_scan_streams(&h,
- full_path,
- full_path_nchars,
- inode,
- params->unhashed_streams,
- file_info.StandardInformation.EndOfFile.QuadPart,
- vol_flags);
- if (ret)
- goto out;
-
+ /* If this is a reparse point, load the reparse data. */
if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
+ if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) {
+ /* See comment above assign_stream_types_encrypted() */
+ WARNING("Ignoring reparse data of encrypted file \"%ls\"",
+ printable_path(full_path));
+ } else {
+ u8 rpbuf[REPARSE_POINT_MAX_SIZE] _aligned_attribute(8);
+ u16 rpbuflen;
+
+ ret = winnt_get_reparse_data(h, full_path, params,
+ rpbuf, &rpbuflen);
+ switch (ret) {
+ case RP_FIXED:
+ inode->i_not_rpfixed = 0;
+ break;
+ case RP_NOT_FIXED:
+ inode->i_not_rpfixed = 1;
+ break;
+ default:
+ goto out;
+ }
+ inode->i_reparse_tag = le32_to_cpu(*(le32*)rpbuf);
+ if (!inode_add_stream_with_data(inode,
+ STREAM_TYPE_REPARSE_POINT,
+ NO_STREAM_NAME,
+ rpbuf + REPARSE_DATA_OFFSET,
+ rpbuflen - REPARSE_DATA_OFFSET,
+ params->blob_table))
+ {
+ ret = WIMLIB_ERR_NOMEM;
+ goto out;
+ }
+ }
+ }
- /* 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_efsrpc_raw_data(inode, full_path,
+ params->unhashed_blobs);
+ if (ret)
+ goto out;
+ } else {
+ /*
+ * Load information about data streams (unnamed and named).
+ *
+ * Skip this step for encrypted files, since the data from
+ * ReadEncryptedFileRaw() already contains all data streams (and
+ * they do in fact all get restored by WriteEncryptedFileRaw().)
+ *
+ * Note: WIMGAPI (as of Windows 8.1) gets wrong and stores both
+ * the EFSRPC data and the named data stream(s)...!
+ */
+ ret = winnt_scan_data_streams(h,
+ full_path,
+ full_path_nchars,
+ inode,
+ params->unhashed_blobs,
+ file_info.StandardInformation.EndOfFile.QuadPart,
+ vol_flags);
if (ret)
goto out;
- } else if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
+ }
+
+ if (inode_is_directory(inode)) {
/* 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,
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;
}
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
- free_dentry_tree(root, params->lookup_table);
+ if (unlikely(ret)) {
+ free_dentry_tree(root, params->blob_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;
- 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
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);