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(), ×)) 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