137 lines
4.0 KiB
Rust
137 lines
4.0 KiB
Rust
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
|
|
}
|
|
|
|
}
|