1#[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, 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
26const 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
141pub trait Writer: Write + Seek {
143 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
160struct 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
176pub 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 { start: u64, size: u64 },
193 Fill { start: u64, size: u64, value: u32 },
197 DontCare { start: u64, size: u64 },
204 #[allow(dead_code)]
208 Crc32 { checksum: u32 },
209}
210
211impl Chunk {
212 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 _ => unreachable!(),
245 }
246 }
247
248 fn valid(&self, block_size: u32) -> bool {
249 self.output_size() % (block_size as u64) == 0
250 }
251
252 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 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 fn output_blocks(&self, block_size: u32) -> u32 {
275 self.output_size().div_ceil(block_size as u64) as u32
276 }
277
278 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 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 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 bincode::serialize_into(dest, value).map_err(|e| SparseError::Serialize {
343 ty: SparseDataType::FillValue,
344 source: e,
345 })?;
346 }
347 Self::DontCare { .. } => {
348 }
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
379pub const NO_SOURCE: Option<&mut Cursor<&[u8]>> = None;
383
384#[derive(Clone, Debug, PartialEq)]
390pub struct SparseFileWriter {
391 pub chunks: Vec<Chunk>,
393}
394
395impl SparseFileWriter {
396 pub fn new(chunks: Vec<Chunk>) -> SparseFileWriter {
398 SparseFileWriter { chunks }
399 }
400
401 pub fn total_blocks(&self) -> u32 {
403 self.chunks.iter().map(|c| c.output_blocks(BLK_SIZE)).sum()
404 }
405
406 pub fn total_bytes(&self) -> u64 {
408 self.chunks.iter().map(|c| c.output_size() as u64).sum()
409 }
410
411 pub fn file_size(&self) -> u64 {
414 let mut size = format::SPARSE_HEADER_SIZE as u64;
415 for chunk in &self.chunks {
416 size += chunk.chunk_data_len() as u64;
417 }
418 size
419 }
420
421 pub fn slice_reader<'a, R: Read + Seek>(
429 &'a self,
430 source: &'a mut R,
431 ) -> Result<SparseSliceReader<'a, R>, SparseError> {
432 SparseSliceReader::new(self, source)
433 }
434
435 pub fn write<W: Write + Seek, R: Read + Seek>(
442 &self,
443 reader: &mut R,
444 writer: &mut W,
445 ) -> Result<(), SparseError> {
446 let header = SparseHeader::new(
447 BLK_SIZE.try_into().unwrap(), self.total_blocks(), self.chunks.len().try_into().unwrap(), );
451
452 bincode::serialize_into(&mut *writer, &header)
453 .map_err(|e| SparseError::Serialize { ty: SparseDataType::Header, source: e })?;
454
455 for chunk in &self.chunks {
456 let mut reader = if let &Chunk::Raw { start, size } = chunk {
457 if reader.stream_position()? != start {
458 reader.seek(SeekFrom::Start(start))?;
459 }
460 Some(LimitedReader(reader, start as usize + size as usize))
461 } else {
462 None
463 };
464 chunk.write(reader.as_mut(), writer, BLK_SIZE)?;
465 }
466
467 Ok(())
468 }
469}
470
471pub struct SparseSliceReader<'a, R> {
476 source: &'a mut R,
477 header_bytes: Vec<u8>,
478 header_pos: usize,
479 chunks: &'a [Chunk],
480 chunk_idx: usize,
481 chunk_header_bytes: Vec<u8>,
482 chunk_header_pos: usize,
483 payload_pos: u64,
484}
485
486impl<'a, R: Read + Seek> SparseSliceReader<'a, R> {
487 pub fn new(writer: &'a SparseFileWriter, source: &'a mut R) -> Result<Self, SparseError> {
495 let header = SparseHeader::new(
496 BLK_SIZE.try_into().unwrap(),
497 writer.total_blocks(),
498 writer.chunks.len().try_into().unwrap(),
499 );
500 let header_bytes = bincode::serialize(&header)
501 .map_err(|e| SparseError::Serialize { ty: SparseDataType::Header, source: e })?;
502 let mut reader = Self {
503 source,
504 header_bytes,
505 header_pos: 0,
506 chunks: &writer.chunks,
507 chunk_idx: 0,
508 chunk_header_bytes: Vec::new(),
509 chunk_header_pos: 0,
510 payload_pos: 0,
511 };
512 reader.prepare_next_chunk_header()?;
513 Ok(reader)
514 }
515
516 fn prepare_next_chunk_header(&mut self) -> Result<(), SparseError> {
517 if self.chunk_idx < self.chunks.len() {
518 let chunk = &self.chunks[self.chunk_idx];
519 if !chunk.valid(BLK_SIZE) {
520 return Err(SparseError::UnalignedChunk);
521 }
522 let header = ChunkHeader::new(
523 chunk.chunk_type(),
524 0x0,
525 chunk.output_blocks(BLK_SIZE),
526 chunk.chunk_data_len(),
527 );
528 self.chunk_header_bytes = bincode::serialize(&header).map_err(|e| {
529 SparseError::Serialize { ty: SparseDataType::ChunkHeader, source: e }
530 })?;
531 self.chunk_header_pos = 0;
532 self.payload_pos = 0;
533 }
534 Ok(())
535 }
536}
537
538impl<'a, R: Read + Seek> Read for SparseSliceReader<'a, R> {
539 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
540 if buf.is_empty() {
541 return Ok(0);
542 }
543
544 let mut total_written = 0;
545
546 while total_written < buf.len() {
547 if self.header_pos < self.header_bytes.len() {
549 let to_copy = std::cmp::min(
550 buf.len() - total_written,
551 self.header_bytes.len() - self.header_pos,
552 );
553 buf[total_written..total_written + to_copy].copy_from_slice(
554 &self.header_bytes[self.header_pos..self.header_pos + to_copy],
555 );
556 self.header_pos += to_copy;
557 total_written += to_copy;
558 continue;
559 }
560
561 if self.chunk_idx >= self.chunks.len() {
562 break;
563 }
564
565 if self.chunk_header_pos < self.chunk_header_bytes.len() {
567 let to_copy = std::cmp::min(
568 buf.len() - total_written,
569 self.chunk_header_bytes.len() - self.chunk_header_pos,
570 );
571 buf[total_written..total_written + to_copy].copy_from_slice(
572 &self.chunk_header_bytes
573 [self.chunk_header_pos..self.chunk_header_pos + to_copy],
574 );
575 self.chunk_header_pos += to_copy;
576 total_written += to_copy;
577 continue;
578 }
579
580 let chunk = &self.chunks[self.chunk_idx];
582 match chunk {
583 Chunk::Raw { start, size } => {
584 let remaining_payload = *size - self.payload_pos;
585 if remaining_payload > 0 {
586 if self.payload_pos == 0 {
587 if self.source.stream_position()? != *start {
588 self.source.seek(SeekFrom::Start(*start))?;
589 }
590 }
591 let to_read =
592 std::cmp::min(buf.len() - total_written, remaining_payload as usize);
593 let n =
594 self.source.read(&mut buf[total_written..total_written + to_read])?;
595 if n == 0 {
596 let zeroes = std::cmp::min(
598 buf.len() - total_written,
599 remaining_payload as usize,
600 );
601 buf[total_written..total_written + zeroes].fill(0);
602 self.payload_pos += zeroes as u64;
603 total_written += zeroes;
604 if self.payload_pos >= *size {
605 self.chunk_idx += 1;
606 self.prepare_next_chunk_header().map_err(|e| {
607 std::io::Error::new(std::io::ErrorKind::Other, e)
608 })?;
609 }
610 continue;
611 }
612 self.payload_pos += n as u64;
613 total_written += n;
614 if self.payload_pos >= *size {
615 self.chunk_idx += 1;
616 self.prepare_next_chunk_header()
617 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
618 }
619 return Ok(total_written);
620 }
621 }
622 Chunk::Fill { value, .. } => {
623 if self.payload_pos < 4 {
624 let val_bytes = value.to_le_bytes();
625 let remaining = 4 - self.payload_pos as usize;
626 let to_copy = std::cmp::min(buf.len() - total_written, remaining);
627 let start = self.payload_pos as usize;
628 buf[total_written..total_written + to_copy]
629 .copy_from_slice(&val_bytes[start..start + to_copy]);
630 self.payload_pos += to_copy as u64;
631 total_written += to_copy;
632 if self.payload_pos >= 4 {
633 self.chunk_idx += 1;
634 self.prepare_next_chunk_header()
635 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
636 }
637 continue;
638 }
639 }
640 Chunk::Crc32 { checksum } => {
641 if self.payload_pos < 4 {
642 let val_bytes = checksum.to_le_bytes();
643 let remaining = 4 - self.payload_pos as usize;
644 let to_copy = std::cmp::min(buf.len() - total_written, remaining);
645 let start = self.payload_pos as usize;
646 buf[total_written..total_written + to_copy]
647 .copy_from_slice(&val_bytes[start..start + to_copy]);
648 self.payload_pos += to_copy as u64;
649 total_written += to_copy;
650 if self.payload_pos >= 4 {
651 self.chunk_idx += 1;
652 self.prepare_next_chunk_header()
653 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
654 }
655 continue;
656 }
657 }
658 Chunk::DontCare { .. } => {
659 self.chunk_idx += 1;
660 self.prepare_next_chunk_header()
661 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
662 continue;
663 }
664 }
665
666 self.chunk_idx += 1;
667 self.prepare_next_chunk_header()
668 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
669 }
670
671 Ok(total_written)
672 }
673}
674
675impl fmt::Display for SparseFileWriter {
676 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
677 write!(f, r"SparseFileWriter: {} Chunks:", self.chunks.len())
678 }
679}
680
681fn add_sparse_chunk(r: &mut Vec<Chunk>, chunk: Chunk) {
690 match r.last_mut() {
691 Some(last) => match (&last, &chunk) {
694 (Chunk::Raw { start, size }, Chunk::Raw { size: new_length, .. })
695 if size.checked_add(*new_length).is_some() =>
696 {
697 *last = Chunk::Raw { start: *start, size: size + new_length };
698 return;
699 }
700 (
701 Chunk::Fill { start, size, value },
702 Chunk::Fill { size: new_size, value: new_value, .. },
703 ) if value == new_value && size.checked_add(*new_size).is_some() => {
704 *last = Chunk::Fill { start: *start, size: size + new_size, value: *value };
705 return;
706 }
707 (Chunk::DontCare { start, size }, Chunk::DontCare { size: new_size, .. })
708 if size.checked_add(*new_size).is_some() =>
709 {
710 *last = Chunk::DontCare { start: *start, size: size + new_size };
711 return;
712 }
713 _ => {}
714 },
715 None => {}
716 }
717
718 r.push(chunk);
723}
724
725pub fn unsparse<W: Writer, R: Read + Seek>(
727 source: &mut R,
728 dest: &mut W,
729) -> Result<(), SparseError> {
730 let header: SparseHeader = deserialize_from(source)
731 .map_err(|e| SparseError::Deserialize { ty: SparseDataType::Header, source: e })?;
732 if !header.valid() {
733 return Err(SparseError::InvalidHeader);
734 }
735
736 for _ in 0..header.total_chunks {
737 expand_chunk(source, dest, header.blk_sz)?;
738 }
739 let offset = dest.stream_position()?;
741 dest.set_len(offset)?;
742 dest.flush()?;
743 Ok(())
744}
745
746fn expand_chunk<R: Read + Seek, W: Write + Seek>(
748 source: &mut R,
749 dest: &mut W,
750 block_size: u32,
751) -> Result<(), SparseError> {
752 let header: ChunkHeader = deserialize_from(source)
753 .map_err(|e| SparseError::Deserialize { ty: SparseDataType::ChunkHeader, source: e })?;
754 if !header.valid() {
755 return Err(SparseError::InvalidChunkHeader);
756 }
757 let size = (header.chunk_sz * block_size) as usize;
758 match header.chunk_type {
759 format::CHUNK_TYPE_RAW => {
760 let limit = source.stream_position()? as usize + size;
761 std::io::copy(&mut LimitedReader(source, limit), dest)
762 .map_err(SparseError::CopyContents)?;
763 }
764 format::CHUNK_TYPE_FILL => {
765 let value: [u8; 4] = deserialize_from(source).map_err(|e| {
766 SparseError::Deserialize { ty: SparseDataType::FillValue, source: e }
767 })?;
768 assert!(size % 4 == 0);
769 let repeated = value.repeat(size / 4);
770 dest.write_all(&repeated).map_err(SparseError::FillContents)?;
771 }
772 format::CHUNK_TYPE_DONT_CARE => {
773 dest.seek(SeekFrom::Current(size as i64)).map_err(SparseError::SkipContents)?;
774 }
775 format::CHUNK_TYPE_CRC32 => {
776 let _: u32 = deserialize_from(source).map_err(|e| SparseError::Deserialize {
777 ty: SparseDataType::Checksum,
778 source: e,
779 })?;
780 }
781 _ => return Err(SparseError::InvalidChunkType(header.chunk_type)),
782 };
783 Ok(())
784}
785
786pub fn resparse(
799 sparse_file: SparseFileWriter,
800 max_download_size: u64,
801) -> Result<Vec<SparseFileWriter>, SparseError> {
802 if max_download_size <= BLK_SIZE as u64 {
803 return Err(SparseError::MaxDownloadSizeTooSmall(max_download_size, BLK_SIZE));
804 }
805 let mut ret = Vec::<SparseFileWriter>::new();
806
807 let sunk_file_length = format::SPARSE_HEADER_SIZE as u64
810 + Chunk::DontCare { start: 0, size: BLK_SIZE.into() }.chunk_data_len() as u64
811 + Chunk::Crc32 { checksum: 2345 }.chunk_data_len() as u64;
812
813 let total_image_bytes = sparse_file.total_bytes();
814 let mut chunk_pos = 0;
815 let mut offset_in_raw = 0u64;
816 let mut output_offset = 0u64;
817
818 while chunk_pos < sparse_file.chunks.len() {
819 log::trace!(
820 "Starting a new file at chunk position: {}, offset_in_raw: {}",
821 chunk_pos,
822 offset_in_raw
823 );
824
825 let mut file_len = sunk_file_length;
826 let mut chunks = Vec::<Chunk>::new();
827
828 if output_offset > 0 {
829 log::trace!("Adding a DontCare chunk offset: {}", output_offset);
832 let dont_care = Chunk::DontCare { start: 0, size: output_offset };
833 chunks.push(dont_care);
834 }
835
836 loop {
837 if chunk_pos >= sparse_file.chunks.len() {
838 log::trace!("Finished iterating chunks");
839 break;
840 }
841
842 let chunk = &sparse_file.chunks[chunk_pos];
843 match chunk {
844 Chunk::Raw { start, size } => {
845 let remaining_raw = *size - offset_in_raw;
846 let chunk_header_len = format::CHUNK_HEADER_SIZE as u64;
847 let available_in_file = max_download_size.saturating_sub(file_len);
848
849 if available_in_file < chunk_header_len + BLK_SIZE as u64 {
850 let remainder_size = total_image_bytes.saturating_sub(output_offset);
852 if remainder_size > 0 {
853 let dont_care =
854 Chunk::DontCare { start: output_offset, size: remainder_size };
855 chunks.push(dont_care);
856 }
857 break;
858 }
859
860 let max_raw_payload = ((available_in_file - chunk_header_len)
861 / BLK_SIZE as u64)
862 * BLK_SIZE as u64;
863 let to_take = std::cmp::min(remaining_raw, max_raw_payload);
864
865 if to_take == 0 {
866 let remainder_size = total_image_bytes.saturating_sub(output_offset);
867 if remainder_size > 0 {
868 let dont_care =
869 Chunk::DontCare { start: output_offset, size: remainder_size };
870 chunks.push(dont_care);
871 }
872 break;
873 }
874
875 let sub_chunk = Chunk::Raw { start: *start + offset_in_raw, size: to_take };
876 add_sparse_chunk(&mut chunks, sub_chunk);
877 file_len += chunk_header_len + to_take;
878 output_offset += to_take;
879 offset_in_raw += to_take;
880
881 if offset_in_raw == *size {
882 chunk_pos += 1;
883 offset_in_raw = 0;
884 } else {
885 let remainder_size = total_image_bytes.saturating_sub(output_offset);
887 if remainder_size > 0 {
888 let dont_care =
889 Chunk::DontCare { start: output_offset, size: remainder_size };
890 chunks.push(dont_care);
891 }
892 break;
893 }
894 }
895 other => {
896 let curr_chunk_data_len = other.chunk_data_len() as u64;
897 if (file_len + curr_chunk_data_len) > max_download_size {
898 let remainder_size = total_image_bytes.saturating_sub(output_offset);
899 if remainder_size > 0 {
900 let dont_care =
901 Chunk::DontCare { start: output_offset, size: remainder_size };
902 chunks.push(dont_care);
903 }
904 break;
905 }
906
907 add_sparse_chunk(&mut chunks, other.clone());
908 file_len += curr_chunk_data_len;
909 output_offset += other.output_size() as u64;
910 chunk_pos += 1;
911 }
912 }
913 }
914
915 let resparsed = SparseFileWriter::new(chunks);
916 log::trace!("resparse: Adding new SparseFile: {}", resparsed);
917 ret.push(resparsed);
918 }
919
920 Ok(ret)
921}
922
923pub fn resparse_sparse_img_writers<R: Read + std::io::Seek>(
936 reader: &mut SparseReader<R>,
937 max_download_size: u64,
938) -> Result<Vec<SparseFileWriter>, SparseError> {
939 log::debug!("Building writers from Reader");
940 let mut chunks = vec![];
941 for (chunk, _offset) in reader.chunks() {
942 chunks.push(chunk.clone());
943 }
944 let sparse_file = SparseFileWriter::new(chunks);
945 resparse(sparse_file, max_download_size)
946}
947
948pub fn resparse_sparse_img<R: Read + std::io::Seek>(
958 reader: &mut SparseReader<R>,
959 dir: &Path,
960 max_download_size: u64,
961) -> Result<Vec<TempPath>, SparseError> {
962 let mut ret = Vec::<TempPath>::new();
963 log::debug!("Resparsing sparse file");
964 for re_sparsed_file in resparse_sparse_img_writers(reader, max_download_size)? {
965 let (file, temp_path) = NamedTempFile::new_in(dir)?.into_parts();
966 let mut file_create = File::from(file);
967
968 log::debug!("Writing resparsed {} to disk", re_sparsed_file);
969 re_sparsed_file.write(reader, &mut file_create)?;
970
971 ret.push(temp_path);
972 }
973
974 log::debug!("Finished building sparse files");
975
976 Ok(ret)
977}
978
979#[inline]
982pub(crate) fn find_fill_value(buf: &[u8]) -> Option<u32> {
983 if buf.len() < 4 || buf.len() % 4 != 0 {
984 return None;
985 }
986 let first = u32::from_le_bytes(buf[0..4].try_into().unwrap());
987 for chunk in buf[4..].chunks_exact(4) {
988 if u32::from_le_bytes(chunk.try_into().unwrap()) != first {
989 return None;
990 }
991 }
992 Some(first)
993}
994
995pub fn build_sparse_writers_streaming<F>(
1011 name: &str,
1012 file_to_upload: &str,
1013 max_download_size: u64,
1014 mut on_slice: F,
1015) -> Result<(), SparseError>
1016where
1017 F: FnMut(SparseFileWriter) -> Result<(), SparseError>,
1018{
1019 if max_download_size <= BLK_SIZE.into() {
1020 return Err(SparseError::MaxDownloadSizeTooSmall(max_download_size, BLK_SIZE));
1021 }
1022 if BLK_SIZE as usize % std::mem::size_of::<u32>() != 0 {
1023 return Err(SparseError::UnalignedDataSource(UnalignedSource::Buffer(BLK_SIZE as usize)));
1024 }
1025 log::debug!("Building sparse writers (streaming) for: {}. File: {}", name, file_to_upload);
1026 let mut in_file = File::open(file_to_upload)?;
1027 let total_image_bytes = in_file.metadata()?.len().next_multiple_of(BLK_SIZE.into());
1028
1029 let sunk_file_length = u64::from(
1030 format::SPARSE_HEADER_SIZE
1031 + CHUNK_HEADER_SIZE
1032 + Chunk::Crc32 { checksum: 2345 }.chunk_data_len(),
1033 );
1034
1035 let mut current_slice_chunks = Vec::<Chunk>::new();
1036 let mut current_slice_file_len = sunk_file_length;
1037 let mut output_offset = 0u64;
1038 let mut total_read = 0usize;
1039
1040 let mut buf = [0u8; BLK_SIZE as usize];
1041 loop {
1042 let read = in_file.read(&mut buf)?;
1043 if read == 0 {
1044 break;
1045 }
1046 buf[read..].fill(0);
1048
1049 let start = total_read as u64;
1050 let size = buf.len().try_into().unwrap();
1051 let candidate_chunk = if let Some(value) = find_fill_value(&buf) {
1052 Chunk::Fill { start, size, value }
1053 } else {
1054 Chunk::Raw { start, size }
1055 };
1056
1057 let candidate_data_len = u64::from(candidate_chunk.chunk_data_len());
1058
1059 if current_slice_file_len + candidate_data_len > max_download_size {
1060 let remainder_size = total_image_bytes.saturating_sub(output_offset);
1061 if remainder_size > 0 {
1062 let dont_care = Chunk::DontCare { start: output_offset, size: remainder_size };
1063 current_slice_chunks.push(dont_care);
1064 }
1065
1066 let slice_writer = SparseFileWriter::new(current_slice_chunks);
1067 log::trace!("Emitting completed sparse slice: {}", slice_writer);
1068 on_slice(slice_writer)?;
1069
1070 current_slice_chunks = if output_offset > 0 {
1071 vec![Chunk::DontCare { start: 0, size: output_offset }]
1072 } else {
1073 Vec::new()
1074 };
1075 current_slice_file_len = sunk_file_length + u64::from(CHUNK_HEADER_SIZE);
1076 }
1077
1078 add_sparse_chunk(&mut current_slice_chunks, candidate_chunk);
1079 current_slice_file_len += candidate_data_len;
1080 output_offset += buf.len() as u64;
1081 total_read += read;
1082 }
1083
1084 if !current_slice_chunks.is_empty() {
1085 let slice_writer = SparseFileWriter::new(current_slice_chunks);
1086 log::trace!("Emitting final sparse slice: {}", slice_writer);
1087 on_slice(slice_writer)?;
1088 }
1089
1090 Ok(())
1091}
1092
1093pub fn build_sparse_writers(
1109 name: &str,
1110 file_to_upload: &str,
1111 max_download_size: u64,
1112) -> Result<Vec<SparseFileWriter>, SparseError> {
1113 let mut writers = Vec::new();
1114 build_sparse_writers_streaming(name, file_to_upload, max_download_size, |writer| {
1115 writers.push(writer);
1116 Ok(())
1117 })?;
1118 Ok(writers)
1119}
1120
1121pub fn build_sparse_files(
1132 name: &str,
1133 file_to_upload: &str,
1134 dir: &Path,
1135 max_download_size: u64,
1136) -> Result<Vec<TempPath>, SparseError> {
1137 let mut in_file = File::open(file_to_upload)?;
1138 let mut ret = Vec::<TempPath>::new();
1139 log::trace!("Resparsing sparse file");
1140 for re_sparsed_file in build_sparse_writers(name, file_to_upload, max_download_size)? {
1141 let (file, temp_path) = NamedTempFile::new_in(dir)?.into_parts();
1142 let mut file_create = File::from(file);
1143
1144 log::trace!("Writing resparsed {} to disk", re_sparsed_file);
1145 re_sparsed_file.write(&mut in_file, &mut file_create)?;
1146
1147 ret.push(temp_path);
1148 }
1149
1150 log::debug!("Finished building sparse files");
1151
1152 Ok(ret)
1153}
1154
1155#[cfg(test)]
1159mod test {
1160 #[cfg(target_os = "linux")]
1161 use crate::build_sparse_files;
1162
1163 use super::builder::{DataSource, SparseImageBuilder};
1164 use super::{
1165 BLK_SIZE, Chunk, NO_SOURCE, SparseFileWriter, add_sparse_chunk, resparse, unsparse,
1166 };
1167 use rand::Rng as _;
1168 use rand::rngs::SmallRng;
1169 use std::io::{Cursor, Read as _, Seek as _, SeekFrom, Write as _};
1170 #[cfg(target_os = "linux")]
1171 use std::path::Path;
1172 #[cfg(target_os = "linux")]
1173 use std::process::{Command, Stdio};
1174 use tempfile::{NamedTempFile, TempDir};
1175
1176 #[test]
1177 fn test_fill_into_bytes() {
1178 let mut dest = Cursor::new(Vec::<u8>::new());
1179
1180 let fill_chunk = Chunk::Fill { start: 0, size: (5 * BLK_SIZE).into(), value: 365 };
1181 fill_chunk.write(NO_SOURCE, &mut dest, BLK_SIZE).unwrap();
1182 assert_eq!(dest.into_inner(), [194, 202, 0, 0, 5, 0, 0, 0, 16, 0, 0, 0, 109, 1, 0, 0]);
1183 }
1184
1185 #[test]
1186 fn test_raw_into_bytes() {
1187 const EXPECTED_RAW_BYTES: [u8; 22] =
1188 [193, 202, 0, 0, 1, 0, 0, 0, 12, 16, 0, 0, 49, 50, 51, 52, 53, 0, 0, 0, 0, 0];
1189
1190 let mut source = Cursor::new(Vec::<u8>::from(&b"12345"[..]));
1191 let mut sparse = Cursor::new(Vec::<u8>::new());
1192 let chunk = Chunk::Raw { start: 0, size: BLK_SIZE.into() };
1193
1194 chunk.write(Some(&mut source), &mut sparse, BLK_SIZE).unwrap();
1195 let buf = sparse.into_inner();
1196 assert_eq!(buf.len(), 4108);
1197 assert_eq!(&buf[..EXPECTED_RAW_BYTES.len()], EXPECTED_RAW_BYTES);
1198 assert_eq!(&buf[EXPECTED_RAW_BYTES.len()..], &[0u8; 4108 - EXPECTED_RAW_BYTES.len()]);
1199 }
1200
1201 #[test]
1202 fn test_dont_care_into_bytes() {
1203 let mut dest = Cursor::new(Vec::<u8>::new());
1204 let chunk = Chunk::DontCare { start: 0, size: (5 * BLK_SIZE).into() };
1205
1206 chunk.write(NO_SOURCE, &mut dest, BLK_SIZE).unwrap();
1207 assert_eq!(dest.into_inner(), [195, 202, 0, 0, 5, 0, 0, 0, 12, 0, 0, 0]);
1208 }
1209
1210 #[test]
1211 fn test_sparse_file_into_bytes() {
1212 let mut source = Cursor::new(Vec::<u8>::from(&b"123"[..]));
1213 let mut sparse = Cursor::new(Vec::<u8>::new());
1214 let mut chunks = Vec::<Chunk>::new();
1215 let fill = Chunk::Fill { start: 0, size: 4096, value: 5 };
1217 chunks.push(fill);
1218 let raw = Chunk::Raw { start: 0, size: 12288 };
1220 chunks.push(raw);
1221 let dontcare = Chunk::DontCare { start: 0, size: 4096 };
1223 chunks.push(dontcare);
1224
1225 let sparsefile = SparseFileWriter::new(chunks);
1226 sparsefile.write(&mut source, &mut sparse).unwrap();
1227
1228 sparse.seek(SeekFrom::Start(0)).unwrap();
1229 let mut unsparsed = Cursor::new(Vec::<u8>::new());
1230 unsparse(&mut sparse, &mut unsparsed).unwrap();
1231 let buf = unsparsed.into_inner();
1232 assert_eq!(buf.len(), 4096 + 12288 + 4096);
1233 {
1234 let chunks = buf[..4096].chunks(4);
1235 for chunk in chunks {
1236 assert_eq!(chunk, &[5u8, 0, 0, 0]);
1237 }
1238 }
1239 assert_eq!(&buf[4096..4099], b"123");
1240 assert_eq!(&buf[4099..16384], &[0u8; 12285]);
1241 assert_eq!(&buf[16384..], &[0u8; 4096]);
1242 }
1243
1244 #[test]
1248 fn test_resparse_bails_on_too_small_size() {
1249 let sparse = SparseFileWriter::new(Vec::<Chunk>::new());
1250 assert!(resparse(sparse, 4095).is_err());
1251 }
1252
1253 #[test]
1254 fn test_resparse_splits() {
1255 let max_download_size = 4096 * 2;
1256
1257 let mut chunks = Vec::<Chunk>::new();
1258 chunks.push(Chunk::Raw { start: 0, size: 4096 });
1259 chunks.push(Chunk::Fill { start: 4096, size: 4096, value: 2 });
1260 chunks.push(Chunk::Raw { start: 8192, size: 4096 });
1263
1264 let input_sparse_file = SparseFileWriter::new(chunks);
1265 let resparsed_files = resparse(input_sparse_file, max_download_size).unwrap();
1266 assert_eq!(2, resparsed_files.len());
1267
1268 assert_eq!(3, resparsed_files[0].chunks.len());
1269 assert_eq!(Chunk::Raw { start: 0, size: 4096 }, resparsed_files[0].chunks[0]);
1270 assert_eq!(Chunk::Fill { start: 4096, size: 4096, value: 2 }, resparsed_files[0].chunks[1]);
1271 assert_eq!(Chunk::DontCare { start: 8192, size: 4096 }, resparsed_files[0].chunks[2]);
1272
1273 assert_eq!(2, resparsed_files[1].chunks.len());
1274 assert_eq!(Chunk::DontCare { start: 0, size: 8192 }, resparsed_files[1].chunks[0]);
1275 assert_eq!(Chunk::Raw { start: 8192, size: 4096 }, resparsed_files[1].chunks[1]);
1276 }
1277
1278 #[test]
1279 fn test_resparse_splits_large_raw_chunk() {
1280 let max_download_size = 4096 * 2;
1282 let mut chunks = Vec::<Chunk>::new();
1283 chunks.push(Chunk::Raw { start: 0, size: 16384 });
1284
1285 let input_sparse_file = SparseFileWriter::new(chunks);
1286 let resparsed_files = resparse(input_sparse_file, max_download_size).unwrap();
1287
1288 let mut total_output = 0;
1292 for file in &resparsed_files {
1293 assert!(file.total_blocks() * 4096 <= 16384 + 4096);
1294 for chunk in &file.chunks {
1295 if let Chunk::Raw { size, .. } = chunk {
1296 total_output += size;
1297 }
1298 }
1299 }
1300 assert_eq!(total_output, 16384);
1301 }
1302
1303 #[test]
1307 fn test_add_sparse_chunk_adds_empty() {
1308 let init_vec = Vec::<Chunk>::new();
1309 let mut res = init_vec.clone();
1310 add_sparse_chunk(&mut res, Chunk::Fill { start: 0, size: 4096, value: 1 });
1311 assert_eq!(0, init_vec.len());
1312 assert_ne!(init_vec, res);
1313 assert_eq!(Chunk::Fill { start: 0, size: 4096, value: 1 }, res[0]);
1314 }
1315
1316 #[test]
1317 fn test_add_sparse_chunk_fill() {
1318 {
1320 let mut init_vec = Vec::<Chunk>::new();
1321 init_vec.push(Chunk::Fill { start: 0, size: 8192, value: 1 });
1322 let mut res = init_vec.clone();
1323 add_sparse_chunk(&mut res, Chunk::Fill { start: 0, size: 8192, value: 1 });
1324 assert_eq!(1, res.len());
1325 assert_eq!(Chunk::Fill { start: 0, size: 16384, value: 1 }, res[0]);
1326 }
1327
1328 {
1330 let mut init_vec = Vec::<Chunk>::new();
1331 init_vec.push(Chunk::Fill { start: 0, size: 4096, value: 1 });
1332 let mut res = init_vec.clone();
1333 add_sparse_chunk(&mut res, Chunk::Fill { start: 0, size: 4096, value: 2 });
1334 assert_ne!(res, init_vec);
1335 assert_eq!(2, res.len());
1336 assert_eq!(
1337 res,
1338 [
1339 Chunk::Fill { start: 0, size: 4096, value: 1 },
1340 Chunk::Fill { start: 0, size: 4096, value: 2 }
1341 ]
1342 );
1343 }
1344
1345 {
1347 let mut init_vec = Vec::<Chunk>::new();
1348 init_vec.push(Chunk::Fill { start: 0, size: 4096, value: 2 });
1349 let mut res = init_vec.clone();
1350 add_sparse_chunk(&mut res, Chunk::DontCare { start: 0, size: 4096 });
1351 assert_ne!(res, init_vec);
1352 assert_eq!(2, res.len());
1353 assert_eq!(
1354 res,
1355 [
1356 Chunk::Fill { start: 0, size: 4096, value: 2 },
1357 Chunk::DontCare { start: 0, size: 4096 }
1358 ]
1359 );
1360 }
1361
1362 {
1364 let mut init_vec = Vec::<Chunk>::new();
1365 init_vec.push(Chunk::Fill { start: 0, size: 4096, value: 1 });
1366 let mut res = init_vec.clone();
1367 add_sparse_chunk(&mut res, Chunk::Fill { start: 0, size: u64::MAX - 4095, value: 1 });
1368 assert_ne!(res, init_vec);
1369 assert_eq!(2, res.len());
1370 assert_eq!(
1371 res,
1372 [
1373 Chunk::Fill { start: 0, size: 4096, value: 1 },
1374 Chunk::Fill { start: 0, size: u64::MAX - 4095, value: 1 }
1375 ]
1376 );
1377 }
1378 }
1379
1380 #[test]
1381 fn test_add_sparse_chunk_dont_care() {
1382 {
1384 let mut init_vec = Vec::<Chunk>::new();
1385 init_vec.push(Chunk::DontCare { start: 0, size: 4096 });
1386 let mut res = init_vec.clone();
1387 add_sparse_chunk(&mut res, Chunk::DontCare { start: 0, size: 4096 });
1388 assert_eq!(1, res.len());
1389 assert_eq!(Chunk::DontCare { start: 0, size: 8192 }, res[0]);
1390 }
1391
1392 {
1394 let mut init_vec = Vec::<Chunk>::new();
1395 init_vec.push(Chunk::DontCare { start: 0, size: 4096 });
1396 let mut res = init_vec.clone();
1397 add_sparse_chunk(&mut res, Chunk::Fill { start: 0, size: 4096, value: 1 });
1398 assert_eq!(2, res.len());
1399 assert_eq!(
1400 res,
1401 [
1402 Chunk::DontCare { start: 0, size: 4096 },
1403 Chunk::Fill { start: 0, size: 4096, value: 1 }
1404 ]
1405 );
1406 }
1407
1408 {
1410 let mut init_vec = Vec::<Chunk>::new();
1411 init_vec.push(Chunk::DontCare { start: 0, size: 4096 });
1412 let mut res = init_vec.clone();
1413 add_sparse_chunk(&mut res, Chunk::DontCare { start: 0, size: u64::MAX - 4095 });
1414 assert_eq!(2, res.len());
1415 assert_eq!(
1416 res,
1417 [
1418 Chunk::DontCare { start: 0, size: 4096 },
1419 Chunk::DontCare { start: 0, size: u64::MAX - 4095 }
1420 ]
1421 );
1422 }
1423 }
1424
1425 #[test]
1426 fn test_add_sparse_chunk_raw() {
1427 {
1429 let mut init_vec = Vec::<Chunk>::new();
1430 init_vec.push(Chunk::Raw { start: 0, size: 12288 });
1431 let mut res = init_vec.clone();
1432 add_sparse_chunk(&mut res, Chunk::Raw { start: 0, size: 16384 });
1433 assert_eq!(1, res.len());
1434 assert_eq!(Chunk::Raw { start: 0, size: 28672 }, res[0]);
1435 }
1436
1437 {
1439 let mut init_vec = Vec::<Chunk>::new();
1440 init_vec.push(Chunk::Raw { start: 0, size: 12288 });
1441 let mut res = init_vec.clone();
1442 add_sparse_chunk(&mut res, Chunk::Fill { start: 3, size: 8192, value: 1 });
1443 assert_eq!(2, res.len());
1444 assert_eq!(
1445 res,
1446 [
1447 Chunk::Raw { start: 0, size: 12288 },
1448 Chunk::Fill { start: 3, size: 8192, value: 1 }
1449 ]
1450 );
1451 }
1452
1453 {
1455 let mut init_vec = Vec::<Chunk>::new();
1456 init_vec.push(Chunk::Raw { start: 0, size: 4096 });
1457 let mut res = init_vec.clone();
1458 add_sparse_chunk(&mut res, Chunk::Raw { start: 0, size: u64::MAX - 4095 });
1459 assert_eq!(2, res.len());
1460 assert_eq!(
1461 res,
1462 [
1463 Chunk::Raw { start: 0, size: 4096 },
1464 Chunk::Raw { start: 0, size: u64::MAX - 4095 }
1465 ]
1466 );
1467 }
1468 }
1469
1470 #[test]
1471 fn test_add_sparse_chunk_crc32() {
1472 {
1474 let mut init_vec = Vec::<Chunk>::new();
1475 init_vec.push(Chunk::Crc32 { checksum: 1234 });
1476 let mut res = init_vec.clone();
1477 add_sparse_chunk(&mut res, Chunk::Crc32 { checksum: 2345 });
1478 assert_eq!(2, res.len());
1479 assert_eq!(res, [Chunk::Crc32 { checksum: 1234 }, Chunk::Crc32 { checksum: 2345 }]);
1480 }
1481
1482 {
1484 let mut init_vec = Vec::<Chunk>::new();
1485 init_vec.push(Chunk::Crc32 { checksum: 1234 });
1486 let mut res = init_vec.clone();
1487 add_sparse_chunk(&mut res, Chunk::Fill { start: 0, size: 4096, value: 1 });
1488 assert_eq!(2, res.len());
1489 assert_eq!(
1490 res,
1491 [Chunk::Crc32 { checksum: 1234 }, Chunk::Fill { start: 0, size: 4096, value: 1 }]
1492 );
1493 }
1494 }
1495
1496 #[test]
1501 fn test_roundtrip() {
1502 let tmpdir = TempDir::new().unwrap();
1503
1504 let (mut file, _temp_path) = NamedTempFile::new_in(&tmpdir).unwrap().into_parts();
1506 let mut rng: SmallRng = rand::make_rng();
1507 let mut buf = Vec::<u8>::new();
1508 buf.resize(1 * 4096, 0);
1509 rng.fill_bytes(&mut buf);
1510 file.write_all(&buf).unwrap();
1511 file.flush().unwrap();
1512 file.seek(SeekFrom::Start(0)).unwrap();
1513 let content_size = buf.len();
1514
1515 let mut sparse_file = NamedTempFile::new_in(&tmpdir).unwrap().into_file();
1517 SparseImageBuilder::new()
1518 .add_source(DataSource::Buffer(Box::new([0xffu8; 8192])))
1519 .add_source(DataSource::Reader { reader: Box::new(file), size: content_size as u64 })
1520 .add_source(DataSource::Fill(0xaaaa_aaaau32, 1024))
1521 .add_source(DataSource::Skip(16384))
1522 .build(&mut sparse_file)
1523 .expect("Build sparse image failed");
1524 sparse_file.seek(SeekFrom::Start(0)).unwrap();
1525
1526 let mut orig_file = NamedTempFile::new_in(&tmpdir).unwrap().into_file();
1527 unsparse(&mut sparse_file, &mut orig_file).expect("unsparse failed");
1528 orig_file.seek(SeekFrom::Start(0)).unwrap();
1529
1530 let mut unsparsed_bytes = vec![];
1531 orig_file.read_to_end(&mut unsparsed_bytes).expect("Failed to read unsparsed image");
1532 assert_eq!(unsparsed_bytes.len(), 8192 + 20480 + content_size);
1533 assert_eq!(&unsparsed_bytes[..8192], &[0xffu8; 8192]);
1534 assert_eq!(&unsparsed_bytes[8192..8192 + content_size], &buf[..]);
1535 assert_eq!(&unsparsed_bytes[8192 + content_size..12288 + content_size], &[0xaau8; 4096]);
1536 assert_eq!(&unsparsed_bytes[12288 + content_size..], &[0u8; 16384]);
1537 }
1538
1539 #[test]
1540 #[cfg(target_os = "linux")]
1552 fn test_with_simg2img() {
1553 let simg2img_path = Path::new("./host_x64/test_data/storage/sparse/simg2img");
1554 assert!(
1555 Path::exists(simg2img_path),
1556 "simg2img binary must exist at {}",
1557 simg2img_path.display()
1558 );
1559
1560 let tmpdir = TempDir::new().unwrap();
1561
1562 let (mut file, temp_path) = NamedTempFile::new_in(&tmpdir).unwrap().into_parts();
1564 let mut rng: SmallRng = rand::make_rng();
1565 let mut buf = Vec::<u8>::new();
1566 buf.resize(50 * 4096 + 1244, 0);
1568 rng.fill_bytes(&mut buf);
1569 file.write_all(&buf).unwrap();
1570 file.flush().unwrap();
1571 file.seek(SeekFrom::Start(0)).unwrap();
1572
1573 let files = build_sparse_files(
1575 "test",
1576 temp_path.to_path_buf().to_str().expect("Should succeed"),
1577 tmpdir.path(),
1578 4096 * 2,
1579 )
1580 .unwrap();
1581
1582 let mut simg2img_output = tmpdir.path().to_path_buf();
1583 simg2img_output.push("output");
1584
1585 let mut simg2img = Command::new(simg2img_path)
1586 .args(&files[..])
1587 .arg(&simg2img_output)
1588 .stdout(Stdio::piped())
1589 .stderr(Stdio::piped())
1590 .spawn()
1591 .expect("Failed to spawn simg2img");
1592 let res = simg2img.wait().expect("simg2img did was not running");
1593 assert!(res.success(), "simg2img did not succeed");
1594 let mut simg2img_stdout = simg2img.stdout.take().expect("Get stdout from simg2img");
1595 let mut simg2img_stderr = simg2img.stderr.take().expect("Get stderr from simg2img");
1596
1597 let mut stdout = String::new();
1598 simg2img_stdout.read_to_string(&mut stdout).expect("Reading simg2img stdout");
1599 assert_eq!(stdout, "");
1600
1601 let mut stderr = String::new();
1602 simg2img_stderr.read_to_string(&mut stderr).expect("Reading simg2img stderr");
1603 assert_eq!(stderr, "");
1604
1605 let simg2img_output_bytes =
1606 std::fs::read(simg2img_output).expect("Failed to read simg2img output");
1607
1608 assert_eq!(
1609 buf,
1610 simg2img_output_bytes[0..buf.len()],
1611 "Output from simg2img should match our generated file"
1612 );
1613
1614 assert_eq!(
1615 simg2img_output_bytes[buf.len()..],
1616 vec![0u8; simg2img_output_bytes.len() - buf.len()],
1617 "The remainder of our simg2img_output_bytes should be 0"
1618 );
1619 }
1620
1621 #[test]
1622 #[cfg(target_os = "linux")]
1623 fn test_resparse_from_sparse() {
1624 use crate::reader::SparseReader;
1625 use crate::resparse_sparse_img;
1626
1627 let simg2img_path = Path::new("./host_x64/test_data/storage/sparse/simg2img");
1628 assert!(
1629 Path::exists(simg2img_path),
1630 "simg2img binary must exist at {}",
1631 simg2img_path.display()
1632 );
1633
1634 let tmpdir = TempDir::new().unwrap();
1635
1636 let (mut file, _temp_path) = NamedTempFile::new_in(&tmpdir).unwrap().into_parts();
1638 let mut rng: SmallRng = rand::make_rng();
1639 let mut buf = Vec::<u8>::new();
1640 buf.resize(10 * 4096, 0);
1641 rng.fill_bytes(&mut buf);
1642 file.write_all(&buf).unwrap();
1643 file.flush().unwrap();
1644 file.seek(SeekFrom::Start(0)).unwrap();
1645 let content_size = buf.len();
1646
1647 let sparse_file_tmp = NamedTempFile::new_in(&tmpdir).unwrap();
1649 let mut sparse_file = sparse_file_tmp.into_file();
1650 SparseImageBuilder::new()
1651 .add_source(DataSource::Buffer(Box::new([0xffu8; 4096 * 2])))
1652 .add_source(DataSource::Reader { reader: Box::new(file), size: content_size as u64 })
1653 .add_source(DataSource::Fill(0xaaaa_aaaau32, 1024))
1654 .add_source(DataSource::Skip(16384))
1655 .build(&mut sparse_file)
1656 .expect("Build sparse image failed");
1657 sparse_file.seek(SeekFrom::Start(0)).unwrap();
1658
1659 let mut reader = SparseReader::new(sparse_file).expect("create reader");
1660
1661 let files = resparse_sparse_img(&mut reader, tmpdir.path(), 4096 * 3).unwrap();
1662
1663 let mut simg2img_output_sparsed = tmpdir.path().to_path_buf();
1665 simg2img_output_sparsed.push("output_sparsed");
1666
1667 let mut simg2img_sparsed = Command::new(simg2img_path)
1668 .args(&files[..])
1669 .arg(&simg2img_output_sparsed)
1670 .stdout(Stdio::piped())
1671 .stderr(Stdio::piped())
1672 .spawn()
1673 .expect("Failed to spawn simg2img");
1674 let res = simg2img_sparsed.wait().expect("simg2img did was not running");
1675 assert!(res.success(), "simg2img did not succeed");
1676 let mut simg2img_stdout = simg2img_sparsed.stdout.take().expect("Get stdout from simg2img");
1677 let mut simg2img_stderr = simg2img_sparsed.stderr.take().expect("Get stderr from simg2img");
1678
1679 let mut stdout = String::new();
1680 simg2img_stdout.read_to_string(&mut stdout).expect("Reading simg2img stdout");
1681 assert_eq!(stdout, "");
1682
1683 let mut stderr = String::new();
1684 simg2img_stderr.read_to_string(&mut stderr).expect("Reading simg2img stderr");
1685 assert_eq!(stderr, "");
1686 }
1687
1688 #[test]
1689 fn test_find_fill_value() {
1690 assert_eq!(super::find_fill_value(&[]), None);
1691 assert_eq!(super::find_fill_value(&[1, 2, 3]), None);
1692 assert_eq!(super::find_fill_value(&[1, 2, 3, 4, 5]), None);
1693
1694 let mut buf = [0u8; 4096];
1695 assert_eq!(super::find_fill_value(&buf), Some(0));
1696
1697 buf.fill(0xaa);
1698 assert_eq!(super::find_fill_value(&buf), Some(0xaaaa_aaaa));
1699
1700 for chunk in buf.chunks_exact_mut(4) {
1701 chunk.copy_from_slice(&0x1234_5678u32.to_le_bytes());
1702 }
1703 assert_eq!(super::find_fill_value(&buf), Some(0x1234_5678));
1704
1705 buf[0] = 0x00;
1707 assert_eq!(super::find_fill_value(&buf), None);
1708
1709 buf[0] = 0x78;
1711 buf[2048] = 0x00;
1712 assert_eq!(super::find_fill_value(&buf), None);
1713
1714 buf[2048] = 0x78;
1716 buf[4095] = 0x00;
1717 assert_eq!(super::find_fill_value(&buf), None);
1718 }
1719
1720 #[test]
1721 fn test_sparse_slice_reader_matches_write() {
1722 let mut source_data = Vec::<u8>::new();
1723 source_data.resize(4096 * 4, 0);
1724 let mut rng: SmallRng = rand::make_rng();
1725 rng.fill_bytes(&mut source_data);
1726
1727 let mut chunks = Vec::<Chunk>::new();
1728 chunks.push(Chunk::Raw { start: 0, size: 4096 * 2 });
1729 chunks.push(Chunk::Fill { start: 4096 * 2, size: 4096, value: 0x1234_5678 });
1730 chunks.push(Chunk::DontCare { start: 4096 * 3, size: 4096 });
1731 chunks.push(Chunk::Raw { start: 4096 * 3, size: 4096 });
1732
1733 let writer = SparseFileWriter::new(chunks);
1734
1735 let mut written_bytes = Cursor::new(Vec::<u8>::new());
1737 let mut source_cursor = Cursor::new(source_data.clone());
1738 writer.write(&mut source_cursor, &mut written_bytes).unwrap();
1739 let expected = written_bytes.into_inner();
1740
1741 let mut slice_source = Cursor::new(source_data.clone());
1743 let mut slice_reader = writer.slice_reader(&mut slice_source).unwrap();
1744 let mut read_bytes = Vec::<u8>::new();
1745 slice_reader.read_to_end(&mut read_bytes).unwrap();
1746
1747 assert_eq!(expected, read_bytes);
1748 assert_eq!(read_bytes.len() as u64, writer.file_size());
1749 }
1750}