]>
Commit | Line | Data |
---|---|---|
983e929e DM |
1 | use anyhow::Error; |
2 | ||
6ef1b649 | 3 | use proxmox_uuid::Uuid; |
983e929e | 4 | |
048b43af | 5 | use pbs_tape::{TapeWrite, MediaContentHeader}; |
983e929e DM |
6 | |
7 | /// Writes data streams using multiple volumes | |
8 | /// | |
9 | /// Note: We do not use this feature currently. | |
10 | pub struct MultiVolumeWriter<'a> { | |
11 | writer: Option<Box<dyn TapeWrite + 'a>>, | |
12 | next_writer_fn: Box<dyn 'a + FnMut() -> Result<Box<dyn TapeWrite +'a>, Error>>, | |
13 | got_leom: bool, | |
14 | finished: bool, | |
15 | wrote_header: bool, | |
16 | header: MediaContentHeader, | |
17 | header_data: Vec<u8>, | |
18 | bytes_written: usize, // does not include bytes from current writer | |
19 | } | |
20 | ||
21 | impl <'a> MultiVolumeWriter<'a> { | |
22 | ||
23 | /// Creates a new instance | |
24 | pub fn new( | |
25 | writer: Box<dyn TapeWrite +'a>, | |
26 | content_magic: [u8; 8], | |
27 | header_data: Vec<u8>, | |
28 | next_writer_fn: Box<dyn 'a + FnMut() -> Result<Box<dyn TapeWrite + 'a>, Error>>, | |
29 | ) -> Self { | |
30 | ||
31 | let header = MediaContentHeader::new(content_magic, header_data.len() as u32); | |
32 | ||
33 | Self { | |
34 | writer: Some(writer), | |
35 | next_writer_fn, | |
36 | got_leom: false, | |
37 | finished: false, | |
38 | header, | |
39 | header_data, | |
40 | wrote_header: false, | |
41 | bytes_written: 0, | |
42 | } | |
43 | } | |
44 | ||
45 | /// Returns the cuntent Uuid with the current part number | |
46 | pub fn uuid_and_part_number(&self) -> (Uuid, usize) { | |
47 | (self.header.uuid.into(), self.header.part_number as usize) | |
48 | } | |
49 | } | |
50 | ||
51 | impl <'a> TapeWrite for MultiVolumeWriter<'a> { | |
52 | ||
53 | fn write_all(&mut self, buf: &[u8]) -> Result<bool, std::io::Error> { | |
54 | ||
55 | if self.finished { | |
56 | proxmox::io_bail!("multi-volume writer already finished: internal error"); | |
57 | } | |
58 | ||
59 | if self.got_leom { | |
60 | if !self.wrote_header { | |
61 | proxmox::io_bail!("multi-volume writer: got LEOM before writing anything - internal error"); | |
62 | } | |
63 | let mut writer = match self.writer.take() { | |
64 | Some(writer) => writer, | |
65 | None => proxmox::io_bail!("multi-volume writer: no writer -internal error"), | |
66 | }; | |
67 | self.bytes_written = writer.bytes_written(); | |
68 | writer.finish(true)?; | |
69 | } | |
70 | ||
71 | if self.writer.is_none() { | |
72 | if self.header.part_number >= 255 { | |
73 | proxmox::io_bail!("multi-volume writer: too many parts"); | |
74 | } | |
75 | self.writer = Some( | |
76 | (self.next_writer_fn)() | |
77 | .map_err(|err| proxmox::io_format_err!("multi-volume get next volume failed: {}", err))? | |
78 | ); | |
79 | self.got_leom = false; | |
80 | self.wrote_header = false; | |
81 | self.header.part_number += 1; | |
82 | } | |
83 | ||
84 | let leom = match self.writer { | |
85 | None => unreachable!(), | |
86 | Some(ref mut writer) => { | |
87 | if !self.wrote_header { | |
88 | writer.write_header(&self.header, &self.header_data)?; | |
89 | self.wrote_header = true; | |
90 | } | |
91 | writer.write_all(buf)? | |
92 | } | |
93 | }; | |
94 | ||
95 | if leom { self.got_leom = true; } | |
96 | ||
97 | Ok(false) | |
98 | } | |
99 | ||
100 | fn bytes_written(&self) -> usize { | |
101 | let mut bytes_written = self.bytes_written; | |
102 | if let Some(ref writer) = self.writer { | |
103 | bytes_written += writer.bytes_written(); | |
104 | } | |
105 | bytes_written | |
106 | } | |
107 | ||
108 | fn finish(&mut self, incomplete: bool) -> Result<bool, std::io::Error> { | |
109 | if incomplete { | |
110 | proxmox::io_bail!( | |
111 | "incomplete flag makes no sense for multi-volume stream: internal error"); | |
112 | } | |
113 | ||
114 | match self.writer.take() { | |
115 | None if self.finished => proxmox::io_bail!( | |
116 | "multi-volume writer already finished: internal error"), | |
117 | None => Ok(false), | |
118 | Some(ref mut writer) => { | |
119 | self.finished = true; | |
120 | if !self.wrote_header { | |
121 | writer.write_header(&self.header, &self.header_data)?; | |
122 | self.wrote_header = true; | |
123 | } | |
124 | writer.finish(false) | |
125 | } | |
126 | } | |
127 | } | |
128 | ||
129 | fn logical_end_of_media(&self) -> bool { | |
130 | self.got_leom | |
131 | } | |
132 | ||
133 | } |