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