xref: /freebsd-src/contrib/llvm-project/libcxx/src/filesystem/file_descriptor.h (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
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