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)>
get_file_type(DirEntT * ent,int)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>
get_file_type(DirEntT *,long)7006c3fb27SDimitry Andric file_type get_file_type(DirEntT*, long) {
7106c3fb27SDimitry Andric return file_type::none;
7206c3fb27SDimitry Andric }
7306c3fb27SDimitry Andric
posix_readdir(DIR * dir_stream,error_code & ec)74*cb14a3feSDimitry Andric inline pair<string_view, file_type> posix_readdir(DIR* dir_stream, error_code& ec) {
7506c3fb27SDimitry Andric struct dirent* dir_entry_ptr = nullptr;
7606c3fb27SDimitry Andric errno = 0; // zero errno in order to detect errors
7706c3fb27SDimitry Andric ec.clear();
7806c3fb27SDimitry Andric if ((dir_entry_ptr = ::readdir(dir_stream)) == nullptr) {
7906c3fb27SDimitry Andric if (errno)
8006c3fb27SDimitry Andric ec = capture_errno();
8106c3fb27SDimitry Andric return {};
8206c3fb27SDimitry Andric } else {
8306c3fb27SDimitry Andric return {dir_entry_ptr->d_name, get_file_type(dir_entry_ptr, 0)};
8406c3fb27SDimitry Andric }
8506c3fb27SDimitry Andric }
8606c3fb27SDimitry Andric
8706c3fb27SDimitry Andric #else // _LIBCPP_WIN32API
8806c3fb27SDimitry Andric
8906c3fb27SDimitry Andric inline file_type get_file_type(const WIN32_FIND_DATAW& data) {
90*cb14a3feSDimitry Andric if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && data.dwReserved0 == IO_REPARSE_TAG_SYMLINK)
9106c3fb27SDimitry Andric return file_type::symlink;
9206c3fb27SDimitry Andric if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
9306c3fb27SDimitry Andric return file_type::directory;
9406c3fb27SDimitry Andric return file_type::regular;
9506c3fb27SDimitry Andric }
9606c3fb27SDimitry Andric inline uintmax_t get_file_size(const WIN32_FIND_DATAW& data) {
9706c3fb27SDimitry Andric return (static_cast<uint64_t>(data.nFileSizeHigh) << 32) + data.nFileSizeLow;
9806c3fb27SDimitry Andric }
9906c3fb27SDimitry Andric inline file_time_type get_write_time(const WIN32_FIND_DATAW& data) {
10006c3fb27SDimitry Andric ULARGE_INTEGER tmp;
10106c3fb27SDimitry Andric const FILETIME& time = data.ftLastWriteTime;
10206c3fb27SDimitry Andric tmp.u.LowPart = time.dwLowDateTime;
10306c3fb27SDimitry Andric tmp.u.HighPart = time.dwHighDateTime;
10406c3fb27SDimitry Andric return file_time_type(file_time_type::duration(tmp.QuadPart));
10506c3fb27SDimitry Andric }
10606c3fb27SDimitry Andric
10706c3fb27SDimitry Andric #endif // !_LIBCPP_WIN32API
10806c3fb27SDimitry Andric
10906c3fb27SDimitry Andric // POSIX HELPERS
11006c3fb27SDimitry Andric
11106c3fb27SDimitry Andric using value_type = path::value_type;
11206c3fb27SDimitry Andric using string_type = path::string_type;
11306c3fb27SDimitry Andric
11406c3fb27SDimitry Andric struct FileDescriptor {
11506c3fb27SDimitry Andric const path& name;
11606c3fb27SDimitry Andric int fd = -1;
11706c3fb27SDimitry Andric StatT m_stat;
11806c3fb27SDimitry Andric file_status m_status;
11906c3fb27SDimitry Andric
12006c3fb27SDimitry Andric template <class... Args>
createFileDescriptor12106c3fb27SDimitry Andric static FileDescriptor create(const path* p, error_code& ec, Args... args) {
12206c3fb27SDimitry Andric ec.clear();
12306c3fb27SDimitry Andric int fd;
12406c3fb27SDimitry Andric #ifdef _LIBCPP_WIN32API
12506c3fb27SDimitry Andric // TODO: most of the filesystem implementation uses native Win32 calls
12606c3fb27SDimitry Andric // (mostly via posix_compat.h). However, here we use the C-runtime APIs to
12706c3fb27SDimitry Andric // open a file, because we subsequently pass the C-runtime fd to
12806c3fb27SDimitry Andric // `std::[io]fstream::__open(int fd)` in order to implement copy_file.
12906c3fb27SDimitry Andric //
13006c3fb27SDimitry Andric // Because we're calling the windows C-runtime, win32 error codes are
13106c3fb27SDimitry Andric // translated into C error numbers by the C runtime, and returned in errno,
13206c3fb27SDimitry Andric // rather than being accessible directly via GetLastError.
13306c3fb27SDimitry Andric //
13406c3fb27SDimitry Andric // Ideally copy_file should be calling the Win32 CopyFile2 function, which
13506c3fb27SDimitry Andric // works on paths, not open files -- at which point this FileDescriptor type
13606c3fb27SDimitry Andric // will no longer be needed on windows at all.
13706c3fb27SDimitry Andric fd = ::_wopen(p->c_str(), args...);
13806c3fb27SDimitry Andric #else
13906c3fb27SDimitry Andric fd = open(p->c_str(), args...);
14006c3fb27SDimitry Andric #endif
14106c3fb27SDimitry Andric
14206c3fb27SDimitry Andric if (fd == -1) {
14306c3fb27SDimitry Andric ec = capture_errno();
14406c3fb27SDimitry Andric return FileDescriptor{p};
14506c3fb27SDimitry Andric }
14606c3fb27SDimitry Andric return FileDescriptor(p, fd);
14706c3fb27SDimitry Andric }
14806c3fb27SDimitry Andric
14906c3fb27SDimitry Andric template <class... Args>
create_with_statusFileDescriptor150*cb14a3feSDimitry Andric static FileDescriptor create_with_status(const path* p, error_code& ec, Args... args) {
15106c3fb27SDimitry Andric FileDescriptor fd = create(p, ec, args...);
15206c3fb27SDimitry Andric if (!ec)
15306c3fb27SDimitry Andric fd.refresh_status(ec);
15406c3fb27SDimitry Andric
15506c3fb27SDimitry Andric return fd;
15606c3fb27SDimitry Andric }
15706c3fb27SDimitry Andric
get_statusFileDescriptor15806c3fb27SDimitry Andric file_status get_status() const { return m_status; }
get_statFileDescriptor15906c3fb27SDimitry Andric StatT const& get_stat() const { return m_stat; }
16006c3fb27SDimitry Andric
status_knownFileDescriptor1615f757f3fSDimitry Andric bool status_known() const { return filesystem::status_known(m_status); }
16206c3fb27SDimitry Andric
16306c3fb27SDimitry Andric file_status refresh_status(error_code& ec);
16406c3fb27SDimitry Andric
closeFileDescriptor16506c3fb27SDimitry Andric void close() noexcept {
16606c3fb27SDimitry Andric if (fd != -1) {
16706c3fb27SDimitry Andric #ifdef _LIBCPP_WIN32API
16806c3fb27SDimitry Andric ::_close(fd);
16906c3fb27SDimitry Andric #else
17006c3fb27SDimitry Andric ::close(fd);
17106c3fb27SDimitry Andric #endif
17206c3fb27SDimitry Andric // FIXME: shouldn't this return an error_code?
17306c3fb27SDimitry Andric }
17406c3fb27SDimitry Andric fd = -1;
17506c3fb27SDimitry Andric }
17606c3fb27SDimitry Andric
FileDescriptorFileDescriptor17706c3fb27SDimitry Andric FileDescriptor(FileDescriptor&& other)
178*cb14a3feSDimitry Andric : name(other.name), fd(other.fd), m_stat(other.m_stat), m_status(other.m_status) {
17906c3fb27SDimitry Andric other.fd = -1;
18006c3fb27SDimitry Andric other.m_status = file_status{};
18106c3fb27SDimitry Andric }
18206c3fb27SDimitry Andric
~FileDescriptorFileDescriptor18306c3fb27SDimitry Andric ~FileDescriptor() { close(); }
18406c3fb27SDimitry Andric
18506c3fb27SDimitry Andric FileDescriptor(FileDescriptor const&) = delete;
18606c3fb27SDimitry Andric FileDescriptor& operator=(FileDescriptor const&) = delete;
18706c3fb27SDimitry Andric
18806c3fb27SDimitry Andric private:
18906c3fb27SDimitry Andric explicit FileDescriptor(const path* p, int descriptor = -1) : name(*p), fd(descriptor) {}
19006c3fb27SDimitry Andric };
19106c3fb27SDimitry Andric
posix_get_perms(const StatT & st)192*cb14a3feSDimitry Andric inline perms posix_get_perms(const StatT& st) noexcept { return static_cast<perms>(st.st_mode) & perms::mask; }
19306c3fb27SDimitry Andric
create_file_status(error_code & m_ec,path const & p,const StatT & path_stat,error_code * ec)194*cb14a3feSDimitry Andric inline file_status create_file_status(error_code& m_ec, path const& p, const StatT& path_stat, error_code* ec) {
19506c3fb27SDimitry Andric if (ec)
19606c3fb27SDimitry Andric *ec = m_ec;
19706c3fb27SDimitry Andric if (m_ec && (m_ec.value() == ENOENT || m_ec.value() == ENOTDIR)) {
19806c3fb27SDimitry Andric return file_status(file_type::not_found);
19906c3fb27SDimitry Andric } else if (m_ec) {
20006c3fb27SDimitry Andric ErrorHandler<void> err("posix_stat", ec, &p);
20106c3fb27SDimitry Andric err.report(m_ec, "failed to determine attributes for the specified path");
20206c3fb27SDimitry Andric return file_status(file_type::none);
20306c3fb27SDimitry Andric }
20406c3fb27SDimitry Andric // else
20506c3fb27SDimitry Andric
20606c3fb27SDimitry Andric file_status fs_tmp;
20706c3fb27SDimitry Andric auto const mode = path_stat.st_mode;
20806c3fb27SDimitry Andric if (S_ISLNK(mode))
20906c3fb27SDimitry Andric fs_tmp.type(file_type::symlink);
21006c3fb27SDimitry Andric else if (S_ISREG(mode))
21106c3fb27SDimitry Andric fs_tmp.type(file_type::regular);
21206c3fb27SDimitry Andric else if (S_ISDIR(mode))
21306c3fb27SDimitry Andric fs_tmp.type(file_type::directory);
21406c3fb27SDimitry Andric else if (S_ISBLK(mode))
21506c3fb27SDimitry Andric fs_tmp.type(file_type::block);
21606c3fb27SDimitry Andric else if (S_ISCHR(mode))
21706c3fb27SDimitry Andric fs_tmp.type(file_type::character);
21806c3fb27SDimitry Andric else if (S_ISFIFO(mode))
21906c3fb27SDimitry Andric fs_tmp.type(file_type::fifo);
22006c3fb27SDimitry Andric else if (S_ISSOCK(mode))
22106c3fb27SDimitry Andric fs_tmp.type(file_type::socket);
22206c3fb27SDimitry Andric else
22306c3fb27SDimitry Andric fs_tmp.type(file_type::unknown);
22406c3fb27SDimitry Andric
22506c3fb27SDimitry Andric fs_tmp.permissions(detail::posix_get_perms(path_stat));
22606c3fb27SDimitry Andric return fs_tmp;
22706c3fb27SDimitry Andric }
22806c3fb27SDimitry Andric
posix_stat(path const & p,StatT & path_stat,error_code * ec)22906c3fb27SDimitry Andric inline file_status posix_stat(path const& p, StatT& path_stat, error_code* ec) {
23006c3fb27SDimitry Andric error_code m_ec;
23106c3fb27SDimitry Andric if (detail::stat(p.c_str(), &path_stat) == -1)
23206c3fb27SDimitry Andric m_ec = detail::capture_errno();
23306c3fb27SDimitry Andric return create_file_status(m_ec, p, path_stat, ec);
23406c3fb27SDimitry Andric }
23506c3fb27SDimitry Andric
posix_stat(path const & p,error_code * ec)23606c3fb27SDimitry Andric inline file_status posix_stat(path const& p, error_code* ec) {
23706c3fb27SDimitry Andric StatT path_stat;
23806c3fb27SDimitry Andric return posix_stat(p, path_stat, ec);
23906c3fb27SDimitry Andric }
24006c3fb27SDimitry Andric
posix_lstat(path const & p,StatT & path_stat,error_code * ec)24106c3fb27SDimitry Andric inline file_status posix_lstat(path const& p, StatT& path_stat, error_code* ec) {
24206c3fb27SDimitry Andric error_code m_ec;
24306c3fb27SDimitry Andric if (detail::lstat(p.c_str(), &path_stat) == -1)
24406c3fb27SDimitry Andric m_ec = detail::capture_errno();
24506c3fb27SDimitry Andric return create_file_status(m_ec, p, path_stat, ec);
24606c3fb27SDimitry Andric }
24706c3fb27SDimitry Andric
posix_lstat(path const & p,error_code * ec)24806c3fb27SDimitry Andric inline file_status posix_lstat(path const& p, error_code* ec) {
24906c3fb27SDimitry Andric StatT path_stat;
25006c3fb27SDimitry Andric return posix_lstat(p, path_stat, ec);
25106c3fb27SDimitry Andric }
25206c3fb27SDimitry Andric
25306c3fb27SDimitry Andric // http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html
posix_ftruncate(const FileDescriptor & fd,off_t to_size,error_code & ec)25406c3fb27SDimitry Andric inline bool posix_ftruncate(const FileDescriptor& fd, off_t to_size, error_code& ec) {
25506c3fb27SDimitry Andric if (detail::ftruncate(fd.fd, to_size) == -1) {
25606c3fb27SDimitry Andric ec = capture_errno();
25706c3fb27SDimitry Andric return true;
25806c3fb27SDimitry Andric }
25906c3fb27SDimitry Andric ec.clear();
26006c3fb27SDimitry Andric return false;
26106c3fb27SDimitry Andric }
26206c3fb27SDimitry Andric
posix_fchmod(const FileDescriptor & fd,const StatT & st,error_code & ec)26306c3fb27SDimitry Andric inline bool posix_fchmod(const FileDescriptor& fd, const StatT& st, error_code& ec) {
26406c3fb27SDimitry Andric if (detail::fchmod(fd.fd, st.st_mode) == -1) {
26506c3fb27SDimitry Andric ec = capture_errno();
26606c3fb27SDimitry Andric return true;
26706c3fb27SDimitry Andric }
26806c3fb27SDimitry Andric ec.clear();
26906c3fb27SDimitry Andric return false;
27006c3fb27SDimitry Andric }
27106c3fb27SDimitry Andric
stat_equivalent(const StatT & st1,const StatT & st2)27206c3fb27SDimitry Andric inline bool stat_equivalent(const StatT& st1, const StatT& st2) {
27306c3fb27SDimitry Andric return (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
27406c3fb27SDimitry Andric }
27506c3fb27SDimitry Andric
refresh_status(error_code & ec)27606c3fb27SDimitry Andric inline file_status FileDescriptor::refresh_status(error_code& ec) {
27706c3fb27SDimitry Andric // FD must be open and good.
27806c3fb27SDimitry Andric m_status = file_status{};
27906c3fb27SDimitry Andric m_stat = {};
28006c3fb27SDimitry Andric error_code m_ec;
28106c3fb27SDimitry Andric if (detail::fstat(fd, &m_stat) == -1)
28206c3fb27SDimitry Andric m_ec = capture_errno();
28306c3fb27SDimitry Andric m_status = create_file_status(m_ec, name, m_stat, &ec);
28406c3fb27SDimitry Andric return m_status;
28506c3fb27SDimitry Andric }
28606c3fb27SDimitry Andric
28706c3fb27SDimitry Andric } // end namespace detail
28806c3fb27SDimitry Andric
28906c3fb27SDimitry Andric _LIBCPP_END_NAMESPACE_FILESYSTEM
29006c3fb27SDimitry Andric
29106c3fb27SDimitry Andric #endif // FILESYSTEM_FILE_DESCRIPTOR_H
292