]> wimlib.net Git - wimlib/commitdiff
Add support for capturing/applying object IDs
authorEric Biggers <ebiggers3@gmail.com>
Thu, 18 Feb 2016 02:21:42 +0000 (20:21 -0600)
committerEric Biggers <ebiggers3@gmail.com>
Thu, 18 Feb 2016 02:57:36 +0000 (20:57 -0600)
15 files changed:
Makefile.am
doc/man1/wimlib-imagex-apply.1
doc/man1/wimlib-imagex-capture.1
doc/man1/wimlib-imagex-mount.1
include/wimlib/apply.h
include/wimlib/object_id.h [new file with mode: 0644]
src/extract.c
src/ntfs-3g_apply.c
src/ntfs-3g_capture.c
src/tagged_items.c
src/win32_apply.c
src/win32_capture.c
tests/test-imagex-ntfs
tests/tree-cmp.c
tests/win32-test-imagex-capture_and_apply.bat

index 32f0fc33a9674a9b881919b92bed153f8f4846e9..90646b44447f52d1244d253f1ea8bcc41ec0df07 100644 (file)
@@ -131,6 +131,7 @@ libwim_la_SOURCES =         \
        include/wimlib/lzx_common.h     \
        include/wimlib/lzx_constants.h  \
        include/wimlib/metadata.h       \
+       include/wimlib/object_id.h      \
        include/wimlib/pathlist.h       \
        include/wimlib/paths.h          \
        include/wimlib/pattern.h        \
index b4acfc9f9be2c2ea43031d787f0aa169015c69a7..ac50318f8453540c9ccf9b38b49b00b3bbfb6dbe 100644 (file)
@@ -118,6 +118,8 @@ DOS/Windows file attribute flags.
 .IP \[bu]
 All names of all files, including names in the Win32 namespace, DOS namespace,
 Win32+DOS namespace, and POSIX namespace.  This includes hard links.
+.IP \[bu]
+Object IDs.
 .PP
 However, there are also several known limitations of the NTFS volume extraction
 mode:
@@ -181,7 +183,9 @@ supported by the filesystem.
 DOS names (8.3) names of files; however, the failure to set them is not
 considered an error condition.
 .IP \[bu]
-Hard links, if supported by the filesystem.
+Hard links, if supported by the target filesystem.
+.IP \[bu]
+Object IDs, if supported by the target filesystem.
 .PP
 Additional notes about extracting files on Windows:
 .IP \[bu] 4
index 5e5353ecd976f074cd08310a0b57d92a0e90928e..31dc74c9c7673a68744ef762d123dec4190723fb 100644 (file)
@@ -109,6 +109,8 @@ DOS/Windows file attribute flags.
 .IP \[bu]
 All names of all files, including names in the Win32 namespace, DOS namespace,
 Win32+DOS namespace, and POSIX namespace.  This includes hard links.
+.IP \[bu]
+Object IDs.
 .PP
 However, the main limitations of this NTFS volume capture mode are:
 .IP \[bu] 4
@@ -158,8 +160,10 @@ DOS names (8.3) names of files; however, the failure to read them is not
 considered an error condition.
 .IP \[bu]
 Hard links, if supported by the source filesystem.
+.IP \[bu]
+Object IDs, if supported by the source filesystem.
 .PP
-There is no support for storing NTFS extended attributes and object IDs.
+There is no support for storing NTFS extended attributes.
 .PP
 The capture process is reversible, since when \fBwimlib-imagex apply\fR (on
 Windows) extracts the captured WIM image, it will extract all of the above
index 45c5db2f0e82ceaed751d6545aa249e30ca4a80b..6d430e30f9ca20296d7eb1d1c579a4059bf62b95 100644 (file)
@@ -58,6 +58,9 @@ Windows file attributes.  These are not exposed in the mounted filesystem,
 although existing values will be preserved on commit.  New files are assigned
 default attributes based on the UNIX file mode bits.
 .IP \[bu]
