#include <stack>
#include <string>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
namespace llvm {
namespace sys {
namespace fs {
/// a platform specific member to store the result.
class file_status
{
- // implementation defined status field.
+ #if defined(LLVM_ON_UNIX)
+ dev_t st_dev;
+ ino_t st_ino;
+ #elif defined (LLVM_ON_WIN32)
+ uint32_t LastWriteTimeHigh;
+ uint32_t LastWriteTimeLow;
+ uint32_t VolumeSerialNumber;
+ uint32_t FileSizeHigh;
+ uint32_t FileSizeLow;
+ uint32_t FileIndexHigh;
+ uint32_t FileIndexLow;
+ #endif
+ friend bool equivalent(file_status A, file_status B);
+ friend error_code status(const Twine &path, file_status &result);
file_type Type;
public:
explicit file_status(file_type v=file_type::status_error)
/// @brief Do paths represent the same thing?
///
+/// assert(status_known(A) || status_known(B));
+///
/// @param A Input path A.
/// @param B Input path B.
/// @param result Set to true if stat(A) and stat(B) have the same device and
return success;
}
-error_code equivalent(const Twine &A, const Twine &B, bool &result) {
- // Get arguments.
- SmallString<128> a_storage;
- SmallString<128> b_storage;
- StringRef a = A.toNullTerminatedStringRef(a_storage);
- StringRef b = B.toNullTerminatedStringRef(b_storage);
-
- struct stat stat_a, stat_b;
- int error_b = ::stat(b.begin(), &stat_b);
- int error_a = ::stat(a.begin(), &stat_a);
-
- // If both are invalid, it's an error. If only one is, the result is false.
- if (error_a != 0 || error_b != 0) {
- if (error_a == error_b)
- return error_code(errno, system_category());
- result = false;
- } else {
- result =
- stat_a.st_dev == stat_b.st_dev &&
- stat_a.st_ino == stat_b.st_ino;
- }
+bool equivalent(file_status A, file_status B) {
+ assert(status_known(A) && status_known(B));
+ return A.st_dev == B.st_dev &&
+ A.st_ino == B.st_ino;
+}
+error_code equivalent(const Twine &A, const Twine &B, bool &result) {
+ file_status fsA, fsB;
+ if (error_code ec = status(A, fsA)) return ec;
+ if (error_code ec = status(B, fsB)) return ec;
+ result = equivalent(fsA, fsB);
return success;
}
else
result = file_status(file_type::type_unknown);
+ result.st_dev = status.st_dev;
+ result.st_ino = status.st_ino;
+
return success;
}
return success;
}
-error_code equivalent(const Twine &A, const Twine &B, bool &result) {
- // Get arguments.
- SmallString<128> a_storage;
- SmallString<128> b_storage;
- StringRef a = A.toStringRef(a_storage);
- StringRef b = B.toStringRef(b_storage);
-
- // Convert to utf-16.
- SmallVector<wchar_t, 128> wide_a;
- SmallVector<wchar_t, 128> wide_b;
- if (error_code ec = UTF8ToUTF16(a, wide_a)) return ec;
- if (error_code ec = UTF8ToUTF16(b, wide_b)) return ec;
-
- ScopedFileHandle HandleB(
- ::CreateFileW(wide_b.begin(),
- 0,
- FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
- 0,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS,
- 0));
-
- ScopedFileHandle HandleA(
- ::CreateFileW(wide_a.begin(),
- 0,
- FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
- 0,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS,
- 0));
-
- // If both handles are invalid, it's an error.
- if (!HandleA && !HandleB)
- return windows_error(::GetLastError());
-
- // If only one is invalid, it's false.
- if (!HandleA || !HandleB) {
- result = false;
- return success;
- }
-
- // Get file information.
- BY_HANDLE_FILE_INFORMATION InfoA, InfoB;
- if (!::GetFileInformationByHandle(HandleA, &InfoA))
- return windows_error(::GetLastError());
- if (!::GetFileInformationByHandle(HandleB, &InfoB))
- return windows_error(::GetLastError());
-
- // See if it's all the same.
- result =
- InfoA.dwVolumeSerialNumber == InfoB.dwVolumeSerialNumber &&
- InfoA.nFileIndexHigh == InfoB.nFileIndexHigh &&
- InfoA.nFileIndexLow == InfoB.nFileIndexLow &&
- InfoA.nFileSizeHigh == InfoB.nFileSizeHigh &&
- InfoA.nFileSizeLow == InfoB.nFileSizeLow &&
- InfoA.ftLastWriteTime.dwLowDateTime ==
- InfoB.ftLastWriteTime.dwLowDateTime &&
- InfoA.ftLastWriteTime.dwHighDateTime ==
- InfoB.ftLastWriteTime.dwHighDateTime;
+bool equivalent(file_status A, file_status B) {
+ assert(status_known(A) && status_known(B));
+ return A.FileIndexHigh == B.FileIndexHigh &&
+ A.FileIndexLow == B.FileIndexLow &&
+ A.FileSizeHigh == B.FileSizeHigh &&
+ A.FileSizeLow == B.FileSizeLow &&
+ A.LastWriteTimeHigh == B.LastWriteTimeHigh &&
+ A.LastWriteTimeLow == B.LastWriteTimeLow &&
+ A.VolumeSerialNumber == B.VolumeSerialNumber;
+}
+error_code equivalent(const Twine &A, const Twine &B, bool &result) {
+ file_status fsA, fsB;
+ if (error_code ec = status(A, fsA)) return ec;
+ if (error_code ec = status(B, fsB)) return ec;
+ result = equivalent(fsA, fsB);
return success;
}
return success;
}
- if (error_code ec = UTF8ToUTF16(path8,
- path_utf16))
+ if (error_code ec = UTF8ToUTF16(path8, path_utf16))
return ec;
DWORD attr = ::GetFileAttributesW(path_utf16.begin());
if (attr & FILE_ATTRIBUTE_DIRECTORY)
result = file_status(file_type::directory_file);
- else
+ else {
result = file_status(file_type::regular_file);
+ ScopedFileHandle h(
+ ::CreateFileW(path_utf16.begin(),
+ 0, // Attributes only.
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ 0));
+ if (!h)
+ goto handle_status_error;
+ BY_HANDLE_FILE_INFORMATION Info;
+ if (!::GetFileInformationByHandle(h, &Info))
+ goto handle_status_error;
+ result.FileIndexHigh = Info.nFileIndexHigh;
+ result.FileIndexLow = Info.nFileIndexLow;
+ result.FileSizeHigh = Info.nFileSizeHigh;
+ result.FileSizeLow = Info.nFileSizeLow;
+ result.LastWriteTimeHigh = Info.ftLastWriteTime.dwHighDateTime;
+ result.LastWriteTimeLow = Info.ftLastWriteTime.dwLowDateTime;
+ result.VolumeSerialNumber = Info.dwVolumeSerialNumber;
+ }
return success;
ASSERT_NO_ERROR(fs::unique_file("%%-%%-%%-%%.temp", FD2, TempPath2));
ASSERT_NE(TempPath.str(), TempPath2.str());
+ fs::file_status A, B;
+ ASSERT_NO_ERROR(fs::status(Twine(TempPath), A));
+ ASSERT_NO_ERROR(fs::status(Twine(TempPath2), B));
+ EXPECT_FALSE(fs::equivalent(A, B));
+
// Try to copy the first to the second.
EXPECT_EQ(
fs::copy_file(Twine(TempPath), Twine(TempPath2)), errc::file_exists);
bool equal;
ASSERT_NO_ERROR(fs::equivalent(Twine(TempPath), Twine(TempPath2), equal));
EXPECT_TRUE(equal);
+ ASSERT_NO_ERROR(fs::status(Twine(TempPath), A));
+ ASSERT_NO_ERROR(fs::status(Twine(TempPath2), B));
+ EXPECT_TRUE(fs::equivalent(A, B));
// Remove Temp1.
::close(FileDescriptor);