Index: include/llvm/Support/FileSystem.h =================================================================== --- include/llvm/Support/FileSystem.h +++ include/llvm/Support/FileSystem.h @@ -181,7 +181,7 @@ file_status(file_type Type) : Type(Type) {} - file_status(file_type Type, uint32_t LastAccessTimeHigh, + 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, @@ -191,7 +191,7 @@ LastWriteTimeLow(LastWriteTimeLow), VolumeSerialNumber(VolumeSerialNumber), FileSizeHigh(FileSizeHigh), FileSizeLow(FileSizeLow), FileIndexHigh(FileIndexHigh), - FileIndexLow(FileIndexLow), Type(Type) {} + FileIndexLow(FileIndexLow), Type(Type), Perms(Perms) {} #endif // getters @@ -562,6 +562,16 @@ /// @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 Permissions New file permissions. +/// @returns errc::success if the permissions were successfully set, otherwise +/// a platform-specific erroc_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 permissions(const Twine &Path, perms Permissions); + /// @brief Get file size. /// /// @param Path Input path. Index: lib/Support/Unix/Path.inc =================================================================== --- lib/Support/Unix/Path.inc +++ lib/Support/Unix/Path.inc @@ -527,6 +527,15 @@ return fillStatus(StatRet, Status, Result); } +std::error_code permissions(const Twine &Path, sys::fs::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]; Index: lib/Support/Windows/Path.inc =================================================================== --- lib/Support/Windows/Path.inc +++ lib/Support/Windows/Path.inc @@ -529,13 +529,15 @@ 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); + 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(); } @@ -588,6 +590,24 @@ return getStatus(FileHandle, Result); } +std::error_code permissions(const Twine &Path, sys::fs::perms Permissions) { + SmallString<128> Storage; + SmallVector PathUTF16; + + StringRef Path8 = Path.toStringRef(Storage); + if (std::error_code ec = widenPath(Path8, PathUTF16)) + return ec; + + DWORD NewAttributes = FILE_ATTRIBUTE_NORMAL; + if (!(Permissions & + (sys::fs::owner_write | sys::fs::group_write | sys::fs::all_write))) + NewAttributes = FILE_ATTRIBUTE_READONLY; + if (!::SetFileAttributesW(PathUTF16.begin(), NewAttributes)) + 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)); Index: unittests/Support/Path.cpp =================================================================== --- unittests/Support/Path.cpp +++ unittests/Support/Path.cpp @@ -1188,4 +1188,217 @@ 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(sys::fs::exists(Twine(TempPath))); + + fs::file_status Status; + std::error_code NoError; + EXPECT_EQ(fs::permissions(TempPath, fs::all_all), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::all_all); + + EXPECT_EQ(fs::permissions(TempPath, fs::all_read | fs::all_exe), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::all_read | fs::all_exe); + +#if defined(LLVM_ON_WIN32) + fs::perms ReadOnly = fs::all_read | fs::all_exe; + EXPECT_EQ(fs::permissions(TempPath, fs::no_perms), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), ReadOnly); + + EXPECT_EQ(fs::permissions(TempPath, fs::owner_read), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), ReadOnly); + + EXPECT_EQ(fs::permissions(TempPath, fs::owner_write), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::all_all); + + EXPECT_EQ(fs::permissions(TempPath, fs::owner_exe), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), ReadOnly); + + EXPECT_EQ(fs::permissions(TempPath, fs::owner_all), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::all_all); + + EXPECT_EQ(fs::permissions(TempPath, fs::group_read), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), ReadOnly); + + EXPECT_EQ(fs::permissions(TempPath, fs::group_write), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::all_all); + + EXPECT_EQ(fs::permissions(TempPath, fs::group_exe), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), ReadOnly); + + EXPECT_EQ(fs::permissions(TempPath, fs::group_all), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::all_all); + + EXPECT_EQ(fs::permissions(TempPath, fs::others_read), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), ReadOnly); + + EXPECT_EQ(fs::permissions(TempPath, fs::others_write), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::all_all); + + EXPECT_EQ(fs::permissions(TempPath, fs::others_exe), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), ReadOnly); + + EXPECT_EQ(fs::permissions(TempPath, fs::others_all), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::all_all); + + EXPECT_EQ(fs::permissions(TempPath, fs::all_read), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), ReadOnly); + + EXPECT_EQ(fs::permissions(TempPath, fs::all_write), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::all_all); + + EXPECT_EQ(fs::permissions(TempPath, fs::all_exe), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), ReadOnly); + + EXPECT_EQ(fs::permissions(TempPath, fs::set_uid_on_exe), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), ReadOnly); + + EXPECT_EQ(fs::permissions(TempPath, fs::set_gid_on_exe), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), ReadOnly); + + EXPECT_EQ(fs::permissions(TempPath, fs::sticky_bit), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), ReadOnly); + + EXPECT_EQ(fs::permissions(TempPath, fs::set_uid_on_exe | fs::set_gid_on_exe | + fs::sticky_bit), + NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), ReadOnly); + + EXPECT_EQ(fs::permissions(TempPath, ReadOnly | fs::set_uid_on_exe | + fs::set_gid_on_exe | fs::sticky_bit), + NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), ReadOnly); + + EXPECT_EQ(fs::permissions(TempPath, fs::all_all | fs::set_uid_on_exe | + fs::set_gid_on_exe | fs::sticky_bit), + NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::all_all); +#else + EXPECT_EQ(fs::permissions(TempPath, fs::no_perms), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::no_perms); + + EXPECT_EQ(fs::permissions(TempPath, fs::owner_read), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::owner_read); + + EXPECT_EQ(fs::permissions(TempPath, fs::owner_write), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::owner_write); + + EXPECT_EQ(fs::permissions(TempPath, fs::owner_exe), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::owner_exe); + + EXPECT_EQ(fs::permissions(TempPath, fs::owner_all), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::owner_all); + + EXPECT_EQ(fs::permissions(TempPath, fs::group_read), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::group_read); + + EXPECT_EQ(fs::permissions(TempPath, fs::group_write), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::group_write); + + EXPECT_EQ(fs::permissions(TempPath, fs::group_exe), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::group_exe); + + EXPECT_EQ(fs::permissions(TempPath, fs::group_all), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::group_all); + + EXPECT_EQ(fs::permissions(TempPath, fs::others_read), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::others_read); + + EXPECT_EQ(fs::permissions(TempPath, fs::others_write), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::others_write); + + EXPECT_EQ(fs::permissions(TempPath, fs::others_exe), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::others_exe); + + EXPECT_EQ(fs::permissions(TempPath, fs::others_all), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::others_all); + + EXPECT_EQ(fs::permissions(TempPath, fs::all_read), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::all_read); + + EXPECT_EQ(fs::permissions(TempPath, fs::all_write), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::all_write); + + EXPECT_EQ(fs::permissions(TempPath, fs::all_exe), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::all_exe); + + EXPECT_EQ(fs::permissions(TempPath, fs::set_uid_on_exe), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::set_uid_on_exe); + + EXPECT_EQ(fs::permissions(TempPath, fs::set_gid_on_exe), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::set_gid_on_exe); + + EXPECT_EQ(fs::permissions(TempPath, fs::sticky_bit), NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::sticky_bit); + + EXPECT_EQ(fs::permissions(TempPath, fs::set_uid_on_exe | fs::set_gid_on_exe | + fs::sticky_bit), + NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), + fs::set_uid_on_exe | fs::set_gid_on_exe | fs::sticky_bit); + + EXPECT_EQ(fs::permissions(TempPath, fs::all_read | fs::set_uid_on_exe | + fs::set_gid_on_exe | fs::sticky_bit), + NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::all_read | fs::set_uid_on_exe | + fs::set_gid_on_exe | fs::sticky_bit); + + EXPECT_EQ(fs::permissions(TempPath, fs::all_all | fs::set_uid_on_exe | + fs::set_gid_on_exe | fs::sticky_bit), + NoError); + ASSERT_NO_ERROR(fs::status(FD, Status)); + EXPECT_EQ(Status.permissions(), fs::all_all | fs::set_uid_on_exe | + fs::set_gid_on_exe | fs::sticky_bit); +#endif +} } // anonymous namespace