+Object IDs.  These are not exposed in the mounted filesystem, although existing
+values will be preserved on commit.  New files are not given object IDs.
+.IP \[bu]
 EFS-encrypted files.  The files themselves will be visible in mounted WIM images
 but their data will not be available.
 .SH SPLIT WIMS
index 31744822613951d39ca38dc34716fed7afbb6484..f77f9e95d3bb6dd174adc7aae4fdfa5f4c5fd2f4 100644 (file)
@@ -28,6 +28,7 @@ struct wim_features {
        unsigned long security_descriptors;
        unsigned long short_names;
        unsigned long unix_data;
+       unsigned long object_ids;
        unsigned long timestamps;
        unsigned long case_sensitive_filenames;
 };
diff --git a/include/wimlib/object_id.h b/include/wimlib/object_id.h
new file mode 100644 (file)
index 0000000..21c1b4a
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef _WIMLIB_OBJECT_ID_H
+#define _WIMLIB_OBJECT_ID_H
+
+#include "wimlib/types.h"
+
+extern bool
+inode_has_object_id(const struct wim_inode *inode);
+
+extern const void *
+inode_get_object_id(const struct wim_inode *inode, u32 *len_ret);
+
+extern bool
+inode_set_object_id(struct wim_inode *inode, const void *object_id, u32 len);
+
+#endif /* _WIMLIB_OBJECT_ID_H  */
index 496d9c9870664ba050902139a942363197956605..65f93338841563593a30b148a747118c11967377 100644 (file)
@@ -53,6 +53,7 @@
 #include "wimlib/endianness.h"
 #include "wimlib/error.h"
 #include "wimlib/metadata.h"
+#include "wimlib/object_id.h"
 #include "wimlib/pathlist.h"
 #include "wimlib/paths.h"
 #include "wimlib/pattern.h"
@@ -1162,6 +1163,8 @@ inode_tally_features(const struct wim_inode *inode,
                features->security_descriptors++;
        if (inode_has_unix_data(inode))
                features->unix_data++;
+       if (inode_has_object_id(inode))
+               features->object_ids++;
 }
 
 /* Tally features necessary to extract a dentry and the corresponding inode.  */
@@ -1314,6 +1317,12 @@ do_feature_check(const struct wim_features *required_features,
                        required_features->unix_data);
        }
 
+       /* Object IDs.  */
+       if (required_features->object_ids && !supported_features->object_ids) {
+               WARNING("Ignoring object IDs of %lu files",
+                       required_features->object_ids);
+       }
+
        /* DOS Names.  */
        if (required_features->short_names &&
            !supported_features->short_names)
index 4b6bc394a99565941ca2a828e2a274dcc8e0b911..9f04dbea5b65b6180c0612461fa4a9099ce2deda 100644 (file)
@@ -3,14 +3,14 @@
  *
  * Apply a WIM image directly to an NTFS volume using libntfs-3g.  Restore as
  * much information as possible, including security data, file attributes, DOS
