Skip to main content

sparse/
lib.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#[cfg(target_endian = "big")]
6assert!(false, "This library assumes little-endian!");
7
8pub mod builder;
9mod format;
10pub mod reader;
11
12use crate::format::{CHUNK_HEADER_SIZE, ChunkHeader, SPARSE_HEADER_SIZE, SparseHeader};
13use crate::reader::SparseReader;
14
15use core::fmt;
16use serde::de::DeserializeOwned;
17use thiserror::Error;
18
19use std::fs::File;
20use std::io::{Cursor, Read, Seek, SeekFrom, Write};
21use std::path::Path;
22use tempfile::{NamedTempFile, TempPath};
23#[cfg(target_os = "fuchsia")]
24use zx;
25
26// Size of blocks to write.  Note that the format supports varied block sizes; this is the preferred
27// size by this library.
28const BLK_SIZE: u32 = 0x1000;
29
30#[derive(Debug, Clone, Copy)]
31pub enum SparseDataType {
32    Header,
33    ChunkHeader,
34    FillValue,
35    Checksum,
36}
37
38impl std::fmt::Display for SparseDataType {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        match self {
41            Self::Header => write!(f, "header"),
42            Self::ChunkHeader => write!(f, "chunk header"),
43            Self::FillValue => write!(f, "fill value"),
44            Self::Checksum => write!(f, "checksum"),
45        }
46    }
47}
48
49#[derive(Debug, Error)]
50pub enum DeserializeError {
51    #[error("IO error: {0}")]
52    Io(#[from] std::io::Error),
53
54    #[error("Bincode error: {0}")]
55    Bincode(#[from] Box<bincode::ErrorKind>),
56}
57
58#[derive(Debug, Clone, Copy, thiserror::Error)]
59pub enum UnalignedSource {
60    #[error("buffer length {0}")]
61    Buffer(usize),
62
63    #[error("Reader length {0}")]
64    Reader(u64),
65
66    #[error("Skip length {0}")]
67    Skip(u64),
68
69    #[error("Fill length {0}")]
70    Fill(u64),
71
72    #[error("Vmo size {0}")]
73    Vmo(u64),
74}
75
76#[derive(Debug, Error)]
77pub enum SparseError {
78    #[error("IO error: {0}")]
79    Io(#[from] std::io::Error),
80
81    #[error("Failed to deserialize {ty}: {source}")]
82    Deserialize {
83        ty: SparseDataType,
84        #[source]
85        source: DeserializeError,
86    },
87
88    #[error("Failed to serialize {ty}: {source}")]
89    Serialize {
90        ty: SparseDataType,
91        #[source]
92        source: Box<bincode::ErrorKind>,
93    },
94
95    #[error("Invalid sparse image header")]
96    InvalidHeader,
97
98    #[error("Invalid chunk header")]
99    InvalidChunkHeader,
100
101    #[error("Invalid chunk type {0}")]
102    InvalidChunkType(u16),
103
104    #[error("Given maximum download size ({0}) is less than the block size ({1})")]
105    MaxDownloadSizeTooSmall(u64, u32),
106
107    #[error("No source for Raw chunk")]
108    NoSourceForRawChunk,
109
110    #[error("Chunk is not block aligned")]
111    UnalignedChunk,
112
113    #[error("Failed to copy contents: {0}")]
114    CopyContents(#[source] std::io::Error),
115
116    #[error("Failed to fill contents: {0}")]
117    FillContents(#[source] std::io::Error),
118
119    #[error("Failed to skip contents: {0}")]
120    SkipContents(#[source] std::io::Error),
121
122    #[cfg(target_os = "fuchsia")]
123    #[error("Zircon error: {0}")]
124    Zircon(#[from] zx::Status),
125
126    #[error("Invalid {0}")]
127    UnalignedDataSource(UnalignedSource),
128
129    #[error("Sparse image would contain too many blocks")]
130    TooManyBlocks,
131}
132
133fn deserialize_from<'a, T: DeserializeOwned, R: Read + ?Sized>(
134    source: &mut R,
135) -> Result<T, DeserializeError> {
136    let mut buf = vec![0u8; std::mem::size_of::<T>()];
137    source.read_exact(&mut buf[..])?;
138    bincode::deserialize(&buf[..]).map_err(Into::into)
139}
140
141/// A union trait for `Write` and `Seek` that also allows truncation.
142pub trait Writer: Write + Seek {
143    /// Sets the length of the output stream.
144    fn set_len(&mut self, size: u64) -> Result<(), SparseError>;
145}
146
147impl Writer for File {
148    fn set_len(&mut self, size: u64) -> Result<(), SparseError> {
149        File::set_len(self, size).map_err(SparseError::from)
150    }
151}
152
153impl Writer for Cursor<Vec<u8>> {
154    fn set_len(&mut self, size: u64) -> Result<(), SparseError> {
155        Vec::resize(self.get_mut(), size as usize, 0u8);
156        Ok(())
157    }
158}
159
160// A wrapper around a Reader, which makes it seem like the underlying stream is only self.1 bytes
161// long.  The underlying reader is still advanced upon reading.
162// This is distinct from `std::io::Take` in that it does not modify the seek offset of the
163// underlying reader.  In other words, `LimitedReader` can be used to read a window within the
164// reader (by setting seek offset to the start, and the size limit to the end).
165struct LimitedReader<'a, R>(pub &'a mut R, pub usize);
166
167impl<'a, R: Read + Seek> Read for LimitedReader<'a, R> {
168    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
169        let offset = self.0.stream_position()?;
170        let avail = self.1.saturating_sub(offset as usize);
171        let to_read = std::cmp::min(avail, buf.len());
172        self.0.read(&mut buf[..to_read])
173    }
174}
175
176/// Returns whether the image in `reader` appears to be in the sparse format.
177pub fn is_sparse_image<R: Read + Seek>(reader: &mut R) -> bool {
178    || -> Option<bool> {
179        let header: SparseHeader = deserialize_from(reader).ok()?;
180        let is_sparse = header.magic == format::SPARSE_HEADER_MAGIC;
181        reader.seek(SeekFrom::Start(0)).ok()?;
182        Some(is_sparse)
183    }()
184    .unwrap_or(false)
185}
186
187#[derive(Clone, PartialEq, Debug)]
188pub enum Chunk {
189    /// `Raw` represents a set of blocks to be written to disk as-is.
190    /// `start` is the offset in the expanded image at which the Raw section starts.
191    /// `start` and `size` are in bytes, but must be block-aligned.
192    Raw { start: u64, size: u64 },
193    /// `Fill` represents a Chunk that has the `value` repeated enough to fill `size` bytes.
194    /// `start` is the offset in the expanded image at which the Fill section starts.
195    /// `start` and `size` are in bytes, but must be block-aligned.
196    Fill { start: u64, size: u64, value: u32 },
197    /// `DontCare` represents a set of blocks that need to be "offset" by the
198    /// image recipient.  If an image needs to be broken up into two sparse images, and we flash n
199    /// bytes for Sparse Image 1, Sparse Image 2 needs to start with a DontCareChunk with
200    /// (n/blocksize) blocks as its "size" property.
201    /// `start` is the offset in the expanded image at which the DontCare section starts.
202    /// `start` and `size` are in bytes, but must be block-aligned.
203    DontCare { start: u64, size: u64 },
204    /// `Crc32Chunk` is used as a checksum of a given set of Chunks for a SparseImage.  This is not
205    /// required and unused in most implementations of the Sparse Image format. The type is included
206    /// for completeness. It has 4 bytes of CRC32 checksum as describable in a u32.
207    #[allow(dead_code)]
208    Crc32 { checksum: u32 },
209}
210
211impl Chunk {
212    /// Attempts to read a `Chunk` from `reader`.  The reader will be positioned at the first byte
213    /// following the chunk header and any extra data; for a Raw chunk this means it will point at
214    /// the data payload, and for other chunks it will point at the next chunk header (or EOF).
215    /// `offset` is the current offset in the logical volume.
216    pub fn read_metadata<R: Read>(
217        reader: &mut R,
218        offset: u64,
219        block_size: u32,
220    ) -> Result<Self, SparseError> {
221        let header: ChunkHeader = deserialize_from(reader)
222            .map_err(|e| SparseError::Deserialize { ty: SparseDataType::ChunkHeader, source: e })?;
223        if !header.valid() {
224            return Err(SparseError::InvalidChunkHeader);
225        }
226
227        let size = header.chunk_sz as u64 * block_size as u64;
228        match header.chunk_type {
229            format::CHUNK_TYPE_RAW => Ok(Self::Raw { start: offset, size }),
230            format::CHUNK_TYPE_FILL => {
231                let value: u32 = deserialize_from(reader).map_err(|e| {
232                    SparseError::Deserialize { ty: SparseDataType::FillValue, source: e }
233                })?;
234                Ok(Self::Fill { start: offset, size, value })
235            }
236            format::CHUNK_TYPE_DONT_CARE => Ok(Self::DontCare { start: offset, size }),
237            format::CHUNK_TYPE_CRC32 => {
238                let checksum: u32 = deserialize_from(reader).map_err(|e| {
239                    SparseError::Deserialize { ty: SparseDataType::Checksum, source: e }
240                })?;
241                Ok(Self::Crc32 { checksum })
242            }
243            // We already validated the chunk_type in `ChunkHeader::is_valid`.
244            _ => unreachable!(),
245        }
246    }
247
248    fn valid(&self, block_size: u32) -> bool {
249        self.output_size() % (block_size as u64) == 0
250    }
251
252    /// Returns the offset into the logical image the chunk refers to, or None if the chunk has no
253    /// output data.
254    fn output_offset(&self) -> Option<u64> {
255        match self {
256            Self::Raw { start, .. } => Some(*start),
257            Self::Fill { start, .. } => Some(*start),
258            Self::DontCare { start, .. } => Some(*start),
259            Self::Crc32 { .. } => None,
260        }
261    }
262
263    /// Return number of bytes the chunk expands to when written to the partition.
264    fn output_size(&self) -> u64 {
265        match self {
266            Self::Raw { size, .. } => *size,
267            Self::Fill { size, .. } => *size,
268            Self::DontCare { size, .. } => *size,
269            Self::Crc32 { .. } => 0,
270        }
271    }
272
273    /// Return number of blocks the chunk expands to when written to the partition.
274    fn output_blocks(&self, block_size: u32) -> u32 {
275        self.output_size().div_ceil(block_size as u64) as u32
276    }
277
278    /// `chunk_type` returns the integer flag to represent the type of chunk
279    /// to use in the ChunkHeader
280    fn chunk_type(&self) -> u16 {
281        match self {
282            Self::Raw { .. } => format::CHUNK_TYPE_RAW,
283            Self::Fill { .. } => format::CHUNK_TYPE_FILL,
284            Self::DontCare { .. } => format::CHUNK_TYPE_DONT_CARE,
285            Self::Crc32 { .. } => format::CHUNK_TYPE_CRC32,
286        }
287    }
288
289    /// `chunk_data_len` returns the length of the chunk's header plus the
290    /// length of the data when serialized.
291    ///
292    /// This gets included in the sparse header and is encoded as a u32.
293    /// But while we are tracking the offsets and total sizes we need it to be
294    /// a u64 to help keep track of files that are greater than 4 GiB
295    fn chunk_data_len(&self) -> u32 {
296        let header_size = format::CHUNK_HEADER_SIZE;
297        let data_size = match self {
298            Self::Raw { size, .. } => *size as u32,
299            Self::Fill { .. } => std::mem::size_of::<u32>() as u32,
300            Self::DontCare { .. } => 0,
301            Self::Crc32 { .. } => std::mem::size_of::<u32>() as u32,
302        };
303        header_size.checked_add(data_size).unwrap()
304    }
305
306    /// Writes the chunk to the given Writer.  `source` is a Reader containing the data payload for
307    /// a Raw type chunk, with the seek offset pointing to the first byte of the data payload, and
308    /// with exactly enough bytes available for the rest of the data payload.
309    fn write<W: Write, R: Read>(
310        &self,
311        source: Option<&mut R>,
312        dest: &mut W,
313        block_size: u32,
314    ) -> Result<(), SparseError> {
315        if !self.valid(block_size) {
316            return Err(SparseError::UnalignedChunk);
317        }
318        let header = ChunkHeader::new(
319            self.chunk_type(),
320            0x0,
321            self.output_blocks(block_size),
322            self.chunk_data_len(),
323        );
324
325        bincode::serialize_into(&mut *dest, &header)
326            .map_err(|e| SparseError::Serialize { ty: SparseDataType::ChunkHeader, source: e })?;
327
328        match self {
329            Self::Raw { size, .. } => {
330                if source.is_none() {
331                    return Err(SparseError::NoSourceForRawChunk);
332                }
333                let n = std::io::copy(source.unwrap(), dest)?;
334                let size = *size as u64;
335                if n < size {
336                    let zeroes = vec![0u8; (size - n) as usize];
337                    dest.write_all(&zeroes)?;
338                }
339            }
340            Self::Fill { value, .. } => {
341                // Serialize the value,
342                bincode::serialize_into(dest, value).map_err(|e| SparseError::Serialize {
343                    ty: SparseDataType::FillValue,
344                    source: e,
345                })?;
346            }
347            Self::DontCare { .. } => {
348                // DontCare has no data to write
349            }
350            Self::Crc32 { checksum } => {
351                bincode::serialize_into(dest, checksum).map_err(|e| SparseError::Serialize {
352                    ty: SparseDataType::Checksum,
353                    source: e,
354                })?;
355            }
356        }
357        Ok(())
358    }
359}
360
361impl fmt::Display for Chunk {
362    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
363        let message = match self {
364            Self::Raw { start, size } => {
365                format!("RawChunk: start: {}, total bytes: {}", start, size)
366            }
367            Self::Fill { start, size, value } => {
368                format!("FillChunk: start: {}, value: {}, n_blocks: {}", start, value, size)
369            }
370            Self::DontCare { start, size } => {
371                format!("DontCareChunk: start: {}, bytes: {}", start, size)
372            }
373            Self::Crc32 { checksum } => format!("Crc32Chunk: checksum: {:?}", checksum),
374        };
375        write!(f, "{}", message)
376    }
377}
378
379/// Chunk::write takes an Option of something that implements Read. The compiler still requires a
380/// concrete type for the generic argument even when the Option is None. This constant can be used
381/// in place of None to avoid having to specify a type for the source.
382pub const NO_SOURCE: Option<&mut Cursor<&[u8]>> = None;
383
384#[derive(Clone, Debug, PartialEq)]
385struct SparseFileWriter {
386    chunks: Vec<Chunk>,
387}
388
389impl SparseFileWriter {
390    fn new(chunks: Vec<Chunk>) -> SparseFileWriter {
391        SparseFileWriter { chunks }
392    }
393
394    fn total_blocks(&self) -> u32 {
395        self.chunks.iter().map(|c| c.output_blocks(BLK_SIZE)).sum()
396    }
397
398    fn total_bytes(&self) -> u64 {
399        self.chunks.iter().map(|c| c.output_size() as u64).sum()
400    }
401
402    fn write<W: Write + Seek, R: Read + Seek>(
403        &self,
404        reader: &mut R,
405        writer: &mut W,
406    ) -> Result<(), SparseError> {
407        let header = SparseHeader::new(
408            BLK_SIZE.try_into().unwrap(),          // Size of the blocks
409            self.total_blocks(),                   // Total blocks in this image
410            self.chunks.len().try_into().unwrap(), // Total chunks in this image
411        );
412
413        bincode::serialize_into(&mut *writer, &header)
414            .map_err(|e| SparseError::Serialize { ty: SparseDataType::Header, source: e })?;
415
416        for chunk in &self.chunks {
417            let mut reader = if let &Chunk::Raw { start, size } = chunk {
418                reader.seek(SeekFrom::Start(start))?;
419                Some(LimitedReader(reader, start as usize + size as usize))
420            } else {
421                None
422            };
423            chunk.write(reader.as_mut(), writer, BLK_SIZE)?;
424        }
425
426        Ok(())
427    }
428}
429
430impl fmt::Display for SparseFileWriter {
431    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
432        write!(f, r"SparseFileWriter: {} Chunks:", self.chunks.len())
433    }
434}
435
436/// `add_sparse_chunk` takes the input vec, v and given `Chunk`, chunk, and
437/// attempts to add the chunk to the end of the vec. If the current last chunk
438/// is the same kind of Chunk as the `chunk`, then it will merge the two chunks
439/// into one chunk.
440///
441/// Example: A `FillChunk` with value 0 and size 1 is the last chunk
442/// in `v`, and `chunk` is a FillChunk with value 0 and size 1, after this,
443/// `v`'s last element will be a FillChunk with value 0 and size 2.
444fn add_sparse_chunk(r: &mut Vec<Chunk>, chunk: Chunk) -> Result<(), SparseError> {
445    match r.last_mut() {
446        // We've got something in the Vec... if they are both the same type,
447        // merge them, otherwise, just push the new one
448        Some(last) => match (&last, &chunk) {
449            (Chunk::Raw { start, size }, Chunk::Raw { size: new_length, .. })
450                if size.checked_add(*new_length).is_some() =>
451            {
452                *last = Chunk::Raw { start: *start, size: size + new_length };
453                return Ok(());
454            }
455            (
456                Chunk::Fill { start, size, value },
457                Chunk::Fill { size: new_size, value: new_value, .. },
458            ) if value == new_value && size.checked_add(*new_size).is_some() => {
459                *last = Chunk::Fill { start: *start, size: size + new_size, value: *value };
460                return Ok(());
461            }
462            (Chunk::DontCare { start, size }, Chunk::DontCare { size: new_size, .. })
463                if size.checked_add(*new_size).is_some() =>
464            {
465                *last = Chunk::DontCare { start: *start, size: size + new_size };
466                return Ok(());
467            }
468            _ => {}
469        },
470        None => {}
471    }
472
473    // If the chunk types differ they cannot be merged.
474    // If they are both Fill but have different values, they cannot be merged.
475    // Crc32 cannot be merged.
476    // If we don't have any chunks then we add it
477    r.push(chunk);
478    Ok(())
479}
480
481/// Reads a sparse image from `source` and expands it to its unsparsed representation in `dest`.
482pub fn unsparse<W: Writer, R: Read + Seek>(
483    source: &mut R,
484    dest: &mut W,
485) -> Result<(), SparseError> {
486    let header: SparseHeader = deserialize_from(source)
487        .map_err(|e| SparseError::Deserialize { ty: SparseDataType::Header, source: e })?;
488    if !header.valid() {
489        return Err(SparseError::InvalidHeader);
490    }
491
492    for _ in 0..header.total_chunks {
493        expand_chunk(source, dest, header.blk_sz)?;
494    }
495    // Truncate output to its current seek offset, in case the last chunk we wrote was DontNeed.
496    let offset = dest.stream_position()?;
497    dest.set_len(offset)?;
498    dest.flush()?;
499    Ok(())
500}
501
502/// Reads a chunk from `source`, and expands it, writing the result to `dest`.
503fn expand_chunk<R: Read + Seek, W: Write + Seek>(
504    source: &mut R,
505    dest: &mut W,
506    block_size: u32,
507) -> Result<(), SparseError> {
508    let header: ChunkHeader = deserialize_from(source)
509        .map_err(|e| SparseError::Deserialize { ty: SparseDataType::ChunkHeader, source: e })?;
510    if !header.valid() {
511        return Err(SparseError::InvalidChunkHeader);
512    }
513    let size = (header.chunk_sz * block_size) as usize;
514    match header.chunk_type {
515        format::CHUNK_TYPE_RAW => {
516            let limit = source.stream_position()? as usize + size;
517            std::io::copy(&mut LimitedReader(source, limit), dest)
518                .map_err(SparseError::CopyContents)?;
519        }
520        format::CHUNK_TYPE_FILL => {
521            let value: [u8; 4] = deserialize_from(source).map_err(|e| {
522                SparseError::Deserialize { ty: SparseDataType::FillValue, source: e }
523            })?;
524            assert!(size % 4 == 0);
525            let repeated = value.repeat(size / 4);
526            dest.write_all(&repeated).map_err(SparseError::FillContents)?;
527        }
528        format::CHUNK_TYPE_DONT_CARE => {
529            dest.seek(SeekFrom::Current(size as i64)).map_err(SparseError::SkipContents)?;
530        }
531        format::CHUNK_TYPE_CRC32 => {
532            let _: u32 = deserialize_from(source).map_err(|e| SparseError::Deserialize {
533                ty: SparseDataType::Checksum,
534                source: e,
535            })?;
536        }
537        _ => return Err(SparseError::InvalidChunkType(header.chunk_type)),
538    };
539    Ok(())
540}
541
542/// `resparse` takes a SparseFile and a maximum size and will
543/// break the single SparseFile into multiple SparseFiles whose
544/// size will not exceed the maximum_download_size.
545///
546/// This will return an error if max_download_size is <= BLK_SIZE
547fn resparse(
548    sparse_file: SparseFileWriter,
549    max_download_size: u64,
550) -> Result<Vec<SparseFileWriter>, SparseError> {
551    if max_download_size <= BLK_SIZE as u64 {
552        return Err(SparseError::MaxDownloadSizeTooSmall(max_download_size, BLK_SIZE));
553    }
554    let mut ret = Vec::<SparseFileWriter>::new();
555
556    // File length already starts with a header for the SparseFile as
557    // well as the size of a potential DontCare and Crc32 Chunk
558    let sunk_file_length = format::SPARSE_HEADER_SIZE as u64
559        + Chunk::DontCare { start: 0, size: BLK_SIZE.into() }.chunk_data_len() as u64
560        + Chunk::Crc32 { checksum: 2345 }.chunk_data_len() as u64;
561
562    let mut chunk_pos = 0;
563    let mut output_offset = 0;
564    while chunk_pos < sparse_file.chunks.len() {
565        log::trace!("Starting a new file at chunk position: {}", chunk_pos);
566
567        let mut file_len = 0;
568        file_len += sunk_file_length;
569
570        let mut chunks = Vec::<Chunk>::new();
571        if chunk_pos > 0 {
572            // If we already have some chunks... add a DontCare block to
573            // move the pointer
574            log::trace!("Adding a DontCare chunk offset: {}", chunk_pos);
575            let dont_care = Chunk::DontCare { start: 0, size: output_offset };
576            chunks.push(dont_care);
577        }
578
579        loop {
580            match sparse_file.chunks.get(chunk_pos) {
581                Some(chunk) => {
582                    let curr_chunk_data_len = chunk.chunk_data_len() as u64;
583                    if (file_len + curr_chunk_data_len) > max_download_size {
584                        log::trace!(
585                            "Current file size is: {} and adding another chunk of len: {} would \
586                              put us over our max: {}",
587                            file_len,
588                            curr_chunk_data_len,
589                            max_download_size
590                        );
591
592                        // Add a don't care chunk to cover everything to the end of the image. While
593                        // this is not strictly speaking needed, other tools (simg2simg) produce
594                        // this chunk, and the Sparse image inspection tool simg_dump will produce a
595                        // warning if a sparse file does not have the same number of output blocks
596                        // as declared in the header.
597                        let remainder_size = sparse_file.total_bytes() - output_offset;
598                        let dont_care =
599                            Chunk::DontCare { start: output_offset, size: remainder_size };
600                        chunks.push(dont_care);
601                        break;
602                    }
603                    log::trace!(
604                        "chunk: {} curr_chunk_data_len: {} current file size: {} \
605                          max_download_size: {} diff: {}",
606                        chunk_pos,
607                        curr_chunk_data_len,
608                        file_len,
609                        max_download_size,
610                        (max_download_size - file_len - curr_chunk_data_len)
611                    );
612                    add_sparse_chunk(&mut chunks, chunk.clone())?;
613                    file_len += curr_chunk_data_len;
614                    chunk_pos = chunk_pos + 1;
615                    output_offset += chunk.output_size() as u64;
616                }
617                None => {
618                    log::trace!("Finished iterating chunks");
619                    break;
620                }
621            }
622        }
623        let resparsed = SparseFileWriter::new(chunks);
624        log::trace!("resparse: Adding new SparseFile: {}", resparsed);
625        ret.push(resparsed);
626    }
627
628    Ok(ret)
629}
630
631/// Takes a provided `reader` and generates a set of temporary files in `dir`
632/// in the Sparse image format. With the provided `max_download_size`
633/// constraining file size.
634///
635/// # Arguments
636///
637/// * `reader` - The Sparse Reader of a Sparse File
638/// * `dir` - Path to the directory to write the Sparse file(s).
639/// * `max_download_size` - Maximum size that can be downloaded by the device.
640pub fn resparse_sparse_img<R: Read + std::io::Seek>(
641    reader: &mut SparseReader<R>,
642    dir: &Path,
643    max_download_size: u64,
644) -> Result<Vec<TempPath>, SparseError> {
645    log::debug!("Building writer from Reader");
646    let mut chunks = vec![];
647    // The sparse image we are reading from has a header and a chunk
648    // in it already. we need to have the offset reflect that.
649    let header_sunk = SPARSE_HEADER_SIZE as u64;
650    let mut raw_chunks_encountered = 0;
651    for (chunk, offset) in reader.chunks() {
652        log::info!("resparse_sparse_img. Processing chunk: {} with offset: {:#?}", chunk, offset);
653        if chunk.chunk_type() == format::CHUNK_TYPE_RAW {
654            raw_chunks_encountered += 1;
655            // This is a raw chunk. We'll split it up into blocks
656            let blks = chunk.output_blocks(BLK_SIZE);
657            log::trace!("resparse_sparse_image: splitting RAW chunk into {} chunks", blks);
658            let sunk: u64 = header_sunk + (raw_chunks_encountered * CHUNK_HEADER_SIZE) as u64;
659            for i in 0..blks {
660                let start: u64 = offset.unwrap_or(0) + (BLK_SIZE * i) as u64 - sunk;
661                log::debug!("resparse_sparse_img: adding RAW chunk at start: {}", start);
662                chunks.push(Chunk::Raw { start, size: BLK_SIZE.into() })
663            }
664        } else {
665            chunks.push(chunk.clone());
666        }
667    }
668    let sparse_file = SparseFileWriter::new(chunks);
669
670    let mut ret = Vec::<TempPath>::new();
671    log::debug!("Resparsing sparse file");
672    for re_sparsed_file in resparse(sparse_file, max_download_size)? {
673        let (file, temp_path) = NamedTempFile::new_in(dir)?.into_parts();
674        let mut file_create = File::from(file);
675
676        log::debug!("Writing resparsed {} to disk", re_sparsed_file);
677        re_sparsed_file.write(reader, &mut file_create)?;
678
679        ret.push(temp_path);
680    }
681
682    log::debug!("Finished building sparse files");
683
684    Ok(ret)
685}
686
687/// Takes the given `file_to_upload` for the `named` partition and creates a
688/// set of temporary files in the given `dir` in Sparse Image Format. With the
689/// provided `max_download_size` constraining file size.
690///
691/// # Arguments
692///
693/// * `name` - Name of the partition the image. Used for logs only.
694/// * `file_to_upload` - Path to the file to translate to sparse image format.
695/// * `dir` - Path to write the Sparse file(s).
696/// * `max_download_size` - Maximum size that can be downloaded by the device.
697pub fn build_sparse_files(
698    name: &str,
699    file_to_upload: &str,
700    dir: &Path,
701    max_download_size: u64,
702) -> Result<Vec<TempPath>, SparseError> {
703    if max_download_size <= BLK_SIZE as u64 {
704        return Err(SparseError::MaxDownloadSizeTooSmall(max_download_size, BLK_SIZE));
705    }
706    log::debug!("Building sparse files for: {}. File: {}", name, file_to_upload);
707    let mut in_file = File::open(file_to_upload)?;
708
709    let mut total_read: usize = 0;
710    // Preallocate vector to avoid reallocations as it grows.
711    let mut chunks =
712        Vec::<Chunk>::with_capacity((in_file.metadata()?.len() as usize / BLK_SIZE as usize) + 1);
713
714    let mut buf = [0u8; BLK_SIZE as usize];
715    loop {
716        let read = in_file.read(&mut buf)?;
717        if read == 0 {
718            break;
719        }
720
721        let is_fill = buf.chunks(4).collect::<Vec<&[u8]>>().array_windows().all(|[a, b]| a == b);
722
723        if is_fill {
724            // The Android Sparse Image Format specifies that a fill block
725            // is a four-byte u32 repeated to fill BLK_SIZE. Here we use
726            // bincode::deserialize to get the repeated four byte pattern from
727            // the buffer so that it can be serialized later when we write
728            // the sparse file with bincode::serialize.
729            let value: u32 =
730                bincode::deserialize(&buf[0..4]).map_err(|e| SparseError::Deserialize {
731                    ty: SparseDataType::FillValue,
732                    source: DeserializeError::Bincode(e),
733                })?;
734            // Add a fill chunk
735            let fill = Chunk::Fill {
736                start: total_read as u64,
737                size: buf.len().try_into().unwrap(),
738                value,
739            };
740            log::trace!("Sparsing file: {}. Created: {}", file_to_upload, fill);
741            chunks.push(fill);
742        } else {
743            // Add a raw chunk
744            let raw = Chunk::Raw { start: total_read as u64, size: buf.len().try_into().unwrap() };
745            log::trace!("Sparsing file: {}. Created: {}", file_to_upload, raw);
746            chunks.push(raw);
747            if read < buf.len() {
748                // We've reached the end of the file add a DontCare chunk to
749                // skip the last bit of the file which is zeroed out from the previous
750                // raw buffer
751                let skip_end =
752                    Chunk::DontCare { start: (total_read + read) as u64, size: BLK_SIZE.into() };
753                chunks.push(skip_end);
754            }
755        }
756        total_read += read;
757    }
758
759    log::trace!("Creating sparse file from: {} chunks", chunks.len());
760
761    // At this point we are making a new sparse file fom an unoptimized set of
762    // Chunks. This primarily means that adjacent Fill chunks of same value are
763    // not collapsed into a single Fill chunk (with a larger size). The advantage
764    // to this two pass approach is that (with some future work), we can create
765    // the "unoptimized" sparse file from a given image, and then "resparse" it
766    // as many times as desired with different `max_download_size` parameters.
767    // This would simplify the scenario where we want to flash the same image
768    // to multiple physical devices which may have slight differences in their
769    // hardware (and therefore different `max_download_size`es)
770    let sparse_file = SparseFileWriter::new(chunks);
771    log::trace!("Created sparse file: {}", sparse_file);
772
773    let mut ret = Vec::<TempPath>::new();
774    log::trace!("Resparsing sparse file");
775    for re_sparsed_file in resparse(sparse_file, max_download_size)? {
776        let (file, temp_path) = NamedTempFile::new_in(dir)?.into_parts();
777        let mut file_create = File::from(file);
778
779        log::trace!("Writing resparsed {} to disk", re_sparsed_file);
780        re_sparsed_file.write(&mut in_file, &mut file_create)?;
781
782        ret.push(temp_path);
783    }
784
785    log::debug!("Finished building sparse files");
786
787    Ok(ret)
788}
789
790////////////////////////////////////////////////////////////////////////////////
791// tests
792
793#[cfg(test)]
794mod test {
795    #[cfg(target_os = "linux")]
796    use crate::build_sparse_files;
797
798    use super::builder::{DataSource, SparseImageBuilder};
799    use super::{
800        BLK_SIZE, Chunk, NO_SOURCE, SparseFileWriter, add_sparse_chunk, resparse, unsparse,
801    };
802    use rand::rngs::SmallRng;
803    use rand::{RngCore, SeedableRng};
804    use std::io::{Cursor, Read as _, Seek as _, SeekFrom, Write as _};
805    #[cfg(target_os = "linux")]
806    use std::path::Path;
807    #[cfg(target_os = "linux")]
808    use std::process::{Command, Stdio};
809    use tempfile::{NamedTempFile, TempDir};
810
811    #[test]
812    fn test_fill_into_bytes() {
813        let mut dest = Cursor::new(Vec::<u8>::new());
814
815        let fill_chunk = Chunk::Fill { start: 0, size: (5 * BLK_SIZE).into(), value: 365 };
816        fill_chunk.write(NO_SOURCE, &mut dest, BLK_SIZE).unwrap();
817        assert_eq!(dest.into_inner(), [194, 202, 0, 0, 5, 0, 0, 0, 16, 0, 0, 0, 109, 1, 0, 0]);
818    }
819
820    #[test]
821    fn test_raw_into_bytes() {
822        const EXPECTED_RAW_BYTES: [u8; 22] =
823            [193, 202, 0, 0, 1, 0, 0, 0, 12, 16, 0, 0, 49, 50, 51, 52, 53, 0, 0, 0, 0, 0];
824
825        let mut source = Cursor::new(Vec::<u8>::from(&b"12345"[..]));
826        let mut sparse = Cursor::new(Vec::<u8>::new());
827        let chunk = Chunk::Raw { start: 0, size: BLK_SIZE.into() };
828
829        chunk.write(Some(&mut source), &mut sparse, BLK_SIZE).unwrap();
830        let buf = sparse.into_inner();
831        assert_eq!(buf.len(), 4108);
832        assert_eq!(&buf[..EXPECTED_RAW_BYTES.len()], EXPECTED_RAW_BYTES);
833        assert_eq!(&buf[EXPECTED_RAW_BYTES.len()..], &[0u8; 4108 - EXPECTED_RAW_BYTES.len()]);
834    }
835
836    #[test]
837    fn test_dont_care_into_bytes() {
838        let mut dest = Cursor::new(Vec::<u8>::new());
839        let chunk = Chunk::DontCare { start: 0, size: (5 * BLK_SIZE).into() };
840
841        chunk.write(NO_SOURCE, &mut dest, BLK_SIZE).unwrap();
842        assert_eq!(dest.into_inner(), [195, 202, 0, 0, 5, 0, 0, 0, 12, 0, 0, 0]);
843    }
844
845    #[test]
846    fn test_sparse_file_into_bytes() {
847        let mut source = Cursor::new(Vec::<u8>::from(&b"123"[..]));
848        let mut sparse = Cursor::new(Vec::<u8>::new());
849        let mut chunks = Vec::<Chunk>::new();
850        // Add a fill chunk
851        let fill = Chunk::Fill { start: 0, size: 4096, value: 5 };
852        chunks.push(fill);
853        // Add a raw chunk
854        let raw = Chunk::Raw { start: 0, size: 12288 };
855        chunks.push(raw);
856        // Add a dontcare chunk
857        let dontcare = Chunk::DontCare { start: 0, size: 4096 };
858        chunks.push(dontcare);
859
860        let sparsefile = SparseFileWriter::new(chunks);
861        sparsefile.write(&mut source, &mut sparse).unwrap();
862
863        sparse.seek(SeekFrom::Start(0)).unwrap();
864        let mut unsparsed = Cursor::new(Vec::<u8>::new());
865        unsparse(&mut sparse, &mut unsparsed).unwrap();
866        let buf = unsparsed.into_inner();
867        assert_eq!(buf.len(), 4096 + 12288 + 4096);
868        {
869            let chunks = buf[..4096].chunks(4);
870            for chunk in chunks {
871                assert_eq!(chunk, &[5u8, 0, 0, 0]);
872            }
873        }
874        assert_eq!(&buf[4096..4099], b"123");
875        assert_eq!(&buf[4099..16384], &[0u8; 12285]);
876        assert_eq!(&buf[16384..], &[0u8; 4096]);
877    }
878
879    ////////////////////////////////////////////////////////////////////////////
880    // Tests for resparse
881
882    #[test]
883    fn test_resparse_bails_on_too_small_size() {
884        let sparse = SparseFileWriter::new(Vec::<Chunk>::new());
885        assert!(resparse(sparse, 4095).is_err());
886    }
887
888    #[test]
889    fn test_resparse_splits() {
890        let max_download_size = 4096 * 2;
891
892        let mut chunks = Vec::<Chunk>::new();
893        chunks.push(Chunk::Raw { start: 0, size: 4096 });
894        chunks.push(Chunk::Fill { start: 4096, size: 4096, value: 2 });
895        // We want 2 sparse files with the second sparse file having a
896        // DontCare chunk and then this chunk
897        chunks.push(Chunk::Raw { start: 8192, size: 4096 });
898
899        let input_sparse_file = SparseFileWriter::new(chunks);
900        let resparsed_files = resparse(input_sparse_file, max_download_size).unwrap();
901        assert_eq!(2, resparsed_files.len());
902
903        assert_eq!(3, resparsed_files[0].chunks.len());
904        assert_eq!(Chunk::Raw { start: 0, size: 4096 }, resparsed_files[0].chunks[0]);
905        assert_eq!(Chunk::Fill { start: 4096, size: 4096, value: 2 }, resparsed_files[0].chunks[1]);
906        assert_eq!(Chunk::DontCare { start: 8192, size: 4096 }, resparsed_files[0].chunks[2]);
907
908        assert_eq!(2, resparsed_files[1].chunks.len());
909        assert_eq!(Chunk::DontCare { start: 0, size: 8192 }, resparsed_files[1].chunks[0]);
910        assert_eq!(Chunk::Raw { start: 8192, size: 4096 }, resparsed_files[1].chunks[1]);
911    }
912
913    ////////////////////////////////////////////////////////////////////////////
914    // Tests for add_sparse_chunk
915
916    #[test]
917    fn test_add_sparse_chunk_adds_empty() {
918        let init_vec = Vec::<Chunk>::new();
919        let mut res = init_vec.clone();
920        add_sparse_chunk(&mut res, Chunk::Fill { start: 0, size: 4096, value: 1 }).unwrap();
921        assert_eq!(0, init_vec.len());
922        assert_ne!(init_vec, res);
923        assert_eq!(Chunk::Fill { start: 0, size: 4096, value: 1 }, res[0]);
924    }
925
926    #[test]
927    fn test_add_sparse_chunk_fill() {
928        // Test they merge.
929        {
930            let mut init_vec = Vec::<Chunk>::new();
931            init_vec.push(Chunk::Fill { start: 0, size: 8192, value: 1 });
932            let mut res = init_vec.clone();
933            add_sparse_chunk(&mut res, Chunk::Fill { start: 0, size: 8192, value: 1 }).unwrap();
934            assert_eq!(1, res.len());
935            assert_eq!(Chunk::Fill { start: 0, size: 16384, value: 1 }, res[0]);
936        }
937
938        // Test don't merge on different value.
939        {
940            let mut init_vec = Vec::<Chunk>::new();
941            init_vec.push(Chunk::Fill { start: 0, size: 4096, value: 1 });
942            let mut res = init_vec.clone();
943            add_sparse_chunk(&mut res, Chunk::Fill { start: 0, size: 4096, value: 2 }).unwrap();
944            assert_ne!(res, init_vec);
945            assert_eq!(2, res.len());
946            assert_eq!(
947                res,
948                [
949                    Chunk::Fill { start: 0, size: 4096, value: 1 },
950                    Chunk::Fill { start: 0, size: 4096, value: 2 }
951                ]
952            );
953        }
954
955        // Test don't merge on different type.
956        {
957            let mut init_vec = Vec::<Chunk>::new();
958            init_vec.push(Chunk::Fill { start: 0, size: 4096, value: 2 });
959            let mut res = init_vec.clone();
960            add_sparse_chunk(&mut res, Chunk::DontCare { start: 0, size: 4096 }).unwrap();
961            assert_ne!(res, init_vec);
962            assert_eq!(2, res.len());
963            assert_eq!(
964                res,
965                [
966                    Chunk::Fill { start: 0, size: 4096, value: 2 },
967                    Chunk::DontCare { start: 0, size: 4096 }
968                ]
969            );
970        }
971
972        // Test don't merge when too large.
973        {
974            let mut init_vec = Vec::<Chunk>::new();
975            init_vec.push(Chunk::Fill { start: 0, size: 4096, value: 1 });
976            let mut res = init_vec.clone();
977            add_sparse_chunk(&mut res, Chunk::Fill { start: 0, size: u64::MAX - 4095, value: 1 })
978                .unwrap();
979            assert_ne!(res, init_vec);
980            assert_eq!(2, res.len());
981            assert_eq!(
982                res,
983                [
984                    Chunk::Fill { start: 0, size: 4096, value: 1 },
985                    Chunk::Fill { start: 0, size: u64::MAX - 4095, value: 1 }
986                ]
987            );
988        }
989    }
990
991    #[test]
992    fn test_add_sparse_chunk_dont_care() {
993        // Test they merge.
994        {
995            let mut init_vec = Vec::<Chunk>::new();
996            init_vec.push(Chunk::DontCare { start: 0, size: 4096 });
997            let mut res = init_vec.clone();
998            add_sparse_chunk(&mut res, Chunk::DontCare { start: 0, size: 4096 }).unwrap();
999            assert_eq!(1, res.len());
1000            assert_eq!(Chunk::DontCare { start: 0, size: 8192 }, res[0]);
1001        }
1002
1003        // Test they don't merge on different type.
1004        {
1005            let mut init_vec = Vec::<Chunk>::new();
1006            init_vec.push(Chunk::DontCare { start: 0, size: 4096 });
1007            let mut res = init_vec.clone();
1008            add_sparse_chunk(&mut res, Chunk::Fill { start: 0, size: 4096, value: 1 }).unwrap();
1009            assert_eq!(2, res.len());
1010            assert_eq!(
1011                res,
1012                [
1013                    Chunk::DontCare { start: 0, size: 4096 },
1014                    Chunk::Fill { start: 0, size: 4096, value: 1 }
1015                ]
1016            );
1017        }
1018
1019        // Test they don't merge when too large.
1020        {
1021            let mut init_vec = Vec::<Chunk>::new();
1022            init_vec.push(Chunk::DontCare { start: 0, size: 4096 });
1023            let mut res = init_vec.clone();
1024            add_sparse_chunk(&mut res, Chunk::DontCare { start: 0, size: u64::MAX - 4095 })
1025                .unwrap();
1026            assert_eq!(2, res.len());
1027            assert_eq!(
1028                res,
1029                [
1030                    Chunk::DontCare { start: 0, size: 4096 },
1031                    Chunk::DontCare { start: 0, size: u64::MAX - 4095 }
1032                ]
1033            );
1034        }
1035    }
1036
1037    #[test]
1038    fn test_add_sparse_chunk_raw() {
1039        // Test they merge.
1040        {
1041            let mut init_vec = Vec::<Chunk>::new();
1042            init_vec.push(Chunk::Raw { start: 0, size: 12288 });
1043            let mut res = init_vec.clone();
1044            add_sparse_chunk(&mut res, Chunk::Raw { start: 0, size: 16384 }).unwrap();
1045            assert_eq!(1, res.len());
1046            assert_eq!(Chunk::Raw { start: 0, size: 28672 }, res[0]);
1047        }
1048
1049        // Test they don't merge on different type.
1050        {
1051            let mut init_vec = Vec::<Chunk>::new();
1052            init_vec.push(Chunk::Raw { start: 0, size: 12288 });
1053            let mut res = init_vec.clone();
1054            add_sparse_chunk(&mut res, Chunk::Fill { start: 3, size: 8192, value: 1 }).unwrap();
1055            assert_eq!(2, res.len());
1056            assert_eq!(
1057                res,
1058                [
1059                    Chunk::Raw { start: 0, size: 12288 },
1060                    Chunk::Fill { start: 3, size: 8192, value: 1 }
1061                ]
1062            );
1063        }
1064
1065        // Test they don't merge when too large.
1066        {
1067            let mut init_vec = Vec::<Chunk>::new();
1068            init_vec.push(Chunk::Raw { start: 0, size: 4096 });
1069            let mut res = init_vec.clone();
1070            add_sparse_chunk(&mut res, Chunk::Raw { start: 0, size: u64::MAX - 4095 }).unwrap();
1071            assert_eq!(2, res.len());
1072            assert_eq!(
1073                res,
1074                [
1075                    Chunk::Raw { start: 0, size: 4096 },
1076                    Chunk::Raw { start: 0, size: u64::MAX - 4095 }
1077                ]
1078            );
1079        }
1080    }
1081
1082    #[test]
1083    fn test_add_sparse_chunk_crc32() {
1084        // Test they don't merge on same type (Crc32 is special).
1085        {
1086            let mut init_vec = Vec::<Chunk>::new();
1087            init_vec.push(Chunk::Crc32 { checksum: 1234 });
1088            let mut res = init_vec.clone();
1089            add_sparse_chunk(&mut res, Chunk::Crc32 { checksum: 2345 }).unwrap();
1090            assert_eq!(2, res.len());
1091            assert_eq!(res, [Chunk::Crc32 { checksum: 1234 }, Chunk::Crc32 { checksum: 2345 }]);
1092        }
1093
1094        // Test they don't merge on different type.
1095        {
1096            let mut init_vec = Vec::<Chunk>::new();
1097            init_vec.push(Chunk::Crc32 { checksum: 1234 });
1098            let mut res = init_vec.clone();
1099            add_sparse_chunk(&mut res, Chunk::Fill { start: 0, size: 4096, value: 1 }).unwrap();
1100            assert_eq!(2, res.len());
1101            assert_eq!(
1102                res,
1103                [Chunk::Crc32 { checksum: 1234 }, Chunk::Fill { start: 0, size: 4096, value: 1 }]
1104            );
1105        }
1106    }
1107
1108    ////////////////////////////////////////////////////////////////////////////
1109    // Integration
1110    //
1111
1112    #[test]
1113    fn test_roundtrip() {
1114        let tmpdir = TempDir::new().unwrap();
1115
1116        // Generate a large temporary file
1117        let (mut file, _temp_path) = NamedTempFile::new_in(&tmpdir).unwrap().into_parts();
1118        let mut rng = SmallRng::from_os_rng();
1119        let mut buf = Vec::<u8>::new();
1120        buf.resize(1 * 4096, 0);
1121        rng.fill_bytes(&mut buf);
1122        file.write_all(&buf).unwrap();
1123        file.flush().unwrap();
1124        file.seek(SeekFrom::Start(0)).unwrap();
1125        let content_size = buf.len();
1126
1127        // build a sparse file
1128        let mut sparse_file = NamedTempFile::new_in(&tmpdir).unwrap().into_file();
1129        SparseImageBuilder::new()
1130            .add_source(DataSource::Buffer(Box::new([0xffu8; 8192])))
1131            .add_source(DataSource::Reader { reader: Box::new(file), size: content_size as u64 })
1132            .add_source(DataSource::Fill(0xaaaa_aaaau32, 1024))
1133            .add_source(DataSource::Skip(16384))
1134            .build(&mut sparse_file)
1135            .expect("Build sparse image failed");
1136        sparse_file.seek(SeekFrom::Start(0)).unwrap();
1137
1138        let mut orig_file = NamedTempFile::new_in(&tmpdir).unwrap().into_file();
1139        unsparse(&mut sparse_file, &mut orig_file).expect("unsparse failed");
1140        orig_file.seek(SeekFrom::Start(0)).unwrap();
1141
1142        let mut unsparsed_bytes = vec![];
1143        orig_file.read_to_end(&mut unsparsed_bytes).expect("Failed to read unsparsed image");
1144        assert_eq!(unsparsed_bytes.len(), 8192 + 20480 + content_size);
1145        assert_eq!(&unsparsed_bytes[..8192], &[0xffu8; 8192]);
1146        assert_eq!(&unsparsed_bytes[8192..8192 + content_size], &buf[..]);
1147        assert_eq!(&unsparsed_bytes[8192 + content_size..12288 + content_size], &[0xaau8; 4096]);
1148        assert_eq!(&unsparsed_bytes[12288 + content_size..], &[0u8; 16384]);
1149    }
1150
1151    #[test]
1152    /// test_with_simg2img is a "round trip" test that does the following
1153    ///
1154    /// 1. Generates a pseudorandom temporary file
1155    /// 2. Builds sparse files out of it
1156    /// 3. Uses the android tool simg2img to take the sparse files and generate
1157    ///    the "original" image file out of them.
1158    /// 4. Asserts the originally created file and the one created by simg2img
1159    ///    have binary equivalent contents.
1160    ///
1161    /// This gives us a reasonable expectation of correctness given that the
1162    /// Android-provided sparse tools are able to interpret our sparse images.
1163    #[cfg(target_os = "linux")]
1164    fn test_with_simg2img() {
1165        let simg2img_path = Path::new("./host_x64/test_data/storage/sparse/simg2img");
1166        assert!(
1167            Path::exists(simg2img_path),
1168            "simg2img binary must exist at {}",
1169            simg2img_path.display()
1170        );
1171
1172        let tmpdir = TempDir::new().unwrap();
1173
1174        // Generate a large temporary file
1175        let (mut file, temp_path) = NamedTempFile::new_in(&tmpdir).unwrap().into_parts();
1176        let mut rng = SmallRng::from_os_rng();
1177        let mut buf = Vec::<u8>::new();
1178        // Dont want it to neatly fit a block size
1179        buf.resize(50 * 4096 + 1244, 0);
1180        rng.fill_bytes(&mut buf);
1181        file.write_all(&buf).unwrap();
1182        file.flush().unwrap();
1183        file.seek(SeekFrom::Start(0)).unwrap();
1184
1185        // build a sparse file
1186        let files = build_sparse_files(
1187            "test",
1188            temp_path.to_path_buf().to_str().expect("Should succeed"),
1189            tmpdir.path(),
1190            4096 * 2,
1191        )
1192        .unwrap();
1193
1194        let mut simg2img_output = tmpdir.path().to_path_buf();
1195        simg2img_output.push("output");
1196
1197        let mut simg2img = Command::new(simg2img_path)
1198            .args(&files[..])
1199            .arg(&simg2img_output)
1200            .stdout(Stdio::piped())
1201            .stderr(Stdio::piped())
1202            .spawn()
1203            .expect("Failed to spawn simg2img");
1204        let res = simg2img.wait().expect("simg2img did was not running");
1205        assert!(res.success(), "simg2img did not succeed");
1206        let mut simg2img_stdout = simg2img.stdout.take().expect("Get stdout from simg2img");
1207        let mut simg2img_stderr = simg2img.stderr.take().expect("Get stderr from simg2img");
1208
1209        let mut stdout = String::new();
1210        simg2img_stdout.read_to_string(&mut stdout).expect("Reading simg2img stdout");
1211        assert_eq!(stdout, "");
1212
1213        let mut stderr = String::new();
1214        simg2img_stderr.read_to_string(&mut stderr).expect("Reading simg2img stderr");
1215        assert_eq!(stderr, "");
1216
1217        let simg2img_output_bytes =
1218            std::fs::read(simg2img_output).expect("Failed to read simg2img output");
1219
1220        assert_eq!(
1221            buf,
1222            simg2img_output_bytes[0..buf.len()],
1223            "Output from simg2img should match our generated file"
1224        );
1225
1226        assert_eq!(
1227            simg2img_output_bytes[buf.len()..],
1228            vec![0u8; simg2img_output_bytes.len() - buf.len()],
1229            "The remainder of our simg2img_output_bytes should be 0"
1230        );
1231    }
1232
1233    #[test]
1234    #[cfg(target_os = "linux")]
1235    fn test_resparse_from_sparse() {
1236        use crate::reader::SparseReader;
1237        use crate::resparse_sparse_img;
1238
1239        let simg2img_path = Path::new("./host_x64/test_data/storage/sparse/simg2img");
1240        assert!(
1241            Path::exists(simg2img_path),
1242            "simg2img binary must exist at {}",
1243            simg2img_path.display()
1244        );
1245
1246        let tmpdir = TempDir::new().unwrap();
1247
1248        // Generate a large temporary file
1249        let (mut file, _temp_path) = NamedTempFile::new_in(&tmpdir).unwrap().into_parts();
1250        let mut rng = SmallRng::from_os_rng();
1251        let mut buf = Vec::<u8>::new();
1252        buf.resize(10 * 4096, 0);
1253        rng.fill_bytes(&mut buf);
1254        file.write_all(&buf).unwrap();
1255        file.flush().unwrap();
1256        file.seek(SeekFrom::Start(0)).unwrap();
1257        let content_size = buf.len();
1258
1259        // build a sparse file
1260        let sparse_file_tmp = NamedTempFile::new_in(&tmpdir).unwrap();
1261        let mut sparse_file = sparse_file_tmp.into_file();
1262        SparseImageBuilder::new()
1263            .add_source(DataSource::Buffer(Box::new([0xffu8; 4096 * 2])))
1264            .add_source(DataSource::Reader { reader: Box::new(file), size: content_size as u64 })
1265            .add_source(DataSource::Fill(0xaaaa_aaaau32, 1024))
1266            .add_source(DataSource::Skip(16384))
1267            .build(&mut sparse_file)
1268            .expect("Build sparse image failed");
1269        sparse_file.seek(SeekFrom::Start(0)).unwrap();
1270
1271        let mut reader = SparseReader::new(sparse_file).expect("create reader");
1272
1273        let files = resparse_sparse_img(&mut reader, tmpdir.path(), 4096 * 3).unwrap();
1274
1275        // Re build the image from the sparse files
1276        let mut simg2img_output_sparsed = tmpdir.path().to_path_buf();
1277        simg2img_output_sparsed.push("output_sparsed");
1278
1279        let mut simg2img_sparsed = Command::new(simg2img_path)
1280            .args(&files[..])
1281            .arg(&simg2img_output_sparsed)
1282            .stdout(Stdio::piped())
1283            .stderr(Stdio::piped())
1284            .spawn()
1285            .expect("Failed to spawn simg2img");
1286        let res = simg2img_sparsed.wait().expect("simg2img did was not running");
1287        assert!(res.success(), "simg2img did not succeed");
1288        let mut simg2img_stdout = simg2img_sparsed.stdout.take().expect("Get stdout from simg2img");
1289        let mut simg2img_stderr = simg2img_sparsed.stderr.take().expect("Get stderr from simg2img");
1290
1291        let mut stdout = String::new();
1292        simg2img_stdout.read_to_string(&mut stdout).expect("Reading simg2img stdout");
1293        assert_eq!(stdout, "");
1294
1295        let mut stderr = String::new();
1296        simg2img_stderr.read_to_string(&mut stderr).expect("Reading simg2img stderr");
1297        assert_eq!(stderr, "");
1298    }
1299}