From 565edc044111fc6b1cdbea8dfabbd3735bb178a7 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Mon, 13 Mar 2017 12:17:14 +0000 Subject: [PATCH] Add support for getting file system permissions and implement sys::fs::permissions to set them. Patch by James Henderson. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@297617 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Support/FileSystem.h | 61 +++++++++---- lib/Support/Path.cpp | 20 ++-- lib/Support/Unix/Path.inc | 21 +++-- lib/Support/Windows/Path.inc | 71 +++++++++++---- unittests/Support/Path.cpp | 186 +++++++++++++++++++++++++++++++++++++- 5 files changed, 304 insertions(+), 55 deletions(-) diff --git a/include/llvm/Support/FileSystem.h b/include/llvm/Support/FileSystem.h index 7fa969dfcc7..e684c5a2390 100644 --- a/include/llvm/Support/FileSystem.h +++ b/include/llvm/Support/FileSystem.h @@ -178,23 +178,23 @@ public: Perms(Perms) {} #elif defined(LLVM_ON_WIN32) file_status() = default; - - file_status(file_type Type) : Type(Type) {} - - file_status(file_type Type, uint32_t LastAccessTimeHigh, - uint32_t LastAccessTimeLow, uint32_t LastWriteTimeHigh, - uint32_t LastWriteTimeLow, uint32_t VolumeSerialNumber, - uint32_t FileSizeHigh, uint32_t FileSizeLow, + + file_status(file_type Type) : Type(Type) {} + + file_status(file_type Type, perms Perms, uint32_t LastAccessTimeHigh, + uint32_t LastAccessTimeLow, uint32_t LastWriteTimeHigh, + uint32_t LastWriteTimeLow, uint32_t VolumeSerialNumber, + uint32_t FileSizeHigh, uint32_t FileSizeLow, uint32_t FileIndexHigh, uint32_t FileIndexLow) : LastAccessedTimeHigh(LastAccessTimeHigh), LastAccessedTimeLow(LastAccessTimeLow), LastWriteTimeHigh(LastWriteTimeHigh), - LastWriteTimeLow(LastWriteTimeLow), - VolumeSerialNumber(VolumeSerialNumber), FileSizeHigh(FileSizeHigh), - FileSizeLow(FileSizeLow), FileIndexHigh(FileIndexHigh), - FileIndexLow(FileIndexLow), Type(Type) {} - #endif - - // getters + LastWriteTimeLow(LastWriteTimeLow), + VolumeSerialNumber(VolumeSerialNumber), FileSizeHigh(FileSizeHigh), + FileSizeLow(FileSizeLow), FileIndexHigh(FileIndexHigh), + FileIndexLow(FileIndexLow), Type(Type), Perms(Perms) {} + #endif + + // getters file_type type() const { return Type; } perms permissions() const { return Perms; } TimePoint<> getLastAccessedTime() const; @@ -602,12 +602,33 @@ std::error_code is_other(const Twine &path, bool &result); std::error_code status(const Twine &path, file_status &result, bool follow = true); -/// @brief A version for when a file descriptor is already available. -std::error_code status(int FD, file_status &Result); - -/// @brief Get file size. -/// -/// @param Path Input path. +/// @brief A version for when a file descriptor is already available. +std::error_code status(int FD, file_status &Result); + +/// @brief Set file permissions. +/// +/// @param Path File to set permissions on. +/// @param Permissions New file permissions. +/// @returns errc::success if the permissions were successfully set, otherwise +/// a platform-specific error_code. +/// @note On Windows, all permissions except *_write are ignored. Using any of +/// owner_write, group_write, or all_write will make the file writable. +/// Otherwise, the file will be marked as read-only. +std::error_code setPermissions(const Twine &Path, perms Permissions); + +/// @brief Get file permissions. +/// +/// @param Path File to get permissions from. +/// @returns the permissions if they were successfully retrieved, otherwise a +/// platform-specific error_code. +/// @note On Windows, if the file does not have the FILE_ATTRIBUTE_READONLY +/// attribute, all_all will be returned. Otherwise, all_read | all_exe +/// will be returned. +ErrorOr getPermissions(const Twine &Path); + +/// @brief Get file size. +/// +/// @param Path Input path. /// @param Result Set to the size of the file in \a Path. /// @returns errc::success if result has been successfully set, otherwise a /// platform-specific error_code. diff --git a/lib/Support/Path.cpp b/lib/Support/Path.cpp index fc3cf525c93..5ac60e8dc3e 100644 --- a/lib/Support/Path.cpp +++ b/lib/Support/Path.cpp @@ -1189,12 +1189,20 @@ std::error_code identify_magic(const Twine &Path, file_magic &Result) { } std::error_code directory_entry::status(file_status &result) const { - return fs::status(Path, result, FollowSymlinks); -} - -} // end namespace fs -} // end namespace sys -} // end namespace llvm + return fs::status(Path, result, FollowSymlinks); +} + +ErrorOr getPermissions(const Twine &Path) { + file_status Status; + if (std::error_code EC = status(Path, Status)) + return EC; + + return Status.permissions(); +} + +} // end namespace fs +} // end namespace sys +} // end namespace llvm // Include the truly platform-specific parts. #if defined(LLVM_ON_UNIX) diff --git a/lib/Support/Unix/Path.inc b/lib/Support/Unix/Path.inc index cdc692b8f1f..b2669d51eb8 100644 --- a/lib/Support/Unix/Path.inc +++ b/lib/Support/Unix/Path.inc @@ -568,12 +568,21 @@ std::error_code status(const Twine &Path, file_status &Result, bool Follow) { std::error_code status(int FD, file_status &Result) { struct stat Status; int StatRet = ::fstat(FD, &Status); - return fillStatus(StatRet, Status, Result); -} - -std::error_code setLastModificationAndAccessTime(int FD, TimePoint<> Time) { -#if defined(HAVE_FUTIMENS) - timespec Times[2]; + return fillStatus(StatRet, Status, Result); +} + +std::error_code setPermissions(const Twine &Path, perms Permissions) { + SmallString<128> PathStorage; + StringRef P = Path.toNullTerminatedStringRef(PathStorage); + + if (::chmod(P.begin(), Permissions)) + return std::error_code(errno, std::generic_category()); + return std::error_code(); +} + +std::error_code setLastModificationAndAccessTime(int FD, TimePoint<> Time) { +#if defined(HAVE_FUTIMENS) + timespec Times[2]; Times[0] = Times[1] = sys::toTimeSpec(Time); if (::futimens(FD, Times)) return std::error_code(errno, std::generic_category()); diff --git a/lib/Support/Windows/Path.inc b/lib/Support/Windows/Path.inc index d8a14b41cb2..8ad1edd12e9 100644 --- a/lib/Support/Windows/Path.inc +++ b/lib/Support/Windows/Path.inc @@ -527,19 +527,21 @@ static std::error_code getStatus(HANDLE FileHandle, file_status &Result) { goto handle_status_error; { - file_type Type = (Info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - ? file_type::directory_file - : file_type::regular_file; - Result = - file_status(Type, Info.ftLastAccessTime.dwHighDateTime, - Info.ftLastAccessTime.dwLowDateTime, - Info.ftLastWriteTime.dwHighDateTime, - Info.ftLastWriteTime.dwLowDateTime, - Info.dwVolumeSerialNumber, Info.nFileSizeHigh, - Info.nFileSizeLow, Info.nFileIndexHigh, Info.nFileIndexLow); - return std::error_code(); - } - + file_type Type = (Info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + ? file_type::directory_file + : file_type::regular_file; + perms Permissions = (Info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) + ? (all_read | all_exe) + : all_all; + Result = file_status( + Type, Permissions, Info.ftLastAccessTime.dwHighDateTime, + Info.ftLastAccessTime.dwLowDateTime, + Info.ftLastWriteTime.dwHighDateTime, Info.ftLastWriteTime.dwLowDateTime, + Info.dwVolumeSerialNumber, Info.nFileSizeHigh, Info.nFileSizeLow, + Info.nFileIndexHigh, Info.nFileIndexLow); + return std::error_code(); + } + handle_status_error: DWORD LastError = ::GetLastError(); if (LastError == ERROR_FILE_NOT_FOUND || @@ -586,12 +588,43 @@ std::error_code status(const Twine &path, file_status &result, bool Follow) { std::error_code status(int FD, file_status &Result) { HANDLE FileHandle = reinterpret_cast(_get_osfhandle(FD)); - return getStatus(FileHandle, Result); -} - -std::error_code setLastModificationAndAccessTime(int FD, TimePoint<> Time) { - FILETIME FT = toFILETIME(Time); - HANDLE FileHandle = reinterpret_cast(_get_osfhandle(FD)); + return getStatus(FileHandle, Result); +} + +std::error_code setPermissions(const Twine &Path, perms Permissions) { + SmallVector PathUTF16; + if (std::error_code EC = widenPath(Path, PathUTF16)) + return EC; + + DWORD Attributes = ::GetFileAttributesW(PathUTF16.begin()); + if (Attributes == INVALID_FILE_ATTRIBUTES) + return mapWindowsError(GetLastError()); + + // There are many Windows file attributes that are not to do with the file + // permissions (e.g. FILE_ATTRIBUTE_HIDDEN). We need to be careful to preserve + // them. + if (Permissions & all_write) { + Attributes &= ~FILE_ATTRIBUTE_READONLY; + if (Attributes == 0) + // FILE_ATTRIBUTE_NORMAL indicates no other attributes are set. + Attributes |= FILE_ATTRIBUTE_NORMAL; + } + else { + Attributes |= FILE_ATTRIBUTE_READONLY; + // FILE_ATTRIBUTE_NORMAL is not compatible with any other attributes, so + // remove it, if it is present. + Attributes &= ~FILE_ATTRIBUTE_NORMAL; + } + + if (!::SetFileAttributesW(PathUTF16.begin(), Attributes)) + return mapWindowsError(GetLastError()); + + return std::error_code(); +} + +std::error_code setLastModificationAndAccessTime(int FD, TimePoint<> Time) { + FILETIME FT = toFILETIME(Time); + HANDLE FileHandle = reinterpret_cast(_get_osfhandle(FD)); if (!SetFileTime(FileHandle, NULL, &FT, &FT)) return mapWindowsError(::GetLastError()); return std::error_code(); diff --git a/unittests/Support/Path.cpp b/unittests/Support/Path.cpp index c9c98f5d70b..fd54c1284b0 100644 --- a/unittests/Support/Path.cpp +++ b/unittests/Support/Path.cpp @@ -1253,7 +1253,185 @@ TEST_F(FileSystemTest, set_current_path) { fs::UniqueID D1, D2; ASSERT_NO_ERROR(fs::getUniqueID(TestDirectory, D1)); ASSERT_NO_ERROR(fs::getUniqueID(path, D2)); - ASSERT_EQ(D1, D2) << "D1: " << TestDirectory << "\nD2: " << path; -} - -} // anonymous namespace + ASSERT_EQ(D1, D2) << "D1: " << TestDirectory << "\nD2: " << path; +} + +TEST_F(FileSystemTest, permissions) { + int FD; + SmallString<64> TempPath; + ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD, TempPath)); + FileRemover Cleanup(TempPath); + + // Make sure it exists. + ASSERT_TRUE(fs::exists(Twine(TempPath))); + + auto CheckPermissions = [&](fs::perms Expected) { + ErrorOr Actual = fs::getPermissions(TempPath); + return Actual && *Actual == Expected; + }; + + std::error_code NoError; + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_all), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read | fs::all_exe), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_read | fs::all_exe)); + +#if defined(LLVM_ON_WIN32) + fs::perms ReadOnly = fs::all_read | fs::all_exe; + EXPECT_EQ(fs::setPermissions(TempPath, fs::no_perms), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_read), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_write), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_exe), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_all), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::group_read), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::group_write), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::group_exe), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::group_all), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::others_read), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::others_write), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::others_exe), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::others_all), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_write), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_exe), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::set_uid_on_exe), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::set_gid_on_exe), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::sticky_bit), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::set_uid_on_exe | + fs::set_gid_on_exe | + fs::sticky_bit), + NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, ReadOnly | fs::set_uid_on_exe | + fs::set_gid_on_exe | + fs::sticky_bit), + NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_all | fs::set_uid_on_exe | + fs::set_gid_on_exe | + fs::sticky_bit), + NoError); + EXPECT_TRUE(CheckPermissions(fs::all_all)); +#else + EXPECT_EQ(fs::setPermissions(TempPath, fs::no_perms), NoError); + EXPECT_TRUE(CheckPermissions(fs::no_perms)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_read), NoError); + EXPECT_TRUE(CheckPermissions(fs::owner_read)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_write), NoError); + EXPECT_TRUE(CheckPermissions(fs::owner_write)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_exe), NoError); + EXPECT_TRUE(CheckPermissions(fs::owner_exe)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_all), NoError); + EXPECT_TRUE(CheckPermissions(fs::owner_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::group_read), NoError); + EXPECT_TRUE(CheckPermissions(fs::group_read)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::group_write), NoError); + EXPECT_TRUE(CheckPermissions(fs::group_write)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::group_exe), NoError); + EXPECT_TRUE(CheckPermissions(fs::group_exe)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::group_all), NoError); + EXPECT_TRUE(CheckPermissions(fs::group_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::others_read), NoError); + EXPECT_TRUE(CheckPermissions(fs::others_read)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::others_write), NoError); + EXPECT_TRUE(CheckPermissions(fs::others_write)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::others_exe), NoError); + EXPECT_TRUE(CheckPermissions(fs::others_exe)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::others_all), NoError); + EXPECT_TRUE(CheckPermissions(fs::others_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_read)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_write), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_write)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_exe), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_exe)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::set_uid_on_exe), NoError); + EXPECT_TRUE(CheckPermissions(fs::set_uid_on_exe)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::set_gid_on_exe), NoError); + EXPECT_TRUE(CheckPermissions(fs::set_gid_on_exe)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::sticky_bit), NoError); + EXPECT_TRUE(CheckPermissions(fs::sticky_bit)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::set_uid_on_exe | + fs::set_gid_on_exe | + fs::sticky_bit), + NoError); + EXPECT_TRUE(CheckPermissions(fs::set_uid_on_exe | fs::set_gid_on_exe | + fs::sticky_bit)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read | fs::set_uid_on_exe | + fs::set_gid_on_exe | + fs::sticky_bit), + NoError); + EXPECT_TRUE(CheckPermissions(fs::all_read | fs::set_uid_on_exe | + fs::set_gid_on_exe | fs::sticky_bit)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_all | fs::set_uid_on_exe | + fs::set_gid_on_exe | + fs::sticky_bit), + NoError); + EXPECT_TRUE(CheckPermissions(fs::all_all | fs::set_uid_on_exe | + fs::set_gid_on_exe | fs::sticky_bit)); +#endif +} + +} // anonymous namespace -- 2.11.0