- * names, and alternate data streams.
+ * names, alternate data streams, and object IDs.
  *
  * Note: because NTFS-3g offers inode-based interfaces, we actually don't need
  * to deal with paths at all!  (Other than for error messages.)
  */
 
 /*
- * Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers
+ * Copyright (C) 2012-2016 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
@@ -35,6 +35,7 @@
 #include <string.h>
 
 #include <ntfs-3g/attrib.h>
+#include <ntfs-3g/object_id.h>
 #include <ntfs-3g/reparse.h>
 #include <ntfs-3g/security.h>
 
@@ -46,6 +47,7 @@
 #include "wimlib/error.h"
 #include "wimlib/metadata.h"
 #include "wimlib/ntfs_3g.h"
+#include "wimlib/object_id.h"
 #include "wimlib/reparse.h"
 #include "wimlib/security.h"
 #include "wimlib/security_descriptor.h"
@@ -65,6 +67,7 @@ ntfs_3g_get_supported_features(const char *target,
        supported_features->reparse_points            = 1;
        supported_features->security_descriptors      = 1;
        supported_features->short_names               = 1;
+       supported_features->object_ids                = 1;
        supported_features->timestamps                = 1;
        supported_features->case_sensitive_filenames  = 1;
        return 0;
@@ -412,6 +415,25 @@ ntfs_3g_set_metadata(ntfs_inode *ni, const struct wim_inode *inode,
        sd = wim_get_current_security_data(ctx->common.wim);
        one_dentry = inode_first_extraction_dentry(inode);
 
+       /* Object ID */
+       {
+               u32 len;
+               const void *object_id = inode_get_object_id(inode, &len);
+               if (unlikely(object_id != NULL) &&
+                   ntfs_set_ntfs_object_id(ni, object_id, len, 0))
+               {
+                       if (errno == EEXIST) {
+                               WARNING("Duplicate object ID on file \"%s\"",
+                                       dentry_full_path(one_dentry));
+                       } else {
+                               ERROR_WITH_ERRNO("Failed to set object ID on "
+                                                "\"%s\" in NTFS volume",
+                                                dentry_full_path(one_dentry));
+                               return WIMLIB_ERR_NTFS_3G;
+                       }
+               }
+       }
+
        /* Attributes  */
        if (!(extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)) {
                u32 attrib = inode->i_attributes;
index d9a7f2f6f1c1abadf8028e748fd32e267976a4d9..93d71e55bcefd951b84c5d57a271cd03285badff 100644 (file)
@@ -6,7 +6,7 @@
  */
 
 /*
- * Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers
+ * Copyright (C) 2012-2016 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
@@ -31,6 +31,7 @@
 #include <errno.h>
 
 #include <ntfs-3g/attrib.h>
+#include <ntfs-3g/object_id.h>
 #include <ntfs-3g/reparse.h>
 #include <ntfs-3g/security.h>
 #include <ntfs-3g/volume.h>
@@ -44,6 +45,7 @@
 #include "wimlib/endianness.h"
 #include "wimlib/error.h"
 #include "wimlib/ntfs_3g.h"
+#include "wimlib/object_id.h"
 #include "wimlib/paths.h"
 #include "wimlib/reparse.h"
 #include "wimlib/security.h"
@@ -436,6 +438,22 @@ out_put_actx:
        return ret;
 }
 
+static noinline_for_stack int
+load_object_id(ntfs_inode *ni, struct wim_inode *inode)
+{
+       OBJECT_ID_ATTR attr;
+       int len;
+
+       len = ntfs_get_ntfs_object_id(ni, (char *)&attr, sizeof(attr));
+       if (likely(len == -ENODATA || len == 0))
+               return 0;
+       if (len < 0)
+               return WIMLIB_ERR_NTFS_3G;
+       if (!inode_set_object_id(inode, &attr, len))
+               return WIMLIB_ERR_NOMEM;
+       return 0;
+}
+
 /* Load the security descriptor of an NTFS inode into the corresponding WIM
  * inode and the WIM image's security descriptor set.  */
 static noinline_for_stack int
@@ -776,6 +794,13 @@ ntfs_3g_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                        goto out;
        }
 
+       /* Load the object ID.  */
+       ret = load_object_id(ni, inode);
+       if (ret) {
+               ERROR_WITH_ERRNO("Error reading object ID of \"%s\"", path);
+               goto out;
+       }
+
        /* Scan the data streams.
         *
         * Note: directories should not have an unnamed data stream, but they
index 723773f94be3e678afe6c0f88bae6a3355a961ce..85bcadfdf38274e7e346fe9c04285877ed2bd7c8 100644 (file)
@@ -6,7 +6,7 @@
  */
 
 /*
- * Copyright (C) 2014 Eric Biggers
+ * Copyright (C) 2014-2016 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 "wimlib/endianness.h"
 #include "wimlib/inode.h"
+#include "wimlib/object_id.h"
 #include "wimlib/types.h"
 #include "wimlib/unix_data.h"
 
-/* Used by the Microsoft implementation.  */
+/* Object ID tag; this is also used by the Microsoft implementation.  */
 #define TAG_OBJECT_ID          0x00000001
 
 /* Random number that we'll use for tagging our UNIX data items.  */
