secure_exec_kernel/
command_registry.rs1use crate::vfs::{VfsError, VfsResult, VirtualFileSystem};
2use std::collections::BTreeMap;
3
4const COMMAND_STUB: &[u8] = b"#!/bin/sh\n# kernel command stub\n";
5
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct CommandDriver {
8 name: String,
9 commands: Vec<String>,
10}
11
12impl CommandDriver {
13 pub fn new<N, I, C>(name: N, commands: I) -> Self
14 where
15 N: Into<String>,
16 I: IntoIterator<Item = C>,
17 C: Into<String>,
18 {
19 Self {
20 name: name.into(),
21 commands: commands.into_iter().map(Into::into).collect(),
22 }
23 }
24
25 pub fn name(&self) -> &str {
26 &self.name
27 }
28
29 pub fn commands(&self) -> &[String] {
30 &self.commands
31 }
32
33 fn validate_commands(&self) -> VfsResult<()> {
34 for command in &self.commands {
35 validate_command_name(command)?;
36 }
37
38 Ok(())
39 }
40}
41
42#[derive(Debug, Default, Clone)]
43pub struct CommandRegistry {
44 commands: BTreeMap<String, CommandDriver>,
45 warnings: Vec<String>,
46}
47
48impl CommandRegistry {
49 pub fn new() -> Self {
50 Self::default()
51 }
52
53 pub fn register(&mut self, driver: CommandDriver) -> VfsResult<()> {
54 driver.validate_commands()?;
55
56 for command in &driver.commands {
57 if let Some(existing) = self.commands.get(command) {
58 self.warnings.push(format!(
59 "command \"{command}\" overridden: {} -> {}",
60 existing.name(),
61 driver.name()
62 ));
63 }
64
65 self.commands.insert(command.clone(), driver.clone());
66 }
67
68 Ok(())
69 }
70
71 pub fn warnings(&self) -> &[String] {
72 &self.warnings
73 }
74
75 pub fn resolve(&self, command: &str) -> Option<&CommandDriver> {
76 self.commands.get(command)
77 }
78
79 pub fn list(&self) -> BTreeMap<String, String> {
80 self.commands
81 .iter()
82 .map(|(command, driver)| (command.clone(), driver.name().to_owned()))
83 .collect()
84 }
85
86 pub fn populate_bin<F>(&self, vfs: &mut F) -> VfsResult<()>
87 where
88 F: VirtualFileSystem,
89 {
90 self.populate_commands(vfs, self.commands.keys())
91 }
92
93 pub fn populate_driver_bin<F>(&self, vfs: &mut F, driver: &CommandDriver) -> VfsResult<()>
94 where
95 F: VirtualFileSystem,
96 {
97 self.populate_commands(vfs, driver.commands())
98 }
99
100 fn populate_commands<F, I, S>(&self, vfs: &mut F, commands: I) -> VfsResult<()>
101 where
102 F: VirtualFileSystem,
103 I: IntoIterator<Item = S>,
104 S: AsRef<str>,
105 {
106 let commands = commands
107 .into_iter()
108 .map(|command| {
109 validate_command_name(command.as_ref())?;
110 Ok(command.as_ref().to_owned())
111 })
112 .collect::<VfsResult<Vec<_>>>()?;
113
114 if !vfs.exists("/bin") {
115 vfs.mkdir("/bin", true)?;
116 }
117
118 for command in commands {
119 let path = format!("/bin/{command}");
120 if !vfs.exists(&path) {
121 vfs.write_file(&path, COMMAND_STUB.to_vec())?;
122 let _ = vfs.chmod(&path, 0o755);
123 }
124 }
125
126 Ok(())
127 }
128}
129
130fn validate_command_name(command: &str) -> VfsResult<()> {
131 if command.is_empty()
132 || command == "."
133 || command == ".."
134 || command.contains('/')
135 || command.contains('\0')
136 {
137 return Err(VfsError::new(
138 "EINVAL",
139 format!("invalid command name {command:?}"),
140 ));
141 }
142
143 Ok(())
144}