brintos

brintos / llvm-project-archived public Read only

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