ops.cc revision 1.1.1.3
124139Sjoerg// Filesystem operations -*- C++ -*-
224139Sjoerg
324139Sjoerg// Copyright (C) 2014-2016 Free Software Foundation, Inc.
424139Sjoerg//
524139Sjoerg// This file is part of the GNU ISO C++ Library.  This library is free
624139Sjoerg// software; you can redistribute it and/or modify it under the
724139Sjoerg// terms of the GNU General Public License as published by the
824139Sjoerg// Free Software Foundation; either version 3, or (at your option)
924139Sjoerg// any later version.
1024139Sjoerg
1124139Sjoerg// This library is distributed in the hope that it will be useful,
1289757Sdwmalone// but WITHOUT ANY WARRANTY; without even the implied warranty of
1389757Sdwmalone// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1489757Sdwmalone// GNU General Public License for more details.
1566641Simp
1666641Simp// Under Section 7 of GPL version 3, you are granted additional
1724139Sjoerg// permissions described in the GCC Runtime Library Exception, version
1824139Sjoerg// 3.1, as published by the Free Software Foundation.
1924139Sjoerg
2024139Sjoerg// You should have received a copy of the GNU General Public License and
2124139Sjoerg// a copy of the GCC Runtime Library Exception along with this program;
2224139Sjoerg// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
2324139Sjoerg// <http://www.gnu.org/licenses/>.
2424139Sjoerg
2524139Sjoerg#ifndef _GLIBCXX_USE_CXX11_ABI
2624139Sjoerg# define _GLIBCXX_USE_CXX11_ABI 1
2724139Sjoerg#endif
2824139Sjoerg
2924139Sjoerg#include <experimental/filesystem>
3024139Sjoerg#include <functional>
3124139Sjoerg#include <ostream>
3224139Sjoerg#include <stack>
3324139Sjoerg#include <ext/stdio_filebuf.h>
3424139Sjoerg#include <stdlib.h>
3524139Sjoerg#include <stdio.h>
3624139Sjoerg#include <errno.h>
3786042Sdwmalone#include <limits.h>  // PATH_MAX
3824139Sjoerg#ifdef _GLIBCXX_HAVE_UNISTD_H
3924139Sjoerg# include <unistd.h>
4024139Sjoerg# if defined(_GLIBCXX_HAVE_SYS_STAT_H) && defined(_GLIBCXX_HAVE_SYS_TYPES_H)
4124139Sjoerg#  include <sys/types.h>
4224139Sjoerg#  include <sys/stat.h>
4324139Sjoerg# endif
4424139Sjoerg#endif
4524139Sjoerg#ifdef _GLIBCXX_HAVE_FCNTL_H
4624139Sjoerg# include <fcntl.h>
4724139Sjoerg#endif
4824139Sjoerg#ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
4924139Sjoerg# include <sys/statvfs.h>
5024139Sjoerg#endif
5124139Sjoerg#ifdef _GLIBCXX_USE_SENDFILE
5224139Sjoerg# include <sys/sendfile.h>
5324139Sjoerg#endif
5424139Sjoerg#if _GLIBCXX_HAVE_UTIME_H
5524139Sjoerg# include <utime.h>
5624139Sjoerg#endif
5724139Sjoerg
5824139Sjoerg#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
5924139Sjoerg# undef utime
6024139Sjoerg# define utime _wutime
6124139Sjoerg# undef chmod
6224139Sjoerg# define chmod _wchmod
6324139Sjoerg#endif
6424139Sjoerg
6524139Sjoergnamespace fs = std::experimental::filesystem;
6624139Sjoerg
6724139Sjoergfs::path
6824139Sjoergfs::absolute(const path& p, const path& base)
6924139Sjoerg{
7024139Sjoerg  const bool has_root_dir = p.has_root_directory();
7124139Sjoerg  const bool has_root_name = p.has_root_name();
7224139Sjoerg  path abs;
7324139Sjoerg  if (has_root_dir && has_root_name)
7424139Sjoerg    abs = p;
7524139Sjoerg  else
7681187Skris    {
7781187Skris      abs = base.is_absolute() ? base : absolute(base);
7881187Skris      if (has_root_dir)
7981187Skris	abs = abs.root_name() / p;
8024139Sjoerg      else if (has_root_name)
8124139Sjoerg	abs = p.root_name() / abs.root_directory() / abs.relative_path()
8224139Sjoerg	  / p.relative_path();
8324139Sjoerg      else
8424139Sjoerg	abs = abs / p;
8524139Sjoerg    }
8624139Sjoerg  return abs;
87145073Skeramida}
8824139Sjoerg
8924139Sjoergnamespace
9024139Sjoerg{
9124139Sjoerg#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
9224139Sjoerg  inline bool is_dot(wchar_t c) { return c == L'.'; }
9324139Sjoerg#else
9424139Sjoerg  inline bool is_dot(char c) { return c == '.'; }
9524139Sjoerg#endif
9624139Sjoerg
9724139Sjoerg  inline bool is_dot(const fs::path& path)
9824139Sjoerg  {
99133817Salfred    const auto& filename = path.native();
10024139Sjoerg    return filename.size() == 1 && is_dot(filename[0]);
10124139Sjoerg  }
102131829Skeramida
10324139Sjoerg  inline bool is_dotdot(const fs::path& path)
10424139Sjoerg  {
10524139Sjoerg    const auto& filename = path.native();
10624139Sjoerg    return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]);
10724139Sjoerg  }
10824139Sjoerg
10924139Sjoerg  struct free_as_in_malloc
11024139Sjoerg  {
11124139Sjoerg    void operator()(void* p) const { ::free(p); }
11224139Sjoerg  };
11324139Sjoerg
11424139Sjoerg  using char_ptr = std::unique_ptr<char[], free_as_in_malloc>;
11524139Sjoerg}
11624139Sjoerg
11724139Sjoergfs::path
11824139Sjoergfs::canonical(const path& p, const path& base, error_code& ec)
11924139Sjoerg{
12024139Sjoerg  const path pa = absolute(p, base);
12124139Sjoerg  path result;
12224142Sjoerg
12324142Sjoerg#ifdef _GLIBCXX_USE_REALPATH
12424139Sjoerg  char_ptr buf{ nullptr };
12524139Sjoerg# if _XOPEN_VERSION < 700
12624139Sjoerg  // Not safe to call realpath(path, NULL)
12724139Sjoerg  buf.reset( (char*)::malloc(PATH_MAX) );
12824139Sjoerg# endif
12924139Sjoerg  if (char* rp = ::realpath(pa.c_str(), buf.get()))
13024139Sjoerg    {
13124139Sjoerg      if (buf == nullptr)
13224139Sjoerg	buf.reset(rp);
13324139Sjoerg      result.assign(rp);
13424139Sjoerg      ec.clear();
13524139Sjoerg      return result;
13624142Sjoerg    }
13724139Sjoerg  if (errno != ENAMETOOLONG)
13824139Sjoerg    {
13924139Sjoerg      ec.assign(errno, std::generic_category());
14024139Sjoerg      return result;
14124139Sjoerg    }
14224139Sjoerg#endif
14324139Sjoerg
14424139Sjoerg  if (!exists(pa, ec))
14524139Sjoerg    {
14624139Sjoerg      if (!ec)
14724139Sjoerg	ec = make_error_code(std::errc::no_such_file_or_directory);
14824139Sjoerg      return result;
14924139Sjoerg    }
15024139Sjoerg  // else: we know there are (currently) no unresolvable symlink loops
15124139Sjoerg
15224139Sjoerg  result = pa.root_path();
15324139Sjoerg
15424139Sjoerg  deque<path> cmpts;
15524139Sjoerg  for (auto& f : pa.relative_path())
15624139Sjoerg    cmpts.push_back(f);
15724139Sjoerg
15824139Sjoerg  int max_allowed_symlinks = 40;
15924139Sjoerg
16024139Sjoerg  while (!cmpts.empty() && !ec)
16124139Sjoerg    {
16286042Sdwmalone      path f = std::move(cmpts.front());
16324139Sjoerg      cmpts.pop_front();
16424139Sjoerg
16524139Sjoerg      if (is_dot(f))
16624139Sjoerg	{
16724139Sjoerg	  if (!is_directory(result, ec) && !ec)
16824139Sjoerg	    ec.assign(ENOTDIR, std::generic_category());
16924139Sjoerg	}
17024139Sjoerg      else if (is_dotdot(f))
17124139Sjoerg	{
17224139Sjoerg	  auto parent = result.parent_path();
17324139Sjoerg	  if (parent.empty())
17424139Sjoerg	    result = pa.root_path();
17524139Sjoerg	  else
17624139Sjoerg	    result.swap(parent);
17724139Sjoerg	}
17824139Sjoerg      else
17924139Sjoerg	{
18024139Sjoerg	  result /= f;
18124139Sjoerg
18224139Sjoerg	  if (is_symlink(result, ec))
18324139Sjoerg	    {
18424139Sjoerg	      path link = read_symlink(result, ec);
18524139Sjoerg	      if (!ec)
18624139Sjoerg		{
18724139Sjoerg		  if (--max_allowed_symlinks == 0)
18824139Sjoerg		    ec.assign(ELOOP, std::generic_category());
18924139Sjoerg		  else
19024139Sjoerg		    {
19189757Sdwmalone		      if (link.is_absolute())
19224139Sjoerg			{
19324139Sjoerg			  result = link.root_path();
19424139Sjoerg			  link = link.relative_path();
19524139Sjoerg			}
196146342Skeramida		      else
19724139Sjoerg			result.remove_filename();
198146342Skeramida
19924139Sjoerg		      cmpts.insert(cmpts.begin(), link.begin(), link.end());
20024139Sjoerg		    }
20124139Sjoerg		}
20224139Sjoerg	    }
20324139Sjoerg	}
20424139Sjoerg    }
20524139Sjoerg
20624139Sjoerg  if (ec || !exists(result, ec))
20724139Sjoerg    result.clear();
20824139Sjoerg
20924139Sjoerg  return result;
21024139Sjoerg}
21124139Sjoerg
21224139Sjoergfs::path
21324139Sjoergfs::canonical(const path& p, error_code& ec)
21424139Sjoerg{
21524139Sjoerg  path cur = current_path(ec);
21624139Sjoerg  if (ec.value())
21738090Sdes    return {};
218117709Sjulian  return canonical(p, cur, ec);
219131402Salfred}
220132005Salfred
221146342Skeramidafs::path
22224139Sjoergfs::canonical(const path& p, const path& base)
223146342Skeramida{
22424139Sjoerg  error_code ec;
22524139Sjoerg  path can = canonical(p, base, ec);
22624139Sjoerg  if (ec)
22724139Sjoerg    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot canonicalize", p, base,
22889757Sdwmalone					     ec));
22989757Sdwmalone  return can;
23024139Sjoerg}
23124139Sjoerg
23224139Sjoergvoid
23324139Sjoergfs::copy(const path& from, const path& to, copy_options options)
23424139Sjoerg{
23524139Sjoerg  error_code ec;
23624139Sjoerg  copy(from, to, options, ec);
23724139Sjoerg  if (ec.value())
23824139Sjoerg    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy", from, to, ec));
23924139Sjoerg}
24024139Sjoerg
24124139Sjoergnamespace
24224139Sjoerg{
24324139Sjoerg  template<typename Bitmask>
24424139Sjoerg    inline bool is_set(Bitmask obj, Bitmask bits)
24524139Sjoerg    {
24624139Sjoerg      return (obj & bits) != Bitmask::none;
24724139Sjoerg    }
24824139Sjoerg}
24924139Sjoerg
25038090Sdes#ifdef _GLIBCXX_HAVE_SYS_STAT_H
25124139Sjoergnamespace
25224139Sjoerg{
253117709Sjulian  typedef struct ::stat stat_type;
254146342Skeramida
25524139Sjoerg  inline fs::file_type
25624139Sjoerg  make_file_type(const stat_type& st) noexcept
25724139Sjoerg  {
25824139Sjoerg    using fs::file_type;
25924139Sjoerg#ifdef _GLIBCXX_HAVE_S_ISREG
26024139Sjoerg    if (S_ISREG(st.st_mode))
26124139Sjoerg      return file_type::regular;
26224139Sjoerg    else if (S_ISDIR(st.st_mode))
26324139Sjoerg      return file_type::directory;
26424139Sjoerg    else if (S_ISCHR(st.st_mode))
26524139Sjoerg      return file_type::character;
26624139Sjoerg    else if (S_ISBLK(st.st_mode))
26724139Sjoerg      return file_type::block;
26824139Sjoerg    else if (S_ISFIFO(st.st_mode))
26924139Sjoerg      return file_type::fifo;
27024139Sjoerg    else if (S_ISLNK(st.st_mode))
27124139Sjoerg      return file_type::symlink;
27224139Sjoerg    else if (S_ISSOCK(st.st_mode))
27324139Sjoerg      return file_type::socket;
27424139Sjoerg#endif
27524139Sjoerg    return file_type::unknown;
27624139Sjoerg
27724139Sjoerg  }
27824139Sjoerg
27924139Sjoerg  inline fs::file_status
280146342Skeramida  make_file_status(const stat_type& st) noexcept
28124139Sjoerg  {
28224139Sjoerg    return fs::file_status{
28324139Sjoerg	make_file_type(st),
28489757Sdwmalone	static_cast<fs::perms>(st.st_mode) & fs::perms::mask
28589757Sdwmalone    };
28689757Sdwmalone  }
28789757Sdwmalone
28889757Sdwmalone  inline bool
28989757Sdwmalone  is_not_found_errno(int err) noexcept
29024139Sjoerg  {
29124139Sjoerg    return err == ENOENT || err == ENOTDIR;
29224139Sjoerg  }
29324139Sjoerg
29424139Sjoerg  inline fs::file_time_type
29524139Sjoerg  file_time(const stat_type& st, std::error_code& ec) noexcept
29624139Sjoerg  {
29724139Sjoerg    using namespace std::chrono;
29824139Sjoerg#ifdef _GLIBCXX_USE_ST_MTIM
29924139Sjoerg    time_t s = st.st_mtim.tv_sec;
30024139Sjoerg    nanoseconds ns{st.st_mtim.tv_nsec};
30124139Sjoerg#else
30224139Sjoerg    time_t s = st.st_mtime;
30324139Sjoerg    nanoseconds ns{};
30424139Sjoerg#endif
30524139Sjoerg
30624139Sjoerg    if (s >= (nanoseconds::max().count() / 1e9))
30724139Sjoerg      {
30824139Sjoerg	ec = std::make_error_code(std::errc::value_too_large); // EOVERFLOW
30924139Sjoerg	return fs::file_time_type::min();
31024139Sjoerg      }
31124139Sjoerg    ec.clear();
31224139Sjoerg    return fs::file_time_type{seconds{s} + ns};
31324139Sjoerg  }
31424139Sjoerg
31524139Sjoerg  bool
31624139Sjoerg  do_copy_file(const fs::path& from, const fs::path& to,
31724139Sjoerg	       fs::copy_options option,
31824139Sjoerg	       stat_type* from_st, stat_type* to_st,
31924139Sjoerg	       std::error_code& ec) noexcept
32024139Sjoerg  {
32124139Sjoerg    stat_type st1, st2;
32224139Sjoerg    fs::file_status t, f;
32324139Sjoerg
32424139Sjoerg    if (to_st == nullptr)
32524139Sjoerg      {
32624139Sjoerg	if (::stat(to.c_str(), &st1))
32724139Sjoerg	  {
32824139Sjoerg	    int err = errno;
32924139Sjoerg	    if (!is_not_found_errno(err))
33024139Sjoerg	      {
33124139Sjoerg		ec.assign(err, std::generic_category());
33224139Sjoerg		return false;
33324139Sjoerg	      }
33489757Sdwmalone	  }
33524139Sjoerg	else
33624139Sjoerg	  to_st = &st1;
33789757Sdwmalone      }
33824139Sjoerg    else if (to_st == from_st)
33924139Sjoerg      to_st = nullptr;
34024139Sjoerg
34124139Sjoerg    if (to_st == nullptr)
34224139Sjoerg      t = fs::file_status{fs::file_type::not_found};
34324139Sjoerg    else
34424139Sjoerg      t = make_file_status(*to_st);
34524139Sjoerg
34624139Sjoerg    if (from_st == nullptr)
34724139Sjoerg      {
34824139Sjoerg	if (::stat(from.c_str(), &st2))
34924139Sjoerg	  {
35024139Sjoerg	    ec.assign(errno, std::generic_category());
35124139Sjoerg	    return false;
35224139Sjoerg	  }
35324139Sjoerg	else
35424139Sjoerg	  from_st = &st2;
35524139Sjoerg      }
35624139Sjoerg    f = make_file_status(*from_st);
35724139Sjoerg    // _GLIBCXX_RESOLVE_LIB_DEFECTS
35824139Sjoerg    // 2712. copy_file() has a number of unspecified error conditions
35924139Sjoerg    if (!is_regular_file(f))
360131616Sdes      {
361131402Salfred	ec = std::make_error_code(std::errc::not_supported);
362131402Salfred	return false;
363131402Salfred      }
364131402Salfred
365131402Salfred    using opts = fs::copy_options;
366131402Salfred
367131402Salfred    if (exists(t))
368131402Salfred      {
369131402Salfred	if (!is_regular_file(t))
370131402Salfred	  {
371131402Salfred	    ec = std::make_error_code(std::errc::not_supported);
372131402Salfred	    return false;
373131402Salfred	  }
37424139Sjoerg
37524139Sjoerg	if (to_st->st_dev == from_st->st_dev
37624139Sjoerg	    && to_st->st_ino == from_st->st_ino)
37724139Sjoerg	  {
37824139Sjoerg	    ec = std::make_error_code(std::errc::file_exists);
37924139Sjoerg	    return false;
38024139Sjoerg	  }
38124139Sjoerg
38224139Sjoerg	if (is_set(option, opts::skip_existing))
38324139Sjoerg	  {
38424139Sjoerg	    ec.clear();
38538090Sdes	    return false;
38638090Sdes	  }
38738090Sdes	else if (is_set(option, opts::update_existing))
388146342Skeramida	  {
389146342Skeramida	    const auto from_mtime = file_time(*from_st, ec);
390146342Skeramida	    if (ec)
391146342Skeramida	      return false;
392146342Skeramida	    if ((from_mtime <= file_time(*to_st, ec)) || ec)
393117709Sjulian	      return false;
394117709Sjulian	  }
395117709Sjulian	else if (!is_set(option, opts::overwrite_existing))
396146342Skeramida	  {
39724139Sjoerg	    ec = std::make_error_code(std::errc::file_exists);
398157842Sru	    return false;
399157842Sru	  }
400157866Sru	else if (!is_regular_file(t))
401157842Sru	  {
40224139Sjoerg	    ec = std::make_error_code(std::errc::not_supported);
40324139Sjoerg	    return false;
40424139Sjoerg	  }
40524139Sjoerg      }
40624139Sjoerg
40724139Sjoerg    struct CloseFD {
40824139Sjoerg      ~CloseFD() { if (fd != -1) ::close(fd); }
40924139Sjoerg      bool close() { return ::close(std::exchange(fd, -1)) == 0; }
41024139Sjoerg      int fd;
41124139Sjoerg    };
41224139Sjoerg
41324139Sjoerg    CloseFD in = { ::open(from.c_str(), O_RDONLY) };
41424139Sjoerg    if (in.fd == -1)
41524139Sjoerg      {
41624139Sjoerg	ec.assign(errno, std::generic_category());
41724139Sjoerg	return false;
41824139Sjoerg      }
41924139Sjoerg    int oflag = O_WRONLY|O_CREAT;
42024139Sjoerg    if (is_set(option, opts::overwrite_existing|opts::update_existing))
42124139Sjoerg      oflag |= O_TRUNC;
42224139Sjoerg    else
42324139Sjoerg      oflag |= O_EXCL;
42424139Sjoerg    CloseFD out = { ::open(to.c_str(), oflag, S_IWUSR) };
42524139Sjoerg    if (out.fd == -1)
42624139Sjoerg      {
42724139Sjoerg	if (errno == EEXIST && is_set(option, opts::skip_existing))
42824139Sjoerg	  ec.clear();
42924139Sjoerg	else
43024139Sjoerg	  ec.assign(errno, std::generic_category());
43124139Sjoerg	return false;
43224139Sjoerg      }
43324139Sjoerg
43424139Sjoerg#ifdef _GLIBCXX_USE_FCHMOD
43524139Sjoerg    if (::fchmod(out.fd, from_st->st_mode))
43624139Sjoerg#elif defined _GLIBCXX_USE_FCHMODAT
43724139Sjoerg    if (::fchmodat(AT_FDCWD, to.c_str(), from_st->st_mode, 0))
43824139Sjoerg#else
43924139Sjoerg    if (::chmod(to.c_str(), from_st->st_mode))
44024139Sjoerg#endif
44124139Sjoerg      {
44224139Sjoerg	ec.assign(errno, std::generic_category());
44324139Sjoerg	return false;
44424139Sjoerg      }
44524139Sjoerg
44624139Sjoerg#ifdef _GLIBCXX_USE_SENDFILE
44724139Sjoerg    off_t offset = 0;
44824139Sjoerg    const auto n = ::sendfile(out.fd, in.fd, &offset, from_st->st_size);
44924139Sjoerg    if (n < 0 && (errno == ENOSYS || errno == EINVAL))
45024139Sjoerg      {
45124139Sjoerg#endif
45224139Sjoerg	__gnu_cxx::stdio_filebuf<char> sbin(in.fd, std::ios::in);
45324139Sjoerg	__gnu_cxx::stdio_filebuf<char> sbout(out.fd, std::ios::out);
45424139Sjoerg	if (sbin.is_open())
45524139Sjoerg	  in.fd = -1;
45624139Sjoerg	if (sbout.is_open())
45724139Sjoerg	  out.fd = -1;
45824139Sjoerg	if (from_st->st_size && !(std::ostream(&sbout) << &sbin))
45924139Sjoerg	  {
46024139Sjoerg	    ec = std::make_error_code(std::errc::io_error);
46124139Sjoerg	    return false;
46224139Sjoerg	  }
46324139Sjoerg	if (!sbout.close() || !sbin.close())
46424139Sjoerg	  {
46524139Sjoerg	    ec.assign(errno, std::generic_category());
46624139Sjoerg	    return false;
46724139Sjoerg	  }
46824139Sjoerg
46924139Sjoerg	ec.clear();
47024139Sjoerg	return true;
47124139Sjoerg
47224139Sjoerg#ifdef _GLIBCXX_USE_SENDFILE
47324139Sjoerg      }
47424139Sjoerg    if (n != from_st->st_size)
47524139Sjoerg      {
47624139Sjoerg	ec.assign(errno, std::generic_category());
47724139Sjoerg	return false;
47824139Sjoerg      }
47924139Sjoerg    if (!out.close() || !in.close())
48024139Sjoerg      {
48124139Sjoerg	ec.assign(errno, std::generic_category());
48224139Sjoerg	return false;
48324139Sjoerg      }
48424139Sjoerg
48524139Sjoerg    ec.clear();
48624139Sjoerg    return true;
48724139Sjoerg#endif
48824139Sjoerg  }
48924139Sjoerg}
49024139Sjoerg#endif
49124139Sjoerg
49224139Sjoergvoid
49324139Sjoergfs::copy(const path& from, const path& to, copy_options options,
49424139Sjoerg	 error_code& ec) noexcept
49524139Sjoerg{
49624139Sjoerg  const bool skip_symlinks = is_set(options, copy_options::skip_symlinks);
49724139Sjoerg  const bool create_symlinks = is_set(options, copy_options::create_symlinks);
49824139Sjoerg  const bool copy_symlinks = is_set(options, copy_options::copy_symlinks);
49924139Sjoerg  const bool use_lstat = create_symlinks || skip_symlinks;
50024139Sjoerg
50124139Sjoerg  file_status f, t;
50224139Sjoerg  stat_type from_st, to_st;
50324139Sjoerg  // _GLIBCXX_RESOLVE_LIB_DEFECTS
50424139Sjoerg  // 2681. filesystem::copy() cannot copy symlinks
50524139Sjoerg  if (use_lstat || copy_symlinks
50624139Sjoerg      ? ::lstat(from.c_str(), &from_st)
50724139Sjoerg      : ::stat(from.c_str(), &from_st))
50824139Sjoerg    {
50924139Sjoerg      ec.assign(errno, std::generic_category());
51024139Sjoerg      return;
51124139Sjoerg    }
51224139Sjoerg  if (use_lstat
51324139Sjoerg      ? ::lstat(to.c_str(), &to_st)
51424139Sjoerg      : ::stat(to.c_str(), &to_st))
51524139Sjoerg    {
51624139Sjoerg      if (!is_not_found_errno(errno))
51724139Sjoerg	{
51824139Sjoerg	  ec.assign(errno, std::generic_category());
51924139Sjoerg	  return;
52024139Sjoerg	}
52124139Sjoerg      t = file_status{file_type::not_found};
52224139Sjoerg    }
52324139Sjoerg  else
52424139Sjoerg    t = make_file_status(to_st);
52524139Sjoerg  f = make_file_status(from_st);
52624139Sjoerg
52724139Sjoerg  if (exists(t) && !is_other(t) && !is_other(f)
52824139Sjoerg      && to_st.st_dev == from_st.st_dev && to_st.st_ino == from_st.st_ino)
52924139Sjoerg    {
53024139Sjoerg      ec = std::make_error_code(std::errc::file_exists);
53124139Sjoerg      return;
53224139Sjoerg    }
53324139Sjoerg  if (is_other(f) || is_other(t))
53424139Sjoerg    {
53524139Sjoerg      ec = std::make_error_code(std::errc::not_supported);
53624139Sjoerg      return;
53724139Sjoerg    }
53824139Sjoerg  if (is_directory(f) && is_regular_file(t))
53924139Sjoerg    {
54024139Sjoerg      ec = std::make_error_code(std::errc::is_a_directory);
54124139Sjoerg      return;
54224139Sjoerg    }
54324139Sjoerg
54424139Sjoerg  if (is_symlink(f))
54524139Sjoerg    {
54624139Sjoerg      if (skip_symlinks)
54724139Sjoerg	ec.clear();
54824139Sjoerg      else if (!exists(t) && copy_symlinks)
54924139Sjoerg	copy_symlink(from, to, ec);
55024139Sjoerg      else
55124139Sjoerg	// Not clear what should be done here.
55224139Sjoerg	// "Otherwise report an error as specified in Error reporting (7)."
55324139Sjoerg	ec = std::make_error_code(std::errc::invalid_argument);
55424139Sjoerg    }
55524139Sjoerg  else if (is_regular_file(f))
55624139Sjoerg    {
55724139Sjoerg      if (is_set(options, copy_options::directories_only))
55824139Sjoerg	ec.clear();
55924139Sjoerg      else if (create_symlinks)
56024139Sjoerg	create_symlink(from, to, ec);
56124139Sjoerg      else if (is_set(options, copy_options::create_hard_links))
56224139Sjoerg	create_hard_link(from, to, ec);
56381187Skris      else if (is_directory(t))
56424139Sjoerg	do_copy_file(from, to / from.filename(), options, &from_st, 0, ec);
56524139Sjoerg      else
56624139Sjoerg	{
56724139Sjoerg	  auto ptr = exists(t) ? &to_st : &from_st;
56824139Sjoerg	  do_copy_file(from, to, options, &from_st, ptr,  ec);
56924139Sjoerg	}
57024139Sjoerg    }
57124139Sjoerg  // _GLIBCXX_RESOLVE_LIB_DEFECTS
572131402Salfred  // 2682. filesystem::copy() won't create a symlink to a directory
573131402Salfred  else if (is_directory(f) && create_symlinks)
574131402Salfred    ec = std::make_error_code(errc::is_a_directory);
57524139Sjoerg  else if (is_directory(f) && (is_set(options, copy_options::recursive)
57624139Sjoerg			       || options == copy_options::none))
57724139Sjoerg    {
57824139Sjoerg      if (!exists(t))
579133817Salfred	if (!create_directory(to, from, ec))
58024139Sjoerg	  return;
581131829Skeramida      // set an unused bit in options to disable further recursion
582131402Salfred      if (!is_set(options, copy_options::recursive))
583131829Skeramida	options |= static_cast<copy_options>(4096);
584131829Skeramida      for (const directory_entry& x : directory_iterator(from))
58524139Sjoerg	copy(x.path(), to/x.path().filename(), options, ec);
58624139Sjoerg    }
587131402Salfred  // _GLIBCXX_RESOLVE_LIB_DEFECTS
588131402Salfred  // 2683. filesystem::copy() says "no effects"
589131402Salfred  else
590131402Salfred    ec.clear();
59124139Sjoerg}
59224139Sjoerg
59324139Sjoergbool
59424139Sjoergfs::copy_file(const path& from, const path& to, copy_options option)
59524139Sjoerg{
59624139Sjoerg  error_code ec;
59724139Sjoerg  bool result = copy_file(from, to, option, ec);
59842447Sobrien  if (ec.value())
59924139Sjoerg    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to,
60024139Sjoerg	  ec));
60124139Sjoerg  return result;
60224139Sjoerg}
60324139Sjoerg
60424139Sjoergbool
60524139Sjoergfs::copy_file(const path& from, const path& to, copy_options option,
60624139Sjoerg	      error_code& ec) noexcept
60724139Sjoerg{
60824139Sjoerg#ifdef _GLIBCXX_HAVE_SYS_STAT_H
60924139Sjoerg  return do_copy_file(from, to, option, nullptr, nullptr, ec);
61024139Sjoerg#else
61124139Sjoerg  ec = std::make_error_code(std::errc::not_supported);
61224139Sjoerg  return false;
61324139Sjoerg#endif
61424139Sjoerg}
61524139Sjoerg
61624139Sjoerg
61724139Sjoergvoid
61824139Sjoergfs::copy_symlink(const path& existing_symlink, const path& new_symlink)
61924139Sjoerg{
62024139Sjoerg  error_code ec;
62124139Sjoerg  copy_symlink(existing_symlink, new_symlink, ec);
62224139Sjoerg  if (ec.value())
62324139Sjoerg    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink",
62424139Sjoerg	  existing_symlink, new_symlink, ec));
62524139Sjoerg}
62624139Sjoerg
62724142Sjoergvoid
62824142Sjoergfs::copy_symlink(const path& existing_symlink, const path& new_symlink,
62924142Sjoerg		 error_code& ec) noexcept
63024139Sjoerg{
63124139Sjoerg  auto p = read_symlink(existing_symlink, ec);
63224139Sjoerg  if (ec.value())
63324139Sjoerg    return;
63424139Sjoerg#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
63524139Sjoerg  if (is_directory(p))
63624139Sjoerg    {
63724139Sjoerg      create_directory_symlink(p, new_symlink, ec);
63824139Sjoerg      return;
63924139Sjoerg    }
64024139Sjoerg#endif
64189757Sdwmalone  create_symlink(p, new_symlink, ec);
64224139Sjoerg}
64324139Sjoerg
64424139Sjoerg
64524139Sjoergbool
64624139Sjoergfs::create_directories(const path& p)
64724139Sjoerg{
64824139Sjoerg  error_code ec;
64924139Sjoerg  bool result = create_directories(p, ec);
65024139Sjoerg  if (ec.value())
65124139Sjoerg    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p,
65224139Sjoerg	  ec));
65324139Sjoerg  return result;
65424139Sjoerg}
65524139Sjoerg
65624139Sjoergbool
65724139Sjoergfs::create_directories(const path& p, error_code& ec) noexcept
65824139Sjoerg{
65924139Sjoerg  if (p.empty())
66024139Sjoerg    {
66124139Sjoerg      ec = std::make_error_code(errc::invalid_argument);
66224139Sjoerg      return false;
66324139Sjoerg    }
66424139Sjoerg  std::stack<path> missing;
66524139Sjoerg  path pp = p;
66689757Sdwmalone
66789757Sdwmalone  while (!pp.empty() && status(pp, ec).type() == file_type::not_found)
66889757Sdwmalone    {
66989757Sdwmalone      ec.clear();
67089757Sdwmalone      const auto& filename = pp.filename();
67189757Sdwmalone      if (!is_dot(filename) && !is_dotdot(filename))
67289757Sdwmalone	missing.push(pp);
67324139Sjoerg      pp.remove_filename();
67424139Sjoerg    }
67524139Sjoerg
67624139Sjoerg  if (ec || missing.empty())
67724139Sjoerg    return false;
67824139Sjoerg
67924139Sjoerg  do
68024139Sjoerg    {
68124139Sjoerg      const path& top = missing.top();
68224139Sjoerg      create_directory(top, ec);
68324139Sjoerg      if (ec && is_directory(top))
68424139Sjoerg	ec.clear();
68524139Sjoerg      missing.pop();
68624139Sjoerg    }
68724139Sjoerg  while (!missing.empty() && !ec);
68824139Sjoerg
68924139Sjoerg  return missing.empty();
69024142Sjoerg}
69124139Sjoerg
69224139Sjoergnamespace
69324139Sjoerg{
69424139Sjoerg  bool
69524139Sjoerg  create_dir(const fs::path& p, fs::perms perm, std::error_code& ec)
69624139Sjoerg  {
69724139Sjoerg    bool created = false;
69824139Sjoerg#ifdef _GLIBCXX_HAVE_SYS_STAT_H
69924139Sjoerg    ::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
70024139Sjoerg    if (::mkdir(p.c_str(), mode))
70124139Sjoerg      {
70224139Sjoerg	const int err = errno;
70324139Sjoerg	if (err != EEXIST || !is_directory(p))
70424139Sjoerg	  ec.assign(err, std::generic_category());
70524139Sjoerg	else
70624139Sjoerg	  ec.clear();
70724139Sjoerg      }
70824139Sjoerg    else
70924139Sjoerg      {
71024139Sjoerg	ec.clear();
71124139Sjoerg	created = true;
71224139Sjoerg      }
71324139Sjoerg#else
71489757Sdwmalone    ec = std::make_error_code(std::errc::not_supported);
71524139Sjoerg#endif
71624139Sjoerg    return created;
71724139Sjoerg  }
71881187Skris} // namespace
71981187Skris
72081187Skrisbool
72181187Skrisfs::create_directory(const path& p)
72281187Skris{
72381187Skris  error_code ec;
72481187Skris  bool result = create_directory(p, ec);
72581187Skris  if (ec.value())
72681187Skris    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
72781187Skris	  ec));
72881187Skris  return result;
72981187Skris}
73081187Skris
73181187Skrisbool
73281187Skrisfs::create_directory(const path& p, error_code& ec) noexcept
73381187Skris{
73481187Skris  return create_dir(p, perms::all, ec);
73581187Skris}
73681187Skris
73781187Skris
73881187Skrisbool
73981187Skrisfs::create_directory(const path& p, const path& attributes)
74081187Skris{
74181187Skris  error_code ec;
74281187Skris  bool result = create_directory(p, attributes, ec);
74381187Skris  if (ec.value())
74481187Skris    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
74581187Skris	  ec));
74681187Skris  return result;
74781187Skris}
74881187Skris
74981187Skrisbool
75081187Skrisfs::create_directory(const path& p, const path& attributes,
75181187Skris		     error_code& ec) noexcept
75281187Skris{
75381187Skris#ifdef _GLIBCXX_HAVE_SYS_STAT_H
75481187Skris  stat_type st;
75581187Skris  if (::stat(attributes.c_str(), &st))
75681187Skris    {
75781187Skris      ec.assign(errno, std::generic_category());
75881187Skris      return false;
75981187Skris    }
76081187Skris  return create_dir(p, static_cast<perms>(st.st_mode), ec);
76181187Skris#else
76281187Skris  ec = std::make_error_code(std::errc::not_supported);
76381187Skris  return false;
76424139Sjoerg#endif
76586042Sdwmalone}
76686042Sdwmalone
76786042Sdwmalone
76886042Sdwmalonevoid
76924139Sjoergfs::create_directory_symlink(const path& to, const path& new_symlink)
77024139Sjoerg{
77124139Sjoerg  error_code ec;
77224139Sjoerg  create_directory_symlink(to, new_symlink, ec);
77324139Sjoerg  if (ec.value())
77424139Sjoerg    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory symlink",
77524139Sjoerg	  to, new_symlink, ec));
77624139Sjoerg}
77724139Sjoerg
77886042Sdwmalonevoid
77989757Sdwmalonefs::create_directory_symlink(const path& to, const path& new_symlink,
78089757Sdwmalone			     error_code& ec) noexcept
78189757Sdwmalone{
78289757Sdwmalone#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
78389757Sdwmalone  ec = std::make_error_code(std::errc::not_supported);
78489757Sdwmalone#else
78589757Sdwmalone  create_symlink(to, new_symlink, ec);
78624139Sjoerg#endif
78724139Sjoerg}
78824142Sjoerg
78924142Sjoerg
79024142Sjoergvoid
79124142Sjoergfs::create_hard_link(const path& to, const path& new_hard_link)
79224142Sjoerg{
79324139Sjoerg  error_code ec;
79424139Sjoerg  create_hard_link(to, new_hard_link, ec);
79524139Sjoerg  if (ec.value())
79624139Sjoerg    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link",
79724139Sjoerg	  to, new_hard_link, ec));
79824139Sjoerg}
79924139Sjoerg
80024139Sjoergvoid
80124139Sjoergfs::create_hard_link(const path& to, const path& new_hard_link,
80224139Sjoerg		     error_code& ec) noexcept
80324139Sjoerg{
80424139Sjoerg#ifdef _GLIBCXX_HAVE_UNISTD_H
80524139Sjoerg  if (::link(to.c_str(), new_hard_link.c_str()))
80624139Sjoerg    ec.assign(errno, std::generic_category());
80724139Sjoerg  else
80824139Sjoerg    ec.clear();
80924139Sjoerg#else
81024139Sjoerg  ec = std::make_error_code(std::errc::not_supported);
81124139Sjoerg#endif
81224139Sjoerg}
81324139Sjoerg
81424139Sjoergvoid
81524139Sjoergfs::create_symlink(const path& to, const path& new_symlink)
81624139Sjoerg{
81724139Sjoerg  error_code ec;
81824139Sjoerg  create_symlink(to, new_symlink, ec);
81924139Sjoerg  if (ec.value())
82024139Sjoerg    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink",
82124139Sjoerg	  to, new_symlink, ec));
82224139Sjoerg}
82324139Sjoerg
82424139Sjoergvoid
82524139Sjoergfs::create_symlink(const path& to, const path& new_symlink,
82624139Sjoerg		   error_code& ec) noexcept
82724139Sjoerg{
82824139Sjoerg#ifdef _GLIBCXX_HAVE_UNISTD_H
82924139Sjoerg  if (::symlink(to.c_str(), new_symlink.c_str()))
83024139Sjoerg    ec.assign(errno, std::generic_category());
83124139Sjoerg  else
83224139Sjoerg    ec.clear();
83324139Sjoerg#else
83424139Sjoerg  ec = std::make_error_code(std::errc::not_supported);
83524139Sjoerg#endif
83624139Sjoerg}
83724139Sjoerg
83824139Sjoerg
83924139Sjoergfs::path
84024139Sjoergfs::current_path()
84124139Sjoerg{
84224139Sjoerg  error_code ec;
84324139Sjoerg  path p = current_path(ec);
84424139Sjoerg  if (ec.value())
84524139Sjoerg    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec));
84624139Sjoerg  return p;
84724139Sjoerg}
84824139Sjoerg
84924139Sjoergfs::path
85024139Sjoergfs::current_path(error_code& ec)
85124139Sjoerg{
85224139Sjoerg  path p;
85324139Sjoerg#ifdef _GLIBCXX_HAVE_UNISTD_H
85424139Sjoerg#ifdef __GLIBC__
85524139Sjoerg  if (char_ptr cwd = char_ptr{::getcwd(nullptr, 0)})
85624139Sjoerg    {
85724139Sjoerg      p.assign(cwd.get());
85824139Sjoerg      ec.clear();
85924139Sjoerg    }
86024139Sjoerg  else
86124139Sjoerg    ec.assign(errno, std::generic_category());
86224139Sjoerg#else
86324139Sjoerg  long path_max = pathconf(".", _PC_PATH_MAX);
86424139Sjoerg  size_t size;
86524139Sjoerg  if (path_max == -1)
86624139Sjoerg      size = 1024;
86724139Sjoerg  else if (path_max > 10240)
86824139Sjoerg      size = 10240;
86924139Sjoerg  else
87024139Sjoerg      size = path_max;
87124139Sjoerg  for (char_ptr buf; p.empty(); size *= 2)
87224139Sjoerg    {
87324139Sjoerg      buf.reset((char*)malloc(size));
87424139Sjoerg      if (buf)
87524139Sjoerg	{
87624139Sjoerg	  if (getcwd(buf.get(), size))
87724139Sjoerg	    {
87824139Sjoerg	      p.assign(buf.get());
87924139Sjoerg	      ec.clear();
88024139Sjoerg	    }
88124139Sjoerg	  else if (errno != ERANGE)
88224139Sjoerg	    {
88324139Sjoerg	      ec.assign(errno, std::generic_category());
88424139Sjoerg	      return {};
88524139Sjoerg	    }
88624139Sjoerg	}
88724139Sjoerg      else
88824139Sjoerg	{
88924139Sjoerg	  ec = std::make_error_code(std::errc::not_enough_memory);
89024139Sjoerg	  return {};
89189757Sdwmalone	}
89289757Sdwmalone    }
89389757Sdwmalone#endif  // __GLIBC__
89489757Sdwmalone#else   // _GLIBCXX_HAVE_UNISTD_H
89524139Sjoerg  ec = std::make_error_code(std::errc::not_supported);
89624139Sjoerg#endif
89724139Sjoerg  return p;
89824139Sjoerg}
89924139Sjoerg
90024139Sjoergvoid
90124139Sjoergfs::current_path(const path& p)
90224139Sjoerg{
90324139Sjoerg  error_code ec;
90424139Sjoerg  current_path(p, ec);
90524139Sjoerg  if (ec.value())
90624139Sjoerg    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec));
90724139Sjoerg}
90824139Sjoerg
90924139Sjoergvoid
91024139Sjoergfs::current_path(const path& p, error_code& ec) noexcept
91124139Sjoerg{
91224139Sjoerg#ifdef _GLIBCXX_HAVE_UNISTD_H
91324139Sjoerg  if (::chdir(p.c_str()))
91424139Sjoerg    ec.assign(errno, std::generic_category());
91524139Sjoerg  else
91624139Sjoerg    ec.clear();
91724139Sjoerg#else
91824139Sjoerg  ec = std::make_error_code(std::errc::not_supported);
91924139Sjoerg#endif
92024139Sjoerg}
92166641Simp
92224139Sjoergbool
92324139Sjoergfs::equivalent(const path& p1, const path& p2)
92424139Sjoerg{
92524139Sjoerg  error_code ec;
92624139Sjoerg  auto result = equivalent(p1, p2, ec);
92724139Sjoerg  if (ec)
92824139Sjoerg    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence",
92924139Sjoerg	  p1, p2, ec));
93024139Sjoerg  return result;
93124139Sjoerg}
93224139Sjoerg
93324139Sjoergbool
93424139Sjoergfs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
93524139Sjoerg{
93624139Sjoerg#ifdef _GLIBCXX_HAVE_SYS_STAT_H
93724139Sjoerg  int err = 0;
93868293Simp  file_status s1, s2;
93924139Sjoerg  stat_type st1, st2;
94024139Sjoerg  if (::stat(p1.c_str(), &st1) == 0)
94124139Sjoerg    s1 = make_file_status(st1);
94224139Sjoerg  else if (is_not_found_errno(errno))
94324139Sjoerg    s1.type(file_type::not_found);
94424139Sjoerg  else
94524139Sjoerg    err = errno;
94624139Sjoerg
94724139Sjoerg  if (::stat(p2.c_str(), &st2) == 0)
94824139Sjoerg    s2 = make_file_status(st2);
94924139Sjoerg  else if (is_not_found_errno(errno))
95024139Sjoerg    s2.type(file_type::not_found);
95124139Sjoerg  else
95224139Sjoerg    err = errno;
95324139Sjoerg
95424139Sjoerg  if (exists(s1) && exists(s2))
95524139Sjoerg    {
95624139Sjoerg      if (is_other(s1) && is_other(s2))
95724139Sjoerg	{
95838090Sdes	  ec = std::make_error_code(std::errc::not_supported);
95938090Sdes	  return false;
96038090Sdes	}
96138090Sdes      ec.clear();
96238090Sdes      if (is_other(s1) || is_other(s2))
96338090Sdes	return false;
96438090Sdes      return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
96538090Sdes    }
96624139Sjoerg  else if (!exists(s1) && !exists(s2))
96724139Sjoerg    ec = std::make_error_code(std::errc::no_such_file_or_directory);
96824139Sjoerg  else if (err)
96924139Sjoerg    ec.assign(err, std::generic_category());
97024139Sjoerg  else
97124139Sjoerg    ec.clear();
97224139Sjoerg  return false;
97324139Sjoerg#else
97424139Sjoerg  ec = std::make_error_code(std::errc::not_supported);
97524139Sjoerg#endif
97624139Sjoerg  return false;
97724139Sjoerg}
97824139Sjoerg
97924139Sjoergstd::uintmax_t
98024139Sjoergfs::file_size(const path& p)
98124139Sjoerg{
98224139Sjoerg  error_code ec;
98324139Sjoerg  auto sz = file_size(p, ec);
98424139Sjoerg  if (ec.value())
98524139Sjoerg    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec));
98624139Sjoerg  return sz;
98724139Sjoerg}
98824139Sjoerg
98924139Sjoergnamespace
99024139Sjoerg{
99124139Sjoerg  template<typename Accessor, typename T>
99224139Sjoerg    inline T
99324139Sjoerg    do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
994117709Sjulian    {
995117709Sjulian#ifdef _GLIBCXX_HAVE_SYS_STAT_H
996117709Sjulian      stat_type st;
997145073Skeramida      if (::stat(p.c_str(), &st))
998145073Skeramida	{
999145073Skeramida	  ec.assign(errno, std::generic_category());
1000145073Skeramida	  return deflt;
1001117709Sjulian	}
1002117709Sjulian      ec.clear();
1003146342Skeramida      return f(st);
1004146342Skeramida#else
1005146342Skeramida      ec = std::make_error_code(std::errc::not_supported);
1006146342Skeramida      return deflt;
1007146342Skeramida#endif
1008146342Skeramida    }
1009146342Skeramida}
1010146342Skeramida
1011146342Skeramidastd::uintmax_t
1012131402Salfredfs::file_size(const path& p, error_code& ec) noexcept
1013131402Salfred{
1014131402Salfred  struct S
1015131402Salfred  {
1016131402Salfred    S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { }
1017131402Salfred    S() : type(file_type::not_found) { }
1018131402Salfred    file_type type;
1019131402Salfred    size_t size;
1020132005Salfred  };
1021132005Salfred  auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{});
1022132005Salfred  if (s.type == file_type::regular)
102324139Sjoerg    return s.size;
102424139Sjoerg  if (!ec)
102524139Sjoerg    {
102624139Sjoerg      if (s.type == file_type::directory)
102724139Sjoerg	ec = std::make_error_code(std::errc::is_a_directory);
102824139Sjoerg      else
102924139Sjoerg	ec = std::make_error_code(std::errc::not_supported);
103024139Sjoerg    }
103124139Sjoerg  return -1;
103224139Sjoerg}
103324139Sjoerg
103424139Sjoergstd::uintmax_t
103524139Sjoergfs::hard_link_count(const path& p)
103624139Sjoerg{
103724139Sjoerg  error_code ec;
103824139Sjoerg  auto count = hard_link_count(p, ec);
103924139Sjoerg  if (ec.value())
104024139Sjoerg    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec));
104124139Sjoerg  return count;
104224139Sjoerg}
104324139Sjoerg
104424139Sjoergstd::uintmax_t
104524139Sjoergfs::hard_link_count(const path& p, error_code& ec) noexcept
104624139Sjoerg{
104724139Sjoerg  return do_stat(p, ec, std::mem_fn(&stat::st_nlink),
104824139Sjoerg		 static_cast<uintmax_t>(-1));
104924139Sjoerg}
105024139Sjoerg
105124139Sjoergbool
105224139Sjoergfs::is_empty(const path& p)
105324139Sjoerg{
105424139Sjoerg  error_code ec;
105524139Sjoerg  bool e = is_empty(p, ec);
105624139Sjoerg  if (ec)
105724139Sjoerg    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty",
105824139Sjoerg					     p, ec));
105924139Sjoerg  return e;
106024139Sjoerg}
106189757Sdwmalone
106289757Sdwmalonebool
106389757Sdwmalonefs::is_empty(const path& p, error_code& ec) noexcept
106424139Sjoerg{
106524139Sjoerg  auto s = status(p, ec);
106624139Sjoerg  if (ec)
106724139Sjoerg    return false;
106824139Sjoerg  bool empty = fs::is_directory(s)
106924139Sjoerg    ? fs::directory_iterator(p, ec) == fs::directory_iterator()
107024139Sjoerg    : fs::file_size(p, ec) == 0;
107124139Sjoerg  return ec ? false : empty;
107224139Sjoerg}
107324139Sjoerg
107424139Sjoergfs::file_time_type
107524139Sjoergfs::last_write_time(const path& p)
107624139Sjoerg{
107724139Sjoerg  error_code ec;
107824139Sjoerg  auto t = last_write_time(p, ec);
107924139Sjoerg  if (ec.value())
108024142Sjoerg    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec));
108124139Sjoerg  return t;
108224139Sjoerg}
108324139Sjoerg
108424139Sjoergfs::file_time_type
108524139Sjoergfs::last_write_time(const path& p, error_code& ec) noexcept
108624139Sjoerg{
108724139Sjoerg  return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); },
108824139Sjoerg		 file_time_type::min());
108924139Sjoerg}
109024139Sjoerg
109124139Sjoergvoid
109224139Sjoergfs::last_write_time(const path& p, file_time_type new_time)
109381187Skris{
109424139Sjoerg  error_code ec;
109524139Sjoerg  last_write_time(p, new_time, ec);
109624139Sjoerg  if (ec.value())
109724139Sjoerg    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec));
109824139Sjoerg}
109924139Sjoerg
110024139Sjoergvoid
110181187Skrisfs::last_write_time(const path& p __attribute__((__unused__)),
110224139Sjoerg		    file_time_type new_time, error_code& ec) noexcept
110324139Sjoerg{
110424139Sjoerg  auto d = new_time.time_since_epoch();
110524139Sjoerg  auto s = chrono::duration_cast<chrono::seconds>(d);
110624139Sjoerg#if _GLIBCXX_USE_UTIMENSAT
110724139Sjoerg  auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s);
110824139Sjoerg  if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9.
110924139Sjoerg    {
111081187Skris      --s;
111124139Sjoerg      ns += chrono::seconds(1);
111224139Sjoerg    }
111324139Sjoerg  struct ::timespec ts[2];
111424139Sjoerg  ts[0].tv_sec = 0;
111524139Sjoerg  ts[0].tv_nsec = UTIME_OMIT;
111624139Sjoerg  ts[1].tv_sec = static_cast<std::time_t>(s.count());
111724139Sjoerg  ts[1].tv_nsec = static_cast<long>(ns.count());
111824139Sjoerg  if (::utimensat(AT_FDCWD, p.c_str(), ts, 0))
111924139Sjoerg    ec.assign(errno, std::generic_category());
112024139Sjoerg  else
112124139Sjoerg    ec.clear();
112224139Sjoerg#elif _GLIBCXX_HAVE_UTIME_H
112324139Sjoerg  ::utimbuf times;
112424139Sjoerg  times.modtime = s.count();
112524139Sjoerg  times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
112624139Sjoerg			 times.modtime);
112724139Sjoerg  if (::utime(p.c_str(), &times))
112824139Sjoerg    ec.assign(errno, std::generic_category());
112924139Sjoerg  else
113024139Sjoerg    ec.clear();
1131#else
1132  ec = std::make_error_code(std::errc::not_supported);
1133#endif
1134}
1135
1136void
1137fs::permissions(const path& p, perms prms)
1138{
1139  error_code ec;
1140  permissions(p, prms, ec);
1141  if (ec.value())
1142    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec));
1143}
1144
1145void
1146fs::permissions(const path& p, perms prms, error_code& ec) noexcept
1147{
1148  const bool add = is_set(prms, perms::add_perms);
1149  const bool remove = is_set(prms, perms::remove_perms);
1150  const bool nofollow = is_set(prms, perms::symlink_nofollow);
1151  if (add && remove)
1152    {
1153      ec = std::make_error_code(std::errc::invalid_argument);
1154      return;
1155    }
1156
1157  prms &= perms::mask;
1158
1159  file_status st;
1160  if (add || remove || nofollow)
1161    {
1162      st = nofollow ? symlink_status(p, ec) : status(p, ec);
1163      if (ec)
1164	return;
1165      auto curr = st.permissions();
1166      if (add)
1167	prms |= curr;
1168      else if (remove)
1169	prms = curr & ~prms;
1170    }
1171
1172  int err = 0;
1173#if _GLIBCXX_USE_FCHMODAT
1174  const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0;
1175  if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag))
1176    err = errno;
1177#else
1178  if (nofollow && is_symlink(st))
1179    ec = std::make_error_code(std::errc::operation_not_supported);
1180  else if (::chmod(p.c_str(), static_cast<mode_t>(prms)))
1181    err = errno;
1182#endif
1183
1184  if (err)
1185    ec.assign(err, std::generic_category());
1186  else
1187    ec.clear();
1188}
1189
1190fs::path
1191fs::read_symlink(const path& p)
1192{
1193  error_code ec;
1194  path tgt = read_symlink(p, ec);
1195  if (ec.value())
1196    _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec));
1197  return tgt;
1198}
1199
1200fs::path fs::read_symlink(const path& p, error_code& ec)
1201{
1202#ifdef _GLIBCXX_HAVE_SYS_STAT_H
1203  stat_type st;
1204  if (::lstat(p.c_str(), &st))
1205    {
1206      ec.assign(errno, std::generic_category());
1207      return {};
1208    }
1209  std::string buf(st.st_size, '\0');
1210  ssize_t len = ::readlink(p.c_str(), &buf.front(), buf.size());
1211  if (len == -1)
1212    {
1213      ec.assign(errno, std::generic_category());
1214      return {};
1215    }
1216  ec.clear();
1217  return path{buf.data(), buf.data()+len};
1218#else
1219  ec = std::make_error_code(std::errc::not_supported);
1220  return {};
1221#endif
1222}
1223
1224
1225bool
1226fs::remove(const path& p)
1227{
1228  error_code ec;
1229  bool result = fs::remove(p, ec);
1230  if (ec.value())
1231    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1232  return result;
1233}
1234
1235bool
1236fs::remove(const path& p, error_code& ec) noexcept
1237{
1238  if (exists(symlink_status(p, ec)))
1239    {
1240      if (::remove(p.c_str()) == 0)
1241	{
1242	  ec.clear();
1243	  return true;
1244	}
1245      else
1246	ec.assign(errno, std::generic_category());
1247    }
1248  return false;
1249}
1250
1251
1252std::uintmax_t
1253fs::remove_all(const path& p)
1254{
1255  error_code ec;
1256  bool result = remove_all(p, ec);
1257  if (ec.value())
1258    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1259  return result;
1260}
1261
1262std::uintmax_t
1263fs::remove_all(const path& p, error_code& ec) noexcept
1264{
1265  auto fs = symlink_status(p, ec);
1266  uintmax_t count = 0;
1267  if (ec.value() == 0 && fs.type() == file_type::directory)
1268    for (directory_iterator d(p, ec), end; ec.value() == 0 && d != end; ++d)
1269      count += fs::remove_all(d->path(), ec);
1270  if (ec.value())
1271    return -1;
1272  return fs::remove(p, ec) ? ++count : -1;  // fs:remove() calls ec.clear()
1273}
1274
1275void
1276fs::rename(const path& from, const path& to)
1277{
1278  error_code ec;
1279  rename(from, to, ec);
1280  if (ec.value())
1281    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1282}
1283
1284void
1285fs::rename(const path& from, const path& to, error_code& ec) noexcept
1286{
1287  if (::rename(from.c_str(), to.c_str()))
1288    ec.assign(errno, std::generic_category());
1289  else
1290    ec.clear();
1291}
1292
1293void
1294fs::resize_file(const path& p, uintmax_t size)
1295{
1296  error_code ec;
1297  resize_file(p, size, ec);
1298  if (ec.value())
1299    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1300}
1301
1302void
1303fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1304{
1305#ifdef _GLIBCXX_HAVE_UNISTD_H
1306  if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
1307    ec.assign(EINVAL, std::generic_category());
1308  else if (::truncate(p.c_str(), size))
1309    ec.assign(errno, std::generic_category());
1310  else
1311    ec.clear();
1312#else
1313  ec = std::make_error_code(std::errc::not_supported);
1314#endif
1315}
1316
1317
1318fs::space_info
1319fs::space(const path& p)
1320{
1321  error_code ec;
1322  space_info s = space(p, ec);
1323  if (ec.value())
1324    _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1325  return s;
1326}
1327
1328fs::space_info
1329fs::space(const path& p, error_code& ec) noexcept
1330{
1331  space_info info = {
1332    static_cast<uintmax_t>(-1),
1333    static_cast<uintmax_t>(-1),
1334    static_cast<uintmax_t>(-1)
1335  };
1336#ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
1337  struct ::statvfs f;
1338  if (::statvfs(p.c_str(), &f))
1339      ec.assign(errno, std::generic_category());
1340  else
1341    {
1342      info = space_info{
1343	f.f_blocks * f.f_frsize,
1344	f.f_bfree * f.f_frsize,
1345	f.f_bavail * f.f_frsize
1346      };
1347      ec.clear();
1348    }
1349#else
1350  ec = std::make_error_code(std::errc::not_supported);
1351#endif
1352  return info;
1353}
1354
1355#ifdef _GLIBCXX_HAVE_SYS_STAT_H
1356fs::file_status
1357fs::status(const fs::path& p, error_code& ec) noexcept
1358{
1359  file_status status;
1360  stat_type st;
1361  if (::stat(p.c_str(), &st))
1362    {
1363      int err = errno;
1364      ec.assign(err, std::generic_category());
1365      if (is_not_found_errno(err))
1366	status.type(file_type::not_found);
1367#ifdef EOVERFLOW
1368      else if (err == EOVERFLOW)
1369	status.type(file_type::unknown);
1370#endif
1371    }
1372  else
1373    {
1374      status = make_file_status(st);
1375      ec.clear();
1376    }
1377  return status;
1378}
1379
1380fs::file_status
1381fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1382{
1383  file_status status;
1384  stat_type st;
1385  if (::lstat(p.c_str(), &st))
1386    {
1387      int err = errno;
1388      ec.assign(err, std::generic_category());
1389      if (is_not_found_errno(err))
1390	status.type(file_type::not_found);
1391    }
1392  else
1393    {
1394      status = make_file_status(st);
1395      ec.clear();
1396    }
1397  return status;
1398}
1399#endif
1400
1401fs::file_status
1402fs::status(const fs::path& p)
1403{
1404  std::error_code ec;
1405  auto result = status(p, ec);
1406  if (result.type() == file_type::none)
1407    _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1408  return result;
1409}
1410
1411fs::file_status
1412fs::symlink_status(const fs::path& p)
1413{
1414  std::error_code ec;
1415  auto result = symlink_status(p, ec);
1416  if (result.type() == file_type::none)
1417    _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1418  return result;
1419}
1420
1421fs::path
1422fs::system_complete(const path& p)
1423{
1424  error_code ec;
1425  path comp = system_complete(p, ec);
1426  if (ec.value())
1427    _GLIBCXX_THROW_OR_ABORT(filesystem_error("system_complete", p, ec));
1428  return comp;
1429}
1430
1431fs::path
1432fs::system_complete(const path& p, error_code& ec)
1433{
1434  path base = current_path(ec);
1435#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1436  if (p.is_absolute() || !p.has_root_name()
1437      || p.root_name() == base.root_name())
1438    return absolute(p, base);
1439  // else TODO
1440  ec = std::make_error_code(std::errc::not_supported);
1441  return {};
1442#else
1443  if (ec.value())
1444    return {};
1445  return absolute(p, base);
1446#endif
1447}
1448
1449fs::path fs::temp_directory_path()
1450{
1451  error_code ec;
1452  path tmp = temp_directory_path(ec);
1453  if (ec.value())
1454    _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
1455  return tmp;
1456}
1457
1458fs::path fs::temp_directory_path(error_code& ec)
1459{
1460#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1461  ec = std::make_error_code(std::errc::not_supported);
1462  return {}; // TODO
1463#else
1464  const char* tmpdir = nullptr;
1465  const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
1466  for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
1467    tmpdir = ::getenv(*e);
1468  path p = tmpdir ? tmpdir : "/tmp";
1469  auto st = status(p, ec);
1470  if (!ec)
1471    {
1472      if (is_directory(st))
1473	{
1474	  ec.clear();
1475	  return p;
1476	}
1477      else
1478	ec = std::make_error_code(std::errc::not_a_directory);
1479    }
1480  return {};
1481#endif
1482}
1483
1484