]> git.proxmox.com Git - proxmox-backup.git/blob - src/backup/data_blob_writer.rs
add pbs-tools subcrate
[proxmox-backup.git] / src / backup / data_blob_writer.rs
1 use anyhow::{Error};
2 use std::sync::Arc;
3 use std::io::{Write, Seek, SeekFrom};
4 use proxmox::tools::io::WriteExt;
5
6 use super::*;
7
8 enum BlobWriterState<'writer, W: Write> {
9 Uncompressed { csum_writer: ChecksumWriter<W> },
10 Compressed { compr: zstd::stream::write::Encoder<'writer, ChecksumWriter<W>> },
11 Encrypted { crypt_writer: CryptWriter<ChecksumWriter<W>> },
12 EncryptedCompressed { compr: zstd::stream::write::Encoder<'writer, CryptWriter<ChecksumWriter<W>>> },
13 }
14
15 /// Data blob writer
16 pub struct DataBlobWriter<'writer, W: Write> {
17 state: BlobWriterState<'writer, W>,
18 }
19
20 impl <W: Write + Seek> DataBlobWriter<'_, W> {
21
22 pub fn new_uncompressed(mut writer: W) -> Result<Self, Error> {
23 writer.seek(SeekFrom::Start(0))?;
24 let head = DataBlobHeader { magic: UNCOMPRESSED_BLOB_MAGIC_1_0, crc: [0; 4] };
25 unsafe {
26 writer.write_le_value(head)?;
27 }
28 let csum_writer = ChecksumWriter::new(writer, None);
29 Ok(Self { state: BlobWriterState::Uncompressed { csum_writer }})
30 }
31
32 pub fn new_compressed(mut writer: W) -> Result<Self, Error> {
33 writer.seek(SeekFrom::Start(0))?;
34 let head = DataBlobHeader { magic: COMPRESSED_BLOB_MAGIC_1_0, crc: [0; 4] };
35 unsafe {
36 writer.write_le_value(head)?;
37 }
38 let csum_writer = ChecksumWriter::new(writer, None);
39 let compr = zstd::stream::write::Encoder::new(csum_writer, 1)?;
40 Ok(Self { state: BlobWriterState::Compressed { compr }})
41 }
42
43 pub fn new_encrypted(mut writer: W, config: Arc<CryptConfig>) -> Result<Self, Error> {
44 writer.seek(SeekFrom::Start(0))?;
45 let head = EncryptedDataBlobHeader {
46 head: DataBlobHeader { magic: ENCRYPTED_BLOB_MAGIC_1_0, crc: [0; 4] },
47 iv: [0u8; 16],
48 tag: [0u8; 16],
49 };
50 unsafe {
51 writer.write_le_value(head)?;
52 }
53
54 let csum_writer = ChecksumWriter::new(writer, None);
55 let crypt_writer = CryptWriter::new(csum_writer, config)?;
56 Ok(Self { state: BlobWriterState::Encrypted { crypt_writer }})
57 }
58
59 pub fn new_encrypted_compressed(mut writer: W, config: Arc<CryptConfig>) -> Result<Self, Error> {
60 writer.seek(SeekFrom::Start(0))?;
61 let head = EncryptedDataBlobHeader {
62 head: DataBlobHeader { magic: ENCR_COMPR_BLOB_MAGIC_1_0, crc: [0; 4] },
63 iv: [0u8; 16],
64 tag: [0u8; 16],
65 };
66 unsafe {
67 writer.write_le_value(head)?;
68 }
69
70 let csum_writer = ChecksumWriter::new(writer, None);
71 let crypt_writer = CryptWriter::new(csum_writer, config)?;
72 let compr = zstd::stream::write::Encoder::new(crypt_writer, 1)?;
73 Ok(Self { state: BlobWriterState::EncryptedCompressed { compr }})
74 }
75
76 pub fn finish(self) -> Result<W, Error> {
77 match self.state {
78 BlobWriterState::Uncompressed { csum_writer } => {
79 // write CRC
80 let (mut writer, crc, _) = csum_writer.finish()?;
81 let head = DataBlobHeader { magic: UNCOMPRESSED_BLOB_MAGIC_1_0, crc: crc.to_le_bytes() };
82
83 writer.seek(SeekFrom::Start(0))?;
84 unsafe {
85 writer.write_le_value(head)?;
86 }
87
88 Ok(writer)
89 }
90 BlobWriterState::Compressed { compr } => {
91 let csum_writer = compr.finish()?;
92 let (mut writer, crc, _) = csum_writer.finish()?;
93
94 let head = DataBlobHeader { magic: COMPRESSED_BLOB_MAGIC_1_0, crc: crc.to_le_bytes() };
95
96 writer.seek(SeekFrom::Start(0))?;
97 unsafe {
98 writer.write_le_value(head)?;
99 }
100
101 Ok(writer)
102 }
103 BlobWriterState::Encrypted { crypt_writer } => {
104 let (csum_writer, iv, tag) = crypt_writer.finish()?;
105 let (mut writer, crc, _) = csum_writer.finish()?;
106
107 let head = EncryptedDataBlobHeader {
108 head: DataBlobHeader { magic: ENCRYPTED_BLOB_MAGIC_1_0, crc: crc.to_le_bytes() },
109 iv, tag,
110 };
111 writer.seek(SeekFrom::Start(0))?;
112 unsafe {
113 writer.write_le_value(head)?;
114 }
115 Ok(writer)
116 }
117 BlobWriterState::EncryptedCompressed { compr } => {
118 let crypt_writer = compr.finish()?;
119 let (csum_writer, iv, tag) = crypt_writer.finish()?;
120 let (mut writer, crc, _) = csum_writer.finish()?;
121
122 let head = EncryptedDataBlobHeader {
123 head: DataBlobHeader { magic: ENCR_COMPR_BLOB_MAGIC_1_0, crc: crc.to_le_bytes() },
124 iv, tag,
125 };
126 writer.seek(SeekFrom::Start(0))?;
127 unsafe {
128 writer.write_le_value(head)?;
129 }
130 Ok(writer)
131 }
132 }
133 }
134 }
135
136 impl <W: Write + Seek> Write for DataBlobWriter<'_, W> {
137
138 fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
139 match self.state {
140 BlobWriterState::Uncompressed { ref mut csum_writer } => {
141 csum_writer.write(buf)
142 }
143 BlobWriterState::Compressed { ref mut compr } => {
144 compr.write(buf)
145 }
146 BlobWriterState::Encrypted { ref mut crypt_writer } => {
147 crypt_writer.write(buf)
148 }
149 BlobWriterState::EncryptedCompressed { ref mut compr } => {
150 compr.write(buf)
151 }
152 }
153 }
154
155 fn flush(&mut self) -> Result<(), std::io::Error> {
156 match self.state {
157 BlobWriterState::Uncompressed { ref mut csum_writer } => {
158 csum_writer.flush()
159 }
160 BlobWriterState::Compressed { ref mut compr } => {
161 compr.flush()
162 }
163 BlobWriterState::Encrypted { ref mut crypt_writer } => {
164 crypt_writer.flush()
165 }
166 BlobWriterState::EncryptedCompressed { ref mut compr } => {
167 compr.flush()
168 }
169 }
170 }
171 }