xref: /aosp_15_r20/external/crosvm/disk/src/sys/linux.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2022 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker use std::fs::File;
6*bb4ee6a4SAndroid Build Coastguard Worker use std::io::Read;
7*bb4ee6a4SAndroid Build Coastguard Worker use std::io::Seek;
8*bb4ee6a4SAndroid Build Coastguard Worker use std::io::SeekFrom;
9*bb4ee6a4SAndroid Build Coastguard Worker use std::os::fd::AsRawFd;
10*bb4ee6a4SAndroid Build Coastguard Worker 
11*bb4ee6a4SAndroid Build Coastguard Worker use cros_async::Executor;
12*bb4ee6a4SAndroid Build Coastguard Worker 
13*bb4ee6a4SAndroid Build Coastguard Worker use crate::DiskFileParams;
14*bb4ee6a4SAndroid Build Coastguard Worker use crate::Error;
15*bb4ee6a4SAndroid Build Coastguard Worker use crate::Result;
16*bb4ee6a4SAndroid Build Coastguard Worker use crate::SingleFileDisk;
17*bb4ee6a4SAndroid Build Coastguard Worker 
open_raw_disk_image(params: &DiskFileParams) -> Result<File>18*bb4ee6a4SAndroid Build Coastguard Worker pub fn open_raw_disk_image(params: &DiskFileParams) -> Result<File> {
19*bb4ee6a4SAndroid Build Coastguard Worker     let mut options = File::options();
20*bb4ee6a4SAndroid Build Coastguard Worker     options.read(true).write(!params.is_read_only);
21*bb4ee6a4SAndroid Build Coastguard Worker 
22*bb4ee6a4SAndroid Build Coastguard Worker     let raw_image = base::open_file_or_duplicate(&params.path, &options)
23*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(|e| Error::OpenFile(params.path.display().to_string(), e))?;
24*bb4ee6a4SAndroid Build Coastguard Worker 
25*bb4ee6a4SAndroid Build Coastguard Worker     if params.lock {
26*bb4ee6a4SAndroid Build Coastguard Worker         // Lock the disk image to prevent other crosvm instances from using it.
27*bb4ee6a4SAndroid Build Coastguard Worker         let lock_op = if params.is_read_only {
28*bb4ee6a4SAndroid Build Coastguard Worker             base::FlockOperation::LockShared
29*bb4ee6a4SAndroid Build Coastguard Worker         } else {
30*bb4ee6a4SAndroid Build Coastguard Worker             base::FlockOperation::LockExclusive
31*bb4ee6a4SAndroid Build Coastguard Worker         };
32*bb4ee6a4SAndroid Build Coastguard Worker         base::flock(&raw_image, lock_op, true).map_err(Error::LockFileFailure)?;
33*bb4ee6a4SAndroid Build Coastguard Worker     }
34*bb4ee6a4SAndroid Build Coastguard Worker 
35*bb4ee6a4SAndroid Build Coastguard Worker     // If O_DIRECT is requested, set the flag via fcntl. It is not done at
36*bb4ee6a4SAndroid Build Coastguard Worker     // open_file_or_reuse time because it will reuse existing fd and will
37*bb4ee6a4SAndroid Build Coastguard Worker     // not actually use the given OpenOptions.
38*bb4ee6a4SAndroid Build Coastguard Worker     if params.is_direct {
39*bb4ee6a4SAndroid Build Coastguard Worker         base::add_fd_flags(raw_image.as_raw_fd(), libc::O_DIRECT).map_err(Error::DirectFailed)?;
40*bb4ee6a4SAndroid Build Coastguard Worker     }
41*bb4ee6a4SAndroid Build Coastguard Worker 
42*bb4ee6a4SAndroid Build Coastguard Worker     Ok(raw_image)
43*bb4ee6a4SAndroid Build Coastguard Worker }
44*bb4ee6a4SAndroid Build Coastguard Worker 
apply_raw_disk_file_options(_raw_image: &File, _is_sparse_file: bool) -> Result<()>45*bb4ee6a4SAndroid Build Coastguard Worker pub fn apply_raw_disk_file_options(_raw_image: &File, _is_sparse_file: bool) -> Result<()> {
46*bb4ee6a4SAndroid Build Coastguard Worker     // No op on unix.
47*bb4ee6a4SAndroid Build Coastguard Worker     Ok(())
48*bb4ee6a4SAndroid Build Coastguard Worker }
49*bb4ee6a4SAndroid Build Coastguard Worker 
read_from_disk( mut file: &File, offset: u64, buf: &mut [u8], _overlapped_mode: bool, ) -> Result<()>50*bb4ee6a4SAndroid Build Coastguard Worker pub fn read_from_disk(
51*bb4ee6a4SAndroid Build Coastguard Worker     mut file: &File,
52*bb4ee6a4SAndroid Build Coastguard Worker     offset: u64,
53*bb4ee6a4SAndroid Build Coastguard Worker     buf: &mut [u8],
54*bb4ee6a4SAndroid Build Coastguard Worker     _overlapped_mode: bool,
55*bb4ee6a4SAndroid Build Coastguard Worker ) -> Result<()> {
56*bb4ee6a4SAndroid Build Coastguard Worker     file.seek(SeekFrom::Start(offset))
57*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(Error::SeekingFile)?;
58*bb4ee6a4SAndroid Build Coastguard Worker     file.read_exact(buf).map_err(Error::ReadingHeader)
59*bb4ee6a4SAndroid Build Coastguard Worker }
60*bb4ee6a4SAndroid Build Coastguard Worker 
61*bb4ee6a4SAndroid Build Coastguard Worker impl SingleFileDisk {
new(disk: File, ex: &Executor) -> Result<Self>62*bb4ee6a4SAndroid Build Coastguard Worker     pub fn new(disk: File, ex: &Executor) -> Result<Self> {
63*bb4ee6a4SAndroid Build Coastguard Worker         let is_block_device_file =
64*bb4ee6a4SAndroid Build Coastguard Worker             base::linux::is_block_file(&disk).map_err(Error::BlockDeviceNew)?;
65*bb4ee6a4SAndroid Build Coastguard Worker         ex.async_from(disk)
66*bb4ee6a4SAndroid Build Coastguard Worker             .map_err(Error::CreateSingleFileDisk)
67*bb4ee6a4SAndroid Build Coastguard Worker             .map(|inner| SingleFileDisk {
68*bb4ee6a4SAndroid Build Coastguard Worker                 inner,
69*bb4ee6a4SAndroid Build Coastguard Worker                 is_block_device_file,
70*bb4ee6a4SAndroid Build Coastguard Worker             })
71*bb4ee6a4SAndroid Build Coastguard Worker     }
72*bb4ee6a4SAndroid Build Coastguard Worker }
73*bb4ee6a4SAndroid Build Coastguard Worker 
74*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)]
75*bb4ee6a4SAndroid Build Coastguard Worker mod tests {
76*bb4ee6a4SAndroid Build Coastguard Worker     use std::fs::File;
77*bb4ee6a4SAndroid Build Coastguard Worker     use std::fs::OpenOptions;
78*bb4ee6a4SAndroid Build Coastguard Worker     use std::io::Write;
79*bb4ee6a4SAndroid Build Coastguard Worker 
80*bb4ee6a4SAndroid Build Coastguard Worker     use base::pagesize;
81*bb4ee6a4SAndroid Build Coastguard Worker     use cros_async::Executor;
82*bb4ee6a4SAndroid Build Coastguard Worker     use cros_async::MemRegion;
83*bb4ee6a4SAndroid Build Coastguard Worker     use vm_memory::GuestAddress;
84*bb4ee6a4SAndroid Build Coastguard Worker     use vm_memory::GuestMemory;
85*bb4ee6a4SAndroid Build Coastguard Worker 
86*bb4ee6a4SAndroid Build Coastguard Worker     use crate::*;
87*bb4ee6a4SAndroid Build Coastguard Worker 
88*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
read_async()89*bb4ee6a4SAndroid Build Coastguard Worker     fn read_async() {
90*bb4ee6a4SAndroid Build Coastguard Worker         async fn read_zeros_async(ex: &Executor) {
91*bb4ee6a4SAndroid Build Coastguard Worker             let guest_mem =
92*bb4ee6a4SAndroid Build Coastguard Worker                 Arc::new(GuestMemory::new(&[(GuestAddress(0), pagesize() as u64)]).unwrap());
93*bb4ee6a4SAndroid Build Coastguard Worker             let f = File::open("/dev/zero").unwrap();
94*bb4ee6a4SAndroid Build Coastguard Worker             let async_file = SingleFileDisk::new(f, ex).unwrap();
95*bb4ee6a4SAndroid Build Coastguard Worker             let result = async_file
96*bb4ee6a4SAndroid Build Coastguard Worker                 .read_to_mem(
97*bb4ee6a4SAndroid Build Coastguard Worker                     0,
98*bb4ee6a4SAndroid Build Coastguard Worker                     guest_mem,
99*bb4ee6a4SAndroid Build Coastguard Worker                     MemRegionIter::new(&[MemRegion { offset: 0, len: 48 }]),
100*bb4ee6a4SAndroid Build Coastguard Worker                 )
101*bb4ee6a4SAndroid Build Coastguard Worker                 .await;
102*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(48, result.unwrap());
103*bb4ee6a4SAndroid Build Coastguard Worker         }
104*bb4ee6a4SAndroid Build Coastguard Worker 
105*bb4ee6a4SAndroid Build Coastguard Worker         let ex = Executor::new().unwrap();
106*bb4ee6a4SAndroid Build Coastguard Worker         ex.run_until(read_zeros_async(&ex)).unwrap();
107*bb4ee6a4SAndroid Build Coastguard Worker     }
108*bb4ee6a4SAndroid Build Coastguard Worker 
109*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
write_async()110*bb4ee6a4SAndroid Build Coastguard Worker     fn write_async() {
111*bb4ee6a4SAndroid Build Coastguard Worker         async fn write_zeros_async(ex: &Executor) {
112*bb4ee6a4SAndroid Build Coastguard Worker             let guest_mem =
113*bb4ee6a4SAndroid Build Coastguard Worker                 Arc::new(GuestMemory::new(&[(GuestAddress(0), pagesize() as u64)]).unwrap());
114*bb4ee6a4SAndroid Build Coastguard Worker             let f = OpenOptions::new().write(true).open("/dev/null").unwrap();
115*bb4ee6a4SAndroid Build Coastguard Worker             let async_file = SingleFileDisk::new(f, ex).unwrap();
116*bb4ee6a4SAndroid Build Coastguard Worker             let result = async_file
117*bb4ee6a4SAndroid Build Coastguard Worker                 .write_from_mem(
118*bb4ee6a4SAndroid Build Coastguard Worker                     0,
119*bb4ee6a4SAndroid Build Coastguard Worker                     guest_mem,
120*bb4ee6a4SAndroid Build Coastguard Worker                     MemRegionIter::new(&[MemRegion { offset: 0, len: 48 }]),
121*bb4ee6a4SAndroid Build Coastguard Worker                 )
122*bb4ee6a4SAndroid Build Coastguard Worker                 .await;
123*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(48, result.unwrap());
124*bb4ee6a4SAndroid Build Coastguard Worker         }
125*bb4ee6a4SAndroid Build Coastguard Worker 
126*bb4ee6a4SAndroid Build Coastguard Worker         let ex = Executor::new().unwrap();
127*bb4ee6a4SAndroid Build Coastguard Worker         ex.run_until(write_zeros_async(&ex)).unwrap();
128*bb4ee6a4SAndroid Build Coastguard Worker     }
129*bb4ee6a4SAndroid Build Coastguard Worker 
130*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
detect_image_type_raw()131*bb4ee6a4SAndroid Build Coastguard Worker     fn detect_image_type_raw() {
132*bb4ee6a4SAndroid Build Coastguard Worker         let mut t = tempfile::tempfile().unwrap();
133*bb4ee6a4SAndroid Build Coastguard Worker         // Fill the first block of the file with "random" data.
134*bb4ee6a4SAndroid Build Coastguard Worker         let buf = "ABCD".as_bytes().repeat(1024);
135*bb4ee6a4SAndroid Build Coastguard Worker         t.write_all(&buf).unwrap();
136*bb4ee6a4SAndroid Build Coastguard Worker         let image_type = detect_image_type(&t, false).expect("failed to detect image type");
137*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(image_type, ImageType::Raw);
138*bb4ee6a4SAndroid Build Coastguard Worker     }
139*bb4ee6a4SAndroid Build Coastguard Worker 
140*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
141*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(feature = "qcow")]
detect_image_type_qcow2()142*bb4ee6a4SAndroid Build Coastguard Worker     fn detect_image_type_qcow2() {
143*bb4ee6a4SAndroid Build Coastguard Worker         let mut t = tempfile::tempfile().unwrap();
144*bb4ee6a4SAndroid Build Coastguard Worker         // Write the qcow2 magic signature. The rest of the header is not filled in, so if
145*bb4ee6a4SAndroid Build Coastguard Worker         // detect_image_type is ever updated to validate more of the header, this test would need
146*bb4ee6a4SAndroid Build Coastguard Worker         // to be updated.
147*bb4ee6a4SAndroid Build Coastguard Worker         let buf: &[u8] = &[0x51, 0x46, 0x49, 0xfb];
148*bb4ee6a4SAndroid Build Coastguard Worker         t.write_all(buf).unwrap();
149*bb4ee6a4SAndroid Build Coastguard Worker         let image_type = detect_image_type(&t, false).expect("failed to detect image type");
150*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(image_type, ImageType::Qcow2);
151*bb4ee6a4SAndroid Build Coastguard Worker     }
152*bb4ee6a4SAndroid Build Coastguard Worker 
153*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
154*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(feature = "android-sparse")]
detect_image_type_android_sparse()155*bb4ee6a4SAndroid Build Coastguard Worker     fn detect_image_type_android_sparse() {
156*bb4ee6a4SAndroid Build Coastguard Worker         let mut t = tempfile::tempfile().unwrap();
157*bb4ee6a4SAndroid Build Coastguard Worker         // Write the Android sparse magic signature. The rest of the header is not filled in, so if
158*bb4ee6a4SAndroid Build Coastguard Worker         // detect_image_type is ever updated to validate more of the header, this test would need
159*bb4ee6a4SAndroid Build Coastguard Worker         // to be updated.
160*bb4ee6a4SAndroid Build Coastguard Worker         let buf: &[u8] = &[0x3a, 0xff, 0x26, 0xed];
161*bb4ee6a4SAndroid Build Coastguard Worker         t.write_all(buf).unwrap();
162*bb4ee6a4SAndroid Build Coastguard Worker         let image_type = detect_image_type(&t, false).expect("failed to detect image type");
163*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(image_type, ImageType::AndroidSparse);
164*bb4ee6a4SAndroid Build Coastguard Worker     }
165*bb4ee6a4SAndroid Build Coastguard Worker 
166*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
167*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(feature = "composite-disk")]
detect_image_type_composite()168*bb4ee6a4SAndroid Build Coastguard Worker     fn detect_image_type_composite() {
169*bb4ee6a4SAndroid Build Coastguard Worker         let mut t = tempfile::tempfile().unwrap();
170*bb4ee6a4SAndroid Build Coastguard Worker         // Write the composite disk magic signature. The rest of the header is not filled in, so if
171*bb4ee6a4SAndroid Build Coastguard Worker         // detect_image_type is ever updated to validate more of the header, this test would need
172*bb4ee6a4SAndroid Build Coastguard Worker         // to be updated.
173*bb4ee6a4SAndroid Build Coastguard Worker         let buf = "composite_disk\x1d".as_bytes();
174*bb4ee6a4SAndroid Build Coastguard Worker         t.write_all(buf).unwrap();
175*bb4ee6a4SAndroid Build Coastguard Worker         let image_type = detect_image_type(&t, false).expect("failed to detect image type");
176*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(image_type, ImageType::CompositeDisk);
177*bb4ee6a4SAndroid Build Coastguard Worker     }
178*bb4ee6a4SAndroid Build Coastguard Worker 
179*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
detect_image_type_small_file()180*bb4ee6a4SAndroid Build Coastguard Worker     fn detect_image_type_small_file() {
181*bb4ee6a4SAndroid Build Coastguard Worker         let mut t = tempfile::tempfile().unwrap();
182*bb4ee6a4SAndroid Build Coastguard Worker         // Write a file smaller than the four-byte qcow2/sparse magic to ensure the small file logic
183*bb4ee6a4SAndroid Build Coastguard Worker         // works correctly and handles it as a raw file.
184*bb4ee6a4SAndroid Build Coastguard Worker         let buf: &[u8] = &[0xAA, 0xBB];
185*bb4ee6a4SAndroid Build Coastguard Worker         t.write_all(buf).unwrap();
186*bb4ee6a4SAndroid Build Coastguard Worker         let image_type = detect_image_type(&t, false).expect("failed to detect image type");
187*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(image_type, ImageType::Raw);
188*bb4ee6a4SAndroid Build Coastguard Worker     }
189*bb4ee6a4SAndroid Build Coastguard Worker }
190