You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

99 lines
2.7 KiB

//! Sync operations
use super::*;
use std::{
fs::{
self,
OpenOptions,
},
path::{
Path,
},
};
use error::{
Error, ErrorKind,
};
fn unlink(path: &Path) -> Result<(), Error>
{
let tmp = temp::TempFile::new_in(path.parent().unwrap());
fs::copy(path, &tmp).map_err(|e| Error::new(ErrorKind::Copy(e), path.to_owned()))?;
fs::remove_file(path).map_err(|e| Error::new(ErrorKind::Unlink(e), path.to_owned()))?;
fs::rename(&tmp, path).map_err(|e| Error::new(ErrorKind::Move(e), path.to_owned()))?;
tmp.release(); // file no longer exists, so no need to drop;
Ok(())
}
fn work<P: AsRef<Path>>(apath: P) -> Result<(P, bool), Error>
{
let path = apath.as_ref();
let file = OpenOptions::new()
.read(true)
.open(path)
.map_err(|e| (ErrorKind::Open(e), path))?;
let meta = match file.metadata() {
Ok(meta) => meta,
Err(err) => {
debug!("Failed to stat file: {}", err);
warn!("Failed to stat {:?}, skipping", path);
return Err((ErrorKind::Stat(err), path).into());
},
};
use std::os::unix::fs::MetadataExt;
let nlink = meta.nlink();
trace!("<{:?}> has {} links", path, nlink);
if nlink > 1 {
unlink(path)?;
Ok((apath, true))
} else {
Ok((apath, false))
}
}
pub fn main<I: IntoIterator<Item=String>>(list: I) -> eyre::Result<()>
{
let list = list.into_iter();
let mut failures = match list.size_hint() {
(0, Some(0)) | (0, None) => Vec::new(),
(_, Some(x)) | (x, None) => Vec::with_capacity(x),
};
let mut done =0;
for file in list
{
match work(file) {
Ok((path, true)) => info!("<{:?}> OK (processed)", path),
Ok((path, false)) => debug!("<{:?}> OK (skipped)", path),
Err(kind) if !kind.kind().is_skippable() => {
failures.push((kind.path().to_owned(), kind.to_string()));
let fuck = format!("{:?}", kind.path());
let sug = kind.kind().suggestion();
let err = Err::<std::convert::Infallible, _>(kind)
.wrap_err_with(|| eyre!("<{}> Failed", fuck))
.with_section(move || fuck.header("Path was"))
.with_suggestion(|| sug)
.unwrap_err();
error!("{}", err);
debug!("Error: {:?}", err);
},
Err(k) => {
failures.push((k.path().to_owned(), k.to_string()));
warn!("<{:?}> Failed (skipped)", k.path());
},
}
done+=1;
}
if failures.len() > 0 {
return Err(eyre!("{}/{} tasks failed to complete successfullly", failures.len(), done))
.with_section(|| failures.into_iter()
.map(|(x, err)| format!("{}: {}", x.into_os_string()
.into_string()
.unwrap_or_else(|os| os.to_string_lossy().into_owned()), err))
.join("\n")
.header("Failed tasks:"))
.with_suggestion(|| "Run with `RUST_LOG=debug` or `RUST_LOG=trace` for verbose error reporting");
}
Ok(())
}