aboutsummaryrefslogtreecommitdiff
path: root/src/libutil
diff options
context:
space:
mode:
authorThéophane Hufschmitt <theophane@hufschmitt.net>2022-03-18 14:25:56 +0100
committerThéophane Hufschmitt <theophane.hufschmitt@tweag.io>2022-08-03 10:27:25 +0200
commitc5db1821a94406eaff86341220bc301ba1dac82e (patch)
tree3f6a6108b6075711026f158de3ac91a5cc3fe532 /src/libutil
parent6f89fb60088c4bc1513a005f0350c2bc13068892 (diff)
Re-implement the recursive directory copy
The recursive copy from the stl doesn’t exactly do what we need because 1. It doesn’t delete things as we go 2. It doesn’t keep the mtime, which change the nars So re-implement it ourselves. A bit dull, but that way we have what we want
Diffstat (limited to 'src/libutil')
-rw-r--r--src/libutil/filesystem.cc52
1 files changed, 50 insertions, 2 deletions
diff --git a/src/libutil/filesystem.cc b/src/libutil/filesystem.cc
index cf99b848a..198db2832 100644
--- a/src/libutil/filesystem.cc
+++ b/src/libutil/filesystem.cc
@@ -43,6 +43,53 @@ void replaceSymlink(const Path & target, const Path & link,
}
}
+void setWriteTime(const fs::path & p, const struct stat & st)
+{
+ struct timeval times[2];
+ times[0] = {
+ .tv_sec = st.st_atime,
+ .tv_usec = 0,
+ };
+ times[1] = {
+ .tv_sec = st.st_mtime,
+ .tv_usec = 0,
+ };
+ warn("Setting the mtime of %s to %d", p.c_str(), st.st_mtim.tv_sec);
+ if (lutimes(p.c_str(), times) != 0)
+ throw SysError("changing modification time of '%s'", p);
+}
+
+void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete)
+{
+ // TODO: Rewrite the `is_*` to use `symlink_status()`
+ auto statOfFrom = lstat(from.path().c_str());
+ auto fromStatus = from.symlink_status();
+
+ // Mark the directory as writable so that we can delete its children
+ if (andDelete && fs::is_directory(fromStatus)) {
+ fs::permissions(from.path(), fs::perms::owner_write, fs::perm_options::add | fs::perm_options::nofollow);
+ }
+
+
+ if (fs::is_symlink(fromStatus) || fs::is_regular_file(fromStatus)) {
+ fs::copy(from.path(), to, fs::copy_options::copy_symlinks | fs::copy_options::overwrite_existing);
+ } else if (fs::is_directory(fromStatus)) {
+ fs::create_directory(to);
+ for (auto & entry : fs::directory_iterator(from.path())) {
+ copy(entry, to / entry.path().filename(), andDelete);
+ }
+ } else {
+ throw Error("file '%s' has an unsupported type", from.path());
+ }
+
+ setWriteTime(to, statOfFrom);
+ if (andDelete) {
+ if (!fs::is_symlink(fromStatus))
+ fs::permissions(from.path(), fs::perms::owner_write, fs::perm_options::add | fs::perm_options::nofollow);
+ fs::remove(from.path());
+ }
+}
+
void moveFile(const Path & oldName, const Path & newName)
{
auto oldPath = fs::path(oldName);
@@ -51,8 +98,9 @@ void moveFile(const Path & oldName, const Path & newName)
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);
+ fs::remove(newPath);
+ warn("Copying %s to %s", oldName, newName);
+ copy(fs::directory_entry(oldPath), newPath, true);
}
}
}