#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/object_id.h"
#include "wimlib/paths.h"
#include "wimlib/reparse.h"
+#include "wimlib/scan.h"
#include "wimlib/win32_vss.h"
#include "wimlib/wof.h"
struct winnt_scan_ctx {
- struct capture_params *params;
+ struct scan_params *params;
bool is_ntfs;
u32 vol_flags;
unsigned long num_get_sd_access_denied;
}
/*
- * If cur_dir is not NULL, open an existing file relative to the already-open
- * directory cur_dir.
- *
- * Otherwise, open the file specified by @path, which must be a Windows NT
- * namespace path.
+ * Open the file named by the NT namespace path @path of length @path_nchars
+ * characters. If @cur_dir is not NULL then the path is given relative to
+ * @cur_dir; otherwise the path is absolute. @perms is the access mask of
+ * permissions to request on the handle. SYNCHRONIZE permision is always added.
*/
static NTSTATUS
winnt_openat(HANDLE cur_dir, const wchar_t *path, size_t path_nchars,
ACCESS_MASK perms, HANDLE *h_ret)
{
- UNICODE_STRING name;
- OBJECT_ATTRIBUTES attr;
+ UNICODE_STRING name = {
+ .Length = path_nchars * sizeof(wchar_t),
+ .MaximumLength = path_nchars * sizeof(wchar_t),
+ .Buffer = (wchar_t *)path,
+ };
+ OBJECT_ATTRIBUTES attr = {
+ .Length = sizeof(attr),
+ .RootDirectory = cur_dir,
+ .ObjectName = &name,
+ };
IO_STATUS_BLOCK iosb;
NTSTATUS status;
+ ULONG options = FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT;
- name.Length = path_nchars * sizeof(wchar_t);
- name.MaximumLength = name.Length;
- name.Buffer = (wchar_t *)path;
-
- attr.Length = sizeof(attr);
- attr.RootDirectory = cur_dir;
- attr.ObjectName = &name;
- attr.Attributes = 0;
- attr.SecurityDescriptor = NULL;
- attr.SecurityQualityOfService = NULL;
-
+ perms |= SYNCHRONIZE;
+ if (perms & (FILE_READ_DATA | FILE_LIST_DIRECTORY)) {
+ options |= FILE_SYNCHRONOUS_IO_NONALERT;
+ options |= FILE_SEQUENTIAL_ONLY;
+ }
retry:
- status = (*func_NtOpenFile)(h_ret, perms, &attr, &iosb,
- FILE_SHARE_VALID_FLAGS,
- FILE_OPEN_REPARSE_POINT |
- FILE_OPEN_FOR_BACKUP_INTENT |
- FILE_SYNCHRONOUS_IO_NONALERT |
- FILE_SEQUENTIAL_ONLY);
+ status = NtOpenFile(h_ret, perms, &attr, &iosb,
+ FILE_SHARE_VALID_FLAGS, options);
if (!NT_SUCCESS(status)) {
/* Try requesting fewer permissions */
if (status == STATUS_ACCESS_DENIED ||
static int
read_winnt_stream_prefix(const struct windows_file *file,
- u64 size, const struct read_blob_callbacks *cbs)
+ u64 size, const struct consume_chunk_callback *cb)
{
IO_STATUS_BLOCK iosb;
UNICODE_STRING name = {
u64 bytes_remaining;
int ret;
- status = (*func_NtOpenFile)(&h, FILE_READ_DATA | SYNCHRONIZE,
- &attr, &iosb,
- FILE_SHARE_VALID_FLAGS,
- FILE_OPEN_REPARSE_POINT |
- FILE_OPEN_FOR_BACKUP_INTENT |
- FILE_SYNCHRONOUS_IO_NONALERT |
- FILE_SEQUENTIAL_ONLY |
- (file->is_file_id ? FILE_OPEN_BY_FILE_ID : 0));
+ status = NtOpenFile(&h, FILE_READ_DATA | SYNCHRONIZE,
+ &attr, &iosb,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_OPEN_REPARSE_POINT |
+ FILE_OPEN_FOR_BACKUP_INTENT |
+ FILE_SYNCHRONOUS_IO_NONALERT |
+ FILE_SEQUENTIAL_ONLY |
+ (file->is_file_id ? FILE_OPEN_BY_FILE_ID : 0));
if (unlikely(!NT_SUCCESS(status))) {
if (status == STATUS_SHARING_VIOLATION) {
ERROR("Can't open %ls for reading:\n"
IO_STATUS_BLOCK iosb;
ULONG count;
ULONG bytes_read;
+ const unsigned max_tries = 5;
+ unsigned tries_remaining = max_tries;
count = min(sizeof(buf), bytes_remaining);
- status = (*func_NtReadFile)(h, NULL, NULL, NULL,
- &iosb, buf, count, NULL, NULL);
+ retry_read:
+ status = NtReadFile(h, NULL, NULL, NULL,
+ &iosb, buf, count, NULL, NULL);
if (unlikely(!NT_SUCCESS(status))) {
if (status == STATUS_END_OF_FILE) {
ERROR("%ls: File was concurrently truncated",
windows_file_to_string(file, buf, sizeof(buf)));
ret = WIMLIB_ERR_CONCURRENT_MODIFICATION_DETECTED;
} else {
- winnt_error(status, L"Error reading data from %ls",
- windows_file_to_string(file, buf, sizeof(buf)));
+ winnt_warning(status, L"Error reading data from %ls",
+ windows_file_to_string(file, buf, sizeof(buf)));
+
+ /* Currently these retries are purely a guess;
+ * there is no reproducible problem that they solve. */
+ if (--tries_remaining) {
+ int delay = 100;
+ if (status == STATUS_INSUFFICIENT_RESOURCES ||
+ status == STATUS_NO_MEMORY) {
+ delay *= 25;
+ }
+ WARNING("Retrying after %dms...", delay);
+ Sleep(delay);
+ goto retry_read;
+ }
+ ERROR("Too many retries; returning failure");
ret = WIMLIB_ERR_READ;
}
break;
+ } else if (unlikely(tries_remaining != max_tries)) {
+ WARNING("A read request had to be retried multiple times "
+ "before it succeeded!");
}
bytes_read = iosb.Information;
bytes_remaining -= bytes_read;
- ret = call_consume_chunk(buf, bytes_read, cbs);
+ ret = consume_chunk(cb, buf, bytes_read);
if (ret)
break;
}
- (*func_NtClose)(h);
+ NtClose(h);
return ret;
}
struct win32_encrypted_read_ctx {
- const struct read_blob_callbacks *cbs;
+ const struct consume_chunk_callback *cb;
int wimlib_err_code;
u64 bytes_remaining;
};
if (bytes_to_consume == 0)
return ERROR_SUCCESS;
- ret = call_consume_chunk(data, bytes_to_consume, ctx->cbs);
+ ret = consume_chunk(ctx->cb, data, bytes_to_consume);
if (ret) {
ctx->wimlib_err_code = ret;
/* It doesn't matter what error code is returned here, as long
static int
read_win32_encrypted_file_prefix(const wchar_t *path, bool is_dir, u64 size,
- const struct read_blob_callbacks *cbs)
+ const struct consume_chunk_callback *cb)
{
struct win32_encrypted_read_ctx export_ctx;
DWORD err;
if (is_dir)
flags |= CREATE_FOR_DIR;
- export_ctx.cbs = cbs;
+ export_ctx.cb = cb;
export_ctx.wimlib_err_code = 0;
export_ctx.bytes_remaining = size;
* described by @blob. */
int
read_windows_file_prefix(const struct blob_descriptor *blob, u64 size,
- const struct read_blob_callbacks *cbs)
+ const struct consume_chunk_callback *cb)
{
const struct windows_file *file = blob->windows_file;
if (unlikely(file->is_encrypted)) {
bool is_dir = (blob->file_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY);
- return read_win32_encrypted_file_prefix(file->path, is_dir, size, cbs);
+ return read_win32_encrypted_file_prefix(file->path, is_dir, size, cb);
}
- return read_winnt_stream_prefix(file, size, cbs);
+ return read_winnt_stream_prefix(file, size, cb);
}
/*
u8 buf[128] _aligned_attribute(8);
const FILE_NAME_INFORMATION *info;
- status = (*func_NtQueryInformationFile)(h, &iosb, buf, sizeof(buf),
- FileAlternateNameInformation);
+ status = NtQueryInformationFile(h, &iosb, buf, sizeof(buf),
+ FileAlternateNameInformation);
info = (const FILE_NAME_INFORMATION *)buf;
if (NT_SUCCESS(status) && info->FileNameLength != 0) {
dentry->d_short_name = utf16le_dupz(info->FileName,
* ntdll function and therefore not officially part of the Win32 API.
* Oh well.
*/
- while (!(NT_SUCCESS(status = (*func_NtQuerySecurityObject)(h,
- requestedInformation,
- (PSECURITY_DESCRIPTOR)buf,
- bufsize,
- &len_needed))))
+ while (!NT_SUCCESS(status = NtQuerySecurityObject(h,
+ requestedInformation,
+ (PSECURITY_DESCRIPTOR)buf,
+ bufsize,
+ &len_needed)))
{
switch (status) {
case STATUS_BUFFER_TOO_SMALL:
}
}
+ /* We can get a length of 0 with Samba. Assume that means "no security
+ * descriptor". */
+ if (len_needed == 0)
+ goto out;
+
/* Add the security descriptor to the WIM image, and save its ID in
- * file's inode. */
+ * the file's inode. */
inode->i_security_id = sd_set_add_sd(ctx->params->sd_set, buf, len_needed);
if (unlikely(inode->i_security_id < 0))
status = STATUS_NO_MEMORY;
return 0;
}
+/* Load a file's object ID into the corresponding WIM inode. */
+static noinline_for_stack int
+winnt_load_object_id(HANDLE h, struct wim_inode *inode,
+ const wchar_t *full_path, struct winnt_scan_ctx *ctx)
+{
+ FILE_OBJECTID_BUFFER buffer;
+ NTSTATUS status;
+ u32 len;
+
+ if (!(ctx->vol_flags & FILE_SUPPORTS_OBJECT_IDS))
+ return 0;
+
+ status = winnt_fsctl(h, FSCTL_GET_OBJECT_ID, NULL, 0,
+ &buffer, sizeof(buffer), &len);
+
+ if (status == STATUS_OBJECTID_NOT_FOUND) /* No object ID */
+ return 0;
+
+ if (status == STATUS_INVALID_DEVICE_REQUEST) {
+ /* The filesystem claimed to support object IDs, but we can't
+ * actually read them. This happens with Samba. */
+ ctx->vol_flags &= ~FILE_SUPPORTS_OBJECT_IDS;
+ return 0;
+ }
+
+ if (!NT_SUCCESS(status)) {
+ winnt_error(status, L"\"%ls\": Can't read object ID",
+ printable_path(full_path));
+ return WIMLIB_ERR_STAT;
+ }
+
+ if (len == 0) /* No object ID (for directories) */
+ return 0;
+
+ if (!inode_set_object_id(inode, &buffer, len))
+ return WIMLIB_ERR_NOMEM;
+
+ return 0;
+}
+
static int
winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
HANDLE cur_dir,
/* Using NtQueryDirectoryFile() we can re-use the same open handle,
* which we opened with FILE_FLAG_BACKUP_SEMANTICS. */
- while (NT_SUCCESS(status = (*func_NtQueryDirectoryFile)(h, NULL, NULL, NULL,
- &iosb, buf, bufsize,
- FileNamesInformation,
- FALSE, NULL, FALSE)))
+ while (NT_SUCCESS(status = NtQueryDirectoryFile(h, NULL, NULL, NULL,
+ &iosb, buf, bufsize,
+ FileNamesInformation,
+ FALSE, NULL, FALSE)))
{
const FILE_NAMES_INFORMATION *info = buf;
for (;;) {
FILE_INTERNAL_INFORMATION int_info;
FILE_FS_VOLUME_INFORMATION vol_info;
- status = (*func_NtQueryInformationFile)(h, &iosb,
- &int_info, sizeof(int_info),
- FileInternalInformation);
+ status = NtQueryInformationFile(h, &iosb, &int_info, sizeof(int_info),
+ FileInternalInformation);
if (!NT_SUCCESS(status))
return false;
if (int_info.IndexNumber.QuadPart != ino)
return false;
- status = (*func_NtQueryVolumeInformationFile)(h, &iosb,
- &vol_info, sizeof(vol_info),
- FileFsVolumeInformation);
+ status = NtQueryVolumeInformationFile(h, &iosb,
+ &vol_info, sizeof(vol_info),
+ FileFsVolumeInformation);
if (!(NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW))
return false;
name.MaximumLength = name.Length;
/* Try opening the file */
- status = (*func_NtOpenFile) (&h,
- FILE_READ_ATTRIBUTES | FILE_TRAVERSE,
- &attr,
- &iosb,
- FILE_SHARE_VALID_FLAGS,
- FILE_OPEN_FOR_BACKUP_INTENT);
+ status = NtOpenFile(&h,
+ FILE_READ_ATTRIBUTES | FILE_TRAVERSE,
+ &attr,
+ &iosb,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_OPEN_FOR_BACKUP_INTENT);
if (NT_SUCCESS(status)) {
/* Reset root directory */
if (attr.RootDirectory)
- (*func_NtClose)(attr.RootDirectory);
+ NtClose(attr.RootDirectory);
attr.RootDirectory = h;
name.Buffer = (wchar_t *)p;
name.Length = 0;
out_close_root_dir:
if (attr.RootDirectory)
- (*func_NtClose)(attr.RootDirectory);
+ 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,
+winnt_rpfix_progress(struct scan_params *params, const wchar_t *path,
const struct link_reparse_point *link, int scan_status)
{
size_t print_name_nchars = link->print_name_nbytes / sizeof(wchar_t);
params->progress.scan.cur_path = path;
params->progress.scan.symlink_target = print_name0;
- return do_capture_progress(params, scan_status, NULL);
+ return do_scan_progress(params, scan_status, NULL);
}
static int
winnt_try_rpfix(struct reparse_buffer_disk *rpbuf, u16 *rpbuflen_p,
- const wchar_t *path, struct capture_params *params)
+ const wchar_t *path, struct scan_params *params)
{
struct link_reparse_point link;
const wchar_t *rel_target;
* 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)
+ const wchar_t *full_path, struct scan_params *params)
{
struct reparse_buffer_disk rpbuf;
- DWORD bytes_returned;
+ NTSTATUS status;
+ u32 len;
u16 rpbuflen;
int ret;
return 0;
}
- if (!DeviceIoControl(h, FSCTL_GET_REPARSE_POINT,
- NULL, 0, &rpbuf, REPARSE_POINT_MAX_SIZE,
- &bytes_returned, NULL))
- {
- win32_error(GetLastError(), L"\"%ls\": Can't get reparse point",
+ status = winnt_fsctl(h, FSCTL_GET_REPARSE_POINT,
+ NULL, 0, &rpbuf, sizeof(rpbuf), &len);
+ if (!NT_SUCCESS(status)) {
+ winnt_error(status, L"\"%ls\": Can't get reparse point",
printable_path(full_path));
return WIMLIB_ERR_READLINK;
}
- rpbuflen = bytes_returned;
+ rpbuflen = len;
if (unlikely(rpbuflen < REPARSE_DATA_OFFSET)) {
ERROR("\"%ls\": reparse point buffer is too short",
return WIMLIB_ERR_INVALID_REPARSE_DATA;
}
+ if (le32_to_cpu(rpbuf.rptag) == WIM_IO_REPARSE_TAG_DEDUP) {
+ /*
+ * Windows treats Data Deduplication reparse points specially.
+ * Reads from the unnamed data stream actually return the
+ * redirected file contents, even with FILE_OPEN_REPARSE_POINT.
+ * Deduplicated files also cannot be properly restored without
+ * also restoring the "System Volume Information" directory,
+ * which wimlib excludes by default. Therefore, the logical
+ * behavior for us seems to be to ignore the reparse point and
+ * treat the file as a normal file.
+ */
+ inode->i_attributes &= ~FILE_ATTRIBUTE_REPARSE_POINT;
+ return 0;
+ }
+
if (params->add_flags & WIMLIB_ADD_FLAG_RPFIX) {
ret = winnt_try_rpfix(&rpbuf, &rpbuflen, full_path, params);
if (ret == RP_FIXED)
goto unnamed_only;
/* Get a buffer containing the stream information. */
- while (!NT_SUCCESS(status = (*func_NtQueryInformationFile)(h,
- &iosb,
- buf,
- bufsize,
- FileStreamInformation)))
+ while (!NT_SUCCESS(status = NtQueryInformationFile(h,
+ &iosb,
+ buf,
+ bufsize,
+ FileStreamInformation)))
{
switch (status) {
{
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))
+ if (!NT_SUCCESS(winnt_fsctl(h, FSCTL_GET_RETRIEVAL_POINTERS,
+ &in, sizeof(in), &out, sizeof(out), NULL)))
return 0;
return extract_starting_lcn(&out);
static inline bool
should_try_to_use_wimboot_hash(const struct wim_inode *inode,
const struct winnt_scan_ctx *ctx,
- const struct capture_params *params)
+ const struct scan_params *params)
{
/* Directories and encrypted files aren't valid for external backing. */
if (inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
struct wof_external_info wof_info;
struct wim_provider_external_info wim_info;
} out;
- IO_STATUS_BLOCK iosb;
NTSTATUS status;
/* WOF may be attached. Try reading this file's external
* backing info. */
- status = (*func_NtFsControlFile)(h, NULL, NULL, NULL, &iosb,
- FSCTL_GET_EXTERNAL_BACKING,
- NULL, 0, &out, sizeof(out));
+ status = winnt_fsctl(h, FSCTL_GET_EXTERNAL_BACKING,
+ NULL, 0, &out, sizeof(out), NULL);
/* Is WOF not attached? */
if (status == STATUS_INVALID_DEVICE_REQUEST ||
NTSTATUS status;
FILE_ALL_INFORMATION all_info;
- status = (*func_NtQueryInformationFile)(h, &iosb, &all_info,
- sizeof(all_info),
- FileAllInformation);
+ status = NtQueryInformationFile(h, &iosb, &all_info, sizeof(all_info),
+ FileAllInformation);
if (unlikely(!NT_SUCCESS(status) && status != STATUS_BUFFER_OVERFLOW))
return status;
NTSTATUS status;
/* Get volume flags */
- status = (*func_NtQueryVolumeInformationFile)(h, &iosb, attr_info,
- sizeof(_attr_info),
- FileFsAttributeInformation);
+ status = NtQueryVolumeInformationFile(h, &iosb, attr_info,
+ sizeof(_attr_info),
+ FileFsAttributeInformation);
if (NT_SUCCESS(status)) {
ctx->vol_flags = attr_info->FileSystemAttributes;
ctx->is_ntfs = (attr_info->FileSystemNameLength == 4 * sizeof(wchar_t)) &&
}
/* Get volume ID. */
- status = (*func_NtQueryVolumeInformationFile)(h, &iosb, &vol_info,
- sizeof(vol_info),
- FileFsVolumeInformation);
+ status = NtQueryVolumeInformationFile(h, &iosb, &vol_info,
+ sizeof(vol_info),
+ FileFsVolumeInformation);
if ((NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW) &&
(iosb.Information >= offsetof(FILE_FS_VOLUME_INFORMATION,
VolumeSerialNumber) +
int ret;
NTSTATUS status;
struct file_info file_info;
- ACCESS_MASK requestedPerms;
u64 sort_key;
ret = try_exclude(full_path, ctx->params);
if (unlikely(ret > 0)) /* Error? */
goto out;
- /* Open the file. */
- requestedPerms = FILE_READ_DATA |
- FILE_READ_ATTRIBUTES |
- READ_CONTROL |
- ACCESS_SYSTEM_SECURITY |
- SYNCHRONIZE;
-retry_open:
+ /* Open the file with permission to read metadata. Although we will
+ * later need a handle with FILE_LIST_DIRECTORY permission (or,
+ * equivalently, FILE_READ_DATA; they're the same numeric value) if the
+ * file is a directory, it can significantly slow things down to request
+ * this permission on all nondirectories. Perhaps it causes Windows to
+ * start prefetching the file contents... */
status = winnt_openat(cur_dir, relative_path, relative_path_nchars,
- requestedPerms, &h);
+ FILE_READ_ATTRIBUTES | READ_CONTROL |
+ ACCESS_SYSTEM_SECURITY,
+ &h);
if (unlikely(!NT_SUCCESS(status))) {
if (status == STATUS_DELETE_PENDING) {
WARNING("\"%ls\": Deletion pending; skipping file",
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;
- }
if (status == STATUS_SHARING_VIOLATION) {
ERROR("Can't open \"%ls\":\n"
" File is in use by another process! "
goto out;
}
- if (unlikely(!(requestedPerms & FILE_READ_DATA)) &&
- !(file_info.attributes & FILE_ATTRIBUTE_ENCRYPTED))
- {
- ERROR("\"%ls\": Permission to read data was denied",
- printable_path(full_path));
- ret = WIMLIB_ERR_OPEN;
- 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
goto out;
}
+ /* Get the file's object ID. */
+ ret = winnt_load_object_id(h, inode, full_path, ctx);
+ 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, ctx->params);
* 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);
+ NtClose(h);
h = NULL;
ret = winnt_scan_efsrpc_raw_data(inode, full_path,
full_path_nchars, ctx);
/* Directory: recurse to children. */
- if (unlikely(!h)) {
- /* Re-open handle that was closed to read raw encrypted
- * data. */
- status = winnt_openat(cur_dir,
- relative_path,
- relative_path_nchars,
- FILE_LIST_DIRECTORY | SYNCHRONIZE,
- &h);
- if (!NT_SUCCESS(status)) {
- winnt_error(status,
- L"\"%ls\": Can't re-open file",
- printable_path(full_path));
- ret = WIMLIB_ERR_OPEN;
- goto out;
- }
+ /* Re-open the directory with FILE_LIST_DIRECTORY access. */
+ if (h) {
+ NtClose(h);
+ h = NULL;
+ }
+ status = winnt_openat(cur_dir, relative_path,
+ relative_path_nchars, FILE_LIST_DIRECTORY,
+ &h);
+ if (!NT_SUCCESS(status)) {
+ winnt_error(status, L"\"%ls\": Can't open directory",
+ printable_path(full_path));
+ ret = WIMLIB_ERR_OPEN;
+ goto out;
}
ret = winnt_recurse_directory(h,
full_path,
out_progress:
ctx->params->progress.scan.cur_path = full_path;
if (likely(root))
- ret = do_capture_progress(ctx->params, WIMLIB_SCAN_DENTRY_OK, inode);
+ ret = do_scan_progress(ctx->params, WIMLIB_SCAN_DENTRY_OK, inode);
else
- ret = do_capture_progress(ctx->params, WIMLIB_SCAN_DENTRY_EXCLUDED, NULL);
+ ret = do_scan_progress(ctx->params, WIMLIB_SCAN_DENTRY_EXCLUDED, NULL);
out:
if (likely(h))
- (*func_NtClose)(h);
+ NtClose(h);
if (unlikely(ret)) {
free_dentry_tree(root, ctx->params->blob_table);
root = NULL;
- ret = report_capture_error(ctx->params, ret, full_path);
+ ret = report_scan_error(ctx->params, ret, full_path);
}
*root_ret = root;
return ret;
u32 attributes;
u32 security_id;
u32 num_aliases;
- u32 num_streams;
+ u32 num_streams : 31;
+ u32 have_object_id : 1;
u32 first_stream_offset;
struct ntfs_dentry *first_child;
wchar_t short_name[13];
stream->StreamIdentifierLength / 2);
}
+static bool
+is_object_id_stream(const STREAM_LAYOUT_ENTRY *stream)
+{
+ return stream->StreamIdentifierLength == 24 &&
+ !wmemcmp(stream->StreamIdentifier, L"::$OBJECT_ID", 12);
+}
+
/*
* If the specified STREAM_LAYOUT_ENTRY represents a DATA stream as opposed to
* some other type of NTFS stream such as a STANDARD_INFORMATION stream, return
/* Validate the STREAM_LAYOUT_ENTRYs of the specified file and compute the total
* length in bytes of the ntfs_stream structures needed to hold the stream
- * information. */
+ * information. In addition, set *have_object_id_ret=true if the file has an
+ * object ID stream. */
static int
validate_streams_and_compute_total_length(const FILE_LAYOUT_ENTRY *file,
- size_t *total_length_ret)
+ size_t *total_length_ret,
+ bool *have_object_id_ret)
{
const STREAM_LAYOUT_ENTRY *stream =
(const void *)file + file->FirstStreamOffset;
if (use_stream(file, stream, &name, &name_nchars)) {
total += ALIGN(sizeof(struct ntfs_stream) +
(name_nchars + 1) * sizeof(wchar_t), 8);
+ } else if (is_object_id_stream(stream)) {
+ *have_object_id_ret = true;
}
if (stream->NextStreamOffset == 0)
break;
size_t n;
int ret;
void *p;
+ bool have_object_id = false;
inode_size = ALIGN(sizeof(struct ntfs_inode), 8);
}
if (file_has_streams(file)) {
- ret = validate_streams_and_compute_total_length(file, &n);
+ ret = validate_streams_and_compute_total_length(file, &n,
+ &have_object_id);
if (ret)
return ret;
inode_size += n;
ni->last_write_time = info->BasicInformation.LastWriteTime;
ni->last_access_time = info->BasicInformation.LastAccessTime;
ni->security_id = info->SecurityId;
+ ni->have_object_id = have_object_id;
p = FIRST_DENTRY(ni);
QUERY_FILE_LAYOUT_INCLUDE_STREAMS_WITH_NO_CLUSTERS_ALLOCATED,
.FilterType = QUERY_FILE_LAYOUT_FILTER_TYPE_NONE,
};
- const size_t outsize = 32768;
+ size_t outsize = 32768;
QUERY_FILE_LAYOUT_OUTPUT *out = NULL;
- DWORD bytes_returned;
int ret;
- DWORD err;
NTSTATUS status;
status = winnt_open(path, wcslen(path),
- FILE_READ_DATA | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
- &h);
+ FILE_READ_DATA | FILE_READ_ATTRIBUTES, &h);
if (!NT_SUCCESS(status)) {
ret = -1; /* Silently try standard recursive scan instead */
goto out;
}
- out = MALLOC(outsize);
- if (!out) {
- ret = WIMLIB_ERR_NOMEM;
- goto out;
- }
+ for (;;) {
+ /* Allocate a buffer for the output of the ioctl. */
+ out = MALLOC(outsize);
+ if (!out) {
+ ret = WIMLIB_ERR_NOMEM;
+ goto out;
+ }
- while (DeviceIoControl(h, FSCTL_QUERY_FILE_LAYOUT, &in, sizeof(in),
- out, outsize, &bytes_returned, NULL))
- {
- const FILE_LAYOUT_ENTRY *file =
- (const void *)out + out->FirstFileOffset;
- for (;;) {
- ret = load_one_file(file, inode_map);
- if (ret)
- goto out;
- if (file->NextFileOffset == 0)
- break;
- file = (const void *)file + file->NextFileOffset;
+ /* Execute FSCTL_QUERY_FILE_LAYOUT until it fails. */
+ while (NT_SUCCESS(status = winnt_fsctl(h,
+ FSCTL_QUERY_FILE_LAYOUT,
+ &in, sizeof(in),
+ out, outsize, NULL)))
+ {
+ const FILE_LAYOUT_ENTRY *file =
+ (const void *)out + out->FirstFileOffset;
+ for (;;) {
+ ret = load_one_file(file, inode_map);
+ if (ret)
+ goto out;
+ if (file->NextFileOffset == 0)
+ break;
+ file = (const void *)file + file->NextFileOffset;
+ }
+ in.Flags &= ~QUERY_FILE_LAYOUT_RESTART;
}
- in.Flags &= ~QUERY_FILE_LAYOUT_RESTART;
+
+ /* Enlarge the buffer if needed. */
+ if (status != STATUS_BUFFER_TOO_SMALL)
+ break;
+ FREE(out);
+ outsize *= 2;
}
- /* Normally, FSCTL_QUERY_FILE_LAYOUT fails with error code 38 after all
- * files have been enumerated. */
- err = GetLastError();
- if (err != 38) {
- if (err == ERROR_INVALID_FUNCTION ||
- err == ERROR_INVALID_PARAMETER) {
+ /* Normally, FSCTL_QUERY_FILE_LAYOUT fails with STATUS_END_OF_FILE after
+ * all files have been enumerated. */
+ if (status != STATUS_END_OF_FILE) {
+ if (status == STATUS_INVALID_DEVICE_REQUEST /* old OS */ ||
+ status == STATUS_INVALID_PARAMETER /* not root directory */ ) {
/* Silently try standard recursive scan instead */
ret = -1;
} else {
- win32_error(err,
+ winnt_error(status,
L"Error enumerating files on volume \"%ls\"",
path);
/* Try standard recursive scan instead */
ret = 0;
out:
FREE(out);
- (*func_NtClose)(h);
+ NtClose(h);
return ret;
}
* filter driver (WOF) hides reparse points from regular filesystem APIs
* but not from FSCTL_QUERY_FILE_LAYOUT. */
if (ni->attributes & (FILE_ATTRIBUTE_REPARSE_POINT |
- FILE_ATTRIBUTE_ENCRYPTED))
+ FILE_ATTRIBUTE_ENCRYPTED) ||
+ ni->have_object_id)
{
ret = winnt_build_dentry_tree_recursive(&root,
NULL,
status = winnt_open(path, path_nchars,
READ_CONTROL |
- ACCESS_SYSTEM_SECURITY |
- SYNCHRONIZE, &h);
+ ACCESS_SYSTEM_SECURITY, &h);
if (!NT_SUCCESS(status)) {
winnt_error(status, L"Can't open \"%ls\" to "
"read security descriptor",
goto out;
}
ret = winnt_load_security_descriptor(h, inode, path, ctx);
- (*func_NtClose)(h);
+ NtClose(h);
if (ret)
goto out;
out_progress:
ctx->params->progress.scan.cur_path = path;
if (likely(root))
- ret = do_capture_progress(ctx->params, WIMLIB_SCAN_DENTRY_OK, inode);
+ ret = do_scan_progress(ctx->params, WIMLIB_SCAN_DENTRY_OK, inode);
else
- ret = do_capture_progress(ctx->params, WIMLIB_SCAN_DENTRY_EXCLUDED, NULL);
+ ret = do_scan_progress(ctx->params, WIMLIB_SCAN_DENTRY_EXCLUDED, NULL);
out:
if (--ni->num_aliases == 0) {
/* Memory usage optimization: when we don't need the ntfs_inode
int
win32_build_dentry_tree(struct wim_dentry **root_ret,
const wchar_t *root_disk_path,
- struct capture_params *params)
+ struct scan_params *params)
{
wchar_t *path = NULL;
struct winnt_scan_ctx ctx = { .params = params };
if (ret)
goto out;
- status = winnt_open(path, ntpath_nchars,
- FILE_TRAVERSE | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
- &h);
+ status = winnt_open(path, ntpath_nchars, FILE_READ_ATTRIBUTES, &h);
if (!NT_SUCCESS(status)) {
winnt_error(status, L"Can't open \"%ls\"", printable_path(path));
if (status == STATUS_FVE_LOCKED_VOLUME)
get_volume_information(h, path, &ctx);
- (*func_NtClose)(h);
+ NtClose(h);
#ifdef ENABLE_FAST_MFT_SCAN
if (ctx.is_ntfs && !_wgetenv(L"WIMLIB_DISABLE_QUERY_FILE_LAYOUT")) {