Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/system/lib/libcxx/src/filesystem/operations.cpp
6178 views
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);
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, 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(orig_p, &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(p.c_str(), nullptr), &::free);
111
if (hold.get() == nullptr)
112
return err.report(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(from, f_st, &m_ec1) : detail::posix_stat(from, f_st, &m_ec1);
138
if (m_ec1)
139
return err.report(m_ec1);
140
141
StatT t_st;
142
const file_status t = sym_status ? detail::posix_lstat(to, t_st, &m_ec1) : detail::posix_stat(to, t_st, &m_ec1);
143
144
if (not status_known(t))
145
return err.report(m_ec1);
146
147
if (!exists(f) || is_other(f) || is_other(t) || (is_directory(f) && is_regular_file(t)) ||
148
(exists(t) && detail::stat_equivalent(f_st, t_st))) {
149
return err.report(errc::function_not_supported);
150
}
151
152
if (is_symlink(f)) {
153
if (bool(copy_options::skip_symlinks & options)) {
154
// do nothing
155
} else if (not exists(t)) {
156
__copy_symlink(from, to, ec);
157
} else {
158
return err.report(errc::file_exists);
159
}
160
return;
161
} else if (is_regular_file(f)) {
162
if (bool(copy_options::directories_only & options)) {
163
// do nothing
164
} else if (bool(copy_options::create_symlinks & options)) {
165
__create_symlink(from, to, ec);
166
} else if (bool(copy_options::create_hard_links & options)) {
167
__create_hard_link(from, to, ec);
168
} else if (is_directory(t)) {
169
__copy_file(from, to / from.filename(), options, ec);
170
} else {
171
__copy_file(from, to, options, ec);
172
}
173
return;
174
} else if (is_directory(f) && bool(copy_options::create_symlinks & options)) {
175
return err.report(errc::is_a_directory);
176
} else if (is_directory(f) && (bool(copy_options::recursive & options) || copy_options::none == options)) {
177
if (!exists(t)) {
178
// create directory to with attributes from 'from'.
179
__create_directory(to, 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(m_ec2)) {
190
__copy(it->path(), to / it->path().filename(), options | copy_options::__in_recursive_copy, ec);
191
if (ec && *ec) {
192
return;
193
}
194
}
195
if (m_ec2) {
196
return err.report(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(read_fd.fd, 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(write_fd.fd, 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(bin, ein, bout);
229
}
230
if (out.fail() || in.fail()) {
231
ec = make_error_code(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(read_fd.fd, &off_in, write_fd.fd, &off_out, count, 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(write_fd.fd, read_fd.fd, nullptr, 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(&from, m_ec, O_RDONLY | O_NONBLOCK | O_BINARY);
386
if (m_ec)
387
return err.report(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(from_st)) {
392
if (not m_ec)
393
m_ec = make_error_code(errc::not_supported);
394
return err.report(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(to, to_stat_path, &m_ec);
403
if (!status_known(to_st))
404
return err.report(m_ec);
405
406
const bool to_exists = exists(to_st);
407
if (to_exists && !is_regular_file(to_st))
408
return err.report(errc::not_supported);
409
410
if (to_exists && detail::stat_equivalent(from_stat, to_stat_path))
411
return err.report(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(from_stat);
419
auto to_time = detail::extract_mtime(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(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(&to, m_ec, to_open_flags, from_stat.st_mode);
439
if (m_ec)
440
return err.report(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(to_stat_path, to_fd.get_stat()))
447
return err.report(errc::bad_file_descriptor);
448
449
// Set the permissions and truncate the file we opened.
450
if (detail::posix_fchmod(to_fd, from_stat, m_ec))
451
return err.report(m_ec);
452
if (detail::posix_ftruncate(to_fd, 0, m_ec))
453
return err.report(m_ec);
454
}
455
456
if (!detail::copy_file_impl(from_fd, to_fd, m_ec)) {
457
// FIXME: Remove the dest file if we failed, and it didn't exist previously.
458
return err.report(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));
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(real_path, new_symlink, 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, &m_ec);
483
if (!status_known(st))
484
return err.report(m_ec);
485
else if (is_directory(st))
486
return false;
487
else if (exists(st))
488
return err.report(errc::file_exists);
489
490
const path parent = p.parent_path();
491
if (!parent.empty()) {
492
const file_status parent_st = status(parent, m_ec);
493
if (not status_known(parent_st))
494
return err.report(m_ec);
495
if (not exists(parent_st)) {
496
if (parent == p)
497
return err.report(errc::invalid_argument);
498
__create_directories(parent, ec);
499
if (ec && *ec) {
500
return false;
501
}
502
} else if (not is_directory(parent_st))
503
return err.report(errc::not_a_directory);
504
}
505
bool ret = __create_directory(p, &m_ec);
506
if (m_ec)
507
return err.report(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(p.c_str(), 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(mec);
520
error_code ignored_ec;
521
const file_status st = status(p, ignored_ec);
522
if (!is_directory(st))
523
return err.report(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(attributes, attr_stat, &mec);
533
if (!status_known(st))
534
return err.report(mec);
535
if (!is_directory(st))
536
return err.report(errc::not_a_directory, "the specified attribute path is invalid");
537
538
if (detail::mkdir(p.c_str(), 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(mec);
544
545
error_code ignored_ec;
546
st = status(p, ignored_ec);
547
if (!is_directory(st))
548
return err.report(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(from.c_str(), to.c_str()) == -1)
555
return err.report(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.c_str(), to.c_str()) == -1)
561
return err.report(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(from.c_str(), to.c_str()) == -1)
567
return err.report(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(ptr, size), deleter);
609
if (hold.get() == nullptr)
610
return err.report(detail::get_last_error(), "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(p.c_str()) == -1)
618
err.report(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(p1.native(), st1, &ec1);
627
if (!exists(s1))
628
return err.report(errc::not_supported);
629
auto s2 = detail::posix_stat(p2.native(), st2, &ec2);
630
if (!exists(s2))
631
return err.report(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, st, &m_ec);
642
if (!exists(fst) || !is_regular_file(fst)) {
643
errc error_kind = is_directory(fst) ? errc::is_a_directory : errc::not_supported;
644
if (!m_ec)
645
m_ec = make_error_code(error_kind);
646
return err.report(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, st, &m_ec);
658
if (m_ec)
659
return err.report(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, pst, &m_ec);
669
if (m_ec)
670
return err.report(m_ec);
671
else if (!is_directory(st) && !is_regular_file(st))
672
return err.report(errc::not_supported);
673
else if (is_directory(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(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, st, &m_ec);
691
if (m_ec)
692
return err.report(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(tbuf[1], new_time))
727
return err.report(errc::value_too_large);
728
729
detail::set_file_times(p, tbuf, m_ec);
730
if (m_ec)
731
return err.report(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, &m_ec) : detail::posix_lstat(p, &m_ec);
751
set_sym_perms = is_symlink(st);
752
if (m_ec)
753
return err.report(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, p.c_str(), real_perms, flags) == -1) {
767
return err.report(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(p.c_str(), buff.get(), size)) == -1)
802
return err.report(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(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(p.c_str()) == -1) {
813
error_code mec = detail::get_last_error();
814
if (mec != errc::no_such_file_or_directory)
815
err.report(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(parent_directory, p.c_str(), 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);
895
if (stream == nullptr) {
896
::close(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(stream); });
903
904
uintmax_t count = 0;
905
while (true) {
906
auto [str, type] = detail::posix_readdir(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(fd, str, ec);
914
}
915
}
916
917
// Then, remove the now-empty directory itself.
918
if (::unlinkat(parent_directory, 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(parent_directory, p.c_str(), /* flags = */ 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, mec);
958
if (mec)
959
return err.report(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(from.c_str(), to.c_str()) == -1)
968
err.report(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(p.c_str(), static_cast< ::off_t>(size)) == -1)
974
return err.report(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(p.c_str(), &m_svfs) == -1) {
982
err.report(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(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, &m_ec);
1036
if (!status_known(st))
1037
return err.report(m_ec, "cannot access path " PATH_CSTR_FMT, p.c_str());
1038
1039
if (!exists(st) || !is_directory(st))
1040
return err.report(errc::not_a_directory, "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("", ec);
1050
1051
path result;
1052
path tmp;
1053
tmp.__reserve(p.native().size());
1054
auto PP = PathParser::CreateEnd(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(createView(p.native().data(), &PP.RawEntry.back()));
1061
file_status st = __status(tmp, &m_ec);
1062
if (!status_known(st)) {
1063
return err.report(m_ec);
1064
} else if (exists(st)) {
1065
result = __canonical(tmp, &m_ec);
1066
if (m_ec) {
1067
return err.report(m_ec);
1068
}
1069
break;
1070
}
1071
DNEParts.push_back(*PP);
1072
--PP;
1073
}
1074
if (PP.State_ == PathParser::PS_BeforeBegin) {
1075
result = __canonical("", &m_ec);
1076
if (m_ec) {
1077
return err.report(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
1089