disk/sys/
linux.rs

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