diff/restore: Treat single file destination properly

This commit is contained in:
Alexander Weiss 2023-02-18 20:59:15 +01:00
parent 34a0667e5d
commit db671febfd
3 changed files with 52 additions and 20 deletions

View File

@ -146,7 +146,39 @@ impl WriteBackend for LocalBackend {
}
}
impl LocalBackend {
#[derive(Clone)]
pub struct LocalDestination {
path: PathBuf,
is_file: bool,
}
impl LocalDestination {
pub fn new(path: &str, create: bool, expect_file: bool) -> Result<Self> {
let is_dir = path.ends_with('/');
let path: PathBuf = path.into();
let is_file = path.is_file() || (!path.is_dir() && !is_dir && expect_file);
if create {
if is_file {
if let Some(path) = path.parent() {
fs::create_dir_all(path)?;
}
} else {
fs::create_dir_all(&path)?;
}
}
Ok(Self { path, is_file })
}
fn path(&self, item: impl AsRef<Path>) -> PathBuf {
if self.is_file {
self.path.clone()
} else {
self.path.join(item)
}
}
pub fn remove_dir(&self, dirname: impl AsRef<Path>) -> Result<()> {
Ok(fs::remove_dir_all(dirname)?)
}
@ -162,7 +194,7 @@ impl LocalBackend {
}
pub fn set_times(&self, item: impl AsRef<Path>, meta: &Metadata) -> Result<()> {
let filename = self.path.join(item);
let filename = self.path(item);
if let Some(mtime) = meta.mtime.map(|t| FileTime::from_system_time(t.into())) {
set_file_mtime(&filename, mtime)?;
}
@ -173,7 +205,7 @@ impl LocalBackend {
}
pub fn set_user_group(&self, item: impl AsRef<Path>, meta: &Metadata) -> Result<()> {
let filename = self.path.join(item);
let filename = self.path(item);
let user = meta
.user
@ -195,7 +227,7 @@ impl LocalBackend {
}
pub fn set_uid_gid(&self, item: impl AsRef<Path>, meta: &Metadata) -> Result<()> {
let filename = self.path.join(item);
let filename = self.path(item);
let uid = meta.uid.map(Uid::from_raw);
let gid = meta.gid.map(Gid::from_raw);
@ -205,7 +237,7 @@ impl LocalBackend {
}
pub fn set_permission(&self, item: impl AsRef<Path>, meta: &Metadata) -> Result<()> {
let filename = self.path.join(item);
let filename = self.path(item);
if let Some(mode) = meta.mode() {
let mode = map_mode_from_go(*mode);
@ -216,7 +248,7 @@ impl LocalBackend {
// set_length sets the length of the given file. If it doesn't exist, create a new (empty) one with given length
pub fn set_length(&self, item: impl AsRef<Path>, size: u64) -> Result<()> {
let filename = self.path.join(item);
let filename = self.path(item);
OpenOptions::new()
.create(true)
.write(true)
@ -226,7 +258,7 @@ impl LocalBackend {
}
pub fn create_special(&self, item: impl AsRef<Path>, node: &Node) -> Result<()> {
let filename = self.path.join(item);
let filename = self.path(item);
match node.node_type() {
NodeType::Symlink { linktarget } => {
@ -270,7 +302,7 @@ impl LocalBackend {
}
pub fn read_at(&self, item: impl AsRef<Path>, offset: u64, length: u64) -> Result<Bytes> {
let filename = self.path.join(item);
let filename = self.path(item);
let mut file = File::open(filename)?;
file.seek(SeekFrom::Start(offset))?;
let mut vec = vec![0; length.try_into()?];
@ -279,7 +311,7 @@ impl LocalBackend {
}
pub fn get_matching_file(&self, item: impl AsRef<Path>, size: u64) -> Option<File> {
let filename = self.path.join(item);
let filename = self.path(item);
match fs::symlink_metadata(&filename) {
Ok(meta) => {
if meta.is_file() && meta.len() == size {
@ -293,7 +325,7 @@ impl LocalBackend {
}
pub fn write_at(&self, item: impl AsRef<Path>, offset: u64, data: &[u8]) -> Result<()> {
let filename = self.path.join(item);
let filename = self.path(item);
let file = fs::OpenOptions::new()
.create(true)
.write(true)

View File

@ -5,7 +5,7 @@ use anyhow::{anyhow, bail, Context, Result};
use clap::Parser;
use super::{progress_counter, RusticConfig};
use crate::backend::{LocalBackend, LocalSource, LocalSourceOptions};
use crate::backend::{LocalDestination, LocalSource, LocalSourceOptions};
use crate::blob::{Node, NodeStreamer, NodeType, Tree};
use crate::commands::helpers::progress_spinner;
use crate::crypto::hash;
@ -79,7 +79,7 @@ pub(super) fn execute(
let index = IndexBackend::new(be, progress_counter(""))?;
let node1 = Tree::node_from_path(&index, snap1.tree, Path::new(path1))?;
let local = LocalBackend::new(path2)?;
let local = LocalDestination::new(path2, false, !node1.is_dir())?;
let path2 = PathBuf::from(path2);
let is_dir = path2
.metadata()
@ -123,7 +123,7 @@ fn arg_to_snap_path<'a>(arg: &'a str, default_path: &'a str) -> (Option<&'a str>
}
fn identical_content_local(
local: &LocalBackend,
local: &LocalDestination,
index: &impl ReadIndex,
path: &Path,
node: &Node,

View File

@ -15,7 +15,7 @@ use rayon::ThreadPoolBuilder;
use super::rustic_config::RusticConfig;
use super::{bytes, progress_bytes, progress_counter, warm_up_wait};
use crate::backend::{DecryptReadBackend, FileType, LocalBackend};
use crate::backend::{DecryptReadBackend, FileType, LocalDestination};
use crate::blob::{Node, NodeStreamer, NodeType, Tree};
use crate::commands::helpers::progress_spinner;
use crate::crypto::hash;
@ -89,7 +89,7 @@ pub(super) fn execute(
let index = IndexBackend::new(be, progress_counter(""))?;
let node = Tree::node_from_path(&index, snap.tree, Path::new(path))?;
let dest = LocalBackend::new(&opts.dest)?;
let dest = LocalDestination::new(&opts.dest, true, !node.is_dir())?;
let p = progress_spinner("collecting file information...");
let (file_infos, stats) = allocate_and_collect(&dest, index.clone(), &node, &opts)?;
@ -150,7 +150,7 @@ struct RestoreStats {
/// collect restore information, scan existing files and allocate non-existing files
fn allocate_and_collect(
dest: &LocalBackend,
dest: &LocalDestination,
index: impl IndexedBackend + Unpin,
node: &Node,
opts: &Opts,
@ -332,7 +332,7 @@ fn allocate_and_collect(
/// using the [`DecryptReadBackend`] `be` and writing them into the [`LocalBackend`] `dest`.
fn restore_contents(
be: &impl DecryptReadBackend,
dest: &LocalBackend,
dest: &LocalDestination,
file_infos: FileInfos,
) -> Result<()> {
let (filenames, restore_info, total_size, _) = file_infos.dissolve();
@ -400,7 +400,7 @@ fn restore_contents(
}
fn restore_metadata(
dest: &LocalBackend,
dest: &LocalDestination,
index: impl IndexedBackend + Unpin,
node: &Node,
opts: &Opts,
@ -435,7 +435,7 @@ fn restore_metadata(
Ok(())
}
fn set_metadata(dest: &LocalBackend, path: &PathBuf, node: &Node, opts: &Opts) {
fn set_metadata(dest: &LocalDestination, path: &PathBuf, node: &Node, opts: &Opts) {
debug!("setting metadata for {:?}", path);
dest.create_special(path, node)
.unwrap_or_else(|_| warn!("restore {:?}: creating special file failed.", path));
@ -512,7 +512,7 @@ impl FileInfos {
/// Returns the computed length of the file
fn add_file(
&mut self,
dest: &LocalBackend,
dest: &LocalDestination,
file: &Node,
name: PathBuf,
index: &impl IndexedBackend,