#![cfg(any(target_os = "android", target_os = "linux"))]
use std::os::unix::fs::symlink;
use fixture::vm::Config;
use fixture::vm::TestVm;
#[test]
fn pmem_ext2() -> anyhow::Result<()> {
const A_TXT_NAME: &str = "a.txt";
const A_TXT_DATA: &str = "Hello!";
const DIR_NAME: &str = "dir";
const B_TXT_NAME: &str = "b.txt";
const B_TXT_DATA: &str = "test test test\ntest test test";
const SYMLINK_A_NAME: &str = "symlink_a";
const SYMLINK_A_DEST: &str = "../a.txt";
let temp_dir = tempfile::tempdir()?;
let a_txt = temp_dir.path().join(A_TXT_NAME);
std::fs::write(a_txt, A_TXT_DATA)?;
let dir = temp_dir.path().join(DIR_NAME);
std::fs::create_dir(&dir)?;
let b_txt = dir.join(B_TXT_NAME);
std::fs::write(b_txt, B_TXT_DATA)?;
let symlink_a = dir.join(SYMLINK_A_NAME);
symlink(SYMLINK_A_DEST, symlink_a)?;
let config = Config::new().extra_args(vec![
"--pmem-ext2".to_string(),
temp_dir.path().to_str().unwrap().to_string(),
]);
let mut vm = TestVm::new(config)?;
vm.exec_in_guest("mount -t ext2 /dev/pmem0 /mnt/")?;
let find_result = vm
.exec_in_guest_async("find /mnt/ | sort")?
.with_timeout(std::time::Duration::from_secs(1))
.wait_ok(&mut vm)?;
assert_eq!(
find_result.stdout.trim(),
r"/mnt/
/mnt/a.txt
/mnt/dir
/mnt/dir/b.txt
/mnt/dir/symlink_a
/mnt/lost+found"
);
let a_result = vm
.exec_in_guest_async(&format!("cat /mnt/{A_TXT_NAME}"))?
.with_timeout(std::time::Duration::from_secs(1))
.wait_ok(&mut vm)?;
assert_eq!(a_result.stdout.trim(), A_TXT_DATA);
let b_result = vm
.exec_in_guest_async(&format!("cat /mnt/{DIR_NAME}/{B_TXT_NAME}"))?
.with_timeout(std::time::Duration::from_secs(1))
.wait_ok(&mut vm)?;
assert_eq!(b_result.stdout.trim(), B_TXT_DATA);
let non_existent_result = vm
.exec_in_guest_async(&format!("cat /mnt/{DIR_NAME}/non-existent"))?
.with_timeout(std::time::Duration::from_secs(1))
.wait_ok(&mut vm);
assert!(non_existent_result.is_err());
let readlink_result = vm
.exec_in_guest_async(&format!("readlink /mnt/{DIR_NAME}/{SYMLINK_A_NAME}"))?
.with_timeout(std::time::Duration::from_secs(1))
.wait_ok(&mut vm)?;
assert_eq!(readlink_result.stdout.trim(), SYMLINK_A_DEST);
let symlink_a_result = vm
.exec_in_guest_async(&format!("cat /mnt/{DIR_NAME}/{SYMLINK_A_NAME}"))?
.with_timeout(std::time::Duration::from_secs(1))
.wait_ok(&mut vm)?;
assert_eq!(symlink_a_result.stdout.trim(), A_TXT_DATA);
Ok(())
}
#[test]
fn pmem_ext2_manyfiles() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
for i in 0..1000 {
let f = temp_dir.path().join(format!("{i}.txt"));
std::fs::write(f, format!("{i}"))?;
}
let config = Config::new().extra_args(vec![
"--pmem-ext2".to_string(),
temp_dir.path().to_str().unwrap().to_string(),
]);
let mut vm = TestVm::new(config)?;
vm.exec_in_guest("mount -t ext2 /dev/pmem0 /mnt/")?;
let ls_result = vm
.exec_in_guest_async("ls -l /mnt/ | wc -l")?
.with_timeout(std::time::Duration::from_secs(1))
.wait_ok(&mut vm)?;
assert_eq!(ls_result.stdout.trim(), "1002");
Ok(())
}
fn start_with_ugid_map(
uid: u32,
uid_map: &str,
gid: u32,
gid_map: &str,
) -> anyhow::Result<(u32, u32)> {
let temp_dir = tempfile::tempdir()?;
let a = temp_dir.path().join("a.txt");
std::fs::write(a, "A")?;
let dir_path = temp_dir.path().to_str().unwrap().to_string();
let config = Config::new().extra_args(vec![
"--pmem-ext2".to_string(),
format!("{dir_path}:uidmap={uid_map}:gidmap={gid_map}:uid={uid}:gid={gid}"),
]);
let mut vm = TestVm::new(config)?;
vm.exec_in_guest("mount -t ext2 /dev/pmem0 /mnt/")?;
let result = vm
.exec_in_guest_async("stat --printf '%u %g' /mnt/a.txt")?
.with_timeout(std::time::Duration::from_secs(1))
.wait_ok(&mut vm)?;
let out = result.stdout.trim();
println!("guest ugid: {out}");
let ids = out
.split(' ')
.map(|s| s.parse::<u32>())
.collect::<Result<Vec<u32>, _>>()
.unwrap();
assert_eq!(ids.len(), 2);
Ok((ids[0], ids[1]))
}
fn geteugid() -> (u32, u32) {
let euid = unsafe { libc::geteuid() };
let egid = unsafe { libc::getegid() };
(euid, egid)
}
#[test]
fn pmem_ext2_ugid_map_identical() {
let (host_uid, host_gid) = geteugid();
let uid_map = format!("{host_uid} {host_uid} 1");
let gid_map = format!("{host_gid} {host_gid} 1");
let (guest_uid, guest_gid) =
start_with_ugid_map(host_uid, &uid_map, host_gid, &gid_map).unwrap();
assert_eq!(host_uid, guest_uid);
assert_eq!(host_gid, guest_gid);
}
#[test]
fn pmem_ext2_ugid_map_to_root() {
let (host_uid, host_gid) = geteugid();
let uid_map = format!("0 {host_uid} 1");
let gid_map = format!("0 {host_gid} 1");
let (guest_uid, guest_gid) = start_with_ugid_map(0, &uid_map, 0, &gid_map).unwrap();
assert_eq!(guest_uid, 0);
assert_eq!(guest_gid, 0);
}
#[test]
fn pmem_ext2_ugid_map_fake_ids() {
let (host_uid, host_gid) = geteugid();
let fake_uid = 1234;
let fake_gid = 5678;
let uid_map = format!("{fake_uid} {host_uid} 1");
let gid_map = format!("{fake_gid} {host_gid} 1");
let (guest_uid, guest_gid) =
start_with_ugid_map(fake_uid, &uid_map, fake_gid, &gid_map).unwrap();
assert_eq!(guest_uid, fake_uid);
assert_eq!(guest_gid, fake_gid);
}