]>
Commit | Line | Data |
---|---|---|
3b90ed62 | 1 | // Filesystem operations -*- C++ -*- |
2 | ||
fbd26352 | 3 | // Copyright (C) 2014-2019 Free Software Foundation, Inc. |
3b90ed62 | 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 | ||
25 | #ifndef _GLIBCXX_USE_CXX11_ABI | |
26 | # define _GLIBCXX_USE_CXX11_ABI 1 | |
730671b5 | 27 | # define NEED_DO_COPY_FILE |
2fd48392 | 28 | # define NEED_DO_SPACE |
3b90ed62 | 29 | #endif |
30 | ||
31 | #include <filesystem> | |
32 | #include <experimental/filesystem> | |
33 | #include <functional> | |
34 | #include <ostream> | |
35 | #include <stack> | |
36 | #include <ext/stdio_filebuf.h> | |
37 | #include <stdlib.h> | |
38 | #include <stdio.h> | |
39 | #include <errno.h> | |
40 | #include <limits.h> // PATH_MAX | |
41 | #ifdef _GLIBCXX_HAVE_FCNTL_H | |
42 | # include <fcntl.h> // AT_FDCWD, AT_SYMLINK_NOFOLLOW | |
43 | #endif | |
44 | #ifdef _GLIBCXX_HAVE_SYS_STAT_H | |
45 | # include <sys/stat.h> // stat, utimensat, fchmodat | |
46 | #endif | |
47 | #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H | |
48 | # include <sys/statvfs.h> // statvfs | |
49 | #endif | |
50 | #ifdef _GLIBCXX_USE_SENDFILE | |
51 | # include <sys/sendfile.h> // sendfile | |
52 | #endif | |
53 | #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H | |
54 | # include <utime.h> // utime | |
55 | #endif | |
2fd48392 | 56 | #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS |
57 | # include <windows.h> | |
58 | #endif | |
3b90ed62 | 59 | |
60 | #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem { | |
61 | #define _GLIBCXX_END_NAMESPACE_FILESYSTEM } | |
62 | #include "ops-common.h" | |
63 | ||
3b90ed62 | 64 | namespace fs = std::filesystem; |
2fd48392 | 65 | namespace posix = std::filesystem::__gnu_posix; |
3b90ed62 | 66 | |
67 | fs::path | |
68 | fs::absolute(const path& p) | |
69 | { | |
70 | #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS | |
71 | error_code ec; | |
72 | path ret = absolute(p, ec); | |
73 | if (ec) | |
74 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make absolute path", p, | |
2fd48392 | 75 | ec)); |
3b90ed62 | 76 | return ret; |
77 | #else | |
78 | return current_path() / p; | |
79 | #endif | |
80 | } | |
81 | ||
82 | fs::path | |
83 | fs::absolute(const path& p, error_code& ec) | |
84 | { | |
b76cdbc2 | 85 | path ret; |
86 | if (p.empty()) | |
87 | { | |
88 | ec = make_error_code(std::errc::no_such_file_or_directory); | |
89 | return ret; | |
90 | } | |
3b90ed62 | 91 | #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS |
2fd48392 | 92 | const wstring& s = p.native(); |
93 | uint32_t len = 1024; | |
94 | wstring buf; | |
95 | do | |
96 | { | |
97 | buf.resize(len); | |
98 | len = GetFullPathNameW(s.c_str(), len, buf.data(), nullptr); | |
99 | } | |
100 | while (len > buf.size()); | |
101 | ||
102 | if (len == 0) | |
103 | ec.assign((int)GetLastError(), std::system_category()); | |
104 | else | |
105 | { | |
106 | ec.clear(); | |
107 | buf.resize(len); | |
108 | ret = std::move(buf); | |
109 | } | |
3b90ed62 | 110 | #else |
111 | ec.clear(); | |
b76cdbc2 | 112 | ret = current_path(); |
113 | ret /= p; | |
3b90ed62 | 114 | #endif |
b76cdbc2 | 115 | return ret; |
3b90ed62 | 116 | } |
117 | ||
118 | namespace | |
119 | { | |
120 | #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS | |
121 | inline bool is_dot(wchar_t c) { return c == L'.'; } | |
122 | #else | |
123 | inline bool is_dot(char c) { return c == '.'; } | |
124 | #endif | |
125 | ||
126 | inline bool is_dot(const fs::path& path) | |
127 | { | |
128 | const auto& filename = path.native(); | |
129 | return filename.size() == 1 && is_dot(filename[0]); | |
130 | } | |
131 | ||
132 | inline bool is_dotdot(const fs::path& path) | |
133 | { | |
134 | const auto& filename = path.native(); | |
135 | return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]); | |
136 | } | |
137 | ||
138 | struct free_as_in_malloc | |
139 | { | |
140 | void operator()(void* p) const { ::free(p); } | |
141 | }; | |
142 | ||
2fd48392 | 143 | using char_ptr = std::unique_ptr<fs::path::value_type[], free_as_in_malloc>; |
3b90ed62 | 144 | } |
145 | ||
146 | fs::path | |
147 | fs::canonical(const path& p, error_code& ec) | |
148 | { | |
149 | path result; | |
150 | const path pa = absolute(p, ec); | |
151 | if (ec) | |
152 | return result; | |
153 | ||
154 | #ifdef _GLIBCXX_USE_REALPATH | |
155 | char_ptr buf{ nullptr }; | |
156 | # if _XOPEN_VERSION < 700 | |
157 | // Not safe to call realpath(path, NULL) | |
2fd48392 | 158 | using char_type = fs::path::value_type; |
159 | buf.reset( (char_type*)::malloc(PATH_MAX * sizeof(char_type)) ); | |
3b90ed62 | 160 | # endif |
161 | if (char* rp = ::realpath(pa.c_str(), buf.get())) | |
162 | { | |
163 | if (buf == nullptr) | |
164 | buf.reset(rp); | |
165 | result.assign(rp); | |
166 | ec.clear(); | |
167 | return result; | |
168 | } | |
169 | if (errno != ENAMETOOLONG) | |
170 | { | |
171 | ec.assign(errno, std::generic_category()); | |
172 | return result; | |
173 | } | |
174 | #endif | |
175 | ||
176 | if (!exists(pa, ec)) | |
177 | { | |
178 | if (!ec) | |
179 | ec = make_error_code(std::errc::no_such_file_or_directory); | |
180 | return result; | |
181 | } | |
182 | // else: we know there are (currently) no unresolvable symlink loops | |
183 | ||
184 | result = pa.root_path(); | |
185 | ||
186 | deque<path> cmpts; | |
187 | for (auto& f : pa.relative_path()) | |
188 | cmpts.push_back(f); | |
189 | ||
190 | int max_allowed_symlinks = 40; | |
191 | ||
192 | while (!cmpts.empty() && !ec) | |
193 | { | |
194 | path f = std::move(cmpts.front()); | |
195 | cmpts.pop_front(); | |
196 | ||
197 | if (f.empty()) | |
198 | { | |
199 | // ignore empty element | |
200 | } | |
201 | else if (is_dot(f)) | |
202 | { | |
203 | if (!is_directory(result, ec) && !ec) | |
204 | ec.assign(ENOTDIR, std::generic_category()); | |
205 | } | |
206 | else if (is_dotdot(f)) | |
207 | { | |
208 | auto parent = result.parent_path(); | |
209 | if (parent.empty()) | |
210 | result = pa.root_path(); | |
211 | else | |
212 | result.swap(parent); | |
213 | } | |
214 | else | |
215 | { | |
216 | result /= f; | |
217 | ||
218 | if (is_symlink(result, ec)) | |
219 | { | |
220 | path link = read_symlink(result, ec); | |
221 | if (!ec) | |
222 | { | |
223 | if (--max_allowed_symlinks == 0) | |
224 | ec.assign(ELOOP, std::generic_category()); | |
225 | else | |
226 | { | |
227 | if (link.is_absolute()) | |
228 | { | |
229 | result = link.root_path(); | |
230 | link = link.relative_path(); | |
231 | } | |
232 | else | |
233 | result = result.parent_path(); | |
234 | ||
235 | cmpts.insert(cmpts.begin(), link.begin(), link.end()); | |
236 | } | |
237 | } | |
238 | } | |
239 | } | |
240 | } | |
241 | ||
242 | if (ec || !exists(result, ec)) | |
243 | result.clear(); | |
244 | ||
245 | return result; | |
246 | } | |
247 | ||
248 | fs::path | |
249 | fs::canonical(const path& p) | |
250 | { | |
251 | error_code ec; | |
252 | path res = canonical(p, ec); | |
253 | if (ec) | |
254 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make canonical path", | |
255 | p, ec)); | |
256 | return res; | |
257 | } | |
258 | ||
259 | void | |
260 | fs::copy(const path& from, const path& to, copy_options options) | |
261 | { | |
262 | error_code ec; | |
263 | copy(from, to, options, ec); | |
264 | if (ec) | |
265 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy", from, to, ec)); | |
266 | } | |
267 | ||
268 | namespace std::filesystem | |
269 | { | |
270 | // Need this as there's no 'perm_options::none' enumerator. | |
271 | inline bool is_set(fs::perm_options obj, fs::perm_options bits) | |
272 | { | |
273 | return (obj & bits) != fs::perm_options{}; | |
274 | } | |
275 | } | |
276 | ||
277 | #ifdef _GLIBCXX_HAVE_SYS_STAT_H | |
730671b5 | 278 | #ifdef NEED_DO_COPY_FILE |
3b90ed62 | 279 | bool |
2fd48392 | 280 | fs::do_copy_file(const path::value_type* from, const path::value_type* to, |
3b90ed62 | 281 | copy_options_existing_file options, |
282 | stat_type* from_st, stat_type* to_st, | |
283 | std::error_code& ec) noexcept | |
284 | { | |
285 | stat_type st1, st2; | |
286 | fs::file_status t, f; | |
287 | ||
288 | if (to_st == nullptr) | |
289 | { | |
2fd48392 | 290 | if (posix::stat(to, &st1)) |
3b90ed62 | 291 | { |
292 | const int err = errno; | |
293 | if (!is_not_found_errno(err)) | |
294 | { | |
295 | ec.assign(err, std::generic_category()); | |
296 | return false; | |
297 | } | |
298 | } | |
299 | else | |
300 | to_st = &st1; | |
301 | } | |
302 | else if (to_st == from_st) | |
303 | to_st = nullptr; | |
304 | ||
305 | if (to_st == nullptr) | |
306 | t = fs::file_status{fs::file_type::not_found}; | |
307 | else | |
308 | t = make_file_status(*to_st); | |
309 | ||
310 | if (from_st == nullptr) | |
311 | { | |
2fd48392 | 312 | if (posix::stat(from, &st2)) |
3b90ed62 | 313 | { |
314 | ec.assign(errno, std::generic_category()); | |
315 | return false; | |
316 | } | |
317 | else | |
318 | from_st = &st2; | |
319 | } | |
320 | f = make_file_status(*from_st); | |
321 | // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
322 | // 2712. copy_file() has a number of unspecified error conditions | |
323 | if (!is_regular_file(f)) | |
324 | { | |
325 | ec = std::make_error_code(std::errc::not_supported); | |
326 | return false; | |
327 | } | |
328 | ||
329 | if (exists(t)) | |
330 | { | |
331 | if (!is_regular_file(t)) | |
332 | { | |
333 | ec = std::make_error_code(std::errc::not_supported); | |
334 | return false; | |
335 | } | |
336 | ||
337 | if (to_st->st_dev == from_st->st_dev | |
338 | && to_st->st_ino == from_st->st_ino) | |
339 | { | |
340 | ec = std::make_error_code(std::errc::file_exists); | |
341 | return false; | |
342 | } | |
343 | ||
344 | if (options.skip) | |
345 | { | |
346 | ec.clear(); | |
347 | return false; | |
348 | } | |
349 | else if (options.update) | |
350 | { | |
351 | const auto from_mtime = file_time(*from_st, ec); | |
352 | if (ec) | |
353 | return false; | |
354 | if ((from_mtime <= file_time(*to_st, ec)) || ec) | |
355 | return false; | |
356 | } | |
357 | else if (!options.overwrite) | |
358 | { | |
359 | ec = std::make_error_code(std::errc::file_exists); | |
360 | return false; | |
361 | } | |
362 | else if (!is_regular_file(t)) | |
363 | { | |
364 | ec = std::make_error_code(std::errc::not_supported); | |
365 | return false; | |
366 | } | |
367 | } | |
368 | ||
369 | struct CloseFD { | |
2fd48392 | 370 | ~CloseFD() { if (fd != -1) posix::close(fd); } |
371 | bool close() { return posix::close(std::exchange(fd, -1)) == 0; } | |
3b90ed62 | 372 | int fd; |
373 | }; | |
374 | ||
2fd48392 | 375 | CloseFD in = { posix::open(from, O_RDONLY) }; |
3b90ed62 | 376 | if (in.fd == -1) |
377 | { | |
378 | ec.assign(errno, std::generic_category()); | |
379 | return false; | |
380 | } | |
381 | int oflag = O_WRONLY|O_CREAT; | |
382 | if (options.overwrite || options.update) | |
383 | oflag |= O_TRUNC; | |
384 | else | |
385 | oflag |= O_EXCL; | |
2fd48392 | 386 | CloseFD out = { posix::open(to, oflag, S_IWUSR) }; |
3b90ed62 | 387 | if (out.fd == -1) |
388 | { | |
389 | if (errno == EEXIST && options.skip) | |
390 | ec.clear(); | |
391 | else | |
392 | ec.assign(errno, std::generic_category()); | |
393 | return false; | |
394 | } | |
395 | ||
2fd48392 | 396 | #if defined _GLIBCXX_USE_FCHMOD && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS |
3b90ed62 | 397 | if (::fchmod(out.fd, from_st->st_mode)) |
2fd48392 | 398 | #elif defined _GLIBCXX_USE_FCHMODAT && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS |
3b90ed62 | 399 | if (::fchmodat(AT_FDCWD, to, from_st->st_mode, 0)) |
400 | #else | |
2fd48392 | 401 | if (posix::chmod(to, from_st->st_mode)) |
3b90ed62 | 402 | #endif |
403 | { | |
404 | ec.assign(errno, std::generic_category()); | |
405 | return false; | |
406 | } | |
407 | ||
6b74e46f | 408 | size_t count = from_st->st_size; |
2fd48392 | 409 | #if defined _GLIBCXX_USE_SENDFILE && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS |
53a3eac0 | 410 | off_t offset = 0; |
411 | ssize_t n = ::sendfile(out.fd, in.fd, &offset, count); | |
6b74e46f | 412 | if (n < 0 && errno != ENOSYS && errno != EINVAL) |
3b90ed62 | 413 | { |
6b74e46f | 414 | ec.assign(errno, std::generic_category()); |
415 | return false; | |
416 | } | |
417 | if ((size_t)n == count) | |
418 | { | |
419 | if (!out.close() || !in.close()) | |
420 | { | |
421 | ec.assign(errno, std::generic_category()); | |
422 | return false; | |
423 | } | |
424 | ec.clear(); | |
425 | return true; | |
426 | } | |
427 | else if (n > 0) | |
428 | count -= n; | |
3b90ed62 | 429 | #endif // _GLIBCXX_USE_SENDFILE |
6b74e46f | 430 | |
53a3eac0 | 431 | using std::ios; |
432 | __gnu_cxx::stdio_filebuf<char> sbin(in.fd, ios::in|ios::binary); | |
433 | __gnu_cxx::stdio_filebuf<char> sbout(out.fd, ios::out|ios::binary); | |
6b74e46f | 434 | |
435 | if (sbin.is_open()) | |
436 | in.fd = -1; | |
437 | if (sbout.is_open()) | |
438 | out.fd = -1; | |
439 | ||
53a3eac0 | 440 | #ifdef _GLIBCXX_USE_SENDFILE |
441 | if (n != 0) | |
6b74e46f | 442 | { |
53a3eac0 | 443 | if (n < 0) |
444 | n = 0; | |
445 | ||
446 | const auto p1 = sbin.pubseekoff(n, ios::beg, ios::in); | |
447 | const auto p2 = sbout.pubseekoff(n, ios::beg, ios::out); | |
448 | ||
449 | const std::streampos errpos(std::streamoff(-1)); | |
6b74e46f | 450 | if (p1 == errpos || p2 == errpos) |
3b90ed62 | 451 | { |
452 | ec = std::make_error_code(std::errc::io_error); | |
453 | return false; | |
454 | } | |
6b74e46f | 455 | } |
53a3eac0 | 456 | #endif |
6b74e46f | 457 | |
458 | if (count && !(std::ostream(&sbout) << &sbin)) | |
3b90ed62 | 459 | { |
6b74e46f | 460 | ec = std::make_error_code(std::errc::io_error); |
3b90ed62 | 461 | return false; |
462 | } | |
6b74e46f | 463 | if (!sbout.close() || !sbin.close()) |
3b90ed62 | 464 | { |
465 | ec.assign(errno, std::generic_category()); | |
466 | return false; | |
467 | } | |
3b90ed62 | 468 | ec.clear(); |
469 | return true; | |
3b90ed62 | 470 | } |
730671b5 | 471 | #endif // NEED_DO_COPY_FILE |
3b90ed62 | 472 | #endif // _GLIBCXX_HAVE_SYS_STAT_H |
473 | ||
474 | void | |
475 | fs::copy(const path& from, const path& to, copy_options options, | |
96040a52 | 476 | error_code& ec) |
3b90ed62 | 477 | { |
478 | const bool skip_symlinks = is_set(options, copy_options::skip_symlinks); | |
479 | const bool create_symlinks = is_set(options, copy_options::create_symlinks); | |
480 | const bool copy_symlinks = is_set(options, copy_options::copy_symlinks); | |
481 | const bool use_lstat = create_symlinks || skip_symlinks; | |
482 | ||
483 | file_status f, t; | |
484 | stat_type from_st, to_st; | |
485 | // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
486 | // 2681. filesystem::copy() cannot copy symlinks | |
487 | if (use_lstat || copy_symlinks | |
2fd48392 | 488 | ? posix::lstat(from.c_str(), &from_st) |
489 | : posix::stat(from.c_str(), &from_st)) | |
3b90ed62 | 490 | { |
491 | ec.assign(errno, std::generic_category()); | |
492 | return; | |
493 | } | |
494 | if (use_lstat | |
2fd48392 | 495 | ? posix::lstat(to.c_str(), &to_st) |
496 | : posix::stat(to.c_str(), &to_st)) | |
3b90ed62 | 497 | { |
498 | if (!is_not_found_errno(errno)) | |
499 | { | |
500 | ec.assign(errno, std::generic_category()); | |
501 | return; | |
502 | } | |
503 | t = file_status{file_type::not_found}; | |
504 | } | |
505 | else | |
506 | t = make_file_status(to_st); | |
507 | f = make_file_status(from_st); | |
508 | ||
509 | if (exists(t) && !is_other(t) && !is_other(f) | |
510 | && to_st.st_dev == from_st.st_dev && to_st.st_ino == from_st.st_ino) | |
511 | { | |
512 | ec = std::make_error_code(std::errc::file_exists); | |
513 | return; | |
514 | } | |
515 | if (is_other(f) || is_other(t)) | |
516 | { | |
517 | ec = std::make_error_code(std::errc::not_supported); | |
518 | return; | |
519 | } | |
520 | if (is_directory(f) && is_regular_file(t)) | |
521 | { | |
522 | ec = std::make_error_code(std::errc::is_a_directory); | |
523 | return; | |
524 | } | |
525 | ||
526 | if (is_symlink(f)) | |
527 | { | |
528 | if (skip_symlinks) | |
529 | ec.clear(); | |
530 | else if (!exists(t) && copy_symlinks) | |
531 | copy_symlink(from, to, ec); | |
532 | else | |
533 | // Not clear what should be done here. | |
534 | // "Otherwise report an error as specified in Error reporting (7)." | |
535 | ec = std::make_error_code(std::errc::invalid_argument); | |
536 | } | |
537 | else if (is_regular_file(f)) | |
538 | { | |
539 | if (is_set(options, copy_options::directories_only)) | |
540 | ec.clear(); | |
541 | else if (create_symlinks) | |
542 | create_symlink(from, to, ec); | |
543 | else if (is_set(options, copy_options::create_hard_links)) | |
544 | create_hard_link(from, to, ec); | |
545 | else if (is_directory(t)) | |
546 | do_copy_file(from.c_str(), (to / from.filename()).c_str(), | |
547 | copy_file_options(options), &from_st, nullptr, ec); | |
548 | else | |
549 | { | |
550 | auto ptr = exists(t) ? &to_st : &from_st; | |
551 | do_copy_file(from.c_str(), to.c_str(), copy_file_options(options), | |
552 | &from_st, ptr, ec); | |
553 | } | |
554 | } | |
555 | // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
556 | // 2682. filesystem::copy() won't create a symlink to a directory | |
557 | else if (is_directory(f) && create_symlinks) | |
558 | ec = std::make_error_code(errc::is_a_directory); | |
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 | // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
572 | // 2683. filesystem::copy() says "no effects" | |
573 | else | |
574 | ec.clear(); | |
575 | } | |
576 | ||
577 | bool | |
578 | fs::copy_file(const path& from, const path& to, copy_options option) | |
579 | { | |
580 | error_code ec; | |
581 | bool result = copy_file(from, to, option, ec); | |
582 | if (ec) | |
583 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to, | |
584 | ec)); | |
585 | return result; | |
586 | } | |
587 | ||
588 | bool | |
589 | fs::copy_file(const path& from, const path& to, copy_options options, | |
96040a52 | 590 | error_code& ec) |
3b90ed62 | 591 | { |
592 | #ifdef _GLIBCXX_HAVE_SYS_STAT_H | |
593 | return do_copy_file(from.c_str(), to.c_str(), copy_file_options(options), | |
594 | nullptr, nullptr, ec); | |
595 | #else | |
596 | ec = std::make_error_code(std::errc::not_supported); | |
597 | return false; | |
598 | #endif | |
599 | } | |
600 | ||
601 | ||
602 | void | |
603 | fs::copy_symlink(const path& existing_symlink, const path& new_symlink) | |
604 | { | |
605 | error_code ec; | |
606 | copy_symlink(existing_symlink, new_symlink, ec); | |
607 | if (ec) | |
608 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink", | |
609 | existing_symlink, new_symlink, ec)); | |
610 | } | |
611 | ||
612 | void | |
613 | fs::copy_symlink(const path& existing_symlink, const path& new_symlink, | |
614 | error_code& ec) noexcept | |
615 | { | |
616 | auto p = read_symlink(existing_symlink, ec); | |
617 | if (ec) | |
618 | return; | |
619 | #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS | |
620 | if (is_directory(p)) | |
621 | { | |
622 | create_directory_symlink(p, new_symlink, ec); | |
623 | return; | |
624 | } | |
625 | #endif | |
626 | create_symlink(p, new_symlink, ec); | |
627 | } | |
628 | ||
629 | ||
630 | bool | |
631 | fs::create_directories(const path& p) | |
632 | { | |
633 | error_code ec; | |
634 | bool result = create_directories(p, ec); | |
635 | if (ec) | |
636 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p, | |
637 | ec)); | |
638 | return result; | |
639 | } | |
640 | ||
641 | bool | |
96040a52 | 642 | fs::create_directories(const path& p, error_code& ec) |
3b90ed62 | 643 | { |
644 | if (p.empty()) | |
645 | { | |
646 | ec = std::make_error_code(errc::invalid_argument); | |
647 | return false; | |
648 | } | |
5424f0c7 | 649 | |
650 | file_status st = symlink_status(p, ec); | |
651 | if (is_directory(st)) | |
652 | return false; | |
653 | else if (ec && !status_known(st)) | |
654 | return false; | |
655 | else if (exists(st)) | |
656 | { | |
657 | if (!ec) | |
658 | ec = std::make_error_code(std::errc::not_a_directory); | |
659 | return false; | |
660 | } | |
661 | ||
3b90ed62 | 662 | std::stack<path> missing; |
663 | path pp = p; | |
664 | ||
5424f0c7 | 665 | // Strip any trailing slash |
666 | if (pp.has_relative_path() && !pp.has_filename()) | |
667 | pp = pp.parent_path(); | |
668 | ||
669 | do | |
3b90ed62 | 670 | { |
3b90ed62 | 671 | const auto& filename = pp.filename(); |
5424f0c7 | 672 | if (is_dot(filename) || is_dotdot(filename)) |
673 | pp = pp.parent_path(); | |
674 | else | |
675 | { | |
676 | missing.push(std::move(pp)); | |
677 | if (missing.size() > 1000) // sanity check | |
678 | { | |
679 | ec = std::make_error_code(std::errc::filename_too_long); | |
680 | return false; | |
681 | } | |
682 | pp = missing.top().parent_path(); | |
683 | } | |
684 | ||
685 | if (pp.empty()) | |
686 | break; | |
3b90ed62 | 687 | |
5424f0c7 | 688 | st = status(pp, ec); |
689 | if (exists(st)) | |
3b90ed62 | 690 | { |
5424f0c7 | 691 | if (ec) |
692 | return false; | |
693 | if (!is_directory(st)) | |
694 | { | |
695 | ec = std::make_error_code(std::errc::not_a_directory); | |
696 | return false; | |
697 | } | |
3b90ed62 | 698 | } |
3b90ed62 | 699 | |
5424f0c7 | 700 | if (ec && exists(st)) |
701 | return false; | |
702 | } | |
703 | while (st.type() == file_type::not_found); | |
3b90ed62 | 704 | |
5424f0c7 | 705 | bool created; |
3b90ed62 | 706 | do |
707 | { | |
708 | const path& top = missing.top(); | |
5424f0c7 | 709 | created = create_directory(top, ec); |
710 | if (ec) | |
711 | return false; | |
3b90ed62 | 712 | missing.pop(); |
713 | } | |
5424f0c7 | 714 | while (!missing.empty()); |
3b90ed62 | 715 | |
5424f0c7 | 716 | return created; |
3b90ed62 | 717 | } |
718 | ||
719 | namespace | |
720 | { | |
721 | bool | |
722 | create_dir(const fs::path& p, fs::perms perm, std::error_code& ec) | |
723 | { | |
724 | bool created = false; | |
725 | #ifdef _GLIBCXX_HAVE_SYS_STAT_H | |
2fd48392 | 726 | posix::mode_t mode |
727 | = static_cast<std::underlying_type_t<fs::perms>>(perm); | |
728 | if (posix::mkdir(p.c_str(), mode)) | |
3b90ed62 | 729 | { |
730 | const int err = errno; | |
9f03f408 | 731 | if (err != EEXIST || !is_directory(p, ec)) |
3b90ed62 | 732 | ec.assign(err, std::generic_category()); |
3b90ed62 | 733 | } |
734 | else | |
735 | { | |
736 | ec.clear(); | |
737 | created = true; | |
738 | } | |
739 | #else | |
740 | ec = std::make_error_code(std::errc::not_supported); | |
741 | #endif | |
742 | return created; | |
743 | } | |
744 | } // namespace | |
745 | ||
746 | bool | |
747 | fs::create_directory(const path& p) | |
748 | { | |
749 | error_code ec; | |
750 | bool result = create_directory(p, ec); | |
751 | if (ec) | |
752 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p, | |
753 | ec)); | |
754 | return result; | |
755 | } | |
756 | ||
757 | bool | |
758 | fs::create_directory(const path& p, error_code& ec) noexcept | |
759 | { | |
760 | return create_dir(p, perms::all, ec); | |
761 | } | |
762 | ||
763 | ||
764 | bool | |
765 | fs::create_directory(const path& p, const path& attributes) | |
766 | { | |
767 | error_code ec; | |
768 | bool result = create_directory(p, attributes, ec); | |
769 | if (ec) | |
770 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p, | |
771 | ec)); | |
772 | return result; | |
773 | } | |
774 | ||
775 | bool | |
776 | fs::create_directory(const path& p, const path& attributes, | |
777 | error_code& ec) noexcept | |
778 | { | |
779 | #ifdef _GLIBCXX_HAVE_SYS_STAT_H | |
780 | stat_type st; | |
2fd48392 | 781 | if (posix::stat(attributes.c_str(), &st)) |
3b90ed62 | 782 | { |
783 | ec.assign(errno, std::generic_category()); | |
784 | return false; | |
785 | } | |
786 | return create_dir(p, static_cast<perms>(st.st_mode), ec); | |
787 | #else | |
788 | ec = std::make_error_code(std::errc::not_supported); | |
789 | return false; | |
790 | #endif | |
791 | } | |
792 | ||
793 | ||
794 | void | |
795 | fs::create_directory_symlink(const path& to, const path& new_symlink) | |
796 | { | |
797 | error_code ec; | |
798 | create_directory_symlink(to, new_symlink, ec); | |
799 | if (ec) | |
800 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory symlink", | |
801 | to, new_symlink, ec)); | |
802 | } | |
803 | ||
804 | void | |
805 | fs::create_directory_symlink(const path& to, const path& new_symlink, | |
806 | error_code& ec) noexcept | |
807 | { | |
808 | #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS | |
809 | ec = std::make_error_code(std::errc::not_supported); | |
810 | #else | |
811 | create_symlink(to, new_symlink, ec); | |
812 | #endif | |
813 | } | |
814 | ||
815 | ||
816 | void | |
817 | fs::create_hard_link(const path& to, const path& new_hard_link) | |
818 | { | |
819 | error_code ec; | |
820 | create_hard_link(to, new_hard_link, ec); | |
821 | if (ec) | |
822 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link", | |
2fd48392 | 823 | to, new_hard_link, ec)); |
3b90ed62 | 824 | } |
825 | ||
826 | void | |
827 | fs::create_hard_link(const path& to, const path& new_hard_link, | |
828 | error_code& ec) noexcept | |
829 | { | |
2fd48392 | 830 | #ifdef _GLIBCXX_HAVE_LINK |
3b90ed62 | 831 | if (::link(to.c_str(), new_hard_link.c_str())) |
832 | ec.assign(errno, std::generic_category()); | |
833 | else | |
834 | ec.clear(); | |
2fd48392 | 835 | #elif defined _GLIBCXX_FILESYSTEM_IS_WINDOWS |
836 | if (CreateHardLinkW(new_hard_link.c_str(), to.c_str(), NULL)) | |
837 | ec.clear(); | |
838 | else | |
839 | ec.assign((int)GetLastError(), generic_category()); | |
3b90ed62 | 840 | #else |
841 | ec = std::make_error_code(std::errc::not_supported); | |
842 | #endif | |
843 | } | |
844 | ||
845 | void | |
846 | fs::create_symlink(const path& to, const path& new_symlink) | |
847 | { | |
848 | error_code ec; | |
849 | create_symlink(to, new_symlink, ec); | |
850 | if (ec) | |
851 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink", | |
852 | to, new_symlink, ec)); | |
853 | } | |
854 | ||
855 | void | |
856 | fs::create_symlink(const path& to, const path& new_symlink, | |
857 | error_code& ec) noexcept | |
858 | { | |
2fd48392 | 859 | #ifdef _GLIBCXX_HAVE_SYMLINK |
3b90ed62 | 860 | if (::symlink(to.c_str(), new_symlink.c_str())) |
861 | ec.assign(errno, std::generic_category()); | |
862 | else | |
863 | ec.clear(); | |
864 | #else | |
865 | ec = std::make_error_code(std::errc::not_supported); | |
866 | #endif | |
867 | } | |
868 | ||
869 | ||
870 | fs::path | |
871 | fs::current_path() | |
872 | { | |
873 | error_code ec; | |
874 | path p = current_path(ec); | |
875 | if (ec) | |
876 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec)); | |
877 | return p; | |
878 | } | |
879 | ||
880 | fs::path | |
881 | fs::current_path(error_code& ec) | |
882 | { | |
883 | path p; | |
884 | #ifdef _GLIBCXX_HAVE_UNISTD_H | |
2fd48392 | 885 | #if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS |
886 | if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)}) | |
3b90ed62 | 887 | { |
888 | p.assign(cwd.get()); | |
889 | ec.clear(); | |
890 | } | |
891 | else | |
892 | ec.assign(errno, std::generic_category()); | |
893 | #else | |
2fd48392 | 894 | #ifdef _PC_PATH_MAX |
3b90ed62 | 895 | long path_max = pathconf(".", _PC_PATH_MAX); |
896 | size_t size; | |
897 | if (path_max == -1) | |
898 | size = 1024; | |
899 | else if (path_max > 10240) | |
900 | size = 10240; | |
901 | else | |
902 | size = path_max; | |
2fd48392 | 903 | #elif defined(PATH_MAX) |
904 | size_t size = PATH_MAX; | |
905 | #else | |
906 | size_t size = 1024; | |
907 | #endif | |
3b90ed62 | 908 | for (char_ptr buf; p.empty(); size *= 2) |
909 | { | |
2fd48392 | 910 | using char_type = fs::path::value_type; |
911 | buf.reset((char_type*)malloc(size * sizeof(char_type))); | |
3b90ed62 | 912 | if (buf) |
913 | { | |
914 | if (getcwd(buf.get(), size)) | |
915 | { | |
916 | p.assign(buf.get()); | |
917 | ec.clear(); | |
918 | } | |
919 | else if (errno != ERANGE) | |
920 | { | |
921 | ec.assign(errno, std::generic_category()); | |
922 | return {}; | |
923 | } | |
924 | } | |
925 | else | |
926 | { | |
927 | ec = std::make_error_code(std::errc::not_enough_memory); | |
928 | return {}; | |
929 | } | |
930 | } | |
931 | #endif // __GLIBC__ | |
932 | #else // _GLIBCXX_HAVE_UNISTD_H | |
933 | ec = std::make_error_code(std::errc::not_supported); | |
934 | #endif | |
935 | return p; | |
936 | } | |
937 | ||
938 | void | |
939 | fs::current_path(const path& p) | |
940 | { | |
941 | error_code ec; | |
942 | current_path(p, ec); | |
943 | if (ec) | |
944 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec)); | |
945 | } | |
946 | ||
947 | void | |
948 | fs::current_path(const path& p, error_code& ec) noexcept | |
949 | { | |
950 | #ifdef _GLIBCXX_HAVE_UNISTD_H | |
2fd48392 | 951 | if (posix::chdir(p.c_str())) |
3b90ed62 | 952 | ec.assign(errno, std::generic_category()); |
953 | else | |
954 | ec.clear(); | |
955 | #else | |
956 | ec = std::make_error_code(std::errc::not_supported); | |
957 | #endif | |
958 | } | |
959 | ||
960 | bool | |
961 | fs::equivalent(const path& p1, const path& p2) | |
962 | { | |
963 | error_code ec; | |
964 | auto result = equivalent(p1, p2, ec); | |
965 | if (ec) | |
966 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence", | |
967 | p1, p2, ec)); | |
968 | return result; | |
969 | } | |
970 | ||
971 | bool | |
972 | fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept | |
973 | { | |
974 | #ifdef _GLIBCXX_HAVE_SYS_STAT_H | |
975 | int err = 0; | |
976 | file_status s1, s2; | |
977 | stat_type st1, st2; | |
2fd48392 | 978 | if (posix::stat(p1.c_str(), &st1) == 0) |
3b90ed62 | 979 | s1 = make_file_status(st1); |
980 | else if (is_not_found_errno(errno)) | |
981 | s1.type(file_type::not_found); | |
982 | else | |
983 | err = errno; | |
984 | ||
2fd48392 | 985 | if (posix::stat(p2.c_str(), &st2) == 0) |
3b90ed62 | 986 | s2 = make_file_status(st2); |
987 | else if (is_not_found_errno(errno)) | |
988 | s2.type(file_type::not_found); | |
989 | else | |
990 | err = errno; | |
991 | ||
992 | if (exists(s1) && exists(s2)) | |
993 | { | |
994 | if (is_other(s1) && is_other(s2)) | |
995 | { | |
996 | ec = std::make_error_code(std::errc::not_supported); | |
997 | return false; | |
998 | } | |
999 | ec.clear(); | |
1000 | if (is_other(s1) || is_other(s2)) | |
1001 | return false; | |
1002 | return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino; | |
1003 | } | |
1004 | else if (!exists(s1) && !exists(s2)) | |
1005 | ec = std::make_error_code(std::errc::no_such_file_or_directory); | |
1006 | else if (err) | |
1007 | ec.assign(err, std::generic_category()); | |
1008 | else | |
1009 | ec.clear(); | |
1010 | return false; | |
1011 | #else | |
1012 | ec = std::make_error_code(std::errc::not_supported); | |
1013 | #endif | |
1014 | return false; | |
1015 | } | |
1016 | ||
1017 | std::uintmax_t | |
1018 | fs::file_size(const path& p) | |
1019 | { | |
1020 | error_code ec; | |
1021 | auto sz = file_size(p, ec); | |
1022 | if (ec) | |
1023 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec)); | |
1024 | return sz; | |
1025 | } | |
1026 | ||
1027 | namespace | |
1028 | { | |
1029 | template<typename Accessor, typename T> | |
1030 | inline T | |
1031 | do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt) | |
1032 | { | |
1033 | #ifdef _GLIBCXX_HAVE_SYS_STAT_H | |
2fd48392 | 1034 | posix::stat_type st; |
1035 | if (posix::stat(p.c_str(), &st)) | |
3b90ed62 | 1036 | { |
1037 | ec.assign(errno, std::generic_category()); | |
1038 | return deflt; | |
1039 | } | |
1040 | ec.clear(); | |
1041 | return f(st); | |
1042 | #else | |
1043 | ec = std::make_error_code(std::errc::not_supported); | |
1044 | return deflt; | |
1045 | #endif | |
1046 | } | |
1047 | } | |
1048 | ||
1049 | std::uintmax_t | |
1050 | fs::file_size(const path& p, error_code& ec) noexcept | |
1051 | { | |
1052 | struct S | |
1053 | { | |
1054 | S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { } | |
1055 | S() : type(file_type::not_found) { } | |
1056 | file_type type; | |
1057 | size_t size; | |
1058 | }; | |
1059 | auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{}); | |
1060 | if (s.type == file_type::regular) | |
1061 | return s.size; | |
1062 | if (!ec) | |
1063 | { | |
1064 | if (s.type == file_type::directory) | |
1065 | ec = std::make_error_code(std::errc::is_a_directory); | |
1066 | else | |
1067 | ec = std::make_error_code(std::errc::not_supported); | |
1068 | } | |
1069 | return -1; | |
1070 | } | |
1071 | ||
1072 | std::uintmax_t | |
1073 | fs::hard_link_count(const path& p) | |
1074 | { | |
1075 | error_code ec; | |
1076 | auto count = hard_link_count(p, ec); | |
1077 | if (ec) | |
1078 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec)); | |
1079 | return count; | |
1080 | } | |
1081 | ||
1082 | std::uintmax_t | |
1083 | fs::hard_link_count(const path& p, error_code& ec) noexcept | |
1084 | { | |
2fd48392 | 1085 | return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink), |
3b90ed62 | 1086 | static_cast<uintmax_t>(-1)); |
1087 | } | |
1088 | ||
1089 | bool | |
1090 | fs::is_empty(const path& p) | |
1091 | { | |
1092 | error_code ec; | |
1093 | bool e = is_empty(p, ec); | |
1094 | if (ec) | |
1095 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty", | |
1096 | p, ec)); | |
1097 | return e; | |
1098 | } | |
1099 | ||
1100 | bool | |
96040a52 | 1101 | fs::is_empty(const path& p, error_code& ec) |
3b90ed62 | 1102 | { |
1103 | auto s = status(p, ec); | |
1104 | if (ec) | |
1105 | return false; | |
1106 | bool empty = fs::is_directory(s) | |
1107 | ? fs::directory_iterator(p, ec) == fs::directory_iterator() | |
1108 | : fs::file_size(p, ec) == 0; | |
1109 | return ec ? false : empty; | |
1110 | } | |
1111 | ||
1112 | fs::file_time_type | |
1113 | fs::last_write_time(const path& p) | |
1114 | { | |
1115 | error_code ec; | |
1116 | auto t = last_write_time(p, ec); | |
1117 | if (ec) | |
1118 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec)); | |
1119 | return t; | |
1120 | } | |
1121 | ||
1122 | fs::file_time_type | |
1123 | fs::last_write_time(const path& p, error_code& ec) noexcept | |
1124 | { | |
1125 | return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); }, | |
1126 | file_time_type::min()); | |
1127 | } | |
1128 | ||
1129 | void | |
1130 | fs::last_write_time(const path& p, file_time_type new_time) | |
1131 | { | |
1132 | error_code ec; | |
1133 | last_write_time(p, new_time, ec); | |
1134 | if (ec) | |
1135 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec)); | |
1136 | } | |
1137 | ||
1138 | void | |
1139 | fs::last_write_time(const path& p __attribute__((__unused__)), | |
1140 | file_time_type new_time, error_code& ec) noexcept | |
1141 | { | |
1142 | auto d = new_time.time_since_epoch(); | |
1143 | auto s = chrono::duration_cast<chrono::seconds>(d); | |
1144 | #if _GLIBCXX_USE_UTIMENSAT | |
1145 | auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s); | |
1146 | if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9. | |
1147 | { | |
1148 | --s; | |
1149 | ns += chrono::seconds(1); | |
1150 | } | |
1151 | struct ::timespec ts[2]; | |
1152 | ts[0].tv_sec = 0; | |
1153 | ts[0].tv_nsec = UTIME_OMIT; | |
1154 | ts[1].tv_sec = static_cast<std::time_t>(s.count()); | |
1155 | ts[1].tv_nsec = static_cast<long>(ns.count()); | |
1156 | if (::utimensat(AT_FDCWD, p.c_str(), ts, 0)) | |
1157 | ec.assign(errno, std::generic_category()); | |
1158 | else | |
1159 | ec.clear(); | |
1160 | #elif _GLIBCXX_HAVE_UTIME_H | |
2fd48392 | 1161 | posix::utimbuf times; |
3b90ed62 | 1162 | times.modtime = s.count(); |
1163 | times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; }, | |
1164 | times.modtime); | |
2fd48392 | 1165 | if (posix::utime(p.c_str(), ×)) |
3b90ed62 | 1166 | ec.assign(errno, std::generic_category()); |
1167 | else | |
1168 | ec.clear(); | |
1169 | #else | |
1170 | ec = std::make_error_code(std::errc::not_supported); | |
1171 | #endif | |
1172 | } | |
1173 | ||
1174 | void | |
1175 | fs::permissions(const path& p, perms prms, perm_options opts) | |
1176 | { | |
1177 | error_code ec; | |
1178 | permissions(p, prms, opts, ec); | |
1179 | if (ec) | |
1180 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec)); | |
1181 | } | |
1182 | ||
1183 | void | |
1184 | fs::permissions(const path& p, perms prms, perm_options opts, | |
1185 | error_code& ec) noexcept | |
1186 | { | |
1187 | const bool replace = is_set(opts, perm_options::replace); | |
1188 | const bool add = is_set(opts, perm_options::add); | |
1189 | const bool remove = is_set(opts, perm_options::remove); | |
1190 | const bool nofollow = is_set(opts, perm_options::nofollow); | |
1191 | if (((int)replace + (int)add + (int)remove) != 1) | |
1192 | { | |
1193 | ec = std::make_error_code(std::errc::invalid_argument); | |
1194 | return; | |
1195 | } | |
1196 | ||
1197 | prms &= perms::mask; | |
1198 | ||
1199 | file_status st; | |
1200 | if (add || remove || nofollow) | |
1201 | { | |
1202 | st = nofollow ? symlink_status(p, ec) : status(p, ec); | |
1203 | if (ec) | |
1204 | return; | |
1205 | auto curr = st.permissions(); | |
1206 | if (add) | |
1207 | prms |= curr; | |
1208 | else if (remove) | |
1209 | prms = curr & ~prms; | |
1210 | } | |
1211 | ||
1212 | int err = 0; | |
1213 | #if _GLIBCXX_USE_FCHMODAT | |
1214 | const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0; | |
1215 | if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag)) | |
1216 | err = errno; | |
1217 | #else | |
1218 | if (nofollow && is_symlink(st)) | |
1219 | ec = std::make_error_code(std::errc::operation_not_supported); | |
2fd48392 | 1220 | else if (posix::chmod(p.c_str(), static_cast<mode_t>(prms))) |
3b90ed62 | 1221 | err = errno; |
1222 | #endif | |
1223 | ||
1224 | if (err) | |
1225 | ec.assign(err, std::generic_category()); | |
1226 | else | |
1227 | ec.clear(); | |
1228 | } | |
1229 | ||
1230 | fs::path | |
1231 | fs::proximate(const path& p, const path& base) | |
1232 | { | |
1233 | return weakly_canonical(p).lexically_proximate(weakly_canonical(base)); | |
1234 | } | |
1235 | ||
1236 | fs::path | |
1237 | fs::proximate(const path& p, const path& base, error_code& ec) | |
1238 | { | |
1239 | path result; | |
1240 | const auto p2 = weakly_canonical(p, ec); | |
1241 | if (!ec) | |
1242 | { | |
1243 | const auto base2 = weakly_canonical(base, ec); | |
1244 | if (!ec) | |
1245 | result = p2.lexically_proximate(base2); | |
1246 | } | |
1247 | return result; | |
1248 | } | |
1249 | ||
1250 | fs::path | |
1251 | fs::read_symlink(const path& p) | |
1252 | { | |
1253 | error_code ec; | |
1254 | path tgt = read_symlink(p, ec); | |
1255 | if (ec) | |
1256 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec)); | |
1257 | return tgt; | |
1258 | } | |
1259 | ||
2fd48392 | 1260 | fs::path fs::read_symlink(const path& p [[gnu::unused]], error_code& ec) |
3b90ed62 | 1261 | { |
730671b5 | 1262 | path result; |
2fd48392 | 1263 | #if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H) |
3b90ed62 | 1264 | stat_type st; |
1265 | if (::lstat(p.c_str(), &st)) | |
1266 | { | |
1267 | ec.assign(errno, std::generic_category()); | |
730671b5 | 1268 | return result; |
3b90ed62 | 1269 | } |
730671b5 | 1270 | std::string buf(st.st_size ? st.st_size + 1 : 128, '\0'); |
1271 | do | |
3b90ed62 | 1272 | { |
730671b5 | 1273 | ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size()); |
1274 | if (len == -1) | |
1275 | { | |
1276 | ec.assign(errno, std::generic_category()); | |
1277 | return result; | |
1278 | } | |
1279 | else if (len == (ssize_t)buf.size()) | |
1280 | { | |
1281 | if (buf.size() > 4096) | |
1282 | { | |
1283 | ec.assign(ENAMETOOLONG, std::generic_category()); | |
1284 | return result; | |
1285 | } | |
1286 | buf.resize(buf.size() * 2); | |
1287 | } | |
1288 | else | |
1289 | { | |
1290 | buf.resize(len); | |
1291 | result.assign(buf); | |
1292 | ec.clear(); | |
1293 | break; | |
1294 | } | |
3b90ed62 | 1295 | } |
730671b5 | 1296 | while (true); |
3b90ed62 | 1297 | #else |
1298 | ec = std::make_error_code(std::errc::not_supported); | |
3b90ed62 | 1299 | #endif |
730671b5 | 1300 | return result; |
3b90ed62 | 1301 | } |
1302 | ||
1303 | fs::path | |
1304 | fs::relative(const path& p, const path& base) | |
1305 | { | |
1306 | return weakly_canonical(p).lexically_relative(weakly_canonical(base)); | |
1307 | } | |
1308 | ||
1309 | fs::path | |
1310 | fs::relative(const path& p, const path& base, error_code& ec) | |
1311 | { | |
1312 | auto result = weakly_canonical(p, ec); | |
1313 | fs::path cbase; | |
1314 | if (!ec) | |
1315 | cbase = weakly_canonical(base, ec); | |
1316 | if (!ec) | |
1317 | result = result.lexically_relative(cbase); | |
1318 | if (ec) | |
1319 | result.clear(); | |
1320 | return result; | |
1321 | } | |
1322 | ||
1323 | bool | |
1324 | fs::remove(const path& p) | |
1325 | { | |
1326 | error_code ec; | |
b160a994 | 1327 | const bool result = fs::remove(p, ec); |
3b90ed62 | 1328 | if (ec) |
1329 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec)); | |
1330 | return result; | |
1331 | } | |
1332 | ||
1333 | bool | |
1334 | fs::remove(const path& p, error_code& ec) noexcept | |
1335 | { | |
2fd48392 | 1336 | #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS |
1337 | if (exists(symlink_status(p, ec))) | |
1338 | { | |
1339 | if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str())) | |
1340 | || DeleteFileW(p.c_str())) | |
1341 | { | |
1342 | ec.clear(); | |
1343 | return true; | |
1344 | } | |
1345 | else if (!ec) | |
1346 | ec.assign((int)GetLastError(), generic_category()); | |
1347 | } | |
1348 | #else | |
53103929 | 1349 | if (::remove(p.c_str()) == 0) |
7fd2dd85 | 1350 | { |
53103929 | 1351 | ec.clear(); |
1352 | return true; | |
7fd2dd85 | 1353 | } |
53103929 | 1354 | else if (errno == ENOENT) |
1355 | ec.clear(); | |
1356 | else | |
1357 | ec.assign(errno, std::generic_category()); | |
2fd48392 | 1358 | #endif |
53103929 | 1359 | return false; |
3b90ed62 | 1360 | } |
1361 | ||
1362 | ||
1363 | std::uintmax_t | |
1364 | fs::remove_all(const path& p) | |
1365 | { | |
1366 | error_code ec; | |
53103929 | 1367 | const auto result = remove_all(p, ec); |
3b90ed62 | 1368 | if (ec) |
1369 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec)); | |
1370 | return result; | |
1371 | } | |
1372 | ||
1373 | std::uintmax_t | |
96040a52 | 1374 | fs::remove_all(const path& p, error_code& ec) |
3b90ed62 | 1375 | { |
7fd2dd85 | 1376 | const auto s = symlink_status(p, ec); |
1377 | if (!status_known(s)) | |
1378 | return -1; | |
53103929 | 1379 | |
7fd2dd85 | 1380 | ec.clear(); |
53103929 | 1381 | if (s.type() == file_type::not_found) |
1382 | return 0; | |
1383 | ||
3b90ed62 | 1384 | uintmax_t count = 0; |
7fd2dd85 | 1385 | if (s.type() == file_type::directory) |
53103929 | 1386 | { |
1387 | for (directory_iterator d(p, ec), end; !ec && d != end; d.increment(ec)) | |
1388 | count += fs::remove_all(d->path(), ec); | |
1389 | if (ec.value() == ENOENT) | |
1390 | ec.clear(); | |
1391 | else if (ec) | |
1392 | return -1; | |
1393 | } | |
1394 | ||
b160a994 | 1395 | if (fs::remove(p, ec)) |
7fd2dd85 | 1396 | ++count; |
b160a994 | 1397 | return ec ? -1 : count; |
3b90ed62 | 1398 | } |
1399 | ||
1400 | void | |
1401 | fs::rename(const path& from, const path& to) | |
1402 | { | |
1403 | error_code ec; | |
1404 | rename(from, to, ec); | |
1405 | if (ec) | |
1406 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec)); | |
1407 | } | |
1408 | ||
1409 | void | |
1410 | fs::rename(const path& from, const path& to, error_code& ec) noexcept | |
1411 | { | |
2fd48392 | 1412 | if (posix::rename(from.c_str(), to.c_str())) |
3b90ed62 | 1413 | ec.assign(errno, std::generic_category()); |
1414 | else | |
1415 | ec.clear(); | |
1416 | } | |
1417 | ||
1418 | void | |
1419 | fs::resize_file(const path& p, uintmax_t size) | |
1420 | { | |
1421 | error_code ec; | |
1422 | resize_file(p, size, ec); | |
1423 | if (ec) | |
1424 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec)); | |
1425 | } | |
1426 | ||
1427 | void | |
1428 | fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept | |
1429 | { | |
1430 | #ifdef _GLIBCXX_HAVE_UNISTD_H | |
1431 | if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max())) | |
1432 | ec.assign(EINVAL, std::generic_category()); | |
2fd48392 | 1433 | else if (posix::truncate(p.c_str(), size)) |
3b90ed62 | 1434 | ec.assign(errno, std::generic_category()); |
1435 | else | |
1436 | ec.clear(); | |
1437 | #else | |
1438 | ec = std::make_error_code(std::errc::not_supported); | |
1439 | #endif | |
1440 | } | |
1441 | ||
1442 | ||
1443 | fs::space_info | |
1444 | fs::space(const path& p) | |
1445 | { | |
1446 | error_code ec; | |
1447 | space_info s = space(p, ec); | |
1448 | if (ec) | |
1449 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec)); | |
1450 | return s; | |
1451 | } | |
1452 | ||
2fd48392 | 1453 | #ifdef NEED_DO_SPACE |
1454 | void | |
1455 | fs::do_space(const __gnu_posix::char_type* pathname, | |
1456 | uintmax_t& capacity, uintmax_t& free, uintmax_t& available, | |
1457 | std::error_code& ec) | |
3b90ed62 | 1458 | { |
3b90ed62 | 1459 | #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H |
1460 | struct ::statvfs f; | |
2fd48392 | 1461 | if (::statvfs(pathname, &f)) |
3b90ed62 | 1462 | ec.assign(errno, std::generic_category()); |
1463 | else | |
1464 | { | |
2fd48392 | 1465 | if (f.f_frsize != (unsigned long)-1) |
1466 | { | |
1467 | const uintmax_t fragment_size = f.f_frsize; | |
1468 | const fsblkcnt_t unknown = -1; | |
1469 | if (f.f_blocks != unknown) | |
1470 | capacity = f.f_blocks * fragment_size; | |
1471 | if (f.f_bfree != unknown) | |
1472 | free = f.f_bfree * fragment_size; | |
1473 | if (f.f_bavail != unknown) | |
1474 | available = f.f_bavail * fragment_size; | |
1475 | } | |
3b90ed62 | 1476 | ec.clear(); |
1477 | } | |
2fd48392 | 1478 | #elif _GLIBCXX_FILESYSTEM_IS_WINDOWS |
1479 | ULARGE_INTEGER bytes_avail = {}, bytes_total = {}, bytes_free = {}; | |
1480 | if (GetDiskFreeSpaceExW(pathname, &bytes_avail, &bytes_total, &bytes_free)) | |
1481 | { | |
1482 | if (bytes_total.QuadPart != 0) | |
1483 | capacity = bytes_total.QuadPart; | |
1484 | if (bytes_free.QuadPart != 0) | |
1485 | free = bytes_free.QuadPart; | |
1486 | if (bytes_avail.QuadPart != 0) | |
1487 | available = bytes_avail.QuadPart; | |
1488 | ec.clear(); | |
1489 | } | |
1490 | else | |
1491 | ec.assign((int)GetLastError(), std::system_category()); | |
3b90ed62 | 1492 | #else |
1493 | ec = std::make_error_code(std::errc::not_supported); | |
1494 | #endif | |
2fd48392 | 1495 | } |
1496 | #endif // NEED_DO_SPACE | |
1497 | ||
1498 | fs::space_info | |
1499 | fs::space(const path& p, error_code& ec) noexcept | |
1500 | { | |
1501 | space_info info = { | |
1502 | static_cast<uintmax_t>(-1), | |
1503 | static_cast<uintmax_t>(-1), | |
1504 | static_cast<uintmax_t>(-1) | |
1505 | }; | |
1506 | #if _GLIBCXX_FILESYSTEM_IS_WINDOWS | |
1507 | path dir = absolute(p); | |
1508 | dir.remove_filename(); | |
1509 | auto str = dir.c_str(); | |
1510 | #else | |
1511 | auto str = p.c_str(); | |
1512 | #endif | |
1513 | do_space(str, info.capacity, info.free, info.available, ec); | |
3b90ed62 | 1514 | return info; |
1515 | } | |
1516 | ||
1517 | #ifdef _GLIBCXX_HAVE_SYS_STAT_H | |
1518 | fs::file_status | |
1519 | fs::status(const fs::path& p, error_code& ec) noexcept | |
1520 | { | |
1521 | file_status status; | |
1522 | stat_type st; | |
2fd48392 | 1523 | if (posix::stat(p.c_str(), &st)) |
3b90ed62 | 1524 | { |
1525 | int err = errno; | |
1526 | ec.assign(err, std::generic_category()); | |
1527 | if (is_not_found_errno(err)) | |
1528 | status.type(file_type::not_found); | |
1529 | #ifdef EOVERFLOW | |
1530 | else if (err == EOVERFLOW) | |
1531 | status.type(file_type::unknown); | |
1532 | #endif | |
1533 | } | |
1534 | else | |
1535 | { | |
1536 | status = make_file_status(st); | |
1537 | ec.clear(); | |
1538 | } | |
1539 | return status; | |
1540 | } | |
1541 | ||
1542 | fs::file_status | |
1543 | fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept | |
1544 | { | |
1545 | file_status status; | |
1546 | stat_type st; | |
2fd48392 | 1547 | if (posix::lstat(p.c_str(), &st)) |
3b90ed62 | 1548 | { |
1549 | int err = errno; | |
1550 | ec.assign(err, std::generic_category()); | |
1551 | if (is_not_found_errno(err)) | |
1552 | status.type(file_type::not_found); | |
1553 | } | |
1554 | else | |
1555 | { | |
1556 | status = make_file_status(st); | |
1557 | ec.clear(); | |
1558 | } | |
1559 | return status; | |
1560 | } | |
1561 | #endif | |
1562 | ||
1563 | fs::file_status | |
1564 | fs::status(const fs::path& p) | |
1565 | { | |
1566 | std::error_code ec; | |
1567 | auto result = status(p, ec); | |
1568 | if (result.type() == file_type::none) | |
1569 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec)); | |
1570 | return result; | |
1571 | } | |
1572 | ||
1573 | fs::file_status | |
1574 | fs::symlink_status(const fs::path& p) | |
1575 | { | |
1576 | std::error_code ec; | |
1577 | auto result = symlink_status(p, ec); | |
1578 | if (result.type() == file_type::none) | |
1579 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec)); | |
1580 | return result; | |
1581 | } | |
1582 | ||
1583 | fs::path fs::temp_directory_path() | |
1584 | { | |
1585 | error_code ec; | |
1586 | path tmp = temp_directory_path(ec); | |
1587 | if (ec) | |
1588 | _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec)); | |
1589 | return tmp; | |
1590 | } | |
1591 | ||
1592 | fs::path fs::temp_directory_path(error_code& ec) | |
1593 | { | |
2fd48392 | 1594 | path p; |
3b90ed62 | 1595 | #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS |
2fd48392 | 1596 | unsigned len = 1024; |
1597 | std::wstring buf; | |
1598 | do | |
1599 | { | |
1600 | buf.resize(len); | |
1601 | len = GetTempPathW(buf.size(), buf.data()); | |
1602 | } while (len > buf.size()); | |
1603 | ||
1604 | if (len == 0) | |
1605 | { | |
1606 | ec.assign((int)GetLastError(), std::system_category()); | |
1607 | return p; | |
1608 | } | |
1609 | buf.resize(len); | |
1610 | p = std::move(buf); | |
3b90ed62 | 1611 | #else |
1612 | const char* tmpdir = nullptr; | |
1613 | const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr }; | |
1614 | for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e) | |
1615 | tmpdir = ::getenv(*e); | |
2fd48392 | 1616 | p = tmpdir ? tmpdir : "/tmp"; |
1617 | #endif | |
3b90ed62 | 1618 | auto st = status(p, ec); |
2fd48392 | 1619 | if (ec) |
1620 | p.clear(); | |
1621 | else if (!is_directory(st)) | |
3b90ed62 | 1622 | { |
2fd48392 | 1623 | p.clear(); |
1624 | ec = std::make_error_code(std::errc::not_a_directory); | |
3b90ed62 | 1625 | } |
2fd48392 | 1626 | return p; |
3b90ed62 | 1627 | } |
1628 | ||
1629 | fs::path | |
1630 | fs::weakly_canonical(const path& p) | |
1631 | { | |
1632 | path result; | |
1633 | if (exists(status(p))) | |
1634 | return canonical(p); | |
1635 | ||
1636 | path tmp; | |
1637 | auto iter = p.begin(), end = p.end(); | |
1638 | // find leading elements of p that exist: | |
1639 | while (iter != end) | |
1640 | { | |
1641 | tmp = result / *iter; | |
1642 | if (exists(status(tmp))) | |
1643 | swap(result, tmp); | |
1644 | else | |
1645 | break; | |
1646 | ++iter; | |
1647 | } | |
1648 | // canonicalize: | |
b76cdbc2 | 1649 | if (!result.empty()) |
1650 | result = canonical(result); | |
3b90ed62 | 1651 | // append the non-existing elements: |
1652 | while (iter != end) | |
1653 | result /= *iter++; | |
1654 | // normalize: | |
1655 | return result.lexically_normal(); | |
1656 | } | |
1657 | ||
1658 | fs::path | |
1659 | fs::weakly_canonical(const path& p, error_code& ec) | |
1660 | { | |
1661 | path result; | |
1662 | file_status st = status(p, ec); | |
1663 | if (exists(st)) | |
1664 | return canonical(p, ec); | |
1665 | else if (status_known(st)) | |
1666 | ec.clear(); | |
1667 | else | |
1668 | return result; | |
1669 | ||
1670 | path tmp; | |
1671 | auto iter = p.begin(), end = p.end(); | |
1672 | // find leading elements of p that exist: | |
1673 | while (iter != end) | |
1674 | { | |
1675 | tmp = result / *iter; | |
1676 | st = status(tmp, ec); | |
1677 | if (exists(st)) | |
1678 | swap(result, tmp); | |
1679 | else | |
1680 | { | |
1681 | if (status_known(st)) | |
1682 | ec.clear(); | |
1683 | break; | |
1684 | } | |
1685 | ++iter; | |
1686 | } | |
1687 | // canonicalize: | |
b76cdbc2 | 1688 | if (!ec && !result.empty()) |
3b90ed62 | 1689 | result = canonical(result, ec); |
1690 | if (ec) | |
1691 | result.clear(); | |
1692 | else | |
1693 | { | |
1694 | // append the non-existing elements: | |
1695 | while (iter != end) | |
1696 | result /= *iter++; | |
1697 | // normalize: | |
1698 | result = result.lexically_normal(); | |
1699 | } | |
1700 | return result; | |
1701 | } |