tape: add multi volume reader/writer implementations
We currently do not use it. Added anaways, to show the possibility.
This commit is contained in:
parent
f47e035721
commit
983e929e25
@ -13,6 +13,12 @@ pub use chunk_archive::*;
|
||||
mod snapshot_archive;
|
||||
pub use snapshot_archive::*;
|
||||
|
||||
mod multi_volume_writer;
|
||||
pub use multi_volume_writer::*;
|
||||
|
||||
mod multi_volume_reader;
|
||||
pub use multi_volume_reader::*;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::{bail, Error};
|
||||
|
102
src/tape/file_formats/multi_volume_reader.rs
Normal file
102
src/tape/file_formats/multi_volume_reader.rs
Normal file
@ -0,0 +1,102 @@
|
||||
use std::io::{Read};
|
||||
|
||||
use anyhow::{bail, Error};
|
||||
|
||||
use proxmox::tools::io::ReadExt;
|
||||
|
||||
use crate::tape::{
|
||||
TapeRead,
|
||||
file_formats::MediaContentHeader,
|
||||
};
|
||||
|
||||
/// Read multi volume data streams written by `MultiVolumeWriter`
|
||||
///
|
||||
/// Note: We do not use this feature currently.
|
||||
pub struct MultiVolumeReader<'a> {
|
||||
reader: Option<Box<dyn TapeRead + 'a>>,
|
||||
next_reader_fn: Box<dyn 'a + FnMut() -> Result<Box<dyn TapeRead +'a>, Error>>,
|
||||
complete: bool,
|
||||
header: MediaContentHeader,
|
||||
}
|
||||
|
||||
impl <'a> MultiVolumeReader<'a> {
|
||||
|
||||
/// Creates a new instance
|
||||
pub fn new(
|
||||
reader: Box<dyn TapeRead +'a>,
|
||||
header: MediaContentHeader,
|
||||
next_reader_fn: Box<dyn 'a + FnMut() -> Result<Box<dyn TapeRead +'a>, Error>>,
|
||||
) -> Result<Self, Error> {
|
||||
|
||||
if header.part_number != 0 {
|
||||
bail!("MultiVolumeReader::new - got wrong header part_number ({} != 0)",
|
||||
header.part_number);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
reader: Some(reader),
|
||||
next_reader_fn,
|
||||
complete: false,
|
||||
header,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl <'a> Read for MultiVolumeReader<'a> {
|
||||
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
|
||||
if self.complete {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
if self.reader.is_none() {
|
||||
let mut reader = (self.next_reader_fn)()
|
||||
.map_err(|err| proxmox::io_format_err!("multi-volume next failed: {}", err))?;
|
||||
|
||||
proxmox::try_block!({
|
||||
let part_header: MediaContentHeader = unsafe { reader.read_le_value()? };
|
||||
self.reader = Some(reader);
|
||||
|
||||
if part_header.uuid != self.header.uuid {
|
||||
proxmox::io_bail!("got wrong part uuid");
|
||||
}
|
||||
if part_header.content_magic!= self.header.content_magic {
|
||||
proxmox::io_bail!("got wrong part content magic");
|
||||
}
|
||||
|
||||
let expect_part_number = self.header.part_number + 1;
|
||||
|
||||
if part_header.part_number != expect_part_number {
|
||||
proxmox::io_bail!("got wrong part number ({} != {})",
|
||||
part_header.part_number, expect_part_number);
|
||||
}
|
||||
|
||||
self.header.part_number = expect_part_number;
|
||||
|
||||
Ok(())
|
||||
}).map_err(|err| {
|
||||
proxmox::io_format_err!("multi-volume read content header failed: {}", err)
|
||||
})?;
|
||||
}
|
||||
|
||||
match self.reader {
|
||||
None => unreachable!(),
|
||||
Some(ref mut reader) => {
|
||||
match reader.read(buf) {
|
||||
Ok(0) => {
|
||||
if reader.is_incomplete()? {
|
||||
self.reader = None;
|
||||
self.read(buf)
|
||||
} else {
|
||||
self.reader = None;
|
||||
self.complete = true;
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
Ok(n) => Ok(n),
|
||||
Err(err) => Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
136
src/tape/file_formats/multi_volume_writer.rs
Normal file
136
src/tape/file_formats/multi_volume_writer.rs
Normal file
@ -0,0 +1,136 @@
|
||||
use anyhow::Error;
|
||||
|
||||
use proxmox::tools::Uuid;
|
||||
|
||||
use crate::tape::{
|
||||
TapeWrite,
|
||||
file_formats::MediaContentHeader,
|
||||
};
|
||||
|
||||
/// Writes data streams using multiple volumes
|
||||
///
|
||||
/// Note: We do not use this feature currently.
|
||||
pub struct MultiVolumeWriter<'a> {
|
||||
writer: Option<Box<dyn TapeWrite + 'a>>,
|
||||
next_writer_fn: Box<dyn 'a + FnMut() -> Result<Box<dyn TapeWrite +'a>, Error>>,
|
||||
got_leom: bool,
|
||||
finished: bool,
|
||||
wrote_header: bool,
|
||||
header: MediaContentHeader,
|
||||
header_data: Vec<u8>,
|
||||
bytes_written: usize, // does not include bytes from current writer
|
||||
}
|
||||
|
||||
impl <'a> MultiVolumeWriter<'a> {
|
||||
|
||||
/// Creates a new instance
|
||||
pub fn new(
|
||||
writer: Box<dyn TapeWrite +'a>,
|
||||
content_magic: [u8; 8],
|
||||
header_data: Vec<u8>,
|
||||
next_writer_fn: Box<dyn 'a + FnMut() -> Result<Box<dyn TapeWrite + 'a>, Error>>,
|
||||
) -> Self {
|
||||
|
||||
let header = MediaContentHeader::new(content_magic, header_data.len() as u32);
|
||||
|
||||
Self {
|
||||
writer: Some(writer),
|
||||
next_writer_fn,
|
||||
got_leom: false,
|
||||
finished: false,
|
||||
header,
|
||||
header_data,
|
||||
wrote_header: false,
|
||||
bytes_written: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the cuntent Uuid with the current part number
|
||||
pub fn uuid_and_part_number(&self) -> (Uuid, usize) {
|
||||
(self.header.uuid.into(), self.header.part_number as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl <'a> TapeWrite for MultiVolumeWriter<'a> {
|
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> Result<bool, std::io::Error> {
|
||||
|
||||
if self.finished {
|
||||
proxmox::io_bail!("multi-volume writer already finished: internal error");
|
||||
}
|
||||
|
||||
if self.got_leom {
|
||||
if !self.wrote_header {
|
||||
proxmox::io_bail!("multi-volume writer: got LEOM before writing anything - internal error");
|
||||
}
|
||||
let mut writer = match self.writer.take() {
|
||||
Some(writer) => writer,
|
||||
None => proxmox::io_bail!("multi-volume writer: no writer -internal error"),
|
||||
};
|
||||
self.bytes_written = writer.bytes_written();
|
||||
writer.finish(true)?;
|
||||
}
|
||||
|
||||
if self.writer.is_none() {
|
||||
if self.header.part_number >= 255 {
|
||||
proxmox::io_bail!("multi-volume writer: too many parts");
|
||||
}
|
||||
self.writer = Some(
|
||||
(self.next_writer_fn)()
|
||||
.map_err(|err| proxmox::io_format_err!("multi-volume get next volume failed: {}", err))?
|
||||
);
|
||||
self.got_leom = false;
|
||||
self.wrote_header = false;
|
||||
self.header.part_number += 1;
|
||||
}
|
||||
|
||||
let leom = match self.writer {
|
||||
None => unreachable!(),
|
||||
Some(ref mut writer) => {
|
||||
if !self.wrote_header {
|
||||
writer.write_header(&self.header, &self.header_data)?;
|
||||
self.wrote_header = true;
|
||||
}
|
||||
writer.write_all(buf)?
|
||||
}
|
||||
};
|
||||
|
||||
if leom { self.got_leom = true; }
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn bytes_written(&self) -> usize {
|
||||
let mut bytes_written = self.bytes_written;
|
||||
if let Some(ref writer) = self.writer {
|
||||
bytes_written += writer.bytes_written();
|
||||
}
|
||||
bytes_written
|
||||
}
|
||||
|
||||
fn finish(&mut self, incomplete: bool) -> Result<bool, std::io::Error> {
|
||||
if incomplete {
|
||||
proxmox::io_bail!(
|
||||
"incomplete flag makes no sense for multi-volume stream: internal error");
|
||||
}
|
||||
|
||||
match self.writer.take() {
|
||||
None if self.finished => proxmox::io_bail!(
|
||||
"multi-volume writer already finished: internal error"),
|
||||
None => Ok(false),
|
||||
Some(ref mut writer) => {
|
||||
self.finished = true;
|
||||
if !self.wrote_header {
|
||||
writer.write_header(&self.header, &self.header_data)?;
|
||||
self.wrote_header = true;
|
||||
}
|
||||
writer.finish(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn logical_end_of_media(&self) -> bool {
|
||||
self.got_leom
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user