android_sparse/
write.rs

1//! Sparse image writing and decoding to raw images.
2
3use crate::{
4    block::Block,
5    ext::{Tell, WriteBlock},
6    headers::{ChunkHeader, ChunkType, FileHeader},
7    result::Result,
8};
9use byteorder::{LittleEndian, WriteBytesExt};
10use crc::crc32::{self, Hasher32};
11use std::io::{prelude::*, BufWriter, SeekFrom};
12
13/// Writes sparse blocks to a sparse image.
14pub struct Writer<W: Write + Seek> {
15    dst: BufWriter<W>,
16    current_chunk: Option<ChunkHeader>,
17    current_fill: Option<[u8; 4]>,
18    num_blocks: u32,
19    num_chunks: u32,
20    crc: Option<crc32::Digest>,
21    finished: bool,
22}
23
24impl<W: Write + Seek> Writer<W> {
25    /// Creates a new writer that writes to `w`.
26    ///
27    /// The created writer skips checksum calculation in favor of
28    /// speed. To get a writer that does checksum calculation, use
29    /// `Writer::with_crc` instead.
30    pub fn new(w: W) -> Result<Self> {
31        let mut dst = BufWriter::new(w);
32        // We cannot write the file header until we know the total number of
33        // blocks and chunks. So we skip it here and write it at the end in
34        // `finish`.
35        dst.seek(SeekFrom::Current(i64::from(FileHeader::SIZE)))?;
36
37        Ok(Self {
38            dst,
39            current_chunk: None,
40            current_fill: None,
41            num_blocks: 0,
42            num_chunks: 0,
43            crc: None,
44            finished: false,
45        })
46    }
47
48    /// Creates a new writer that writes to `w` and adds a checksum
49    /// at the end.
50    pub fn with_crc(w: W) -> Result<Self> {
51        let mut writer = Self::new(w)?;
52        writer.crc = Some(crc32::Digest::new(crc32::IEEE));
53        Ok(writer)
54    }
55
56    /// Writes a sparse block to this writer.
57    ///
58    /// The sparse block is converted into the sparse file format and
59    /// written to this decoder's destination.
60    pub fn write_block(&mut self, block: &Block) -> Result<()> {
61        if !self.can_merge(block) {
62            self.finish_chunk()?;
63            self.start_chunk(block)?;
64        }
65
66        let chunk = self.current_chunk.as_mut().unwrap();
67
68        match block {
69            Block::Raw(buf) => {
70                self.dst.write_all(&**buf)?;
71                chunk.total_size += Block::SIZE;
72            }
73            Block::Fill(value) => {
74                if self.current_fill.is_none() {
75                    self.dst.write_all(value)?;
76                    self.current_fill = Some(*value);
77                }
78            }
79            Block::Skip => (),
80            Block::Crc32(checksum) => {
81                self.dst.write_u32::<LittleEndian>(*checksum)?;
82                // CRC chunk size must remain 0, so drop out here already.
83                return Ok(());
84            }
85        }
86
87        if let Some(digest) = self.crc.as_mut() {
88            digest.write_block(block);
89        }
90
91        chunk.chunk_size += 1;
92        Ok(())
93    }
94
95    /// Finishes writing the sparse image and flushes any buffered data.
96    ///
97    /// Consumes the reader as using it afterward would be invalid.
98    pub fn close(mut self) -> Result<()> {
99        self.finish()
100    }
101
102    fn finish(&mut self) -> Result<()> {
103        assert!(!self.finished);
104        self.finished = true;
105
106        self.write_checksum()?;
107        self.finish_chunk()?;
108
109        // Like libsparse, we always set the checksum value in the file header
110        // to 0. If checksum writing is enabled, we append a Crc32 chunk at
111        // the end of the file instead.
112        let image_checksum = 0;
113        let header = FileHeader {
114            total_blocks: self.num_blocks,
115            total_chunks: self.num_chunks,
116            image_checksum,
117        };
118
119        self.dst.seek(SeekFrom::Start(0))?;
120        header.write_to(&mut self.dst)?;
121
122        self.dst.flush()?;
123        Ok(())
124    }
125
126    fn can_merge(&self, block: &Block) -> bool {
127        let chunk = match self.current_chunk.as_ref() {
128            Some(c) => c,
129            None => return false,
130        };
131
132        match (chunk.chunk_type, block) {
133            (ChunkType::Raw, Block::Raw(_)) | (ChunkType::DontCare, Block::Skip) => true,
134            (ChunkType::Fill, Block::Fill(value)) => self.current_fill.unwrap() == *value,
135            _ => false,
136        }
137    }
138
139    fn start_chunk(&mut self, block: &Block) -> Result<()> {
140        assert!(self.current_chunk.is_none());
141
142        let (chunk_type, init_size) = match block {
143            Block::Raw(_) => (ChunkType::Raw, 0),
144            Block::Fill(_) => (ChunkType::Fill, 4),
145            Block::Skip => (ChunkType::DontCare, 0),
146            Block::Crc32(_) => (ChunkType::Crc32, 4),
147        };
148
149        let chunk = ChunkHeader {
150            chunk_type,
151            chunk_size: 0,
152            total_size: init_size + u32::from(ChunkHeader::SIZE),
153        };
154
155        // We cannot write the chunk header until we know the total number of
156        // blocks in the chunk. So we skip it here and write it later in
157        // `finish_chunk`.
158        let header_size = i64::from(ChunkHeader::SIZE);
159        self.dst.seek(SeekFrom::Current(header_size))?;
160
161        self.current_chunk = Some(chunk);
162
163        Ok(())
164    }
165
166    fn finish_chunk(&mut self) -> Result<()> {
167        let chunk = match self.current_chunk.take() {
168            Some(c) => c,
169            None => return Ok(()),
170        };
171
172        let pos = self.dst.tell()?;
173        let header_off = i64::from(chunk.total_size);
174        self.dst.seek(SeekFrom::Current(-header_off))?;
175        chunk.write_to(&mut self.dst)?;
176        self.dst.seek(SeekFrom::Start(pos))?;
177
178        self.current_fill = None;
179        self.num_chunks += 1;
180        self.num_blocks += chunk.chunk_size;
181
182        Ok(())
183    }
184
185    fn write_checksum(&mut self) -> Result<()> {
186        let checksum = match self.crc.as_ref() {
187            Some(digest) => digest.sum32(),
188            None => return Ok(()),
189        };
190
191        let block = Block::Crc32(checksum);
192        self.write_block(&block)
193    }
194}
195
196impl<W: Write + Seek> Drop for Writer<W> {
197    fn drop(&mut self) {
198        if !self.finished {
199            self.finish().ok();
200        }
201    }
202}
203
204/// Decodes sparse blocks and writes them to a raw image.
205pub struct Decoder<W: Write + Seek> {
206    dst: BufWriter<W>,
207    finished: bool,
208}
209
210impl<W: Write + Seek> Decoder<W> {
211    /// Creates a new decoder that writes to `w`.
212    pub fn new(w: W) -> Result<Self> {
213        let dst = BufWriter::new(w);
214        Ok(Self {
215            dst,
216            finished: false,
217        })
218    }
219
220    /// Writes a sparse block to this decoder.
221    ///
222    /// The sparse block is decoded into its raw form and written to
223    /// this decoder's destination.
224    pub fn write_block(&mut self, block: &Block) -> Result<()> {
225        match block {
226            Block::Raw(buf) => self.dst.write_all(&**buf)?,
227            Block::Fill(value) => {
228                let count = Block::SIZE as usize / 4;
229                for _ in 0..count {
230                    self.dst.write_all(value)?;
231                }
232            }
233            Block::Skip => {
234                let offset = i64::from(Block::SIZE) - 1;
235                self.dst.seek(SeekFrom::Current(offset))?;
236                self.dst.write_all(&[0])?;
237            }
238            Block::Crc32(_) => (),
239        }
240        Ok(())
241    }
242
243    /// Finishes writing the raw image and flushes any buffered data.
244    ///
245    /// Consumes the reader as using it afterward would be invalid.
246    pub fn close(mut self) -> Result<()> {
247        self.finish()
248    }
249
250    fn finish(&mut self) -> Result<()> {
251        assert!(!self.finished);
252        self.finished = true;
253
254        // Ensure the file has the correct size if the last block was a
255        // skip block.
256        let file = self.dst.get_mut();
257
258        file.flush()?;
259        Ok(())
260    }
261}
262
263impl<W: Write + Seek> Drop for Decoder<W> {
264    fn drop(&mut self) {
265        if !self.finished {
266            self.finish().ok();
267        }
268    }
269}