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