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