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