diff options
author | Théophane Hufschmitt <theophane@hufschmitt.net> | 2022-03-17 16:13:29 +0100 |
---|---|---|
committer | Théophane Hufschmitt <theophane.hufschmitt@tweag.io> | 2022-08-03 10:27:25 +0200 |
commit | 6f89fb60088c4bc1513a005f0350c2bc13068892 (patch) | |
tree | a8f2c53173ff5dc8dfabb9f0fb43c8806315ac85 | |
parent | c2de0a232c1cfddb1f1385ffd23dd43a2099458e (diff) |
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
-rw-r--r-- | src/libutil/filesystem.cc | 15 |
1 files changed, 13 insertions, 2 deletions
diff --git a/src/libutil/filesystem.cc b/src/libutil/filesystem.cc index 33a8d81a6..cf99b848a 100644 --- a/src/libutil/filesystem.cc +++ b/src/libutil/filesystem.cc @@ -1,8 +1,11 @@ #include <sys/time.h> +#include <filesystem> #include "util.hh" #include "types.hh" +namespace fs = std::filesystem; + namespace nix { 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) { - if (::rename(oldName.c_str(), newName.c_str())) - throw SysError("renaming '%1%' to '%2%'", oldName, newName); + auto oldPath = fs::path(oldName); + 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); + } + } } } |