Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/e2e_tests/tests/pmem_ext2.rs
5394 views
1
// Copyright 2024 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
//! Testing pmem-ext2 device.
6
7
#![cfg(any(target_os = "android", target_os = "linux"))]
8
9
use std::os::unix::fs::symlink;
10
11
use fixture::vm::Config;
12
use fixture::vm::TestVm;
13
14
/// Check file contents on pmem-ext2
15
#[test]
16
fn pmem_ext2() -> anyhow::Result<()> {
17
// /temp_dir/
18
// ├── a.txt
19
// └── dir
20
// ├── b.txt
21
// └── symlink_a -> ../a.txt
22
23
const A_TXT_NAME: &str = "a.txt";
24
const A_TXT_DATA: &str = "Hello!";
25
const DIR_NAME: &str = "dir";
26
const B_TXT_NAME: &str = "b.txt";
27
const B_TXT_DATA: &str = "test test test\ntest test test";
28
const SYMLINK_A_NAME: &str = "symlink_a";
29
const SYMLINK_A_DEST: &str = "../a.txt";
30
31
let temp_dir = tempfile::tempdir()?;
32
let a_txt = temp_dir.path().join(A_TXT_NAME);
33
std::fs::write(a_txt, A_TXT_DATA)?;
34
let dir = temp_dir.path().join(DIR_NAME);
35
std::fs::create_dir(&dir)?;
36
let b_txt = dir.join(B_TXT_NAME);
37
std::fs::write(b_txt, B_TXT_DATA)?;
38
let symlink_a = dir.join(SYMLINK_A_NAME);
39
symlink(SYMLINK_A_DEST, symlink_a)?;
40
41
let config = Config::new().extra_args(vec![
42
"--pmem-ext2".to_string(),
43
temp_dir.path().to_str().unwrap().to_string(),
44
]);
45
46
let mut vm = TestVm::new(config)?;
47
vm.exec_in_guest("mount -t ext2 /dev/pmem0 /mnt/")?;
48
49
// List all files
50
let find_result = vm
51
.exec_in_guest_async("find /mnt/ | sort")?
52
.with_timeout(std::time::Duration::from_secs(1))
53
.wait_ok(&mut vm)?;
54
assert_eq!(
55
find_result.stdout.trim(),
56
r"/mnt/
57
/mnt/a.txt
58
/mnt/dir
59
/mnt/dir/b.txt
60
/mnt/dir/symlink_a
61
/mnt/lost+found"
62
);
63
64
let a_result = vm
65
.exec_in_guest_async(&format!("cat /mnt/{A_TXT_NAME}"))?
66
.with_timeout(std::time::Duration::from_secs(1))
67
.wait_ok(&mut vm)?;
68
assert_eq!(a_result.stdout.trim(), A_TXT_DATA);
69
let b_result = vm
70
.exec_in_guest_async(&format!("cat /mnt/{DIR_NAME}/{B_TXT_NAME}"))?
71
.with_timeout(std::time::Duration::from_secs(1))
72
.wait_ok(&mut vm)?;
73
assert_eq!(b_result.stdout.trim(), B_TXT_DATA);
74
75
// Trying to read a non-existent file should return an error
76
let non_existent_result = vm
77
.exec_in_guest_async(&format!("cat /mnt/{DIR_NAME}/non-existent"))?
78
.with_timeout(std::time::Duration::from_secs(1))
79
.wait_ok(&mut vm);
80
assert!(non_existent_result.is_err());
81
82
let readlink_result = vm
83
.exec_in_guest_async(&format!("readlink /mnt/{DIR_NAME}/{SYMLINK_A_NAME}"))?
84
.with_timeout(std::time::Duration::from_secs(1))
85
.wait_ok(&mut vm)?;
86
assert_eq!(readlink_result.stdout.trim(), SYMLINK_A_DEST);
87
88
let symlink_a_result = vm
89
.exec_in_guest_async(&format!("cat /mnt/{DIR_NAME}/{SYMLINK_A_NAME}"))?
90
.with_timeout(std::time::Duration::from_secs(1))
91
.wait_ok(&mut vm)?;
92
assert_eq!(symlink_a_result.stdout.trim(), A_TXT_DATA);
93
94
Ok(())
95
}
96
97
/// Check a case with 1000 files in a directory.
98
#[test]
99
fn pmem_ext2_manyfiles() -> anyhow::Result<()> {
100
// /temp_dir/
101
// ├── 0.txt
102
// ...
103
// └── 999.txt
104
105
let temp_dir = tempfile::tempdir()?;
106
for i in 0..1000 {
107
let f = temp_dir.path().join(format!("{i}.txt"));
108
std::fs::write(f, format!("{i}"))?;
109
}
110
111
let config = Config::new().extra_args(vec![
112
"--pmem-ext2".to_string(),
113
temp_dir.path().to_str().unwrap().to_string(),
114
]);
115
116
let mut vm = TestVm::new(config)?;
117
vm.exec_in_guest("mount -t ext2 /dev/pmem0 /mnt/")?;
118
119
// `ls -l` returns 1002 lines because 1000 files + 'lost+found' and the total line.
120
let ls_result = vm
121
.exec_in_guest_async("ls -l /mnt/ | wc -l")?
122
.with_timeout(std::time::Duration::from_secs(1))
123
.wait_ok(&mut vm)?;
124
assert_eq!(ls_result.stdout.trim(), "1002");
125
126
Ok(())
127
}
128
129
/// Starts pmem-ext2 device with the given uid/gid setting and share a file created by the current
130
/// user with the guest. Returns (uid, gid) in the guest.
131
fn start_with_ugid_map(
132
uid: u32,
133
uid_map: &str,
134
gid: u32,
135
gid_map: &str,
136
) -> anyhow::Result<(u32, u32)> {
137
let temp_dir = tempfile::tempdir()?;
138
let a = temp_dir.path().join("a.txt");
139
std::fs::write(a, "A")?;
140
141
let dir_path = temp_dir.path().to_str().unwrap().to_string();
142
let config = Config::new().extra_args(vec![
143
"--pmem-ext2".to_string(),
144
format!("{dir_path}:uidmap={uid_map}:gidmap={gid_map}:uid={uid}:gid={gid}"),
145
]);
146
147
let mut vm = TestVm::new(config)?;
148
vm.exec_in_guest("mount -t ext2 /dev/pmem0 /mnt/")?;
149
150
let result = vm
151
.exec_in_guest_async("stat --printf '%u %g' /mnt/a.txt")?
152
.with_timeout(std::time::Duration::from_secs(1))
153
.wait_ok(&mut vm)?;
154
let out = result.stdout.trim();
155
println!("guest ugid: {out}");
156
let ids = out
157
.split(' ')
158
.map(|s| s.parse::<u32>())
159
.collect::<Result<Vec<u32>, _>>()
160
.unwrap();
161
assert_eq!(ids.len(), 2);
162
Ok((ids[0], ids[1])) // (uid, gid)
163
}
164
165
fn geteugid() -> (u32, u32) {
166
// SAFETY: geteuid never fails.
167
let euid = unsafe { libc::geteuid() };
168
// SAFETY: getegid never fails.
169
let egid = unsafe { libc::getegid() };
170
(euid, egid)
171
}
172
173
/// Maps to the same id in the guest.
174
#[test]
175
fn pmem_ext2_ugid_map_identical() {
176
let (host_uid, host_gid) = geteugid();
177
178
let uid_map = format!("{host_uid} {host_uid} 1");
179
let gid_map = format!("{host_gid} {host_gid} 1");
180
let (guest_uid, guest_gid) =
181
start_with_ugid_map(host_uid, &uid_map, host_gid, &gid_map).unwrap();
182
assert_eq!(host_uid, guest_uid);
183
assert_eq!(host_gid, guest_gid);
184
}
185
186
/// Maps to the root in the guest.
187
#[test]
188
fn pmem_ext2_ugid_map_to_root() {
189
let (host_uid, host_gid) = geteugid();
190
191
let uid_map = format!("0 {host_uid} 1");
192
let gid_map = format!("0 {host_gid} 1");
193
let (guest_uid, guest_gid) = start_with_ugid_map(0, &uid_map, 0, &gid_map).unwrap();
194
assert_eq!(guest_uid, 0);
195
assert_eq!(guest_gid, 0);
196
}
197
198
/// Maps to fake ids in the guest.
199
#[test]
200
fn pmem_ext2_ugid_map_fake_ids() {
201
let (host_uid, host_gid) = geteugid();
202
203
let fake_uid = 1234;
204
let fake_gid = 5678;
205
206
let uid_map = format!("{fake_uid} {host_uid} 1");
207
let gid_map = format!("{fake_gid} {host_gid} 1");
208
let (guest_uid, guest_gid) =
209
start_with_ugid_map(fake_uid, &uid_map, fake_gid, &gid_map).unwrap();
210
assert_eq!(guest_uid, fake_uid);
211
assert_eq!(guest_gid, fake_gid);
212
}
213
214