#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;
options |= FILE_SEQUENTIAL_ONLY;
}
retry:
- status = (*func_NtOpenFile)(h_ret, perms, &attr, &iosb,
- FILE_SHARE_VALID_FLAGS, options);
+ 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 ||
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"
count = min(sizeof(buf), bytes_remaining);
retry_read:
- status = (*func_NtReadFile)(h, NULL, NULL, NULL,
- &iosb, buf, count, NULL, NULL);
+ 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",
if (ret)
break;
}
- (*func_NtClose)(h);
+ NtClose(h);
return ret;
}
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:
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;
NTSTATUS status;
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) {
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 |
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) +
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);
/* Re-open the directory with FILE_LIST_DIRECTORY access. */
if (h) {
- (*func_NtClose)(h);
+ NtClose(h);
h = NULL;
}
status = winnt_openat(cur_dir, relative_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;
int ret;
NTSTATUS status;
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 (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;
+ /* 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 STATUS_END_OF_FILE after
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,
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 };
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")) {