diff --git a/include/llvm/Support/FileSystem.h b/include/llvm/Support/FileSystem.h index 21159dd2642..9497e4de1db 100644 --- a/include/llvm/Support/FileSystem.h +++ b/include/llvm/Support/FileSystem.h @@ -162,6 +162,7 @@ class file_status #endif friend bool equivalent(file_status A, file_status B); friend error_code status(const Twine &path, file_status &result); + friend error_code GetUniqueID(const Twine Path, uint64_t &Result); file_type Type; perms Perms; public: diff --git a/lib/Support/Windows/PathV2.inc b/lib/Support/Windows/PathV2.inc index bec95e36cc5..e6888f93e68 100644 --- a/lib/Support/Windows/PathV2.inc +++ b/lib/Support/Windows/PathV2.inc @@ -426,15 +426,20 @@ error_code file_size(const Twine &path, uint64_t &result) { } error_code GetUniqueID(const Twine Path, uint64_t &Result) { - // FIXME: this is only unique if the file is accessed by the same file path. - // How do we do this for C:\dir\file and ..\dir\file ? Unix has inode - // numbers, but the concept doesn't exist in Windows. - SmallString<128> Storage; - StringRef P = Path.toStringRef(Storage); - uint64_t UniqueID = 0; - for (StringRef::iterator I = P.begin(), E = P.end(); I != E; ++I) - UniqueID += *I; - Result = UniqueID; + file_status Status; + if (error_code E = status(Path, Status)) + return E; + + // The file is uniquely identified by the volume serial number along + // with the 64-bit file identifier. + Result = (static_cast(Status.FileIndexHigh) << 32ULL) | + static_cast(Status.FileIndexLow); + + // Because the serial number is 32-bits, but we've already used up all 64 + // bits for the file index, XOR the serial number into the high 32 bits of + // the resulting value. We could potentially get collisons from this, but + // the likelihood is low. + Result ^= (static_cast(Status.VolumeSerialNumber) << 32ULL); return error_code::success(); } diff --git a/unittests/Support/Path.cpp b/unittests/Support/Path.cpp index 2f820b9181d..4f9d146c460 100644 --- a/unittests/Support/Path.cpp +++ b/unittests/Support/Path.cpp @@ -164,6 +164,42 @@ protected: } }; +TEST_F(FileSystemTest, Unique) { + // Create a temp file. + int FileDescriptor; + SmallString<64> TempPath; + ASSERT_NO_ERROR( + fs::unique_file("%%-%%-%%-%%.temp", FileDescriptor, TempPath)); + + // The same file should return an identical unique id. + uint64_t F1, F2; + ASSERT_NO_ERROR(fs::GetUniqueID(Twine(TempPath), F1)); + ASSERT_NO_ERROR(fs::GetUniqueID(Twine(TempPath), F2)); + ASSERT_EQ(F1, F2); + + // Different files should return different unique ids. + int FileDescriptor2; + SmallString<64> TempPath2; + ASSERT_NO_ERROR( + fs::unique_file("%%-%%-%%-%%.temp", FileDescriptor2, TempPath2)); + + uint64_t D; + ASSERT_NO_ERROR(fs::GetUniqueID(Twine(TempPath2), D)); + ASSERT_NE(D, F1); + ::close(FileDescriptor2); + + ASSERT_NO_ERROR(fs::remove(Twine(TempPath2))); + + // Two paths representing the same file on disk should still provide the + // same unique id. We can test this by making a hard link. + ASSERT_NO_ERROR(fs::create_hard_link(Twine(TempPath), Twine(TempPath2))); + uint64_t D2; + ASSERT_NO_ERROR(fs::GetUniqueID(Twine(TempPath2), D2)); + ASSERT_EQ(D2, F1); + + ::close(FileDescriptor); +} + TEST_F(FileSystemTest, TempFiles) { // Create a temp file. int FileDescriptor;