106c3fb27SDimitry Andric //===----------------------------------------------------------------------===//// 206c3fb27SDimitry Andric // 306c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 406c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 506c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 606c3fb27SDimitry Andric // 706c3fb27SDimitry Andric //===----------------------------------------------------------------------===//// 806c3fb27SDimitry Andric 906c3fb27SDimitry Andric #ifndef FILESYSTEM_FILE_DESCRIPTOR_H 1006c3fb27SDimitry Andric #define FILESYSTEM_FILE_DESCRIPTOR_H 1106c3fb27SDimitry Andric 1206c3fb27SDimitry Andric #include <__config> 1306c3fb27SDimitry Andric #include <cstdint> 1406c3fb27SDimitry Andric #include <filesystem> 1506c3fb27SDimitry Andric #include <string_view> 1606c3fb27SDimitry Andric #include <system_error> 1706c3fb27SDimitry Andric #include <utility> 1806c3fb27SDimitry Andric 1906c3fb27SDimitry Andric #include "error.h" 2006c3fb27SDimitry Andric #include "posix_compat.h" 2106c3fb27SDimitry Andric #include "time_utils.h" 2206c3fb27SDimitry Andric 2306c3fb27SDimitry Andric #if defined(_LIBCPP_WIN32API) 2406c3fb27SDimitry Andric # define WIN32_LEAN_AND_MEAN 2506c3fb27SDimitry Andric # define NOMINMAX 2606c3fb27SDimitry Andric # include <windows.h> 2706c3fb27SDimitry Andric #else 2806c3fb27SDimitry Andric # include <dirent.h> // for DIR & friends 2906c3fb27SDimitry Andric # include <fcntl.h> // values for fchmodat 3006c3fb27SDimitry Andric # include <sys/stat.h> 3106c3fb27SDimitry Andric # include <sys/statvfs.h> 3206c3fb27SDimitry Andric # include <unistd.h> 3306c3fb27SDimitry Andric #endif // defined(_LIBCPP_WIN32API) 3406c3fb27SDimitry Andric 3506c3fb27SDimitry Andric _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM 3606c3fb27SDimitry Andric 3706c3fb27SDimitry Andric namespace detail { 3806c3fb27SDimitry Andric 3906c3fb27SDimitry Andric #if !defined(_LIBCPP_WIN32API) 4006c3fb27SDimitry Andric 4106c3fb27SDimitry Andric #if defined(DT_BLK) 4206c3fb27SDimitry Andric template <class DirEntT, class = decltype(DirEntT::d_type)> 4306c3fb27SDimitry Andric file_type get_file_type(DirEntT* ent, int) { 4406c3fb27SDimitry Andric switch (ent->d_type) { 4506c3fb27SDimitry Andric case DT_BLK: 4606c3fb27SDimitry Andric return file_type::block; 4706c3fb27SDimitry Andric case DT_CHR: 4806c3fb27SDimitry Andric return file_type::character; 4906c3fb27SDimitry Andric case DT_DIR: 5006c3fb27SDimitry Andric return file_type::directory; 5106c3fb27SDimitry Andric case DT_FIFO: 5206c3fb27SDimitry Andric return file_type::fifo; 5306c3fb27SDimitry Andric case DT_LNK: 5406c3fb27SDimitry Andric return file_type::symlink; 5506c3fb27SDimitry Andric case DT_REG: 5606c3fb27SDimitry Andric return file_type::regular; 5706c3fb27SDimitry Andric case DT_SOCK: 5806c3fb27SDimitry Andric return file_type::socket; 5906c3fb27SDimitry Andric // Unlike in lstat, hitting "unknown" here simply means that the underlying 6006c3fb27SDimitry Andric // filesystem doesn't support d_type. Report is as 'none' so we correctly 6106c3fb27SDimitry Andric // set the cache to empty. 6206c3fb27SDimitry Andric case DT_UNKNOWN: 6306c3fb27SDimitry Andric break; 6406c3fb27SDimitry Andric } 6506c3fb27SDimitry Andric return file_type::none; 6606c3fb27SDimitry Andric } 6706c3fb27SDimitry Andric #endif // defined(DT_BLK) 6806c3fb27SDimitry Andric 6906c3fb27SDimitry Andric template <class DirEntT> 7006c3fb27SDimitry Andric file_type get_file_type(DirEntT*, long) { 7106c3fb27SDimitry Andric return file_type::none; 7206c3fb27SDimitry Andric } 7306c3fb27SDimitry Andric 7406c3fb27SDimitry Andric inline pair<string_view, file_type> posix_readdir(DIR* dir_stream, 7506c3fb27SDimitry Andric error_code& ec) { 7606c3fb27SDimitry Andric struct dirent* dir_entry_ptr = nullptr; 7706c3fb27SDimitry Andric errno = 0; // zero errno in order to detect errors 7806c3fb27SDimitry Andric ec.clear(); 7906c3fb27SDimitry Andric if ((dir_entry_ptr = ::readdir(dir_stream)) == nullptr) { 8006c3fb27SDimitry Andric if (errno) 8106c3fb27SDimitry Andric ec = capture_errno(); 8206c3fb27SDimitry Andric return {}; 8306c3fb27SDimitry Andric } else { 8406c3fb27SDimitry Andric return {dir_entry_ptr->d_name, get_file_type(dir_entry_ptr, 0)}; 8506c3fb27SDimitry Andric } 8606c3fb27SDimitry Andric } 8706c3fb27SDimitry Andric 8806c3fb27SDimitry Andric #else // _LIBCPP_WIN32API 8906c3fb27SDimitry Andric 9006c3fb27SDimitry Andric inline file_type get_file_type(const WIN32_FIND_DATAW& data) { 9106c3fb27SDimitry Andric if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && 9206c3fb27SDimitry Andric data.dwReserved0 == IO_REPARSE_TAG_SYMLINK) 9306c3fb27SDimitry Andric return file_type::symlink; 9406c3fb27SDimitry Andric if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 9506c3fb27SDimitry Andric return file_type::directory; 9606c3fb27SDimitry Andric return file_type::regular; 9706c3fb27SDimitry Andric } 9806c3fb27SDimitry Andric inline uintmax_t get_file_size(const WIN32_FIND_DATAW& data) { 9906c3fb27SDimitry Andric return (static_cast<uint64_t>(data.nFileSizeHigh) << 32) + data.nFileSizeLow; 10006c3fb27SDimitry Andric } 10106c3fb27SDimitry Andric inline file_time_type get_write_time(const WIN32_FIND_DATAW& data) { 10206c3fb27SDimitry Andric ULARGE_INTEGER tmp; 10306c3fb27SDimitry Andric const FILETIME& time = data.ftLastWriteTime; 10406c3fb27SDimitry Andric tmp.u.LowPart = time.dwLowDateTime; 10506c3fb27SDimitry Andric tmp.u.HighPart = time.dwHighDateTime; 10606c3fb27SDimitry Andric return file_time_type(file_time_type::duration(tmp.QuadPart)); 10706c3fb27SDimitry Andric } 10806c3fb27SDimitry Andric 10906c3fb27SDimitry Andric #endif // !_LIBCPP_WIN32API 11006c3fb27SDimitry Andric 11106c3fb27SDimitry Andric // POSIX HELPERS 11206c3fb27SDimitry Andric 11306c3fb27SDimitry Andric using value_type = path::value_type; 11406c3fb27SDimitry Andric using string_type = path::string_type; 11506c3fb27SDimitry Andric 11606c3fb27SDimitry Andric struct FileDescriptor { 11706c3fb27SDimitry Andric const path& name; 11806c3fb27SDimitry Andric int fd = -1; 11906c3fb27SDimitry Andric StatT m_stat; 12006c3fb27SDimitry Andric file_status m_status; 12106c3fb27SDimitry Andric 12206c3fb27SDimitry Andric template <class... Args> 12306c3fb27SDimitry Andric static FileDescriptor create(const path* p, error_code& ec, Args... args) { 12406c3fb27SDimitry Andric ec.clear(); 12506c3fb27SDimitry Andric int fd; 12606c3fb27SDimitry Andric #ifdef _LIBCPP_WIN32API 12706c3fb27SDimitry Andric // TODO: most of the filesystem implementation uses native Win32 calls 12806c3fb27SDimitry Andric // (mostly via posix_compat.h). However, here we use the C-runtime APIs to 12906c3fb27SDimitry Andric // open a file, because we subsequently pass the C-runtime fd to 13006c3fb27SDimitry Andric // `std::[io]fstream::__open(int fd)` in order to implement copy_file. 13106c3fb27SDimitry Andric // 13206c3fb27SDimitry Andric // Because we're calling the windows C-runtime, win32 error codes are 13306c3fb27SDimitry Andric // translated into C error numbers by the C runtime, and returned in errno, 13406c3fb27SDimitry Andric // rather than being accessible directly via GetLastError. 13506c3fb27SDimitry Andric // 13606c3fb27SDimitry Andric // Ideally copy_file should be calling the Win32 CopyFile2 function, which 13706c3fb27SDimitry Andric // works on paths, not open files -- at which point this FileDescriptor type 13806c3fb27SDimitry Andric // will no longer be needed on windows at all. 13906c3fb27SDimitry Andric fd = ::_wopen(p->c_str(), args...); 14006c3fb27SDimitry Andric #else 14106c3fb27SDimitry Andric fd = open(p->c_str(), args...); 14206c3fb27SDimitry Andric #endif 14306c3fb27SDimitry Andric 14406c3fb27SDimitry Andric if (fd == -1) { 14506c3fb27SDimitry Andric ec = capture_errno(); 14606c3fb27SDimitry Andric return FileDescriptor{p}; 14706c3fb27SDimitry Andric } 14806c3fb27SDimitry Andric return FileDescriptor(p, fd); 14906c3fb27SDimitry Andric } 15006c3fb27SDimitry Andric 15106c3fb27SDimitry Andric template <class... Args> 15206c3fb27SDimitry Andric static FileDescriptor create_with_status(const path* p, error_code& ec, 15306c3fb27SDimitry Andric Args... args) { 15406c3fb27SDimitry Andric FileDescriptor fd = create(p, ec, args...); 15506c3fb27SDimitry Andric if (!ec) 15606c3fb27SDimitry Andric fd.refresh_status(ec); 15706c3fb27SDimitry Andric 15806c3fb27SDimitry Andric return fd; 15906c3fb27SDimitry Andric } 16006c3fb27SDimitry Andric 16106c3fb27SDimitry Andric file_status get_status() const { return m_status; } 16206c3fb27SDimitry Andric StatT const& get_stat() const { return m_stat; } 16306c3fb27SDimitry Andric 164*5f757f3fSDimitry Andric bool status_known() const { return filesystem::status_known(m_status); } 16506c3fb27SDimitry Andric 16606c3fb27SDimitry Andric file_status refresh_status(error_code& ec); 16706c3fb27SDimitry Andric 16806c3fb27SDimitry Andric void close() noexcept { 16906c3fb27SDimitry Andric if (fd != -1) { 17006c3fb27SDimitry Andric #ifdef _LIBCPP_WIN32API 17106c3fb27SDimitry Andric ::_close(fd); 17206c3fb27SDimitry Andric #else 17306c3fb27SDimitry Andric ::close(fd); 17406c3fb27SDimitry Andric #endif 17506c3fb27SDimitry Andric // FIXME: shouldn't this return an error_code? 17606c3fb27SDimitry Andric } 17706c3fb27SDimitry Andric fd = -1; 17806c3fb27SDimitry Andric } 17906c3fb27SDimitry Andric 18006c3fb27SDimitry Andric FileDescriptor(FileDescriptor&& other) 18106c3fb27SDimitry Andric : name(other.name), fd(other.fd), m_stat(other.m_stat), 18206c3fb27SDimitry Andric m_status(other.m_status) { 18306c3fb27SDimitry Andric other.fd = -1; 18406c3fb27SDimitry Andric other.m_status = file_status{}; 18506c3fb27SDimitry Andric } 18606c3fb27SDimitry Andric 18706c3fb27SDimitry Andric ~FileDescriptor() { close(); } 18806c3fb27SDimitry Andric 18906c3fb27SDimitry Andric FileDescriptor(FileDescriptor const&) = delete; 19006c3fb27SDimitry Andric FileDescriptor& operator=(FileDescriptor const&) = delete; 19106c3fb27SDimitry Andric 19206c3fb27SDimitry Andric private: 19306c3fb27SDimitry Andric explicit FileDescriptor(const path* p, int descriptor = -1) : name(*p), fd(descriptor) {} 19406c3fb27SDimitry Andric }; 19506c3fb27SDimitry Andric 19606c3fb27SDimitry Andric inline perms posix_get_perms(const StatT& st) noexcept { 19706c3fb27SDimitry Andric return static_cast<perms>(st.st_mode) & perms::mask; 19806c3fb27SDimitry Andric } 19906c3fb27SDimitry Andric 20006c3fb27SDimitry Andric inline file_status create_file_status(error_code& m_ec, path const& p, 20106c3fb27SDimitry Andric const StatT& path_stat, error_code* ec) { 20206c3fb27SDimitry Andric if (ec) 20306c3fb27SDimitry Andric *ec = m_ec; 20406c3fb27SDimitry Andric if (m_ec && (m_ec.value() == ENOENT || m_ec.value() == ENOTDIR)) { 20506c3fb27SDimitry Andric return file_status(file_type::not_found); 20606c3fb27SDimitry Andric } else if (m_ec) { 20706c3fb27SDimitry Andric ErrorHandler<void> err("posix_stat", ec, &p); 20806c3fb27SDimitry Andric err.report(m_ec, "failed to determine attributes for the specified path"); 20906c3fb27SDimitry Andric return file_status(file_type::none); 21006c3fb27SDimitry Andric } 21106c3fb27SDimitry Andric // else 21206c3fb27SDimitry Andric 21306c3fb27SDimitry Andric file_status fs_tmp; 21406c3fb27SDimitry Andric auto const mode = path_stat.st_mode; 21506c3fb27SDimitry Andric if (S_ISLNK(mode)) 21606c3fb27SDimitry Andric fs_tmp.type(file_type::symlink); 21706c3fb27SDimitry Andric else if (S_ISREG(mode)) 21806c3fb27SDimitry Andric fs_tmp.type(file_type::regular); 21906c3fb27SDimitry Andric else if (S_ISDIR(mode)) 22006c3fb27SDimitry Andric fs_tmp.type(file_type::directory); 22106c3fb27SDimitry Andric else if (S_ISBLK(mode)) 22206c3fb27SDimitry Andric fs_tmp.type(file_type::block); 22306c3fb27SDimitry Andric else if (S_ISCHR(mode)) 22406c3fb27SDimitry Andric fs_tmp.type(file_type::character); 22506c3fb27SDimitry Andric else if (S_ISFIFO(mode)) 22606c3fb27SDimitry Andric fs_tmp.type(file_type::fifo); 22706c3fb27SDimitry Andric else if (S_ISSOCK(mode)) 22806c3fb27SDimitry Andric fs_tmp.type(file_type::socket); 22906c3fb27SDimitry Andric else 23006c3fb27SDimitry Andric fs_tmp.type(file_type::unknown); 23106c3fb27SDimitry Andric 23206c3fb27SDimitry Andric fs_tmp.permissions(detail::posix_get_perms(path_stat)); 23306c3fb27SDimitry Andric return fs_tmp; 23406c3fb27SDimitry Andric } 23506c3fb27SDimitry Andric 23606c3fb27SDimitry Andric inline file_status posix_stat(path const& p, StatT& path_stat, error_code* ec) { 23706c3fb27SDimitry Andric error_code m_ec; 23806c3fb27SDimitry Andric if (detail::stat(p.c_str(), &path_stat) == -1) 23906c3fb27SDimitry Andric m_ec = detail::capture_errno(); 24006c3fb27SDimitry Andric return create_file_status(m_ec, p, path_stat, ec); 24106c3fb27SDimitry Andric } 24206c3fb27SDimitry Andric 24306c3fb27SDimitry Andric inline file_status posix_stat(path const& p, error_code* ec) { 24406c3fb27SDimitry Andric StatT path_stat; 24506c3fb27SDimitry Andric return posix_stat(p, path_stat, ec); 24606c3fb27SDimitry Andric } 24706c3fb27SDimitry Andric 24806c3fb27SDimitry Andric inline file_status posix_lstat(path const& p, StatT& path_stat, error_code* ec) { 24906c3fb27SDimitry Andric error_code m_ec; 25006c3fb27SDimitry Andric if (detail::lstat(p.c_str(), &path_stat) == -1) 25106c3fb27SDimitry Andric m_ec = detail::capture_errno(); 25206c3fb27SDimitry Andric return create_file_status(m_ec, p, path_stat, ec); 25306c3fb27SDimitry Andric } 25406c3fb27SDimitry Andric 25506c3fb27SDimitry Andric inline file_status posix_lstat(path const& p, error_code* ec) { 25606c3fb27SDimitry Andric StatT path_stat; 25706c3fb27SDimitry Andric return posix_lstat(p, path_stat, ec); 25806c3fb27SDimitry Andric } 25906c3fb27SDimitry Andric 26006c3fb27SDimitry Andric // http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html 26106c3fb27SDimitry Andric inline bool posix_ftruncate(const FileDescriptor& fd, off_t to_size, error_code& ec) { 26206c3fb27SDimitry Andric if (detail::ftruncate(fd.fd, to_size) == -1) { 26306c3fb27SDimitry Andric ec = capture_errno(); 26406c3fb27SDimitry Andric return true; 26506c3fb27SDimitry Andric } 26606c3fb27SDimitry Andric ec.clear(); 26706c3fb27SDimitry Andric return false; 26806c3fb27SDimitry Andric } 26906c3fb27SDimitry Andric 27006c3fb27SDimitry Andric inline bool posix_fchmod(const FileDescriptor& fd, const StatT& st, error_code& ec) { 27106c3fb27SDimitry Andric if (detail::fchmod(fd.fd, st.st_mode) == -1) { 27206c3fb27SDimitry Andric ec = capture_errno(); 27306c3fb27SDimitry Andric return true; 27406c3fb27SDimitry Andric } 27506c3fb27SDimitry Andric ec.clear(); 27606c3fb27SDimitry Andric return false; 27706c3fb27SDimitry Andric } 27806c3fb27SDimitry Andric 27906c3fb27SDimitry Andric inline bool stat_equivalent(const StatT& st1, const StatT& st2) { 28006c3fb27SDimitry Andric return (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino); 28106c3fb27SDimitry Andric } 28206c3fb27SDimitry Andric 28306c3fb27SDimitry Andric inline file_status FileDescriptor::refresh_status(error_code& ec) { 28406c3fb27SDimitry Andric // FD must be open and good. 28506c3fb27SDimitry Andric m_status = file_status{}; 28606c3fb27SDimitry Andric m_stat = {}; 28706c3fb27SDimitry Andric error_code m_ec; 28806c3fb27SDimitry Andric if (detail::fstat(fd, &m_stat) == -1) 28906c3fb27SDimitry Andric m_ec = capture_errno(); 29006c3fb27SDimitry Andric m_status = create_file_status(m_ec, name, m_stat, &ec); 29106c3fb27SDimitry Andric return m_status; 29206c3fb27SDimitry Andric } 29306c3fb27SDimitry Andric 29406c3fb27SDimitry Andric } // end namespace detail 29506c3fb27SDimitry Andric 29606c3fb27SDimitry Andric _LIBCPP_END_NAMESPACE_FILESYSTEM 29706c3fb27SDimitry Andric 29806c3fb27SDimitry Andric #endif // FILESYSTEM_FILE_DESCRIPTOR_H 299