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