use super::mount::*;
use super::tests_mount::make_mount_resource;
use crate::core::types::Resource;
const SRC: &str = "//nas/media";
const TGT: &str = "/mnt/unas";
fn cifs(options: &str) -> Resource {
let mut r = make_mount_resource();
r.source = Some(SRC.to_string());
r.path = Some(TGT.to_string());
r.fs_type = Some("cifs".to_string());
r.options = Some(options.to_string());
r
}
fn fake_host(live: &str, busy: bool) -> tempfile::TempDir {
let d = tempfile::tempdir().expect("tempdir");
let calls = d.path().join("calls");
let m = d.path().join("m");
std::fs::write(&m, "").expect("mounted marker");
let m = m.display();
let findmnt = format!(
"#!/bin/sh\ncase \"$*\" in *SOURCE*) echo '{SRC}' ;; *OPTIONS*) echo '{live}' ;; *FSTYPE*) [ -e '{m}' ] && echo cifs ;; esac\nexit 0\n"
);
let rc = if busy { 32 } else { 0 };
let umount = format!(
"#!/bin/sh\necho \"umount $*\" >> '{}'\n[ {rc} -eq 0 ] || exit {rc}\nrm -f '{m}'\n",
calls.display()
);
let mount = format!(
"#!/bin/sh\necho \"mount $*\" >> '{}'\n: > '{m}'\n",
calls.display()
);
for (name, body) in [
("findmnt", findmnt),
("umount", umount),
("mount", mount),
("mountpoint", "#!/bin/sh\nexit 0\n".into()),
("mkdir", "#!/bin/sh\nexit 0\n".into()),
] {
let p = d.path().join(name);
std::fs::write(&p, body).expect("write fake");
std::process::Command::new("chmod")
.arg("+x")
.arg(&p)
.status()
.expect("chmod");
}
d
}
fn run(script: &str, host: &tempfile::TempDir) -> i32 {
run_in("sh", script, host)
}
fn run_bash(script: &str, host: &tempfile::TempDir) -> i32 {
run_in("bash", script, host)
}
fn run_in(shell: &str, script: &str, host: &tempfile::TempDir) -> i32 {
let path = format!(
"{}:{}",
host.path().display(),
std::env::var("PATH").unwrap_or_default()
);
std::process::Command::new(shell)
.arg("-c")
.arg(script)
.env("PATH", path)
.output()
.expect("sh")
.status
.code()
.unwrap_or(-1)
}
fn calls(host: &tempfile::TempDir) -> String {
std::fs::read_to_string(host.path().join("calls")).unwrap_or_default()
}
const LIVE_1000: &str =
"rw,relatime,vers=3.1.1,uid=1000,forceuid,gid=1000,forcegid,file_mode=0664,dir_mode=0775,soft";
const LIVE_997: &str =
"rw,relatime,vers=3.1.1,uid=1000,forceuid,gid=997,forcegid,file_mode=0664,dir_mode=0775,soft";
const DECL_997: &str =
"rw,vers=3.1.1,credentials=/etc/c,uid=1000,gid=997,file_mode=0664,dir_mode=0775,noatime";
#[test]
fn fj642_check_is_red_when_the_right_share_has_the_wrong_gid() {
let h = fake_host(LIVE_1000, false);
assert_ne!(
run(&check_script(&cifs(DECL_997)), &h),
0,
"same source, gid 1000 live vs 997 declared must diverge"
);
}
#[test]
fn fj642_check_is_green_when_every_declared_ownership_option_is_live() {
let h = fake_host(LIVE_997, false);
assert_eq!(run(&check_script(&cifs(DECL_997)), &h), 0);
}
#[test]
fn fj642_modes_compare_as_numbers_and_absent_keys_are_the_default() {
let h = fake_host("rw,nosuid,size=1024k,mode=755", false);
assert_eq!(
run(&check_script(&cifs("size=1024k,mode=0755,uid=0")), &h),
0
);
assert_ne!(
run(&check_script(&cifs("size=1024k,mode=0700")), &h),
0,
"a different mode must diverge"
);
}
#[test]
fn fj642_an_omitted_key_is_its_default_not_a_wildcard() {
let h = fake_host("rw,nosuid,size=1024k", false);
assert_ne!(
run(&check_script(&cifs("size=1024k,uid=1000")), &h),
0,
"omitted uid is 0, not whatever was declared"
);
assert_eq!(run(&check_script(&cifs("size=1024k,mode=1777")), &h), 0);
assert_ne!(run(&check_script(&cifs("dir_mode=02775")), &h), 0);
}
#[test]
fn fj642_a_group_name_is_compared_by_its_number() {
let h = fake_host("rw,gid=0", false);
assert_eq!(run(&check_script(&cifs("gid=root")), &h), 0);
let h = fake_host("rw,gid=1000", false);
assert_ne!(run(&check_script(&cifs("gid=root")), &h), 0);
}
#[test]
fn fj642_no_ownership_option_declared_keeps_the_source_only_check() {
let h = fake_host(LIVE_1000, false);
assert_eq!(run(&check_script(&cifs("rw,noatime")), &h), 0);
}
#[test]
fn fj642_apply_remounts_on_ownership_drift_without_a_lazy_detach() {
let h = fake_host(LIVE_1000, false);
let script = apply_script(&cifs(DECL_997))
.replace("/etc/fstab", &h.path().join("fstab").display().to_string());
assert_eq!(run_bash(&script, &h), 0);
let c = calls(&h);
assert!(c.contains("umount /mnt/unas"), "{c}");
assert!(
c.contains("mount -t cifs -o rw,vers=3.1.1,credentials=/etc/c,uid=1000,gid=997"),
"{c}"
);
assert!(!c.contains("-l"), "never a lazy detach: {c}");
}
#[test]
fn fj642_apply_refuses_to_detach_a_busy_mount() {
let h = fake_host(LIVE_1000, true);
let script = apply_script(&cifs(DECL_997))
.replace("/etc/fstab", &h.path().join("fstab").display().to_string());
assert_ne!(
run_bash(&script, &h),
0,
"busy must fail loudly, not converge"
);
let c = calls(&h);
assert!(!c.contains("mount -t"), "no mount over a busy path: {c}");
}
#[test]
fn fj642_apply_leaves_a_converged_mount_alone() {
let h = fake_host(LIVE_997, false);
let script = apply_script(&cifs(DECL_997))
.replace("/etc/fstab", &h.path().join("fstab").display().to_string());
assert_eq!(run_bash(&script, &h), 0);
assert!(!calls(&h).contains("umount"), "{}", calls(&h));
}