Skip to content

Commit c67ca9e

Browse files
committed
mmap: support for devdax and block devices
metadata.len() returns 0 when trying to mmap files such as block devices and devdax (character devices) that are not regular files, hence returning MappingPastEof even if the mapping would fit at the provided file_offset. This patch adds support for checking the size of devdax and block devices, and returns a new error, InvalidFileType, if the mmap being created is not for a regular file, block or devdax device, or if the size the devices couldn't be found in sysfs. Usecase: Devdax and block devices can be used in cloud-hypervisor as memory-zone. MmapRegion::build from vm-memory is called while creating a GuestRegionMmap for the VM memory-zone. Reviewed-by: Fam Zheng <[email protected]> Signed-off-by: Usama Arif <[email protected]>
1 parent f6ef1b6 commit c67ca9e

File tree

2 files changed

+65
-4
lines changed

2 files changed

+65
-4
lines changed

src/mmap.rs

+62-4
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,14 @@
1515
use std::borrow::Borrow;
1616
use std::error;
1717
use std::fmt;
18+
#[cfg(unix)]
19+
use std::fs::{read_link, read_to_string, File};
1820
use std::io::{Read, Write};
1921
use std::ops::Deref;
22+
#[cfg(unix)]
23+
use std::os::unix::fs::FileTypeExt;
24+
#[cfg(unix)]
25+
use std::os::unix::io::AsRawFd;
2026
use std::result;
2127
use std::sync::atomic::Ordering;
2228
use std::sync::Arc;
@@ -106,6 +112,56 @@ impl fmt::Display for Error {
106112

107113
impl error::Error for Error {}
108114

115+
#[cfg(unix)]
116+
/// Return the size of `file`.
117+
///
118+
/// For regular files this returns the files' metadata.length().
119+
/// For devdax and block devices, the size is checked in sysfs.
120+
fn calculate_file_size(file: &File) -> Option<u64> {
121+
let metadata = file.metadata().ok()?;
122+
let file_type = metadata.file_type();
123+
124+
if file_type.is_file() {
125+
return Some(metadata.len());
126+
}
127+
128+
let mut device_size_multiplier = 0;
129+
let mut sys_fs_location = "";
130+
let proc_file_name = format!("/proc/self/fd/{}", file.as_raw_fd());
131+
let file_name = read_link(proc_file_name).ok()?;
132+
133+
if file_type.is_block_device() {
134+
// block device size are reported as 512 byte blocks.
135+
device_size_multiplier = 512;
136+
sys_fs_location = "/sys/block";
137+
} else if file_type.is_char_device() {
138+
if let Some(file_name) = file_name.to_str() {
139+
if file_name.starts_with("/dev/dax") {
140+
device_size_multiplier = 1;
141+
sys_fs_location = "/sys/bus/dax/devices";
142+
} else {
143+
return None;
144+
}
145+
}
146+
} else {
147+
return None;
148+
}
149+
150+
if let Some(file_name) = file_name.to_str() {
151+
if let Some(device_name) = file_name.split('/').last() {
152+
if let Ok(device_size_str) =
153+
read_to_string(format!("{}/{}/size", sys_fs_location, device_name))
154+
{
155+
if let Ok(device_size_int) = device_size_str.trim().parse::<u64>() {
156+
return Some(device_size_int * device_size_multiplier);
157+
}
158+
}
159+
}
160+
}
161+
162+
None
163+
}
164+
109165
// TODO: use this for Windows as well after we redefine the Error type there.
110166
#[cfg(unix)]
111167
/// Checks if a mapping of `size` bytes fits at the provided `file_offset`.
@@ -119,14 +175,16 @@ pub fn check_file_offset(
119175
let file = file_offset.file();
120176
let start = file_offset.start();
121177

122-
if let Some(end) = start.checked_add(size as u64) {
123-
if let Ok(metadata) = file.metadata() {
124-
if metadata.len() < end {
178+
if let Some(file_size) = calculate_file_size(file) {
179+
if let Some(end) = start.checked_add(size as u64) {
180+
if file_size < end {
125181
return Err(MmapRegionError::MappingPastEof);
126182
}
183+
} else {
184+
return Err(MmapRegionError::InvalidOffsetLength);
127185
}
128186
} else {
129-
return Err(MmapRegionError::InvalidOffsetLength);
187+
return Err(MmapRegionError::InvalidFileType);
130188
}
131189

132190
Ok(())

src/mmap_unix.rs

+3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ pub enum Error {
2929
InvalidOffsetLength,
3030
/// The specified pointer to the mapping is not page-aligned.
3131
InvalidPointer,
32+
/// The specified file type is invalid.
33+
InvalidFileType,
3234
/// The forbidden `MAP_FIXED` flag was specified.
3335
MapFixed,
3436
/// Mappings using the same fd overlap in terms of file offset and length.
@@ -50,6 +52,7 @@ impl fmt::Display for Error {
5052
f,
5153
"The specified pointer to the mapping is not page-aligned",
5254
),
55+
Error::InvalidFileType => write!(f, "The specified file type is invalid"),
5356
Error::MapFixed => write!(f, "The forbidden `MAP_FIXED` flag was specified"),
5457
Error::MappingOverlap => write!(
5558
f,

0 commit comments

Comments
 (0)