parent
4459d58df9
commit
1a23ce4c44
@ -1,3 +1,86 @@
|
||||
//! 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();
|
||||
debug!("<{:?}> 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)) => info!("<{:?}> OK (skipped)", path),
|
||||
Err(k) => {
|
||||
failures.push((k.path().to_owned(), k.to_string()));
|
||||
trace!("<{:?}> 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(())
|
||||
}
|
||||
|
Loading…
Reference in new issue