return g_umask;
}
-void
-hard_link(const std::string& oldpath, const std::string& newpath)
-{
- // Assumption: newpath may already exist as a left-over file from a previous
- // run, but it's only we who can create the file entry now so we don't try to
- // handle a race between unlink() and link() below.
- unlink(newpath.c_str());
-
-#ifndef _WIN32
- if (link(oldpath.c_str(), newpath.c_str()) != 0) {
- throw core::Error(strerror(errno));
- }
-#else
- if (!CreateHardLink(newpath.c_str(), oldpath.c_str(), nullptr)) {
- throw core::Error(Win32Util::error_message(GetLastError()));
- }
-#endif
-}
-
std::optional<size_t>
is_absolute_path_with_prefix(std::string_view path)
{
// Get process umask.
mode_t get_umask();
-// Hard-link `oldpath` to `newpath`. Throws `core::Error` on error.
-void hard_link(const std::string& oldpath, const std::string& newpath);
-
// Determine if `path` is an absolute path with prefix, returning the split
// point.
std::optional<size_t> is_absolute_path_with_prefix(std::string_view path);
#include <algorithm>
#include <atomic>
#include <cstdlib>
+#include <filesystem>
#include <memory>
#include <numeric>
#include <string>
#include <utility>
+namespace fs = std::filesystem;
+
using core::Statistic;
using core::StatisticsCounters;
#endif
}
if (m_config.hard_link()) {
+ // Assumption: dest may already exist as a left-over file from a previous
+ // run, but it's only we who can create the file entry now so we don't try
+ // to handle a race between remove() and create_hard_link() below.
+
+ std::error_code ec;
+ fs::remove(dest, ec); // Ignore any error.
LOG("Hard linking {} to {}", source, dest);
- try {
- Util::hard_link(source, dest);
+ fs::create_hard_link(source, dest, ec);
+ if (!ec) {
#ifndef _WIN32
if (chmod(dest.c_str(), 0444 & ~Util::get_umask()) != 0) {
LOG("Failed to chmod {}: {}", dest.c_str(), strerror(errno));
}
#endif
return;
- } catch (const core::Error& e) {
- LOG("Failed to hard link {} to {}: {}", source, dest, e.what());
- // Fall back to copying.
}
+ LOG("Failed to hard link {} to {}: {}", source, dest, ec.message());
+ // Fall back to copying.
}
LOG("Copying {} to {}", source, dest);
#endif
}
-TEST_CASE("Util::hard_link")
-{
- TestContext test_context;
-
- SUBCASE("Link file to nonexistent destination")
- {
- util::write_file("old", "content");
- CHECK_NOTHROW(Util::hard_link("old", "new"));
- CHECK(*util::read_file<std::string>("new") == "content");
- }
-
- SUBCASE("Link file to existing destination")
- {
- util::write_file("old", "content");
- util::write_file("new", "other content");
- CHECK_NOTHROW(Util::hard_link("old", "new"));
- CHECK(*util::read_file<std::string>("new") == "content");
- }
-
- SUBCASE("Link nonexistent file")
- {
- CHECK_THROWS_AS(Util::hard_link("old", "new"), core::Error);
- }
-}
-
TEST_CASE("Util::is_absolute_path_with_prefix")
{
CHECK(*Util::is_absolute_path_with_prefix("-I/c/foo") == 2);