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