xref: /freebsd/contrib/llvm-project/libcxx/src/filesystem/operations.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include <__assert>
10 #include <__config>
11 #include <__utility/unreachable.h>
12 #include <array>
13 #include <climits>
14 #include <cstdlib>
15 #include <filesystem>
16 #include <iterator>
17 #include <string_view>
18 #include <type_traits>
19 #include <vector>
20 
21 #include "error.h"
22 #include "file_descriptor.h"
23 #include "path_parser.h"
24 #include "posix_compat.h"
25 #include "time_utils.h"
26 
27 #if defined(_LIBCPP_WIN32API)
28 #  define WIN32_LEAN_AND_MEAN
29 #  define NOMINMAX
30 #  include <windows.h>
31 #else
32 #  include <dirent.h>
33 #  include <sys/stat.h>
34 #  include <sys/statvfs.h>
35 #  include <unistd.h>
36 #endif
37 #include <fcntl.h> /* values for fchmodat */
38 #include <time.h>
39 
40 #if __has_include(<sys/sendfile.h>)
41 #  include <sys/sendfile.h>
42 #  define _LIBCPP_FILESYSTEM_USE_SENDFILE
43 #elif defined(__APPLE__) || __has_include(<copyfile.h>)
44 #  include <copyfile.h>
45 #  define _LIBCPP_FILESYSTEM_USE_COPYFILE
46 #else
47 #  include <fstream>
48 #  define _LIBCPP_FILESYSTEM_USE_FSTREAM
49 #endif
50 
51 #if defined(__ELF__) && defined(_LIBCPP_LINK_RT_LIB)
52 #  pragma comment(lib, "rt")
53 #endif
54 
55 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
56 
57 using detail::capture_errno;
58 using detail::ErrorHandler;
59 using detail::StatT;
60 using detail::TimeSpec;
61 using parser::createView;
62 using parser::PathParser;
63 using parser::string_view_t;
64 
__do_absolute(const path & p,path * cwd,error_code * ec)65 static path __do_absolute(const path& p, path* cwd, error_code* ec) {
66   if (ec)
67     ec->clear();
68   if (p.is_absolute())
69     return p;
70   *cwd = __current_path(ec);
71   if (ec && *ec)
72     return {};
73   return (*cwd) / p;
74 }
75 
__absolute(const path & p,error_code * ec)76 path __absolute(const path& p, error_code* ec) {
77   path cwd;
78   return __do_absolute(p, &cwd, ec);
79 }
80 
__canonical(path const & orig_p,error_code * ec)81 path __canonical(path const& orig_p, error_code* ec) {
82   path cwd;
83   ErrorHandler<path> err("canonical", ec, &orig_p, &cwd);
84 
85   path p = __do_absolute(orig_p, &cwd, ec);
86 #if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || defined(_LIBCPP_WIN32API)
87   std::unique_ptr<path::value_type, decltype(&::free)> hold(detail::realpath(p.c_str(), nullptr), &::free);
88   if (hold.get() == nullptr)
89     return err.report(capture_errno());
90   return {hold.get()};
91 #else
92 #  if defined(__MVS__) && !defined(PATH_MAX)
93   path::value_type buff[_XOPEN_PATH_MAX + 1];
94 #  else
95   path::value_type buff[PATH_MAX + 1];
96 #  endif
97   path::value_type* ret;
98   if ((ret = detail::realpath(p.c_str(), buff)) == nullptr)
99     return err.report(capture_errno());
100   return {ret};
101 #endif
102 }
103 
__copy(const path & from,const path & to,copy_options options,error_code * ec)104 void __copy(const path& from, const path& to, copy_options options, error_code* ec) {
105   ErrorHandler<void> err("copy", ec, &from, &to);
106 
107   const bool sym_status = bool(options & (copy_options::create_symlinks | copy_options::skip_symlinks));
108 
109   const bool sym_status2 = bool(options & copy_options::copy_symlinks);
110 
111   error_code m_ec1;
112   StatT f_st;
113   const file_status f =
114       sym_status || sym_status2 ? detail::posix_lstat(from, f_st, &m_ec1) : detail::posix_stat(from, f_st, &m_ec1);
115   if (m_ec1)
116     return err.report(m_ec1);
117 
118   StatT t_st;
119   const file_status t = sym_status ? detail::posix_lstat(to, t_st, &m_ec1) : detail::posix_stat(to, t_st, &m_ec1);
120 
121   if (not status_known(t))
122     return err.report(m_ec1);
123 
124   if (!exists(f) || is_other(f) || is_other(t) || (is_directory(f) && is_regular_file(t)) ||
125       (exists(t) && detail::stat_equivalent(f_st, t_st))) {
126     return err.report(errc::function_not_supported);
127   }
128 
129   if (is_symlink(f)) {
130     if (bool(copy_options::skip_symlinks & options)) {
131       // do nothing
132     } else if (not exists(t)) {
133       __copy_symlink(from, to, ec);
134     } else {
135       return err.report(errc::file_exists);
136     }
137     return;
138   } else if (is_regular_file(f)) {
139     if (bool(copy_options::directories_only & options)) {
140       // do nothing
141     } else if (bool(copy_options::create_symlinks & options)) {
142       __create_symlink(from, to, ec);
143     } else if (bool(copy_options::create_hard_links & options)) {
144       __create_hard_link(from, to, ec);
145     } else if (is_directory(t)) {
146       __copy_file(from, to / from.filename(), options, ec);
147     } else {
148       __copy_file(from, to, options, ec);
149     }
150     return;
151   } else if (is_directory(f) && bool(copy_options::create_symlinks & options)) {
152     return err.report(errc::is_a_directory);
153   } else if (is_directory(f) && (bool(copy_options::recursive & options) || copy_options::none == options)) {
154     if (!exists(t)) {
155       // create directory to with attributes from 'from'.
156       __create_directory(to, from, ec);
157       if (ec && *ec) {
158         return;
159       }
160     }
161     directory_iterator it = ec ? directory_iterator(from, *ec) : directory_iterator(from);
162     if (ec && *ec) {
163       return;
164     }
165     error_code m_ec2;
166     for (; !m_ec2 && it != directory_iterator(); it.increment(m_ec2)) {
167       __copy(it->path(), to / it->path().filename(), options | copy_options::__in_recursive_copy, ec);
168       if (ec && *ec) {
169         return;
170       }
171     }
172     if (m_ec2) {
173       return err.report(m_ec2);
174     }
175   }
176 }
177 
178 namespace detail {
179 namespace {
180 
181 #if defined(_LIBCPP_FILESYSTEM_USE_SENDFILE)
copy_file_impl(FileDescriptor & read_fd,FileDescriptor & write_fd,error_code & ec)182 bool copy_file_impl(FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
183   size_t count = read_fd.get_stat().st_size;
184   do {
185     ssize_t res;
186     if ((res = ::sendfile(write_fd.fd, read_fd.fd, nullptr, count)) == -1) {
187       ec = capture_errno();
188       return false;
189     }
190     count -= res;
191   } while (count > 0);
192 
193   ec.clear();
194 
195   return true;
196 }
197 #elif defined(_LIBCPP_FILESYSTEM_USE_COPYFILE)
198 bool copy_file_impl(FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
199   struct CopyFileState {
200     copyfile_state_t state;
201     CopyFileState() { state = copyfile_state_alloc(); }
202     ~CopyFileState() { copyfile_state_free(state); }
203 
204   private:
205     CopyFileState(CopyFileState const&)            = delete;
206     CopyFileState& operator=(CopyFileState const&) = delete;
207   };
208 
209   CopyFileState cfs;
210   if (fcopyfile(read_fd.fd, write_fd.fd, cfs.state, COPYFILE_DATA) < 0) {
211     ec = capture_errno();
212     return false;
213   }
214 
215   ec.clear();
216   return true;
217 }
218 #elif defined(_LIBCPP_FILESYSTEM_USE_FSTREAM)
219 bool copy_file_impl(FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
220   ifstream in;
221   in.__open(read_fd.fd, ios::binary);
222   if (!in.is_open()) {
223     // This assumes that __open didn't reset the error code.
224     ec = capture_errno();
225     return false;
226   }
227   read_fd.fd = -1;
228   ofstream out;
229   out.__open(write_fd.fd, ios::binary);
230   if (!out.is_open()) {
231     ec = capture_errno();
232     return false;
233   }
234   write_fd.fd = -1;
235 
236   if (in.good() && out.good()) {
237     using InIt  = istreambuf_iterator<char>;
238     using OutIt = ostreambuf_iterator<char>;
239     InIt bin(in);
240     InIt ein;
241     OutIt bout(out);
242     copy(bin, ein, bout);
243   }
244   if (out.fail() || in.fail()) {
245     ec = make_error_code(errc::io_error);
246     return false;
247   }
248 
249   ec.clear();
250   return true;
251 }
252 #else
253 #  error "Unknown implementation for copy_file_impl"
254 #endif // copy_file_impl implementation
255 
256 } // end anonymous namespace
257 } // end namespace detail
258 
__copy_file(const path & from,const path & to,copy_options options,error_code * ec)259 bool __copy_file(const path& from, const path& to, copy_options options, error_code* ec) {
260   using detail::FileDescriptor;
261   ErrorHandler<bool> err("copy_file", ec, &to, &from);
262 
263   error_code m_ec;
264   FileDescriptor from_fd = FileDescriptor::create_with_status(&from, m_ec, O_RDONLY | O_NONBLOCK | O_BINARY);
265   if (m_ec)
266     return err.report(m_ec);
267 
268   auto from_st           = from_fd.get_status();
269   StatT const& from_stat = from_fd.get_stat();
270   if (!is_regular_file(from_st)) {
271     if (not m_ec)
272       m_ec = make_error_code(errc::not_supported);
273     return err.report(m_ec);
274   }
275 
276   const bool skip_existing      = bool(copy_options::skip_existing & options);
277   const bool update_existing    = bool(copy_options::update_existing & options);
278   const bool overwrite_existing = bool(copy_options::overwrite_existing & options);
279 
280   StatT to_stat_path;
281   file_status to_st = detail::posix_stat(to, to_stat_path, &m_ec);
282   if (!status_known(to_st))
283     return err.report(m_ec);
284 
285   const bool to_exists = exists(to_st);
286   if (to_exists && !is_regular_file(to_st))
287     return err.report(errc::not_supported);
288 
289   if (to_exists && detail::stat_equivalent(from_stat, to_stat_path))
290     return err.report(errc::file_exists);
291 
292   if (to_exists && skip_existing)
293     return false;
294 
295   bool ShouldCopy = [&]() {
296     if (to_exists && update_existing) {
297       auto from_time = detail::extract_mtime(from_stat);
298       auto to_time   = detail::extract_mtime(to_stat_path);
299       if (from_time.tv_sec < to_time.tv_sec)
300         return false;
301       if (from_time.tv_sec == to_time.tv_sec && from_time.tv_nsec <= to_time.tv_nsec)
302         return false;
303       return true;
304     }
305     if (!to_exists || overwrite_existing)
306       return true;
307     return err.report(errc::file_exists);
308   }();
309   if (!ShouldCopy)
310     return false;
311 
312   // Don't truncate right away. We may not be opening the file we originally
313   // looked at; we'll check this later.
314   int to_open_flags = O_WRONLY | O_BINARY;
315   if (!to_exists)
316     to_open_flags |= O_CREAT;
317   FileDescriptor to_fd = FileDescriptor::create_with_status(&to, m_ec, to_open_flags, from_stat.st_mode);
318   if (m_ec)
319     return err.report(m_ec);
320 
321   if (to_exists) {
322     // Check that the file we initially stat'ed is equivalent to the one
323     // we opened.
324     // FIXME: report this better.
325     if (!detail::stat_equivalent(to_stat_path, to_fd.get_stat()))
326       return err.report(errc::bad_file_descriptor);
327 
328     // Set the permissions and truncate the file we opened.
329     if (detail::posix_fchmod(to_fd, from_stat, m_ec))
330       return err.report(m_ec);
331     if (detail::posix_ftruncate(to_fd, 0, m_ec))
332       return err.report(m_ec);
333   }
334 
335   if (!detail::copy_file_impl(from_fd, to_fd, m_ec)) {
336     // FIXME: Remove the dest file if we failed, and it didn't exist previously.
337     return err.report(m_ec);
338   }
339 
340   return true;
341 }
342 
__copy_symlink(const path & existing_symlink,const path & new_symlink,error_code * ec)343 void __copy_symlink(const path& existing_symlink, const path& new_symlink, error_code* ec) {
344   const path real_path(__read_symlink(existing_symlink, ec));
345   if (ec && *ec) {
346     return;
347   }
348 #if defined(_LIBCPP_WIN32API)
349   error_code local_ec;
350   if (is_directory(real_path, local_ec))
351     __create_directory_symlink(real_path, new_symlink, ec);
352   else
353 #endif
354     __create_symlink(real_path, new_symlink, ec);
355 }
356 
__create_directories(const path & p,error_code * ec)357 bool __create_directories(const path& p, error_code* ec) {
358   ErrorHandler<bool> err("create_directories", ec, &p);
359 
360   error_code m_ec;
361   auto const st = detail::posix_stat(p, &m_ec);
362   if (!status_known(st))
363     return err.report(m_ec);
364   else if (is_directory(st))
365     return false;
366   else if (exists(st))
367     return err.report(errc::file_exists);
368 
369   const path parent = p.parent_path();
370   if (!parent.empty()) {
371     const file_status parent_st = status(parent, m_ec);
372     if (not status_known(parent_st))
373       return err.report(m_ec);
374     if (not exists(parent_st)) {
375       if (parent == p)
376         return err.report(errc::invalid_argument);
377       __create_directories(parent, ec);
378       if (ec && *ec) {
379         return false;
380       }
381     } else if (not is_directory(parent_st))
382       return err.report(errc::not_a_directory);
383   }
384   bool ret = __create_directory(p, &m_ec);
385   if (m_ec)
386     return err.report(m_ec);
387   return ret;
388 }
389 
__create_directory(const path & p,error_code * ec)390 bool __create_directory(const path& p, error_code* ec) {
391   ErrorHandler<bool> err("create_directory", ec, &p);
392 
393   if (detail::mkdir(p.c_str(), static_cast<int>(perms::all)) == 0)
394     return true;
395 
396   if (errno != EEXIST)
397     return err.report(capture_errno());
398   error_code mec = capture_errno();
399   error_code ignored_ec;
400   const file_status st = status(p, ignored_ec);
401   if (!is_directory(st))
402     return err.report(mec);
403   return false;
404 }
405 
__create_directory(path const & p,path const & attributes,error_code * ec)406 bool __create_directory(path const& p, path const& attributes, error_code* ec) {
407   ErrorHandler<bool> err("create_directory", ec, &p, &attributes);
408 
409   StatT attr_stat;
410   error_code mec;
411   file_status st = detail::posix_stat(attributes, attr_stat, &mec);
412   if (!status_known(st))
413     return err.report(mec);
414   if (!is_directory(st))
415     return err.report(errc::not_a_directory, "the specified attribute path is invalid");
416 
417   if (detail::mkdir(p.c_str(), attr_stat.st_mode) == 0)
418     return true;
419 
420   if (errno != EEXIST)
421     return err.report(capture_errno());
422 
423   mec = capture_errno();
424   error_code ignored_ec;
425   st = status(p, ignored_ec);
426   if (!is_directory(st))
427     return err.report(mec);
428   return false;
429 }
430 
__create_directory_symlink(path const & from,path const & to,error_code * ec)431 void __create_directory_symlink(path const& from, path const& to, error_code* ec) {
432   ErrorHandler<void> err("create_directory_symlink", ec, &from, &to);
433   if (detail::symlink_dir(from.c_str(), to.c_str()) == -1)
434     return err.report(capture_errno());
435 }
436 
__create_hard_link(const path & from,const path & to,error_code * ec)437 void __create_hard_link(const path& from, const path& to, error_code* ec) {
438   ErrorHandler<void> err("create_hard_link", ec, &from, &to);
439   if (detail::link(from.c_str(), to.c_str()) == -1)
440     return err.report(capture_errno());
441 }
442 
__create_symlink(path const & from,path const & to,error_code * ec)443 void __create_symlink(path const& from, path const& to, error_code* ec) {
444   ErrorHandler<void> err("create_symlink", ec, &from, &to);
445   if (detail::symlink_file(from.c_str(), to.c_str()) == -1)
446     return err.report(capture_errno());
447 }
448 
__current_path(error_code * ec)449 path __current_path(error_code* ec) {
450   ErrorHandler<path> err("current_path", ec);
451 
452 #if defined(_LIBCPP_WIN32API) || defined(__GLIBC__) || defined(__APPLE__)
453   // Common extension outside of POSIX getcwd() spec, without needing to
454   // preallocate a buffer. Also supported by a number of other POSIX libcs.
455   int size              = 0;
456   path::value_type* ptr = nullptr;
457   typedef decltype(&::free) Deleter;
458   Deleter deleter = &::free;
459 #else
460   errno     = 0; // Note: POSIX mandates that modifying `errno` is thread-safe.
461   auto size = ::pathconf(".", _PC_PATH_MAX);
462   if (size == -1) {
463     if (errno != 0) {
464       return err.report(capture_errno(), "call to pathconf failed");
465 
466       // `pathconf` returns `-1` without an error to indicate no limit.
467     } else {
468 #  if defined(__MVS__) && !defined(PATH_MAX)
469       size = _XOPEN_PATH_MAX + 1;
470 #  else
471       size = PATH_MAX + 1;
472 #  endif
473     }
474   }
475 
476   auto buff             = unique_ptr<path::value_type[]>(new path::value_type[size + 1]);
477   path::value_type* ptr = buff.get();
478 
479   // Preallocated buffer, don't free the buffer in the second unique_ptr
480   // below.
481   struct Deleter {
482     void operator()(void*) const {}
483   };
484   Deleter deleter;
485 #endif
486 
487   unique_ptr<path::value_type, Deleter> hold(detail::getcwd(ptr, size), deleter);
488   if (hold.get() == nullptr)
489     return err.report(capture_errno(), "call to getcwd failed");
490 
491   return {hold.get()};
492 }
493 
__current_path(const path & p,error_code * ec)494 void __current_path(const path& p, error_code* ec) {
495   ErrorHandler<void> err("current_path", ec, &p);
496   if (detail::chdir(p.c_str()) == -1)
497     err.report(capture_errno());
498 }
499 
__equivalent(const path & p1,const path & p2,error_code * ec)500 bool __equivalent(const path& p1, const path& p2, error_code* ec) {
501   ErrorHandler<bool> err("equivalent", ec, &p1, &p2);
502 
503   error_code ec1, ec2;
504   StatT st1 = {}, st2 = {};
505   auto s1 = detail::posix_stat(p1.native(), st1, &ec1);
506   if (!exists(s1))
507     return err.report(errc::not_supported);
508   auto s2 = detail::posix_stat(p2.native(), st2, &ec2);
509   if (!exists(s2))
510     return err.report(errc::not_supported);
511 
512   return detail::stat_equivalent(st1, st2);
513 }
514 
__file_size(const path & p,error_code * ec)515 uintmax_t __file_size(const path& p, error_code* ec) {
516   ErrorHandler<uintmax_t> err("file_size", ec, &p);
517 
518   error_code m_ec;
519   StatT st;
520   file_status fst = detail::posix_stat(p, st, &m_ec);
521   if (!exists(fst) || !is_regular_file(fst)) {
522     errc error_kind = is_directory(fst) ? errc::is_a_directory : errc::not_supported;
523     if (!m_ec)
524       m_ec = make_error_code(error_kind);
525     return err.report(m_ec);
526   }
527   // is_regular_file(p) == true
528   return static_cast<uintmax_t>(st.st_size);
529 }
530 
__hard_link_count(const path & p,error_code * ec)531 uintmax_t __hard_link_count(const path& p, error_code* ec) {
532   ErrorHandler<uintmax_t> err("hard_link_count", ec, &p);
533 
534   error_code m_ec;
535   StatT st;
536   detail::posix_stat(p, st, &m_ec);
537   if (m_ec)
538     return err.report(m_ec);
539   return static_cast<uintmax_t>(st.st_nlink);
540 }
541 
__fs_is_empty(const path & p,error_code * ec)542 bool __fs_is_empty(const path& p, error_code* ec) {
543   ErrorHandler<bool> err("is_empty", ec, &p);
544 
545   error_code m_ec;
546   StatT pst;
547   auto st = detail::posix_stat(p, pst, &m_ec);
548   if (m_ec)
549     return err.report(m_ec);
550   else if (!is_directory(st) && !is_regular_file(st))
551     return err.report(errc::not_supported);
552   else if (is_directory(st)) {
553     auto it = ec ? directory_iterator(p, *ec) : directory_iterator(p);
554     if (ec && *ec)
555       return false;
556     return it == directory_iterator{};
557   } else if (is_regular_file(st))
558     return static_cast<uintmax_t>(pst.st_size) == 0;
559 
560   __libcpp_unreachable();
561 }
562 
__last_write_time(const path & p,error_code * ec)563 file_time_type __last_write_time(const path& p, error_code* ec) {
564   using namespace chrono;
565   ErrorHandler<file_time_type> err("last_write_time", ec, &p);
566 
567   error_code m_ec;
568   StatT st;
569   detail::posix_stat(p, st, &m_ec);
570   if (m_ec)
571     return err.report(m_ec);
572   return detail::__extract_last_write_time(p, st, ec);
573 }
574 
__last_write_time(const path & p,file_time_type new_time,error_code * ec)575 void __last_write_time(const path& p, file_time_type new_time, error_code* ec) {
576   using detail::fs_time;
577   ErrorHandler<void> err("last_write_time", ec, &p);
578 
579 #if defined(_LIBCPP_WIN32API)
580   TimeSpec ts;
581   if (!fs_time::convert_to_timespec(ts, new_time))
582     return err.report(errc::value_too_large);
583   detail::WinHandle h(p.c_str(), FILE_WRITE_ATTRIBUTES, 0);
584   if (!h)
585     return err.report(detail::make_windows_error(GetLastError()));
586   FILETIME last_write = timespec_to_filetime(ts);
587   if (!SetFileTime(h, nullptr, nullptr, &last_write))
588     return err.report(detail::make_windows_error(GetLastError()));
589 #else
590   error_code m_ec;
591   array<TimeSpec, 2> tbuf;
592 #  if !defined(_LIBCPP_USE_UTIMENSAT)
593   // This implementation has a race condition between determining the
594   // last access time and attempting to set it to the same value using
595   // ::utimes
596   StatT st;
597   file_status fst = detail::posix_stat(p, st, &m_ec);
598   if (m_ec)
599     return err.report(m_ec);
600   tbuf[0] = detail::extract_atime(st);
601 #  else
602   tbuf[0].tv_sec  = 0;
603   tbuf[0].tv_nsec = UTIME_OMIT;
604 #  endif
605   if (!fs_time::convert_to_timespec(tbuf[1], new_time))
606     return err.report(errc::value_too_large);
607 
608   detail::set_file_times(p, tbuf, m_ec);
609   if (m_ec)
610     return err.report(m_ec);
611 #endif
612 }
613 
__permissions(const path & p,perms prms,perm_options opts,error_code * ec)614 void __permissions(const path& p, perms prms, perm_options opts, error_code* ec) {
615   ErrorHandler<void> err("permissions", ec, &p);
616 
617   auto has_opt                = [&](perm_options o) { return bool(o & opts); };
618   const bool resolve_symlinks = !has_opt(perm_options::nofollow);
619   const bool add_perms        = has_opt(perm_options::add);
620   const bool remove_perms     = has_opt(perm_options::remove);
621   _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
622       (add_perms + remove_perms + has_opt(perm_options::replace)) == 1,
623       "One and only one of the perm_options constants 'replace', 'add', or 'remove' must be present in opts");
624 
625   bool set_sym_perms = false;
626   prms &= perms::mask;
627   if (!resolve_symlinks || (add_perms || remove_perms)) {
628     error_code m_ec;
629     file_status st = resolve_symlinks ? detail::posix_stat(p, &m_ec) : detail::posix_lstat(p, &m_ec);
630     set_sym_perms  = is_symlink(st);
631     if (m_ec)
632       return err.report(m_ec);
633     // TODO(hardening): double-check this assertion -- it might be a valid (if rare) case when the permissions are
634     // unknown.
635     _LIBCPP_ASSERT_VALID_EXTERNAL_API_CALL(st.permissions() != perms::unknown, "Permissions unexpectedly unknown");
636     if (add_perms)
637       prms |= st.permissions();
638     else if (remove_perms)
639       prms = st.permissions() & ~prms;
640   }
641   const auto real_perms = static_cast<detail::ModeT>(prms & perms::mask);
642 
643 #if defined(AT_SYMLINK_NOFOLLOW) && defined(AT_FDCWD)
644   const int flags = set_sym_perms ? AT_SYMLINK_NOFOLLOW : 0;
645   if (detail::fchmodat(AT_FDCWD, p.c_str(), real_perms, flags) == -1) {
646     return err.report(capture_errno());
647   }
648 #else
649   if (set_sym_perms)
650     return err.report(errc::operation_not_supported);
651   if (::chmod(p.c_str(), real_perms) == -1) {
652     return err.report(capture_errno());
653   }
654 #endif
655 }
656 
__read_symlink(const path & p,error_code * ec)657 path __read_symlink(const path& p, error_code* ec) {
658   ErrorHandler<path> err("read_symlink", ec, &p);
659 
660 #if defined(PATH_MAX) || defined(MAX_SYMLINK_SIZE)
661   struct NullDeleter {
662     void operator()(void*) const {}
663   };
664 #  ifdef MAX_SYMLINK_SIZE
665   const size_t size = MAX_SYMLINK_SIZE + 1;
666 #  else
667   const size_t size = PATH_MAX + 1;
668 #  endif
669   path::value_type stack_buff[size];
670   auto buff = std::unique_ptr<path::value_type[], NullDeleter>(stack_buff);
671 #else
672   StatT sb;
673   if (detail::lstat(p.c_str(), &sb) == -1) {
674     return err.report(capture_errno());
675   }
676   const size_t size = sb.st_size + 1;
677   auto buff         = unique_ptr<path::value_type[]>(new path::value_type[size]);
678 #endif
679   detail::SSizeT ret;
680   if ((ret = detail::readlink(p.c_str(), buff.get(), size)) == -1)
681     return err.report(capture_errno());
682   // Note that `ret` returning `0` would work, resulting in a valid empty string being returned.
683   if (static_cast<size_t>(ret) >= size)
684     return err.report(errc::value_too_large);
685   buff[ret] = 0;
686   return {buff.get()};
687 }
688 
__remove(const path & p,error_code * ec)689 bool __remove(const path& p, error_code* ec) {
690   ErrorHandler<bool> err("remove", ec, &p);
691   if (detail::remove(p.c_str()) == -1) {
692     if (errno != ENOENT)
693       err.report(capture_errno());
694     return false;
695   }
696   return true;
697 }
698 
699 // We currently have two implementations of `__remove_all`. The first one is general and
700 // used on platforms where we don't have access to the `openat()` family of POSIX functions.
701 // That implementation uses `directory_iterator`, however it is vulnerable to some race
702 // conditions, see https://reviews.llvm.org/D118134 for details.
703 //
704 // The second implementation is used on platforms where `openat()` & friends are available,
705 // and it threads file descriptors through recursive calls to avoid such race conditions.
706 #if defined(_LIBCPP_WIN32API) || defined(__MVS__)
707 #  define REMOVE_ALL_USE_DIRECTORY_ITERATOR
708 #endif
709 
710 #if defined(REMOVE_ALL_USE_DIRECTORY_ITERATOR)
711 
712 namespace {
713 
remove_all_impl(path const & p,error_code & ec)714 uintmax_t remove_all_impl(path const& p, error_code& ec) {
715   const auto npos      = static_cast<uintmax_t>(-1);
716   const file_status st = __symlink_status(p, &ec);
717   if (ec)
718     return npos;
719   uintmax_t count = 1;
720   if (is_directory(st)) {
721     for (directory_iterator it(p, ec); !ec && it != directory_iterator(); it.increment(ec)) {
722       auto other_count = remove_all_impl(it->path(), ec);
723       if (ec)
724         return npos;
725       count += other_count;
726     }
727     if (ec)
728       return npos;
729   }
730   if (!__remove(p, &ec))
731     return npos;
732   return count;
733 }
734 
735 } // end namespace
736 
__remove_all(const path & p,error_code * ec)737 uintmax_t __remove_all(const path& p, error_code* ec) {
738   ErrorHandler<uintmax_t> err("remove_all", ec, &p);
739 
740   error_code mec;
741   auto count = remove_all_impl(p, mec);
742   if (mec) {
743     if (mec == errc::no_such_file_or_directory)
744       return 0;
745     return err.report(mec);
746   }
747   return count;
748 }
749 
750 #else // !REMOVE_ALL_USE_DIRECTORY_ITERATOR
751 
752 namespace {
753 
754 template <class Cleanup>
755 struct scope_exit {
scope_exit__anon9ba8c6480511::scope_exit756   explicit scope_exit(Cleanup const& cleanup) : cleanup_(cleanup) {}
757 
~scope_exit__anon9ba8c6480511::scope_exit758   ~scope_exit() { cleanup_(); }
759 
760 private:
761   Cleanup cleanup_;
762 };
763 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(scope_exit);
764 
remove_all_impl(int parent_directory,const path & p,error_code & ec)765 uintmax_t remove_all_impl(int parent_directory, const path& p, error_code& ec) {
766   // First, try to open the path as a directory.
767   const int options = O_CLOEXEC | O_RDONLY | O_DIRECTORY | O_NOFOLLOW;
768   int fd            = ::openat(parent_directory, p.c_str(), options);
769   if (fd != -1) {
770     // If that worked, iterate over the contents of the directory and
771     // remove everything in it, recursively.
772     DIR* stream = ::fdopendir(fd);
773     if (stream == nullptr) {
774       ::close(fd);
775       ec = detail::capture_errno();
776       return 0;
777     }
778     // Note: `::closedir` will also close the associated file descriptor, so
779     // there should be no call to `close(fd)`.
780     scope_exit close_stream([=] { ::closedir(stream); });
781 
782     uintmax_t count = 0;
783     while (true) {
784       auto [str, type] = detail::posix_readdir(stream, ec);
785       static_assert(std::is_same_v<decltype(str), std::string_view>);
786       if (str == "." || str == "..") {
787         continue;
788       } else if (ec || str.empty()) {
789         break; // we're done iterating through the directory
790       } else {
791         count += remove_all_impl(fd, str, ec);
792       }
793     }
794 
795     // Then, remove the now-empty directory itself.
796     if (::unlinkat(parent_directory, p.c_str(), AT_REMOVEDIR) == -1) {
797       ec = detail::capture_errno();
798       return count;
799     }
800 
801     return count + 1; // the contents of the directory + the directory itself
802   }
803 
804   ec = detail::capture_errno();
805 
806   // If we failed to open `p` because it didn't exist, it's not an
807   // error -- it might have moved or have been deleted already.
808   if (ec == errc::no_such_file_or_directory) {
809     ec.clear();
810     return 0;
811   }
812 
813   // If opening `p` failed because it wasn't a directory, remove it as
814   // a normal file instead. Note that `openat()` can return either ENOTDIR
815   // or ELOOP depending on the exact reason of the failure. On FreeBSD it
816   // may return EMLINK instead of ELOOP, contradicting POSIX.
817   if (ec == errc::not_a_directory || ec == errc::too_many_symbolic_link_levels || ec == errc::too_many_links) {
818     ec.clear();
819     if (::unlinkat(parent_directory, p.c_str(), /* flags = */ 0) == -1) {
820       ec = detail::capture_errno();
821       return 0;
822     }
823     return 1;
824   }
825 
826   // Otherwise, it's a real error -- we don't remove anything.
827   return 0;
828 }
829 
830 } // end namespace
831 
__remove_all(const path & p,error_code * ec)832 uintmax_t __remove_all(const path& p, error_code* ec) {
833   ErrorHandler<uintmax_t> err("remove_all", ec, &p);
834   error_code mec;
835   uintmax_t count = remove_all_impl(AT_FDCWD, p, mec);
836   if (mec)
837     return err.report(mec);
838   return count;
839 }
840 
841 #endif // REMOVE_ALL_USE_DIRECTORY_ITERATOR
842 
__rename(const path & from,const path & to,error_code * ec)843 void __rename(const path& from, const path& to, error_code* ec) {
844   ErrorHandler<void> err("rename", ec, &from, &to);
845   if (detail::rename(from.c_str(), to.c_str()) == -1)
846     err.report(capture_errno());
847 }
848 
__resize_file(const path & p,uintmax_t size,error_code * ec)849 void __resize_file(const path& p, uintmax_t size, error_code* ec) {
850   ErrorHandler<void> err("resize_file", ec, &p);
851   if (detail::truncate(p.c_str(), static_cast< ::off_t>(size)) == -1)
852     return err.report(capture_errno());
853 }
854 
__space(const path & p,error_code * ec)855 space_info __space(const path& p, error_code* ec) {
856   ErrorHandler<void> err("space", ec, &p);
857   space_info si;
858   detail::StatVFS m_svfs = {};
859   if (detail::statvfs(p.c_str(), &m_svfs) == -1) {
860     err.report(capture_errno());
861     si.capacity = si.free = si.available = static_cast<uintmax_t>(-1);
862     return si;
863   }
864   // Multiply with overflow checking.
865   auto do_mult = [&](uintmax_t& out, uintmax_t other) {
866     out = other * m_svfs.f_frsize;
867     if (other == 0 || out / other != m_svfs.f_frsize)
868       out = static_cast<uintmax_t>(-1);
869   };
870   do_mult(si.capacity, m_svfs.f_blocks);
871   do_mult(si.free, m_svfs.f_bfree);
872   do_mult(si.available, m_svfs.f_bavail);
873   return si;
874 }
875 
__status(const path & p,error_code * ec)876 file_status __status(const path& p, error_code* ec) { return detail::posix_stat(p, ec); }
877 
__symlink_status(const path & p,error_code * ec)878 file_status __symlink_status(const path& p, error_code* ec) { return detail::posix_lstat(p, ec); }
879 
__temp_directory_path(error_code * ec)880 path __temp_directory_path(error_code* ec) {
881   ErrorHandler<path> err("temp_directory_path", ec);
882 
883 #if defined(_LIBCPP_WIN32API)
884   wchar_t buf[MAX_PATH];
885   DWORD retval = GetTempPathW(MAX_PATH, buf);
886   if (!retval)
887     return err.report(detail::make_windows_error(GetLastError()));
888   if (retval > MAX_PATH)
889     return err.report(errc::filename_too_long);
890   // GetTempPathW returns a path with a trailing slash, which we
891   // shouldn't include for consistency.
892   if (buf[retval - 1] == L'\\')
893     buf[retval - 1] = L'\0';
894   path p(buf);
895 #else
896   const char* env_paths[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"};
897   const char* ret         = nullptr;
898 
899   for (auto& ep : env_paths)
900     if ((ret = getenv(ep)))
901       break;
902   if (ret == nullptr) {
903 #  if defined(__ANDROID__)
904     ret = "/data/local/tmp";
905 #  else
906     ret = "/tmp";
907 #  endif
908   }
909 
910   path p(ret);
911 #endif
912   error_code m_ec;
913   file_status st = detail::posix_stat(p, &m_ec);
914   if (!status_known(st))
915     return err.report(m_ec, "cannot access path " PATH_CSTR_FMT, p.c_str());
916 
917   if (!exists(st) || !is_directory(st))
918     return err.report(errc::not_a_directory, "path " PATH_CSTR_FMT " is not a directory", p.c_str());
919 
920   return p;
921 }
922 
__weakly_canonical(const path & p,error_code * ec)923 path __weakly_canonical(const path& p, error_code* ec) {
924   ErrorHandler<path> err("weakly_canonical", ec, &p);
925 
926   if (p.empty())
927     return __canonical("", ec);
928 
929   path result;
930   path tmp;
931   tmp.__reserve(p.native().size());
932   auto PP = PathParser::CreateEnd(p.native());
933   --PP;
934   vector<string_view_t> DNEParts;
935 
936   error_code m_ec;
937   while (PP.State_ != PathParser::PS_BeforeBegin) {
938     tmp.assign(createView(p.native().data(), &PP.RawEntry.back()));
939     file_status st = __status(tmp, &m_ec);
940     if (!status_known(st)) {
941       return err.report(m_ec);
942     } else if (exists(st)) {
943       result = __canonical(tmp, &m_ec);
944       if (m_ec) {
945         return err.report(m_ec);
946       }
947       break;
948     }
949     DNEParts.push_back(*PP);
950     --PP;
951   }
952   if (PP.State_ == PathParser::PS_BeforeBegin) {
953     result = __canonical("", &m_ec);
954     if (m_ec) {
955       return err.report(m_ec);
956     }
957   }
958   if (DNEParts.empty())
959     return result;
960   for (auto It = DNEParts.rbegin(); It != DNEParts.rend(); ++It)
961     result /= *It;
962   return result.lexically_normal();
963 }
964 
965 _LIBCPP_END_NAMESPACE_FILESYSTEM
966