aboutsummaryrefslogtreecommitdiff
path: root/src/libutil
diff options
context:
space:
mode:
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);
}
}
}