+static const struct {
+ u32 flag;
+ const char *name;
+} file_attr_flags[] = {
+ {FILE_ATTRIBUTE_READONLY, "READONLY"},
+ {FILE_ATTRIBUTE_HIDDEN, "HIDDEN"},
+ {FILE_ATTRIBUTE_SYSTEM, "SYSTEM"},
+ {FILE_ATTRIBUTE_DIRECTORY, "DIRECTORY"},
+ {FILE_ATTRIBUTE_ARCHIVE, "ARCHIVE"},
+ {FILE_ATTRIBUTE_DEVICE, "DEVICE"},
+ {FILE_ATTRIBUTE_NORMAL, "NORMAL"},
+ {FILE_ATTRIBUTE_TEMPORARY, "TEMPORARY"},
+ {FILE_ATTRIBUTE_SPARSE_FILE, "SPARSE_FILE"},
+ {FILE_ATTRIBUTE_REPARSE_POINT, "REPARSE_POINT"},
+ {FILE_ATTRIBUTE_COMPRESSED, "COMPRESSED"},
+ {FILE_ATTRIBUTE_OFFLINE, "OFFLINE"},
+ {FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, "NOT_CONTENT_INDEXED"},
+ {FILE_ATTRIBUTE_ENCRYPTED, "ENCRYPTED"},
+ {FILE_ATTRIBUTE_VIRTUAL, "VIRTUAL"},
+};
+
+static int
+cmp_attributes(const struct wim_inode *inode1,
+ const struct wim_inode *inode2, int cmp_flags)
+{
+ const u32 changed = inode1->i_attributes ^ inode2->i_attributes;
+ const u32 set = inode2->i_attributes & ~inode1->i_attributes;
+ const u32 cleared = inode1->i_attributes & ~inode2->i_attributes;
+
+ /* NORMAL may change, but it must never be set along with other
+ * attributes. */
+ if ((inode2->i_attributes & FILE_ATTRIBUTE_NORMAL) &&
+ (inode2->i_attributes & ~FILE_ATTRIBUTE_NORMAL))
+ goto mismatch;
+
+ /* DIRECTORY must not change. */
+ if (changed & FILE_ATTRIBUTE_DIRECTORY)
+ goto mismatch;
+
+ /* REPARSE_POINT may be cleared in UNIX mode if the inode is not a
+ * symlink. */
+ if ((changed & FILE_ATTRIBUTE_REPARSE_POINT) &&
+ !((cleared & FILE_ATTRIBUTE_REPARSE_POINT) &&
+ (cmp_flags & WIMLIB_CMP_FLAG_UNIX_MODE) &&
+ !inode_is_symlink(inode1)))
+ goto mismatch;
+
+ /* SPARSE_FILE may be cleared in UNIX and NTFS-3G modes, or in Windows
+ * mode if the inode is a directory. */
+ if ((changed & FILE_ATTRIBUTE_SPARSE_FILE) &&
+ !((cleared & FILE_ATTRIBUTE_SPARSE_FILE) &&
+ ((cmp_flags & (WIMLIB_CMP_FLAG_UNIX_MODE |
+ WIMLIB_CMP_FLAG_NTFS_3G_MODE)) ||
+ ((cmp_flags & WIMLIB_CMP_FLAG_WINDOWS_MODE) &&
+ (inode1->i_attributes & FILE_ATTRIBUTE_DIRECTORY)))))
+ goto mismatch;
+
+ /* COMPRESSED may change in UNIX and NTFS-3G modes. (It *should* be
+ * preserved in NTFS-3G mode, but it's not implemented yet.) */
+ if ((changed & FILE_ATTRIBUTE_COMPRESSED) &&
+ !(cmp_flags & (WIMLIB_CMP_FLAG_UNIX_MODE |
+ WIMLIB_CMP_FLAG_NTFS_3G_MODE)))
+ goto mismatch;
+
+ /* All other attributes can change in UNIX mode, but not in any other
+ * mode. */
+ if ((changed & ~(FILE_ATTRIBUTE_NORMAL |
+ FILE_ATTRIBUTE_DIRECTORY |
+ FILE_ATTRIBUTE_REPARSE_POINT |
+ FILE_ATTRIBUTE_SPARSE_FILE |
+ FILE_ATTRIBUTE_COMPRESSED)) &&
+ !(cmp_flags & WIMLIB_CMP_FLAG_UNIX_MODE))
+ goto mismatch;
+
+ return 0;
+
+mismatch:
+ ERROR("Attribute mismatch for %"TS": 0x%08"PRIx32" vs. 0x%08"PRIx32":",
+ inode_any_full_path(inode1), inode1->i_attributes,
+ inode2->i_attributes);
+ for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++) {
+ u32 flag = file_attr_flags[i].flag;
+ if (changed & flag) {
+ fprintf(stderr, "\tFILE_ATTRIBUTE_%s was %s\n",
+ file_attr_flags[i].name,
+ (set & flag) ? "set" : "cleared");
+ }
+ }
+ return WIMLIB_ERR_IMAGES_ARE_DIFFERENT;
+}
+