mod datastore;
pub use datastore::{check_backup_owner, DataStore};
+
+mod snapshot_reader;
+pub use snapshot_reader::SnapshotReader;
--- /dev/null
+use std::path::Path;
+use std::sync::Arc;
+use std::os::unix::io::{AsRawFd, FromRawFd};
+use std::fs::File;
+
+use anyhow::{bail, Error};
+use nix::dir::Dir;
+
+use crate::backup_info::BackupDir;
+use crate::index::IndexFile;
+use crate::fixed_index::FixedIndexReader;
+use crate::dynamic_index::DynamicIndexReader;
+use crate::manifest::{archive_type, ArchiveType, CLIENT_LOG_BLOB_NAME, MANIFEST_BLOB_NAME};
+use crate::DataStore;
+use pbs_tools::fs::lock_dir_noblock_shared;
+
+/// Helper to access the contents of a datastore backup snapshot
+///
+/// This make it easy to iterate over all used chunks and files.
+pub struct SnapshotReader {
+ snapshot: BackupDir,
+ datastore_name: String,
+ file_list: Vec<String>,
+ locked_dir: Dir,
+}
+
+impl SnapshotReader {
+
+ /// Lock snapshot, reads the manifest and returns a new instance
+ pub fn new(datastore: Arc<DataStore>, snapshot: BackupDir) -> Result<Self, Error> {
+
+ let snapshot_path = datastore.snapshot_path(&snapshot);
+
+ let locked_dir = lock_dir_noblock_shared(
+ &snapshot_path,
+ "snapshot",
+ "locked by another operation")?;
+
+ let datastore_name = datastore.name().to_string();
+
+ let manifest = match datastore.load_manifest(&snapshot) {
+ Ok((manifest, _)) => manifest,
+ Err(err) => {
+ bail!("manifest load error on datastore '{}' snapshot '{}' - {}",
+ datastore_name, snapshot, err);
+ }
+ };
+
+ let mut client_log_path = snapshot_path;
+ client_log_path.push(CLIENT_LOG_BLOB_NAME);
+
+ let mut file_list = Vec::new();
+ file_list.push(MANIFEST_BLOB_NAME.to_string());
+ for item in manifest.files() { file_list.push(item.filename.clone()); }
+ if client_log_path.exists() {
+ file_list.push(CLIENT_LOG_BLOB_NAME.to_string());
+ }
+
+ Ok(Self { snapshot, datastore_name, file_list, locked_dir })
+ }
+
+ /// Return the snapshot directory
+ pub fn snapshot(&self) -> &BackupDir {
+ &self.snapshot
+ }
+
+ /// Return the datastore name
+ pub fn datastore_name(&self) -> &str {
+ &self.datastore_name
+ }
+
+ /// Returns the list of files the snapshot refers to.
+ pub fn file_list(&self) -> &Vec<String> {
+ &self.file_list
+ }
+
+ /// Opens a file inside the snapshot (using openat) for reading
+ pub fn open_file(&self, filename: &str) -> Result<File, Error> {
+ let raw_fd = nix::fcntl::openat(
+ self.locked_dir.as_raw_fd(),
+ Path::new(filename),
+ nix::fcntl::OFlag::O_RDONLY,
+ nix::sys::stat::Mode::empty(),
+ )?;
+ let file = unsafe { File::from_raw_fd(raw_fd) };
+ Ok(file)
+ }
+
+ /// Returns an iterator for all used chunks.
+ pub fn chunk_iterator(&self) -> Result<SnapshotChunkIterator, Error> {
+ SnapshotChunkIterator::new(&self)
+ }
+}
+
+/// Iterates over all chunks used by a backup snapshot
+///
+/// Note: The iterator returns a `Result`, and the iterator state is
+/// undefined after the first error. So it make no sense to continue
+/// iteration after the first error.
+pub struct SnapshotChunkIterator<'a> {
+ snapshot_reader: &'a SnapshotReader,
+ todo_list: Vec<String>,
+ current_index: Option<(Arc<Box<dyn IndexFile + Send>>, usize, Vec<(usize, u64)>)>,
+}
+
+impl <'a> Iterator for SnapshotChunkIterator<'a> {
+ type Item = Result<[u8; 32], Error>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ proxmox::try_block!({
+ loop {
+ if self.current_index.is_none() {
+ if let Some(filename) = self.todo_list.pop() {
+ let file = self.snapshot_reader.open_file(&filename)?;
+ let index: Box<dyn IndexFile + Send> = match archive_type(&filename)? {
+ ArchiveType::FixedIndex => Box::new(FixedIndexReader::new(file)?),
+ ArchiveType::DynamicIndex => Box::new(DynamicIndexReader::new(file)?),
+ _ => bail!("SnapshotChunkIterator: got unknown file type - internal error"),
+ };
+
+ let datastore =
+ DataStore::lookup_datastore(self.snapshot_reader.datastore_name())?;
+ let order = datastore.get_chunks_in_order(&index, |_| false, |_| Ok(()))?;
+
+ self.current_index = Some((Arc::new(index), 0, order));
+ } else {
+ return Ok(None);
+ }
+ }
+ let (index, pos, list) = self.current_index.take().unwrap();
+ if pos < list.len() {
+ let (real_pos, _) = list[pos];
+ let digest = *index.index_digest(real_pos).unwrap();
+ self.current_index = Some((index, pos + 1, list));
+ return Ok(Some(digest));
+ } else {
+ // pop next index
+ }
+ }
+ }).transpose()
+ }
+}
+
+impl <'a> SnapshotChunkIterator<'a> {
+
+ pub fn new(snapshot_reader: &'a SnapshotReader) -> Result<Self, Error> {
+
+ let mut todo_list = Vec::new();
+
+ for filename in snapshot_reader.file_list() {
+ match archive_type(filename)? {
+ ArchiveType::FixedIndex | ArchiveType::DynamicIndex => {
+ todo_list.push(filename.to_owned());
+ },
+ ArchiveType::Blob => { /* no chunks, do nothing */ },
+ }
+ }
+
+ Ok(Self { snapshot_reader, todo_list, current_index: None })
+ }
+}
UPID_SCHEMA, JOB_ID_SCHEMA, PRIV_DATASTORE_READ, PRIV_TAPE_AUDIT, PRIV_TAPE_WRITE,
};
-use pbs_datastore::{DataStore, StoreProgress};
+use pbs_datastore::{DataStore, StoreProgress, SnapshotReader};
use pbs_datastore::backup_info::{BackupDir, BackupInfo};
use pbs_tools::{task_log, task_warn, task::WorkerTaskContext};
use pbs_config::CachedUserInfo;
compute_schedule_status,
},
},
- backup::SnapshotReader,
tape::{
TAPE_STATUS_DIR,
Inventory,
mod verify;
pub use verify::*;
-
-mod snapshot_reader;
-pub use snapshot_reader::SnapshotReader;
+++ /dev/null
-use std::path::Path;
-use std::sync::Arc;
-use std::os::unix::io::{AsRawFd, FromRawFd};
-use std::fs::File;
-
-use anyhow::{bail, Error};
-use nix::dir::Dir;
-
-use pbs_datastore::backup_info::BackupDir;
-use pbs_datastore::index::IndexFile;
-use pbs_datastore::fixed_index::FixedIndexReader;
-use pbs_datastore::dynamic_index::DynamicIndexReader;
-use pbs_datastore::manifest::{archive_type, ArchiveType, CLIENT_LOG_BLOB_NAME, MANIFEST_BLOB_NAME};
-use pbs_datastore::DataStore;
-use pbs_tools::fs::lock_dir_noblock_shared;
-
-/// Helper to access the contents of a datastore backup snapshot
-///
-/// This make it easy to iterate over all used chunks and files.
-pub struct SnapshotReader {
- snapshot: BackupDir,
- datastore_name: String,
- file_list: Vec<String>,
- locked_dir: Dir,
-}
-
-impl SnapshotReader {
-
- /// Lock snapshot, reads the manifest and returns a new instance
- pub fn new(datastore: Arc<DataStore>, snapshot: BackupDir) -> Result<Self, Error> {
-
- let snapshot_path = datastore.snapshot_path(&snapshot);
-
- let locked_dir = lock_dir_noblock_shared(
- &snapshot_path,
- "snapshot",
- "locked by another operation")?;
-
- let datastore_name = datastore.name().to_string();
-
- let manifest = match datastore.load_manifest(&snapshot) {
- Ok((manifest, _)) => manifest,
- Err(err) => {
- bail!("manifest load error on datastore '{}' snapshot '{}' - {}",
- datastore_name, snapshot, err);
- }
- };
-
- let mut client_log_path = snapshot_path;
- client_log_path.push(CLIENT_LOG_BLOB_NAME);
-
- let mut file_list = Vec::new();
- file_list.push(MANIFEST_BLOB_NAME.to_string());
- for item in manifest.files() { file_list.push(item.filename.clone()); }
- if client_log_path.exists() {
- file_list.push(CLIENT_LOG_BLOB_NAME.to_string());
- }
-
- Ok(Self { snapshot, datastore_name, file_list, locked_dir })
- }
-
- /// Return the snapshot directory
- pub fn snapshot(&self) -> &BackupDir {
- &self.snapshot
- }
-
- /// Return the datastore name
- pub fn datastore_name(&self) -> &str {
- &self.datastore_name
- }
-
- /// Returns the list of files the snapshot refers to.
- pub fn file_list(&self) -> &Vec<String> {
- &self.file_list
- }
-
- /// Opens a file inside the snapshot (using openat) for reading
- pub fn open_file(&self, filename: &str) -> Result<File, Error> {
- let raw_fd = nix::fcntl::openat(
- self.locked_dir.as_raw_fd(),
- Path::new(filename),
- nix::fcntl::OFlag::O_RDONLY,
- nix::sys::stat::Mode::empty(),
- )?;
- let file = unsafe { File::from_raw_fd(raw_fd) };
- Ok(file)
- }
-
- /// Returns an iterator for all used chunks.
- pub fn chunk_iterator(&self) -> Result<SnapshotChunkIterator, Error> {
- SnapshotChunkIterator::new(&self)
- }
-}
-
-/// Iterates over all chunks used by a backup snapshot
-///
-/// Note: The iterator returns a `Result`, and the iterator state is
-/// undefined after the first error. So it make no sense to continue
-/// iteration after the first error.
-pub struct SnapshotChunkIterator<'a> {
- snapshot_reader: &'a SnapshotReader,
- todo_list: Vec<String>,
- current_index: Option<(Arc<Box<dyn IndexFile + Send>>, usize, Vec<(usize, u64)>)>,
-}
-
-impl <'a> Iterator for SnapshotChunkIterator<'a> {
- type Item = Result<[u8; 32], Error>;
-
- fn next(&mut self) -> Option<Self::Item> {
- proxmox::try_block!({
- loop {
- if self.current_index.is_none() {
- if let Some(filename) = self.todo_list.pop() {
- let file = self.snapshot_reader.open_file(&filename)?;
- let index: Box<dyn IndexFile + Send> = match archive_type(&filename)? {
- ArchiveType::FixedIndex => Box::new(FixedIndexReader::new(file)?),
- ArchiveType::DynamicIndex => Box::new(DynamicIndexReader::new(file)?),
- _ => bail!("SnapshotChunkIterator: got unknown file type - internal error"),
- };
-
- let datastore =
- DataStore::lookup_datastore(self.snapshot_reader.datastore_name())?;
- let order = datastore.get_chunks_in_order(&index, |_| false, |_| Ok(()))?;
-
- self.current_index = Some((Arc::new(index), 0, order));
- } else {
- return Ok(None);
- }
- }
- let (index, pos, list) = self.current_index.take().unwrap();
- if pos < list.len() {
- let (real_pos, _) = list[pos];
- let digest = *index.index_digest(real_pos).unwrap();
- self.current_index = Some((index, pos + 1, list));
- return Ok(Some(digest));
- } else {
- // pop next index
- }
- }
- }).transpose()
- }
-}
-
-impl <'a> SnapshotChunkIterator<'a> {
-
- pub fn new(snapshot_reader: &'a SnapshotReader) -> Result<Self, Error> {
-
- let mut todo_list = Vec::new();
-
- for filename in snapshot_reader.file_list() {
- match archive_type(filename)? {
- ArchiveType::FixedIndex | ArchiveType::DynamicIndex => {
- todo_list.push(filename.to_owned());
- },
- ArchiveType::Blob => { /* no chunks, do nothing */ },
- }
- }
-
- Ok(Self { snapshot_reader, todo_list, current_index: None })
- }
-}
PROXMOX_TAPE_BLOCK_SIZE,
TapeWrite, MediaContentHeader,
};
-
-use crate::backup::SnapshotReader;
+use pbs_datastore::SnapshotReader;
use crate::tape::file_formats::{
PROXMOX_BACKUP_SNAPSHOT_ARCHIVE_MAGIC_1_1,
TapeWrite,
sg_tape::tape_alert_flags_critical,
};
-use pbs_datastore::DataStore;
+use pbs_datastore::{DataStore, SnapshotReader};
use proxmox_rest_server::WorkerTask;
use crate::{
- backup::SnapshotReader,
tape::{
TAPE_STATUS_DIR,
MAX_CHUNK_ARCHIVE_SIZE,
use anyhow::{format_err, Error};
-use pbs_datastore::{DataStore, DataBlob};
+use pbs_datastore::{DataStore, DataBlob, SnapshotReader};
-use crate::backup::SnapshotReader;
use crate::tape::CatalogSet;
/// Chunk iterator which use a separate thread to read chunks