@@ -51,6 +52,10 @@ struct tagged_item_header {
        u8 data[];
 };
 
+/* Unconfirmed: are all 64 bytes of the object ID always present?  Since NTFS-3g
+ * permits shorter object IDs, we'll do the same for now.  */
+#define OBJECT_ID_MIN_LENGTH   16
+
 struct object_id_disk {
        u8 object_id[16];
        u8 birth_volume_id[16];
@@ -71,7 +76,7 @@ struct wimlib_unix_data_disk {
  * NULL.  */
 static void *
 inode_get_tagged_item(const struct wim_inode *inode,
-                     u32 desired_tag, u32 min_data_len)
+                     u32 desired_tag, u32 min_data_len, u32 *actual_len_ret)
 {
        size_t minlen_with_hdr = sizeof(struct tagged_item_header) + min_data_len;
        size_t len_remaining;
@@ -98,8 +103,11 @@ inode_get_tagged_item(const struct wim_inode *inode,
                        return NULL;
 
                /* Matches the item we wanted?  */
-               if (tag == desired_tag && len >= min_data_len)
+               if (tag == desired_tag && len >= min_data_len) {
+                       if (actual_len_ret)
+                               *actual_len_ret = len;
                        return hdr->data;
+               }
 
                len_remaining -= sizeof(struct tagged_item_header) + len;
                p += sizeof(struct tagged_item_header) + len;
@@ -145,7 +153,8 @@ static inline struct wimlib_unix_data_disk *
 inode_get_unix_data_disk(const struct wim_inode *inode)
 {
        return inode_get_tagged_item(inode, TAG_WIMLIB_UNIX_DATA,
-                                    sizeof(struct wimlib_unix_data_disk));
+                                    sizeof(struct wimlib_unix_data_disk),
+                                    NULL);
 }
 
 static inline struct wimlib_unix_data_disk *
@@ -216,3 +225,35 @@ inode_set_unix_data(struct wim_inode *inode, struct wimlib_unix_data *unix_data,
                p->rdev = cpu_to_le32(unix_data->rdev);
        return true;
 }
+
+/* Return %true iff the specified inode has an object ID.  */
+bool
+inode_has_object_id(const struct wim_inode *inode)
+{
+       return inode_get_object_id(inode, NULL) != NULL;
+}
+
+/* Retrieve a pointer to the object ID of the specified inode and write its
+ * length to @len_ret.  Return NULL if the inode does not have an object ID.  */
+const void *
+inode_get_object_id(const struct wim_inode *inode, u32 *len_ret)
+{
+       return inode_get_tagged_item(inode, TAG_OBJECT_ID,
+                                    OBJECT_ID_MIN_LENGTH, len_ret);
+}
+
+/* Set the inode's object ID to the value specified by @object_id and @len.
+ * Assumes the inode didn't already have an object ID set.  Returns %true if
+ * successful, %false if failed (out of memory).  */
+bool
+inode_set_object_id(struct wim_inode *inode, const void *object_id, u32 len)
+{
+       void *p;
+
+       p = inode_add_tagged_item(inode, TAG_OBJECT_ID, len);
+       if (!p)
+               return false;
+
+       memcpy(p, object_id, len);
+       return true;
+}
index fbed6b9c685d479cca4ba69559d98bd6de8a7ee2..d3534643115e86b93c63f0460cb6e4ed7eb0b6dc 100644 (file)
@@ -35,6 +35,7 @@
 #include "wimlib/encoding.h"
 #include "wimlib/error.h"
 #include "wimlib/metadata.h"
+#include "wimlib/object_id.h"
 #include "wimlib/paths.h"
 #include "wimlib/pattern.h"
 #include "wimlib/reparse.h"
@@ -158,6 +159,9 @@ struct win32_apply_ctx {
         * [PrepopulateList].  */
        unsigned long num_system_compression_exclusions;
 
+       /* Number of files for which we couldn't set the object ID.  */
+       unsigned long num_object_id_failures;
+
        /* The Windows build number of the image being applied, or 0 if unknown.
         */
        u64 windows_build_number;
@@ -299,6 +303,9 @@ win32_get_supported_features(const wchar_t *target,
        if (short_names_supported)
                supported_features->short_names = 1;
 
+       if (vol_flags & FILE_SUPPORTS_OBJECT_IDS)
+               supported_features->object_ids = 1;
+
        supported_features->timestamps = 1;
 
        /* Note: Windows does not support case sensitive filenames!  At least
@@ -2660,6 +2667,46 @@ end_extract_blob(struct blob_descriptor *blob, int status, void *_ctx)
         FILE_ATTRIBUTE_SPARSE_FILE     |       \
         FILE_ATTRIBUTE_COMPRESSED)
 
+static void
+set_object_id(HANDLE h, const struct wim_inode *inode,
+             struct win32_apply_ctx *ctx)
+{
+       const void *object_id;
+       u32 len;
+       NTSTATUS status;
+
+       if (!ctx->common.supported_features.object_ids)
+               return;
+
+       object_id = inode_get_object_id(inode, &len);
+       if (likely(object_id == NULL))  /* No object ID?  */
+               return;
+
+       status = winnt_fsctl(h, FSCTL_SET_OBJECT_ID,
+                            object_id, len, NULL, 0, NULL);
+       if (NT_SUCCESS(status))
+               return;
+
+       /* Object IDs must be unique within the filesystem.  A duplicate might
+        * occur if an image containing object IDs is applied twice to the same
+        * filesystem.  Arguably, the user should be warned in this case; but
+        * the reality seems to be that nothing important cares about object IDs
+        * except the Distributed Link Tracking Service... so for now these
+        * failures are just ignored.  */
+       if (status == STATUS_DUPLICATE_NAME ||
+           status == STATUS_OBJECT_NAME_COLLISION)
+               return;
+
+       ctx->num_object_id_failures++;
+       if (ctx->num_object_id_failures < 10) {
+               winnt_warning(status, L"Can't set object ID on \"%ls\"",
+                             current_path(ctx));
+       } else if (ctx->num_object_id_failures == 10) {
+               WARNING("Suppressing further warnings about failure to set "
+                       "object IDs.");
+       }
+}
+
 /* Set the security descriptor @desc, of @desc_size bytes, on the file with open
  * handle @h.  */
 static NTSTATUS
@@ -2804,7 +2851,12 @@ do_apply_metadata_to_file(HANDLE h, const struct wim_inode *inode,
        FILE_BASIC_INFORMATION info;
        NTSTATUS status;
 
-       /* Set security descriptor if present and not in NO_ACLS mode  */
+       /* Set the file's object ID if present and object IDs are supported by
+        * the filesystem.  */
+       set_object_id(h, inode, ctx);
+
+       /* Set the file's security descriptor if present and we're not in
+        * NO_ACLS mode  */
        if (inode_has_security_descriptor(inode) &&
            !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS))
        {
index 06a5f343eb70299cd5ec867735749fdd4a01514b..12212910f860500db4905a281fc7a971412b3daf 100644 (file)
@@ -36,6 +36,7 @@
 #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"
@@ -655,6 +656,46 @@ out:
        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,
@@ -1673,6 +1714,11 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                        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);
@@ -1937,7 +1983,8 @@ struct ntfs_inode {
        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];
@@ -2108,6 +2155,13 @@ is_valid_stream_entry(const STREAM_LAYOUT_ENTRY *stream)
                         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
@@ -2144,10 +2198,12 @@ use_stream(const FILE_LAYOUT_ENTRY *file, const STREAM_LAYOUT_ENTRY *stream,
 
 /* 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;
@@ -2171,6 +2227,8 @@ validate_streams_and_compute_total_length(const FILE_LAYOUT_ENTRY *file,
                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;
@@ -2274,6 +2332,7 @@ load_one_file(const FILE_LAYOUT_ENTRY *file, struct ntfs_inode_map *inode_map)
        size_t n;
        int ret;
        void *p;
+       bool have_object_id = false;
 
        inode_size = ALIGN(sizeof(struct ntfs_inode), 8);
 
@@ -2290,7 +2349,8 @@ load_one_file(const FILE_LAYOUT_ENTRY *file, struct ntfs_inode_map *inode_map)
        }
 
        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;
@@ -2308,6 +2368,7 @@ load_one_file(const FILE_LAYOUT_ENTRY *file, struct ntfs_inode_map *inode_map)
        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);
 
@@ -2550,7 +2611,8 @@ generate_wim_structures_recursive(struct wim_dentry **root_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,
index 8abab0d368bb4557f1b1f084ae03f0a4f9f10d9f..13a54a4643ba9cb4cd42df1be9fdb7f79c353b2f 100755 (executable)
@@ -213,6 +213,12 @@ msg "file with security descriptor"
 do_test 'touch file;
         setfattr -n system.ntfs_acl -v 0s`cat $srcdir/tests/security_descriptor_1.base64` file'
 
+msg "file with object ID"
+do_test 'touch file;
+        touch file2;
+        setfattr -n system.ntfs_object_id -v 0x15ac83a36dc6cf8ec459b8017dd8626f file
+        setfattr -n system.ntfs_object_id -v 0xf67394c12b17608e1d050d181ba8ffd27df80cbdf620f4c82c79b9e6799147b697621aff72915ade05abb96b15dea1a3e0bda4caa9e33cfd461c92c16be9713d file2'
+
 msg "files with different security descriptors"
 do_test 'touch file;
         touch file2;
index a7327a64fee9ccbb9bd94cc1909b4b9e718dd18d..b21309541924f6161218f49cedde2641f0e3189a 100644 (file)
@@ -280,6 +280,7 @@ static void special_cmp(const char *file1, const char *file2)
        cmp_xattr(file1, file2, "system.ntfs_acl", 0, false);
        cmp_xattr(file1, file2, "system.ntfs_attrib", 0, false);
        cmp_xattr(file1, file2, "system.ntfs_dos_name", 0, true);
+       cmp_xattr(file1, file2, "system.ntfs_object_id", 64, true);
        cmp_xattr(file1, file2, "system.ntfs_reparse_data", 0, true);
        cmp_xattr(file1, file2, "system.ntfs_times", 16, false);
        cmp_ads(file1, file2);
index 8f573eceb62bd5ea4c7f8f7ca4b0692fa1ef816f..6ef76513b00344ea1fdc63466af6a25c598b7cc1 100644 (file)
@@ -490,6 +490,23 @@ mklink /h link file > nul
 call :do_test\r
 if %errorlevel% neq 0 goto :fail\r
 \r
+REM Note: since object IDs must be unique per filesystem, we can't expect them\r
+REM to preserved using our testing scheme.  Therefore, win32-tree-cmp doesn't\r
+REM compare them, and the below tests really just ensure the object ID code is\r
+REM run to some extent.\r
+\r
+call :msg "file with object ID"\r
+echo hello > file\r
+fsutil objectid create file > nul\r
+call :do_test\r
+if %errorlevel% neq 0 goto :fail\r
+\r
+call :msg "directory with object ID"\r
+md subdir\r
+fsutil objectid set f67394c12b17608e1d050d181ba8ffd2 7df80cbdf620f4c82c79b9e6799147b6 97621aff72915ade05abb96b15dea1a3 e0bda4caa9e33cfd461c92c16be9713d subdir\r
+call :do_test\r
+if %errorlevel% neq 0 goto :fail\r
+\r
 :rpfix_tests\r
 \r
 echo Testing rpfix junction\r