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  
62  namespace fs = std::experimental::filesystem;
63  namespace posix = std::filesystem::__gnu_posix;
64  
65  fs::path
absolute(const path & p,const path & base)66  fs::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  
87  namespace
88  {
89  #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
is_dot(wchar_t c)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  
is_dot(const fs::path & path)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  
is_dotdot(const fs::path & path)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    {
operator ()__anon536881820111::free_as_in_malloc109      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  
115  fs::path
canonical(const path & p,const path & base,error_code & ec)116  fs::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  
211  fs::path
canonical(const path & p,error_code & ec)212  fs::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  
220  fs::path
canonical(const path & p,const path & base)221  fs::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  
231  void
copy(const path & from,const path & to,copy_options options)232  fs::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  
240  namespace
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  
253  void
copy(const path & from,const path & to,copy_options options,error_code & ec)254  fs::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  
356  bool
copy_file(const path & from,const path & to,copy_options option)357  fs::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  
367  bool
copy_file(const path & from,const path & to,copy_options options,error_code & ec)368  fs::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  
381  void
copy_symlink(const path & existing_symlink,const path & new_symlink)382  fs::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  
391  void
copy_symlink(const path & existing_symlink,const path & new_symlink,error_code & ec)392  fs::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  
409  bool
create_directories(const path & p)410  fs::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  
420  bool
create_directories(const path & p,error_code & ec)421  fs::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 = 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  
474  namespace
475  {
476    bool
create_dir(const fs::path & p,fs::perms perm,std::error_code & ec)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  
500  bool
create_directory(const path & p)501  fs::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  
511  bool
create_directory(const path & p,error_code & ec)512  fs::create_directory(const path& p, error_code& ec) noexcept
513  {
514    return create_dir(p, perms::all, ec);
515  }
516  
517  
518  bool
create_directory(const path & p,const path & attributes)519  fs::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  
529  bool
create_directory(const path & p,const path & attributes,error_code & ec)530  fs::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  
548  void
create_directory_symlink(const path & to,const path & new_symlink)549  fs::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  
558  void
create_directory_symlink(const path & to,const path & new_symlink,error_code & ec)559  fs::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  
570  void
create_hard_link(const path & to,const path & new_hard_link)571  fs::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  
580  void
create_hard_link(const path & to,const path & new_hard_link,error_code & ec)581  fs::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(), system_category());
594  #else
595    ec = std::make_error_code(std::errc::not_supported);
596  #endif
597  }
598  
599  void
create_symlink(const path & to,const path & new_symlink)600  fs::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  
609  void
create_symlink(const path & to,const path & new_symlink,error_code & ec)610  fs::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  
623  fs::path
current_path()624  fs::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  
633  fs::path
current_path(error_code & ec)634  fs::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  
691  void
current_path(const path & p)692  fs::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  
700  void
current_path(const path & p,error_code & ec)701  fs::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  
713  bool
equivalent(const path & p1,const path & p2)714  fs::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  
724  bool
equivalent(const path & p1,const path & p2,error_code & ec)725  fs::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  
770  std::uintmax_t
file_size(const path & p)771  fs::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  
780  namespace
781  {
782    template<typename Accessor, typename T>
783      inline T
do_stat(const fs::path & p,std::error_code & ec,Accessor f,T deflt)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  
802  std::uintmax_t
file_size(const path & p,error_code & ec)803  fs::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  
825  std::uintmax_t
hard_link_count(const path & p)826  fs::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  
835  std::uintmax_t
hard_link_count(const path & p,error_code & ec)836  fs::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  
842  bool
is_empty(const path & p)843  fs::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  
853  bool
is_empty(const path & p,error_code & ec)854  fs::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  
865  fs::file_time_type
last_write_time(const path & p)866  fs::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  
875  fs::file_time_type
last_write_time(const path & p,error_code & ec)876  fs::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  
882  void
last_write_time(const path & p,file_time_type new_time)883  fs::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  
891  void
last_write_time(const path & p,file_time_type new_time,error_code & ec)892  fs::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  
927  void
permissions(const path & p,perms prms)928  fs::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  
936  void
permissions(const path & p,perms prms,error_code & ec)937  fs::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  
981  fs::path
read_symlink(const path & p)982  fs::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  
read_symlink(const path & p,error_code & ec)991  fs::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 (posix::lstat(p.c_str(), &st))
997      {
998        ec.assign(errno, std::generic_category());
999        return result;
1000      }
1001    else if (!fs::is_symlink(make_file_status(st)))
1002      {
1003        ec.assign(EINVAL, std::generic_category());
1004        return result;
1005      }
1006  
1007    std::string buf(st.st_size ? st.st_size + 1 : 128, '\0');
1008    do
1009      {
1010        ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size());
1011        if (len == -1)
1012  	{
1013  	  ec.assign(errno, std::generic_category());
1014  	  return result;
1015  	}
1016        else if (len == (ssize_t)buf.size())
1017  	{
1018  	  if (buf.size() > 4096)
1019  	    {
1020  	      ec.assign(ENAMETOOLONG, std::generic_category());
1021  	      return result;
1022  	    }
1023  	  buf.resize(buf.size() * 2);
1024  	}
1025        else
1026  	{
1027  	  buf.resize(len);
1028  	  result.assign(buf);
1029  	  ec.clear();
1030  	  break;
1031  	}
1032      }
1033    while (true);
1034  #else
1035    ec = std::make_error_code(std::errc::not_supported);
1036  #endif
1037    return result;
1038  }
1039  
1040  
1041  bool
remove(const path & p)1042  fs::remove(const path& p)
1043  {
1044    error_code ec;
1045    bool result = fs::remove(p, ec);
1046    if (ec)
1047      _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1048    return result;
1049  }
1050  
1051  bool
remove(const path & p,error_code & ec)1052  fs::remove(const path& p, error_code& ec) noexcept
1053  {
1054  #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1055    auto st = symlink_status(p, ec);
1056    if (exists(st))
1057      {
1058        if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str()))
1059  	  || DeleteFileW(p.c_str()))
1060  	{
1061  	  ec.clear();
1062  	  return true;
1063  	}
1064        else if (!ec)
1065  	ec.assign((int)GetLastError(), system_category());
1066      }
1067    else if (status_known(st))
1068      ec.clear();
1069  #else
1070    if (::remove(p.c_str()) == 0)
1071      {
1072        ec.clear();
1073        return true;
1074      }
1075    else if (errno == ENOENT)
1076      ec.clear();
1077    else
1078      ec.assign(errno, std::generic_category());
1079  #endif
1080    return false;
1081  }
1082  
1083  
1084  std::uintmax_t
remove_all(const path & p)1085  fs::remove_all(const path& p)
1086  {
1087    error_code ec;
1088    const auto result = remove_all(p, ec);
1089    if (ec)
1090      _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1091    return result;
1092  }
1093  
1094  std::uintmax_t
remove_all(const path & p,error_code & ec)1095  fs::remove_all(const path& p, error_code& ec) noexcept
1096  {
1097    const auto s = symlink_status(p, ec);
1098    if (!status_known(s))
1099      return -1;
1100  
1101    ec.clear();
1102    if (s.type() == file_type::not_found)
1103      return 0;
1104  
1105    uintmax_t count = 0;
1106    if (s.type() == file_type::directory)
1107      {
1108        directory_iterator d(p, ec), end;
1109        while (!ec && d != end)
1110  	{
1111  	  const auto removed = fs::remove_all(d->path(), ec);
1112  	  if (removed == numeric_limits<uintmax_t>::max())
1113  	    return -1;
1114  	  count += removed;
1115  	  d.increment(ec);
1116  	  if (ec)
1117  	    return -1;
1118  	}
1119      }
1120  
1121    if (fs::remove(p, ec))
1122      ++count;
1123    return ec ? -1 : count;
1124  }
1125  
1126  void
rename(const path & from,const path & to)1127  fs::rename(const path& from, const path& to)
1128  {
1129    error_code ec;
1130    rename(from, to, ec);
1131    if (ec.value())
1132      _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1133  }
1134  
1135  void
rename(const path & from,const path & to,error_code & ec)1136  fs::rename(const path& from, const path& to, error_code& ec) noexcept
1137  {
1138    if (posix::rename(from.c_str(), to.c_str()))
1139      ec.assign(errno, std::generic_category());
1140    else
1141      ec.clear();
1142  }
1143  
1144  void
resize_file(const path & p,uintmax_t size)1145  fs::resize_file(const path& p, uintmax_t size)
1146  {
1147    error_code ec;
1148    resize_file(p, size, ec);
1149    if (ec.value())
1150      _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1151  }
1152  
1153  void
resize_file(const path & p,uintmax_t size,error_code & ec)1154  fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1155  {
1156  #ifdef _GLIBCXX_HAVE_UNISTD_H
1157    if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
1158      ec.assign(EINVAL, std::generic_category());
1159    else if (posix::truncate(p.c_str(), size))
1160      ec.assign(errno, std::generic_category());
1161    else
1162      ec.clear();
1163  #else
1164    ec = std::make_error_code(std::errc::not_supported);
1165  #endif
1166  }
1167  
1168  
1169  fs::space_info
space(const path & p)1170  fs::space(const path& p)
1171  {
1172    error_code ec;
1173    space_info s = space(p, ec);
1174    if (ec.value())
1175      _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1176    return s;
1177  }
1178  
1179  fs::space_info
space(const path & p,error_code & ec)1180  fs::space(const path& p, error_code& ec) noexcept
1181  {
1182    space_info info = {
1183      static_cast<uintmax_t>(-1),
1184      static_cast<uintmax_t>(-1),
1185      static_cast<uintmax_t>(-1)
1186    };
1187  #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1188    path dir = absolute(p);
1189    dir.remove_filename();
1190    auto str = dir.c_str();
1191  #else
1192    auto str = p.c_str();
1193  #endif
1194    fs::do_space(str, info.capacity, info.free, info.available, ec);
1195    return info;
1196  }
1197  
1198  #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1199  fs::file_status
status(const fs::path & p,error_code & ec)1200  fs::status(const fs::path& p, error_code& ec) noexcept
1201  {
1202    file_status status;
1203    stat_type st;
1204    if (posix::stat(p.c_str(), &st))
1205      {
1206        int err = errno;
1207        ec.assign(err, std::generic_category());
1208        if (is_not_found_errno(err))
1209  	status.type(file_type::not_found);
1210  #ifdef EOVERFLOW
1211        else if (err == EOVERFLOW)
1212  	status.type(file_type::unknown);
1213  #endif
1214      }
1215    else
1216      {
1217        status = make_file_status(st);
1218        ec.clear();
1219      }
1220    return status;
1221  }
1222  
1223  fs::file_status
symlink_status(const fs::path & p,std::error_code & ec)1224  fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1225  {
1226    file_status status;
1227    stat_type st;
1228    if (posix::lstat(p.c_str(), &st))
1229      {
1230        int err = errno;
1231        ec.assign(err, std::generic_category());
1232        if (is_not_found_errno(err))
1233  	status.type(file_type::not_found);
1234      }
1235    else
1236      {
1237        status = make_file_status(st);
1238        ec.clear();
1239      }
1240    return status;
1241  }
1242  #endif
1243  
1244  fs::file_status
status(const fs::path & p)1245  fs::status(const fs::path& p)
1246  {
1247    std::error_code ec;
1248    auto result = status(p, ec);
1249    if (result.type() == file_type::none)
1250      _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1251    return result;
1252  }
1253  
1254  fs::file_status
symlink_status(const fs::path & p)1255  fs::symlink_status(const fs::path& p)
1256  {
1257    std::error_code ec;
1258    auto result = symlink_status(p, ec);
1259    if (result.type() == file_type::none)
1260      _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1261    return result;
1262  }
1263  
1264  fs::path
system_complete(const path & p)1265  fs::system_complete(const path& p)
1266  {
1267    error_code ec;
1268    path comp = system_complete(p, ec);
1269    if (ec.value())
1270      _GLIBCXX_THROW_OR_ABORT(filesystem_error("system_complete", p, ec));
1271    return comp;
1272  }
1273  
1274  fs::path
system_complete(const path & p,error_code & ec)1275  fs::system_complete(const path& p, error_code& ec)
1276  {
1277    path base = current_path(ec);
1278  #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1279    if (p.is_absolute() || !p.has_root_name()
1280        || p.root_name() == base.root_name())
1281      return absolute(p, base);
1282    // else TODO
1283    ec = std::make_error_code(std::errc::not_supported);
1284    return {};
1285  #else
1286    if (ec.value())
1287      return {};
1288    return absolute(p, base);
1289  #endif
1290  }
1291  
temp_directory_path()1292  fs::path fs::temp_directory_path()
1293  {
1294    error_code ec;
1295    path tmp = temp_directory_path(ec);
1296    if (ec.value())
1297      _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
1298    return tmp;
1299  }
1300  
temp_directory_path(error_code & ec)1301  fs::path fs::temp_directory_path(error_code& ec)
1302  {
1303    path p;
1304  #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1305    unsigned len = 1024;
1306    std::wstring buf;
1307    do
1308      {
1309        buf.resize(len);
1310        len = GetTempPathW(buf.size(), buf.data());
1311      } while (len > buf.size());
1312  
1313    if (len == 0)
1314      {
1315        ec.assign((int)GetLastError(), std::system_category());
1316        return p;
1317      }
1318    buf.resize(len);
1319    p = std::move(buf);
1320  #else
1321    const char* tmpdir = nullptr;
1322    const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
1323    for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
1324      tmpdir = ::getenv(*e);
1325    p = tmpdir ? tmpdir : "/tmp";
1326    auto st = status(p, ec);
1327    if (ec)
1328      p.clear();
1329    else if (!is_directory(st))
1330      {
1331        p.clear();
1332        ec = std::make_error_code(std::errc::not_a_directory);
1333      }
1334  #endif
1335    return p;
1336  }
1337  
1338