]>
Commit | Line | Data |
---|---|---|
0ca7ba9a JW |
1 | // Filesystem operations -*- C++ -*- |
2 | ||
818ab71a | 3 | // Copyright (C) 2014-2016 Free Software Foundation, Inc. |
0ca7ba9a JW |
4 | // |
5 | // This file is part of the GNU ISO C++ Library. This library is free | |
6 | // software; you can redistribute it and/or modify it under the | |
7 | // terms of the GNU General Public License as published by the | |
8 | // Free Software Foundation; either version 3, or (at your option) | |
9 | // any later version. | |
10 | ||
11 | // This library is distributed in the hope that it will be useful, | |
12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | // GNU General Public License for more details. | |
15 | ||
16 | // Under Section 7 of GPL version 3, you are granted additional | |
17 | // permissions described in the GCC Runtime Library Exception, version | |
18 | // 3.1, as published by the Free Software Foundation. | |
19 | ||
20 | // You should have received a copy of the GNU General Public License and | |
21 | // a copy of the GCC Runtime Library Exception along with this program; | |
22 | // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
23 | // <http://www.gnu.org/licenses/>. | |
24 | ||
8b756210 JW |
25 | #ifndef _GLIBCXX_USE_CXX11_ABI |
26 | # define _GLIBCXX_USE_CXX11_ABI 1 | |
27 | #endif | |
28 | ||
0ca7ba9a JW |
29 | #include <experimental/filesystem> |
30 | #include <functional> | |
0657379e | 31 | #include <ostream> |
0ca7ba9a | 32 | #include <stack> |
0657379e | 33 | #include <ext/stdio_filebuf.h> |
0ca7ba9a JW |
34 | #include <stdlib.h> |
35 | #include <stdio.h> | |
36 | #include <errno.h> | |
7c928f72 | 37 | #include <limits.h> // PATH_MAX |
0ca7ba9a JW |
38 | #ifdef _GLIBCXX_HAVE_UNISTD_H |
39 | # include <unistd.h> | |
40 | # if defined(_GLIBCXX_HAVE_SYS_STAT_H) && defined(_GLIBCXX_HAVE_SYS_TYPES_H) | |
41 | # include <sys/types.h> | |
42 | # include <sys/stat.h> | |
43 | # endif | |
44 | #endif | |
45 | #ifdef _GLIBCXX_HAVE_FCNTL_H | |
46 | # include <fcntl.h> | |
47 | #endif | |
48 | #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H | |
49 | # include <sys/statvfs.h> | |
50 | #endif | |
a0c4531c | 51 | #ifdef _GLIBCXX_USE_SENDFILE |
0ca7ba9a | 52 | # include <sys/sendfile.h> |
0ca7ba9a | 53 | #endif |
bf53e6a9 JW |
54 | #if _GLIBCXX_HAVE_UTIME_H |
55 | # include <utime.h> | |
56 | #endif | |
57 | ||
58 | #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS | |
59 | # undef utime | |
60 | # define utime _wutime | |
61 | # undef chmod | |
62 | # define chmod _wchmod | |
63 | #endif | |
0ca7ba9a JW |
64 | |
65 | namespace fs = std::experimental::filesystem; | |
66 | ||
67 | fs::path | |
68 | fs::absolute(const path& p, const path& base) | |
69 | { | |
70 | const bool has_root_dir = p.has_root_directory(); | |
71 | const bool has_root_name = p.has_root_name(); | |
72 | path abs; | |
73 | if (has_root_dir && has_root_name) | |
74 | abs = p; | |
75 | else | |
76 | { | |
77 | abs = base.is_absolute() ? base : absolute(base); | |
78 | if (has_root_dir) | |
79 | abs = abs.root_name() / p; | |
80 | else if (has_root_name) | |
81 | abs = p.root_name() / abs.root_directory() / abs.relative_path() | |
82 | / p.relative_path(); | |
83 | else | |
84 | abs = abs / p; | |
85 | } | |
86 | return abs; | |
87 | } | |
88 | ||
89 | namespace | |
90 | { | |
36670311 JW |
91 | #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS |
92 | inline bool is_dot(wchar_t c) { return c == L'.'; } | |
93 | #else | |
94 | inline bool is_dot(char c) { return c == '.'; } | |
95 | #endif | |
96 | ||
97 | inline bool is_dot(const fs::path& path) | |
98 | { | |
99 | const auto& filename = path.native(); | |
100 | return filename.size() == 1 && is_dot(filename[0]); | |
101 | } | |
102 | ||
103 | inline bool is_dotdot(const fs::path& path) | |
104 | { | |
105 | const auto& filename = path.native(); | |
106 | return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]); | |
107 | } | |
108 | ||
0ca7ba9a JW |
109 | struct free_as_in_malloc |
110 | { | |
111 | void operator()(void* p) const { ::free(p); } | |
112 | }; | |
113 | ||
114 | using char_ptr = std::unique_ptr<char[], free_as_in_malloc>; | |
115 | } | |
116 | ||
117 | fs::path | |
118 | fs::canonical(const path& p, const path& base, error_code& ec) | |
119 | { | |
30362998 JW |
120 | const path pa = absolute(p, base); |
121 | path result; | |
07dc170b | 122 | |
0ca7ba9a | 123 | #ifdef _GLIBCXX_USE_REALPATH |
30362998 JW |
124 | char_ptr buf{ nullptr }; |
125 | # if _XOPEN_VERSION < 700 | |
126 | // Not safe to call realpath(path, NULL) | |
127 | buf.reset( (char*)::malloc(PATH_MAX) ); | |
128 | # endif | |
129 | if (char* rp = ::realpath(pa.c_str(), buf.get())) | |
0ca7ba9a | 130 | { |
30362998 JW |
131 | if (buf == nullptr) |
132 | buf.reset(rp); | |
133 | result.assign(rp); | |
0ca7ba9a | 134 | ec.clear(); |
30362998 JW |
135 | return result; |
136 | } | |
137 | if (errno != ENAMETOOLONG) | |
138 | { | |
139 | ec.assign(errno, std::generic_category()); | |
140 | return result; | |
0ca7ba9a | 141 | } |
0ca7ba9a | 142 | #endif |
30362998 | 143 | |
30362998 | 144 | if (!exists(pa, ec)) |
07dc170b JW |
145 | return result; |
146 | // else: we know there are (currently) no unresolvable symlink loops | |
30362998 JW |
147 | |
148 | result = pa.root_path(); | |
149 | ||
150 | deque<path> cmpts; | |
151 | for (auto& f : pa.relative_path()) | |
152 | cmpts.push_back(f); | |
153 | ||
07dc170b JW |
154 | int max_allowed_symlinks = 40; |
155 | ||
156 | while (!cmpts.empty() && !ec) | |
30362998 JW |
157 | { |
158 | path f = std::move(cmpts.front()); | |
159 | cmpts.pop_front(); | |
160 | ||
07dc170b | 161 | if (is_dot(f)) |
30362998 | 162 | { |
07dc170b JW |
163 | if (!is_directory(result, ec) && !ec) |
164 | ec.assign(ENOTDIR, std::generic_category()); | |
30362998 | 165 | } |
07dc170b | 166 | else if (is_dotdot(f)) |
30362998 JW |
167 | { |
168 | auto parent = result.parent_path(); | |
169 | if (parent.empty()) | |
170 | result = pa.root_path(); | |
171 | else | |
172 | result.swap(parent); | |
173 | } | |
174 | else | |
175 | { | |
176 | result /= f; | |
177 | ||
178 | if (is_symlink(result, ec)) | |
179 | { | |
180 | path link = read_symlink(result, ec); | |
07dc170b | 181 | if (!ec) |
30362998 | 182 | { |
07dc170b JW |
183 | if (--max_allowed_symlinks == 0) |
184 | ec.assign(ELOOP, std::generic_category()); | |
185 | else | |
30362998 | 186 | { |
07dc170b JW |
187 | if (link.is_absolute()) |
188 | { | |
189 | result = link.root_path(); | |
190 | link = link.relative_path(); | |
191 | } | |
192 | else | |
193 | result.remove_filename(); | |
194 | ||
195 | cmpts.insert(cmpts.begin(), link.begin(), link.end()); | |
30362998 | 196 | } |
30362998 JW |
197 | } |
198 | } | |
30362998 JW |
199 | } |
200 | } | |
07dc170b JW |
201 | |
202 | if (ec || !exists(result, ec)) | |
203 | result.clear(); | |
204 | ||
30362998 | 205 | return result; |
0ca7ba9a JW |
206 | } |
207 | ||
208 | fs::path | |
209 | fs::canonical(const path& p, error_code& ec) | |
210 | { | |
211 | path cur = current_path(ec); | |
212 | if (ec.value()) | |
213 | return {}; | |
214 | return canonical(p, cur, ec); | |
215 | } | |
216 | ||
217 | fs::path | |
218 | fs::canonical(const path& p, const path& base) | |
219 | { | |
220 | error_code ec; | |
221 | path can = canonical(p, base, ec); | |
6f0800d4 JW |
222 | if (ec) |
223 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot canonicalize", p, base, | |
224 | ec)); | |
0ca7ba9a JW |
225 | return can; |
226 | } | |
227 | ||
228 | void | |
229 | fs::copy(const path& from, const path& to, copy_options options) | |
230 | { | |
231 | error_code ec; | |
232 | copy(from, to, options, ec); | |
233 | if (ec.value()) | |
234 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy", from, to, ec)); | |
235 | } | |
236 | ||
237 | namespace | |
238 | { | |
239 | template<typename Bitmask> | |
bf53e6a9 | 240 | inline bool is_set(Bitmask obj, Bitmask bits) |
0ca7ba9a JW |
241 | { |
242 | return (obj & bits) != Bitmask::none; | |
243 | } | |
244 | } | |
245 | ||
246 | #ifdef _GLIBCXX_HAVE_SYS_STAT_H | |
247 | namespace | |
248 | { | |
9caf7b27 JW |
249 | typedef struct ::stat stat_type; |
250 | ||
251 | inline fs::file_type | |
9c476ad4 | 252 | make_file_type(const stat_type& st) noexcept |
0ca7ba9a | 253 | { |
0ca7ba9a | 254 | using fs::file_type; |
0ca7ba9a JW |
255 | #ifdef _GLIBCXX_HAVE_S_ISREG |
256 | if (S_ISREG(st.st_mode)) | |
9caf7b27 | 257 | return file_type::regular; |
0ca7ba9a | 258 | else if (S_ISDIR(st.st_mode)) |
9caf7b27 | 259 | return file_type::directory; |
0ca7ba9a | 260 | else if (S_ISCHR(st.st_mode)) |
9caf7b27 | 261 | return file_type::character; |
0ca7ba9a | 262 | else if (S_ISBLK(st.st_mode)) |
9caf7b27 | 263 | return file_type::block; |
0ca7ba9a | 264 | else if (S_ISFIFO(st.st_mode)) |
9caf7b27 | 265 | return file_type::fifo; |
0ca7ba9a | 266 | else if (S_ISLNK(st.st_mode)) |
9caf7b27 | 267 | return file_type::symlink; |
0ca7ba9a | 268 | else if (S_ISSOCK(st.st_mode)) |
9caf7b27 | 269 | return file_type::socket; |
0ca7ba9a | 270 | #endif |
9caf7b27 JW |
271 | return file_type::unknown; |
272 | ||
273 | } | |
274 | ||
275 | inline fs::file_status | |
9c476ad4 | 276 | make_file_status(const stat_type& st) noexcept |
9caf7b27 JW |
277 | { |
278 | return fs::file_status{ | |
279 | make_file_type(st), | |
280 | static_cast<fs::perms>(st.st_mode) & fs::perms::mask | |
281 | }; | |
0ca7ba9a JW |
282 | } |
283 | ||
284 | inline bool | |
9c476ad4 | 285 | is_not_found_errno(int err) noexcept |
0ca7ba9a JW |
286 | { |
287 | return err == ENOENT || err == ENOTDIR; | |
288 | } | |
289 | ||
290 | inline fs::file_time_type | |
fd5effb1 | 291 | file_time(const stat_type& st, std::error_code& ec) noexcept |
0ca7ba9a JW |
292 | { |
293 | using namespace std::chrono; | |
0ca7ba9a | 294 | #ifdef _GLIBCXX_USE_ST_MTIM |
fd5effb1 JW |
295 | time_t s = st.st_mtim.tv_sec; |
296 | nanoseconds ns{st.st_mtim.tv_nsec}; | |
0ca7ba9a | 297 | #else |
fd5effb1 JW |
298 | time_t s = st.st_mtime; |
299 | nanoseconds ns{}; | |
0ca7ba9a | 300 | #endif |
fd5effb1 JW |
301 | |
302 | if (s >= (nanoseconds::max().count() / 1e9)) | |
303 | { | |
304 | ec = std::make_error_code(std::errc::value_too_large); // EOVERFLOW | |
305 | return fs::file_time_type::min(); | |
306 | } | |
307 | ec.clear(); | |
308 | return fs::file_time_type{seconds{s} + ns}; | |
0ca7ba9a JW |
309 | } |
310 | ||
14905251 JW |
311 | // Returns true if the file descriptor was successfully closed, |
312 | // otherwise returns false and the reason will be in errno. | |
313 | inline bool | |
314 | close_fd(int fd) | |
315 | { | |
316 | while (::close(fd)) | |
317 | if (errno != EINTR) | |
318 | return false; | |
319 | return true; | |
320 | } | |
321 | ||
0ca7ba9a JW |
322 | bool |
323 | do_copy_file(const fs::path& from, const fs::path& to, | |
324 | fs::copy_options option, | |
9caf7b27 | 325 | stat_type* from_st, stat_type* to_st, |
0ca7ba9a JW |
326 | std::error_code& ec) noexcept |
327 | { | |
9caf7b27 | 328 | stat_type st1, st2; |
0ca7ba9a JW |
329 | fs::file_status t, f; |
330 | ||
331 | if (to_st == nullptr) | |
332 | { | |
333 | if (::stat(to.c_str(), &st1)) | |
334 | { | |
335 | int err = errno; | |
336 | if (!is_not_found_errno(err)) | |
337 | { | |
338 | ec.assign(err, std::generic_category()); | |
339 | return false; | |
340 | } | |
341 | } | |
342 | else | |
343 | to_st = &st1; | |
344 | } | |
345 | else if (to_st == from_st) | |
346 | to_st = nullptr; | |
347 | ||
348 | if (to_st == nullptr) | |
349 | t = fs::file_status{fs::file_type::not_found}; | |
350 | else | |
351 | t = make_file_status(*to_st); | |
352 | ||
353 | if (from_st == nullptr) | |
354 | { | |
355 | if (::stat(from.c_str(), &st2)) | |
356 | { | |
357 | ec.assign(errno, std::generic_category()); | |
358 | return false; | |
359 | } | |
360 | else | |
361 | from_st = &st2; | |
362 | } | |
363 | f = make_file_status(*from_st); | |
364 | ||
a0c4531c JW |
365 | using opts = fs::copy_options; |
366 | ||
0ca7ba9a JW |
367 | if (exists(t)) |
368 | { | |
369 | if (!is_other(t) && !is_other(f) | |
370 | && to_st->st_dev == from_st->st_dev | |
371 | && to_st->st_ino == from_st->st_ino) | |
372 | { | |
373 | ec = std::make_error_code(std::errc::file_exists); | |
374 | return false; | |
375 | } | |
376 | ||
a0c4531c | 377 | if (is_set(option, opts::skip_existing)) |
0ca7ba9a JW |
378 | { |
379 | ec.clear(); | |
380 | return false; | |
381 | } | |
a0c4531c | 382 | else if (is_set(option, opts::update_existing)) |
0ca7ba9a | 383 | { |
fd5effb1 JW |
384 | const auto from_mtime = file_time(*from_st, ec); |
385 | if (ec) | |
386 | return false; | |
387 | if ((from_mtime <= file_time(*to_st, ec)) || ec) | |
388 | return false; | |
0ca7ba9a | 389 | } |
a0c4531c | 390 | else if (!is_set(option, opts::overwrite_existing)) |
0ca7ba9a JW |
391 | { |
392 | ec = std::make_error_code(std::errc::file_exists); | |
393 | return false; | |
394 | } | |
395 | } | |
396 | ||
397 | struct CloseFD { | |
14905251 JW |
398 | ~CloseFD() { if (fd != -1) close_fd(fd); } |
399 | bool close() { return close_fd(std::exchange(fd, -1)); } | |
0ca7ba9a JW |
400 | int fd; |
401 | }; | |
402 | ||
403 | CloseFD in = { ::open(from.c_str(), O_RDONLY) }; | |
404 | if (in.fd == -1) | |
405 | { | |
406 | ec.assign(errno, std::generic_category()); | |
407 | return false; | |
408 | } | |
a0c4531c JW |
409 | int oflag = O_WRONLY|O_CREAT; |
410 | if (is_set(option, opts::overwrite_existing|opts::update_existing)) | |
411 | oflag |= O_TRUNC; | |
412 | else | |
413 | oflag |= O_EXCL; | |
414 | CloseFD out = { ::open(to.c_str(), oflag, S_IWUSR) }; | |
0ca7ba9a JW |
415 | if (out.fd == -1) |
416 | { | |
a0c4531c JW |
417 | if (errno == EEXIST && is_set(option, opts::skip_existing)) |
418 | ec.clear(); | |
419 | else | |
420 | ec.assign(errno, std::generic_category()); | |
0ca7ba9a JW |
421 | return false; |
422 | } | |
423 | ||
14905251 JW |
424 | #ifdef _GLIBCXX_USE_FCHMOD |
425 | if (::fchmod(out.fd, from_st->st_mode)) | |
0657379e | 426 | #elif defined _GLIBCXX_USE_FCHMODAT |
14905251 JW |
427 | if (::fchmodat(AT_FDCWD, to.c_str(), from_st->st_mode, 0)) |
428 | #else | |
429 | if (::chmod(to.c_str(), from_st->st_mode)) | |
430 | #endif | |
431 | { | |
432 | ec.assign(errno, std::generic_category()); | |
433 | return false; | |
434 | } | |
435 | ||
a0c4531c | 436 | #ifdef _GLIBCXX_USE_SENDFILE |
14905251 | 437 | const auto n = ::sendfile(out.fd, in.fd, nullptr, from_st->st_size); |
0657379e | 438 | if (n < 0 && (errno == ENOSYS || errno == EINVAL)) |
0ca7ba9a | 439 | { |
0657379e UB |
440 | #endif |
441 | __gnu_cxx::stdio_filebuf<char> sbin(in.fd, std::ios::in); | |
442 | __gnu_cxx::stdio_filebuf<char> sbout(out.fd, std::ios::out); | |
443 | if (sbin.is_open()) | |
444 | in.fd = -1; | |
445 | if (sbout.is_open()) | |
446 | out.fd = -1; | |
447 | if (from_st->st_size && !(std::ostream(&sbout) << &sbin)) | |
448 | { | |
449 | ec = std::make_error_code(std::errc::io_error); | |
450 | return false; | |
451 | } | |
452 | if (!sbout.close() || !sbin.close()) | |
453 | { | |
454 | ec.assign(errno, std::generic_category()); | |
455 | return false; | |
456 | } | |
457 | ||
458 | ec.clear(); | |
459 | return true; | |
460 | ||
461 | #ifdef _GLIBCXX_USE_SENDFILE | |
0ca7ba9a | 462 | } |
0657379e | 463 | if (n != from_st->st_size) |
14905251 JW |
464 | { |
465 | ec.assign(errno, std::generic_category()); | |
466 | return false; | |
467 | } | |
0657379e | 468 | if (!out.close() || !in.close()) |
0ca7ba9a JW |
469 | { |
470 | ec.assign(errno, std::generic_category()); | |
471 | return false; | |
472 | } | |
14905251 | 473 | |
a0c4531c | 474 | ec.clear(); |
0ca7ba9a | 475 | return true; |
0657379e | 476 | #endif |
0ca7ba9a JW |
477 | } |
478 | } | |
479 | #endif | |
480 | ||
481 | void | |
482 | fs::copy(const path& from, const path& to, copy_options options, | |
483 | error_code& ec) noexcept | |
484 | { | |
14905251 JW |
485 | const bool skip_symlinks = is_set(options, copy_options::skip_symlinks); |
486 | const bool create_symlinks = is_set(options, copy_options::create_symlinks); | |
487 | const bool copy_symlinks = is_set(options, copy_options::copy_symlinks); | |
488 | const bool use_lstat = create_symlinks || skip_symlinks; | |
0ca7ba9a JW |
489 | |
490 | file_status f, t; | |
9caf7b27 | 491 | stat_type from_st, to_st; |
14905251 JW |
492 | // N4099 doesn't check copy_symlinks here, but I think that's a defect. |
493 | if (use_lstat || copy_symlinks | |
0ca7ba9a JW |
494 | ? ::lstat(from.c_str(), &from_st) |
495 | : ::stat(from.c_str(), &from_st)) | |
496 | { | |
497 | ec.assign(errno, std::generic_category()); | |
498 | return; | |
499 | } | |
500 | if (use_lstat | |
501 | ? ::lstat(to.c_str(), &to_st) | |
502 | : ::stat(to.c_str(), &to_st)) | |
503 | { | |
504 | if (!is_not_found_errno(errno)) | |
505 | { | |
506 | ec.assign(errno, std::generic_category()); | |
507 | return; | |
508 | } | |
509 | t = file_status{file_type::not_found}; | |
510 | } | |
511 | else | |
512 | t = make_file_status(to_st); | |
513 | f = make_file_status(from_st); | |
514 | ||
515 | if (exists(t) && !is_other(t) && !is_other(f) | |
516 | && to_st.st_dev == from_st.st_dev && to_st.st_ino == from_st.st_ino) | |
517 | { | |
518 | ec = std::make_error_code(std::errc::file_exists); | |
519 | return; | |
520 | } | |
521 | if (is_other(f) || is_other(t)) | |
522 | { | |
523 | ec = std::make_error_code(std::errc::not_supported); | |
524 | return; | |
525 | } | |
526 | if (is_directory(f) && is_regular_file(t)) | |
527 | { | |
528 | ec = std::make_error_code(std::errc::is_a_directory); | |
529 | return; | |
530 | } | |
531 | ||
532 | if (is_symlink(f)) | |
533 | { | |
534 | if (skip_symlinks) | |
535 | ec.clear(); | |
14905251 | 536 | else if (!exists(t) && copy_symlinks) |
0ca7ba9a JW |
537 | copy_symlink(from, to, ec); |
538 | else | |
539 | // Not clear what should be done here. | |
540 | // "Otherwise report an error as specified in Error reporting (7)." | |
541 | ec = std::make_error_code(std::errc::invalid_argument); | |
542 | } | |
543 | else if (is_regular_file(f)) | |
544 | { | |
545 | if (is_set(options, copy_options::directories_only)) | |
546 | ec.clear(); | |
547 | else if (create_symlinks) | |
548 | create_symlink(from, to, ec); | |
549 | else if (is_set(options, copy_options::create_hard_links)) | |
550 | create_hard_link(from, to, ec); | |
551 | else if (is_directory(t)) | |
552 | do_copy_file(from, to / from.filename(), options, &from_st, 0, ec); | |
553 | else | |
554 | { | |
555 | auto ptr = exists(t) ? &to_st : &from_st; | |
556 | do_copy_file(from, to, options, &from_st, ptr, ec); | |
557 | } | |
558 | } | |
559 | else if (is_directory(f) && (is_set(options, copy_options::recursive) | |
560 | || options == copy_options::none)) | |
561 | { | |
562 | if (!exists(t)) | |
563 | if (!create_directory(to, from, ec)) | |
564 | return; | |
565 | // set an unused bit in options to disable further recursion | |
566 | if (!is_set(options, copy_options::recursive)) | |
567 | options |= static_cast<copy_options>(4096); | |
568 | for (const directory_entry& x : directory_iterator(from)) | |
569 | copy(x.path(), to/x.path().filename(), options, ec); | |
570 | } | |
571 | // "Otherwise no effects." (should ec.clear() be called?) | |
572 | } | |
573 | ||
574 | bool | |
575 | fs::copy_file(const path& from, const path& to, copy_options option) | |
576 | { | |
577 | error_code ec; | |
578 | bool result = copy_file(from, to, option, ec); | |
579 | if (ec.value()) | |
580 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to, | |
581 | ec)); | |
582 | return result; | |
583 | } | |
584 | ||
585 | bool | |
586 | fs::copy_file(const path& from, const path& to, copy_options option, | |
587 | error_code& ec) noexcept | |
588 | { | |
589 | #ifdef _GLIBCXX_HAVE_SYS_STAT_H | |
590 | return do_copy_file(from, to, option, nullptr, nullptr, ec); | |
591 | #else | |
592 | ec = std::make_error_code(std::errc::not_supported); | |
593 | return false; | |
594 | #endif | |
595 | } | |
596 | ||
597 | ||
598 | void | |
599 | fs::copy_symlink(const path& existing_symlink, const path& new_symlink) | |
600 | { | |
601 | error_code ec; | |
602 | copy_symlink(existing_symlink, new_symlink, ec); | |
603 | if (ec.value()) | |
604 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink", | |
605 | existing_symlink, new_symlink, ec)); | |
606 | } | |
607 | ||
608 | void | |
609 | fs::copy_symlink(const path& existing_symlink, const path& new_symlink, | |
610 | error_code& ec) noexcept | |
611 | { | |
612 | auto p = read_symlink(existing_symlink, ec); | |
613 | if (ec.value()) | |
614 | return; | |
615 | #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS | |
616 | if (is_directory(p)) | |
617 | { | |
618 | create_directory_symlink(p, new_symlink, ec); | |
619 | return; | |
620 | } | |
621 | #endif | |
622 | create_symlink(p, new_symlink, ec); | |
623 | } | |
624 | ||
625 | ||
626 | bool | |
627 | fs::create_directories(const path& p) | |
628 | { | |
629 | error_code ec; | |
630 | bool result = create_directories(p, ec); | |
631 | if (ec.value()) | |
632 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p, | |
633 | ec)); | |
634 | return result; | |
635 | } | |
636 | ||
637 | bool | |
638 | fs::create_directories(const path& p, error_code& ec) noexcept | |
639 | { | |
36670311 JW |
640 | if (p.empty()) |
641 | { | |
642 | ec = std::make_error_code(errc::invalid_argument); | |
643 | return false; | |
644 | } | |
0ca7ba9a JW |
645 | std::stack<path> missing; |
646 | path pp = p; | |
36670311 JW |
647 | |
648 | while (!pp.empty() && status(pp, ec).type() == file_type::not_found) | |
0ca7ba9a | 649 | { |
36670311 JW |
650 | ec.clear(); |
651 | const auto& filename = pp.filename(); | |
652 | if (!is_dot(filename) && !is_dotdot(filename)) | |
653 | missing.push(pp); | |
654 | pp.remove_filename(); | |
0ca7ba9a | 655 | } |
36670311 JW |
656 | |
657 | if (ec || missing.empty()) | |
658 | return false; | |
659 | ||
660 | do | |
0ca7ba9a | 661 | { |
36670311 JW |
662 | const path& top = missing.top(); |
663 | create_directory(top, ec); | |
664 | if (ec && is_directory(top)) | |
665 | ec.clear(); | |
0ca7ba9a JW |
666 | missing.pop(); |
667 | } | |
36670311 JW |
668 | while (!missing.empty() && !ec); |
669 | ||
0ca7ba9a JW |
670 | return missing.empty(); |
671 | } | |
672 | ||
673 | namespace | |
674 | { | |
675 | bool | |
676 | create_dir(const fs::path& p, fs::perms perm, std::error_code& ec) | |
677 | { | |
f9a39467 | 678 | bool created = false; |
0ca7ba9a JW |
679 | #ifdef _GLIBCXX_HAVE_SYS_STAT_H |
680 | ::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm); | |
681 | if (::mkdir(p.c_str(), mode)) | |
682 | { | |
f9a39467 JW |
683 | const int err = errno; |
684 | if (err != EEXIST || !is_directory(p)) | |
685 | ec.assign(err, std::generic_category()); | |
686 | else | |
687 | ec.clear(); | |
0ca7ba9a JW |
688 | } |
689 | else | |
690 | { | |
691 | ec.clear(); | |
f9a39467 | 692 | created = true; |
0ca7ba9a JW |
693 | } |
694 | #else | |
695 | ec = std::make_error_code(std::errc::not_supported); | |
0ca7ba9a | 696 | #endif |
f9a39467 | 697 | return created; |
0ca7ba9a JW |
698 | } |
699 | } // namespace | |
700 | ||
701 | bool | |
702 | fs::create_directory(const path& p) | |
703 | { | |
704 | error_code ec; | |
705 | bool result = create_directory(p, ec); | |
706 | if (ec.value()) | |
707 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p, | |
708 | ec)); | |
709 | return result; | |
710 | } | |
711 | ||
712 | bool | |
713 | fs::create_directory(const path& p, error_code& ec) noexcept | |
714 | { | |
715 | return create_dir(p, perms::all, ec); | |
716 | } | |
717 | ||
718 | ||
719 | bool | |
720 | fs::create_directory(const path& p, const path& attributes) | |
721 | { | |
722 | error_code ec; | |
723 | bool result = create_directory(p, attributes, ec); | |
724 | if (ec.value()) | |
725 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p, | |
726 | ec)); | |
727 | return result; | |
728 | } | |
729 | ||
730 | bool | |
731 | fs::create_directory(const path& p, const path& attributes, | |
732 | error_code& ec) noexcept | |
733 | { | |
734 | #ifdef _GLIBCXX_HAVE_SYS_STAT_H | |
9caf7b27 | 735 | stat_type st; |
0ca7ba9a JW |
736 | if (::stat(attributes.c_str(), &st)) |
737 | { | |
738 | ec.assign(errno, std::generic_category()); | |
739 | return false; | |
740 | } | |
741 | return create_dir(p, static_cast<perms>(st.st_mode), ec); | |
742 | #else | |
743 | ec = std::make_error_code(std::errc::not_supported); | |
744 | return false; | |
745 | #endif | |
746 | } | |
747 | ||
748 | ||
749 | void | |
750 | fs::create_directory_symlink(const path& to, const path& new_symlink) | |
751 | { | |
752 | error_code ec; | |
753 | create_directory_symlink(to, new_symlink, ec); | |
754 | if (ec.value()) | |
755 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory symlink", | |
756 | to, new_symlink, ec)); | |
757 | } | |
758 | ||
759 | void | |
760 | fs::create_directory_symlink(const path& to, const path& new_symlink, | |
761 | error_code& ec) noexcept | |
762 | { | |
763 | #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS | |
764 | ec = std::make_error_code(std::errc::not_supported); | |
765 | #else | |
766 | create_symlink(to, new_symlink, ec); | |
767 | #endif | |
768 | } | |
769 | ||
770 | ||
771 | void | |
772 | fs::create_hard_link(const path& to, const path& new_hard_link) | |
773 | { | |
774 | error_code ec; | |
775 | create_hard_link(to, new_hard_link, ec); | |
776 | if (ec.value()) | |
777 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link", | |
778 | to, new_hard_link, ec)); | |
779 | } | |
780 | ||
781 | void | |
782 | fs::create_hard_link(const path& to, const path& new_hard_link, | |
783 | error_code& ec) noexcept | |
784 | { | |
785 | #ifdef _GLIBCXX_HAVE_UNISTD_H | |
786 | if (::link(to.c_str(), new_hard_link.c_str())) | |
787 | ec.assign(errno, std::generic_category()); | |
788 | else | |
789 | ec.clear(); | |
790 | #else | |
791 | ec = std::make_error_code(std::errc::not_supported); | |
792 | #endif | |
793 | } | |
794 | ||
795 | void | |
796 | fs::create_symlink(const path& to, const path& new_symlink) | |
797 | { | |
798 | error_code ec; | |
799 | create_symlink(to, new_symlink, ec); | |
800 | if (ec.value()) | |
801 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink", | |
802 | to, new_symlink, ec)); | |
803 | } | |
804 | ||
805 | void | |
806 | fs::create_symlink(const path& to, const path& new_symlink, | |
807 | error_code& ec) noexcept | |
808 | { | |
809 | #ifdef _GLIBCXX_HAVE_UNISTD_H | |
810 | if (::symlink(to.c_str(), new_symlink.c_str())) | |
811 | ec.assign(errno, std::generic_category()); | |
812 | else | |
813 | ec.clear(); | |
814 | #else | |
815 | ec = std::make_error_code(std::errc::not_supported); | |
816 | #endif | |
817 | } | |
818 | ||
819 | ||
820 | fs::path | |
821 | fs::current_path() | |
822 | { | |
823 | error_code ec; | |
824 | path p = current_path(ec); | |
825 | if (ec.value()) | |
826 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec)); | |
827 | return p; | |
828 | } | |
829 | ||
830 | fs::path | |
831 | fs::current_path(error_code& ec) | |
832 | { | |
833 | path p; | |
834 | #ifdef _GLIBCXX_HAVE_UNISTD_H | |
835 | #ifdef __GLIBC__ | |
836 | if (char_ptr cwd = char_ptr{::getcwd(nullptr, 0)}) | |
837 | { | |
838 | p.assign(cwd.get()); | |
839 | ec.clear(); | |
840 | } | |
841 | else | |
842 | ec.assign(errno, std::generic_category()); | |
843 | #else | |
844 | long path_max = pathconf(".", _PC_PATH_MAX); | |
845 | size_t size; | |
846 | if (path_max == -1) | |
847 | size = 1024; | |
848 | else if (path_max > 10240) | |
849 | size = 10240; | |
850 | else | |
851 | size = path_max; | |
852 | for (char_ptr buf; p.empty(); size *= 2) | |
853 | { | |
854 | buf.reset((char*)malloc(size)); | |
855 | if (buf) | |
856 | { | |
857 | if (getcwd(buf.get(), size)) | |
858 | { | |
859 | p.assign(buf.get()); | |
860 | ec.clear(); | |
861 | } | |
862 | else if (errno != ERANGE) | |
863 | { | |
864 | ec.assign(errno, std::generic_category()); | |
865 | return {}; | |
866 | } | |
867 | } | |
868 | else | |
869 | { | |
870 | ec = std::make_error_code(std::errc::not_enough_memory); | |
871 | return {}; | |
872 | } | |
873 | } | |
874 | #endif // __GLIBC__ | |
875 | #else // _GLIBCXX_HAVE_UNISTD_H | |
876 | ec = std::make_error_code(std::errc::not_supported); | |
877 | #endif | |
878 | return p; | |
879 | } | |
880 | ||
881 | void | |
882 | fs::current_path(const path& p) | |
883 | { | |
884 | error_code ec; | |
885 | current_path(p, ec); | |
886 | if (ec.value()) | |
887 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec)); | |
888 | } | |
889 | ||
890 | void | |
891 | fs::current_path(const path& p, error_code& ec) noexcept | |
892 | { | |
893 | #ifdef _GLIBCXX_HAVE_UNISTD_H | |
a0c4531c JW |
894 | if (::chdir(p.c_str())) |
895 | ec.assign(errno, std::generic_category()); | |
0ca7ba9a JW |
896 | else |
897 | ec.clear(); | |
898 | #else | |
899 | ec = std::make_error_code(std::errc::not_supported); | |
900 | #endif | |
901 | } | |
902 | ||
903 | bool | |
904 | fs::equivalent(const path& p1, const path& p2) | |
905 | { | |
906 | error_code ec; | |
907 | auto result = equivalent(p1, p2, ec); | |
908 | if (ec.value()) | |
909 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence", | |
910 | p1, p2, ec)); | |
911 | return result; | |
912 | } | |
913 | ||
914 | bool | |
915 | fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept | |
916 | { | |
917 | #ifdef _GLIBCXX_HAVE_SYS_STAT_H | |
9caf7b27 | 918 | stat_type st1, st2; |
0ca7ba9a JW |
919 | if (::stat(p1.c_str(), &st1) == 0 && ::stat(p2.c_str(), &st2) == 0) |
920 | { | |
921 | file_status s1 = make_file_status(st1); | |
922 | file_status s2 = make_file_status(st2); | |
923 | if (is_other(s1) && is_other(s2)) | |
924 | { | |
925 | ec = std::make_error_code(std::errc::not_supported); | |
926 | return false; | |
927 | } | |
928 | ec.clear(); | |
929 | return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino; | |
930 | } | |
931 | else if (is_not_found_errno(errno)) | |
932 | { | |
933 | ec = std::make_error_code(std::errc::no_such_file_or_directory); | |
934 | return false; | |
935 | } | |
936 | ec.assign(errno, std::generic_category()); | |
937 | #else | |
938 | ec = std::make_error_code(std::errc::not_supported); | |
939 | #endif | |
940 | return false; | |
941 | } | |
942 | ||
943 | std::uintmax_t | |
944 | fs::file_size(const path& p) | |
945 | { | |
946 | error_code ec; | |
947 | auto sz = file_size(p, ec); | |
948 | if (ec.value()) | |
949 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec)); | |
950 | return sz; | |
951 | } | |
952 | ||
953 | namespace | |
954 | { | |
955 | template<typename Accessor, typename T> | |
bf53e6a9 | 956 | inline T |
0ca7ba9a JW |
957 | do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt) |
958 | { | |
959 | #ifdef _GLIBCXX_HAVE_SYS_STAT_H | |
9caf7b27 | 960 | stat_type st; |
0ca7ba9a JW |
961 | if (::stat(p.c_str(), &st)) |
962 | { | |
963 | ec.assign(errno, std::generic_category()); | |
964 | return deflt; | |
965 | } | |
966 | ec.clear(); | |
967 | return f(st); | |
968 | #else | |
969 | ec = std::make_error_code(std::errc::not_supported); | |
970 | return deflt; | |
971 | #endif | |
972 | } | |
973 | } | |
974 | ||
975 | std::uintmax_t | |
976 | fs::file_size(const path& p, error_code& ec) noexcept | |
977 | { | |
9caf7b27 JW |
978 | struct S |
979 | { | |
980 | S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { } | |
981 | S() : type(file_type::not_found) { } | |
982 | file_type type; | |
983 | size_t size; | |
984 | }; | |
985 | auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{}); | |
986 | if (s.type == file_type::regular) | |
987 | return s.size; | |
988 | if (!ec) | |
989 | { | |
990 | if (s.type == file_type::directory) | |
991 | ec = std::make_error_code(std::errc::is_a_directory); | |
992 | else | |
993 | ec = std::make_error_code(std::errc::not_supported); | |
994 | } | |
995 | return -1; | |
0ca7ba9a JW |
996 | } |
997 | ||
998 | std::uintmax_t | |
999 | fs::hard_link_count(const path& p) | |
1000 | { | |
1001 | error_code ec; | |
1002 | auto count = hard_link_count(p, ec); | |
1003 | if (ec.value()) | |
1004 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec)); | |
1005 | return count; | |
1006 | } | |
1007 | ||
1008 | std::uintmax_t | |
1009 | fs::hard_link_count(const path& p, error_code& ec) noexcept | |
1010 | { | |
1011 | return do_stat(p, ec, std::mem_fn(&stat::st_nlink), | |
1012 | static_cast<uintmax_t>(-1)); | |
1013 | } | |
1014 | ||
1015 | bool | |
1016 | fs::is_empty(const path& p) | |
1017 | { | |
1018 | return fs::is_directory(status(p)) | |
1019 | ? fs::directory_iterator(p) == fs::directory_iterator() | |
1020 | : fs::file_size(p) == 0; | |
1021 | } | |
1022 | ||
1023 | bool | |
1024 | fs::is_empty(const path& p, error_code& ec) noexcept | |
1025 | { | |
1026 | auto s = status(p, ec); | |
1027 | if (ec.value()) | |
1028 | return false; | |
1029 | return fs::is_directory(s) | |
1030 | ? fs::directory_iterator(p, ec) == fs::directory_iterator() | |
1031 | : fs::file_size(p, ec) == 0; | |
1032 | } | |
1033 | ||
1034 | fs::file_time_type | |
1035 | fs::last_write_time(const path& p) | |
1036 | { | |
1037 | error_code ec; | |
1038 | auto t = last_write_time(p, ec); | |
1039 | if (ec.value()) | |
1040 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec)); | |
1041 | return t; | |
1042 | } | |
1043 | ||
1044 | fs::file_time_type | |
1045 | fs::last_write_time(const path& p, error_code& ec) noexcept | |
1046 | { | |
fd5effb1 | 1047 | return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); }, |
0ca7ba9a JW |
1048 | file_time_type::min()); |
1049 | } | |
1050 | ||
1051 | void | |
1052 | fs::last_write_time(const path& p, file_time_type new_time) | |
1053 | { | |
1054 | error_code ec; | |
1055 | last_write_time(p, new_time, ec); | |
1056 | if (ec.value()) | |
1057 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec)); | |
1058 | } | |
1059 | ||
1060 | void | |
1061 | fs::last_write_time(const path& p __attribute__((__unused__)), | |
1062 | file_time_type new_time, error_code& ec) noexcept | |
1063 | { | |
1064 | auto d = new_time.time_since_epoch(); | |
1065 | auto s = chrono::duration_cast<chrono::seconds>(d); | |
58f270df | 1066 | #if _GLIBCXX_USE_UTIMENSAT |
0ca7ba9a | 1067 | auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s); |
58f270df JW |
1068 | struct ::timespec ts[2]; |
1069 | ts[0].tv_sec = 0; | |
1070 | ts[0].tv_nsec = UTIME_OMIT; | |
1071 | ts[1].tv_sec = static_cast<std::time_t>(s.count()); | |
1072 | ts[1].tv_nsec = static_cast<long>(ns.count()); | |
1073 | if (::utimensat(AT_FDCWD, p.c_str(), ts, 0)) | |
0ca7ba9a JW |
1074 | ec.assign(errno, std::generic_category()); |
1075 | else | |
1076 | ec.clear(); | |
bf53e6a9 JW |
1077 | #elif _GLIBCXX_HAVE_UTIME_H |
1078 | ::utimbuf times; | |
1079 | times.modtime = s.count(); | |
58f270df JW |
1080 | times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; }, |
1081 | times.modtime); | |
bf53e6a9 JW |
1082 | if (::utime(p.c_str(), ×)) |
1083 | ec.assign(errno, std::generic_category()); | |
1084 | else | |
1085 | ec.clear(); | |
0ca7ba9a JW |
1086 | #else |
1087 | ec = std::make_error_code(std::errc::not_supported); | |
1088 | #endif | |
1089 | } | |
1090 | ||
1091 | void | |
1092 | fs::permissions(const path& p, perms prms) | |
1093 | { | |
1094 | error_code ec; | |
1095 | permissions(p, prms, ec); | |
1096 | if (ec.value()) | |
1097 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec)); | |
1098 | } | |
1099 | ||
1100 | void fs::permissions(const path& p, perms prms, error_code& ec) noexcept | |
1101 | { | |
94229fb6 JW |
1102 | const bool add = is_set(prms, perms::add_perms); |
1103 | const bool remove = is_set(prms, perms::remove_perms); | |
d17f7088 | 1104 | const bool nofollow = is_set(prms, perms::symlink_nofollow); |
94229fb6 JW |
1105 | if (add && remove) |
1106 | { | |
1107 | ec = std::make_error_code(std::errc::invalid_argument); | |
1108 | return; | |
1109 | } | |
1110 | ||
1111 | prms &= perms::mask; | |
1112 | ||
1113 | if (add || remove) | |
1114 | { | |
d17f7088 | 1115 | auto st = nofollow ? symlink_status(p, ec) : status(p, ec); |
94229fb6 JW |
1116 | if (ec) |
1117 | return; | |
1118 | auto curr = st.permissions(); | |
1119 | if (add) | |
1120 | prms |= curr; | |
1121 | else | |
1122 | prms = curr & ~prms; | |
1123 | } | |
1124 | ||
bf53e6a9 | 1125 | #if _GLIBCXX_USE_FCHMODAT |
d17f7088 JW |
1126 | const int flag = nofollow ? AT_SYMLINK_NOFOLLOW : 0; |
1127 | if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag)) | |
bf53e6a9 | 1128 | #else |
d17f7088 JW |
1129 | if (nofollow) |
1130 | ec = std::make_error_code(std::errc::operation_not_supported); | |
1131 | else if (::chmod(p.c_str(), static_cast<mode_t>(prms))) | |
bf53e6a9 | 1132 | #endif |
a0c4531c | 1133 | ec.assign(errno, std::generic_category()); |
0ca7ba9a JW |
1134 | else |
1135 | ec.clear(); | |
1136 | } | |
1137 | ||
1138 | fs::path | |
1139 | fs::read_symlink(const path& p) | |
1140 | { | |
1141 | error_code ec; | |
1142 | path tgt = read_symlink(p, ec); | |
1143 | if (ec.value()) | |
1144 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec)); | |
1145 | return tgt; | |
1146 | } | |
1147 | ||
1148 | fs::path fs::read_symlink(const path& p, error_code& ec) | |
1149 | { | |
1150 | #ifdef _GLIBCXX_HAVE_SYS_STAT_H | |
9caf7b27 | 1151 | stat_type st; |
0ca7ba9a JW |
1152 | if (::lstat(p.c_str(), &st)) |
1153 | { | |
1154 | ec.assign(errno, std::generic_category()); | |
1155 | return {}; | |
1156 | } | |
1157 | std::string buf(st.st_size, '\0'); | |
1158 | ssize_t len = ::readlink(p.c_str(), &buf.front(), buf.size()); | |
1159 | if (len == -1) | |
1160 | { | |
1161 | ec.assign(errno, std::generic_category()); | |
1162 | return {}; | |
1163 | } | |
1164 | return path{buf.data(), buf.data()+len}; | |
1165 | #else | |
1166 | ec = std::make_error_code(std::errc::not_supported); | |
1167 | return {}; | |
1168 | #endif | |
1169 | } | |
1170 | ||
1171 | ||
1172 | bool | |
1173 | fs::remove(const path& p) | |
1174 | { | |
1175 | error_code ec; | |
1176 | bool result = fs::remove(p, ec); | |
1177 | if (ec.value()) | |
1178 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec)); | |
1179 | return result; | |
1180 | } | |
1181 | ||
1182 | bool | |
1183 | fs::remove(const path& p, error_code& ec) noexcept | |
1184 | { | |
1185 | if (exists(symlink_status(p, ec))) | |
1186 | { | |
1187 | if (::remove(p.c_str()) == 0) | |
1188 | { | |
1189 | ec.clear(); | |
1190 | return true; | |
1191 | } | |
1192 | else | |
1193 | ec.assign(errno, std::generic_category()); | |
1194 | } | |
1195 | return false; | |
1196 | } | |
1197 | ||
1198 | ||
1199 | std::uintmax_t | |
1200 | fs::remove_all(const path& p) | |
1201 | { | |
1202 | error_code ec; | |
1203 | bool result = remove_all(p, ec); | |
1204 | if (ec.value()) | |
1205 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec)); | |
1206 | return result; | |
1207 | } | |
1208 | ||
1209 | std::uintmax_t | |
1210 | fs::remove_all(const path& p, error_code& ec) noexcept | |
1211 | { | |
1212 | auto fs = symlink_status(p, ec); | |
1213 | uintmax_t count = 0; | |
1214 | if (ec.value() == 0 && fs.type() == file_type::directory) | |
1215 | for (directory_iterator d(p, ec), end; ec.value() == 0 && d != end; ++d) | |
e12880f9 | 1216 | count += fs::remove_all(d->path(), ec); |
0ca7ba9a JW |
1217 | if (ec.value()) |
1218 | return -1; | |
1219 | return fs::remove(p, ec) ? ++count : -1; // fs:remove() calls ec.clear() | |
1220 | } | |
1221 | ||
1222 | void | |
1223 | fs::rename(const path& from, const path& to) | |
1224 | { | |
1225 | error_code ec; | |
1226 | rename(from, to, ec); | |
1227 | if (ec.value()) | |
1228 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec)); | |
1229 | } | |
1230 | ||
1231 | void | |
1232 | fs::rename(const path& from, const path& to, error_code& ec) noexcept | |
1233 | { | |
1234 | if (::rename(from.c_str(), to.c_str())) | |
1235 | ec.assign(errno, std::generic_category()); | |
1236 | else | |
1237 | ec.clear(); | |
1238 | } | |
1239 | ||
1240 | void | |
1241 | fs::resize_file(const path& p, uintmax_t size) | |
1242 | { | |
1243 | error_code ec; | |
1244 | resize_file(p, size, ec); | |
1245 | if (ec.value()) | |
1246 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec)); | |
1247 | } | |
1248 | ||
1249 | void | |
1250 | fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept | |
1251 | { | |
1252 | #ifdef _GLIBCXX_HAVE_UNISTD_H | |
1253 | if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max())) | |
1254 | ec.assign(EINVAL, std::generic_category()); | |
1255 | else if (::truncate(p.c_str(), size)) | |
1256 | ec.assign(errno, std::generic_category()); | |
1257 | else | |
1258 | ec.clear(); | |
1259 | #else | |
1260 | ec = std::make_error_code(std::errc::not_supported); | |
1261 | #endif | |
1262 | } | |
1263 | ||
1264 | ||
1265 | fs::space_info | |
1266 | fs::space(const path& p) | |
1267 | { | |
1268 | error_code ec; | |
1269 | space_info s = space(p, ec); | |
1270 | if (ec.value()) | |
1271 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec)); | |
1272 | return s; | |
1273 | } | |
1274 | ||
1275 | fs::space_info | |
1276 | fs::space(const path& p, error_code& ec) noexcept | |
1277 | { | |
1278 | space_info info = { | |
1279 | static_cast<uintmax_t>(-1), | |
1280 | static_cast<uintmax_t>(-1), | |
1281 | static_cast<uintmax_t>(-1) | |
1282 | }; | |
1283 | #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H | |
1284 | struct ::statvfs f; | |
a0c4531c JW |
1285 | if (::statvfs(p.c_str(), &f)) |
1286 | ec.assign(errno, std::generic_category()); | |
0ca7ba9a JW |
1287 | else |
1288 | { | |
1289 | info = space_info{ | |
1290 | f.f_blocks * f.f_frsize, | |
1291 | f.f_bfree * f.f_frsize, | |
1292 | f.f_bavail * f.f_frsize | |
1293 | }; | |
1294 | ec.clear(); | |
1295 | } | |
bf53e6a9 JW |
1296 | #else |
1297 | ec = std::make_error_code(std::errc::not_supported); | |
0ca7ba9a JW |
1298 | #endif |
1299 | return info; | |
1300 | } | |
1301 | ||
1302 | #ifdef _GLIBCXX_HAVE_SYS_STAT_H | |
1303 | fs::file_status | |
2be92127 | 1304 | fs::status(const fs::path& p, error_code& ec) noexcept |
0ca7ba9a JW |
1305 | { |
1306 | file_status status; | |
9caf7b27 | 1307 | stat_type st; |
0ca7ba9a JW |
1308 | if (::stat(p.c_str(), &st)) |
1309 | { | |
1310 | int err = errno; | |
1311 | ec.assign(err, std::generic_category()); | |
1312 | if (is_not_found_errno(err)) | |
9caf7b27 | 1313 | status.type(file_type::not_found); |
2be92127 JW |
1314 | #ifdef EOVERFLOW |
1315 | else if (err == EOVERFLOW) | |
1316 | status.type(file_type::unknown); | |
1317 | #endif | |
0ca7ba9a JW |
1318 | } |
1319 | else | |
1320 | { | |
1321 | status = make_file_status(st); | |
1322 | ec.clear(); | |
1323 | } | |
1324 | return status; | |
1325 | } | |
1326 | ||
1327 | fs::file_status | |
1328 | fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept | |
1329 | { | |
1330 | file_status status; | |
9caf7b27 | 1331 | stat_type st; |
0ca7ba9a JW |
1332 | if (::lstat(p.c_str(), &st)) |
1333 | { | |
1334 | int err = errno; | |
1335 | ec.assign(err, std::generic_category()); | |
1336 | if (is_not_found_errno(err)) | |
9caf7b27 | 1337 | status.type(file_type::not_found); |
0ca7ba9a JW |
1338 | } |
1339 | else | |
1340 | { | |
1341 | status = make_file_status(st); | |
1342 | ec.clear(); | |
1343 | } | |
1344 | return status; | |
1345 | } | |
1346 | #endif | |
1347 | ||
1348 | fs::file_status | |
1349 | fs::status(const fs::path& p) | |
1350 | { | |
1351 | std::error_code ec; | |
9caf7b27 JW |
1352 | auto result = status(p, ec); |
1353 | if (result.type() == file_type::none) | |
0ca7ba9a | 1354 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec)); |
9caf7b27 | 1355 | return result; |
0ca7ba9a JW |
1356 | } |
1357 | ||
1358 | fs::file_status | |
1359 | fs::symlink_status(const fs::path& p) | |
1360 | { | |
1361 | std::error_code ec; | |
9caf7b27 JW |
1362 | auto result = symlink_status(p, ec); |
1363 | if (result.type() == file_type::none) | |
1364 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec)); | |
1365 | return result; | |
0ca7ba9a JW |
1366 | } |
1367 | ||
1368 | fs::path | |
1369 | fs::system_complete(const path& p) | |
1370 | { | |
1371 | error_code ec; | |
1372 | path comp = system_complete(p, ec); | |
1373 | if (ec.value()) | |
1374 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("system_complete", p, ec)); | |
1375 | return comp; | |
1376 | } | |
1377 | ||
1378 | fs::path | |
1379 | fs::system_complete(const path& p, error_code& ec) | |
1380 | { | |
1381 | path base = current_path(ec); | |
1382 | #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS | |
1383 | if (p.is_absolute() || !p.has_root_name() | |
1384 | || p.root_name() == base.root_name()) | |
1385 | return absolute(p, base); | |
1386 | // else TODO | |
1387 | ec = std::make_error_code(std::errc::not_supported); | |
1388 | return {}; | |
1389 | #else | |
1390 | if (ec.value()) | |
1391 | return {}; | |
1392 | return absolute(p, base); | |
1393 | #endif | |
1394 | } | |
1395 | ||
1396 | fs::path fs::temp_directory_path() | |
1397 | { | |
1398 | error_code ec; | |
1399 | path tmp = temp_directory_path(ec); | |
1400 | if (ec.value()) | |
1401 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec)); | |
1402 | return tmp; | |
1403 | } | |
1404 | ||
1405 | fs::path fs::temp_directory_path(error_code& ec) | |
1406 | { | |
1407 | #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS | |
bf53e6a9 | 1408 | ec = std::make_error_code(std::errc::not_supported); |
0ca7ba9a JW |
1409 | return {}; // TODO |
1410 | #else | |
9caf7b27 JW |
1411 | const char* tmpdir = nullptr; |
1412 | const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr }; | |
1413 | for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e) | |
1414 | tmpdir = ::getenv(*e); | |
1415 | path p = tmpdir ? tmpdir : "/tmp"; | |
1416 | if (exists(p) && is_directory(p)) | |
1417 | { | |
1418 | ec.clear(); | |
1419 | return p; | |
1420 | } | |
1421 | ec = std::make_error_code(std::errc::not_a_directory); | |
1422 | return {}; | |
0ca7ba9a JW |
1423 | #endif |
1424 | } | |
1425 |