rename: Fallback to a copy if the filesystems mismatch

In `nix::rename`, if the call to `rename` fails with `EXDEV` (failure
because the source and the destination are in a different filesystems)
switch to copying and removing the source.

To avoid having to re-implement the copy manually, I switched the
function to use the c++17 `filesystem` library (which has a `copy`
function that should do what we want).

Fix #6262
This commit is contained in:
Théophane Hufschmitt 2022-03-17 16:13:29 +01:00 committed by Théophane Hufschmitt
parent c2de0a232c
commit 6f89fb6008

View file

@ -1,8 +1,11 @@
#include <sys/time.h> #include <sys/time.h>
#include <filesystem>
#include "util.hh" #include "util.hh"
#include "types.hh" #include "types.hh"
namespace fs = std::filesystem;
namespace nix { namespace nix {
void createSymlink(const Path & target, const Path & link, void createSymlink(const Path & target, const Path & link,
@ -42,8 +45,16 @@ void replaceSymlink(const Path & target, const Path & link,
void moveFile(const Path & oldName, const Path & newName) void moveFile(const Path & oldName, const Path & newName)
{ {
if (::rename(oldName.c_str(), newName.c_str())) auto oldPath = fs::path(oldName);
throw SysError("renaming '%1%' to '%2%'", oldName, newName); auto newPath = fs::path(newName);
try {
fs::rename(oldPath, newPath);
} catch (fs::filesystem_error & e) {
if (e.code().value() == EXDEV) {
fs::copy(oldName, newName, fs::copy_options::copy_symlinks);
fs::remove_all(oldName);
}
}
} }
} }