diff --git a/llvm/include/llvm/Support/FileSystem.h b/llvm/include/llvm/Support/FileSystem.h --- a/llvm/include/llvm/Support/FileSystem.h +++ b/llvm/include/llvm/Support/FileSystem.h @@ -648,6 +648,14 @@ /// A version for when a file descriptor is already available. std::error_code status(int FD, file_status &Result); +/// Get file creation mode mask of the process. +/// +/// @returns Mask reported by umask(2) +/// @note There is no umask on Windows. This function returns 0 always +/// on Windows. This function does not return an error_code because +/// umask(2) never fails. +unsigned getUmask(); + /// Set file permissions. /// /// @param Path File to set permissions on. @@ -657,7 +665,8 @@ /// @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); +std::error_code setPermissions(const Twine &Path, perms Permissions, + bool RespectUmask = false); /// Get file permissions. /// diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc --- a/llvm/lib/Support/Unix/Path.inc +++ b/llvm/lib/Support/Unix/Path.inc @@ -691,10 +691,23 @@ return fillStatus(StatRet, Status, Result); } -std::error_code setPermissions(const Twine &Path, perms Permissions) { +unsigned getUmask() { + // Chose arbitary new mask and ret set the umask to the old mask. + // umask(2) never fails so ignore the return of the second call. + unsigned Mask = ::umask(0); + (void) ::umask(Mask); + return Mask; +} + +std::error_code setPermissions(const Twine &Path, perms Permissions, bool RespectUmask) { SmallString<128> PathStorage; StringRef P = Path.toNullTerminatedStringRef(PathStorage); + if (RespectUmask) { + unsigned Mask = getUmask(); + Permissions = static_cast(Permissions & ~Mask); + } + if (::chmod(P.begin(), Permissions)) return std::error_code(errno, std::generic_category()); return std::error_code(); diff --git a/llvm/lib/Support/Windows/Path.inc b/llvm/lib/Support/Windows/Path.inc --- a/llvm/lib/Support/Windows/Path.inc +++ b/llvm/lib/Support/Windows/Path.inc @@ -734,7 +734,11 @@ return getStatus(FileHandle, Result); } -std::error_code setPermissions(const Twine &Path, perms Permissions) { +unsigned getUmask() { + return 0; +} + +std::error_code setPermissions(const Twine &Path, perms Permissions, bool /*RespectUmask*/) { SmallVector PathUTF16; if (std::error_code EC = widenPath(Path, PathUTF16)) return EC; diff --git a/llvm/unittests/Support/Path.cpp b/llvm/unittests/Support/Path.cpp --- a/llvm/unittests/Support/Path.cpp +++ b/llvm/unittests/Support/Path.cpp @@ -1526,6 +1526,51 @@ EXPECT_EQ(TestDirectoryIsLocal, TempFileIsLocal); } +TEST_F(FileSystemTest, getUmask) { +#ifdef _WIN32 + EXPECT_EQ(fs::getUmask(), 0U) << "Should always be 0 on Windows."; +#else + unsigned OldMask = ::umask(0022); + unsigned CurrentMask = fs::getUmask(); + EXPECT_EQ(CurrentMask, 0022U) + << "getUmask() didn't return previously set umask()"; + EXPECT_EQ(::umask(OldMask), 0022) << "getUmask() may have changed umask()"; +#endif +} + +TEST_F(FileSystemTest, RespectUmask) { +#ifndef _WIN32 + unsigned OldMask = ::umask(0022); + + int FD; + SmallString<128> TempPath; + ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD, TempPath)); + + fs::perms AllRWE = static_cast(0777); + + ASSERT_NO_ERROR(fs::setPermissions(TempPath, AllRWE /*respectUmask=false*/)); + + ErrorOr Perms = fs::getPermissions(TempPath); + ASSERT_TRUE(!!Perms); + EXPECT_EQ(Perms.get(), AllRWE) << "setPermissions respects on default"; + + ASSERT_NO_ERROR(fs::setPermissions(TempPath, AllRWE, /*respectUmask=*/true)); + Perms = fs::getPermissions(TempPath); + ASSERT_TRUE(!!Perms); + EXPECT_EQ(Perms.get(), static_cast(0755)) << "Did not respect Umask"; + + (void)::umask(0057); + + ASSERT_NO_ERROR(fs::setPermissions(TempPath, AllRWE, /*respectUmask=*/true)); + Perms = fs::getPermissions(TempPath); + ASSERT_TRUE(!!Perms); + EXPECT_EQ(Perms.get(), static_cast(0720)) << "Did not respect Umask"; + + (void)::umask(OldMask); + (void)::close(FD); +#endif +} + TEST_F(FileSystemTest, set_current_path) { SmallString<128> path;