Skip to content

Commit 34ed5bc

Browse files
youenn98crosvm LUCI
authored and
crosvm LUCI
committed
device: fs: add vhost-user-fs e2e test
Add unit tests for vhost-user-fs device mount, verifying successful mounting of vhost-user fs device with sandbox abled. Test without sandbox is not prepared, since the vhost-user-fs device need to run as root. A new test scenario(mount_rw) has been added to e2e test. This scenario mounts virtiofs in the guest, then verifies data by reading from the host's shared directory. It also creates a file in the guest, writes data to it, and has the host verify the written data. This scenario is tested using both in-process virtiofs and vhost-user-fs devices. Additionally, the copy_file functionality has been refactored into a separate scenario that runs on both virtio-fs and vhost-user-fs devices. BUG=b:355159487 TEST=tools/dev_container tools/presubmit Change-Id: Icc09355fee1aae464b98fa153a98cbdd4a9e117d Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/5746575 Reviewed-by: Keiichi Watanabe <[email protected]> Commit-Queue: Yuan Yao <[email protected]>
1 parent 54e5b6b commit 34ed5bc

File tree

2 files changed

+137
-17
lines changed

2 files changed

+137
-17
lines changed

e2e_tests/fixture/src/vm.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,14 @@ impl Config {
282282
));
283283
self
284284
}
285+
286+
#[cfg(any(target_os = "android", target_os = "linux"))]
287+
pub fn with_vhost_user_fs(mut self, socket_path: &Path, tag: &str) -> Self {
288+
self.extra_args.push("--vhost-user-fs".to_string());
289+
self.extra_args
290+
.push(format!("{},tag={}", socket_path.to_str().unwrap(), tag));
291+
self
292+
}
285293
}
286294

287295
static PREP_ONCE: Once = Once::new();

e2e_tests/tests/fs.rs

Lines changed: 129 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,80 @@
44

55
//! Testing virtio-fs.
66
7+
#![cfg(any(target_os = "android", target_os = "linux"))]
8+
9+
use std::path::Path;
10+
11+
use fixture::vhost_user::CmdType;
12+
use fixture::vhost_user::Config as VuConfig;
13+
use fixture::vhost_user::VhostUserBackend;
714
use fixture::vm::Config;
815
use fixture::vm::TestVm;
16+
use tempfile::NamedTempFile;
17+
use tempfile::TempDir;
918

