From 8c15560b681d6a857c4f475fdb9853d32be8e397 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Sat, 5 Dec 2020 10:45:08 +0100 Subject: [PATCH] tape: add file format definitions --- src/lib.rs | 2 + src/tape/file_formats.rs | 141 +++++++++++++++++++++++++++++++++++++++ src/tape/mod.rs | 1 + 3 files changed, 144 insertions(+) create mode 100644 src/tape/file_formats.rs create mode 100644 src/tape/mod.rs diff --git a/src/lib.rs b/src/lib.rs index cc51b2e0..200cf496 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,3 +30,5 @@ pub mod auth_helpers; pub mod auth; pub mod rrd; + +pub mod tape; diff --git a/src/tape/file_formats.rs b/src/tape/file_formats.rs new file mode 100644 index 00000000..551c8d62 --- /dev/null +++ b/src/tape/file_formats.rs @@ -0,0 +1,141 @@ +use anyhow::{bail, Error}; +use ::serde::{Deserialize, Serialize}; +use endian_trait::Endian; +use bitflags::bitflags; + +use proxmox::tools::Uuid; + +/// We use 256KB blocksize (always) +pub const PROXMOX_TAPE_BLOCK_SIZE: usize = 256*1024; + +// openssl::sha::sha256(b"Proxmox Tape Block Header v1.0")[0..8] +pub const PROXMOX_TAPE_BLOCK_HEADER_MAGIC_1_0: [u8; 8] = [220, 189, 175, 202, 235, 160, 165, 40]; + +// openssl::sha::sha256(b"Proxmox Backup Content Header v1.0")[0..8]; +pub const PROXMOX_BACKUP_CONTENT_HEADER_MAGIC_1_0: [u8; 8] = [99, 238, 20, 159, 205, 242, 155, 12]; +// openssl::sha::sha256(b"Proxmox Backup Tape Label v1.0")[0..8]; +pub const PROXMOX_BACKUP_DRIVE_LABEL_MAGIC_1_0: [u8; 8] = [42, 5, 191, 60, 176, 48, 170, 57]; +// openssl::sha::sha256(b"Proxmox Backup MediaSet Label v1.0") +pub const PROXMOX_BACKUP_MEDIA_SET_LABEL_MAGIC_1_0: [u8; 8] = [8, 96, 99, 249, 47, 151, 83, 216]; + +// openssl::sha::sha256(b"Proxmox Backup Chunk Archive v1.0")[0..8] +pub const PROXMOX_BACKUP_CHUNK_ARCHIVE_MAGIC_1_0: [u8; 8] = [62, 173, 167, 95, 49, 76, 6, 110]; +// openssl::sha::sha256(b"Proxmox Backup Chunk Archive Entry v1.0")[0..8] +pub const PROXMOX_BACKUP_CHUNK_ARCHIVE_ENTRY_MAGIC_1_0: [u8; 8] = [72, 87, 109, 242, 222, 66, 143, 220]; + +// openssl::sha::sha256(b"Proxmox Backup Snapshot Archive v1.0")[0..8]; +pub const PROXMOX_BACKUP_SNAPSHOT_ARCHIVE_MAGIC_1_0: [u8; 8] = [9, 182, 2, 31, 125, 232, 114, 133]; + +/// Note: this struct is large, never put this on the stack! +/// so we use an unsized type to avoid that. +#[repr(C,packed)] +pub struct BlockHeader { + pub magic: [u8; 8], + pub flags: BlockHeaderFlags, + /// size as 3 bytes unsigned, little endian + pub size: [u8; 3], + /// block sequence number + pub seq_nr: u32, + pub payload: [u8], +} + +bitflags! { + pub struct BlockHeaderFlags: u8 { + /// Marks the last block in a stream. + const END_OF_STREAM = 0b00000001; + /// Mark multivolume streams (when set in the last block) + const INCOMPLETE = 0b00000010; + } +} + +#[derive(Endian)] +#[repr(C,packed)] +pub struct ChunkArchiveEntryHeader { + pub magic: [u8; 8], + pub digest: [u8; 32], + pub size: u64, +} + +#[derive(Endian, Copy, Clone, Debug)] +#[repr(C,packed)] +pub struct MediaContentHeader { + pub magic: [u8; 8], + pub content_magic: [u8; 8], + pub uuid: [u8; 16], + pub ctime: i64, + pub size: u32, + pub part_number: u8, + pub reserved_0: u8, + pub reserved_1: u8, + pub reserved_2: u8, +} + +impl MediaContentHeader { + + pub fn new(content_magic: [u8; 8], size: u32) -> Self { + let uuid = *proxmox::tools::uuid::Uuid::generate() + .into_inner(); + Self { + magic: PROXMOX_BACKUP_CONTENT_HEADER_MAGIC_1_0, + content_magic, + uuid, + ctime: proxmox::tools::time::epoch_i64(), + size, + part_number: 0, + reserved_0: 0, + reserved_1: 0, + reserved_2: 0, + } + } + + pub fn check(&self, content_magic: [u8; 8], min_size: u32, max_size: u32) -> Result<(), Error> { + if self.magic != PROXMOX_BACKUP_CONTENT_HEADER_MAGIC_1_0 { + bail!("MediaContentHeader: wrong magic"); + } + if self.content_magic != content_magic { + bail!("MediaContentHeader: wrong content magic"); + } + if self.size < min_size || self.size > max_size { + bail!("MediaContentHeader: got unexpected size"); + } + Ok(()) + } + + pub fn content_uuid(&self) -> Uuid { + Uuid::from(self.uuid) + } +} + +#[derive(Serialize,Deserialize,Clone,Debug)] +pub struct DriveLabel { + /// Unique ID + pub uuid: Uuid, + /// Media Changer ID or Barcode + pub changer_id: String, + /// Creation time stamp + pub ctime: i64, +} + + +#[derive(Serialize,Deserialize,Clone,Debug)] +pub struct MediaSetLabel { + pub pool: String, + /// MediaSet Uuid. We use the all-zero Uuid to reseve an empty media for a specific pool + pub uuid: Uuid, + /// MediaSet media sequence number + pub seq_nr: u64, + /// Creation time stamp + pub ctime: i64, +} + +impl MediaSetLabel { + + pub fn with_data(pool: &str, uuid: Uuid, seq_nr: u64, ctime: i64) -> Self { + Self { + pool: pool.to_string(), + uuid, + seq_nr, + ctime, + } + } +} diff --git a/src/tape/mod.rs b/src/tape/mod.rs new file mode 100644 index 00000000..b5858a6f --- /dev/null +++ b/src/tape/mod.rs @@ -0,0 +1 @@ +pub mod file_formats;