disk/
android_sparse.rs

1// Copyright 2019 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// https://android.googlesource.com/platform/system/core/+/7b444f0/libsparse/sparse_format.h
6
7use std::collections::BTreeMap;
8use std::fs::File;
9use std::io;
10use std::io::ErrorKind;
11use std::io::Read;
12use std::io::Seek;
13use std::io::SeekFrom;
14use std::mem;
15use std::sync::Arc;
16
17use async_trait::async_trait;
18use base::AsRawDescriptor;
19use base::FileAllocate;
20use base::FileReadWriteAtVolatile;
21use base::FileSetLen;
22use base::RawDescriptor;
23use base::VolatileSlice;
24use cros_async::BackingMemory;
25use cros_async::Executor;
26use cros_async::IoOptions;
27use cros_async::IoSource;
28use data_model::Le16;
29use data_model::Le32;
30use remain::sorted;
31use thiserror::Error;
32use zerocopy::FromBytes;
33use zerocopy::FromZeros;
34use zerocopy::Immutable;
35use zerocopy::IntoBytes;
36use zerocopy::KnownLayout;
37
38use crate::AsyncDisk;
39use crate::DiskFile;
40use crate::DiskGetLen;
41use crate::Error as DiskError;
42use crate::Result as DiskResult;
43use crate::ToAsyncDisk;
44
45#[sorted]
46#[derive(Error, Debug)]
47pub enum Error {
48    #[error("invalid magic header for android sparse format")]
49    InvalidMagicHeader,
50    #[error("invalid specification: \"{0}\"")]
51    InvalidSpecification(String),
52    #[error("failed to read specification: \"{0}\"")]
53    ReadSpecificationError(io::Error),
54}
55
56pub type Result<T> = std::result::Result<T, Error>;
57
58pub const SPARSE_HEADER_MAGIC: u32 = 0xed26ff3a;
59const MAJOR_VERSION: u16 = 1;
60
61#[repr(C)]
62#[derive(Clone, Copy, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
63struct SparseHeader {
64    magic: Le32,          // SPARSE_HEADER_MAGIC
65    major_version: Le16,  // (0x1) - reject images with higher major versions
66    minor_version: Le16,  // (0x0) - allow images with higer minor versions
67    file_hdr_sz: Le16,    // 28 bytes for first revision of the file format
68    chunk_hdr_size: Le16, // 12 bytes for first revision of the file format
69    blk_sz: Le32,         // block size in bytes, must be a multiple of 4 (4096)
70    total_blks: Le32,     // total blocks in the non-sparse output image
71    total_chunks: Le32,   // total chunks in the sparse input image
72    // CRC32 checksum of the original data, counting "don't care" as 0. Standard 802.3 polynomial,
73    // use a Public Domain table implementation
74    image_checksum: Le32,
75}
76
77const CHUNK_TYPE_RAW: u16 = 0xCAC1;
78const CHUNK_TYPE_FILL: u16 = 0xCAC2;
79const CHUNK_TYPE_DONT_CARE: u16 = 0xCAC3;
80const CHUNK_TYPE_CRC32: u16 = 0xCAC4;
81
82#[repr(C)]
83#[derive(Clone, Copy, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
84struct ChunkHeader {
85    chunk_type: Le16, /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
86    reserved1: u16,
87    chunk_sz: Le32, /* in blocks in output image */
88    total_sz: Le32, /* in bytes of chunk input file including chunk header and data */
89}
90
91#[derive(Clone, Debug, PartialEq, Eq)]
92enum Chunk {
93    Raw(u64), // Offset into the file
94    Fill([u8; 4]),
95    DontCare,
96}
97
98#[derive(Clone, Debug, PartialEq, Eq)]
99struct ChunkWithSize {
100    chunk: Chunk,
101    expanded_size: u64,
102}
103
104/* Following a Raw or Fill or CRC32 chunk is data.
105 *  For a Raw chunk, it's the data in chunk_sz * blk_sz.
106 *  For a Fill chunk, it's 4 bytes of the fill data.
107 *  For a CRC32 chunk, it's 4 bytes of CRC32
108 */
109#[derive(Debug)]
110pub struct AndroidSparse {
111    file: File,
112    total_size: u64,
113    chunks: BTreeMap<u64, ChunkWithSize>,
114}
115
116fn parse_chunk<T: Read + Seek>(input: &mut T, blk_sz: u64) -> Result<Option<ChunkWithSize>> {
117    const HEADER_SIZE: usize = mem::size_of::<ChunkHeader>();
118    let current_offset = input
119        .stream_position()
120        .map_err(Error::ReadSpecificationError)?;
121    let mut chunk_header = ChunkHeader::new_zeroed();
122    input
123        .read_exact(chunk_header.as_mut_bytes())
124        .map_err(Error::ReadSpecificationError)?;
125    let chunk_body_size = (chunk_header.total_sz.to_native() as usize)
126        .checked_sub(HEADER_SIZE)
127        .ok_or(Error::InvalidSpecification(format!(
128            "chunk total_sz {} smaller than header size {}",
129            chunk_header.total_sz.to_native(),
130            HEADER_SIZE
131        )))?;
132    let chunk = match chunk_header.chunk_type.to_native() {
133        CHUNK_TYPE_RAW => {
134            input
135                .seek(SeekFrom::Current(chunk_body_size as i64))
136                .map_err(Error::ReadSpecificationError)?;
137            Chunk::Raw(current_offset + HEADER_SIZE as u64)
138        }
139        CHUNK_TYPE_FILL => {
140            let mut fill_bytes = [0u8; 4];
141            if chunk_body_size != fill_bytes.len() {
142                return Err(Error::InvalidSpecification(format!(
143                    "Fill chunk had bad size. Expected {}, was {}",
144                    fill_bytes.len(),
145                    chunk_body_size
146                )));
147            }
148            input
149                .read_exact(&mut fill_bytes)
150                .map_err(Error::ReadSpecificationError)?;
151            Chunk::Fill(fill_bytes)
152        }
153        CHUNK_TYPE_DONT_CARE => Chunk::DontCare,
154        CHUNK_TYPE_CRC32 => return Ok(None), // TODO(schuffelen): Validate crc32s in input
155        unknown_type => {
156            return Err(Error::InvalidSpecification(format!(
157                "Chunk had invalid type, was {unknown_type:x}"
158            )))
159        }
160    };
161    let expanded_size = chunk_header.chunk_sz.to_native() as u64 * blk_sz;
162    Ok(Some(ChunkWithSize {
163        chunk,
164        expanded_size,
165    }))
166}
167
168impl AndroidSparse {
169    pub fn from_file(mut file: File) -> Result<AndroidSparse> {
170        file.seek(SeekFrom::Start(0))
171            .map_err(Error::ReadSpecificationError)?;
172        let mut sparse_header = SparseHeader::new_zeroed();
173        file.read_exact(sparse_header.as_mut_bytes())
174            .map_err(Error::ReadSpecificationError)?;
175        if sparse_header.magic != SPARSE_HEADER_MAGIC {
176            return Err(Error::InvalidSpecification(format!(
177                "Header did not match magic constant. Expected {:x}, was {:x}",
178                SPARSE_HEADER_MAGIC,
179                sparse_header.magic.to_native()
180            )));
181        } else if sparse_header.major_version != MAJOR_VERSION {
182            return Err(Error::InvalidSpecification(format!(
183                "Header major version did not match. Expected {}, was {}",
184                MAJOR_VERSION,
185                sparse_header.major_version.to_native(),
186            )));
187        } else if sparse_header.chunk_hdr_size.to_native() as usize != mem::size_of::<ChunkHeader>()
188        {
189            // The canonical parser for this format allows `chunk_hdr_size >= sizeof(ChunkHeader)`,
190            // but we've chosen to be stricter for simplicity.
191            return Err(Error::InvalidSpecification(format!(
192                "Chunk header size does not match chunk header struct, expected {}, was {}",
193                sparse_header.chunk_hdr_size.to_native(),
194                mem::size_of::<ChunkHeader>()
195            )));
196        }
197        let block_size = sparse_header.blk_sz.to_native() as u64;
198        let chunks = (0..sparse_header.total_chunks.to_native())
199            .filter_map(|_| parse_chunk(&mut file, block_size).transpose())
200            .collect::<Result<Vec<ChunkWithSize>>>()?;
201        let total_size =
202            sparse_header.total_blks.to_native() as u64 * sparse_header.blk_sz.to_native() as u64;
203        AndroidSparse::from_parts(file, total_size, chunks)
204    }
205
206    fn from_parts(file: File, size: u64, chunks: Vec<ChunkWithSize>) -> Result<AndroidSparse> {
207        let mut chunks_map: BTreeMap<u64, ChunkWithSize> = BTreeMap::new();
208        let mut expanded_location: u64 = 0;
209        for chunk_with_size in chunks {
210            let size = chunk_with_size.expanded_size;
211            if chunks_map
212                .insert(expanded_location, chunk_with_size)
213                .is_some()
214            {
215                return Err(Error::InvalidSpecification(format!(
216                    "Two chunks were at {expanded_location}"
217                )));
218            }
219            expanded_location += size;
220        }
221        let image = AndroidSparse {
222            file,
223            total_size: size,
224            chunks: chunks_map,
225        };
226        let calculated_len: u64 = image.chunks.iter().map(|x| x.1.expanded_size).sum();
227        if calculated_len != size {
228            return Err(Error::InvalidSpecification(format!(
229                "Header promised size {size}, chunks added up to {calculated_len}"
230            )));
231        }
232        Ok(image)
233    }
234}
235
236impl DiskGetLen for AndroidSparse {
237    fn get_len(&self) -> io::Result<u64> {
238        Ok(self.total_size)
239    }
240}
241
242impl FileSetLen for AndroidSparse {
243    fn set_len(&self, _len: u64) -> io::Result<()> {
244        Err(io::Error::new(
245            ErrorKind::PermissionDenied,
246            "unsupported operation",
247        ))
248    }
249}
250
251impl AsRawDescriptor for AndroidSparse {
252    fn as_raw_descriptor(&self) -> RawDescriptor {
253        self.file.as_raw_descriptor()
254    }
255}
256
257// Performs reads up to the chunk boundary.
258impl FileReadWriteAtVolatile for AndroidSparse {
259    fn read_at_volatile(&self, slice: VolatileSlice, offset: u64) -> io::Result<usize> {
260        let found_chunk = self.chunks.range(..=offset).next_back();
261        let (
262            chunk_start,
263            ChunkWithSize {
264                chunk,
265                expanded_size,
266            },
267        ) = found_chunk.ok_or_else(|| {
268            io::Error::new(
269                ErrorKind::UnexpectedEof,
270                format!("no chunk for offset {offset}"),
271            )
272        })?;
273        let chunk_offset = offset - chunk_start;
274        let chunk_size = *expanded_size;
275        let subslice = if chunk_offset + (slice.size() as u64) > chunk_size {
276            slice
277                .sub_slice(0, (chunk_size - chunk_offset) as usize)
278                .map_err(|e| io::Error::new(ErrorKind::InvalidData, format!("{e:?}")))?
279        } else {
280            slice
281        };
282        match chunk {
283            Chunk::DontCare => {
284                subslice.write_bytes(0);
285                Ok(subslice.size())
286            }
287            Chunk::Raw(file_offset) => self
288                .file
289                .read_at_volatile(subslice, *file_offset + chunk_offset),
290            Chunk::Fill(fill_bytes) => {
291                let chunk_offset_mod = chunk_offset % fill_bytes.len() as u64;
292                let filled_memory: Vec<u8> = fill_bytes
293                    .iter()
294                    .cloned()
295                    .cycle()
296                    .skip(chunk_offset_mod as usize)
297                    .take(subslice.size())
298                    .collect();
299                subslice.copy_from(&filled_memory);
300                Ok(subslice.size())
301            }
302        }
303    }
304    fn write_at_volatile(&self, _slice: VolatileSlice, _offset: u64) -> io::Result<usize> {
305        Err(io::Error::new(
306            ErrorKind::PermissionDenied,
307            "unsupported operation",
308        ))
309    }
310}
311
312// TODO(b/271381851): implement `try_clone`. It allows virtio-blk to run multiple workers.
313impl DiskFile for AndroidSparse {}
314
315/// An Android Sparse disk that implements `AsyncDisk` for access.
316pub struct AsyncAndroidSparse {
317    inner: IoSource<File>,
318    total_size: u64,
319    chunks: BTreeMap<u64, ChunkWithSize>,
320}
321
322impl ToAsyncDisk for AndroidSparse {
323    fn to_async_disk(self: Box<Self>, ex: &Executor) -> DiskResult<Box<dyn AsyncDisk>> {
324        Ok(Box::new(AsyncAndroidSparse {
325            inner: ex.async_from(self.file).map_err(DiskError::ToAsync)?,
326            total_size: self.total_size,
327            chunks: self.chunks,
328        }))
329    }
330}
331
332impl DiskGetLen for AsyncAndroidSparse {
333    fn get_len(&self) -> io::Result<u64> {
334        Ok(self.total_size)
335    }
336}
337
338impl FileSetLen for AsyncAndroidSparse {
339    fn set_len(&self, _len: u64) -> io::Result<()> {
340        Err(io::Error::new(
341            ErrorKind::PermissionDenied,
342            "unsupported operation",
343        ))
344    }
345}
346
347impl FileAllocate for AsyncAndroidSparse {
348    fn allocate(&self, _offset: u64, _length: u64) -> io::Result<()> {
349        Err(io::Error::new(
350            ErrorKind::PermissionDenied,
351            "unsupported operation",
352        ))
353    }
354}
355
356#[async_trait(?Send)]
357impl AsyncDisk for AsyncAndroidSparse {
358    async fn flush(&self) -> crate::Result<()> {
359        // android sparse is read-only, nothing to flush.
360        Ok(())
361    }
362
363    async fn fsync(&self) -> DiskResult<()> {
364        // Do nothing because it's read-only.
365        Ok(())
366    }
367
368    async fn fdatasync(&self) -> DiskResult<()> {
369        // Do nothing because it's read-only.
370        Ok(())
371    }
372
373    /// Reads data from `file_offset` to the end of the current chunk and write them into memory
374    /// `mem` at `mem_offsets`.
375    async fn read_to_mem<'a>(
376        &'a self,
377        file_offset: u64,
378        mem: Arc<dyn BackingMemory + Send + Sync>,
379        mem_offsets: cros_async::MemRegionIter<'a>,
380        options: IoOptions,
381    ) -> DiskResult<usize> {
382        let found_chunk = self.chunks.range(..=file_offset).next_back();
383        let (
384            chunk_start,
385            ChunkWithSize {
386                chunk,
387                expanded_size,
388            },
389        ) = found_chunk.ok_or(DiskError::ReadingData(io::Error::new(
390            ErrorKind::UnexpectedEof,
391            format!("no chunk for offset {file_offset}"),
392        )))?;
393        let chunk_offset = file_offset - chunk_start;
394        let chunk_size = *expanded_size;
395
396        // Truncate `mem_offsets` to the remaining size of the current chunk.
397        let mem_offsets = mem_offsets.take_bytes((chunk_size - chunk_offset) as usize);
398        let mem_size = mem_offsets.clone().map(|x| x.len).sum();
399        match chunk {
400            Chunk::DontCare => {
401                for region in mem_offsets {
402                    mem.get_volatile_slice(region)
403                        .map_err(DiskError::GuestMemory)?
404                        .write_bytes(0);
405                }
406                Ok(mem_size)
407            }
408            Chunk::Raw(offset) => self
409                .inner
410                .read_to_mem(Some(offset + chunk_offset), mem, mem_offsets, options)
411                .await
412                .map_err(DiskError::ReadToMem),
413            Chunk::Fill(fill_bytes) => {
414                let chunk_offset_mod = chunk_offset % fill_bytes.len() as u64;
415                let filled_memory: Vec<u8> = fill_bytes
416                    .iter()
417                    .cloned()
418                    .cycle()
419                    .skip(chunk_offset_mod as usize)
420                    .take(mem_size)
421                    .collect();
422
423                let mut filled_count = 0;
424                for region in mem_offsets {
425                    let buf = &filled_memory[filled_count..filled_count + region.len];
426                    mem.get_volatile_slice(region)
427                        .map_err(DiskError::GuestMemory)?
428                        .copy_from(buf);
429                    filled_count += region.len;
430                }
431                Ok(mem_size)
432            }
433        }
434    }
435
436    async fn write_from_mem<'a>(
437        &'a self,
438        _file_offset: u64,
439        _mem: Arc<dyn BackingMemory + Send + Sync>,
440        _mem_offsets: cros_async::MemRegionIter<'a>,
441        _options: IoOptions,
442    ) -> DiskResult<usize> {
443        Err(DiskError::UnsupportedOperation)
444    }
445
446    async fn punch_hole(&self, _file_offset: u64, _length: u64) -> DiskResult<()> {
447        Err(DiskError::UnsupportedOperation)
448    }
449
450    async fn write_zeroes_at(&self, _file_offset: u64, _length: u64) -> DiskResult<()> {
451        Err(DiskError::UnsupportedOperation)
452    }
453}
454
455#[cfg(test)]
456mod tests {
457    use std::io::Cursor;
458    use std::io::Write;
459
460    use super::*;
461
462    const CHUNK_SIZE: usize = mem::size_of::<ChunkHeader>();
463
464    #[test]
465    fn parse_raw() {
466        let chunk_raw = ChunkHeader {
467            chunk_type: CHUNK_TYPE_RAW.into(),
468            reserved1: 0,
469            chunk_sz: 1.into(),
470            total_sz: (CHUNK_SIZE as u32 + 123).into(),
471        };
472        let header_bytes = chunk_raw.as_bytes();
473        let mut chunk_bytes: Vec<u8> = Vec::new();
474        chunk_bytes.extend_from_slice(header_bytes);
475        chunk_bytes.extend_from_slice(&[0u8; 123]);
476        let mut chunk_cursor = Cursor::new(chunk_bytes);
477        let chunk = parse_chunk(&mut chunk_cursor, 123)
478            .expect("Failed to parse")
479            .expect("Failed to determine chunk type");
480        let expected_chunk = ChunkWithSize {
481            chunk: Chunk::Raw(CHUNK_SIZE as u64),
482            expanded_size: 123,
483        };
484        assert_eq!(expected_chunk, chunk);
485    }
486
487    #[test]
488    fn parse_dont_care() {
489        let chunk_raw = ChunkHeader {
490            chunk_type: CHUNK_TYPE_DONT_CARE.into(),
491            reserved1: 0,
492            chunk_sz: 100.into(),
493            total_sz: (CHUNK_SIZE as u32).into(),
494        };
495        let header_bytes = chunk_raw.as_bytes();
496        let mut chunk_cursor = Cursor::new(header_bytes);
497        let chunk = parse_chunk(&mut chunk_cursor, 123)
498            .expect("Failed to parse")
499            .expect("Failed to determine chunk type");
500        let expected_chunk = ChunkWithSize {
501            chunk: Chunk::DontCare,
502            expanded_size: 12300,
503        };
504        assert_eq!(expected_chunk, chunk);
505    }
506
507    #[test]
508    fn parse_fill() {
509        let chunk_raw = ChunkHeader {
510            chunk_type: CHUNK_TYPE_FILL.into(),
511            reserved1: 0,
512            chunk_sz: 100.into(),
513            total_sz: (CHUNK_SIZE as u32 + 4).into(),
514        };
515        let header_bytes = chunk_raw.as_bytes();
516        let mut chunk_bytes: Vec<u8> = Vec::new();
517        chunk_bytes.extend_from_slice(header_bytes);
518        chunk_bytes.extend_from_slice(&[123u8; 4]);
519        let mut chunk_cursor = Cursor::new(chunk_bytes);
520        let chunk = parse_chunk(&mut chunk_cursor, 123)
521            .expect("Failed to parse")
522            .expect("Failed to determine chunk type");
523        let expected_chunk = ChunkWithSize {
524            chunk: Chunk::Fill([123, 123, 123, 123]),
525            expanded_size: 12300,
526        };
527        assert_eq!(expected_chunk, chunk);
528    }
529
530    #[test]
531    fn parse_crc32() {
532        let chunk_raw = ChunkHeader {
533            chunk_type: CHUNK_TYPE_CRC32.into(),
534            reserved1: 0,
535            chunk_sz: 0.into(),
536            total_sz: (CHUNK_SIZE as u32 + 4).into(),
537        };
538        let header_bytes = chunk_raw.as_bytes();
539        let mut chunk_bytes: Vec<u8> = Vec::new();
540        chunk_bytes.extend_from_slice(header_bytes);
541        chunk_bytes.extend_from_slice(&[123u8; 4]);
542        let mut chunk_cursor = Cursor::new(chunk_bytes);
543        let chunk = parse_chunk(&mut chunk_cursor, 123).expect("Failed to parse");
544        assert_eq!(None, chunk);
545    }
546
547    fn test_image(chunks: Vec<ChunkWithSize>) -> AndroidSparse {
548        let file = tempfile::tempfile().expect("failed to create tempfile");
549        let size = chunks.iter().map(|x| x.expanded_size).sum();
550        AndroidSparse::from_parts(file, size, chunks).expect("Could not create image")
551    }
552
553    #[test]
554    fn read_dontcare() {
555        let chunks = vec![ChunkWithSize {
556            chunk: Chunk::DontCare,
557            expanded_size: 100,
558        }];
559        let image = test_image(chunks);
560        let mut input_memory = [55u8; 100];
561        image
562            .read_exact_at_volatile(VolatileSlice::new(&mut input_memory[..]), 0)
563            .expect("Could not read");
564        let expected = [0u8; 100];
565        assert_eq!(&expected[..], &input_memory[..]);
566    }
567
568    #[test]
569    fn read_fill_simple() {
570        let chunks = vec![ChunkWithSize {
571            chunk: Chunk::Fill([10, 20, 10, 20]),
572            expanded_size: 8,
573        }];
574        let image = test_image(chunks);
575        let mut input_memory = [55u8; 8];
576        image
577            .read_exact_at_volatile(VolatileSlice::new(&mut input_memory[..]), 0)
578            .expect("Could not read");
579        let expected = [10, 20, 10, 20, 10, 20, 10, 20];
580        assert_eq!(&expected[..], &input_memory[..]);
581    }
582
583    #[test]
584    fn read_fill_edges() {
585        let chunks = vec![ChunkWithSize {
586            chunk: Chunk::Fill([10, 20, 30, 40]),
587            expanded_size: 8,
588        }];
589        let image = test_image(chunks);
590        let mut input_memory = [55u8; 6];
591        image
592            .read_exact_at_volatile(VolatileSlice::new(&mut input_memory[..]), 1)
593            .expect("Could not read");
594        let expected = [20, 30, 40, 10, 20, 30];
595        assert_eq!(&expected[..], &input_memory[..]);
596    }
597
598    #[test]
599    fn read_fill_offset_edges() {
600        let chunks = vec![
601            ChunkWithSize {
602                chunk: Chunk::DontCare,
603                expanded_size: 20,
604            },
605            ChunkWithSize {
606                chunk: Chunk::Fill([10, 20, 30, 40]),
607                expanded_size: 100,
608            },
609        ];
610        let image = test_image(chunks);
611        let mut input_memory = [55u8; 7];
612        image
613            .read_exact_at_volatile(VolatileSlice::new(&mut input_memory[..]), 39)
614            .expect("Could not read");
615        let expected = [40, 10, 20, 30, 40, 10, 20];
616        assert_eq!(&expected[..], &input_memory[..]);
617    }
618
619    #[test]
620    fn read_raw() {
621        let chunks = vec![ChunkWithSize {
622            chunk: Chunk::Raw(0),
623            expanded_size: 100,
624        }];
625        let mut image = test_image(chunks);
626        write!(image.file, "hello").expect("Failed to write into internal file");
627        let mut input_memory = [55u8; 5];
628        image
629            .read_exact_at_volatile(VolatileSlice::new(&mut input_memory[..]), 0)
630            .expect("Could not read");
631        let expected = [104, 101, 108, 108, 111];
632        assert_eq!(&expected[..], &input_memory[..]);
633    }
634
635    #[test]
636    fn read_two_fills() {
637        let chunks = vec![
638            ChunkWithSize {
639                chunk: Chunk::Fill([10, 20, 10, 20]),
640                expanded_size: 4,
641            },
642            ChunkWithSize {
643                chunk: Chunk::Fill([30, 40, 30, 40]),
644                expanded_size: 4,
645            },
646        ];
647        let image = test_image(chunks);
648        let mut input_memory = [55u8; 8];
649        image
650            .read_exact_at_volatile(VolatileSlice::new(&mut input_memory[..]), 0)
651            .expect("Could not read");
652        let expected = [10, 20, 10, 20, 30, 40, 30, 40];
653        assert_eq!(&expected[..], &input_memory[..]);
654    }
655
656    /**
657     * Tests for Async.
658     */
659    use cros_async::MemRegion;
660    use cros_async::MemRegionIter;
661    use vm_memory::GuestAddress;
662    use vm_memory::GuestMemory;
663
664    fn test_async_image(
665        chunks: Vec<ChunkWithSize>,
666        ex: &Executor,
667    ) -> DiskResult<Box<dyn AsyncDisk>> {
668        Box::new(test_image(chunks)).to_async_disk(ex)
669    }
670
671    /// Reads `len` bytes of data from `image` at 'offset'.
672    async fn read_exact_at(image: &dyn AsyncDisk, offset: usize, len: usize) -> Vec<u8> {
673        let guest_mem = Arc::new(GuestMemory::new(&[(GuestAddress(0), 4096)]).unwrap());
674        // Fill in guest_mem with dirty data.
675        guest_mem
676            .write_all_at_addr(&vec![55u8; len], GuestAddress(0))
677            .unwrap();
678
679        let mut count = 0usize;
680        while count < len {
681            let result = image
682                .read_to_mem(
683                    (offset + count) as u64,
684                    guest_mem.clone(),
685                    MemRegionIter::new(&[MemRegion {
686                        offset: count as u64,
687                        len: len - count,
688                    }]),
689                    Default::default(),
690                )
691                .await;
692            count += result.unwrap();
693        }
694
695        let mut buf = vec![0; len];
696        guest_mem.read_at_addr(&mut buf, GuestAddress(0)).unwrap();
697        buf
698    }
699
700    #[test]
701    fn async_read_dontcare() {
702        let ex = Executor::new().unwrap();
703        ex.run_until(async {
704            let chunks = vec![ChunkWithSize {
705                chunk: Chunk::DontCare,
706                expanded_size: 100,
707            }];
708            let image = test_async_image(chunks, &ex).unwrap();
709            let buf = read_exact_at(&*image, 0, 100).await;
710            assert!(buf.iter().all(|x| *x == 0));
711        })
712        .unwrap();
713    }
714
715    #[test]
716    fn async_read_dontcare_with_offsets() {
717        let ex = Executor::new().unwrap();
718        ex.run_until(async {
719            let chunks = vec![ChunkWithSize {
720                chunk: Chunk::DontCare,
721                expanded_size: 10,
722            }];
723            let image = test_async_image(chunks, &ex).unwrap();
724            // Prepare guest_mem with dirty data.
725            let guest_mem = Arc::new(GuestMemory::new(&[(GuestAddress(0), 4096)]).unwrap());
726            guest_mem
727                .write_all_at_addr(&[55u8; 20], GuestAddress(0))
728                .unwrap();
729
730            // Pass multiple `MemRegion` to `read_to_mem`.
731            image
732                .read_to_mem(
733                    0,
734                    guest_mem.clone(),
735                    MemRegionIter::new(&[
736                        MemRegion { offset: 1, len: 3 },
737                        MemRegion { offset: 6, len: 2 },
738                    ]),
739                    Default::default(),
740                )
741                .await
742                .unwrap();
743            let mut buf = vec![0; 10];
744            guest_mem.read_at_addr(&mut buf, GuestAddress(0)).unwrap();
745            let expected = [55, 0, 0, 0, 55, 55, 0, 0, 55, 55];
746            assert_eq!(expected[..], buf[..]);
747        })
748        .unwrap();
749    }
750
751    #[test]
752    fn async_read_fill_simple() {
753        let ex = Executor::new().unwrap();
754        ex.run_until(async {
755            let chunks = vec![ChunkWithSize {
756                chunk: Chunk::Fill([10, 20, 10, 20]),
757                expanded_size: 8,
758            }];
759            let image = test_async_image(chunks, &ex).unwrap();
760            let buf = read_exact_at(&*image, 0, 8).await;
761            let expected = [10, 20, 10, 20, 10, 20, 10, 20];
762            assert_eq!(expected[..], buf[..]);
763        })
764        .unwrap();
765    }
766
767    #[test]
768    fn async_read_fill_simple_with_offset() {
769        let ex = Executor::new().unwrap();
770        ex.run_until(async {
771            let chunks = vec![ChunkWithSize {
772                chunk: Chunk::Fill([10, 20, 10, 20]),
773                expanded_size: 8,
774            }];
775            let image = test_async_image(chunks, &ex).unwrap();
776            // Prepare guest_mem with dirty data.
777            let guest_mem = Arc::new(GuestMemory::new(&[(GuestAddress(0), 4096)]).unwrap());
778            guest_mem
779                .write_all_at_addr(&[55u8; 20], GuestAddress(0))
780                .unwrap();
781
782            // Pass multiple `MemRegion` to `read_to_mem`.
783            image
784                .read_to_mem(
785                    0,
786                    guest_mem.clone(),
787                    MemRegionIter::new(&[
788                        MemRegion { offset: 1, len: 3 },
789                        MemRegion { offset: 6, len: 2 },
790                    ]),
791                    Default::default(),
792                )
793                .await
794                .unwrap();
795            let mut buf = vec![0; 10];
796            guest_mem.read_at_addr(&mut buf, GuestAddress(0)).unwrap();
797            let expected = [55, 10, 20, 10, 55, 55, 20, 10, 55, 55];
798            assert_eq!(expected[..], buf[..]);
799        })
800        .unwrap();
801    }
802
803    #[test]
804    fn async_read_fill_edges() {
805        let ex = Executor::new().unwrap();
806        ex.run_until(async {
807            let chunks = vec![ChunkWithSize {
808                chunk: Chunk::Fill([10, 20, 30, 40]),
809                expanded_size: 8,
810            }];
811            let image = test_async_image(chunks, &ex).unwrap();
812            let buf = read_exact_at(&*image, 1, 6).await;
813            let expected = [20, 30, 40, 10, 20, 30];
814            assert_eq!(expected[..], buf[..]);
815        })
816        .unwrap();
817    }
818
819    #[test]
820    fn async_read_fill_offset_edges() {
821        let ex = Executor::new().unwrap();
822        ex.run_until(async {
823            let chunks = vec![
824                ChunkWithSize {
825                    chunk: Chunk::DontCare,
826                    expanded_size: 20,
827                },
828                ChunkWithSize {
829                    chunk: Chunk::Fill([10, 20, 30, 40]),
830                    expanded_size: 100,
831                },
832            ];
833            let image = test_async_image(chunks, &ex).unwrap();
834            let buf = read_exact_at(&*image, 39, 7).await;
835            let expected = [40, 10, 20, 30, 40, 10, 20];
836            assert_eq!(expected[..], buf[..]);
837        })
838        .unwrap();
839    }
840
841    #[test]
842    fn async_read_raw() {
843        let ex = Executor::new().unwrap();
844        ex.run_until(async {
845            let chunks = vec![ChunkWithSize {
846                chunk: Chunk::Raw(0),
847                expanded_size: 100,
848            }];
849            let mut image = Box::new(test_image(chunks));
850            write!(image.file, "hello").unwrap();
851            let async_image = image.to_async_disk(&ex).unwrap();
852            let buf = read_exact_at(&*async_image, 0, 5).await;
853            let expected = [104, 101, 108, 108, 111];
854            assert_eq!(&expected[..], &buf[..]);
855        })
856        .unwrap();
857    }
858
859    #[test]
860    fn async_read_fill_raw_with_offset() {
861        let ex = Executor::new().unwrap();
862        ex.run_until(async {
863            let chunks = vec![ChunkWithSize {
864                chunk: Chunk::Raw(0),
865                expanded_size: 100,
866            }];
867            let mut image = Box::new(test_image(chunks));
868            write!(image.file, "hello").unwrap();
869            let async_image = image.to_async_disk(&ex).unwrap();
870            // Prepare guest_mem with dirty data.
871            let guest_mem = Arc::new(GuestMemory::new(&[(GuestAddress(0), 4096)]).unwrap());
872            guest_mem
873                .write_all_at_addr(&[55u8; 20], GuestAddress(0))
874                .unwrap();
875
876            // Pass multiple `MemRegion` to `read_to_mem`.
877            async_image
878                .read_to_mem(
879                    0,
880                    guest_mem.clone(),
881                    MemRegionIter::new(&[
882                        MemRegion { offset: 1, len: 3 },
883                        MemRegion { offset: 6, len: 2 },
884                    ]),
885                    Default::default(),
886                )
887                .await
888                .unwrap();
889            let mut buf = vec![0; 10];
890            guest_mem.read_at_addr(&mut buf, GuestAddress(0)).unwrap();
891            let expected = [55, 104, 101, 108, 55, 55, 108, 111, 55, 55];
892            assert_eq!(expected[..], buf[..]);
893        })
894        .unwrap();
895    }
896
897    #[test]
898    fn async_read_two_fills() {
899        let ex = Executor::new().unwrap();
900        ex.run_until(async {
901            let chunks = vec![
902                ChunkWithSize {
903                    chunk: Chunk::Fill([10, 20, 10, 20]),
904                    expanded_size: 4,
905                },
906                ChunkWithSize {
907                    chunk: Chunk::Fill([30, 40, 30, 40]),
908                    expanded_size: 4,
909                },
910            ];
911            let image = test_async_image(chunks, &ex).unwrap();
912            let buf = read_exact_at(&*image, 0, 8).await;
913            let expected = [10, 20, 10, 20, 30, 40, 30, 40];
914            assert_eq!(&expected[..], &buf[..]);
915        })
916        .unwrap();
917    }
918}