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