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