1 //===----------------------------------------------------------------------===////
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-exception
7 //===----------------------------------------------------------------------===////
9 #ifndef FILESYSTEM_FILE_DESCRIPTOR_H
10 #define FILESYSTEM_FILE_DESCRIPTOR_H
15 #include <string_view>
16 #include <system_error>
20 #include "posix_compat.h"
21 #include "time_utils.h"
23 #if defined(_LIBCPP_WIN32API)
24 # define WIN32_LEAN_AND_MEAN
28 # include <dirent.h> // for DIR & friends
29 # include <fcntl.h> // values for fchmodat
30 # include <sys/stat.h>
31 # include <sys/statvfs.h>
33 #endif // defined(_LIBCPP_WIN32API)
35 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
39 #if !defined(_LIBCPP_WIN32API)
42 template <class DirEntT, class = decltype(DirEntT::d_type)>
43 file_type get_file_type(DirEntT* ent, int) {
44 switch (ent->d_type) {
46 return file_type::block;
48 return file_type::character;
50 return file_type::directory;
52 return file_type::fifo;
54 return file_type::symlink;
56 return file_type::regular;
58 return file_type::socket;
59 // Unlike in lstat, hitting "unknown" here simply means that the underlying
60 // filesystem doesn't support d_type. Report is as 'none' so we correctly
61 // set the cache to empty.
65 return file_type::none;
67 #endif // defined(DT_BLK)
69 template <class DirEntT>
70 file_type get_file_type(DirEntT*, long) {
71 return file_type::none;
74 inline pair<string_view, file_type> posix_readdir(DIR* dir_stream,
76 struct dirent* dir_entry_ptr = nullptr;
77 errno = 0; // zero errno in order to detect errors
79 if ((dir_entry_ptr = ::readdir(dir_stream)) == nullptr) {
84 return {dir_entry_ptr->d_name, get_file_type(dir_entry_ptr, 0)};
88 #else // _LIBCPP_WIN32API
90 inline file_type get_file_type(const WIN32_FIND_DATAW& data) {
91 if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
92 data.dwReserved0 == IO_REPARSE_TAG_SYMLINK)
93 return file_type::symlink;
94 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
95 return file_type::directory;
96 return file_type::regular;
98 inline uintmax_t get_file_size(const WIN32_FIND_DATAW& data) {
99 return (static_cast<uint64_t>(data.nFileSizeHigh) << 32) + data.nFileSizeLow;
101 inline file_time_type get_write_time(const WIN32_FIND_DATAW& data) {
103 const FILETIME& time = data.ftLastWriteTime;
104 tmp.u.LowPart = time.dwLowDateTime;
105 tmp.u.HighPart = time.dwHighDateTime;
106 return file_time_type(file_time_type::duration(tmp.QuadPart));
109 #endif // !_LIBCPP_WIN32API
113 using value_type = path::value_type;
114 using string_type = path::string_type;
116 struct FileDescriptor {
120 file_status m_status;
122 template <class... Args>
123 static FileDescriptor create(const path* p, error_code& ec, Args... args) {
126 #ifdef _LIBCPP_WIN32API
127 // TODO: most of the filesystem implementation uses native Win32 calls
128 // (mostly via posix_compat.h). However, here we use the C-runtime APIs to
129 // open a file, because we subsequently pass the C-runtime fd to
130 // `std::[io]fstream::__open(int fd)` in order to implement copy_file.
132 // Because we're calling the windows C-runtime, win32 error codes are
133 // translated into C error numbers by the C runtime, and returned in errno,
134 // rather than being accessible directly via GetLastError.
136 // Ideally copy_file should be calling the Win32 CopyFile2 function, which
137 // works on paths, not open files -- at which point this FileDescriptor type
138 // will no longer be needed on windows at all.
139 fd = ::_wopen(p->c_str(), args...);
141 fd = open(p->c_str(), args...);
145 ec = capture_errno();
146 return FileDescriptor{p};
148 return FileDescriptor(p, fd);
151 template <class... Args>
152 static FileDescriptor create_with_status(const path* p, error_code& ec,
154 FileDescriptor fd = create(p, ec, args...);
156 fd.refresh_status(ec);
161 file_status get_status() const { return m_status; }
162 StatT const& get_stat() const { return m_stat; }
164 bool status_known() const { return _VSTD_FS::status_known(m_status); }
166 file_status refresh_status(error_code& ec);
168 void close() noexcept {
170 #ifdef _LIBCPP_WIN32API
175 // FIXME: shouldn't this return an error_code?
180 FileDescriptor(FileDescriptor&& other)
181 : name(other.name), fd(other.fd), m_stat(other.m_stat),
182 m_status(other.m_status) {
184 other.m_status = file_status{};
187 ~FileDescriptor() { close(); }
189 FileDescriptor(FileDescriptor const&) = delete;
190 FileDescriptor& operator=(FileDescriptor const&) = delete;
193 explicit FileDescriptor(const path* p, int descriptor = -1) : name(*p), fd(descriptor) {}
196 inline perms posix_get_perms(const StatT& st) noexcept {
197 return static_cast<perms>(st.st_mode) & perms::mask;
200 inline file_status create_file_status(error_code& m_ec, path const& p,
201 const StatT& path_stat, error_code* ec) {
204 if (m_ec && (m_ec.value() == ENOENT || m_ec.value() == ENOTDIR)) {
205 return file_status(file_type::not_found);
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);
214 auto const mode = path_stat.st_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);
230 fs_tmp.type(file_type::unknown);
232 fs_tmp.permissions(detail::posix_get_perms(path_stat));
236 inline file_status posix_stat(path const& p, StatT& path_stat, error_code* ec) {
238 if (detail::stat(p.c_str(), &path_stat) == -1)
239 m_ec = detail::capture_errno();
240 return create_file_status(m_ec, p, path_stat, ec);
243 inline file_status posix_stat(path const& p, error_code* ec) {
245 return posix_stat(p, path_stat, ec);
248 inline file_status posix_lstat(path const& p, StatT& path_stat, error_code* ec) {
250 if (detail::lstat(p.c_str(), &path_stat) == -1)
251 m_ec = detail::capture_errno();
252 return create_file_status(m_ec, p, path_stat, ec);
255 inline file_status posix_lstat(path const& p, error_code* ec) {
257 return posix_lstat(p, path_stat, ec);
260 // http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html
261 inline bool posix_ftruncate(const FileDescriptor& fd, off_t to_size, error_code& ec) {
262 if (detail::ftruncate(fd.fd, to_size) == -1) {
263 ec = capture_errno();
270 inline bool posix_fchmod(const FileDescriptor& fd, const StatT& st, error_code& ec) {
271 if (detail::fchmod(fd.fd, st.st_mode) == -1) {
272 ec = capture_errno();
279 inline bool stat_equivalent(const StatT& st1, const StatT& st2) {
280 return (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
283 inline file_status FileDescriptor::refresh_status(error_code& ec) {
284 // FD must be open and good.
285 m_status = file_status{};
288 if (detail::fstat(fd, &m_stat) == -1)
289 m_ec = capture_errno();
290 m_status = create_file_status(m_ec, name, m_stat, &ec);
294 } // end namespace detail
296 _LIBCPP_END_NAMESPACE_FILESYSTEM
298 #endif // FILESYSTEM_FILE_DESCRIPTOR_H