10-
/// Tests file copy on virtiofs
19+
/// Tests file copy
1120
///
1221
/// 1. Create `original.txt` on a temporal directory.
1322
/// 2. Start a VM with a virtiofs device for the temporal directory.
1423
/// 3. Copy `original.txt` to `new.txt` in the guest.
1524
/// 4. Check that `new.txt` is created in the host.
16-
#[test]
17-
fn copy_file() {
25+
fn copy_file(mut vm: TestVm, tag: &str, dir: TempDir) {
1826
const ORIGINAL_FILE_NAME: &str = "original.txt";
1927
const NEW_FILE_NAME: &str = "new.txt";
2028
const TEST_DATA: &str = "virtiofs works!";
2129

22-
let temp_dir = tempfile::tempdir().unwrap();
23-
let orig_file = temp_dir.path().join(ORIGINAL_FILE_NAME);
30+
let orig_file = dir.path().join(ORIGINAL_FILE_NAME);
2431

2532
std::fs::write(orig_file, TEST_DATA).unwrap();
2633

34+
// TODO(b/269137600): Split this into multiple lines instead of connecting commands with `&&`.
35+
vm.exec_in_guest(&format!(
36+
"mount -t virtiofs {tag} /mnt && cp /mnt/{} /mnt/{} && sync",
37+
ORIGINAL_FILE_NAME, NEW_FILE_NAME,
38+
))
39+
.unwrap();
40+
41+
let new_file = dir.path().join(NEW_FILE_NAME);
42+
let contents = std::fs::read(new_file).unwrap();
43+
assert_eq!(TEST_DATA.as_bytes(), &contents);
44+
}
45+
46+
/// Tests mount/read/create/write
47+
/// 1. Create `read_file.txt` with test data in host's temporal directory.
48+
/// 2. Start a VM with a virtiofs device for the temporal directory.
49+
/// 3. Guest reads read_file.txt file & verify the content is test data
50+
/// 4. Guest creates a write_file.txt file in shared directory
51+
/// 5. Host reads file from host's temporal directory & verify content is test data
52+
fn mount_rw(mut vm: TestVm, tag: &str, dir: TempDir) {
53+
const READ_FILE_NAME: &str = "read_test.txt";
54+
const WRITE_FILE_NAME: &str = "write_test.txt";
55+
const TEST_DATA: &str = "hello world";
56+
57+
let read_test_file = dir.path().join(READ_FILE_NAME);
58+
let write_test_file = dir.path().join(WRITE_FILE_NAME);
59+
std::fs::write(read_test_file, TEST_DATA).unwrap();
60+
61+
assert_eq!(
62+
vm.exec_in_guest(&format!(
63+
"mount -t virtiofs {tag} /mnt && cat /mnt/read_test.txt"
64+
))
65+
.unwrap()
66+
.stdout
67+
.trim(),
68+
TEST_DATA
69+
);
70+
71+
const IN_FS_WRITE_FILE_PATH: &str = "/mnt/write_test.txt";
72+
let _ = vm.exec_in_guest(&format!("echo -n {TEST_DATA} > {IN_FS_WRITE_FILE_PATH}"));
73+
let read_contents = std::fs::read(write_test_file).unwrap();
74+
assert_eq!(TEST_DATA.as_bytes(), &read_contents);
75+
}
76+
77+
#[test]
78+
fn fs_copy_file() {
2779
let tag = "mtdtest";
80+
let temp_dir = tempfile::tempdir().unwrap();
2881

2982
let config = Config::new().extra_args(vec![
3083
"--shared-dir".to_string(),
@@ -34,17 +87,25 @@ fn copy_file() {
3487
),
3588
]);
3689

37-
let mut vm = TestVm::new(config).unwrap();
38-
// TODO(b/269137600): Split this into multiple lines instead of connecting commands with `&&`.
39-
vm.exec_in_guest(&format!(
40-
"mount -t virtiofs {tag} /mnt && cp /mnt/{} /mnt/{} && sync",
41-
ORIGINAL_FILE_NAME, NEW_FILE_NAME,
42-
))
43-
.unwrap();
90+
let vm = TestVm::new(config).unwrap();
91+
copy_file(vm, tag, temp_dir)
92+
}
4493

45-
let new_file = temp_dir.path().join(NEW_FILE_NAME);
46-
let contents = std::fs::read(new_file).unwrap();
47-
assert_eq!(TEST_DATA.as_bytes(), &contents);
94+
#[test]
95+
fn fs_mount_rw() {
96+
let tag = "mtdtest";
97+
let temp_dir = tempfile::tempdir().unwrap();
98+
99+
let config = Config::new().extra_args(vec![
100+
"--shared-dir".to_string(),
101+
format!(
102+
"{}:{tag}:type=fs:cache=auto",
103+
temp_dir.path().to_str().unwrap()
104+
),
105+
]);
106+
107+
let vm = TestVm::new(config).unwrap();
108+
mount_rw(vm, tag, temp_dir)
48109
}
49110

50111
/// Tests file ownership seen by the VM.
@@ -54,15 +115,14 @@ fn copy_file() {
54115
/// 3. Start a VM with a virtiofs device for the temporal directory.
55116
/// 4. Check that `user_file.txt`'s uid is <mapped-uid> in the VM.
56117
/// 5. Verify gid similarly.
57-
#[cfg(any(target_os = "android", target_os = "linux"))]
58118
#[test]
59119
fn file_ugid() {
60120
const FILE_NAME: &str = "user_file.txt";
61121
let uid = base::geteuid();
62122
let gid = base::getegid();
63123
let mapped_uid: u32 = rand::random();
64124
let mapped_gid: u32 = rand::random();
65-
let uid_map = format!("{} {} 1", mapped_uid, uid);
125+
let uid_map: String = format!("{} {} 1", mapped_uid, uid);
66126
let gid_map = format!("{} {} 1", mapped_gid, gid);
67127

68128
let temp_dir = tempfile::tempdir().unwrap();
@@ -101,3 +161,55 @@ fn file_ugid() {
101161
assert!(output.stdout.contains(&format!("Uid: ({}/", mapped_uid)));
102162
assert!(output.stdout.contains(&format!("Gid: ({}/", mapped_gid)));
103163
}
164+
165+
pub fn create_vu_fs_config(socket: &Path, shared_dir: &Path, tag: &str) -> VuConfig {
166+
let uid = base::geteuid();
167+
let gid = base::getegid();
168+
let socket_path = socket.to_str().unwrap();
169+
let shared_dir_path = shared_dir.to_str().unwrap();
170+
println!("socket={socket_path}, tag={tag}, shared_dir={shared_dir_path}");
171+
VuConfig::new(CmdType::Device, "vhost-user-fs").extra_args(vec![
172+
"fs".to_string(),
173+
format!("--socket={socket_path}"),
174+
format!("--shared-dir={shared_dir_path}"),
175+
format!("--tag={tag}"),
176+
format!("--uid-map=0 {uid} 1"),
177+
format!("--gid-map=0 {gid} 1"),
178+
])
179+
}
180+
181+
/// Tests vhost-user fs device copy file.
182+
#[test]
183+
fn vhost_user_fs_copy_file() {
184+
let socket = NamedTempFile::new().unwrap();
185+
let temp_dir = tempfile::tempdir().unwrap();
186+
187+
let config = Config::new();
188+
let tag = "mtdtest";
189+
190+
let vu_config = create_vu_fs_config(socket.path(), temp_dir.path(), tag);
191+
let _vu_device = VhostUserBackend::new(vu_config).unwrap();
192+
193+
let config = config.with_vhost_user_fs(socket.path(), tag);
194+
let vm = TestVm::new(config).unwrap();
195+
196+
copy_file(vm, tag, temp_dir);
197+
}
198+
199+
/// Tests vhost-user fs device mount and read write.
200+
#[test]
201+
fn vhost_user_fs_mount_rw() {
202+
let socket = NamedTempFile::new().unwrap();
203+
let temp_dir = tempfile::tempdir().unwrap();
204+
205+
let config = Config::new();
206+
let tag = "mtdtest";
207+
208+
let vu_config = create_vu_fs_config(socket.path(), temp_dir.path(), tag);
209+
let _vu_device = VhostUserBackend::new(vu_config).unwrap();
210+
211+
let config = config.with_vhost_user_fs(socket.path(), tag);
212+
let vm = TestVm::new(config).unwrap();
213+
214+
mount_rw(vm, tag, temp_dir);
215+
}

0 commit comments

Comments
 (0)