*/
/*
- * 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
# include "config.h"
#endif
-#include <errno.h>
-
#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"
}
/* 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,
+ const struct read_blob_callbacks *cbs)
{
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;
}
bytes_read = iosb.Information;
bytes_remaining -= bytes_read;
- ret = (*cb)(buf, bytes_read, cb_ctx);
+ ret = call_consume_chunk(buf, bytes_read, cbs);
if (ret)
break;
}
}
struct win32_encrypted_read_ctx {
- consume_data_callback_t read_prefix_cb;
- void *read_prefix_ctx;
+ const struct read_blob_callbacks *cbs;
int wimlib_err_code;
u64 bytes_remaining;
};
if (bytes_to_consume == 0)
return ERROR_SUCCESS;
- ret = (*ctx->read_prefix_cb)(data, bytes_to_consume, ctx->read_prefix_ctx);
+ ret = call_consume_chunk(data, bytes_to_consume, ctx->cbs);
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)
+ const struct read_blob_callbacks *cbs)
{
struct win32_encrypted_read_ctx export_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.cbs = cbs;
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 "
"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 short name of a file into a WIM dentry.
*/
-static NTSTATUS
+static noinline_for_stack NTSTATUS
winnt_get_short_name(HANDLE h, struct wim_dentry *dentry)
{
/* It's not any harder to just make the NtQueryInformationFile() system
FileAlternateNameInformation);
info = (const FILE_NAME_INFORMATION *)buf;
if (NT_SUCCESS(status) && info->FileNameLength != 0) {
- dentry->short_name = utf16le_dupz(info->FileName,
- info->FileNameLength);
- if (!dentry->short_name)
+ dentry->d_short_name = utf16le_dupz(info->FileName,
+ info->FileNameLength);
+ if (!dentry->d_short_name)
return STATUS_NO_MEMORY;
- dentry->short_name_nbytes = info->FileNameLength;
+ dentry->d_short_name_nbytes = info->FileNameLength;
}
return status;
}
* Load the security descriptor of a file into the corresponding inode and the
* WIM image's security descriptor set.
*/
-static NTSTATUS
+static noinline_for_stack NTSTATUS
winnt_get_security_descriptor(HANDLE h, struct wim_inode *inode,
struct wim_sd_set *sd_set,
struct winnt_scan_stats *stats, int add_flags)
{
const FILE_NAMES_INFORMATION *info = buf;
for (;;) {
- if (!(info->FileNameLength == 2 && info->FileName[0] == L'.') &&
- !(info->FileNameLength == 4 && info->FileName[0] == L'.' &&
- info->FileName[1] == L'.'))
+ if (!should_ignore_filename(info->FileName,
+ info->FileNameLength / 2))
{
wchar_t *p;
wchar_t *filename;
}
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 will be captured literally (no fixup) */
- RP_NOT_FIXED = -1,
-
- /* Reparse point will be captured with fixup */
- RP_FIXED = -2,
-};
+#define RP_FIXED (-1)
static bool
file_has_ino_and_dev(HANDLE h, u64 ino, u64 dev)
}
/*
- * Given an (expected) NT namespace symbolic link or junction target @target of
- * length @target_nbytes, determine if a prefix of the target points to a file
- * identified by @capture_root_ino and @capture_root_dev.
- *
- * If yes, return a pointer to the portion of the link following this prefix.
- *
- * If no, return NULL.
- *
- * If the link target does not appear to be a valid NT namespace path, return
- * @target itself.
+ * This is the Windows equivalent of unix_relativize_link_target(); see there
+ * for general details. This version works with an "absolute" Windows link
+ * target, specified from the root of the Windows kernel object namespace. Note
+ * that we have to open directories with a trailing slash when present because
+ * \??\E: opens the E: device itself and not the filesystem root directory.
*/
static const wchar_t *
-winnt_get_root_relative_target(const wchar_t *target, size_t target_nbytes,
- u64 capture_root_ino, u64 capture_root_dev)
+winnt_relativize_link_target(const wchar_t *target, size_t target_nbytes,
+ u64 ino, u64 dev)
{
UNICODE_STRING name;
OBJECT_ATTRIBUTES attr;
name.Buffer = (wchar_t *)p;
name.Length = 0;
- if (file_has_ino_and_dev(h, capture_root_ino,
- capture_root_dev))
+ if (file_has_ino_and_dev(h, ino, dev))
goto out_close_root_dir;
}
} while (p != target_end);
- p = NULL;
+ p = target;
out_close_root_dir:
if (attr.RootDirectory)
(*func_NtClose)(attr.RootDirectory);
+ while (p > target && *(p - 1) == L'\\')
+ p--;
return p;
}
static int
winnt_rpfix_progress(struct capture_params *params, const wchar_t *path,
- const struct reparse_data *rpdata, int scan_status)
+ const struct link_reparse_point *link, int scan_status)
{
- size_t print_name_nchars = rpdata->print_name_nbytes / sizeof(wchar_t);
+ size_t print_name_nchars = link->print_name_nbytes / sizeof(wchar_t);
wchar_t print_name0[print_name_nchars + 1];
- wmemcpy(print_name0, rpdata->print_name, print_name_nchars);
+ wmemcpy(print_name0, link->print_name, print_name_nchars);
print_name0[print_name_nchars] = L'\0';
params->progress.scan.cur_path = printable_path(path);
}
static int
-winnt_try_rpfix(u8 *rpbuf, u16 *rpbuflen_p,
- u64 capture_root_ino, u64 capture_root_dev,
+winnt_try_rpfix(struct reparse_buffer_disk *rpbuf, u16 *rpbuflen_p,
const wchar_t *path, struct capture_params *params)
{
- struct reparse_data rpdata;
+ struct link_reparse_point link;
const wchar_t *rel_target;
int ret;
- if (parse_reparse_data(rpbuf, *rpbuflen_p, &rpdata)) {
- /* Couldn't even understand the reparse data. Don't try the
- * fixup. */
- return RP_NOT_FIXED;
+ if (parse_link_reparse_point(rpbuf, *rpbuflen_p, &link)) {
+ /* Couldn't understand the reparse data; don't do the fixup. */
+ return 0;
}
/*
* - \??\Volume{c47cb07c-946e-4155-b8f7-052e9cec7628}\Users\Public
* - \DosDevices\Volume{c47cb07c-946e-4155-b8f7-052e9cec7628}\Users\Public
*/
- if (rpdata.rptag == WIM_IO_REPARSE_TAG_SYMLINK &&
- (rpdata.rpflags & SYMBOLIC_LINK_RELATIVE))
- return RP_NOT_FIXED;
-
- rel_target = winnt_get_root_relative_target(rpdata.substitute_name,
- rpdata.substitute_name_nbytes,
- capture_root_ino,
- capture_root_dev);
- if (!rel_target) {
- /* Target points outside of the tree being captured. Don't
- * adjust it. */
- ret = winnt_rpfix_progress(params, path, &rpdata,
- WIMLIB_SCAN_DENTRY_NOT_FIXED_SYMLINK);
- if (ret)
- return ret;
- return RP_NOT_FIXED;
- }
+ if (link_is_relative_symlink(&link))
+ return 0;
+
+ rel_target = winnt_relativize_link_target(link.substitute_name,
+ link.substitute_name_nbytes,
+ params->capture_root_ino,
+ params->capture_root_dev);
- if (rel_target == rpdata.substitute_name) {
- /* Weird target --- keep the reparse point and don't mess with
- * it. */
- return RP_NOT_FIXED;
+ if (rel_target == link.substitute_name) {
+ /* Target points outside of the tree being captured or had an
+ * unrecognized path format. Don't adjust it. */
+ return winnt_rpfix_progress(params, path, &link,
+ WIMLIB_SCAN_DENTRY_NOT_FIXED_SYMLINK);
}
/* We have an absolute target pointing within the directory being
*
* We will cut off the prefix before this part (which is the path to the
* directory being captured) and add a dummy prefix. Since the process
- * will need to be reversed when applying the image, it shouldn't matter
+ * will need to be reversed when applying the image, it doesn't matter
* what exactly the prefix is, as long as it looks like an absolute
- * path.
- */
+ * path. */
- {
- size_t rel_target_nbytes =
- rpdata.substitute_name_nbytes - ((const u8 *)rel_target -
- (const u8 *)rpdata.substitute_name);
- size_t rel_target_nchars = rel_target_nbytes / sizeof(wchar_t);
+ static const wchar_t prefix[6] = L"\\??\\X:";
+ static const size_t num_unprintable_chars = 4;
- wchar_t tmp[rel_target_nchars + 7];
+ size_t rel_target_nbytes =
+ link.substitute_name_nbytes - ((const u8 *)rel_target -
+ (const u8 *)link.substitute_name);
- wmemcpy(tmp, L"\\??\\X:\\", 7);
- wmemcpy(tmp + 7, rel_target, rel_target_nchars);
+ wchar_t tmp[(sizeof(prefix) + rel_target_nbytes) / sizeof(wchar_t)];
- rpdata.substitute_name = tmp;
- rpdata.substitute_name_nbytes = rel_target_nbytes + (7 * sizeof(wchar_t));
- rpdata.print_name = tmp + 4;
- rpdata.print_name_nbytes = rel_target_nbytes + (3 * sizeof(wchar_t));
+ memcpy(tmp, prefix, sizeof(prefix));
+ memcpy(tmp + ARRAY_LEN(prefix), rel_target, rel_target_nbytes);
- if (make_reparse_buffer(&rpdata, rpbuf, rpbuflen_p))
- return RP_NOT_FIXED;
- }
- ret = winnt_rpfix_progress(params, path, &rpdata,
+ link.substitute_name = tmp;
+ link.substitute_name_nbytes = sizeof(tmp);
+
+ link.print_name = link.substitute_name + num_unprintable_chars;
+ link.print_name_nbytes = link.substitute_name_nbytes -
+ (num_unprintable_chars * sizeof(wchar_t));
+
+ if (make_link_reparse_point(&link, rpbuf, rpbuflen_p))
+ return 0;
+
+ ret = winnt_rpfix_progress(params, path, &link,
WIMLIB_SCAN_DENTRY_FIXED_SYMLINK);
if (ret)
return ret;
return RP_FIXED;
}
-/*
- * Loads the reparse point data from a reparse point into memory, optionally
- * fixing the targets of absolute symbolic links and junction points to be
- * relative to the root of capture.
- *
- * @h:
- * Open handle to the reparse point file.
- * @path:
- * Path to the reparse point file.
- * @params:
- * Capture parameters. add_flags, capture_root_ino, capture_root_dev,
- * progfunc, progctx, and progress are used.
- * @rpbuf:
- * Buffer of length at least REPARSE_POINT_MAX_SIZE bytes into which the
- * reparse point buffer will be loaded.
- * @rpbuflen_ret:
- * On success, the length of the reparse point buffer in bytes is written
- * to this location.
- *
- * 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 capture_params *params,
- u8 *rpbuf, u16 *rpbuflen_ret)
+/* Load the reparse data of a file into the corresponding WIM inode. If the
+ * reparse point is a symbolic link or junction with an absolute target and
+ * RPFIX mode is enabled, then also rewrite its target to be relative to the
+ * capture root. */
+static noinline_for_stack int
+winnt_load_reparse_data(HANDLE h, struct wim_inode *inode,
+ const wchar_t *full_path, struct capture_params *params)
{
+ struct reparse_buffer_disk rpbuf;
DWORD bytes_returned;
- u32 reparse_tag;
- int ret;
u16 rpbuflen;
+ int ret;
+
+ 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));
+ return 0;
+ }
if (!DeviceIoControl(h, FSCTL_GET_REPARSE_POINT,
- NULL, 0, rpbuf, REPARSE_POINT_MAX_SIZE,
+ NULL, 0, &rpbuf, REPARSE_POINT_MAX_SIZE,
&bytes_returned, NULL))
{
- set_errno_from_GetLastError();
- return WIMLIB_ERR_READ;
+ win32_error(GetLastError(), L"\"%ls\": Can't get reparse point",
+ printable_path(full_path));
+ return WIMLIB_ERR_READLINK;
}
- if (unlikely(bytes_returned < 8)) {
- errno = EINVAL;
+ rpbuflen = bytes_returned;
+
+ if (unlikely(rpbuflen < REPARSE_DATA_OFFSET)) {
+ ERROR("\"%ls\": reparse point buffer is too short",
+ printable_path(full_path));
return WIMLIB_ERR_INVALID_REPARSE_DATA;
}
- rpbuflen = bytes_returned;
- reparse_tag = le32_to_cpu(*(le32*)rpbuf);
- ret = RP_NOT_FIXED;
- if (params->add_flags & WIMLIB_ADD_FLAG_RPFIX &&
- (reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
- reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT))
- {
- ret = winnt_try_rpfix(rpbuf, &rpbuflen,
- params->capture_root_ino,
- params->capture_root_dev,
- path, params);
+ if (params->add_flags & WIMLIB_ADD_FLAG_RPFIX) {
+ ret = winnt_try_rpfix(&rpbuf, &rpbuflen, full_path, params);
+ if (ret == RP_FIXED)
+ inode->i_rp_flags &= ~WIM_RP_FLAG_NOT_FIXED;
+ else if (ret)
+ return ret;
}
- *rpbuflen_ret = rpbuflen;
- return ret;
+
+ inode->i_reparse_tag = le32_to_cpu(rpbuf.rptag);
+ inode->i_rp_reserved = le16_to_cpu(rpbuf.rpreserved);
+
+ if (!inode_add_stream_with_data(inode,
+ STREAM_TYPE_REPARSE_POINT,
+ NO_STREAM_NAME,
+ rpbuf.rpdata,
+ rpbuflen - REPARSE_DATA_OFFSET,
+ params->blob_table))
+ return WIMLIB_ERR_NOMEM;
+
+ return 0;
}
static DWORD WINAPI
}
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_scan_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;
+
+ /* Empty EFSRPC data does not make sense */
+ wimlib_assert(blob->size != 0);
+
+ 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 data 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)
+build_data_stream_path(const wchar_t *path, size_t path_nchars,
+ const wchar_t *stream_name, size_t stream_name_nchars)
{
size_t stream_path_nchars;
wchar_t *stream_path;
}
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;
- } else if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
- /* Ignore unnamed data stream of reparse point */
- return 0;
+ 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_data_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;
- }
- lte->file_inode = inode;
- 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:
* and later, whereas the stream support in NtQueryInformationFile() was
* 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)
+static noinline_for_stack int
+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);
+ u8 _buf[4096] _aligned_attribute(8);
u8 *buf;
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))
return ret;
}
+static noinline_for_stack u64
+get_sort_key(HANDLE h)
+{
+ STARTING_VCN_INPUT_BUFFER in = { .StartingVcn.QuadPart = 0 };
+ RETRIEVAL_POINTERS_BUFFER out;
+ DWORD bytesReturned;
+
+ if (!DeviceIoControl(h, FSCTL_GET_RETRIEVAL_POINTERS,
+ &in, sizeof(in),
+ &out, sizeof(out),
+ &bytesReturned, NULL))
+ return 0;
+
+ if (out.ExtentCount < 1)
+ return 0;
+
+ return out.Extents[0].Lcn.QuadPart;
+}
+
+static void
+set_sort_key(struct wim_inode *inode, u64 sort_key)
+{
+ for (unsigned i = 0; i < inode->i_num_streams; i++) {
+ struct wim_inode_stream *strm = &inode->i_streams[i];
+ struct blob_descriptor *blob = stream_blob_resolved(strm);
+ if (blob && (blob->blob_location == BLOB_IN_WINNT_FILE_ON_DISK ||
+ blob->blob_location == BLOB_WIN32_ENCRYPTED))
+ blob->sort_key = sort_key;
+ }
+}
+
+static noinline_for_stack u32
+get_volume_information(HANDLE h, const wchar_t *full_path,
+ struct capture_params *params)
+{
+ FILE_FS_ATTRIBUTE_INFORMATION attr_info;
+ FILE_FS_VOLUME_INFORMATION vol_info;
+ IO_STATUS_BLOCK iosb;
+ NTSTATUS status;
+ u32 vol_flags;
+
+ /* Get volume flags */
+ status = (*func_NtQueryVolumeInformationFile)(h, &iosb,
+ &attr_info,
+ sizeof(attr_info),
+ FileFsAttributeInformation);
+ if (likely((NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW) &&
+ (iosb.Information >=
+ offsetof(FILE_FS_ATTRIBUTE_INFORMATION,
+ FileSystemAttributes) +
+ sizeof(attr_info.FileSystemAttributes))))
+ {
+ vol_flags = attr_info.FileSystemAttributes;
+ } else {
+ winnt_warning(status, L"\"%ls\": Can't get volume attributes",
+ printable_path(full_path));
+ vol_flags = 0;
+ }
+
+ /* Get volume ID. */
+ status = (*func_NtQueryVolumeInformationFile)(h, &iosb,
+ &vol_info,
+ sizeof(vol_info),
+ FileFsVolumeInformation);
+ if (likely((NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW) &&
+ (iosb.Information >=
+ offsetof(FILE_FS_VOLUME_INFORMATION,
+ VolumeSerialNumber) +
+ sizeof(vol_info.VolumeSerialNumber))))
+ {
+ params->capture_root_dev = vol_info.VolumeSerialNumber;
+ } else {
+ winnt_warning(status, L"\"%ls\": Can't get volume ID",
+ printable_path(full_path));
+ params->capture_root_dev = 0;
+ }
+ return vol_flags;
+}
+
+struct file_info {
+ u32 attributes;
+ u32 num_links;
+ u64 creation_time;
+ u64 last_write_time;
+ u64 last_access_time;
+ u64 ino;
+ u64 end_of_file;
+};
+
+static noinline_for_stack NTSTATUS
+get_file_info(HANDLE h, struct file_info *info)
+{
+ IO_STATUS_BLOCK iosb;
+ NTSTATUS status;
+ FILE_ALL_INFORMATION all_info;
+
+ status = (*func_NtQueryInformationFile)(h, &iosb, &all_info,
+ sizeof(all_info),
+ FileAllInformation);
+
+ if (unlikely(!NT_SUCCESS(status) && status != STATUS_BUFFER_OVERFLOW))
+ return status;
+
+ info->attributes = all_info.BasicInformation.FileAttributes;
+ info->num_links = all_info.StandardInformation.NumberOfLinks;
+ info->creation_time = all_info.BasicInformation.CreationTime.QuadPart;
+ info->last_write_time = all_info.BasicInformation.LastWriteTime.QuadPart;
+ info->last_access_time = all_info.BasicInformation.LastAccessTime.QuadPart;
+ info->ino = all_info.InternalInformation.IndexNumber.QuadPart;
+ info->end_of_file = all_info.StandardInformation.EndOfFile.QuadPart;
+ return STATUS_SUCCESS;
+}
+
static int
winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
HANDLE cur_dir,
{
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;
+ struct file_info file_info;
ACCESS_MASK requestedPerms;
+ u64 sort_key;
- ret = try_exclude(full_path, full_path_nchars, params);
- if (ret < 0) /* Excluded? */
+ ret = try_exclude(full_path, params);
+ if (unlikely(ret < 0)) /* Excluded? */
goto out_progress;
- if (ret > 0) /* Error? */
+ if (unlikely(ret > 0)) /* Error? */
goto out;
/* Open the file. */
goto retry_open;
}
- if (status == STATUS_FVE_LOCKED_VOLUME) {
- ERROR("\"%ls\": Can't open file "
- "(encrypted volume has not been unlocked)",
- printable_path(full_path));
+ 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;
- 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;
+ else
+ ret = WIMLIB_ERR_OPEN;
goto out;
}
/* Get information about the file. */
- {
- IO_STATUS_BLOCK iosb;
-
- status = (*func_NtQueryInformationFile)(h, &iosb,
- &file_info,
- sizeof(file_info),
- FileAllInformation);
-
- 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);
- ret = WIMLIB_ERR_STAT;
- goto out;
- }
+ status = get_file_info(h, &file_info);
+ if (!NT_SUCCESS(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))
+ !(file_info.attributes & FILE_ATTRIBUTE_ENCRYPTED))
{
ERROR("\"%ls\": Permission to read data was denied",
printable_path(full_path));
}
if (unlikely(!cur_dir)) {
-
/* Root of tree being captured; get volume information. */
-
- FILE_FS_ATTRIBUTE_INFORMATION attr_info;
- FILE_FS_VOLUME_INFORMATION vol_info;
- IO_STATUS_BLOCK iosb;
-
- /* Get volume flags */
- status = (*func_NtQueryVolumeInformationFile)(h, &iosb,
- &attr_info,
- sizeof(attr_info),
- FileFsAttributeInformation);
- if (likely((NT_SUCCESS(status) ||
- (status == STATUS_BUFFER_OVERFLOW)) &&
- (iosb.Information >=
- offsetof(FILE_FS_ATTRIBUTE_INFORMATION,
- FileSystemAttributes) +
- sizeof(attr_info.FileSystemAttributes))))
- {
- 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);
- vol_flags = 0;
- }
-
- /* Set inode number of root directory */
- params->capture_root_ino =
- file_info.InternalInformation.IndexNumber.QuadPart;
-
- /* Get volume ID. */
- status = (*func_NtQueryVolumeInformationFile)(h, &iosb,
- &vol_info,
- sizeof(vol_info),
- FileFsVolumeInformation);
- if (likely((NT_SUCCESS(status) ||
- (status == STATUS_BUFFER_OVERFLOW)) &&
- (iosb.Information >=
- offsetof(FILE_FS_VOLUME_INFORMATION,
- VolumeSerialNumber) +
- sizeof(vol_info.VolumeSerialNumber))))
- {
- 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);
- params->capture_root_dev = 0;
- }
+ vol_flags = get_volume_information(h, full_path, params);
+ params->capture_root_ino = file_info.ino;
}
- /* 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_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.
*
* files on different volumes. */
ret = inode_table_new_dentry(params->inode_table,
filename,
- file_info.InternalInformation.IndexNumber.QuadPart,
+ file_info.ino,
params->capture_root_dev,
- (file_info.StandardInformation.NumberOfLinks <= 1 ||
- (file_info.BasicInformation.FileAttributes &
- FILE_ATTRIBUTE_DIRECTORY)),
+ (file_info.num_links <= 1 ||
+ (file_info.attributes & FILE_ATTRIBUTE_DIRECTORY)),
&root);
if (ret)
goto out;
goto out_progress;
}
- inode->i_attributes = file_info.BasicInformation.FileAttributes;
- 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;
+ inode->i_attributes = file_info.attributes;
+ inode->i_creation_time = file_info.creation_time;
+ inode->i_last_write_time = file_info.last_write_time;
+ inode->i_last_access_time = file_info.last_access_time;
/* 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"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;
}
}
- /* 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)) {
+ ret = winnt_load_reparse_data(h, inode, full_path, params);
+ if (ret)
+ goto out;
+ }
- /* Reparse point: set the reparse data (already read). */
+ sort_key = get_sort_key(h);
- 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_scan_efsrpc_raw_data(inode, full_path,
+ params->unhashed_blobs);
if (ret)
goto out;
- } else if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
+ } 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.end_of_file,
+ vol_flags);
+ if (ret)
+ goto out;
+ }
+
+ set_sort_key(inode, sort_key);
+
+ 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 (unlikely(ret)) {
- free_dentry_tree(root, params->lookup_table);
+ free_dentry_tree(root, params->blob_table);
root = NULL;
ret = report_capture_error(params, ret, full_path);
}