#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;
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,
}
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;
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 |
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);
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))
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
* 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,
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 };