+static const struct {
+ uint32_t flag;
+ const wchar_t *name;
+} file_attr_flags[] = {
+ {FILE_ATTRIBUTE_READONLY, L"READONLY"},
+ {FILE_ATTRIBUTE_HIDDEN, L"HIDDEN"},
+ {FILE_ATTRIBUTE_SYSTEM, L"SYSTEM"},
+ {FILE_ATTRIBUTE_DIRECTORY, L"DIRECTORY"},
+ {FILE_ATTRIBUTE_ARCHIVE, L"ARCHIVE"},
+ {FILE_ATTRIBUTE_DEVICE, L"DEVICE"},
+ {FILE_ATTRIBUTE_NORMAL, L"NORMAL"},
+ {FILE_ATTRIBUTE_TEMPORARY, L"TEMPORARY"},
+ {FILE_ATTRIBUTE_SPARSE_FILE, L"SPARSE_FILE"},
+ {FILE_ATTRIBUTE_REPARSE_POINT, L"REPARSE_POINT"},
+ {FILE_ATTRIBUTE_COMPRESSED, L"COMPRESSED"},
+ {FILE_ATTRIBUTE_OFFLINE, L"OFFLINE"},
+ {FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, L"NOT_CONTENT_INDEXED"},
+ {FILE_ATTRIBUTE_ENCRYPTED, L"ENCRYPTED"},
+ {FILE_ATTRIBUTE_VIRTUAL, L"VIRTUAL"},
+};
+
+static void
+cmp_attributes(const wchar_t *path_1, const BY_HANDLE_FILE_INFORMATION *file_info_1,
+ const wchar_t *path_2, const BY_HANDLE_FILE_INFORMATION *file_info_2)
+{
+ u32 attrib_1 = file_info_1->dwFileAttributes;
+ u32 attrib_2 = file_info_2->dwFileAttributes;
+ u32 differences = attrib_1 ^ attrib_2;
+
+ if (!differences)
+ return;
+
+ difference(L"Attributes for %ls (0x%"PRIx32") differ "
+ "from attributes for %ls (0x%"PRIx32"):",
+ path_1, attrib_1, path_2, attrib_2);
+ for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++) {
+ if (differences & file_attr_flags[i].flag) {
+ const wchar_t *set_path;
+ const wchar_t *unset_path;
+ if (attrib_1 & file_attr_flags[i].flag) {
+ set_path = path_1;
+ unset_path = path_2;
+ } else {
+ set_path = path_2;
+ unset_path = path_1;
+ }
+ fwprintf(stderr, L"\t%ls has FILE_ATTRIBUTE_%ls set but %ls does not\n",
+ set_path, file_attr_flags[i].name, unset_path);
+ }
+ }
+}
+