1// Filesystem TS operations -*- C++ -*-
2
3// Copyright (C) 2014-2020 Free Software Foundation, Inc.
4//
5// This file is part of the GNU ISO C++ Library.  This library is free
6// software; you can redistribute it and/or modify it under the
7// terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 3, or (at your option)
9// any later version.
10
11// This library is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14// GNU General Public License for more details.
15
16// Under Section 7 of GPL version 3, you are granted additional
17// permissions described in the GCC Runtime Library Exception, version
18// 3.1, as published by the Free Software Foundation.
19
20// You should have received a copy of the GNU General Public License and
21// a copy of the GCC Runtime Library Exception along with this program;
22// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23// <http://www.gnu.org/licenses/>.
24
25#ifndef _GLIBCXX_USE_CXX11_ABI
26# define _GLIBCXX_USE_CXX11_ABI 1
27# define NEED_DO_COPY_FILE
28# define NEED_DO_SPACE
29#endif
30
31#include <bits/largefile-config.h>
32#include <experimental/filesystem>
33#include <functional>
34#include <ostream>
35#include <stack>
36#include <ext/stdio_filebuf.h>
37#include <stdlib.h>
38#include <stdio.h>
39#include <errno.h>
40#include <limits.h>  // PATH_MAX
41#ifdef _GLIBCXX_HAVE_FCNTL_H
42# include <fcntl.h>  // AT_FDCWD, AT_SYMLINK_NOFOLLOW
43#endif
44#ifdef _GLIBCXX_HAVE_SYS_STAT_H
45# include <sys/stat.h>   // stat, utimensat, fchmodat
46#endif
47#ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
48# include <sys/statvfs.h> // statvfs
49#endif
50#if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
51# include <utime.h> // utime
52#endif
53#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
54# include <windows.h>
55#endif
56
57#define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM \
58  namespace experimental { namespace filesystem {
59#define _GLIBCXX_END_NAMESPACE_FILESYSTEM } }
60#include "ops-common.h"
61
62namespace fs = std::experimental::filesystem;
63namespace posix = std::filesystem::__gnu_posix;
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{
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
107  struct free_as_in_malloc
108  {
109    void operator()(void* p) const { ::free(p); }
110  };
111
112  using char_ptr = std::unique_ptr<fs::path::value_type[], free_as_in_malloc>;
113}
114
115fs::path
116fs::canonical(const path& p, const path& base, error_code& ec)
117{
118  const path pa = absolute(p, base);
119  path result;
120
121#ifdef _GLIBCXX_USE_REALPATH
122  char_ptr buf{ nullptr };
123# if _XOPEN_VERSION < 700
124  // Not safe to call realpath(path, NULL)
125  using char_type = fs::path::value_type;
126  buf.reset( (char_type*)::malloc(PATH_MAX * sizeof(char_type)) );
127# endif
128  if (char* rp = ::realpath(pa.c_str(), buf.get()))
129    {
130      if (buf == nullptr)
131	buf.reset(rp);
132      result.assign(rp);
133      ec.clear();
134      return result;
135    }
136  if (errno != ENAMETOOLONG)
137    {
138      ec.assign(errno, std::generic_category());
139      return result;
140    }
141#endif
142
143  if (!exists(pa, ec))
144    {
145      if (!ec)
146	ec = make_error_code(std::errc::no_such_file_or_directory);
147      return result;
148    }
149  // else: we know there are (currently) no unresolvable symlink loops
150
151  result = pa.root_path();
152
153  deque<path> cmpts;
154  for (auto& f : pa.relative_path())
155    cmpts.push_back(f);
156
157  int max_allowed_symlinks = 40;
158
159  while (!cmpts.empty() && !ec)
160    {
161      path f = std::move(cmpts.front());
162      cmpts.pop_front();
163
164      if (is_dot(f))
165	{
166	  if (!is_directory(result, ec) && !ec)
167	    ec.assign(ENOTDIR, std::generic_category());
168	}
169      else if (is_dotdot(f))
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);
184	      if (!ec)
185		{
186		  if (--max_allowed_symlinks == 0)
187		    ec.assign(ELOOP, std::generic_category());
188		  else
189		    {
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());
199		    }
200		}
201	    }
202	}
203    }
204
205  if (ec || !exists(result, ec))
206    result.clear();
207
208  return result;
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);
225  if (ec)
226    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot canonicalize", p, base,
227					     ec));
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{
242  using std::filesystem::is_set;
243
244#ifdef _GLIBCXX_HAVE_SYS_STAT_H
245  using posix::stat_type;
246
247  using std::filesystem::is_not_found_errno;
248  using std::filesystem::file_time;
249#endif // _GLIBCXX_HAVE_SYS_STAT_H
250
251} // namespace
252
253void
254fs::copy(const path& from, const path& to, copy_options options,
255	 error_code& ec) noexcept
256{
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;
261
262  file_status f, t;
263  stat_type from_st, to_st;
264  // _GLIBCXX_RESOLVE_LIB_DEFECTS
265  // 2681. filesystem::copy() cannot copy symlinks
266  if (use_lstat || copy_symlinks
267      ? posix::lstat(from.c_str(), &from_st)
268      : posix::stat(from.c_str(), &from_st))
269    {
270      ec.assign(errno, std::generic_category());
271      return;
272    }
273  if (use_lstat
274      ? posix::lstat(to.c_str(), &to_st)
275      : posix::stat(to.c_str(), &to_st))
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    {
296      ec = std::make_error_code(std::errc::not_supported);
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();
309      else if (!exists(t) && copy_symlinks)
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))
325	do_copy_file(from.c_str(), (to / from.filename()).c_str(),
326		     copy_file_options(options), &from_st, nullptr, ec);
327      else
328	{
329	  auto ptr = exists(t) ? &to_st : &from_st;
330	  do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
331		       &from_st, ptr,  ec);
332	}
333    }
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);
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, ec))
348	{
349	  copy(x.path(), to/x.path().filename(), options, ec);
350	  if (ec)
351	    return;
352	}
353    }
354  // _GLIBCXX_RESOLVE_LIB_DEFECTS
355  // 2683. filesystem::copy() says "no effects"
356  else
357    ec.clear();
358}
359
360bool
361fs::copy_file(const path& from, const path& to, copy_options option)
362{
363  error_code ec;
364  bool result = copy_file(from, to, option, ec);
365  if (ec.value())
366    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to,
367	  ec));
368  return result;
369}
370
371bool
372fs::copy_file(const path& from, const path& to, copy_options options,
373	      error_code& ec)
374{
375#ifdef _GLIBCXX_HAVE_SYS_STAT_H
376  return do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
377		      nullptr, nullptr, ec);
378#else
379  ec = std::make_error_code(std::errc::not_supported);
380  return false;
381#endif
382}
383
384
385void
386fs::copy_symlink(const path& existing_symlink, const path& new_symlink)
387{
388  error_code ec;
389  copy_symlink(existing_symlink, new_symlink, ec);
390  if (ec.value())
391    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink",
392	  existing_symlink, new_symlink, ec));
393}
394
395void
396fs::copy_symlink(const path& existing_symlink, const path& new_symlink,
397		 error_code& ec) noexcept
398{
399  auto p = read_symlink(existing_symlink, ec);
400  if (ec.value())
401    return;
402#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
403  if (is_directory(p))
404    {
405      create_directory_symlink(p, new_symlink, ec);
406      return;
407    }
408#endif
409  create_symlink(p, new_symlink, ec);
410}
411
412
413bool
414fs::create_directories(const path& p)
415{
416  error_code ec;
417  bool result = create_directories(p, ec);
418  if (ec.value())
419    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p,
420	  ec));
421  return result;
422}
423
424bool
425fs::create_directories(const path& p, error_code& ec)
426{
427  if (p.empty())
428    {
429      ec = std::make_error_code(errc::invalid_argument);
430      return false;
431    }
432
433  file_status st = status(p, ec);
434  if (is_directory(st))
435    return false;
436  else if (ec && !status_known(st))
437    return false;
438  else if (exists(st))
439    {
440      if (!ec)
441	ec = std::make_error_code(std::errc::not_a_directory);
442      return false;
443    }
444
445  std::stack<path> missing;
446  path pp = p;
447
448  while (!pp.empty() && status(pp, ec).type() == file_type::not_found)
449    {
450      ec.clear();
451      const auto& filename = pp.filename();
452      if (!is_dot(filename) && !is_dotdot(filename))
453	{
454	  missing.push(std::move(pp));
455	  pp = missing.top().parent_path();
456	}
457      else
458	pp = pp.parent_path();
459    }
460
461  if (ec || missing.empty())
462    return false;
463
464  bool created;
465  do
466    {
467      const path& top = missing.top();
468      created = create_directory(top, ec);
469      if (ec)
470	return false;
471      missing.pop();
472    }
473  while (!missing.empty());
474
475  return created;
476}
477
478namespace
479{
480  bool
481  create_dir(const fs::path& p, fs::perms perm, std::error_code& ec)
482  {
483    bool created = false;
484#ifdef _GLIBCXX_HAVE_SYS_STAT_H
485    posix::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
486    if (posix::mkdir(p.c_str(), mode))
487      {
488	const int err = errno;
489	if (err != EEXIST || !is_directory(p, ec))
490	  ec.assign(err, std::generic_category());
491      }
492    else
493      {
494	ec.clear();
495	created = true;
496      }
497#else
498    ec = std::make_error_code(std::errc::not_supported);
499#endif
500    return created;
501  }
502} // namespace
503
504bool
505fs::create_directory(const path& p)
506{
507  error_code ec;
508  bool result = create_directory(p, ec);
509  if (ec.value())
510    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
511	  ec));
512  return result;
513}
514
515bool
516fs::create_directory(const path& p, error_code& ec) noexcept
517{
518  return create_dir(p, perms::all, ec);
519}
520
521
522bool
523fs::create_directory(const path& p, const path& attributes)
524{
525  error_code ec;
526  bool result = create_directory(p, attributes, ec);
527  if (ec.value())
528    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
529	  ec));
530  return result;
531}
532
533bool
534fs::create_directory(const path& p, const path& attributes,
535		     error_code& ec) noexcept
536{
537#ifdef _GLIBCXX_HAVE_SYS_STAT_H
538  stat_type st;
539  if (posix::stat(attributes.c_str(), &st))
540    {
541      ec.assign(errno, std::generic_category());
542      return false;
543    }
544  return create_dir(p, static_cast<perms>(st.st_mode), ec);
545#else
546  ec = std::make_error_code(std::errc::not_supported);
547  return false;
548#endif
549}
550
551
552void
553fs::create_directory_symlink(const path& to, const path& new_symlink)
554{
555  error_code ec;
556  create_directory_symlink(to, new_symlink, ec);
557  if (ec.value())
558    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory symlink",
559	  to, new_symlink, ec));
560}
561
562void
563fs::create_directory_symlink(const path& to, const path& new_symlink,
564			     error_code& ec) noexcept
565{
566#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
567  ec = std::make_error_code(std::errc::not_supported);
568#else
569  create_symlink(to, new_symlink, ec);
570#endif
571}
572
573
574void
575fs::create_hard_link(const path& to, const path& new_hard_link)
576{
577  error_code ec;
578  create_hard_link(to, new_hard_link, ec);
579  if (ec.value())
580    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link",
581	  to, new_hard_link, ec));
582}
583
584void
585fs::create_hard_link(const path& to, const path& new_hard_link,
586		     error_code& ec) noexcept
587{
588#ifdef _GLIBCXX_HAVE_LINK
589  if (::link(to.c_str(), new_hard_link.c_str()))
590    ec.assign(errno, std::generic_category());
591  else
592    ec.clear();
593#elif defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
594  if (CreateHardLinkW(new_hard_link.c_str(), to.c_str(), NULL))
595    ec.clear();
596  else
597    ec.assign((int)GetLastError(), system_category());
598#else
599  ec = std::make_error_code(std::errc::not_supported);
600#endif
601}
602
603void
604fs::create_symlink(const path& to, const path& new_symlink)
605{
606  error_code ec;
607  create_symlink(to, new_symlink, ec);
608  if (ec.value())
609    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink",
610	  to, new_symlink, ec));
611}
612
613void
614fs::create_symlink(const path& to, const path& new_symlink,
615		   error_code& ec) noexcept
616{
617#ifdef _GLIBCXX_HAVE_SYMLINK
618  if (::symlink(to.c_str(), new_symlink.c_str()))
619    ec.assign(errno, std::generic_category());
620  else
621    ec.clear();
622#else
623  ec = std::make_error_code(std::errc::not_supported);
624#endif
625}
626
627fs::path
628fs::current_path()
629{
630  error_code ec;
631  path p = current_path(ec);
632  if (ec.value())
633    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec));
634  return p;
635}
636
637fs::path
638fs::current_path(error_code& ec)
639{
640  path p;
641#ifdef _GLIBCXX_HAVE_UNISTD_H
642#if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
643  if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)})
644    {
645      p.assign(cwd.get());
646      ec.clear();
647    }
648  else
649    ec.assign(errno, std::generic_category());
650#else
651#ifdef _PC_PATH_MAX
652  long path_max = pathconf(".", _PC_PATH_MAX);
653  size_t size;
654  if (path_max == -1)
655      size = 1024;
656  else if (path_max > 10240)
657      size = 10240;
658  else
659      size = path_max;
660#elif defined(PATH_MAX)
661  size_t size = PATH_MAX;
662#else
663  size_t size = 1024;
664#endif
665  for (char_ptr buf; p.empty(); size *= 2)
666    {
667      using char_type = fs::path::value_type;
668      buf.reset((char_type*)malloc(size * sizeof(char_type)));
669      if (buf)
670	{
671	  if (getcwd(buf.get(), size))
672	    {
673	      p.assign(buf.get());
674	      ec.clear();
675	    }
676	  else if (errno != ERANGE)
677	    {
678	      ec.assign(errno, std::generic_category());
679	      return {};
680	    }
681	}
682      else
683	{
684	  ec = std::make_error_code(std::errc::not_enough_memory);
685	  return {};
686	}
687    }
688#endif  // __GLIBC__
689#else   // _GLIBCXX_HAVE_UNISTD_H
690  ec = std::make_error_code(std::errc::not_supported);
691#endif
692  return p;
693}
694
695void
696fs::current_path(const path& p)
697{
698  error_code ec;
699  current_path(p, ec);
700  if (ec.value())
701    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec));
702}
703
704void
705fs::current_path(const path& p, error_code& ec) noexcept
706{
707#ifdef _GLIBCXX_HAVE_UNISTD_H
708  if (posix::chdir(p.c_str()))
709    ec.assign(errno, std::generic_category());
710  else
711    ec.clear();
712#else
713  ec = std::make_error_code(std::errc::not_supported);
714#endif
715}
716
717bool
718fs::equivalent(const path& p1, const path& p2)
719{
720  error_code ec;
721  auto result = equivalent(p1, p2, ec);
722  if (ec)
723    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence",
724	  p1, p2, ec));
725  return result;
726}
727
728bool
729fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
730{
731#ifdef _GLIBCXX_HAVE_SYS_STAT_H
732  int err = 0;
733  file_status s1, s2;
734  stat_type st1, st2;
735  if (posix::stat(p1.c_str(), &st1) == 0)
736    s1 = make_file_status(st1);
737  else if (is_not_found_errno(errno))
738    s1.type(file_type::not_found);
739  else
740    err = errno;
741
742  if (posix::stat(p2.c_str(), &st2) == 0)
743    s2 = make_file_status(st2);
744  else if (is_not_found_errno(errno))
745    s2.type(file_type::not_found);
746  else
747    err = errno;
748
749  if (exists(s1) && exists(s2))
750    {
751      if (is_other(s1) && is_other(s2))
752	{
753	  ec = std::make_error_code(std::errc::not_supported);
754	  return false;
755	}
756      ec.clear();
757      if (is_other(s1) || is_other(s2))
758	return false;
759      return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
760    }
761  else if (!exists(s1) && !exists(s2))
762    ec = std::make_error_code(std::errc::no_such_file_or_directory);
763  else if (err)
764    ec.assign(err, std::generic_category());
765  else
766    ec.clear();
767  return false;
768#else
769  ec = std::make_error_code(std::errc::not_supported);
770#endif
771  return false;
772}
773
774std::uintmax_t
775fs::file_size(const path& p)
776{
777  error_code ec;
778  auto sz = file_size(p, ec);
779  if (ec.value())
780    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec));
781  return sz;
782}
783
784namespace
785{
786  template<typename Accessor, typename T>
787    inline T
788    do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
789    {
790#ifdef _GLIBCXX_HAVE_SYS_STAT_H
791      stat_type st;
792      if (posix::stat(p.c_str(), &st))
793	{
794	  ec.assign(errno, std::generic_category());
795	  return deflt;
796	}
797      ec.clear();
798      return f(st);
799#else
800      ec = std::make_error_code(std::errc::not_supported);
801      return deflt;
802#endif
803    }
804}
805
806std::uintmax_t
807fs::file_size(const path& p, error_code& ec) noexcept
808{
809  struct S
810  {
811    S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { }
812    S() : type(file_type::not_found) { }
813    file_type type;
814    uintmax_t size;
815  };
816  auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{});
817  if (s.type == file_type::regular)
818    return s.size;
819  if (!ec)
820    {
821      if (s.type == file_type::directory)
822	ec = std::make_error_code(std::errc::is_a_directory);
823      else
824	ec = std::make_error_code(std::errc::not_supported);
825    }
826  return -1;
827}
828
829std::uintmax_t
830fs::hard_link_count(const path& p)
831{
832  error_code ec;
833  auto count = hard_link_count(p, ec);
834  if (ec.value())
835    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec));
836  return count;
837}
838
839std::uintmax_t
840fs::hard_link_count(const path& p, error_code& ec) noexcept
841{
842  return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink),
843		 static_cast<uintmax_t>(-1));
844}
845
846bool
847fs::is_empty(const path& p)
848{
849  error_code ec;
850  bool e = is_empty(p, ec);
851  if (ec)
852    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty",
853					     p, ec));
854  return e;
855}
856
857bool
858fs::is_empty(const path& p, error_code& ec) noexcept
859{
860  auto s = status(p, ec);
861  if (ec)
862    return false;
863  bool empty = fs::is_directory(s)
864    ? fs::directory_iterator(p, ec) == fs::directory_iterator()
865    : fs::file_size(p, ec) == 0;
866  return ec ? false : empty;
867}
868
869fs::file_time_type
870fs::last_write_time(const path& p)
871{
872  error_code ec;
873  auto t = last_write_time(p, ec);
874  if (ec.value())
875    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec));
876  return t;
877}
878
879fs::file_time_type
880fs::last_write_time(const path& p, error_code& ec) noexcept
881{
882  return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); },
883		 file_time_type::min());
884}
885
886void
887fs::last_write_time(const path& p, file_time_type new_time)
888{
889  error_code ec;
890  last_write_time(p, new_time, ec);
891  if (ec.value())
892    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec));
893}
894
895void
896fs::last_write_time(const path& p __attribute__((__unused__)),
897		    file_time_type new_time, error_code& ec) noexcept
898{
899  auto d = new_time.time_since_epoch();
900  auto s = chrono::duration_cast<chrono::seconds>(d);
901#if _GLIBCXX_USE_UTIMENSAT
902  auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s);
903  if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9.
904    {
905      --s;
906      ns += chrono::seconds(1);
907    }
908  struct ::timespec ts[2];
909  ts[0].tv_sec = 0;
910  ts[0].tv_nsec = UTIME_OMIT;
911  ts[1].tv_sec = static_cast<std::time_t>(s.count());
912  ts[1].tv_nsec = static_cast<long>(ns.count());
913  if (::utimensat(AT_FDCWD, p.c_str(), ts, 0))
914    ec.assign(errno, std::generic_category());
915  else
916    ec.clear();
917#elif _GLIBCXX_USE_UTIME && _GLIBCXX_HAVE_SYS_STAT_H
918  posix::utimbuf times;
919  times.modtime = s.count();
920  times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
921			 times.modtime);
922  if (posix::utime(p.c_str(), &times))
923    ec.assign(errno, std::generic_category());
924  else
925    ec.clear();
926#else
927  ec = std::make_error_code(std::errc::not_supported);
928#endif
929}
930
931void
932fs::permissions(const path& p, perms prms)
933{
934  error_code ec;
935  permissions(p, prms, ec);
936  if (ec.value())
937    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec));
938}
939
940void
941fs::permissions(const path& p, perms prms, error_code& ec) noexcept
942{
943  const bool add = is_set(prms, perms::add_perms);
944  const bool remove = is_set(prms, perms::remove_perms);
945  const bool nofollow = is_set(prms, perms::symlink_nofollow);
946  if (add && remove)
947    {
948      ec = std::make_error_code(std::errc::invalid_argument);
949      return;
950    }
951
952  prms &= perms::mask;
953
954  file_status st;
955  if (add || remove || nofollow)
956    {
957      st = nofollow ? symlink_status(p, ec) : status(p, ec);
958      if (ec)
959	return;
960      auto curr = st.permissions();
961      if (add)
962	prms |= curr;
963      else if (remove)
964	prms = curr & ~prms;
965    }
966
967  int err = 0;
968#if _GLIBCXX_USE_FCHMODAT
969  const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0;
970  if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag))
971    err = errno;
972#else
973  if (nofollow && is_symlink(st))
974    ec = std::make_error_code(std::errc::operation_not_supported);
975  else if (posix::chmod(p.c_str(), static_cast<mode_t>(prms)))
976    err = errno;
977#endif
978
979  if (err)
980    ec.assign(err, std::generic_category());
981  else
982    ec.clear();
983}
984
985fs::path
986fs::read_symlink(const path& p)
987{
988  error_code ec;
989  path tgt = read_symlink(p, ec);
990  if (ec.value())
991    _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec));
992  return tgt;
993}
994
995fs::path fs::read_symlink(const path& p [[gnu::unused]], error_code& ec)
996{
997  path result;
998#if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H)
999  stat_type st;
1000  if (posix::lstat(p.c_str(), &st))
1001    {
1002      ec.assign(errno, std::generic_category());
1003      return result;
1004    }
1005  else if (!fs::is_symlink(make_file_status(st)))
1006    {
1007      ec.assign(EINVAL, std::generic_category());
1008      return result;
1009    }
1010
1011  std::string buf(st.st_size ? st.st_size + 1 : 128, '\0');
1012  do
1013    {
1014      ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size());
1015      if (len == -1)
1016	{
1017	  ec.assign(errno, std::generic_category());
1018	  return result;
1019	}
1020      else if (len == (ssize_t)buf.size())
1021	{
1022	  if (buf.size() > 4096)
1023	    {
1024	      ec.assign(ENAMETOOLONG, std::generic_category());
1025	      return result;
1026	    }
1027	  buf.resize(buf.size() * 2);
1028	}
1029      else
1030	{
1031	  buf.resize(len);
1032	  result.assign(buf);
1033	  ec.clear();
1034	  break;
1035	}
1036    }
1037  while (true);
1038#else
1039  ec = std::make_error_code(std::errc::not_supported);
1040#endif
1041  return result;
1042}
1043
1044
1045bool
1046fs::remove(const path& p)
1047{
1048  error_code ec;
1049  bool result = fs::remove(p, ec);
1050  if (ec)
1051    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1052  return result;
1053}
1054
1055bool
1056fs::remove(const path& p, error_code& ec) noexcept
1057{
1058#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1059  auto st = symlink_status(p, ec);
1060  if (exists(st))
1061    {
1062      if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str()))
1063	  || DeleteFileW(p.c_str()))
1064	{
1065	  ec.clear();
1066	  return true;
1067	}
1068      else if (!ec)
1069	ec.assign((int)GetLastError(), system_category());
1070    }
1071  else if (status_known(st))
1072    ec.clear();
1073#else
1074  if (::remove(p.c_str()) == 0)
1075    {
1076      ec.clear();
1077      return true;
1078    }
1079  else if (errno == ENOENT)
1080    ec.clear();
1081  else
1082    ec.assign(errno, std::generic_category());
1083#endif
1084  return false;
1085}
1086
1087
1088std::uintmax_t
1089fs::remove_all(const path& p)
1090{
1091  error_code ec;
1092  const auto result = remove_all(p, ec);
1093  if (ec)
1094    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1095  return result;
1096}
1097
1098std::uintmax_t
1099fs::remove_all(const path& p, error_code& ec)
1100{
1101  const auto s = symlink_status(p, ec);
1102  if (!status_known(s))
1103    return -1;
1104
1105  ec.clear();
1106  if (s.type() == file_type::not_found)
1107    return 0;
1108
1109  uintmax_t count = 0;
1110  if (s.type() == file_type::directory)
1111    {
1112      directory_iterator d(p, ec), end;
1113      while (!ec && d != end)
1114	{
1115	  const auto removed = fs::remove_all(d->path(), ec);
1116	  if (removed == numeric_limits<uintmax_t>::max())
1117	    return -1;
1118	  count += removed;
1119	  d.increment(ec);
1120	  if (ec)
1121	    return -1;
1122	}
1123    }
1124
1125  if (fs::remove(p, ec))
1126    ++count;
1127  return ec ? -1 : count;
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{
1142  if (posix::rename(from.c_str(), to.c_str()))
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{
1160#ifdef _GLIBCXX_HAVE_UNISTD_H
1161  if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
1162    ec.assign(EINVAL, std::generic_category());
1163  else if (posix::truncate(p.c_str(), size))
1164    ec.assign(errno, std::generic_category());
1165  else
1166    ec.clear();
1167#else
1168  ec = std::make_error_code(std::errc::not_supported);
1169#endif
1170}
1171
1172
1173fs::space_info
1174fs::space(const path& p)
1175{
1176  error_code ec;
1177  space_info s = space(p, ec);
1178  if (ec.value())
1179    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1180  return s;
1181}
1182
1183fs::space_info
1184fs::space(const path& p, error_code& ec) noexcept
1185{
1186  space_info info = {
1187    static_cast<uintmax_t>(-1),
1188    static_cast<uintmax_t>(-1),
1189    static_cast<uintmax_t>(-1)
1190  };
1191#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1192  path dir = absolute(p);
1193  dir.remove_filename();
1194  auto str = dir.c_str();
1195#else
1196  auto str = p.c_str();
1197#endif
1198  fs::do_space(str, info.capacity, info.free, info.available, ec);
1199  return info;
1200}
1201
1202#ifdef _GLIBCXX_HAVE_SYS_STAT_H
1203fs::file_status
1204fs::status(const fs::path& p, error_code& ec) noexcept
1205{
1206  file_status status;
1207  stat_type st;
1208  if (posix::stat(p.c_str(), &st))
1209    {
1210      int err = errno;
1211      ec.assign(err, std::generic_category());
1212      if (is_not_found_errno(err))
1213	status.type(file_type::not_found);
1214#ifdef EOVERFLOW
1215      else if (err == EOVERFLOW)
1216	status.type(file_type::unknown);
1217#endif
1218    }
1219  else
1220    {
1221      status = make_file_status(st);
1222      ec.clear();
1223    }
1224  return status;
1225}
1226
1227fs::file_status
1228fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1229{
1230  file_status status;
1231  stat_type st;
1232  if (posix::lstat(p.c_str(), &st))
1233    {
1234      int err = errno;
1235      ec.assign(err, std::generic_category());
1236      if (is_not_found_errno(err))
1237	status.type(file_type::not_found);
1238    }
1239  else
1240    {
1241      status = make_file_status(st);
1242      ec.clear();
1243    }
1244  return status;
1245}
1246#endif
1247
1248fs::file_status
1249fs::status(const fs::path& p)
1250{
1251  std::error_code ec;
1252  auto result = status(p, ec);
1253  if (result.type() == file_type::none)
1254    _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1255  return result;
1256}
1257
1258fs::file_status
1259fs::symlink_status(const fs::path& p)
1260{
1261  std::error_code ec;
1262  auto result = symlink_status(p, ec);
1263  if (result.type() == file_type::none)
1264    _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1265  return result;
1266}
1267
1268fs::path
1269fs::system_complete(const path& p)
1270{
1271  error_code ec;
1272  path comp = system_complete(p, ec);
1273  if (ec.value())
1274    _GLIBCXX_THROW_OR_ABORT(filesystem_error("system_complete", p, ec));
1275  return comp;
1276}
1277
1278fs::path
1279fs::system_complete(const path& p, error_code& ec)
1280{
1281  path base = current_path(ec);
1282#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1283  if (p.is_absolute() || !p.has_root_name()
1284      || p.root_name() == base.root_name())
1285    return absolute(p, base);
1286  // else TODO
1287  ec = std::make_error_code(std::errc::not_supported);
1288  return {};
1289#else
1290  if (ec.value())
1291    return {};
1292  return absolute(p, base);
1293#endif
1294}
1295
1296fs::path fs::temp_directory_path()
1297{
1298  error_code ec;
1299  path tmp = temp_directory_path(ec);
1300  if (ec.value())
1301    _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
1302  return tmp;
1303}
1304
1305fs::path fs::temp_directory_path(error_code& ec)
1306{
1307  path p;
1308#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1309  unsigned len = 1024;
1310  std::wstring buf;
1311  do
1312    {
1313      buf.resize(len);
1314      len = GetTempPathW(buf.size(), buf.data());
1315    } while (len > buf.size());
1316
1317  if (len == 0)
1318    {
1319      ec.assign((int)GetLastError(), std::system_category());
1320      return p;
1321    }
1322  buf.resize(len);
1323  p = std::move(buf);
1324#else
1325  const char* tmpdir = nullptr;
1326  const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
1327  for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
1328    tmpdir = ::getenv(*e);
1329  p = tmpdir ? tmpdir : "/tmp";
1330  auto st = status(p, ec);
1331  if (ec)
1332    p.clear();
1333  else if (!is_directory(st))
1334    {
1335      p.clear();
1336      ec = std::make_error_code(std::errc::not_a_directory);
1337    }
1338#endif
1339  return p;
1340}
1341
1342