1*06c3fb27SDimitry Andric //===----------------------------------------------------------------------===//// 2*06c3fb27SDimitry Andric // 3*06c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*06c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*06c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*06c3fb27SDimitry Andric // 7*06c3fb27SDimitry Andric //===----------------------------------------------------------------------===//// 8*06c3fb27SDimitry Andric 9*06c3fb27SDimitry Andric #ifndef FILESYSTEM_FILE_DESCRIPTOR_H 10*06c3fb27SDimitry Andric #define FILESYSTEM_FILE_DESCRIPTOR_H 11*06c3fb27SDimitry Andric 12*06c3fb27SDimitry Andric #include <__config> 13*06c3fb27SDimitry Andric #include <cstdint> 14*06c3fb27SDimitry Andric #include <filesystem> 15*06c3fb27SDimitry Andric #include <string_view> 16*06c3fb27SDimitry Andric #include <system_error> 17*06c3fb27SDimitry Andric #include <utility> 18*06c3fb27SDimitry Andric 19*06c3fb27SDimitry Andric #include "error.h" 20*06c3fb27SDimitry Andric #include "posix_compat.h" 21*06c3fb27SDimitry Andric #include "time_utils.h" 22*06c3fb27SDimitry Andric 23*06c3fb27SDimitry Andric #if defined(_LIBCPP_WIN32API) 24*06c3fb27SDimitry Andric # define WIN32_LEAN_AND_MEAN 25*06c3fb27SDimitry Andric # define NOMINMAX 26*06c3fb27SDimitry Andric # include <windows.h> 27*06c3fb27SDimitry Andric #else 28*06c3fb27SDimitry Andric # include <dirent.h> // for DIR & friends 29*06c3fb27SDimitry Andric # include <fcntl.h> // values for fchmodat 30*06c3fb27SDimitry Andric # include <sys/stat.h> 31*06c3fb27SDimitry Andric # include <sys/statvfs.h> 32*06c3fb27SDimitry Andric # include <unistd.h> 33*06c3fb27SDimitry Andric #endif // defined(_LIBCPP_WIN32API) 34*06c3fb27SDimitry Andric 35*06c3fb27SDimitry Andric _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM 36*06c3fb27SDimitry Andric 37*06c3fb27SDimitry Andric namespace detail { 38*06c3fb27SDimitry Andric 39*06c3fb27SDimitry Andric #if !defined(_LIBCPP_WIN32API) 40*06c3fb27SDimitry Andric 41*06c3fb27SDimitry Andric #if defined(DT_BLK) 42*06c3fb27SDimitry Andric template <class DirEntT, class = decltype(DirEntT::d_type)> 43*06c3fb27SDimitry Andric file_type get_file_type(DirEntT* ent, int) { 44*06c3fb27SDimitry Andric switch (ent->d_type) { 45*06c3fb27SDimitry Andric case DT_BLK: 46*06c3fb27SDimitry Andric return file_type::block; 47*06c3fb27SDimitry Andric case DT_CHR: 48*06c3fb27SDimitry Andric return file_type::character; 49*06c3fb27SDimitry Andric case DT_DIR: 50*06c3fb27SDimitry Andric return file_type::directory; 51*06c3fb27SDimitry Andric case DT_FIFO: 52*06c3fb27SDimitry Andric return file_type::fifo; 53*06c3fb27SDimitry Andric case DT_LNK: 54*06c3fb27SDimitry Andric return file_type::symlink; 55*06c3fb27SDimitry Andric case DT_REG: 56*06c3fb27SDimitry Andric return file_type::regular; 57*06c3fb27SDimitry Andric case DT_SOCK: 58*06c3fb27SDimitry Andric return file_type::socket; 59*06c3fb27SDimitry Andric // Unlike in lstat, hitting "unknown" here simply means that the underlying 60*06c3fb27SDimitry Andric // filesystem doesn't support d_type. Report is as 'none' so we correctly 61*06c3fb27SDimitry Andric // set the cache to empty. 62*06c3fb27SDimitry Andric case DT_UNKNOWN: 63*06c3fb27SDimitry Andric break; 64*06c3fb27SDimitry Andric } 65*06c3fb27SDimitry Andric return file_type::none; 66*06c3fb27SDimitry Andric } 67*06c3fb27SDimitry Andric #endif // defined(DT_BLK) 68*06c3fb27SDimitry Andric 69*06c3fb27SDimitry Andric template <class DirEntT> 70*06c3fb27SDimitry Andric file_type get_file_type(DirEntT*, long) { 71*06c3fb27SDimitry Andric return file_type::none; 72*06c3fb27SDimitry Andric } 73*06c3fb27SDimitry Andric 74*06c3fb27SDimitry Andric inline pair<string_view, file_type> posix_readdir(DIR* dir_stream, 75*06c3fb27SDimitry Andric error_code& ec) { 76*06c3fb27SDimitry Andric struct dirent* dir_entry_ptr = nullptr; 77*06c3fb27SDimitry Andric errno = 0; // zero errno in order to detect errors 78*06c3fb27SDimitry Andric ec.clear(); 79*06c3fb27SDimitry Andric if ((dir_entry_ptr = ::readdir(dir_stream)) == nullptr) { 80*06c3fb27SDimitry Andric if (errno) 81*06c3fb27SDimitry Andric ec = capture_errno(); 82*06c3fb27SDimitry Andric return {}; 83*06c3fb27SDimitry Andric } else { 84*06c3fb27SDimitry Andric return {dir_entry_ptr->d_name, get_file_type(dir_entry_ptr, 0)}; 85*06c3fb27SDimitry Andric } 86*06c3fb27SDimitry Andric } 87*06c3fb27SDimitry Andric 88*06c3fb27SDimitry Andric #else // _LIBCPP_WIN32API 89*06c3fb27SDimitry Andric 90*06c3fb27SDimitry Andric inline file_type get_file_type(const WIN32_FIND_DATAW& data) { 91*06c3fb27SDimitry Andric if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && 92*06c3fb27SDimitry Andric data.dwReserved0 == IO_REPARSE_TAG_SYMLINK) 93*06c3fb27SDimitry Andric return file_type::symlink; 94*06c3fb27SDimitry Andric if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 95*06c3fb27SDimitry Andric return file_type::directory; 96*06c3fb27SDimitry Andric return file_type::regular; 97*06c3fb27SDimitry Andric } 98*06c3fb27SDimitry Andric inline uintmax_t get_file_size(const WIN32_FIND_DATAW& data) { 99*06c3fb27SDimitry Andric return (static_cast<uint64_t>(data.nFileSizeHigh) << 32) + data.nFileSizeLow; 100*06c3fb27SDimitry Andric } 101*06c3fb27SDimitry Andric inline file_time_type get_write_time(const WIN32_FIND_DATAW& data) { 102*06c3fb27SDimitry Andric ULARGE_INTEGER tmp; 103*06c3fb27SDimitry Andric const FILETIME& time = data.ftLastWriteTime; 104*06c3fb27SDimitry Andric tmp.u.LowPart = time.dwLowDateTime; 105*06c3fb27SDimitry Andric tmp.u.HighPart = time.dwHighDateTime; 106*06c3fb27SDimitry Andric return file_time_type(file_time_type::duration(tmp.QuadPart)); 107*06c3fb27SDimitry Andric } 108*06c3fb27SDimitry Andric 109*06c3fb27SDimitry Andric #endif // !_LIBCPP_WIN32API 110*06c3fb27SDimitry Andric 111*06c3fb27SDimitry Andric // POSIX HELPERS 112*06c3fb27SDimitry Andric 113*06c3fb27SDimitry Andric using value_type = path::value_type; 114*06c3fb27SDimitry Andric using string_type = path::string_type; 115*06c3fb27SDimitry Andric 116*06c3fb27SDimitry Andric struct FileDescriptor { 117*06c3fb27SDimitry Andric const path& name; 118*06c3fb27SDimitry Andric int fd = -1; 119*06c3fb27SDimitry Andric StatT m_stat; 120*06c3fb27SDimitry Andric file_status m_status; 121*06c3fb27SDimitry Andric 122*06c3fb27SDimitry Andric template <class... Args> 123*06c3fb27SDimitry Andric static FileDescriptor create(const path* p, error_code& ec, Args... args) { 124*06c3fb27SDimitry Andric ec.clear(); 125*06c3fb27SDimitry Andric int fd; 126*06c3fb27SDimitry Andric #ifdef _LIBCPP_WIN32API 127*06c3fb27SDimitry Andric // TODO: most of the filesystem implementation uses native Win32 calls 128*06c3fb27SDimitry Andric // (mostly via posix_compat.h). However, here we use the C-runtime APIs to 129*06c3fb27SDimitry Andric // open a file, because we subsequently pass the C-runtime fd to 130*06c3fb27SDimitry Andric // `std::[io]fstream::__open(int fd)` in order to implement copy_file. 131*06c3fb27SDimitry Andric // 132*06c3fb27SDimitry Andric // Because we're calling the windows C-runtime, win32 error codes are 133*06c3fb27SDimitry Andric // translated into C error numbers by the C runtime, and returned in errno, 134*06c3fb27SDimitry Andric // rather than being accessible directly via GetLastError. 135*06c3fb27SDimitry Andric // 136*06c3fb27SDimitry Andric // Ideally copy_file should be calling the Win32 CopyFile2 function, which 137*06c3fb27SDimitry Andric // works on paths, not open files -- at which point this FileDescriptor type 138*06c3fb27SDimitry Andric // will no longer be needed on windows at all. 139*06c3fb27SDimitry Andric fd = ::_wopen(p->c_str(), args...); 140*06c3fb27SDimitry Andric #else 141*06c3fb27SDimitry Andric fd = open(p->c_str(), args...); 142*06c3fb27SDimitry Andric #endif 143*06c3fb27SDimitry Andric 144*06c3fb27SDimitry Andric if (fd == -1) { 145*06c3fb27SDimitry Andric ec = capture_errno(); 146*06c3fb27SDimitry Andric return FileDescriptor{p}; 147*06c3fb27SDimitry Andric } 148*06c3fb27SDimitry Andric return FileDescriptor(p, fd); 149*06c3fb27SDimitry Andric } 150*06c3fb27SDimitry Andric 151*06c3fb27SDimitry Andric template <class... Args> 152*06c3fb27SDimitry Andric static FileDescriptor create_with_status(const path* p, error_code& ec, 153*06c3fb27SDimitry Andric Args... args) { 154*06c3fb27SDimitry Andric FileDescriptor fd = create(p, ec, args...); 155*06c3fb27SDimitry Andric if (!ec) 156*06c3fb27SDimitry Andric fd.refresh_status(ec); 157*06c3fb27SDimitry Andric 158*06c3fb27SDimitry Andric return fd; 159*06c3fb27SDimitry Andric } 160*06c3fb27SDimitry Andric 161*06c3fb27SDimitry Andric file_status get_status() const { return m_status; } 162*06c3fb27SDimitry Andric StatT const& get_stat() const { return m_stat; } 163*06c3fb27SDimitry Andric 164*06c3fb27SDimitry Andric bool status_known() const { return _VSTD_FS::status_known(m_status); } 165*06c3fb27SDimitry Andric 166*06c3fb27SDimitry Andric file_status refresh_status(error_code& ec); 167*06c3fb27SDimitry Andric 168*06c3fb27SDimitry Andric void close() noexcept { 169*06c3fb27SDimitry Andric if (fd != -1) { 170*06c3fb27SDimitry Andric #ifdef _LIBCPP_WIN32API 171*06c3fb27SDimitry Andric ::_close(fd); 172*06c3fb27SDimitry Andric #else 173*06c3fb27SDimitry Andric ::close(fd); 174*06c3fb27SDimitry Andric #endif 175*06c3fb27SDimitry Andric // FIXME: shouldn't this return an error_code? 176*06c3fb27SDimitry Andric } 177*06c3fb27SDimitry Andric fd = -1; 178*06c3fb27SDimitry Andric } 179*06c3fb27SDimitry Andric 180*06c3fb27SDimitry Andric FileDescriptor(FileDescriptor&& other) 181*06c3fb27SDimitry Andric : name(other.name), fd(other.fd), m_stat(other.m_stat), 182*06c3fb27SDimitry Andric m_status(other.m_status) { 183*06c3fb27SDimitry Andric other.fd = -1; 184*06c3fb27SDimitry Andric other.m_status = file_status{}; 185*06c3fb27SDimitry Andric } 186*06c3fb27SDimitry Andric 187*06c3fb27SDimitry Andric ~FileDescriptor() { close(); } 188*06c3fb27SDimitry Andric 189*06c3fb27SDimitry Andric FileDescriptor(FileDescriptor const&) = delete; 190*06c3fb27SDimitry Andric FileDescriptor& operator=(FileDescriptor const&) = delete; 191*06c3fb27SDimitry Andric 192*06c3fb27SDimitry Andric private: 193*06c3fb27SDimitry Andric explicit FileDescriptor(const path* p, int descriptor = -1) : name(*p), fd(descriptor) {} 194*06c3fb27SDimitry Andric }; 195*06c3fb27SDimitry Andric 196*06c3fb27SDimitry Andric inline perms posix_get_perms(const StatT& st) noexcept { 197*06c3fb27SDimitry Andric return static_cast<perms>(st.st_mode) & perms::mask; 198*06c3fb27SDimitry Andric } 199*06c3fb27SDimitry Andric 200*06c3fb27SDimitry Andric inline file_status create_file_status(error_code& m_ec, path const& p, 201*06c3fb27SDimitry Andric const StatT& path_stat, error_code* ec) { 202*06c3fb27SDimitry Andric if (ec) 203*06c3fb27SDimitry Andric *ec = m_ec; 204*06c3fb27SDimitry Andric if (m_ec && (m_ec.value() == ENOENT || m_ec.value() == ENOTDIR)) { 205*06c3fb27SDimitry Andric return file_status(file_type::not_found); 206*06c3fb27SDimitry Andric } else if (m_ec) { 207*06c3fb27SDimitry Andric ErrorHandler<void> err("posix_stat", ec, &p); 208*06c3fb27SDimitry Andric err.report(m_ec, "failed to determine attributes for the specified path"); 209*06c3fb27SDimitry Andric return file_status(file_type::none); 210*06c3fb27SDimitry Andric } 211*06c3fb27SDimitry Andric // else 212*06c3fb27SDimitry Andric 213*06c3fb27SDimitry Andric file_status fs_tmp; 214*06c3fb27SDimitry Andric auto const mode = path_stat.st_mode; 215*06c3fb27SDimitry Andric if (S_ISLNK(mode)) 216*06c3fb27SDimitry Andric fs_tmp.type(file_type::symlink); 217*06c3fb27SDimitry Andric else if (S_ISREG(mode)) 218*06c3fb27SDimitry Andric fs_tmp.type(file_type::regular); 219*06c3fb27SDimitry Andric else if (S_ISDIR(mode)) 220*06c3fb27SDimitry Andric fs_tmp.type(file_type::directory); 221*06c3fb27SDimitry Andric else if (S_ISBLK(mode)) 222*06c3fb27SDimitry Andric fs_tmp.type(file_type::block); 223*06c3fb27SDimitry Andric else if (S_ISCHR(mode)) 224*06c3fb27SDimitry Andric fs_tmp.type(file_type::character); 225*06c3fb27SDimitry Andric else if (S_ISFIFO(mode)) 226*06c3fb27SDimitry Andric fs_tmp.type(file_type::fifo); 227*06c3fb27SDimitry Andric else if (S_ISSOCK(mode)) 228*06c3fb27SDimitry Andric fs_tmp.type(file_type::socket); 229*06c3fb27SDimitry Andric else 230*06c3fb27SDimitry Andric fs_tmp.type(file_type::unknown); 231*06c3fb27SDimitry Andric 232*06c3fb27SDimitry Andric fs_tmp.permissions(detail::posix_get_perms(path_stat)); 233*06c3fb27SDimitry Andric return fs_tmp; 234*06c3fb27SDimitry Andric } 235*06c3fb27SDimitry Andric 236*06c3fb27SDimitry Andric inline file_status posix_stat(path const& p, StatT& path_stat, error_code* ec) { 237*06c3fb27SDimitry Andric error_code m_ec; 238*06c3fb27SDimitry Andric if (detail::stat(p.c_str(), &path_stat) == -1) 239*06c3fb27SDimitry Andric m_ec = detail::capture_errno(); 240*06c3fb27SDimitry Andric return create_file_status(m_ec, p, path_stat, ec); 241*06c3fb27SDimitry Andric } 242*06c3fb27SDimitry Andric 243*06c3fb27SDimitry Andric inline file_status posix_stat(path const& p, error_code* ec) { 244*06c3fb27SDimitry Andric StatT path_stat; 245*06c3fb27SDimitry Andric return posix_stat(p, path_stat, ec); 246*06c3fb27SDimitry Andric } 247*06c3fb27SDimitry Andric 248*06c3fb27SDimitry Andric inline file_status posix_lstat(path const& p, StatT& path_stat, error_code* ec) { 249*06c3fb27SDimitry Andric error_code m_ec; 250*06c3fb27SDimitry Andric if (detail::lstat(p.c_str(), &path_stat) == -1) 251*06c3fb27SDimitry Andric m_ec = detail::capture_errno(); 252*06c3fb27SDimitry Andric return create_file_status(m_ec, p, path_stat, ec); 253*06c3fb27SDimitry Andric } 254*06c3fb27SDimitry Andric 255*06c3fb27SDimitry Andric inline file_status posix_lstat(path const& p, error_code* ec) { 256*06c3fb27SDimitry Andric StatT path_stat; 257*06c3fb27SDimitry Andric return posix_lstat(p, path_stat, ec); 258*06c3fb27SDimitry Andric } 259*06c3fb27SDimitry Andric 260*06c3fb27SDimitry Andric // http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html 261*06c3fb27SDimitry Andric inline bool posix_ftruncate(const FileDescriptor& fd, off_t to_size, error_code& ec) { 262*06c3fb27SDimitry Andric if (detail::ftruncate(fd.fd, to_size) == -1) { 263*06c3fb27SDimitry Andric ec = capture_errno(); 264*06c3fb27SDimitry Andric return true; 265*06c3fb27SDimitry Andric } 266*06c3fb27SDimitry Andric ec.clear(); 267*06c3fb27SDimitry Andric return false; 268*06c3fb27SDimitry Andric } 269*06c3fb27SDimitry Andric 270*06c3fb27SDimitry Andric inline bool posix_fchmod(const FileDescriptor& fd, const StatT& st, error_code& ec) { 271*06c3fb27SDimitry Andric if (detail::fchmod(fd.fd, st.st_mode) == -1) { 272*06c3fb27SDimitry Andric ec = capture_errno(); 273*06c3fb27SDimitry Andric return true; 274*06c3fb27SDimitry Andric } 275*06c3fb27SDimitry Andric ec.clear(); 276*06c3fb27SDimitry Andric return false; 277*06c3fb27SDimitry Andric } 278*06c3fb27SDimitry Andric 279*06c3fb27SDimitry Andric inline bool stat_equivalent(const StatT& st1, const StatT& st2) { 280*06c3fb27SDimitry Andric return (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino); 281*06c3fb27SDimitry Andric } 282*06c3fb27SDimitry Andric 283*06c3fb27SDimitry Andric inline file_status FileDescriptor::refresh_status(error_code& ec) { 284*06c3fb27SDimitry Andric // FD must be open and good. 285*06c3fb27SDimitry Andric m_status = file_status{}; 286*06c3fb27SDimitry Andric m_stat = {}; 287*06c3fb27SDimitry Andric error_code m_ec; 288*06c3fb27SDimitry Andric if (detail::fstat(fd, &m_stat) == -1) 289*06c3fb27SDimitry Andric m_ec = capture_errno(); 290*06c3fb27SDimitry Andric m_status = create_file_status(m_ec, name, m_stat, &ec); 291*06c3fb27SDimitry Andric return m_status; 292*06c3fb27SDimitry Andric } 293*06c3fb27SDimitry Andric 294*06c3fb27SDimitry Andric } // end namespace detail 295*06c3fb27SDimitry Andric 296*06c3fb27SDimitry Andric _LIBCPP_END_NAMESPACE_FILESYSTEM 297*06c3fb27SDimitry Andric 298*06c3fb27SDimitry Andric #endif // FILESYSTEM_FILE_DESCRIPTOR_H 299