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