]> git.proxmox.com Git - proxmox-backup.git/blob - pbs-tape/src/blocked_writer.rs
update to proxmox-sys 0.2 crate
[proxmox-backup.git] / pbs-tape / src / blocked_writer.rs
1 use proxmox_io::vec;
2
3 use crate::{
4 TapeWrite,
5 BlockWrite,
6 BlockHeader,
7 BlockHeaderFlags,
8 };
9
10 /// Assemble and write blocks of data
11 ///
12 /// This type implement 'TapeWrite'. Data written is assembled to
13 /// equally sized blocks (see 'BlockHeader'), which are then written
14 /// to the underlying writer.
15 pub struct BlockedWriter<W: BlockWrite> {
16 writer: W,
17 buffer: Box<BlockHeader>,
18 buffer_pos: usize,
19 seq_nr: u32,
20 logical_end_of_media: bool,
21 bytes_written: usize,
22 wrote_eof: bool,
23 }
24
25 impl <W: BlockWrite> Drop for BlockedWriter<W> {
26
27 // Try to make sure to end the file with a filemark
28 fn drop(&mut self) {
29 if !self.wrote_eof {
30 let _ = self.writer.write_filemark();
31 }
32 }
33 }
34
35 impl <W: BlockWrite> BlockedWriter<W> {
36
37 /// Allow access to underlying writer
38 pub fn writer_ref_mut(&mut self) -> &mut W {
39 &mut self.writer
40 }
41
42 /// Creates a new instance.
43 pub fn new(writer: W) -> Self {
44 Self {
45 writer,
46 buffer: BlockHeader::new(),
47 buffer_pos: 0,
48 seq_nr: 0,
49 logical_end_of_media: false,
50 bytes_written: 0,
51 wrote_eof: false,
52 }
53 }
54
55 fn write_block(buffer: &BlockHeader, writer: &mut W) -> Result<bool, std::io::Error> {
56
57 let data = unsafe {
58 std::slice::from_raw_parts(
59 (buffer as *const BlockHeader) as *const u8,
60 BlockHeader::SIZE,
61 )
62 };
63 writer.write_block(data)
64 }
65
66 fn write_eof(&mut self) -> Result<(), std::io::Error> {
67 if self.wrote_eof {
68 proxmox_sys::io_bail!("BlockedWriter: detected multiple EOF writes");
69 }
70 self.wrote_eof = true;
71
72 self.writer.write_filemark()
73 }
74
75 fn write(&mut self, data: &[u8]) -> Result<usize, std::io::Error> {
76
77 if data.is_empty() { return Ok(0); }
78
79 let rest = self.buffer.payload.len() - self.buffer_pos;
80 let bytes = if data.len() < rest { data.len() } else { rest };
81 self.buffer.payload[self.buffer_pos..(self.buffer_pos+bytes)]
82 .copy_from_slice(&data[..bytes]);
83
84 let rest = rest - bytes;
85
86 if rest == 0 {
87 self.buffer.flags = BlockHeaderFlags::empty();
88 self.buffer.set_size(self.buffer.payload.len());
89 self.buffer.set_seq_nr(self.seq_nr);
90 self.seq_nr += 1;
91 let leom = Self::write_block(&self.buffer, &mut self.writer)?;
92 if leom { self.logical_end_of_media = true; }
93 self.buffer_pos = 0;
94 self.bytes_written += BlockHeader::SIZE;
95
96 } else {
97 self.buffer_pos += bytes;
98 }
99
100 Ok(bytes)
101 }
102
103 }
104
105 impl <W: BlockWrite> TapeWrite for BlockedWriter<W> {
106
107 fn write_all(&mut self, mut data: &[u8]) -> Result<bool, std::io::Error> {
108 while !data.is_empty() {
109 match self.write(data) {
110 Ok(n) => data = &data[n..],
111 Err(e) => return Err(e),
112 }
113 }
114 Ok(self.logical_end_of_media)
115 }
116
117 fn bytes_written(&self) -> usize {
118 self.bytes_written
119 }
120
121 /// flush last block, set END_OF_STREAM flag
122 ///
123 /// Note: This may write an empty block just including the
124 /// END_OF_STREAM flag.
125 fn finish(&mut self, incomplete: bool) -> Result<bool, std::io::Error> {
126 vec::clear(&mut self.buffer.payload[self.buffer_pos..]);
127 self.buffer.flags = BlockHeaderFlags::END_OF_STREAM;
128 if incomplete { self.buffer.flags |= BlockHeaderFlags::INCOMPLETE; }
129 self.buffer.set_size(self.buffer_pos);
130 self.buffer.set_seq_nr(self.seq_nr);
131 self.seq_nr += 1;
132 self.bytes_written += BlockHeader::SIZE;
133 let leom = Self::write_block(&self.buffer, &mut self.writer)?;
134 self.write_eof()?;
135 Ok(leom)
136 }
137
138 /// Returns if the writer already detected the logical end of media
139 fn logical_end_of_media(&self) -> bool {
140 self.logical_end_of_media
141 }
142
143 }