]>
Commit | Line | Data |
---|---|---|
f323e906 | 1 | use anyhow::Error; |
018d11bb | 2 | use proxmox::tools::io::WriteExt; |
f323e906 WB |
3 | use std::io::{Seek, SeekFrom, Write}; |
4 | use std::sync::Arc; | |
018d11bb | 5 | |
bbdda58b DM |
6 | use pbs_tools::crypt_config::CryptConfig; |
7 | ||
f323e906 | 8 | use crate::checksum_writer::ChecksumWriter; |
f323e906 WB |
9 | use crate::crypt_writer::CryptWriter; |
10 | use crate::file_formats::{self, DataBlobHeader, EncryptedDataBlobHeader}; | |
018d11bb | 11 | |
90ff75f8 | 12 | enum BlobWriterState<'writer, W: Write> { |
f323e906 WB |
13 | Uncompressed { |
14 | csum_writer: ChecksumWriter<W>, | |
15 | }, | |
16 | Compressed { | |
17 | compr: zstd::stream::write::Encoder<'writer, ChecksumWriter<W>>, | |
18 | }, | |
19 | Encrypted { | |
20 | crypt_writer: CryptWriter<ChecksumWriter<W>>, | |
21 | }, | |
22 | EncryptedCompressed { | |
23 | compr: zstd::stream::write::Encoder<'writer, CryptWriter<ChecksumWriter<W>>>, | |
24 | }, | |
018d11bb DM |
25 | } |
26 | ||
27 | /// Data blob writer | |
90ff75f8 FG |
28 | pub struct DataBlobWriter<'writer, W: Write> { |
29 | state: BlobWriterState<'writer, W>, | |
018d11bb DM |
30 | } |
31 | ||
f323e906 | 32 | impl<W: Write + Seek> DataBlobWriter<'_, W> { |
018d11bb DM |
33 | pub fn new_uncompressed(mut writer: W) -> Result<Self, Error> { |
34 | writer.seek(SeekFrom::Start(0))?; | |
f323e906 WB |
35 | let head = DataBlobHeader { |
36 | magic: file_formats::UNCOMPRESSED_BLOB_MAGIC_1_0, | |
37 | crc: [0; 4], | |
38 | }; | |
018d11bb DM |
39 | unsafe { |
40 | writer.write_le_value(head)?; | |
41 | } | |
42 | let csum_writer = ChecksumWriter::new(writer, None); | |
f323e906 WB |
43 | Ok(Self { |
44 | state: BlobWriterState::Uncompressed { csum_writer }, | |
45 | }) | |
018d11bb DM |
46 | } |
47 | ||
48 | pub fn new_compressed(mut writer: W) -> Result<Self, Error> { | |
f323e906 WB |
49 | writer.seek(SeekFrom::Start(0))?; |
50 | let head = DataBlobHeader { | |
51 | magic: file_formats::COMPRESSED_BLOB_MAGIC_1_0, | |
52 | crc: [0; 4], | |
53 | }; | |
018d11bb DM |
54 | unsafe { |
55 | writer.write_le_value(head)?; | |
56 | } | |
57 | let csum_writer = ChecksumWriter::new(writer, None); | |
58 | let compr = zstd::stream::write::Encoder::new(csum_writer, 1)?; | |
f323e906 WB |
59 | Ok(Self { |
60 | state: BlobWriterState::Compressed { compr }, | |
61 | }) | |
018d11bb DM |
62 | } |
63 | ||
9025312a | 64 | pub fn new_encrypted(mut writer: W, config: Arc<CryptConfig>) -> Result<Self, Error> { |
018d11bb DM |
65 | writer.seek(SeekFrom::Start(0))?; |
66 | let head = EncryptedDataBlobHeader { | |
f323e906 WB |
67 | head: DataBlobHeader { |
68 | magic: file_formats::ENCRYPTED_BLOB_MAGIC_1_0, | |
69 | crc: [0; 4], | |
70 | }, | |
018d11bb DM |
71 | iv: [0u8; 16], |
72 | tag: [0u8; 16], | |
73 | }; | |
74 | unsafe { | |
75 | writer.write_le_value(head)?; | |
76 | } | |
77 | ||
78 | let csum_writer = ChecksumWriter::new(writer, None); | |
f323e906 WB |
79 | let crypt_writer = CryptWriter::new(csum_writer, config)?; |
80 | Ok(Self { | |
81 | state: BlobWriterState::Encrypted { crypt_writer }, | |
82 | }) | |
018d11bb DM |
83 | } |
84 | ||
f323e906 WB |
85 | pub fn new_encrypted_compressed( |
86 | mut writer: W, | |
87 | config: Arc<CryptConfig>, | |
88 | ) -> Result<Self, Error> { | |
018d11bb DM |
89 | writer.seek(SeekFrom::Start(0))?; |
90 | let head = EncryptedDataBlobHeader { | |
f323e906 WB |
91 | head: DataBlobHeader { |
92 | magic: file_formats::ENCR_COMPR_BLOB_MAGIC_1_0, | |
93 | crc: [0; 4], | |
94 | }, | |
018d11bb DM |
95 | iv: [0u8; 16], |
96 | tag: [0u8; 16], | |
97 | }; | |
98 | unsafe { | |
99 | writer.write_le_value(head)?; | |
100 | } | |
101 | ||
102 | let csum_writer = ChecksumWriter::new(writer, None); | |
f323e906 | 103 | let crypt_writer = CryptWriter::new(csum_writer, config)?; |
018d11bb | 104 | let compr = zstd::stream::write::Encoder::new(crypt_writer, 1)?; |
f323e906 WB |
105 | Ok(Self { |
106 | state: BlobWriterState::EncryptedCompressed { compr }, | |
107 | }) | |
018d11bb DM |
108 | } |
109 | ||
110 | pub fn finish(self) -> Result<W, Error> { | |
111 | match self.state { | |
112 | BlobWriterState::Uncompressed { csum_writer } => { | |
113 | // write CRC | |
114 | let (mut writer, crc, _) = csum_writer.finish()?; | |
f323e906 WB |
115 | let head = DataBlobHeader { |
116 | magic: file_formats::UNCOMPRESSED_BLOB_MAGIC_1_0, | |
117 | crc: crc.to_le_bytes(), | |
118 | }; | |
018d11bb DM |
119 | |
120 | writer.seek(SeekFrom::Start(0))?; | |
121 | unsafe { | |
122 | writer.write_le_value(head)?; | |
123 | } | |
124 | ||
62ee2eb4 | 125 | Ok(writer) |
018d11bb DM |
126 | } |
127 | BlobWriterState::Compressed { compr } => { | |
128 | let csum_writer = compr.finish()?; | |
129 | let (mut writer, crc, _) = csum_writer.finish()?; | |
130 | ||
f323e906 WB |
131 | let head = DataBlobHeader { |
132 | magic: file_formats::COMPRESSED_BLOB_MAGIC_1_0, | |
133 | crc: crc.to_le_bytes(), | |
134 | }; | |
018d11bb DM |
135 | |
136 | writer.seek(SeekFrom::Start(0))?; | |
137 | unsafe { | |
138 | writer.write_le_value(head)?; | |
139 | } | |
140 | ||
62ee2eb4 | 141 | Ok(writer) |
018d11bb | 142 | } |
018d11bb DM |
143 | BlobWriterState::Encrypted { crypt_writer } => { |
144 | let (csum_writer, iv, tag) = crypt_writer.finish()?; | |
145 | let (mut writer, crc, _) = csum_writer.finish()?; | |
146 | ||
147 | let head = EncryptedDataBlobHeader { | |
f323e906 WB |
148 | head: DataBlobHeader { |
149 | magic: file_formats::ENCRYPTED_BLOB_MAGIC_1_0, | |
150 | crc: crc.to_le_bytes(), | |
151 | }, | |
152 | iv, | |
153 | tag, | |
018d11bb DM |
154 | }; |
155 | writer.seek(SeekFrom::Start(0))?; | |
156 | unsafe { | |
157 | writer.write_le_value(head)?; | |
158 | } | |
62ee2eb4 | 159 | Ok(writer) |
018d11bb DM |
160 | } |
161 | BlobWriterState::EncryptedCompressed { compr } => { | |
162 | let crypt_writer = compr.finish()?; | |
163 | let (csum_writer, iv, tag) = crypt_writer.finish()?; | |
164 | let (mut writer, crc, _) = csum_writer.finish()?; | |
165 | ||
166 | let head = EncryptedDataBlobHeader { | |
f323e906 WB |
167 | head: DataBlobHeader { |
168 | magic: file_formats::ENCR_COMPR_BLOB_MAGIC_1_0, | |
169 | crc: crc.to_le_bytes(), | |
170 | }, | |
171 | iv, | |
172 | tag, | |
018d11bb DM |
173 | }; |
174 | writer.seek(SeekFrom::Start(0))?; | |
175 | unsafe { | |
176 | writer.write_le_value(head)?; | |
177 | } | |
62ee2eb4 | 178 | Ok(writer) |
018d11bb DM |
179 | } |
180 | } | |
181 | } | |
182 | } | |
183 | ||
f323e906 | 184 | impl<W: Write + Seek> Write for DataBlobWriter<'_, W> { |
018d11bb DM |
185 | fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> { |
186 | match self.state { | |
f323e906 WB |
187 | BlobWriterState::Uncompressed { |
188 | ref mut csum_writer, | |
189 | } => csum_writer.write(buf), | |
190 | BlobWriterState::Compressed { ref mut compr } => compr.write(buf), | |
191 | BlobWriterState::Encrypted { | |
192 | ref mut crypt_writer, | |
193 | } => crypt_writer.write(buf), | |
194 | BlobWriterState::EncryptedCompressed { ref mut compr } => compr.write(buf), | |
018d11bb DM |
195 | } |
196 | } | |
197 | ||
198 | fn flush(&mut self) -> Result<(), std::io::Error> { | |
199 | match self.state { | |
f323e906 WB |
200 | BlobWriterState::Uncompressed { |
201 | ref mut csum_writer, | |
202 | } => csum_writer.flush(), | |
203 | BlobWriterState::Compressed { ref mut compr } => compr.flush(), | |
204 | BlobWriterState::Encrypted { | |
205 | ref mut crypt_writer, | |
206 | } => crypt_writer.flush(), | |
207 | BlobWriterState::EncryptedCompressed { ref mut compr } => compr.flush(), | |
018d11bb DM |
208 | } |
209 | } | |
210 | } |