1 // Copyright 2022 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 use std::fs::File;
6 use std::io::Read;
7 use std::io::Seek;
8 use std::io::SeekFrom;
9
10 use cros_async::Executor;
11
12 use crate::Error;
13 use crate::Result;
14 use crate::SingleFileDisk;
15
apply_raw_disk_file_options(_raw_image: &File, _is_sparse_file: bool) -> Result<()>16 pub fn apply_raw_disk_file_options(_raw_image: &File, _is_sparse_file: bool) -> Result<()> {
17 // No op on unix.
18 Ok(())
19 }
20
read_from_disk( mut file: &File, offset: u64, buf: &mut [u8], _overlapped_mode: bool, ) -> Result<()>21 pub fn read_from_disk(
22 mut file: &File,
23 offset: u64,
24 buf: &mut [u8],
25 _overlapped_mode: bool,
26 ) -> Result<()> {
27 file.seek(SeekFrom::Start(offset))
28 .map_err(Error::SeekingFile)?;
29 file.read_exact(buf).map_err(Error::ReadingHeader)
30 }
31
32 impl SingleFileDisk {
new(disk: File, ex: &Executor) -> Result<Self>33 pub fn new(disk: File, ex: &Executor) -> Result<Self> {
34 let is_block_device_file =
35 base::linux::is_block_file(&disk).map_err(Error::BlockDeviceNew)?;
36 ex.async_from(disk)
37 .map_err(Error::CreateSingleFileDisk)
38 .map(|inner| SingleFileDisk {
39 inner,
40 is_block_device_file,
41 })
42 }
43 }
44
45 #[cfg(test)]
46 mod tests {
47 use std::fs::File;
48 use std::fs::OpenOptions;
49 use std::io::Write;
50
51 use base::pagesize;
52 use cros_async::Executor;
53 use cros_async::MemRegion;
54 use vm_memory::GuestAddress;
55 use vm_memory::GuestMemory;
56
57 use crate::*;
58
59 #[test]
read_async()60 fn read_async() {
61 async fn read_zeros_async(ex: &Executor) {
62 let guest_mem =
63 Arc::new(GuestMemory::new(&[(GuestAddress(0), pagesize() as u64)]).unwrap());
64 let f = File::open("/dev/zero").unwrap();
65 let async_file = SingleFileDisk::new(f, ex).unwrap();
66 let result = async_file
67 .read_to_mem(
68 0,
69 guest_mem,
70 MemRegionIter::new(&[MemRegion { offset: 0, len: 48 }]),
71 )
72 .await;
73 assert_eq!(48, result.unwrap());
74 }
75
76 let ex = Executor::new().unwrap();
77 ex.run_until(read_zeros_async(&ex)).unwrap();
78 }
79
80 #[test]
write_async()81 fn write_async() {
82 async fn write_zeros_async(ex: &Executor) {
83 let guest_mem =
84 Arc::new(GuestMemory::new(&[(GuestAddress(0), pagesize() as u64)]).unwrap());
85 let f = OpenOptions::new().write(true).open("/dev/null").unwrap();
86 let async_file = SingleFileDisk::new(f, ex).unwrap();
87 let result = async_file
88 .write_from_mem(
89 0,
90 guest_mem,
91 MemRegionIter::new(&[MemRegion { offset: 0, len: 48 }]),
92 )
93 .await;
94 assert_eq!(48, result.unwrap());
95 }
96
97 let ex = Executor::new().unwrap();
98 ex.run_until(write_zeros_async(&ex)).unwrap();
99 }
100
101 #[test]
detect_image_type_raw()102 fn detect_image_type_raw() {
103 let mut t = tempfile::tempfile().unwrap();
104 // Fill the first block of the file with "random" data.
105 let buf = "ABCD".as_bytes().repeat(1024);
106 t.write_all(&buf).unwrap();
107 let image_type = detect_image_type(&t, false).expect("failed to detect image type");
108 assert_eq!(image_type, ImageType::Raw);
109 }
110
111 #[test]
112 #[cfg(feature = "qcow")]
detect_image_type_qcow2()113 fn detect_image_type_qcow2() {
114 let mut t = tempfile::tempfile().unwrap();
115 // Write the qcow2 magic signature. The rest of the header is not filled in, so if
116 // detect_image_type is ever updated to validate more of the header, this test would need
117 // to be updated.
118 let buf: &[u8] = &[0x51, 0x46, 0x49, 0xfb];
119 t.write_all(buf).unwrap();
120 let image_type = detect_image_type(&t, false).expect("failed to detect image type");
121 assert_eq!(image_type, ImageType::Qcow2);
122 }
123
124 #[test]
125 #[cfg(feature = "android-sparse")]
detect_image_type_android_sparse()126 fn detect_image_type_android_sparse() {
127 let mut t = tempfile::tempfile().unwrap();
128 // Write the Android sparse magic signature. The rest of the header is not filled in, so if
129 // detect_image_type is ever updated to validate more of the header, this test would need
130 // to be updated.
131 let buf: &[u8] = &[0x3a, 0xff, 0x26, 0xed];
132 t.write_all(buf).unwrap();
133 let image_type = detect_image_type(&t, false).expect("failed to detect image type");
134 assert_eq!(image_type, ImageType::AndroidSparse);
135 }
136
137 #[test]
138 #[cfg(feature = "composite-disk")]
detect_image_type_composite()139 fn detect_image_type_composite() {
140 let mut t = tempfile::tempfile().unwrap();
141 // Write the composite disk magic signature. The rest of the header is not filled in, so if
142 // detect_image_type is ever updated to validate more of the header, this test would need
143 // to be updated.
144 let buf = "composite_disk\x1d".as_bytes();
145 t.write_all(buf).unwrap();
146 let image_type = detect_image_type(&t, false).expect("failed to detect image type");
147 assert_eq!(image_type, ImageType::CompositeDisk);
148 }
149
150 #[test]
detect_image_type_small_file()151 fn detect_image_type_small_file() {
152 let mut t = tempfile::tempfile().unwrap();
153 // Write a file smaller than the four-byte qcow2/sparse magic to ensure the small file logic
154 // works correctly and handles it as a raw file.
155 let buf: &[u8] = &[0xAA, 0xBB];
156 t.write_all(buf).unwrap();
157 let image_type = detect_image_type(&t, false).expect("failed to detect image type");
158 assert_eq!(image_type, ImageType::Raw);
159 }
160 }
161