Index: include/llvm/Support/FileSystem.h =================================================================== --- include/llvm/Support/FileSystem.h +++ include/llvm/Support/FileSystem.h @@ -577,6 +577,19 @@ std::error_code status(const Twine &path, file_status &result, bool follow = true); +/// @brief Get file status as if by POSIX fstatat(). +/// +/// @param DirFD File descriptor of the working directory. +/// @param path Input path. +/// @param result Set to the file status. +/// @param follow When true, follows symlinks. Otherwise, the symlink itself is +/// statted. +/// @returns errc::success if result has been successfully set, otherwise a +/// platform-specific error_code. +std::error_code status_at(int DirFD, 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); @@ -726,6 +739,9 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD, SmallVectorImpl *RealPath = nullptr); +std::error_code openFileForRead(int DirFD, const Twine &Name, int &ResultFD, + SmallVectorImpl *RealPath = nullptr); + std::error_code getUniqueID(const Twine Path, UniqueID &Result); /// @brief Get disk space usage information. @@ -793,6 +809,7 @@ /// from the result of the iteration syscall, or the first time status is /// called. class directory_entry { + llvm::Optional CWDFD; std::string Path; bool FollowSymlinks; mutable file_status Status; @@ -800,7 +817,11 @@ public: explicit directory_entry(const Twine &path, bool follow_symlinks = true, file_status st = file_status()) - : Path(path.str()), FollowSymlinks(follow_symlinks), Status(st) {} + : directory_entry(llvm::None, path, follow_symlinks, st) {} + + explicit directory_entry(llvm::Optional CWDFD, const Twine &path, bool follow_symlinks = true, + file_status st = file_status()) + : CWDFD(CWDFD), Path(path.str()), FollowSymlinks(follow_symlinks), Status(st) {} directory_entry() = default; @@ -811,6 +832,8 @@ void replace_filename(const Twine &filename, file_status st = file_status()); + llvm::Optional cwd_fd() const { return CWDFD; } + const std::string &path() const { return Path; } std::error_code status(file_status &result) const; @@ -823,10 +846,11 @@ }; namespace detail { + std::error_code open_path_fd(llvm::Optional cwd_fd, const Twine& Name, int& result_fd) struct DirIterState; - std::error_code directory_iterator_construct(DirIterState &, StringRef, bool); + std::error_code directory_iterator_construct(DirIterState &, llvm::Optional, StringRef, bool); std::error_code directory_iterator_increment(DirIterState &); std::error_code directory_iterator_destruct(DirIterState &); @@ -850,13 +874,22 @@ bool FollowSymlinks = true; public: + explicit directory_iterator(int CWDFD, const Twine &path, std::error_code &ec, + bool follow_symlinks = true) + : FollowSymlinks(follow_symlinks) { + State = std::make_shared(); + SmallString<128> path_storage; + ec = detail::directory_iterator_construct( + *State, CWDFD, path.toStringRef(path_storage), FollowSymlinks); + } + explicit directory_iterator(const Twine &path, std::error_code &ec, bool follow_symlinks = true) : FollowSymlinks(follow_symlinks) { State = std::make_shared(); SmallString<128> path_storage; ec = detail::directory_iterator_construct( - *State, path.toStringRef(path_storage), FollowSymlinks); + *State, llvm::None, path.toStringRef(path_storage), FollowSymlinks); } explicit directory_iterator(const directory_entry &de, std::error_code &ec, @@ -864,7 +897,7 @@ : FollowSymlinks(follow_symlinks) { State = std::make_shared(); ec = - detail::directory_iterator_construct(*State, de.path(), FollowSymlinks); + detail::directory_iterator_construct(*State, de.cwd_fd(), de.path(), FollowSymlinks); } /// Construct end iterator. Index: lib/Support/Unix/Path.inc =================================================================== --- lib/Support/Unix/Path.inc +++ lib/Support/Unix/Path.inc @@ -574,6 +574,16 @@ return fillStatus(StatRet, Status, Result); } +std::error_code status_at(int DirFD, const Twine &Path, file_status &Result, bool Follow) { + SmallString<128> PathStorage; + StringRef P = Path.toNullTerminatedStringRef(PathStorage); + + struct stat Status; + int StatRet = ::fstatat(DirFD, P.begin(), &Status, Follow ? 0 : AT_SYMLINK_NOFOLLOW); + return fillStatus(StatRet, Status, Result); +} + + std::error_code status(int FD, file_status &Result) { struct stat Status; int StatRet = ::fstat(FD, &Status); @@ -679,11 +689,26 @@ return Process::getPageSize(); } +std::error_code detail::open_path_fd(llvm::Optional cwd_fd, const Twine& Name, int& result_fd) { +} + std::error_code detail::directory_iterator_construct(detail::DirIterState &it, + llvm::Optional cwd_fd, StringRef path, bool follow_symlinks) { SmallString<128> path_null(path); - DIR *directory = ::opendir(path_null.c_str()); + int directory_fd; + if (cwd_fd) { + // TODO: O_CLOEXEC? + directory_fd = ::openat(*cwd_fd, path_null.c_str(), O_RDONLY); + } else { + // TODO: O_CLOEXEC? + directory_fd = ::open(path_null.c_str(), O_RDONLY); + } + if (directory_fd == -1) + return std::error_code(errno, std::generic_category()); + + DIR *directory = ::fdopendir(directory_fd); if (!directory) return std::error_code(errno, std::generic_category()); @@ -728,7 +753,7 @@ } #endif -std::error_code openFileForRead(const Twine &Name, int &ResultFD, +static std::error_code openFileForReadImpl(llvm::Optional DirFD, const Twine &Name, int &ResultFD, SmallVectorImpl *RealPath) { SmallString<128> Storage; StringRef P = Name.toNullTerminatedStringRef(Storage); @@ -736,8 +761,15 @@ #ifdef O_CLOEXEC OpenFlags |= O_CLOEXEC; #endif - if ((ResultFD = sys::RetryAfterSignal(-1, open, P.begin(), OpenFlags)) < 0) + if (!DirFD) { + // Use 'open'. + if ((ResultFD = sys::RetryAfterSignal(-1, open, P.begin(), OpenFlags)) < 0) + return std::error_code(errno, std::generic_category()); + } else { + // Use 'openat'. + if ((ResultFD = sys::RetryAfterSignal(-1, openat, *DirFD, P.begin(), OpenFlags)) < 0) return std::error_code(errno, std::generic_category()); + } #ifndef O_CLOEXEC int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC); (void)r; @@ -762,6 +794,8 @@ if (CharCount > 0) RealPath->append(Buffer, Buffer + CharCount); } else { + if (DirFD) + return std::error_code(); // there's no equivalent of ::realpath that has a file descriptor. // Use ::realpath to get the real path name if (::realpath(P.begin(), Buffer) != nullptr) RealPath->append(Buffer, Buffer + strlen(Buffer)); @@ -770,6 +804,21 @@ return std::error_code(); } +std::error_code openDirectoryForAt(const Twine &Name, int &ResultFD, SmallVectorImpl *RealPath) { + // TODO: might use O_PATH flag here + return openFileForRead(Name, ResultFD, RealPath); +} + +std::error_code openFileForRead(const Twine &Name, int &ResultFD, + SmallVectorImpl *RealPath) { + return openFileForReadImpl(llvm::None, Name, ResultFD, RealPath); +} + +std::error_code openFileForRead(int DirFD, const Twine &Name, int &ResultFD, + SmallVectorImpl *RealPath) { + return openFileForReadImpl(DirFD, Name, ResultFD, RealPath); +} + std::error_code openFileForWrite(const Twine &Name, int &ResultFD, sys::fs::OpenFlags Flags, unsigned Mode) { // Verify that we don't have both "append" and "excl".