summaryrefslogtreecommitdiff
path: root/src/cmd/restore.rs
diff options
context:
space:
mode:
authorAlexander Batischev <eual.jp@gmail.com>2021-06-13 01:45:02 +0300
committerAlexander Batischev <eual.jp@gmail.com>2021-06-13 01:45:02 +0300
commit8641c594b0d065f5000efd84d30333c2762ef74c (patch)
treeef554b000ef98a5556d31cc4276763cee10a0a2d /src/cmd/restore.rs
parent481c5d8df21c72db3a3d76e851d25426f3e40647 (diff)
downloadobnam2-8641c594b0d065f5000efd84d30333c2762ef74c.tar.gz
feat: restore symlink metadata
The weird thing about this commit is that all the tests for it already exist: the subplot compares manifests of "live" and restored data, and that includes the mtime. The subplot passes on CI, and it passed for me too — until today. Today, if I run `./check` on the main branch (which is currently at 481c5d8df21c72db3a3d76e851d25426f3e40647), subplot fails because the symlinks' mtime is wrong. Most often it's just the nsec part that's wrong: tests run fast enough that they finish within a second, and they rarely happen at the edge of the second. I don't understand why this didn't happen to me before, and why it doesn't happen to CI — and to Lars, for that matter. git-bisect points at 755c18a11f87040245964cf411ea8f518b61e0f5, which is a couple months old, so we should've spotted the breakage by now. Needless to say, I didn't do any major overhauls of my system lately, just your usual `apt upgrade` (Debian bullseye/testing amd64). Did some change in Subplot or Summain obfuscate this? It bothers me that I don't understand how we could miss this for so long.
Diffstat (limited to 'src/cmd/restore.rs')
-rw-r--r--src/cmd/restore.rs21
1 files changed, 14 insertions, 7 deletions
diff --git a/src/cmd/restore.rs b/src/cmd/restore.rs
index 01e96bf..3c5b896 100644
--- a/src/cmd/restore.rs
+++ b/src/cmd/restore.rs
@@ -5,7 +5,7 @@ use crate::error::ObnamError;
use crate::fsentry::{FilesystemEntry, FilesystemKind};
use crate::generation::{LocalGeneration, LocalGenerationError};
use indicatif::{ProgressBar, ProgressStyle};
-use libc::{chmod, mkfifo, timespec, utimensat, AT_FDCWD};
+use libc::{chmod, mkfifo, timespec, utimensat, AT_FDCWD, AT_SYMLINK_NOFOLLOW};
use log::{debug, error, info};
use std::ffi::CString;
use std::io::prelude::*;
@@ -209,6 +209,7 @@ fn restore_symlink(path: &Path, entry: &FilesystemEntry) -> RestoreResult<()> {
}
symlink(entry.symlink_target().unwrap(), path)
.map_err(|err| RestoreError::Symlink(path.to_path_buf(), err))?;
+ restore_metadata(path, entry)?;
debug!("restored symlink {}", path.display());
Ok(())
}
@@ -254,15 +255,21 @@ fn restore_metadata(path: &Path, entry: &FilesystemEntry) -> RestoreResult<()> {
// We have to use unsafe here to be able call the libc functions
// below.
unsafe {
- debug!("chmod {:?}", path);
- if chmod(path.as_ptr(), entry.mode()) == -1 {
- let error = Error::last_os_error();
- error!("chmod failed on {:?}", path);
- return Err(RestoreError::Chmod(pathbuf, error));
+ if entry.kind() != FilesystemKind::Symlink {
+ debug!("chmod {:?}", path);
+ if chmod(path.as_ptr(), entry.mode()) == -1 {
+ let error = Error::last_os_error();
+ error!("chmod failed on {:?}", path);
+ return Err(RestoreError::Chmod(pathbuf, error));
+ }
+ } else {
+ debug!(
+ "skipping chmod of a symlink because it'll attempt to change the pointed-at file"
+ );
}
debug!("utimens {:?}", path);
- if utimensat(AT_FDCWD, path.as_ptr(), times, 0) == -1 {
+ if utimensat(AT_FDCWD, path.as_ptr(), times, AT_SYMLINK_NOFOLLOW) == -1 {
let error = Error::last_os_error();
error!("utimensat failed on {:?}", path);
return Err(RestoreError::SetTimestamp(pathbuf, error));