Skip to content

Commit 026b2d0

Browse files
committed
Filesystem: Announce mounts in FDT
1 parent 738bded commit 026b2d0

File tree

10 files changed

+169
-17
lines changed

10 files changed

+169
-17
lines changed

src/fdt.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ impl Fdt {
7070
Ok(self)
7171
}
7272

73+
/// Adds a `/uhyve,mounts` node to the FDT.
74+
pub fn mounts(mut self, dirs: Vec<String>) -> FdtWriterResult<Self> {
75+
let mounts_node = self.writer.begin_node("uhyve,mounts")?;
76+
self.writer.property_string_list("mounts", dirs)?;
77+
self.writer.end_node(mounts_node)?;
78+
79+
Ok(self)
80+
}
81+
7382
#[cfg(target_arch = "aarch64")]
7483
fn cpu(&mut self, id: u32) -> FdtWriterResult<()> {
7584
let node_name = format!("cpu@{id}");

src/isolation/filemap.rs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
#[cfg(target_os = "linux")]
2-
use std::path::Path;
31
use std::{
42
collections::HashMap,
53
ffi::{CStr, CString, OsStr, OsString},
6-
fs::canonicalize,
4+
fs::{canonicalize, metadata},
75
os::unix::ffi::OsStrExt,
8-
path::PathBuf,
6+
path::{Path, PathBuf},
97
};
108

119
use clean_path::clean;
@@ -96,6 +94,38 @@ impl UhyveFileMap {
9694
self.files.values().map(|i| i.as_os_str())
9795
}
9896

97+
/// Returns an iterator (non-unique) over all mountable guest directories.
98+
pub(crate) fn get_all_guest_dirs(&self) -> impl Iterator<Item = &Path> {
99+
self.files.iter().filter_map(|(gp, hp)| {
100+
// We check the host_path filetype, and return the parent directory for everything non-file.
101+
if let Ok(hp_metadata) = metadata(hp) {
102+
if hp_metadata.is_dir() {
103+
Some(gp.as_path())
104+
} else if hp_metadata.is_file() {
105+
Some(gp.as_path().parent().unwrap())
106+
} else if hp_metadata.is_symlink() {
107+
error!(
108+
"{} is a symlink. This is not supported (yet?)",
109+
hp.display()
110+
);
111+
None
112+
} else {
113+
Some(gp.as_path().parent().unwrap())
114+
}
115+
} else if let Some(parent_path) = hp.parent()
116+
&& let Ok(parent_metadata) = metadata(parent_path)
117+
&& parent_metadata.is_dir()
118+
{
119+
// Parent directory exists, so this is a mounted file
120+
Some(gp.as_path().parent().unwrap())
121+
} else {
122+
error!("{} isn't a valid host path", hp.display());
123+
// return Err(ErrorKind::InvalidFilename);
124+
None
125+
}
126+
})
127+
}
128+
99129
/// Returns the path to the temporary directory (for Landlock).
100130
#[cfg(target_os = "linux")]
101131
pub(crate) fn get_temp_dir(&self) -> &Path {

src/vm.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,13 @@ impl<VirtBackend: VirtualizationBackend> UhyveVm<VirtBackend> {
208208
&params.file_mapping,
209209
params.tempdir.clone(),
210210
));
211+
let mut mounts: Vec<_> = file_mapping
212+
.lock()
213+
.unwrap()
214+
.get_all_guest_dirs()
215+
.map(|s| s.to_str().unwrap().to_string())
216+
.collect();
217+
mounts.dedup();
211218

212219
let serial = UhyveSerial::from_params(&params.output)?;
213220

@@ -279,7 +286,7 @@ impl<VirtBackend: VirtualizationBackend> UhyveVm<VirtBackend> {
279286

280287
let freq = vcpus[0].get_cpu_frequency();
281288

282-
write_fdt_into_mem(&peripherals.mem, &kernel_info.params, freq);
289+
write_fdt_into_mem(&peripherals.mem, &kernel_info.params, freq, mounts);
283290
write_boot_info_to_mem(&peripherals.mem, load_info, cpu_count as u64, freq);
284291

285292
let legacy_mapping = if let Some(version) = hermit_version {
@@ -445,7 +452,12 @@ fn init_guest_mem(
445452
crate::arch::init_guest_mem(mem, guest_addr, memory_size, legacy_mapping);
446453
}
447454

448-
fn write_fdt_into_mem(mem: &MmapMemory, params: &Params, cpu_freq: Option<NonZeroU32>) {
455+
fn write_fdt_into_mem(
456+
mem: &MmapMemory,
457+
params: &Params,
458+
cpu_freq: Option<NonZeroU32>,
459+
mounts: Vec<String>,
460+
) {
449461
trace!("Writing FDT in memory");
450462

451463
let sep = params
@@ -468,6 +480,10 @@ fn write_fdt_into_mem(mem: &MmapMemory, params: &Params, cpu_freq: Option<NonZer
468480
EnvVars::Set(map) => fdt.envs(map.iter().map(|(a, b)| (a.as_str(), b.as_str()))),
469481
};
470482

483+
if !mounts.is_empty() {
484+
fdt = fdt.mounts(mounts).unwrap();
485+
}
486+
471487
#[cfg(target_arch = "aarch64")]
472488
{
473489
fdt = fdt.gic().unwrap();
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
12345
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
abcde
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
a1b2c3

tests/fs-test.rs

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use common::{
1212
build_hermit_bin, check_result, get_fs_fixture_path, remove_file_if_exists, run_vm_in_thread,
1313
};
1414
use rand::{Rng, distr::Alphanumeric};
15+
use tempfile::TempDir;
1516
use uhyvelib::{params::Params, vm::UhyveVm};
1617

1718
/// Verifies successful file creation on the host OS and its contents.
@@ -39,13 +40,35 @@ impl AsStr for &str {
3940

4041
/// Gets a "base" guest and host path, only useful for UhyveFileMap tests.
4142
fn get_default_paths() -> (PathBuf, PathBuf) {
42-
let guest_dir_path: PathBuf = PathBuf::from("/root/");
43+
let guest_dir_path: PathBuf = PathBuf::from("/uhyve_mount");
4344
let mut host_dir_path = get_fs_fixture_path();
4445
host_dir_path.push("ignore_everything_here");
4546

4647
(guest_dir_path, host_dir_path)
4748
}
4849

50+
/// Creates a temporary directory for a uhyve mount and returns the path to it
51+
/// as well as the owning TempDir
52+
fn create_tmp_mount() -> (PathBuf, TempDir) {
53+
let tempdir = TempDir::new().unwrap();
54+
let host_path = tempdir.path().to_path_buf();
55+
(host_path, tempdir)
56+
}
57+
58+
/// creates a tempfile from the given filename and returns a filemap, the guest
59+
/// filepath and a directory handle to the tempdir.
60+
fn create_filemap(guest_filename: &str) -> (Vec<String>, PathBuf, TempDir) {
61+
let guest_file_path = get_testname_derived_guest_path(guest_filename);
62+
let (mut host_path, tmpdir) = create_tmp_mount();
63+
host_path.push(guest_filename);
64+
let filemap_str = format!(
65+
"{}:{}",
66+
host_path.as_os_str().display(),
67+
guest_file_path.as_os_str().display()
68+
);
69+
(vec![filemap_str], guest_file_path, tmpdir)
70+
}
71+
4972
/// Generates a filename in the format of prefixab1cD23.txt
5073
fn generate_filename(prefix: &str) -> String {
5174
let mut filename = prefix.to_owned();
@@ -64,7 +87,7 @@ fn generate_filename(prefix: &str) -> String {
6487
/// * `test_name` - Name of the test.
6588
fn get_testname_derived_guest_path(test_name: &str) -> PathBuf {
6689
// Starting off with the "guest_dir_path".
67-
let mut guest_file_path = PathBuf::from("/root/");
90+
let mut guest_file_path = PathBuf::from("/uhyve_mount/");
6891
guest_file_path.push(generate_filename(test_name));
6992
guest_file_path
7093
}
@@ -272,8 +295,8 @@ fn fd_open_remove_close() {
272295
env_logger::try_init().ok();
273296

274297
let test_name: &'static str = "fd_open_remove_close";
275-
let guest_file_path = get_testname_derived_guest_path(test_name);
276-
let params = generate_params(None, test_name, &guest_file_path);
298+
let (filemap, guest_file_path, _tmpdir) = create_filemap(test_name);
299+
let params = generate_params(Some(filemap), test_name, &guest_file_path);
277300

278301
let bin_path: PathBuf = build_hermit_bin("fs_tests");
279302
let res = run_vm_in_thread(bin_path, params);
@@ -317,8 +340,8 @@ fn open_read_only_write() {
317340
env_logger::try_init().ok();
318341

319342
let test_name: &'static str = "open_read_only_write";
320-
let guest_file_path = get_testname_derived_guest_path(test_name);
321-
let params = generate_params(None, test_name, &guest_file_path);
343+
let (filemap, guest_file_path, _tmpdir) = create_filemap(test_name);
344+
let params = generate_params(Some(filemap), test_name, &guest_file_path);
322345

323346
let bin_path: PathBuf = build_hermit_bin("fs_tests");
324347

@@ -356,13 +379,55 @@ fn fd_write_to_fd() {
356379
check_result(&res);
357380
}
358381

382+
#[test]
383+
fn mounts_test() {
384+
env_logger::try_init().ok();
385+
386+
let test_name: &'static str = "mounts_test";
387+
let guest_dir_path: PathBuf = PathBuf::from("/");
388+
let host_dir_path = get_fs_fixture_path();
389+
390+
let uhyvefilemap_params = vec![
391+
format!(
392+
"{}/testdir1:{}testdir1",
393+
(&host_dir_path).as_str(),
394+
(&guest_dir_path).as_str()
395+
),
396+
format!(
397+
"{}/testdir2:{}testdir2",
398+
(&host_dir_path).as_str(),
399+
(&guest_dir_path).as_str()
400+
),
401+
format!(
402+
"{}/testdir3:{}testdir3/subdir1/subdir2/subdir3",
403+
(&host_dir_path).as_str(),
404+
(&guest_dir_path).as_str()
405+
),
406+
format!(
407+
"{}/testdir2:{}testdir4",
408+
(&host_dir_path).as_str(),
409+
(&guest_dir_path).as_str()
410+
),
411+
format!(
412+
"{}/testdir1/testfile_a.txt:/anothermountpoint/test_a.txt",
413+
(&host_dir_path).as_str(),
414+
),
415+
];
416+
let guest_file_path = get_testname_derived_guest_path(test_name);
417+
let params = generate_params(uhyvefilemap_params.into(), test_name, &guest_file_path);
418+
419+
let bin_path: PathBuf = build_hermit_bin("fs_tests");
420+
let res = run_vm_in_thread(bin_path, params);
421+
check_result(&res);
422+
}
423+
359424
#[test]
360425
fn lseek_test() {
361426
env_logger::try_init().ok();
362427

363428
let test_name: &'static str = "lseek_file";
364-
let guest_file_path = get_testname_derived_guest_path(test_name);
365-
let params = generate_params(None, test_name, &guest_file_path);
429+
let (filemap, guest_file_path, _tmpdir) = create_filemap(test_name);
430+
let params = generate_params(Some(filemap), test_name, &guest_file_path);
366431

367432
let bin_path: PathBuf = build_hermit_bin("fs_tests");
368433
let res = run_vm_in_thread(bin_path, params);

tests/test-kernels/Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/test-kernels/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ edition = "2024"
99
publish = false
1010

1111
[target.'cfg(target_os = "hermit")'.dependencies]
12-
hermit = "0.11"
12+
hermit = "0.12"
1313

1414
[target.'cfg(target_arch = "x86_64")'.dependencies]
1515
x86_64 = { version = "0.15", default-features = false, features = [

tests/test-kernels/src/bin/fs_tests.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,34 @@ fn lseek_file(filename: &str) {
128128
assert_eq!(buf, [5, 6, 7, 8, 9, 10, 11, 12, 13, 14]);
129129
}
130130

131+
fn mount_test() {
132+
println!("Mounts Test");
133+
let mut f = File::open("/testdir1/testfile_a.txt").unwrap();
134+
let mut contents = String::new();
135+
f.read_to_string(&mut contents).unwrap();
136+
assert_eq!(contents, "12345");
137+
138+
let mut f = File::open("/testdir2/testfile_b.txt").unwrap();
139+
let mut contents = String::new();
140+
f.read_to_string(&mut contents).unwrap();
141+
assert_eq!(contents, "abcde");
142+
143+
let mut f = File::open("/testdir3/subdir1/subdir2/subdir3/testfile_c.txt").unwrap();
144+
let mut contents = String::new();
145+
f.read_to_string(&mut contents).unwrap();
146+
assert_eq!(contents, "a1b2c3");
147+
148+
let mut f = File::open("/testdir4/testfile_b.txt").unwrap();
149+
let mut contents = String::new();
150+
f.read_to_string(&mut contents).unwrap();
151+
assert_eq!(contents, "abcde");
152+
153+
let mut f = File::open("/anothermountpoint/test_a.txt").unwrap();
154+
let mut contents = String::new();
155+
f.read_to_string(&mut contents).unwrap();
156+
assert_eq!(contents, "12345");
157+
}
158+
131159
fn main() {
132160
let args: Vec<String> = env::args().collect();
133161
let testname = &args[1].split('=').collect::<Vec<_>>()[1];
@@ -147,6 +175,7 @@ fn main() {
147175
"fd_remove_twice_before_closing" => remove_twice_before_closing(filename),
148176
"open_read_only_write" => open_read_only_write(filename),
149177
"lseek_file" => lseek_file(filename),
178+
"mounts_test" => mount_test(),
150179
_ => panic!("test not found"),
151180
}
152181
}

0 commit comments

Comments
 (0)