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
57using detail::capture_errno;
58using detail::ErrorHandler;
59using detail::StatT;
60using detail::TimeSpec;
61using parser::createView;
62using parser::PathParser;
63using parser::string_view_t;
64
65static 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
76path __absolute(const path& p, error_code* ec) {
77 path cwd;
78 return __do_absolute(p, &cwd, ec);
79}
80
81path __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
104void __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
178namespace detail {
179namespace {
180
181#if defined(_LIBCPP_FILESYSTEM_USE_SENDFILE)
182bool 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)
198bool 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)
219bool 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
259bool __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
343void __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
357bool __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
390bool __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
406bool __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
431void __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
437void __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
443void __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
449path __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
494void __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
500bool __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
515uintmax_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
531uintmax_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
542bool __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
563file_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
575void __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
614void __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
657path __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
689bool __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
712namespace {
713
714uintmax_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
737uintmax_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
752namespace {
753
754template <class Cleanup>
755struct scope_exit {
756 explicit scope_exit(Cleanup const& cleanup) : cleanup_(cleanup) {}
757
758 ~scope_exit() { cleanup_(); }
759
760private:
761 Cleanup cleanup_;
762};
763_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(scope_exit);
764
765uintmax_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
832uintmax_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
843void __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
849void __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
855space_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
876file_status __status(const path& p, error_code* ec) { return detail::posix_stat(p, ec); }
877
878file_status __symlink_status(const path& p, error_code* ec) { return detail::posix_lstat(p, ec); }
879
880path __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
923path __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