proxmox-backup/src/tape/helpers/blocked_writer.rs

125 lines
3.5 KiB
Rust
Raw Normal View History

use std::io::Write;
use proxmox::tools::vec;
use crate::tape::{
TapeWrite,
tape_device_write_block,
file_formats::{
BlockHeader,
BlockHeaderFlags,
},
};
/// Assemble and write blocks of data
///
/// This type implement 'TapeWrite'. Data written is assembled to
/// equally sized blocks (see 'BlockHeader'), which are then written
/// to the underlying writer.
pub struct BlockedWriter<W> {
writer: W,
buffer: Box<BlockHeader>,
buffer_pos: usize,
seq_nr: u32,
logical_end_of_media: bool,
bytes_written: usize,
}
impl <W: Write> BlockedWriter<W> {
/// Allow access to underlying writer
pub fn writer_ref_mut(&mut self) -> &mut W {
&mut self.writer
}
/// Creates a new instance.
pub fn new(writer: W) -> Self {
Self {
writer,
buffer: BlockHeader::new(),
buffer_pos: 0,
seq_nr: 0,
logical_end_of_media: false,
bytes_written: 0,
}
}
fn write_block(buffer: &BlockHeader, writer: &mut W) -> Result<bool, std::io::Error> {
let data = unsafe {
std::slice::from_raw_parts(
(buffer as *const BlockHeader) as *const u8,
BlockHeader::SIZE,
)
};
tape_device_write_block(writer, data)
}
fn write(&mut self, data: &[u8]) -> Result<usize, std::io::Error> {
if data.is_empty() { return Ok(0); }
let rest = self.buffer.payload.len() - self.buffer_pos;
let bytes = if data.len() < rest { data.len() } else { rest };
self.buffer.payload[self.buffer_pos..(self.buffer_pos+bytes)]
.copy_from_slice(&data[..bytes]);
let rest = rest - bytes;
if rest == 0 {
self.buffer.flags = BlockHeaderFlags::empty();
self.buffer.set_size(self.buffer.payload.len());
self.buffer.set_seq_nr(self.seq_nr);
self.seq_nr += 1;
let leom = Self::write_block(&self.buffer, &mut self.writer)?;
if leom { self.logical_end_of_media = true; }
self.buffer_pos = 0;
self.bytes_written += BlockHeader::SIZE;
} else {
self.buffer_pos = self.buffer_pos + bytes;
}
Ok(bytes)
}
}
impl <W: Write> TapeWrite for BlockedWriter<W> {
fn write_all(&mut self, mut data: &[u8]) -> Result<bool, std::io::Error> {
while !data.is_empty() {
match self.write(data) {
Ok(n) => data = &data[n..],
Err(e) => return Err(e),
}
}
Ok(self.logical_end_of_media)
}
fn bytes_written(&self) -> usize {
self.bytes_written
}
/// flush last block, set END_OF_STREAM flag
///
/// Note: This may write an empty block just including the
/// END_OF_STREAM flag.
fn finish(&mut self, incomplete: bool) -> Result<bool, std::io::Error> {
vec::clear(&mut self.buffer.payload[self.buffer_pos..]);
self.buffer.flags = BlockHeaderFlags::END_OF_STREAM;
if incomplete { self.buffer.flags |= BlockHeaderFlags::INCOMPLETE; }
self.buffer.set_size(self.buffer_pos);
self.buffer.set_seq_nr(self.seq_nr);
self.seq_nr += 1;
self.bytes_written += BlockHeader::SIZE;
Self::write_block(&self.buffer, &mut self.writer)
}
/// Returns if the writer already detected the logical end of media
fn logical_end_of_media(&self) -> bool {
self.logical_end_of_media
}
}