diff --git a/Cargo.lock b/Cargo.lock index 1e22becd8af553ca4fe4d41f6ee8202da795f589..91d7294e6e248c77566891c5a6f6678a09656396 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -377,6 +377,7 @@ version = "0.5.1" dependencies = [ "basic", "bitflags", + "device", "event", "fnmatch-sys", "libc", diff --git a/exts/devmaster/Cargo.toml b/exts/devmaster/Cargo.toml index 4a3a8176b3d3e8a7621a6f857f99b48d4d419a16..af4c51a3ff2406b0e2b0a4ef2da1a8f57de8fd9c 100644 --- a/exts/devmaster/Cargo.toml +++ b/exts/devmaster/Cargo.toml @@ -35,7 +35,7 @@ basic = { path = "../../libs/basic", default-features = false, features = [ "time_util", ] } blkid_rs = { path = "../../libs/blkid_rs" } -device = { path = "../../libs/device" } +device = { path = "../../libs/device", default-features = false } event = { path = "../../libs/event" } input_event_codes_rs = { path = "../../libs/input_event_codes_rs" } kmod_rs = { path = "../../libs/kmod_rs" } @@ -79,3 +79,6 @@ fnmatch-sys = "1.0.0" [build-dependencies] basic = { path = "../../libs/basic", features = ["cargo"] } + +[dev-dependencies] +device = { path = "../../libs/device", features = ["loopdev"] } diff --git a/exts/devmaster/src/bin/devctl/daemon/mod.rs b/exts/devmaster/src/bin/devctl/daemon/mod.rs index 53931650a7a8ee3aaf12ac96ae7b363243f9a573..4aa957a4952ab9228982536bbfbecc592a0d0c29 100644 --- a/exts/devmaster/src/bin/devctl/daemon/mod.rs +++ b/exts/devmaster/src/bin/devctl/daemon/mod.rs @@ -61,7 +61,10 @@ mod test { #[test] fn test_run_daemon() { - /* Require root privilege, skip in ci environment. */ + /* Require root privilege, skip in ci environment. + * + * In addition, this test case will fail when devmaster daemon is running. + */ let dev = Device::from_subsystem_sysname("net", "lo").unwrap(); if dev.trigger(DeviceAction::Change).is_err() { return; @@ -73,9 +76,9 @@ mod test { let dev = Device::from_subsystem_sysname("net", "lo").unwrap(); /* Trigger more than the number of workers. */ - dev.trigger(DeviceAction::Change).unwrap(); - dev.trigger(DeviceAction::Change).unwrap(); - dev.trigger(DeviceAction::Change).unwrap(); + dev.trigger(DeviceAction::Remove).unwrap(); + dev.trigger(DeviceAction::Add).unwrap(); + dev.trigger(DeviceAction::Add).unwrap(); dev.trigger(DeviceAction::Change).unwrap(); dev.trigger(DeviceAction::Change).unwrap(); dev.trigger(DeviceAction::Change).unwrap(); diff --git a/exts/devmaster/src/lib/rules/exec_mgr.rs b/exts/devmaster/src/lib/rules/exec_mgr.rs index 66c1a06c5d46bd14e381231bfe0b445451e00aed..03da901dc8d48cc46acf97d2de12c568f0daaef8 100644 --- a/exts/devmaster/src/lib/rules/exec_mgr.rs +++ b/exts/devmaster/src/lib/rules/exec_mgr.rs @@ -2140,8 +2140,13 @@ impl RuleLine { #[cfg(test)] mod tests { + use std::fs::remove_file; + use super::*; + use crate::rules::rules_load::tests::create_tmp_file; use crate::rules::FormatSubstitutionType; + use device::utils::LoopDev; + use log::{init_log, Level}; #[test] #[ignore] @@ -2294,4 +2299,916 @@ mod tests { assert_eq!(unit.apply_format("$parent", false).unwrap(), "sda"); assert_eq!(unit.apply_format("$devnode", false).unwrap(), "/dev/sda1"); } + + impl ExecuteManager { + #[allow(clippy::too_many_arguments)] + fn test_apply_one_rule_token( + &self, + key: &str, + attr: &str, + op: &str, + value: &str, + rules: Arc>, + rule_line: Arc>>, + device: Rc>, + ) -> Result { + let token = RuleToken::parse_token( + key.to_string(), + if attr.is_empty() { + None + } else { + Some(attr.to_string()) + }, + op.to_string(), + value.to_string(), + rules, + rule_line, + ) + .unwrap(); + *self.current_rule_token.borrow_mut() = Arc::new(RwLock::new(Some(token))); + self.apply_rule_token(device) + } + } + + #[test] + fn test_apply_rules() { + init_log( + "test_apply_rules", + Level::Debug, + vec!["console"], + "", + 0, + 0, + false, + ); + + let device = Rc::new(RefCell::new( + Device::from_subsystem_sysname("net", "lo").unwrap(), + )); + device.borrow().set_base_path("/tmp/devmaster"); + let rules = Arc::new(RwLock::new(Rules::new(vec![], ResolveNameTime::Early))); + let rule_file = Arc::new(RwLock::new(Some(RuleFile::new("".to_string())))); + let rule_line = Arc::new(RwLock::new(Some(RuleLine::new( + "".to_string(), + 0, + rule_file, + )))); + let unit = ExecuteUnit::new(device.clone()); + let mgr = ExecuteManager::new(Arc::new(RwLock::new(Cache::new(vec![], vec![])))); + *mgr.current_unit.borrow_mut() = Some(unit); + + device.borrow().set_action_from_string("change").unwrap(); + assert!(mgr + .test_apply_one_rule_token( + "ACTION", + "", + "==", + "change", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + assert!(!mgr + .test_apply_one_rule_token( + "ACTION", + "", + "==", + "add", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "DEVPATH", + "", + "==", + "*lo", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(!mgr + .test_apply_one_rule_token( + "ENV", + "xxx", + "==", + "xxx", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + device.borrow().add_tag("xxx", true); + assert!(mgr + .test_apply_one_rule_token( + "TAG", + "", + "==", + "xxx", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "SUBSYSTEM", + "", + "==", + "net", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "TEST", + "444", + "==", + "[net/lo]ifindex", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "TEST", + "644", + "==", + "queues/*/rps_cpus", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "TEST", + "444", + "==", + "ifindex", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "TEST", + "", + "!=", + "asfsdfa", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(!mgr + .test_apply_one_rule_token( + "TEST", + "444", + "==", + "$attr", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + create_tmp_file( + "/tmp", + "property", + "HELLO=WORLD +GOOD=LUCK", + true, + ); + + assert!(mgr + .test_apply_one_rule_token( + "IMPORT", + "file", + "==", + "/tmp/property", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert_eq!( + &device.borrow().get_property_value("HELLO").unwrap(), + "WORLD" + ); + + assert_eq!(&device.borrow().get_property_value("GOOD").unwrap(), "LUCK"); + + remove_file("/tmp/property").unwrap(); + + assert!(mgr + .test_apply_one_rule_token( + "IMPORT", + "program", + "==", + "echo WATER=FLOW", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + assert_eq!( + &device.borrow().get_property_value("WATER").unwrap(), + "FLOW" + ); + + create_tmp_file( + "/tmp/devmaster/data", + &device.borrow().get_device_id().unwrap(), + "E:BLACK=PINK", + true, + ); + mgr.current_unit + .borrow() + .as_ref() + .unwrap() + .clone_device_db() + .unwrap(); + assert!(mgr + .test_apply_one_rule_token( + "IMPORT", + "db", + "==", + "BLACK", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + assert_eq!( + &device.borrow().get_property_value("BLACK").unwrap(), + "PINK" + ); + remove_file(&format!( + "/tmp/devmaster/data/{}", + device.borrow().get_device_id().unwrap() + )) + .unwrap(); + + assert!(mgr + .test_apply_one_rule_token( + "IMPORT", + "cmdline", + "==", + "root", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + assert!(!device + .borrow() + .get_property_value("root") + .unwrap() + .is_empty()); + + assert!(mgr + .test_apply_one_rule_token( + "IMPORT", + "cmdline", + "!=", + "asdfasdf", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(!mgr + .test_apply_one_rule_token( + "PROGRAM", + "", + "==", + "echo $attr", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(!mgr + .test_apply_one_rule_token( + "PROGRAM", + "", + "==", + "cat /tmp/test_nonexist", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(!mgr + .test_apply_one_rule_token( + "PROGRAM", + "", + "==", + "asdfasdf", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "PROGRAM", + "", + "==", + "echo hello", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "RESULT", + "", + "==", + "hello", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "OPTIONS", + "", + "+=", + "string_escape=none", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "OPTIONS", + "", + "+=", + "string_escape=replace", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "OPTIONS", + "", + "+=", + "db_persist", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "OPTIONS", + "", + "+=", + "db_persist", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "OPTIONS", + "", + "+=", + "link_priority=1", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "OWNER", + "", + "=", + "0", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "GROUP", + "", + "=", + "0", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "MODE", + "", + "=", + "777", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + rules.write().unwrap().resolve_name_time = ResolveNameTime::Late; + assert!(mgr + .test_apply_one_rule_token( + "OWNER", + "", + "=", + "root", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "GROUP", + "", + "=", + "root", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + mgr.test_apply_one_rule_token( + "ENV", + "mode", + "=", + "777", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap(); + + assert!(mgr + .test_apply_one_rule_token( + "MODE", + "", + "=", + "$env{mode}", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + rules.write().unwrap().resolve_name_time = ResolveNameTime::Early; + + assert!(mgr + .test_apply_one_rule_token( + "OWNER", + "", + ":=", + "0", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "GROUP", + "", + ":=", + "0", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "MODE", + "", + ":=", + "777", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "OWNER", + "", + "=", + "0", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "GROUP", + "", + "=", + "0", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "MODE", + "", + "=", + "777", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "ENV", + "BLACK", + "=", + "YELLOW", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + assert_eq!( + &device.borrow().get_property_value("BLACK").unwrap(), + "YELLOW" + ); + + assert!(mgr + .test_apply_one_rule_token( + "TAG", + "", + "+=", + "aaa", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + assert!(device.borrow().has_tag("aaa").unwrap()); + assert!(device.borrow().has_current_tag("aaa").unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "TAG", + "", + "-=", + "aaa", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + assert!(device.borrow().has_tag("aaa").unwrap()); + assert!(!device.borrow().has_current_tag("aaa").unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "TAG", + "", + "=", + "bbb", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + assert!(!device.borrow().has_tag("aaa").unwrap()); + assert!(!device.borrow().has_current_tag("aaa").unwrap()); + assert!(device.borrow().has_tag("bbb").unwrap()); + assert!(device.borrow().has_current_tag("bbb").unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "NAME", + "", + ":=", + "test", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + assert!(mgr + .test_apply_one_rule_token( + "NAME", + "", + "=", + "test", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + if mgr + .test_apply_one_rule_token( + "ATTR", + "ifalias", + "=", + "test", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .is_ok() + { + assert!(mgr + .test_apply_one_rule_token( + "ATTR", + "ifalias", + "==", + "test", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + } + + assert!(mgr + .test_apply_one_rule_token( + "RUN", + "builtin", + "+=", + "path_id", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "ENV", + "PAPER", + "+=", + "", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "ENV", + "PAPER", + "==", + "", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "ENV", + "PAPER", + "=", + "", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "ENV", + "PAPER", + "==", + "", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "ENV", + "PAPER", + "+=", + "BOOK", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + assert_eq!( + &device.borrow().get_property_value("PAPER").unwrap(), + "BOOK" + ); + + assert!(!mgr + .test_apply_one_rule_token( + "IMPORT", + "builtin", + "==", + "path_id $attr", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "TAG", + "", + "+=", + "$env", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "NAME", + "", + "=", + "$env", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "ATTR", + "ifalias", + "=", + "$env", + rules.clone(), + rule_line.clone(), + device.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "RUN", + "", + "+=", + "$env", + rules.clone(), + rule_line.clone(), + device, + ) + .unwrap()); + + // /* TODO rules */ + // let rules_c = rules.clone(); + // let rule_line_c = rule_line.clone(); + // let device_c = device.clone(); + // assert!(catch_unwind(move || { + // let _ = mgr.test_apply_one_rule_token( + // "CONST", + // "virt", + // "==", + // "xxx", + // rules_c, + // rule_line_c, + // device_c, + // ); + // }) + // .is_err()); + + match LoopDev::new("/tmp/test_apply_rules", 1024 * 1024 * 10) { + Ok(lo) => { + let devpath = lo.get_device_path().unwrap(); + let dev = Rc::new(RefCell::new( + Device::from_path(devpath.to_str().unwrap()).unwrap(), + )); + dev.borrow().set_base_path("/tmp/devmaster"); + + dev.borrow().add_devlink("/dev/test").unwrap(); + assert!(mgr + .test_apply_one_rule_token( + "SYMLINK", + "", + "==", + "/dev/test", + rules.clone(), + rule_line.clone(), + dev.clone(), + ) + .unwrap()); + assert!(mgr + .test_apply_one_rule_token( + "SYMLINK", + "", + "!=", + "/dev/xxx", + rules.clone(), + rule_line.clone(), + dev.clone(), + ) + .unwrap()); + assert!(mgr + .test_apply_one_rule_token( + "SYMLINK", + "", + "+=", + "xxx", + rules.clone(), + rule_line.clone(), + dev.clone(), + ) + .unwrap()); + assert!(dev.borrow().has_devlink("/dev/xxx")); + + assert!(mgr + .test_apply_one_rule_token( + "OPTIONS", + "", + "+=", + "watch", + rules.clone(), + rule_line.clone(), + dev.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token( + "OPTIONS", + "", + ":=", + "watch", + rules.clone(), + rule_line.clone(), + dev.clone(), + ) + .unwrap()); + + assert!(mgr + .test_apply_one_rule_token("OPTIONS", "", "=", "watch", rules, rule_line, dev,) + .unwrap()); + } + Err(e) => { + assert!(e.is_errno(nix::Error::EACCES) || e.is_errno(nix::Error::EBUSY)); + } + } + } } diff --git a/exts/devmaster/src/lib/rules/rules_load.rs b/exts/devmaster/src/lib/rules/rules_load.rs index e9dd2259045a88fdb705e9ffea7d8bd0cc1f1951..b035f56262dc10f641940a1190b08e63afd5ba17 100644 --- a/exts/devmaster/src/lib/rules/rules_load.rs +++ b/exts/devmaster/src/lib/rules/rules_load.rs @@ -1724,7 +1724,7 @@ impl RuleToken { } #[cfg(test)] -mod tests { +pub(crate) mod tests { use basic::fs_util::touch_file; use log::init_log; use log::Level; @@ -1736,14 +1736,24 @@ mod tests { use std::panic::catch_unwind; use std::{fs, path::Path}; - fn create_tmp_rules(dir: &'static str, file: &str, content: &str) { + pub(crate) fn create_tmp_file(dir: &'static str, file: &str, content: &str, truncate: bool) { assert!(fs::create_dir_all(dir).is_ok()); - assert!(fs::write(format!("{}/{}", dir, file), content,).is_ok()); + let s = format!("{}/{}", dir, file); + let p = Path::new(&s); + assert!(fs::write(p, content,).is_ok()); + let mut f = fs::OpenOptions::new() + .write(true) + .truncate(truncate) + .open(p) + .unwrap(); + f.write_all(content.as_bytes()).unwrap(); + f.flush().unwrap(); + while !p.exists() {} } fn clear_tmp_rules(dir: &'static str) { if Path::new(dir).exists() { - assert!(fs::remove_dir_all(dir).is_ok()); + fs::remove_dir_all(dir).unwrap(); } } @@ -1758,7 +1768,7 @@ mod tests { 0, false, ); - clear_tmp_rules("/tmp/devmaster/rules"); + clear_tmp_rules("/tmp/devmaster/test_load_rules"); let legal_rule = vec![ "ACTION == \"change\", SYMLINK += \"test1\"", // Test legal rules. @@ -1810,23 +1820,21 @@ mod tests { "SECLABEL{x}:=\"$hello\"", // Illegal placeholder in value will throw warning rather than panic. ]; - create_tmp_rules("/tmp/devmaster/rules", "00-test.rules", ""); - for &content in legal_rule.iter() { - let mut f = fs::OpenOptions::new() - .write(true) - .truncate(true) - .open("/tmp/devmaster/rules/00-test.rules") - .unwrap(); - f.write_all(content.as_bytes()).unwrap(); + create_tmp_file( + "/tmp/devmaster/test_load_rules", + "00-test.rules", + content, + true, + ); let _ = Rules::load_rules( - vec!["/tmp/devmaster/rules".to_string()], + vec!["/tmp/devmaster/test_load_rules".to_string()], ResolveNameTime::Early, ); } - clear_tmp_rules("/tmp/devmaster/rules"); + clear_tmp_rules("/tmp/devmaster/test_load_rules"); } #[test] @@ -1840,7 +1848,7 @@ mod tests { 0, false, ); - clear_tmp_rules("/tmp/devmaster/rules"); + clear_tmp_rules("/tmp/devmaster/test_load_rules_panic"); let illegal_rule = vec![ "action==\"change\"", // Error in State::Pre @@ -1945,26 +1953,24 @@ mod tests { "XXX=\"xxx\"", // Invalid token key. ]; - create_tmp_rules("/tmp/devmaster/rules", "00-test.rules", ""); - for content in illegal_rule.iter() { - let mut f = fs::OpenOptions::new() - .write(true) - .truncate(true) - .open("/tmp/devmaster/rules/00-test.rules") - .unwrap(); - f.write_all(content.as_bytes()).unwrap(); + create_tmp_file( + "/tmp/devmaster/test_load_rules_panic", + "00-test.rules", + content, + true, + ); assert!(catch_unwind(|| { let _ = Rules::load_rules( - vec!["/tmp/devmaster/rules".to_string()], + vec!["/tmp/devmaster/test_load_rules_panic".to_string()], ResolveNameTime::Early, ); }) .is_err()); } - clear_tmp_rules("/tmp/devmaster/rules"); + clear_tmp_rules("/tmp/devmaster/test_load_rules_panic"); } #[test] @@ -1978,50 +1984,48 @@ mod tests { 0, false, ); - clear_tmp_rules("/tmp/devmaster/rules"); + clear_tmp_rules("/tmp/devmaster/test_resolve_name_time"); let legal = vec!["OWNER=\"root\"", "GROUP=\"root\""]; let illegal = vec!["OWNER=\"xxxx\"", "GROUP=\"xxxx\""]; - create_tmp_rules("/tmp/devmaster/rules", "00-test.rules", ""); - for &content in legal.iter() { - let mut f = fs::OpenOptions::new() - .write(true) - .truncate(true) - .open("/tmp/devmaster/rules/00-test.rules") - .unwrap(); - f.write_all(content.as_bytes()).unwrap(); + create_tmp_file( + "/tmp/devmaster/test_resolve_name_time", + "00-test.rules", + content, + true, + ); let _ = Rules::load_rules( - vec!["/tmp/devmaster/rules".to_string()], + vec!["/tmp/devmaster/test_resolve_name_time".to_string()], ResolveNameTime::Early, ); } for &content in illegal.iter() { - let mut f = fs::OpenOptions::new() - .write(true) - .truncate(true) - .open("/tmp/devmaster/rules/00-test.rules") - .unwrap(); - f.write_all(content.as_bytes()).unwrap(); + create_tmp_file( + "/tmp/devmaster/test_resolve_name_time", + "00-test.rules", + content, + true, + ); let _ = Rules::load_rules( - vec!["/tmp/devmaster/rules".to_string()], + vec!["/tmp/devmaster/test_resolve_name_time".to_string()], ResolveNameTime::Late, ); assert!(catch_unwind(|| { let _ = Rules::load_rules( - vec!["/tmp/devmaster/rules".to_string()], + vec!["/tmp/devmaster/test_resolve_name_time".to_string()], ResolveNameTime::Early, ); }) .is_err()); } - clear_tmp_rules("/tmp/devmaster/rules"); + clear_tmp_rules("/tmp/devmaster/test_resolve_name_time"); } #[test] @@ -2330,11 +2334,11 @@ mod tests { #[test] fn test_parse_rules() { - create_dir_all("/tmp/devmaster/rules").unwrap(); + create_dir_all("/tmp/devmaster/test_parse_rules").unwrap(); /* Normal rule file. */ touch_file( - "/tmp/devmaster/rules/00-a.rules", + "/tmp/devmaster/test_parse_rules/00-a.rules", false, Some(0o777), None, @@ -2342,10 +2346,17 @@ mod tests { ) .unwrap(); /* Skip parsing the file with invalid suffix. */ - touch_file("/tmp/devmaster/rules/01-b", false, Some(0o777), None, None).unwrap(); + touch_file( + "/tmp/devmaster/test_parse_rules/01-b", + false, + Some(0o777), + None, + None, + ) + .unwrap(); /* Failed to parse the file as it is not readable. */ touch_file( - "/tmp/devmaster/rules/02-c.rules", + "/tmp/devmaster/test_parse_rules/02-c.rules", false, Some(0o000), None, @@ -2354,21 +2365,25 @@ mod tests { .unwrap(); let rules = Arc::new(RwLock::new(Rules::new( - vec!["/tmp/devmaster/rules".to_string()], + vec!["/tmp/devmaster/test_parse_rules".to_string()], ResolveNameTime::Never, ))); - // Rules::parse_rules(Arc::new(RwLock::new(rules))); - - RuleFile::load_file("/tmp/devmaster/rules/00-a.rules".to_string(), rules.clone()); + RuleFile::load_file( + "/tmp/devmaster/test_parse_rules/00-a.rules".to_string(), + rules.clone(), + ); if nix::unistd::getuid().as_raw() != 0 { assert!(catch_unwind(|| { - RuleFile::load_file("/tmp/devmaster/rules/02-c.rules".to_string(), rules.clone()); + RuleFile::load_file( + "/tmp/devmaster/test_parse_rules/02-c.rules".to_string(), + rules.clone(), + ); }) .is_err()); } - remove_dir_all("/tmp/devmaster").unwrap(); + remove_dir_all("/tmp/devmaster/test_parse_rules").unwrap(); } } diff --git a/libs/device/Cargo.toml b/libs/device/Cargo.toml index ecb9955c80efda38c332b5e849a03f299bd01654..1737c17b75b0a08072e6df0f561d36097adcb97c 100644 --- a/libs/device/Cargo.toml +++ b/libs/device/Cargo.toml @@ -20,8 +20,7 @@ log = { path = "../log" } # third libraries bitflags = "1.3.2" libc = { default-features = false, version = "0.2.140" } -# only used in test case -loopdev = "0.4.0" +loopdev = { version = "0.4.0", optional = true } # only used in test case nix = { default-features = false, version = "0.24", features = [ "ioctl", "user", @@ -32,3 +31,6 @@ nix = { default-features = false, version = "0.24", features = [ ] } snafu = { default-features = false, version = "0.7" } fnmatch-sys = "1.0.0" + +[dev-dependencies] +device = { path = ".", features = ["loopdev"] } diff --git a/libs/device/src/device.rs b/libs/device/src/device.rs index 70070c72d2804a2332e54cac0207cbb8b7a9e437..e8e2dcc5b7d85e44d73b611bbc5ec5ce05a6eb11 100644 --- a/libs/device/src/device.rs +++ b/libs/device/src/device.rs @@ -2390,6 +2390,8 @@ impl Device { pub fn shallow_clone(&self) -> Result { let device = Self::default(); + device.set_base_path(self.base_path.borrow().as_str()); + let syspath = self.get_syspath()?; device.set_syspath(&syspath, false)?; @@ -2843,16 +2845,17 @@ impl PartialEq for Device { #[cfg(test)] mod tests { - use std::fs::OpenOptions; - use std::panic::catch_unwind; - use crate::{ device::*, device_enumerator::{DeviceEnumerationType, DeviceEnumerator}, - utils::LoopDev, }; use basic::IN_SET; use libc::S_IFBLK; + use std::fs::OpenOptions; + use std::panic::catch_unwind; + + #[cfg(feature = "loopdev")] + use crate::utils::LoopDev; fn compare(dev1: &Device, dev2: &Device) -> bool { let syspath_1 = dev1.get_syspath().unwrap(); diff --git a/libs/device/src/utils.rs b/libs/device/src/utils.rs index df750edf1e6d869cf038d6fe4825962d75dfc5c7..2b814fa8cc48f15bdf27bb82521d71b2607bedd0 100644 --- a/libs/device/src/utils.rs +++ b/libs/device/src/utils.rs @@ -11,12 +11,14 @@ // See the Mulan PSL v2 for more details. //! utilities for device operation +use crate::{error::*, Device}; use nix::errno::Errno; +use std::{cmp::Ordering, fmt::Debug, fs::DirEntry, path::Path}; -use crate::{error::*, Device}; +#[cfg(feature = "loopdev")] use loopdev::*; +#[cfg(feature = "loopdev")] use std::path::PathBuf; -use std::{cmp::Ordering, fmt::Debug, fs::DirEntry, path::Path}; /// compare sound device pub(crate) fn sound_device_compare(devpath_a: &str, devpath_b: &str) -> Ordering { @@ -106,11 +108,13 @@ pub(crate) fn readlink_value + Debug>(path: P) -> Result