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;
|
mod snapshot_archive;
|
||||||
pub use 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 std::collections::HashMap;
|
||||||
|
|
||||||
use anyhow::{bail, Error};
|
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