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