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 32f0fc3..90646b4 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 b4acfc9..ac50318 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 5e5353e..31dc74c 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 45c5db2..6d430e3 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 3174482..f77f9e9 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 496d9c9..65f9333 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 4b6bc39..9f04dbe 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 d9a7f2f..93d71e5 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 723773f..85bcadf 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 fbed6b9..d353464 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 06a5f34..1221291 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 8abab0d..13a54a4 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 a7327a6..b213095 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 8f573ec..6ef7651 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