For better mount point include control...
//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;
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
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 };
&filename,
&target,
chunk_size_opt,
- all_file_systems,
+ devices.clone(),
verbose,
crypt_config.clone(),
)?;
).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."))
use std::sync::Arc;
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
+use std::collections::HashSet;
use proxmox_backup::pxar;
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(
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()?;
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;
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()?;
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);
}
});
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)
}
}
use failure::*;
use endian_trait::Endian;
-use std::collections::HashMap;
+use std::collections::{HashSet, HashMap};
use super::format_definition::*;
use super::binary_search_tree::*;
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,
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> {
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) {
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,
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
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 {
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 {
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")