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