src/pxar/encoder.rs: allow to pass list of devices

For better mount point include control...
This commit is contained in:
Dietmar Maurer 2019-07-24 07:48:59 +02:00
parent fa5d6977dd
commit 2eeaacb974
5 changed files with 73 additions and 19 deletions

View File

@ -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."))

View File

@ -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()?;

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -26,7 +26,7 @@ fn run_test(dir_name: &str) -> Result<(), Error> {
let path = std::path::PathBuf::from(dir_name);
Encoder::encode(path, &mut dir, &mut writer, false, false, CA_FORMAT_DEFAULT)?;
Encoder::encode(path, &mut dir, &mut writer, None, false, CA_FORMAT_DEFAULT)?;
Command::new("cmp")
.arg("--verbose")