diff --git a/libstdc++-v3/src/filesystem/ops.cc b/libstdc++-v3/src/filesystem/ops.cc index b5c8eb9..95146bf 100644 --- a/libstdc++-v3/src/filesystem/ops.cc +++ b/libstdc++-v3/src/filesystem/ops.cc @@ -98,6 +98,7 @@ fs::canonical(const path& p, const path& base, error_code& ec) { const path pa = absolute(p, base); path result; + #ifdef _GLIBCXX_USE_REALPATH char_ptr buf{ nullptr }; # if _XOPEN_VERSION < 700 @@ -119,18 +120,9 @@ fs::canonical(const path& p, const path& base, error_code& ec) } #endif - auto fail = [&ec, &result](int e) mutable { - if (!ec.value()) - ec.assign(e, std::generic_category()); - result.clear(); - }; - if (!exists(pa, ec)) - { - fail(ENOENT); - return result; - } - // else we can assume no unresolvable symlink loops + return result; + // else: we know there are (currently) no unresolvable symlink loops result = pa.root_path(); @@ -138,18 +130,17 @@ fs::canonical(const path& p, const path& base, error_code& ec) for (auto& f : pa.relative_path()) cmpts.push_back(f); - while (!cmpts.empty()) + int max_allowed_symlinks = 40; + + while (!cmpts.empty() && !ec) { path f = std::move(cmpts.front()); cmpts.pop_front(); if (f.compare(".") == 0) { - if (!is_directory(result, ec)) - { - fail(ENOTDIR); - break; - } + if (!is_directory(result, ec) && !ec) + ec.assign(ENOTDIR, std::generic_category()); } else if (f.compare("..") == 0) { @@ -166,27 +157,30 @@ fs::canonical(const path& p, const path& base, error_code& ec) if (is_symlink(result, ec)) { path link = read_symlink(result, ec); - if (!ec.value()) + if (!ec) { - if (link.is_absolute()) + if (--max_allowed_symlinks == 0) + ec.assign(ELOOP, std::generic_category()); + else { - result = link.root_path(); - link = link.relative_path(); + if (link.is_absolute()) + { + result = link.root_path(); + link = link.relative_path(); + } + else + result.remove_filename(); + + cmpts.insert(cmpts.begin(), link.begin(), link.end()); } - else - result.remove_filename(); - - cmpts.insert(cmpts.begin(), link.begin(), link.end()); } } - - if (ec.value() || !exists(result, ec)) - { - fail(ENOENT); - break; - } } } + + if (ec || !exists(result, ec)) + result.clear(); + return result; }