Path: blob/main/contrib/llvm-project/libcxx/src/filesystem/file_descriptor.h
35231 views
//===----------------------------------------------------------------------===////1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===////78#ifndef FILESYSTEM_FILE_DESCRIPTOR_H9#define FILESYSTEM_FILE_DESCRIPTOR_H1011#include <__config>12#include <cstdint>13#include <filesystem>14#include <string_view>15#include <system_error>16#include <utility>1718#include "error.h"19#include "posix_compat.h"20#include "time_utils.h"2122#if defined(_LIBCPP_WIN32API)23# define WIN32_LEAN_AND_MEAN24# define NOMINMAX25# include <windows.h>26#else27# include <dirent.h> // for DIR & friends28# include <fcntl.h> // values for fchmodat29# include <sys/stat.h>30# include <sys/statvfs.h>31# include <unistd.h>32#endif // defined(_LIBCPP_WIN32API)3334_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM3536namespace detail {3738#if !defined(_LIBCPP_WIN32API)3940# if defined(DT_BLK)41template <class DirEntT, class = decltype(DirEntT::d_type)>42file_type get_file_type(DirEntT* ent, int) {43switch (ent->d_type) {44case DT_BLK:45return file_type::block;46case DT_CHR:47return file_type::character;48case DT_DIR:49return file_type::directory;50case DT_FIFO:51return file_type::fifo;52case DT_LNK:53return file_type::symlink;54case DT_REG:55return file_type::regular;56case DT_SOCK:57return file_type::socket;58// Unlike in lstat, hitting "unknown" here simply means that the underlying59// filesystem doesn't support d_type. Report is as 'none' so we correctly60// set the cache to empty.61case DT_UNKNOWN:62break;63}64return file_type::none;65}66# endif // defined(DT_BLK)6768template <class DirEntT>69file_type get_file_type(DirEntT*, long) {70return file_type::none;71}7273inline pair<string_view, file_type> posix_readdir(DIR* dir_stream, error_code& ec) {74struct dirent* dir_entry_ptr = nullptr;75errno = 0; // zero errno in order to detect errors76ec.clear();77if ((dir_entry_ptr = ::readdir(dir_stream)) == nullptr) {78if (errno)79ec = capture_errno();80return {};81} else {82return {dir_entry_ptr->d_name, get_file_type(dir_entry_ptr, 0)};83}84}8586#else // _LIBCPP_WIN32API8788inline file_type get_file_type(const WIN32_FIND_DATAW& data) {89if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && data.dwReserved0 == IO_REPARSE_TAG_SYMLINK)90return file_type::symlink;91if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)92return file_type::directory;93return file_type::regular;94}95inline uintmax_t get_file_size(const WIN32_FIND_DATAW& data) {96return (static_cast<uint64_t>(data.nFileSizeHigh) << 32) + data.nFileSizeLow;97}98inline file_time_type get_write_time(const WIN32_FIND_DATAW& data) {99ULARGE_INTEGER tmp;100const FILETIME& time = data.ftLastWriteTime;101tmp.u.LowPart = time.dwLowDateTime;102tmp.u.HighPart = time.dwHighDateTime;103return file_time_type(file_time_type::duration(tmp.QuadPart));104}105106#endif // !_LIBCPP_WIN32API107108// POSIX HELPERS109110using value_type = path::value_type;111using string_type = path::string_type;112113struct FileDescriptor {114const path& name;115int fd = -1;116StatT m_stat;117file_status m_status;118119template <class... Args>120static FileDescriptor create(const path* p, error_code& ec, Args... args) {121ec.clear();122int fd;123#ifdef _LIBCPP_WIN32API124// TODO: most of the filesystem implementation uses native Win32 calls125// (mostly via posix_compat.h). However, here we use the C-runtime APIs to126// open a file, because we subsequently pass the C-runtime fd to127// `std::[io]fstream::__open(int fd)` in order to implement copy_file.128//129// Because we're calling the windows C-runtime, win32 error codes are130// translated into C error numbers by the C runtime, and returned in errno,131// rather than being accessible directly via GetLastError.132//133// Ideally copy_file should be calling the Win32 CopyFile2 function, which134// works on paths, not open files -- at which point this FileDescriptor type135// will no longer be needed on windows at all.136fd = ::_wopen(p->c_str(), args...);137#else138fd = open(p->c_str(), args...);139#endif140141if (fd == -1) {142ec = capture_errno();143return FileDescriptor{p};144}145return FileDescriptor(p, fd);146}147148template <class... Args>149static FileDescriptor create_with_status(const path* p, error_code& ec, Args... args) {150FileDescriptor fd = create(p, ec, args...);151if (!ec)152fd.refresh_status(ec);153154return fd;155}156157file_status get_status() const { return m_status; }158StatT const& get_stat() const { return m_stat; }159160bool status_known() const { return filesystem::status_known(m_status); }161162file_status refresh_status(error_code& ec);163164void close() noexcept {165if (fd != -1) {166#ifdef _LIBCPP_WIN32API167::_close(fd);168#else169::close(fd);170#endif171// FIXME: shouldn't this return an error_code?172}173fd = -1;174}175176FileDescriptor(FileDescriptor&& other)177: name(other.name), fd(other.fd), m_stat(other.m_stat), m_status(other.m_status) {178other.fd = -1;179other.m_status = file_status{};180}181182~FileDescriptor() { close(); }183184FileDescriptor(FileDescriptor const&) = delete;185FileDescriptor& operator=(FileDescriptor const&) = delete;186187private:188explicit FileDescriptor(const path* p, int descriptor = -1) : name(*p), fd(descriptor) {}189};190191inline perms posix_get_perms(const StatT& st) noexcept { return static_cast<perms>(st.st_mode) & perms::mask; }192193inline file_status create_file_status(error_code& m_ec, path const& p, const StatT& path_stat, error_code* ec) {194if (ec)195*ec = m_ec;196if (m_ec && (m_ec.value() == ENOENT || m_ec.value() == ENOTDIR)) {197return file_status(file_type::not_found);198} else if (m_ec) {199ErrorHandler<void> err("posix_stat", ec, &p);200err.report(m_ec, "failed to determine attributes for the specified path");201return file_status(file_type::none);202}203// else204205file_status fs_tmp;206auto const mode = path_stat.st_mode;207if (S_ISLNK(mode))208fs_tmp.type(file_type::symlink);209else if (S_ISREG(mode))210fs_tmp.type(file_type::regular);211else if (S_ISDIR(mode))212fs_tmp.type(file_type::directory);213else if (S_ISBLK(mode))214fs_tmp.type(file_type::block);215else if (S_ISCHR(mode))216fs_tmp.type(file_type::character);217else if (S_ISFIFO(mode))218fs_tmp.type(file_type::fifo);219else if (S_ISSOCK(mode))220fs_tmp.type(file_type::socket);221else222fs_tmp.type(file_type::unknown);223224fs_tmp.permissions(detail::posix_get_perms(path_stat));225return fs_tmp;226}227228inline file_status posix_stat(path const& p, StatT& path_stat, error_code* ec) {229error_code m_ec;230if (detail::stat(p.c_str(), &path_stat) == -1)231m_ec = detail::capture_errno();232return create_file_status(m_ec, p, path_stat, ec);233}234235inline file_status posix_stat(path const& p, error_code* ec) {236StatT path_stat;237return posix_stat(p, path_stat, ec);238}239240inline file_status posix_lstat(path const& p, StatT& path_stat, error_code* ec) {241error_code m_ec;242if (detail::lstat(p.c_str(), &path_stat) == -1)243m_ec = detail::capture_errno();244return create_file_status(m_ec, p, path_stat, ec);245}246247inline file_status posix_lstat(path const& p, error_code* ec) {248StatT path_stat;249return posix_lstat(p, path_stat, ec);250}251252// http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html253inline bool posix_ftruncate(const FileDescriptor& fd, off_t to_size, error_code& ec) {254if (detail::ftruncate(fd.fd, to_size) == -1) {255ec = capture_errno();256return true;257}258ec.clear();259return false;260}261262inline bool posix_fchmod(const FileDescriptor& fd, const StatT& st, error_code& ec) {263if (detail::fchmod(fd.fd, st.st_mode) == -1) {264ec = capture_errno();265return true;266}267ec.clear();268return false;269}270271inline bool stat_equivalent(const StatT& st1, const StatT& st2) {272return (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);273}274275inline file_status FileDescriptor::refresh_status(error_code& ec) {276// FD must be open and good.277m_status = file_status{};278m_stat = {};279error_code m_ec;280if (detail::fstat(fd, &m_stat) == -1)281m_ec = capture_errno();282m_status = create_file_status(m_ec, name, m_stat, &ec);283return m_status;284}285286} // end namespace detail287288_LIBCPP_END_NAMESPACE_FILESYSTEM289290#endif // FILESYSTEM_FILE_DESCRIPTOR_H291292293