1use 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, major_version: Le16, minor_version: Le16, file_hdr_sz: Le16, chunk_hdr_size: Le16, blk_sz: Le32, total_blks: Le32, total_chunks: Le32, 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, reserved1: u16,
87 chunk_sz: Le32, total_sz: Le32, }
90
91#[derive(Clone, Debug, PartialEq, Eq)]
92enum Chunk {
93 Raw(u64), 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#[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), 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 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
257impl 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
312impl DiskFile for AndroidSparse {}
314
315pub 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 Ok(())
361 }
362
363 async fn fsync(&self) -> DiskResult<()> {
364 Ok(())
366 }
367
368 async fn fdatasync(&self) -> DiskResult<()> {
369 Ok(())
371 }
372
373 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 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 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 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 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 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 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 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 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 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 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}