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