Home | History | Annotate | Download | only in filesystem
      1 //===--------------------- filesystem/ops.cpp -----------------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is dual licensed under the MIT and the University of Illinois Open
      6 // Source Licenses. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #include "experimental/filesystem"
     11 #include "iterator"
     12 #include "fstream"
     13 #include "type_traits"
     14 #include "random"  /* for unique_path */
     15 #include "cstdlib"
     16 #include "climits"
     17 
     18 #include "filesystem_time_helper.h"
     19 
     20 #include <unistd.h>
     21 #include <sys/stat.h>
     22 #include <sys/statvfs.h>
     23 #include <fcntl.h>  /* values for fchmodat */
     24 #if !defined(UTIME_OMIT)
     25 #include <sys/time.h> // for ::utimes as used in __last_write_time
     26 #endif
     27 
     28 _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
     29 
     30 filesystem_error::~filesystem_error() {}
     31 
     32 
     33 //                       POSIX HELPERS
     34 
     35 namespace detail { namespace  {
     36 
     37 using value_type = path::value_type;
     38 using string_type = path::string_type;
     39 
     40 inline std::error_code capture_errno() {
     41   _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
     42   return std::error_code(errno, std::generic_category());
     43 }
     44 
     45 void set_or_throw(std::error_code const& m_ec, std::error_code* ec,
     46                   const char* msg, path const& p = {}, path const& p2 = {})
     47 {
     48     if (ec) {
     49         *ec = m_ec;
     50     } else {
     51         string msg_s("std::experimental::filesystem::");
     52         msg_s += msg;
     53         __throw_filesystem_error(msg_s, p, p2, m_ec);
     54     }
     55 }
     56 
     57 void set_or_throw(std::error_code* ec, const char* msg,
     58                   path const& p = {}, path const& p2 = {})
     59 {
     60     return set_or_throw(capture_errno(), ec, msg, p, p2);
     61 }
     62 
     63 perms posix_get_perms(const struct ::stat & st) noexcept {
     64     return static_cast<perms>(st.st_mode) & perms::mask;
     65 }
     66 
     67 ::mode_t posix_convert_perms(perms prms) {
     68     return static_cast< ::mode_t>(prms & perms::mask);
     69 }
     70 
     71 file_status create_file_status(std::error_code& m_ec, path const& p,
     72                                struct ::stat& path_stat,
     73                                std::error_code* ec)
     74 {
     75     if (ec) *ec = m_ec;
     76     if (m_ec && (m_ec.value() == ENOENT || m_ec.value() == ENOTDIR)) {
     77         return file_status(file_type::not_found);
     78     }
     79     else if (m_ec) {
     80         set_or_throw(m_ec, ec, "posix_stat", p);
     81         return file_status(file_type::none);
     82     }
     83     // else
     84 
     85     file_status fs_tmp;
     86     auto const mode = path_stat.st_mode;
     87     if      (S_ISLNK(mode))  fs_tmp.type(file_type::symlink);
     88     else if (S_ISREG(mode))  fs_tmp.type(file_type::regular);
     89     else if (S_ISDIR(mode))  fs_tmp.type(file_type::directory);
     90     else if (S_ISBLK(mode))  fs_tmp.type(file_type::block);
     91     else if (S_ISCHR(mode))  fs_tmp.type(file_type::character);
     92     else if (S_ISFIFO(mode)) fs_tmp.type(file_type::fifo);
     93     else if (S_ISSOCK(mode)) fs_tmp.type(file_type::socket);
     94     else                     fs_tmp.type(file_type::unknown);
     95 
     96     fs_tmp.permissions(detail::posix_get_perms(path_stat));
     97     return fs_tmp;
     98 }
     99 
    100 file_status posix_stat(path const & p, struct ::stat& path_stat,
    101                        std::error_code* ec)
    102 {
    103     std::error_code m_ec;
    104     if (::stat(p.c_str(), &path_stat) == -1)
    105         m_ec = detail::capture_errno();
    106     return create_file_status(m_ec, p, path_stat, ec);
    107 }
    108 
    109 file_status posix_stat(path const & p, std::error_code* ec) {
    110     struct ::stat path_stat;
    111     return posix_stat(p, path_stat, ec);
    112 }
    113 
    114 file_status posix_lstat(path const & p, struct ::stat & path_stat,
    115                         std::error_code* ec)
    116 {
    117     std::error_code m_ec;
    118     if (::lstat(p.c_str(), &path_stat) == -1)
    119         m_ec = detail::capture_errno();
    120     return create_file_status(m_ec, p, path_stat, ec);
    121 }
    122 
    123 file_status posix_lstat(path const & p, std::error_code* ec) {
    124     struct ::stat path_stat;
    125     return posix_lstat(p, path_stat, ec);
    126 }
    127 
    128 bool stat_equivalent(struct ::stat& st1, struct ::stat& st2) {
    129     return (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
    130 }
    131 
    132 //                           DETAIL::MISC
    133 
    134 
    135 bool copy_file_impl(const path& from, const path& to, perms from_perms,
    136                     std::error_code *ec)
    137 {
    138     std::ifstream in(from.c_str(), std::ios::binary);
    139     std::ofstream out(to.c_str(),  std::ios::binary);
    140 
    141     if (in.good() && out.good()) {
    142         using InIt = std::istreambuf_iterator<char>;
    143         using OutIt = std::ostreambuf_iterator<char>;
    144         InIt bin(in);
    145         InIt ein;
    146         OutIt bout(out);
    147         std::copy(bin, ein, bout);
    148     }
    149     if (out.fail() || in.fail()) {
    150         set_or_throw(make_error_code(errc::operation_not_permitted),
    151                      ec, "copy_file", from, to);
    152         return false;
    153     }
    154     __permissions(to, from_perms, ec);
    155     // TODO what if permissions fails?
    156     return true;
    157 }
    158 
    159 }} // end namespace detail
    160 
    161 using detail::set_or_throw;
    162 
    163 path __canonical(path const & orig_p, const path& base, std::error_code *ec)
    164 {
    165     path p = absolute(orig_p, base);
    166     char buff[PATH_MAX + 1];
    167     char *ret;
    168     if ((ret = ::realpath(p.c_str(), buff)) == nullptr) {
    169         set_or_throw(ec, "canonical", orig_p, base);
    170         return {};
    171     }
    172     if (ec) ec->clear();
    173     return {ret};
    174 }
    175 
    176 void __copy(const path& from, const path& to, copy_options options,
    177             std::error_code *ec)
    178 {
    179     const bool sym_status = bool(options &
    180         (copy_options::create_symlinks | copy_options::skip_symlinks));
    181 
    182     const bool sym_status2 = bool(options &
    183         copy_options::copy_symlinks);
    184 
    185     std::error_code m_ec;
    186     struct ::stat f_st = {};
    187     const file_status f = sym_status || sym_status2
    188                                      ? detail::posix_lstat(from, f_st, &m_ec)
    189                                      : detail::posix_stat(from,  f_st, &m_ec);
    190     if (m_ec)
    191         return set_or_throw(m_ec, ec, "copy", from, to);
    192 
    193     struct ::stat t_st = {};
    194     const file_status t = sym_status ? detail::posix_lstat(to, t_st, &m_ec)
    195                                      : detail::posix_stat(to, t_st, &m_ec);
    196 
    197     if (not status_known(t))
    198         return set_or_throw(m_ec, ec, "copy", from, to);
    199 
    200     if (!exists(f) || is_other(f) || is_other(t)
    201         || (is_directory(f) && is_regular_file(t))
    202         || detail::stat_equivalent(f_st, t_st))
    203     {
    204         return set_or_throw(make_error_code(errc::function_not_supported),
    205                             ec, "copy", from, to);
    206     }
    207 
    208     if (ec) ec->clear();
    209 
    210     if (is_symlink(f)) {
    211         if (bool(copy_options::skip_symlinks & options)) {
    212             // do nothing
    213         } else if (not exists(t)) {
    214             __copy_symlink(from, to, ec);
    215         } else {
    216             set_or_throw(make_error_code(errc::file_exists),
    217                          ec, "copy", from, to);
    218         }
    219         return;
    220     }
    221     else if (is_regular_file(f)) {
    222         if (bool(copy_options::directories_only & options)) {
    223             // do nothing
    224         }
    225         else if (bool(copy_options::create_symlinks & options)) {
    226             __create_symlink(from, to, ec);
    227         }
    228         else if (bool(copy_options::create_hard_links & options)) {
    229             __create_hard_link(from, to, ec);
    230         }
    231         else if (is_directory(t)) {
    232             __copy_file(from, to / from.filename(), options, ec);
    233         } else {
    234             __copy_file(from, to, options, ec);
    235         }
    236         return;
    237     }
    238     else if (is_directory(f) && bool(copy_options::create_symlinks & options)) {
    239         return set_or_throw(make_error_code(errc::is_a_directory), ec, "copy");
    240     }
    241     else if (is_directory(f) && (bool(copy_options::recursive & options) ||
    242              copy_options::none == options)) {
    243 
    244         if (!exists(t)) {
    245             // create directory to with attributes from 'from'.
    246             __create_directory(to, from, ec);
    247             if (ec && *ec) { return; }
    248         }
    249         directory_iterator it = ec ? directory_iterator(from, *ec)
    250                                    : directory_iterator(from);
    251         if (ec && *ec) { return; }
    252         std::error_code m_ec;
    253         for (; it != directory_iterator(); it.increment(m_ec)) {
    254             if (m_ec) return set_or_throw(m_ec, ec, "copy", from, to);
    255             __copy(it->path(), to / it->path().filename(),
    256                    options | copy_options::__in_recursive_copy, ec);
    257             if (ec && *ec) { return; }
    258         }
    259     }
    260 }
    261 
    262 
    263 bool __copy_file(const path& from, const path& to, copy_options options,
    264                  std::error_code *ec)
    265 {
    266     if (ec) ec->clear();
    267 
    268     std::error_code m_ec;
    269     auto from_st = detail::posix_stat(from, &m_ec);
    270     if (not is_regular_file(from_st)) {
    271         if (not m_ec)
    272             m_ec = make_error_code(errc::not_supported);
    273         set_or_throw(m_ec, ec, "copy_file", from, to);
    274         return false;
    275     }
    276 
    277     auto to_st = detail::posix_stat(to, &m_ec);
    278     if (!status_known(to_st)) {
    279         set_or_throw(m_ec, ec, "copy_file", from, to);
    280         return false;
    281     }
    282 
    283     const bool to_exists = exists(to_st);
    284     if (to_exists && !is_regular_file(to_st)) {
    285         set_or_throw(make_error_code(errc::not_supported), ec, "copy_file", from, to);
    286         return false;
    287     }
    288     if (to_exists && bool(copy_options::skip_existing & options)) {
    289         return false;
    290     }
    291     else if (to_exists && bool(copy_options::update_existing & options)) {
    292         auto from_time = __last_write_time(from, ec);
    293         if (ec && *ec) { return false; }
    294         auto to_time = __last_write_time(to, ec);
    295         if (ec && *ec) { return false; }
    296         if (from_time <= to_time) {
    297             return false;
    298         }
    299         return detail::copy_file_impl(from, to, from_st.permissions(), ec);
    300     }
    301     else if (!to_exists || bool(copy_options::overwrite_existing & options)) {
    302         return detail::copy_file_impl(from, to, from_st.permissions(), ec);
    303     }
    304     else {
    305         set_or_throw(make_error_code(errc::file_exists), ec, "copy", from, to);
    306         return false;
    307     }
    308 
    309     _LIBCPP_UNREACHABLE();
    310 }
    311 
    312 void __copy_symlink(const path& existing_symlink, const path& new_symlink,
    313                     std::error_code *ec)
    314 {
    315     const path real_path(__read_symlink(existing_symlink, ec));
    316     if (ec && *ec) { return; }
    317     // NOTE: proposal says you should detect if you should call
    318     // create_symlink or create_directory_symlink. I don't think this
    319     // is needed with POSIX
    320     __create_symlink(real_path, new_symlink, ec);
    321 }
    322 
    323 
    324 bool __create_directories(const path& p, std::error_code *ec)
    325 {
    326     std::error_code m_ec;
    327     auto const st = detail::posix_stat(p, &m_ec);
    328     if (!status_known(st)) {
    329         set_or_throw(m_ec, ec, "create_directories", p);
    330         return false;
    331     }
    332     else if (is_directory(st)) {
    333         if (ec) ec->clear();
    334         return false;
    335     }
    336     else if (exists(st)) {
    337         set_or_throw(make_error_code(errc::file_exists),
    338                      ec, "create_directories", p);
    339         return false;
    340     }
    341 
    342     const path parent = p.parent_path();
    343     if (!parent.empty()) {
    344         const file_status parent_st = status(parent, m_ec);
    345         if (not status_known(parent_st)) {
    346             set_or_throw(m_ec, ec, "create_directories", p);
    347             return false;
    348         }
    349         if (not exists(parent_st)) {
    350             __create_directories(parent, ec);
    351             if (ec && *ec) { return false; }
    352         }
    353     }
    354     return __create_directory(p, ec);
    355 }
    356 
    357 bool __create_directory(const path& p, std::error_code *ec)
    358 {
    359     if (ec) ec->clear();
    360     if (::mkdir(p.c_str(), static_cast<int>(perms::all)) == 0)
    361         return true;
    362     if (errno != EEXIST || !is_directory(p))
    363         set_or_throw(ec, "create_directory", p);
    364     return false;
    365 }
    366 
    367 bool __create_directory(path const & p, path const & attributes,
    368                         std::error_code *ec)
    369 {
    370     struct ::stat attr_stat;
    371     std::error_code mec;
    372     auto st = detail::posix_stat(attributes, attr_stat, &mec);
    373     if (!status_known(st)) {
    374         set_or_throw(mec, ec, "create_directory", p, attributes);
    375         return false;
    376     }
    377     if (ec) ec->clear();
    378     if (::mkdir(p.c_str(), attr_stat.st_mode) == 0)
    379         return true;
    380     if (errno != EEXIST || !is_directory(p))
    381         set_or_throw(ec, "create_directory", p, attributes);
    382     return false;
    383 }
    384 
    385 void __create_directory_symlink(path const & from, path const & to,
    386                                 std::error_code *ec){
    387     if (::symlink(from.c_str(), to.c_str()) != 0)
    388         set_or_throw(ec, "create_directory_symlink", from, to);
    389     else if (ec)
    390         ec->clear();
    391 }
    392 
    393 void __create_hard_link(const path& from, const path& to, std::error_code *ec){
    394     if (::link(from.c_str(), to.c_str()) == -1)
    395         set_or_throw(ec, "create_hard_link", from, to);
    396     else if (ec)
    397         ec->clear();
    398 }
    399 
    400 void __create_symlink(path const & from, path const & to, std::error_code *ec) {
    401 
    402     if (::symlink(from.c_str(), to.c_str()) == -1)
    403         set_or_throw(ec, "create_symlink", from, to);
    404     else if (ec)
    405         ec->clear();
    406 }
    407 
    408 path __current_path(std::error_code *ec) {
    409     auto size = ::pathconf(".", _PC_PATH_MAX);
    410     _LIBCPP_ASSERT(size >= 0, "pathconf returned a 0 as max size");
    411 
    412     auto buff = std::unique_ptr<char[]>(new char[size + 1]);
    413     char* ret;
    414     if ((ret = ::getcwd(buff.get(), static_cast<size_t>(size))) == nullptr) {
    415         set_or_throw(ec, "current_path");
    416         return {};
    417     }
    418     if (ec) ec->clear();
    419     return {buff.get()};
    420 }
    421 
    422 void __current_path(const path& p, std::error_code *ec) {
    423     if (::chdir(p.c_str()) == -1)
    424         set_or_throw(ec, "current_path", p);
    425     else if (ec)
    426         ec->clear();
    427 }
    428 
    429 bool __equivalent(const path& p1, const path& p2, std::error_code *ec)
    430 {
    431     auto make_unsupported_error = [&]() {
    432       set_or_throw(make_error_code(errc::not_supported), ec,
    433                      "equivalent", p1, p2);
    434       return false;
    435     };
    436     std::error_code ec1, ec2;
    437     struct ::stat st1 = {};
    438     struct ::stat st2 = {};
    439     auto s1 = detail::posix_stat(p1.native(), st1, &ec1);
    440     if (!exists(s1))
    441       return make_unsupported_error();
    442     auto s2 = detail::posix_stat(p2.native(), st2, &ec2);
    443     if (!exists(s2))
    444       return make_unsupported_error();
    445     if (ec) ec->clear();
    446     return (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
    447 }
    448 
    449 
    450 std::uintmax_t __file_size(const path& p, std::error_code *ec)
    451 {
    452     std::error_code m_ec;
    453     struct ::stat st;
    454     file_status fst = detail::posix_stat(p, st, &m_ec);
    455     if (!exists(fst) || !is_regular_file(fst)) {
    456         if (!m_ec)
    457             m_ec = make_error_code(errc::not_supported);
    458         set_or_throw(m_ec, ec, "file_size", p);
    459         return static_cast<uintmax_t>(-1);
    460     }
    461     // is_regular_file(p) == true
    462     if (ec) ec->clear();
    463     return static_cast<std::uintmax_t>(st.st_size);
    464 }
    465 
    466 std::uintmax_t __hard_link_count(const path& p, std::error_code *ec)
    467 {
    468     std::error_code m_ec;
    469     struct ::stat st;
    470     detail::posix_stat(p, st, &m_ec);
    471     if (m_ec) {
    472         set_or_throw(m_ec, ec, "hard_link_count", p);
    473         return static_cast<std::uintmax_t>(-1);
    474     }
    475     if (ec) ec->clear();
    476     return static_cast<std::uintmax_t>(st.st_nlink);
    477 }
    478 
    479 
    480 bool __fs_is_empty(const path& p, std::error_code *ec)
    481 {
    482     if (ec) ec->clear();
    483     std::error_code m_ec;
    484     struct ::stat pst;
    485     auto st = detail::posix_stat(p, pst, &m_ec);
    486     if (m_ec) {
    487         set_or_throw(m_ec, ec, "is_empty", p);
    488         return false;
    489     }
    490     else if (!is_directory(st) && !is_regular_file(st)) {
    491         m_ec = make_error_code(errc::not_supported);
    492         set_or_throw(m_ec, ec, "is_empty");
    493         return false;
    494     }
    495     else if (is_directory(st)) {
    496         auto it = ec ? directory_iterator(p, *ec) : directory_iterator(p);
    497         if (ec && *ec)
    498             return false;
    499         return it == directory_iterator{};
    500     }
    501     else if (is_regular_file(st))
    502         return static_cast<std::uintmax_t>(pst.st_size) == 0;
    503 
    504     _LIBCPP_UNREACHABLE();
    505 }
    506 
    507 
    508 namespace detail { namespace {
    509 
    510 using TimeSpec = struct timespec;
    511 using StatT =  struct stat;
    512 
    513 #if defined(__APPLE__)
    514 TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
    515 TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
    516 #else
    517 TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
    518 __attribute__((unused)) // Suppress warning
    519 TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
    520 #endif
    521 
    522 }} // end namespace detail
    523 
    524 using FSTime = fs_time_util<file_time_type, time_t, struct timespec>;
    525 
    526 file_time_type __last_write_time(const path& p, std::error_code *ec)
    527 {
    528     using namespace ::std::chrono;
    529     std::error_code m_ec;
    530     struct ::stat st;
    531     detail::posix_stat(p, st, &m_ec);
    532     if (m_ec) {
    533         set_or_throw(m_ec, ec, "last_write_time", p);
    534         return file_time_type::min();
    535     }
    536     if (ec) ec->clear();
    537     auto ts = detail::extract_mtime(st);
    538     if (!FSTime::is_representable(ts)) {
    539         set_or_throw(error_code(EOVERFLOW, generic_category()), ec,
    540                      "last_write_time", p);
    541         return file_time_type::min();
    542     }
    543     return FSTime::convert_timespec(ts);
    544 }
    545 
    546 void __last_write_time(const path& p, file_time_type new_time,
    547                        std::error_code *ec)
    548 {
    549     using namespace std::chrono;
    550     std::error_code m_ec;
    551 
    552     // We can use the presence of UTIME_OMIT to detect platforms that do not
    553     // provide utimensat.
    554 #if !defined(UTIME_OMIT)
    555     // This implementation has a race condition between determining the
    556     // last access time and attempting to set it to the same value using
    557     // ::utimes
    558     struct ::stat st;
    559     file_status fst = detail::posix_stat(p, st, &m_ec);
    560     if (m_ec && !status_known(fst)) {
    561         set_or_throw(m_ec, ec, "last_write_time", p);
    562         return;
    563     }
    564     auto atime = detail::extract_atime(st);
    565     struct ::timeval tbuf[2];
    566     tbuf[0].tv_sec = atime.tv_sec;
    567     tbuf[0].tv_usec = duration_cast<microseconds>(nanoseconds(atime.tv_nsec)).count();
    568     const bool overflowed = !FSTime::set_times_checked<microseconds>(
    569         &tbuf[1].tv_sec, &tbuf[1].tv_usec, new_time);
    570 
    571     if (overflowed) {
    572         set_or_throw(make_error_code(errc::invalid_argument), ec,
    573                      "last_write_time", p);
    574         return;
    575     }
    576     if (::utimes(p.c_str(), tbuf) == -1) {
    577         m_ec = detail::capture_errno();
    578     }
    579 #else
    580     struct ::timespec tbuf[2];
    581     tbuf[0].tv_sec = 0;
    582     tbuf[0].tv_nsec = UTIME_OMIT;
    583 
    584     const bool overflowed = !FSTime::set_times_checked<nanoseconds>(
    585         &tbuf[1].tv_sec, &tbuf[1].tv_nsec, new_time);
    586     if (overflowed) {
    587         set_or_throw(make_error_code(errc::invalid_argument),
    588             ec, "last_write_time", p);
    589         return;
    590     }
    591     if (::utimensat(AT_FDCWD, p.c_str(), tbuf, 0) == -1) {
    592         m_ec = detail::capture_errno();
    593     }
    594 #endif
    595     if (m_ec)
    596         set_or_throw(m_ec, ec, "last_write_time", p);
    597     else if (ec)
    598         ec->clear();
    599 }
    600 
    601 
    602 void __permissions(const path& p, perms prms, std::error_code *ec)
    603 {
    604 
    605     const bool resolve_symlinks = !bool(perms::symlink_nofollow & prms);
    606     const bool add_perms = bool(perms::add_perms & prms);
    607     const bool remove_perms = bool(perms::remove_perms & prms);
    608     _LIBCPP_ASSERT(!(add_perms && remove_perms),
    609                    "Both add_perms and remove_perms are set");
    610 
    611     bool set_sym_perms = false;
    612     prms &= perms::mask;
    613     if (!resolve_symlinks || (add_perms || remove_perms)) {
    614         std::error_code m_ec;
    615         file_status st = resolve_symlinks ? detail::posix_stat(p, &m_ec)
    616                                           : detail::posix_lstat(p, &m_ec);
    617         set_sym_perms = is_symlink(st);
    618         if (m_ec) return set_or_throw(m_ec, ec, "permissions", p);
    619         _LIBCPP_ASSERT(st.permissions() != perms::unknown,
    620                        "Permissions unexpectedly unknown");
    621         if (add_perms)
    622             prms |= st.permissions();
    623         else if (remove_perms)
    624            prms = st.permissions() & ~prms;
    625     }
    626     const auto real_perms = detail::posix_convert_perms(prms);
    627 
    628 # if defined(AT_SYMLINK_NOFOLLOW) && defined(AT_FDCWD)
    629     const int flags = set_sym_perms ? AT_SYMLINK_NOFOLLOW : 0;
    630     if (::fchmodat(AT_FDCWD, p.c_str(), real_perms, flags) == -1) {
    631         return set_or_throw(ec, "permissions", p);
    632     }
    633 # else
    634     if (set_sym_perms)
    635         return set_or_throw(make_error_code(errc::operation_not_supported),
    636                             ec, "permissions", p);
    637     if (::chmod(p.c_str(), real_perms) == -1) {
    638         return set_or_throw(ec, "permissions", p);
    639     }
    640 # endif
    641     if (ec) ec->clear();
    642 }
    643 
    644 
    645 path __read_symlink(const path& p, std::error_code *ec) {
    646     char buff[PATH_MAX + 1];
    647     std::error_code m_ec;
    648     ::ssize_t ret;
    649     if ((ret = ::readlink(p.c_str(), buff, PATH_MAX)) == -1) {
    650         set_or_throw(ec, "read_symlink", p);
    651         return {};
    652     }
    653     _LIBCPP_ASSERT(ret <= PATH_MAX, "TODO");
    654     _LIBCPP_ASSERT(ret > 0, "TODO");
    655     if (ec) ec->clear();
    656     buff[ret] = 0;
    657     return {buff};
    658 }
    659 
    660 
    661 bool __remove(const path& p, std::error_code *ec) {
    662     if (ec) ec->clear();
    663     if (::remove(p.c_str()) == -1) {
    664         set_or_throw(ec, "remove", p);
    665         return false;
    666     }
    667     return true;
    668 }
    669 
    670 namespace {
    671 
    672 std::uintmax_t remove_all_impl(path const & p, std::error_code& ec)
    673 {
    674     const auto npos = static_cast<std::uintmax_t>(-1);
    675     const file_status st = __symlink_status(p, &ec);
    676     if (ec) return npos;
    677      std::uintmax_t count = 1;
    678     if (is_directory(st)) {
    679         for (directory_iterator it(p, ec); !ec && it != directory_iterator();
    680              it.increment(ec)) {
    681             auto other_count = remove_all_impl(it->path(), ec);
    682             if (ec) return npos;
    683             count += other_count;
    684         }
    685         if (ec) return npos;
    686     }
    687     if (!__remove(p, &ec)) return npos;
    688     return count;
    689 }
    690 
    691 } // end namespace
    692 
    693 std::uintmax_t __remove_all(const path& p, std::error_code *ec) {
    694     std::error_code mec;
    695     auto count = remove_all_impl(p, mec);
    696     if (mec) {
    697         set_or_throw(mec, ec, "remove_all", p);
    698         return static_cast<std::uintmax_t>(-1);
    699     }
    700     if (ec) ec->clear();
    701     return count;
    702 }
    703 
    704 void __rename(const path& from, const path& to, std::error_code *ec) {
    705     if (::rename(from.c_str(), to.c_str()) == -1)
    706         set_or_throw(ec, "rename", from, to);
    707     else if (ec)
    708         ec->clear();
    709 }
    710 
    711 void __resize_file(const path& p, std::uintmax_t size, std::error_code *ec) {
    712     if (::truncate(p.c_str(), static_cast<::off_t>(size)) == -1)
    713         set_or_throw(ec, "resize_file", p);
    714     else if (ec)
    715         ec->clear();
    716 }
    717 
    718 space_info __space(const path& p, std::error_code *ec) {
    719     space_info si;
    720     struct statvfs m_svfs = {};
    721     if (::statvfs(p.c_str(), &m_svfs) == -1)  {
    722         set_or_throw(ec, "space", p);
    723         si.capacity = si.free = si.available =
    724             static_cast<std::uintmax_t>(-1);
    725         return si;
    726     }
    727     if (ec) ec->clear();
    728     // Multiply with overflow checking.
    729     auto do_mult = [&](std::uintmax_t& out, std::uintmax_t other) {
    730       out = other * m_svfs.f_frsize;
    731       if (other == 0 || out / other != m_svfs.f_frsize)
    732           out = static_cast<std::uintmax_t>(-1);
    733     };
    734     do_mult(si.capacity, m_svfs.f_blocks);
    735     do_mult(si.free, m_svfs.f_bfree);
    736     do_mult(si.available, m_svfs.f_bavail);
    737     return si;
    738 }
    739 
    740 file_status __status(const path& p, std::error_code *ec) {
    741     return detail::posix_stat(p, ec);
    742 }
    743 
    744 file_status __symlink_status(const path& p, std::error_code *ec) {
    745     return detail::posix_lstat(p, ec);
    746 }
    747 
    748 path __system_complete(const path& p, std::error_code *ec) {
    749     if (ec) ec->clear();
    750     return absolute(p, current_path());
    751 }
    752 
    753 path __temp_directory_path(std::error_code* ec) {
    754   const char* env_paths[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"};
    755   const char* ret = nullptr;
    756 
    757   for (auto& ep : env_paths)
    758     if ((ret = std::getenv(ep)))
    759       break;
    760   if (ret == nullptr)
    761     ret = "/tmp";
    762 
    763   path p(ret);
    764   std::error_code m_ec;
    765   if (!exists(p, m_ec) || !is_directory(p, m_ec)) {
    766     if (!m_ec || m_ec == make_error_code(errc::no_such_file_or_directory))
    767       m_ec = make_error_code(errc::not_a_directory);
    768     set_or_throw(m_ec, ec, "temp_directory_path");
    769     return {};
    770   }
    771 
    772   if (ec)
    773     ec->clear();
    774   return p;
    775 }
    776 
    777 // An absolute path is composed according to the table in [fs.op.absolute].
    778 path absolute(const path& p, const path& base) {
    779     auto root_name = p.root_name();
    780     auto root_dir = p.root_directory();
    781 
    782     if (!root_name.empty() && !root_dir.empty())
    783       return p;
    784 
    785     auto abs_base = base.is_absolute() ? base : absolute(base);
    786 
    787     /* !has_root_name && !has_root_dir */
    788     if (root_name.empty() && root_dir.empty())
    789     {
    790       return abs_base / p;
    791     }
    792     else if (!root_name.empty()) /* has_root_name && !has_root_dir */
    793     {
    794       return  root_name / abs_base.root_directory()
    795               /
    796               abs_base.relative_path() / p.relative_path();
    797     }
    798     else /* !has_root_name && has_root_dir */
    799     {
    800       if (abs_base.has_root_name())
    801         return abs_base.root_name() / p;
    802       // else p is absolute,  return outside of block
    803     }
    804     return p;
    805 }
    806 
    807 _LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM
    808