ops.cc revision 1.5
1// Filesystem TS operations -*- C++ -*-
2
3// Copyright (C) 2014-2019 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))
348	copy(x.path(), to/x.path().filename(), options, ec);
349    }
350  // _GLIBCXX_RESOLVE_LIB_DEFECTS
351  // 2683. filesystem::copy() says "no effects"
352  else
353    ec.clear();
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
368fs::copy_file(const path& from, const path& to, copy_options options,
369	      error_code& ec) noexcept
370{
371#ifdef _GLIBCXX_HAVE_SYS_STAT_H
372  return do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
373		      nullptr, nullptr, ec);
374#else
375  ec = std::make_error_code(std::errc::not_supported);
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{
423  if (p.empty())
424    {
425      ec = std::make_error_code(errc::invalid_argument);
426      return false;
427    }
428
429  file_status st = symlink_status(p, ec);
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
441  std::stack<path> missing;
442  path pp = p;
443
444  while (!pp.empty() && status(pp, ec).type() == file_type::not_found)
445    {
446      ec.clear();
447      const auto& filename = pp.filename();
448      if (!is_dot(filename) && !is_dotdot(filename))
449	{
450	  missing.push(std::move(pp));
451	  pp = missing.top().parent_path();
452	}
453      else
454	pp = pp.parent_path();
455    }
456
457  if (ec || missing.empty())
458    return false;
459
460  bool created;
461  do
462    {
463      const path& top = missing.top();
464      created = create_directory(top, ec);
465      if (ec)
466	return false;
467      missing.pop();
468    }
469  while (!missing.empty());
470
471  return created;
472}
473
474namespace
475{
476  bool
477  create_dir(const fs::path& p, fs::perms perm, std::error_code& ec)
478  {
479    bool created = false;
480#ifdef _GLIBCXX_HAVE_SYS_STAT_H
481    posix::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
482    if (posix::mkdir(p.c_str(), mode))
483      {
484	const int err = errno;
485	if (err != EEXIST || !is_directory(p, ec))
486	  ec.assign(err, std::generic_category());
487      }
488    else
489      {
490	ec.clear();
491	created = true;
492      }
493#else
494    ec = std::make_error_code(std::errc::not_supported);
495#endif
496    return created;
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
534  stat_type st;
535  if (posix::stat(attributes.c_str(), &st))
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
542  ec = std::make_error_code(std::errc::not_supported);
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
563  ec = std::make_error_code(std::errc::not_supported);
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{
584#ifdef _GLIBCXX_HAVE_LINK
585  if (::link(to.c_str(), new_hard_link.c_str()))
586    ec.assign(errno, std::generic_category());
587  else
588    ec.clear();
589#elif defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
590  if (CreateHardLinkW(new_hard_link.c_str(), to.c_str(), NULL))
591    ec.clear();
592  else
593    ec.assign((int)GetLastError(), generic_category());
594#else
595  ec = std::make_error_code(std::errc::not_supported);
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{
613#ifdef _GLIBCXX_HAVE_SYMLINK
614  if (::symlink(to.c_str(), new_symlink.c_str()))
615    ec.assign(errno, std::generic_category());
616  else
617    ec.clear();
618#else
619  ec = std::make_error_code(std::errc::not_supported);
620#endif
621}
622
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
638#if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
639  if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)})
640    {
641      p.assign(cwd.get());
642      ec.clear();
643    }
644  else
645    ec.assign(errno, std::generic_category());
646#else
647#ifdef _PC_PATH_MAX
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;
656#elif defined(PATH_MAX)
657  size_t size = PATH_MAX;
658#else
659  size_t size = 1024;
660#endif
661  for (char_ptr buf; p.empty(); size *= 2)
662    {
663      using char_type = fs::path::value_type;
664      buf.reset((char_type*)malloc(size * sizeof(char_type)));
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
686  ec = std::make_error_code(std::errc::not_supported);
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
704  if (posix::chdir(p.c_str()))
705    ec.assign(errno, std::generic_category());
706  else
707    ec.clear();
708#else
709  ec = std::make_error_code(std::errc::not_supported);
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);
718  if (ec)
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
728  int err = 0;
729  file_status s1, s2;
730  stat_type st1, st2;
731  if (posix::stat(p1.c_str(), &st1) == 0)
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
738  if (posix::stat(p2.c_str(), &st2) == 0)
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))
746    {
747      if (is_other(s1) && is_other(s2))
748	{
749	  ec = std::make_error_code(std::errc::not_supported);
750	  return false;
751	}
752      ec.clear();
753      if (is_other(s1) || is_other(s2))
754	return false;
755      return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
756    }
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;
764#else
765  ec = std::make_error_code(std::errc::not_supported);
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>
783    inline T
784    do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
785    {
786#ifdef _GLIBCXX_HAVE_SYS_STAT_H
787      stat_type st;
788      if (posix::stat(p.c_str(), &st))
789	{
790	  ec.assign(errno, std::generic_category());
791	  return deflt;
792	}
793      ec.clear();
794      return f(st);
795#else
796      ec = std::make_error_code(std::errc::not_supported);
797      return deflt;
798#endif
799    }
800}
801
802std::uintmax_t
803fs::file_size(const path& p, error_code& ec) noexcept
804{
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;
810    uintmax_t size;
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
820	ec = std::make_error_code(std::errc::not_supported);
821    }
822  return -1;
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{
838  return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink),
839		 static_cast<uintmax_t>(-1));
840}
841
842bool
843fs::is_empty(const path& p)
844{
845  error_code ec;
846  bool e = is_empty(p, ec);
847  if (ec)
848    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty",
849					     p, ec));
850  return e;
851}
852
853bool
854fs::is_empty(const path& p, error_code& ec) noexcept
855{
856  auto s = status(p, ec);
857  if (ec)
858    return false;
859  bool empty = fs::is_directory(s)
860    ? fs::directory_iterator(p, ec) == fs::directory_iterator()
861    : fs::file_size(p, ec) == 0;
862  return ec ? false : empty;
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{
878  return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); },
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);
897#if _GLIBCXX_USE_UTIMENSAT
898  auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s);
899  if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9.
900    {
901      --s;
902      ns += chrono::seconds(1);
903    }
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))
910    ec.assign(errno, std::generic_category());
911  else
912    ec.clear();
913#elif _GLIBCXX_USE_UTIME && _GLIBCXX_HAVE_SYS_STAT_H
914  posix::utimbuf times;
915  times.modtime = s.count();
916  times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
917			 times.modtime);
918  if (posix::utime(p.c_str(), &times))
919    ec.assign(errno, std::generic_category());
920  else
921    ec.clear();
922#else
923  ec = std::make_error_code(std::errc::not_supported);
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
936void
937fs::permissions(const path& p, perms prms, error_code& ec) noexcept
938{
939  const bool add = is_set(prms, perms::add_perms);
940  const bool remove = is_set(prms, perms::remove_perms);
941  const bool nofollow = is_set(prms, perms::symlink_nofollow);
942  if (add && remove)
943    {
944      ec = std::make_error_code(std::errc::invalid_argument);
945      return;
946    }
947
948  prms &= perms::mask;
949
950  file_status st;
951  if (add || remove || nofollow)
952    {
953      st = nofollow ? symlink_status(p, ec) : status(p, ec);
954      if (ec)
955	return;
956      auto curr = st.permissions();
957      if (add)
958	prms |= curr;
959      else if (remove)
960	prms = curr & ~prms;
961    }
962
963  int err = 0;
964#if _GLIBCXX_USE_FCHMODAT
965  const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0;
966  if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag))
967    err = errno;
968#else
969  if (nofollow && is_symlink(st))
970    ec = std::make_error_code(std::errc::operation_not_supported);
971  else if (posix::chmod(p.c_str(), static_cast<mode_t>(prms)))
972    err = errno;
973#endif
974
975  if (err)
976    ec.assign(err, std::generic_category());
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
991fs::path fs::read_symlink(const path& p [[gnu::unused]], error_code& ec)
992{
993  path result;
994#if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H)
995  stat_type st;
996  if (::lstat(p.c_str(), &st))
997    {
998      ec.assign(errno, std::generic_category());
999      return result;
1000    }
1001  std::string buf(st.st_size ? st.st_size + 1 : 128, '\0');
1002  do
1003    {
1004      ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size());
1005      if (len == -1)
1006	{
1007	  ec.assign(errno, std::generic_category());
1008	  return result;
1009	}
1010      else if (len == (ssize_t)buf.size())
1011	{
1012	  if (buf.size() > 4096)
1013	    {
1014	      ec.assign(ENAMETOOLONG, std::generic_category());
1015	      return result;
1016	    }
1017	  buf.resize(buf.size() * 2);
1018	}
1019      else
1020	{
1021	  buf.resize(len);
1022	  result.assign(buf);
1023	  ec.clear();
1024	  break;
1025	}
1026    }
1027  while (true);
1028#else
1029  ec = std::make_error_code(std::errc::not_supported);
1030#endif
1031  return result;
1032}
1033
1034
1035bool
1036fs::remove(const path& p)
1037{
1038  error_code ec;
1039  bool result = fs::remove(p, ec);
1040  if (ec)
1041    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1042  return result;
1043}
1044
1045bool
1046fs::remove(const path& p, error_code& ec) noexcept
1047{
1048#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1049  auto st = symlink_status(p, ec);
1050  if (exists(st))
1051    {
1052      if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str()))
1053	  || DeleteFileW(p.c_str()))
1054	{
1055	  ec.clear();
1056	  return true;
1057	}
1058      else if (!ec)
1059	ec.assign((int)GetLastError(), generic_category());
1060    }
1061  else if (status_known(st))
1062    ec.clear();
1063#else
1064  if (::remove(p.c_str()) == 0)
1065    {
1066      ec.clear();
1067      return true;
1068    }
1069  else if (errno == ENOENT)
1070    ec.clear();
1071  else
1072    ec.assign(errno, std::generic_category());
1073#endif
1074  return false;
1075}
1076
1077
1078std::uintmax_t
1079fs::remove_all(const path& p)
1080{
1081  error_code ec;
1082  const auto result = remove_all(p, ec);
1083  if (ec)
1084    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1085  return result;
1086}
1087
1088std::uintmax_t
1089fs::remove_all(const path& p, error_code& ec) noexcept
1090{
1091  const auto s = symlink_status(p, ec);
1092  if (!status_known(s))
1093    return -1;
1094
1095  ec.clear();
1096  if (s.type() == file_type::not_found)
1097    return 0;
1098
1099  uintmax_t count = 0;
1100  if (s.type() == file_type::directory)
1101    {
1102      directory_iterator d(p, ec), end;
1103      while (!ec && d != end)
1104	{
1105	  const auto removed = fs::remove_all(d->path(), ec);
1106	  if (removed == numeric_limits<uintmax_t>::max())
1107	    return -1;
1108	  count += removed;
1109	  d.increment(ec);
1110	  if (ec)
1111	    return -1;
1112	}
1113    }
1114
1115  if (fs::remove(p, ec))
1116    ++count;
1117  return ec ? -1 : count;
1118}
1119
1120void
1121fs::rename(const path& from, const path& to)
1122{
1123  error_code ec;
1124  rename(from, to, ec);
1125  if (ec.value())
1126    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1127}
1128
1129void
1130fs::rename(const path& from, const path& to, error_code& ec) noexcept
1131{
1132  if (posix::rename(from.c_str(), to.c_str()))
1133    ec.assign(errno, std::generic_category());
1134  else
1135    ec.clear();
1136}
1137
1138void
1139fs::resize_file(const path& p, uintmax_t size)
1140{
1141  error_code ec;
1142  resize_file(p, size, ec);
1143  if (ec.value())
1144    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1145}
1146
1147void
1148fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1149{
1150#ifdef _GLIBCXX_HAVE_UNISTD_H
1151  if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
1152    ec.assign(EINVAL, std::generic_category());
1153  else if (posix::truncate(p.c_str(), size))
1154    ec.assign(errno, std::generic_category());
1155  else
1156    ec.clear();
1157#else
1158  ec = std::make_error_code(std::errc::not_supported);
1159#endif
1160}
1161
1162
1163fs::space_info
1164fs::space(const path& p)
1165{
1166  error_code ec;
1167  space_info s = space(p, ec);
1168  if (ec.value())
1169    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1170  return s;
1171}
1172
1173fs::space_info
1174fs::space(const path& p, error_code& ec) noexcept
1175{
1176  space_info info = {
1177    static_cast<uintmax_t>(-1),
1178    static_cast<uintmax_t>(-1),
1179    static_cast<uintmax_t>(-1)
1180  };
1181#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1182  path dir = absolute(p);
1183  dir.remove_filename();
1184  auto str = dir.c_str();
1185#else
1186  auto str = p.c_str();
1187#endif
1188  fs::do_space(str, info.capacity, info.free, info.available, ec);
1189  return info;
1190}
1191
1192#ifdef _GLIBCXX_HAVE_SYS_STAT_H
1193fs::file_status
1194fs::status(const fs::path& p, error_code& ec) noexcept
1195{
1196  file_status status;
1197  stat_type st;
1198  if (posix::stat(p.c_str(), &st))
1199    {
1200      int err = errno;
1201      ec.assign(err, std::generic_category());
1202      if (is_not_found_errno(err))
1203	status.type(file_type::not_found);
1204#ifdef EOVERFLOW
1205      else if (err == EOVERFLOW)
1206	status.type(file_type::unknown);
1207#endif
1208    }
1209  else
1210    {
1211      status = make_file_status(st);
1212      ec.clear();
1213    }
1214  return status;
1215}
1216
1217fs::file_status
1218fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1219{
1220  file_status status;
1221  stat_type st;
1222  if (posix::lstat(p.c_str(), &st))
1223    {
1224      int err = errno;
1225      ec.assign(err, std::generic_category());
1226      if (is_not_found_errno(err))
1227	status.type(file_type::not_found);
1228    }
1229  else
1230    {
1231      status = make_file_status(st);
1232      ec.clear();
1233    }
1234  return status;
1235}
1236#endif
1237
1238fs::file_status
1239fs::status(const fs::path& p)
1240{
1241  std::error_code ec;
1242  auto result = status(p, ec);
1243  if (result.type() == file_type::none)
1244    _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1245  return result;
1246}
1247
1248fs::file_status
1249fs::symlink_status(const fs::path& p)
1250{
1251  std::error_code ec;
1252  auto result = symlink_status(p, ec);
1253  if (result.type() == file_type::none)
1254    _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1255  return result;
1256}
1257
1258fs::path
1259fs::system_complete(const path& p)
1260{
1261  error_code ec;
1262  path comp = system_complete(p, ec);
1263  if (ec.value())
1264    _GLIBCXX_THROW_OR_ABORT(filesystem_error("system_complete", p, ec));
1265  return comp;
1266}
1267
1268fs::path
1269fs::system_complete(const path& p, error_code& ec)
1270{
1271  path base = current_path(ec);
1272#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1273  if (p.is_absolute() || !p.has_root_name()
1274      || p.root_name() == base.root_name())
1275    return absolute(p, base);
1276  // else TODO
1277  ec = std::make_error_code(std::errc::not_supported);
1278  return {};
1279#else
1280  if (ec.value())
1281    return {};
1282  return absolute(p, base);
1283#endif
1284}
1285
1286fs::path fs::temp_directory_path()
1287{
1288  error_code ec;
1289  path tmp = temp_directory_path(ec);
1290  if (ec.value())
1291    _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
1292  return tmp;
1293}
1294
1295fs::path fs::temp_directory_path(error_code& ec)
1296{
1297  path p;
1298#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1299  unsigned len = 1024;
1300  std::wstring buf;
1301  do
1302    {
1303      buf.resize(len);
1304      len = GetTempPathW(buf.size(), buf.data());
1305    } while (len > buf.size());
1306
1307  if (len == 0)
1308    {
1309      ec.assign((int)GetLastError(), std::system_category());
1310      return p;
1311    }
1312  buf.resize(len);
1313  p = std::move(buf);
1314#else
1315  const char* tmpdir = nullptr;
1316  const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
1317  for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
1318    tmpdir = ::getenv(*e);
1319  p = tmpdir ? tmpdir : "/tmp";
1320  auto st = status(p, ec);
1321  if (ec)
1322    p.clear();
1323  else if (!is_directory(st))
1324    {
1325      p.clear();
1326      ec = std::make_error_code(std::errc::not_a_directory);
1327    }
1328#endif
1329  return p;
1330}
1331
1332