Skip to content

Commit fd6203f

Browse files
committed
Add functions to determine disk image format
Adds the `detect_image_type` function in the new `disk`module by checking the header. It currently only supports checking the header for Raw and Qcow2 files. Signed-off-by: Jake Correnti <[email protected]>
1 parent 20756c0 commit fd6203f

File tree

3 files changed

+91
-1
lines changed

3 files changed

+91
-1
lines changed

src/devices/src/virtio/block/device.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use super::{
3333
};
3434

3535
use crate::legacy::Gic;
36-
use crate::virtio::ActivateError;
36+
use crate::virtio::{block::disk, ActivateError};
3737

3838
/// Configuration options for disk caching.
3939
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
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+
use std::fs::File;
6+
use std::io::{self, Read, Seek, SeekFrom};
7+
8+
// QCOW magic constant that starts the header.
9+
pub const QCOW_MAGIC: u32 = 0x5146_49fb;
10+
11+
#[derive(Debug)]
12+
pub enum Error {
13+
ReadingHeader(io::Error),
14+
SeekingFile(io::Error),
15+
}
16+
17+
type Result<T> = std::result::Result<T, Error>;
18+
19+
/// The variants of image files on the host that can be used as virtual disks.
20+
#[derive(Debug, PartialEq, Eq)]
21+
pub enum ImageType {
22+
Raw,
23+
Qcow2,
24+
}
25+
/// A trait for getting the length of a disk image or raw block device.
26+
pub trait DiskGetLen {
27+
/// Get the current length of the disk in bytes.
28+
fn get_len(&self) -> io::Result<u64>;
29+
}
30+
impl DiskGetLen for File {
31+
fn get_len(&self) -> io::Result<u64> {
32+
let mut s = self;
33+
let orig_seek = s.stream_position()?;
34+
let end = s.seek(SeekFrom::End(0))?;
35+
s.seek(SeekFrom::Start(orig_seek))?;
36+
Ok(end)
37+
}
38+
}
39+
/// Detect the type of an image file by checking for a valid header of the supported formats.
40+
pub fn detect_image_type(file: &File, overlapped_mode: bool) -> Result<ImageType> {
41+
let mut f = file;
42+
let disk_size = f.get_len().map_err(Error::SeekingFile)?;
43+
let orig_seek = f.stream_position().map_err(Error::SeekingFile)?;
44+
45+
// Try to read the disk in a nicely-aligned block size unless the whole file is smaller.
46+
const MAGIC_BLOCK_SIZE: usize = 4096;
47+
48+
#[repr(align(4096))]
49+
struct BlockAlignedBuffer {
50+
data: [u8; MAGIC_BLOCK_SIZE],
51+
}
52+
53+
let mut magic = BlockAlignedBuffer {
54+
data: [0u8; MAGIC_BLOCK_SIZE],
55+
};
56+
57+
let magic_read_len = if disk_size > MAGIC_BLOCK_SIZE as u64 {
58+
MAGIC_BLOCK_SIZE
59+
} else {
60+
// This cast is safe since we know disk_size is less than MAGIC_BLOCK_SIZE (4096) and
61+
// therefore is representable in usize.
62+
disk_size as usize
63+
};
64+
65+
read_from_disk(f, 0, &mut magic.data[0..magic_read_len], overlapped_mode)?;
66+
67+
f.seek(SeekFrom::Start(orig_seek))
68+
.map_err(Error::SeekingFile)?;
69+
70+
#[allow(unused_variables)] // magic4 is only used with the qcow or android-sparse features.
71+
if let Some(magic4) = magic.data.get(0..4) {
72+
if magic4 == QCOW_MAGIC.to_be_bytes() {
73+
return Ok(ImageType::Qcow2);
74+
}
75+
}
76+
77+
Ok(ImageType::Raw)
78+
}
79+
80+
pub fn read_from_disk(
81+
mut file: &File,
82+
offset: u64,
83+
buf: &mut [u8],
84+
_overlapped_mode: bool,
85+
) -> Result<()> {
86+
file.seek(SeekFrom::Start(offset))
87+
.map_err(Error::SeekingFile)?;
88+
file.read_exact(buf).map_err(Error::ReadingHeader)
89+
}

src/devices/src/virtio/block/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
pub mod device;
5+
pub mod disk;
56
mod worker;
67

78
pub use self::device::{Block, CacheType};

0 commit comments

Comments
 (0)