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 | |
80 | using detail::capture_errno; |
81 | using detail::ErrorHandler; |
82 | using detail::StatT; |
83 | using detail::TimeSpec; |
84 | using parser::createView; |
85 | using parser::PathParser; |
86 | using parser::string_view_t; |
87 | |
88 | static 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 | |
99 | path __absolute(const path& p, error_code* ec) { |
100 | path cwd; |
101 | return __do_absolute(p, cwd: &cwd, ec); |
102 | } |
103 | |
104 | path __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 | |
127 | void __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 | |
201 | namespace detail { |
202 | namespace { |
203 | |
204 | #if defined(_LIBCPP_FILESYSTEM_NEED_FSTREAM) |
205 | bool 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) |
241 | bool 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) |
275 | bool 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. |
305 | bool 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) |
349 | bool 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) |
370 | bool 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 | |
380 | bool __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 | |
464 | void __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 | |
478 | bool __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 | |
511 | bool __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 | |
527 | bool __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 | |
552 | void __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 | |
558 | void __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 | |
564 | void __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 | |
570 | path __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 | |
615 | void __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 | |
621 | bool __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 | |
636 | uintmax_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 | |
652 | uintmax_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 | |
663 | bool __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 | |
684 | file_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 | |
696 | void __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 | |
735 | void __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 | |
778 | path __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 | |
810 | bool __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 | |
834 | namespace { |
835 | |
836 | uintmax_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 | |
859 | uintmax_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 | |
874 | namespace { |
875 | |
876 | template <class Cleanup> |
877 | struct scope_exit { |
878 | explicit scope_exit(Cleanup const& cleanup) : cleanup_(cleanup) {} |
879 | |
880 | ~scope_exit() { cleanup_(); } |
881 | |
882 | private: |
883 | Cleanup cleanup_; |
884 | }; |
885 | _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(scope_exit); |
886 | |
887 | uintmax_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 | |
954 | uintmax_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 | |
965 | void __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 | |
971 | void __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 | |
977 | space_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 | |
998 | file_status __status(const path& p, error_code* ec) { return detail::posix_stat(p, ec); } |
999 | |
1000 | file_status __symlink_status(const path& p, error_code* ec) { return detail::posix_lstat(p, ec); } |
1001 | |
1002 | path __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 | |
1045 | path __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 | |