#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/win32_vss.h"
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,
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);
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);
* 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,