src/pxar/encoder.rs: allow to pass list of devices
For better mount point include control...
This commit is contained in:
		@ -5,7 +5,7 @@ use failure::*;
 | 
			
		||||
//use std::os::unix::io::AsRawFd;
 | 
			
		||||
use chrono::{Local, Utc, TimeZone};
 | 
			
		||||
use std::path::{Path, PathBuf};
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use std::collections::{HashSet, HashMap};
 | 
			
		||||
use std::io::Write;
 | 
			
		||||
 | 
			
		||||
use proxmox_backup::tools;
 | 
			
		||||
@ -151,12 +151,12 @@ fn backup_directory<P: AsRef<Path>>(
 | 
			
		||||
    dir_path: P,
 | 
			
		||||
    archive_name: &str,
 | 
			
		||||
    chunk_size: Option<usize>,
 | 
			
		||||
    all_file_systems: bool,
 | 
			
		||||
    device_set: Option<HashSet<u64>>,
 | 
			
		||||
    verbose: bool,
 | 
			
		||||
    crypt_config: Option<Arc<CryptConfig>>,
 | 
			
		||||
) -> Result<(), Error> {
 | 
			
		||||
 | 
			
		||||
    let pxar_stream = PxarBackupStream::open(dir_path.as_ref(), all_file_systems, verbose)?;
 | 
			
		||||
    let pxar_stream = PxarBackupStream::open(dir_path.as_ref(), device_set, verbose)?;
 | 
			
		||||
    let chunk_stream = ChunkStream::new(pxar_stream, chunk_size);
 | 
			
		||||
 | 
			
		||||
    let (tx, rx) = mpsc::channel(10); // allow to buffer 10 chunks
 | 
			
		||||
@ -431,6 +431,25 @@ fn create_backup(
 | 
			
		||||
 | 
			
		||||
    let backup_id = param["host-id"].as_str().unwrap_or(&tools::nodename());
 | 
			
		||||
 | 
			
		||||
    let include_dev = param["include-dev"].as_array();
 | 
			
		||||
 | 
			
		||||
    let mut devices = if all_file_systems { None } else { Some(HashSet::new()) };
 | 
			
		||||
 | 
			
		||||
    if let Some(include_dev) = include_dev {
 | 
			
		||||
        if all_file_systems {
 | 
			
		||||
            bail!("option 'all-file-systems' conflicts with option 'include-dev'");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut set = HashSet::new();
 | 
			
		||||
        for path in include_dev {
 | 
			
		||||
            let path = path.as_str().unwrap();
 | 
			
		||||
            let stat = nix::sys::stat::stat(path)
 | 
			
		||||
                .map_err(|err| format_err!("fstat {:?} failed - {}", path, err))?;
 | 
			
		||||
            set.insert(stat.st_dev);
 | 
			
		||||
        }
 | 
			
		||||
        devices = Some(set);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let mut upload_list = vec![];
 | 
			
		||||
 | 
			
		||||
    enum BackupType { PXAR, IMAGE, CONFIG };
 | 
			
		||||
@ -522,7 +541,7 @@ fn create_backup(
 | 
			
		||||
                    &filename,
 | 
			
		||||
                    &target,
 | 
			
		||||
                    chunk_size_opt,
 | 
			
		||||
                    all_file_systems,
 | 
			
		||||
                    devices.clone(),
 | 
			
		||||
                    verbose,
 | 
			
		||||
                    crypt_config.clone(),
 | 
			
		||||
                )?;
 | 
			
		||||
@ -1219,6 +1238,13 @@ fn main() {
 | 
			
		||||
                    ).min_length(1)
 | 
			
		||||
                )
 | 
			
		||||
                .optional("repository", REPO_URL_SCHEMA.clone())
 | 
			
		||||
                .optional(
 | 
			
		||||
                    "include-dev",
 | 
			
		||||
                    ArraySchema::new(
 | 
			
		||||
                        "Include mountpoints with same st_dev number (see ``man fstat``) as specified files.",
 | 
			
		||||
                        StringSchema::new("Path to file.").into()
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
                .optional(
 | 
			
		||||
                    "keyfile",
 | 
			
		||||
                    StringSchema::new("Path to encryption key. All data will be encrypted using this key."))
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@ use std::fs::OpenOptions;
 | 
			
		||||
use std::sync::Arc;
 | 
			
		||||
use std::os::unix::fs::OpenOptionsExt;
 | 
			
		||||
use std::os::unix::io::AsRawFd;
 | 
			
		||||
use std::collections::HashSet;
 | 
			
		||||
 | 
			
		||||
use proxmox_backup::pxar;
 | 
			
		||||
 | 
			
		||||
@ -155,6 +156,8 @@ fn create_archive(
 | 
			
		||||
    let no_fcaps = param["no-fcaps"].as_bool().unwrap_or(false);
 | 
			
		||||
    let no_acls = param["no-acls"].as_bool().unwrap_or(false);
 | 
			
		||||
 | 
			
		||||
    let devices = if all_file_systems { None } else { Some(HashSet::new()) };
 | 
			
		||||
 | 
			
		||||
    let source = PathBuf::from(source);
 | 
			
		||||
 | 
			
		||||
    let mut dir = nix::dir::Dir::open(
 | 
			
		||||
@ -178,7 +181,7 @@ fn create_archive(
 | 
			
		||||
        feature_flags ^= pxar::CA_FORMAT_WITH_ACL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pxar::Encoder::encode(source, &mut dir, &mut writer, all_file_systems, verbose, feature_flags)?;
 | 
			
		||||
    pxar::Encoder::encode(source, &mut dir, &mut writer, devices, verbose, feature_flags)?;
 | 
			
		||||
 | 
			
		||||
    writer.flush()?;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@ use failure::*;
 | 
			
		||||
use std::thread;
 | 
			
		||||
use std::os::unix::io::FromRawFd;
 | 
			
		||||
use std::path::{Path, PathBuf};
 | 
			
		||||
use std::collections::HashSet;
 | 
			
		||||
 | 
			
		||||
use futures::Poll;
 | 
			
		||||
use futures::stream::Stream;
 | 
			
		||||
@ -34,7 +35,7 @@ impl Drop for PxarBackupStream {
 | 
			
		||||
 | 
			
		||||
impl PxarBackupStream {
 | 
			
		||||
 | 
			
		||||
    pub fn new(mut dir: Dir, path: PathBuf, all_file_systems: bool, verbose: bool) -> Result<Self, Error> {
 | 
			
		||||
    pub fn new(mut dir: Dir, path: PathBuf, device_set: Option<HashSet<u64>>, verbose: bool) -> Result<Self, Error> {
 | 
			
		||||
 | 
			
		||||
        let (rx, tx) = nix::unistd::pipe()?;
 | 
			
		||||
 | 
			
		||||
@ -43,7 +44,7 @@ impl PxarBackupStream {
 | 
			
		||||
 | 
			
		||||
        let child = thread::spawn(move|| {
 | 
			
		||||
            let mut writer = unsafe { std::fs::File::from_raw_fd(tx) };
 | 
			
		||||
            if let Err(err) = pxar::Encoder::encode(path, &mut dir, &mut writer, all_file_systems, verbose, pxar::CA_FORMAT_DEFAULT) {
 | 
			
		||||
            if let Err(err) = pxar::Encoder::encode(path, &mut dir, &mut writer, device_set, verbose, pxar::CA_FORMAT_DEFAULT) {
 | 
			
		||||
                eprintln!("pxar encode failed - {}", err);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
@ -54,12 +55,12 @@ impl PxarBackupStream {
 | 
			
		||||
        Ok(Self { stream: Some(stream), child: Some(child) })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn open(dirname: &Path,  all_file_systems: bool, verbose: bool) -> Result<Self, Error> {
 | 
			
		||||
    pub fn open(dirname: &Path, device_set: Option<HashSet<u64>>, verbose: bool) -> Result<Self, Error> {
 | 
			
		||||
 | 
			
		||||
        let dir = nix::dir::Dir::open(dirname, OFlag::O_DIRECTORY, Mode::empty())?;
 | 
			
		||||
        let path = std::path::PathBuf::from(dirname);
 | 
			
		||||
 | 
			
		||||
        Self::new(dir, path, all_file_systems, verbose)
 | 
			
		||||
        Self::new(dir, path, device_set, verbose)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@
 | 
			
		||||
 | 
			
		||||
use failure::*;
 | 
			
		||||
use endian_trait::Endian;
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use std::collections::{HashSet, HashMap};
 | 
			
		||||
 | 
			
		||||
use super::format_definition::*;
 | 
			
		||||
use super::binary_search_tree::*;
 | 
			
		||||
@ -48,8 +48,7 @@ pub struct Encoder<'a, W: Write> {
 | 
			
		||||
    writer_pos: usize,
 | 
			
		||||
    _size: usize,
 | 
			
		||||
    file_copy_buffer: Vec<u8>,
 | 
			
		||||
    all_file_systems: bool,
 | 
			
		||||
    root_st_dev: u64,
 | 
			
		||||
    device_set: Option<HashSet<u64>>,
 | 
			
		||||
    verbose: bool,
 | 
			
		||||
    // Flags set by the user
 | 
			
		||||
    feature_flags: u64,
 | 
			
		||||
@ -65,11 +64,20 @@ impl <'a, W: Write> Encoder<'a, W> {
 | 
			
		||||
        self.base_path.join(&self.relative_path)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Create archive, write result data to ``writer``.
 | 
			
		||||
    ///
 | 
			
		||||
    /// The ``device_set`` can be use used to limit included mount points.
 | 
			
		||||
    ///
 | 
			
		||||
    /// - ``None``: include all mount points
 | 
			
		||||
    /// - ``Some(set)``: only include devices listed in this set (the
 | 
			
		||||
    ///   root path device is automathically added to this list, so
 | 
			
		||||
    ///   you can pass an empty set if you want to archive a single
 | 
			
		||||
    ///   mount point.)
 | 
			
		||||
    pub fn encode(
 | 
			
		||||
        path: PathBuf,
 | 
			
		||||
        dir: &mut nix::dir::Dir,
 | 
			
		||||
        writer: &'a mut W,
 | 
			
		||||
        all_file_systems: bool,
 | 
			
		||||
        device_set: Option<HashSet<u64>>,
 | 
			
		||||
        verbose: bool,
 | 
			
		||||
        feature_flags: u64,
 | 
			
		||||
    ) -> Result<(), Error> {
 | 
			
		||||
@ -90,6 +98,11 @@ impl <'a, W: Write> Encoder<'a, W> {
 | 
			
		||||
            bail!("got unexpected file type {:?} (not a directory)", path);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut device_set = device_set.clone();
 | 
			
		||||
        if let Some(ref mut set) = device_set {
 | 
			
		||||
            set.insert(stat.st_dev);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let magic = detect_fs_type(dir_fd)?;
 | 
			
		||||
 | 
			
		||||
        if is_virtual_file_system(magic) {
 | 
			
		||||
@ -105,8 +118,7 @@ impl <'a, W: Write> Encoder<'a, W> {
 | 
			
		||||
            writer_pos: 0,
 | 
			
		||||
            _size: 0,
 | 
			
		||||
            file_copy_buffer,
 | 
			
		||||
            all_file_systems,
 | 
			
		||||
            root_st_dev: stat.st_dev,
 | 
			
		||||
            device_set,
 | 
			
		||||
            verbose,
 | 
			
		||||
            feature_flags,
 | 
			
		||||
            fs_feature_flags,
 | 
			
		||||
@ -616,7 +628,11 @@ impl <'a, W: Write> Encoder<'a, W> {
 | 
			
		||||
        if is_virtual_file_system(magic) {
 | 
			
		||||
            include_children = false;
 | 
			
		||||
        } else {
 | 
			
		||||
            include_children = (self.root_st_dev == dir_stat.st_dev) || self.all_file_systems;
 | 
			
		||||
            if let Some(set) = &self.device_set {
 | 
			
		||||
                include_children = set.contains(&dir_stat.st_dev);
 | 
			
		||||
            } else {
 | 
			
		||||
                include_children = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Expand the exclude match pattern inherited from the parent by local entries, if present
 | 
			
		||||
@ -859,7 +875,11 @@ impl <'a, W: Write> Encoder<'a, W> {
 | 
			
		||||
        if is_virtual_file_system(magic) {
 | 
			
		||||
            include_payload = false;
 | 
			
		||||
        } else {
 | 
			
		||||
            include_payload = (stat.st_dev == self.root_st_dev) || self.all_file_systems;
 | 
			
		||||
            if let Some(ref set) = &self.device_set {
 | 
			
		||||
                include_payload = set.contains(&stat.st_dev);
 | 
			
		||||
            } else {
 | 
			
		||||
                include_payload = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if !include_payload {
 | 
			
		||||
@ -986,7 +1006,11 @@ impl <'a, W: Write> Encoder<'a, W> {
 | 
			
		||||
        if is_virtual_file_system(magic) {
 | 
			
		||||
            include_payload = false;
 | 
			
		||||
        } else {
 | 
			
		||||
            include_payload = (stat.st_dev == self.root_st_dev) || self.all_file_systems;
 | 
			
		||||
            if let Some(set) = &self.device_set {
 | 
			
		||||
                include_payload = set.contains(&stat.st_dev);
 | 
			
		||||
            } else {
 | 
			
		||||
                include_payload = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if !include_payload {
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user