From 566fdf4a2a898bc9b1bd4b5e1cd110b8e94d2fcc Mon Sep 17 00:00:00 2001 From: James Henderson Date: Thu, 16 Mar 2017 11:22:09 +0000 Subject: [Support] Add support for getting file system permissions on Windows and implement sys::fs::set/getPermissions to work with them This change adds support for functions to set and get file permissions, in a similar manner to the C++17 permissions() function in . The setter uses chmod on Unix systems and SetFileAttributes on Windows, setting the permissions as passed in. The getter simply uses the existing status() function. Prior to this change, status() would always return an unknown value for the permissions on a Windows file, making it impossible to test the new function on Windows. I have therefore added support for this as well. On Linux, prior to this change, the permissions included the file type, which should actually be accessed via a different member of the file_status class. Note that on Windows, only the *_write permission bits have any affect - if any are set, the file is writable, and if not, the file is read-only. This is in common with what MSDN describes for their behaviour of std::filesystem::permissions(), and also what boost::filesystem does. The motivation behind this change is so that we can easily test behaviour on read-only files in LLVM unit tests, but I am sure that others may find it useful in some situations. Reviewers: zturner, amccarth, aaron.ballman Differential Revision: https://reviews.llvm.org/D30736 llvm-svn: 297945 --- llvm/lib/Support/Path.cpp | 8 +++++++ llvm/lib/Support/Unix/Path.inc | 11 ++++++++- llvm/lib/Support/Windows/Path.inc | 47 +++++++++++++++++++++++++++++++++------ 3 files changed, 58 insertions(+), 8 deletions(-) (limited to 'llvm/lib') diff --git a/llvm/lib/Support/Path.cpp b/llvm/lib/Support/Path.cpp index fc3cf525c93..26b3eadb289 100644 --- a/llvm/lib/Support/Path.cpp +++ b/llvm/lib/Support/Path.cpp @@ -1192,6 +1192,14 @@ std::error_code directory_entry::status(file_status &result) const { 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 diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc index cdc692b8f1f..c1ffd1eeb29 100644 --- a/llvm/lib/Support/Unix/Path.inc +++ b/llvm/lib/Support/Unix/Path.inc @@ -547,7 +547,7 @@ static std::error_code fillStatus(int StatRet, const struct stat &Status, else if (S_ISLNK(Status.st_mode)) Type = file_type::symlink_file; - perms Perms = static_cast(Status.st_mode); + perms Perms = static_cast(Status.st_mode) & all_perms; Result = file_status(Type, Perms, Status.st_dev, Status.st_ino, Status.st_atime, Status.st_mtime, Status.st_uid, Status.st_gid, @@ -571,6 +571,15 @@ std::error_code status(int FD, file_status &Result) { 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]; diff --git a/llvm/lib/Support/Windows/Path.inc b/llvm/lib/Support/Windows/Path.inc index d8a14b41cb2..f8a75a21e48 100644 --- a/llvm/lib/Support/Windows/Path.inc +++ b/llvm/lib/Support/Windows/Path.inc @@ -530,13 +530,15 @@ static std::error_code getStatus(HANDLE FileHandle, file_status &Result) { 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(); } @@ -589,6 +591,37 @@ std::error_code status(int FD, file_status &Result) { 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)); -- cgit v1.2.3