tape: implement fast catalog restore

This commit is contained in:
Dietmar Maurer 2021-03-23 13:39:33 +01:00
parent c6f55139f8
commit 074503f288
2 changed files with 153 additions and 4 deletions

View File

@ -48,7 +48,10 @@ use crate::{
MamAttribute,
LinuxDriveAndMediaStatus,
},
tape::restore::restore_media,
tape::restore::{
fast_catalog_restore,
restore_media,
},
},
server::WorkerTask,
tape::{
@ -1279,7 +1282,7 @@ pub fn catalog_media(
let mut inventory = Inventory::new(status_path);
let _media_set_lock = match media_id.media_set_label {
let (_media_set_lock, media_set_uuid) = match media_id.media_set_label {
None => {
worker.log("media is empty");
let _lock = lock_unassigned_media_pool(status_path)?;
@ -1307,7 +1310,7 @@ pub fn catalog_media(
inventory.store(media_id.clone(), false)?;
media_set_lock
(media_set_lock, &set.uuid)
}
};
@ -1315,7 +1318,14 @@ pub fn catalog_media(
bail!("media catalog exists (please use --force to overwrite)");
}
// fixme: implement fast catalog restore
let media_set = inventory.compute_media_set_members(media_set_uuid)?;
if fast_catalog_restore(&worker, &mut drive, &media_set, &media_id.label.uuid)? {
return Ok(())
}
task_log!(worker, "no catalog found - scaning entire media now");
restore_media(&worker, &mut drive, &media_id, None, verbose)?;
Ok(())

View File

@ -1,6 +1,7 @@
use std::path::Path;
use std::ffi::OsStr;
use std::convert::TryFrom;
use std::io::{Seek, SeekFrom};
use anyhow::{bail, format_err, Error};
use serde_json::Value;
@ -26,6 +27,7 @@ use proxmox::{
use crate::{
task_log,
task_warn,
task::TaskState,
tools::compute_file_csum,
api2::types::{
@ -65,6 +67,7 @@ use crate::{
TAPE_STATUS_DIR,
TapeRead,
MediaId,
MediaSet,
MediaCatalog,
Inventory,
lock_media_set,
@ -76,10 +79,12 @@ use crate::{
PROXMOX_BACKUP_CONTENT_HEADER_MAGIC_1_0,
PROXMOX_BACKUP_CHUNK_ARCHIVE_MAGIC_1_0,
PROXMOX_BACKUP_CHUNK_ARCHIVE_MAGIC_1_1,
PROXMOX_BACKUP_CATALOG_ARCHIVE_MAGIC_1_0,
MediaContentHeader,
ChunkArchiveHeader,
ChunkArchiveDecoder,
SnapshotArchiveHeader,
CatalogArchiveHeader,
},
drive::{
TapeDriver,
@ -661,3 +666,137 @@ fn try_restore_snapshot_archive<R: pxar::decoder::SeqRead>(
Ok(())
}
/// Try to restore media catalogs (form catalog_archives)
pub fn fast_catalog_restore(
worker: &WorkerTask,
drive: &mut Box<dyn TapeDriver>,
media_set: &MediaSet,
uuid: &Uuid, // current media Uuid
) -> Result<bool, Error> {
let status_path = Path::new(TAPE_STATUS_DIR);
let current_file_number = drive.current_file_number()?;
if current_file_number != 2 {
bail!("fast_catalog_restore: wrong media position - internal error");
}
let mut found_catalog = false;
let mut moved_to_eom = false;
loop {
let current_file_number = drive.current_file_number()?;
{ // limit reader scope
let mut reader = match drive.read_next_file()? {
None => {
task_log!(worker, "detected EOT after {} files", current_file_number);
break;
}
Some(reader) => reader,
};
let header: MediaContentHeader = unsafe { reader.read_le_value()? };
if header.magic != PROXMOX_BACKUP_CONTENT_HEADER_MAGIC_1_0 {
bail!("missing MediaContentHeader");
}
if header.content_magic == PROXMOX_BACKUP_CATALOG_ARCHIVE_MAGIC_1_0 {
task_log!(worker, "found catalog at pos {}", current_file_number);
let header_data = reader.read_exact_allocated(header.size as usize)?;
let archive_header: CatalogArchiveHeader = serde_json::from_slice(&header_data)
.map_err(|err| format_err!("unable to parse catalog archive header - {}", err))?;
if &archive_header.media_set_uuid != media_set.uuid() {
task_log!(worker, "skipping unrelated catalog at pos {}", current_file_number);
reader.skip_to_end()?; // read all data
continue;
}
let catalog_uuid = &archive_header.uuid;
let wanted = media_set
.media_list()
.iter()
.find(|e| {
match e {
None => false,
Some(uuid) => uuid == catalog_uuid,
}
})
.is_some();
if !wanted {
task_log!(worker, "skip catalog because media '{}' not inventarized", catalog_uuid);
reader.skip_to_end()?; // read all data
continue;
}
if catalog_uuid == uuid {
// always restore and overwrite catalog
} else {
// only restore if catalog does not exist
if MediaCatalog::exists(status_path, catalog_uuid) {
task_log!(worker, "catalog for media '{}' already exists", catalog_uuid);
reader.skip_to_end()?; // read all data
continue;
}
}
let mut file = MediaCatalog::create_temporary_database_file(status_path, catalog_uuid)?;
std::io::copy(&mut reader, &mut file)?;
file.seek(SeekFrom::Start(0))?;
match MediaCatalog::parse_catalog_header(&mut file)? {
(true, Some(media_uuid), Some(media_set_uuid)) => {
if &media_uuid != catalog_uuid {
task_log!(worker, "catalog uuid missmatch at pos {}", current_file_number);
continue;
}
if media_set_uuid != archive_header.media_set_uuid {
task_log!(worker, "catalog media_set missmatch at pos {}", current_file_number);
continue;
}
MediaCatalog::finish_temporary_database(status_path, &media_uuid, true)?;
if catalog_uuid == uuid {
task_log!(worker, "successfully restored catalog");
found_catalog = true
} else {
task_log!(worker, "successfully restored related catalog {}", media_uuid);
}
}
_ => {
task_warn!(worker, "got incomplete catalog header - skip file");
continue;
}
}
continue;
}
}
if moved_to_eom {
break; // already done - stop
}
moved_to_eom = true;
task_log!(worker, "searching for catalog at EOT (moving to EOT)");
drive.move_to_last_file()?;
let new_file_number = drive.current_file_number()?;
if new_file_number < (current_file_number + 1) {
break; // no new content - stop
}
}
Ok(found_catalog)
}