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