starnix_core/vfs/
fs_context.rs1use crate::security;
6use crate::task::CurrentTask;
7use crate::vfs::{ActiveNamespaceNode, CheckAccessReason, Namespace, NamespaceNode};
8use starnix_logging::log_trace;
9use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked, RwLock};
10use starnix_uapi::auth::CAP_SYS_CHROOT;
11use starnix_uapi::errno;
12use starnix_uapi::errors::Errno;
13use starnix_uapi::file_mode::{Access, FileMode};
14use std::sync::Arc;
15
16#[derive(Debug, Clone)]
20struct FsContextState {
21 namespace: Arc<Namespace>,
25
26 root: ActiveNamespaceNode,
31
32 cwd: ActiveNamespaceNode,
34
35 umask: FileMode,
37}
38
39impl FsContextState {
40 fn set_namespace(&mut self, new_ns: Arc<Namespace>) -> Result<(), Errno> {
41 log_trace!("updating namespace");
42 let new_root = Namespace::translate_node(self.root.to_passive(), &new_ns)
43 .ok_or_else(|| errno!(EINVAL))?;
44 let new_cwd = Namespace::translate_node(self.cwd.to_passive(), &new_ns)
45 .ok_or_else(|| errno!(EINVAL))?;
46
47 self.root = new_root.into_active();
49 self.cwd = new_cwd.into_active();
50 self.namespace = new_ns;
51 log_trace!("namespace update succeeded");
52 Ok(())
53 }
54}
55
56#[derive(Debug)]
61pub struct FsContext {
62 state: RwLock<FsContextState>,
64}
65
66impl FsContext {
67 pub fn new(namespace: Arc<Namespace>) -> Arc<FsContext> {
72 let root = namespace.root();
73 Arc::new(FsContext {
74 state: RwLock::new(FsContextState {
75 namespace,
76 root: root.clone().into_active(),
77 cwd: root.into_active(),
78 umask: FileMode::DEFAULT_UMASK,
79 }),
80 })
81 }
82
83 pub fn fork(&self) -> Arc<FsContext> {
84 Arc::new(FsContext { state: RwLock::new(self.state.read().clone()) })
90 }
91
92 pub fn cwd(&self) -> NamespaceNode {
94 let state = self.state.read();
95 state.cwd.to_passive()
96 }
97
98 pub fn root(&self) -> NamespaceNode {
100 let state = self.state.read();
101 state.root.to_passive()
102 }
103
104 pub fn chdir<L>(
106 &self,
107 locked: &mut Locked<L>,
108 current_task: &CurrentTask,
109 name: NamespaceNode,
110 ) -> Result<(), Errno>
111 where
112 L: LockEqualOrBefore<FileOpsCore>,
113 {
114 name.check_access(locked, current_task, Access::EXEC, CheckAccessReason::Chdir)?;
115 let mut state = self.state.write();
116 state.cwd = name.into_active();
117 Ok(())
118 }
119
120 pub fn chroot<L>(
122 &self,
123 locked: &mut Locked<L>,
124 current_task: &CurrentTask,
125 name: NamespaceNode,
126 ) -> Result<(), Errno>
127 where
128 L: LockEqualOrBefore<FileOpsCore>,
129 {
130 name.check_access(locked, current_task, Access::EXEC, CheckAccessReason::Chroot)
131 .map_err(|_| errno!(EACCES))?;
132 security::check_task_capable(current_task, CAP_SYS_CHROOT)?;
133
134 let mut state = self.state.write();
135 state.root = name.into_active();
136 Ok(())
137 }
138
139 pub fn umask(&self) -> FileMode {
140 self.state.read().umask
141 }
142
143 pub fn apply_umask(&self, mode: FileMode) -> FileMode {
144 let umask = self.state.read().umask;
145 mode & !umask
146 }
147
148 pub fn set_umask(&self, umask: FileMode) -> FileMode {
149 let mut state = self.state.write();
150 let old_umask = state.umask;
151
152 state.umask = umask & FileMode::from_bits(0o777);
158
159 old_umask
160 }
161
162 pub fn set_namespace(&self, new_ns: Arc<Namespace>) -> Result<(), Errno> {
163 let mut state = self.state.write();
164 state.set_namespace(new_ns)?;
165 Ok(())
166 }
167
168 pub fn unshare_namespace(&self) {
169 let mut state = self.state.write();
170 let cloned = state.namespace.clone_namespace();
174 state.set_namespace(cloned).expect("nodes should exist in the cloned namespace");
175 }
176
177 pub fn namespace(&self) -> Arc<Namespace> {
178 Arc::clone(&self.state.read().namespace)
179 }
180}
181
182#[cfg(test)]
183mod test {
184 use crate::fs::tmpfs::TmpFs;
185 use crate::testing::{spawn_kernel_and_run, spawn_kernel_and_run_with_pkgfs};
186 use crate::vfs::{FsContext, Namespace};
187 use starnix_uapi::file_mode::FileMode;
188 use starnix_uapi::open_flags::OpenFlags;
189
190 #[::fuchsia::test]
191 async fn test_umask() {
192 spawn_kernel_and_run(async |locked, current_task| {
193 let kernel = current_task.kernel();
194 let fs = FsContext::new(Namespace::new(TmpFs::new_fs(locked, &kernel)));
195
196 assert_eq!(FileMode::from_bits(0o22), fs.set_umask(FileMode::from_bits(0o3020)));
197 assert_eq!(FileMode::from_bits(0o646), fs.apply_umask(FileMode::from_bits(0o666)));
198 assert_eq!(FileMode::from_bits(0o3646), fs.apply_umask(FileMode::from_bits(0o3666)));
199 assert_eq!(FileMode::from_bits(0o20), fs.set_umask(FileMode::from_bits(0o11)));
200 })
201 .await;
202 }
203
204 #[::fuchsia::test]
205 async fn test_chdir() {
206 spawn_kernel_and_run_with_pkgfs(async |locked, current_task| {
207 assert_eq!("/", current_task.fs().cwd().path_escaping_chroot());
208
209 let bin = current_task
210 .open_file(locked, "bin".into(), OpenFlags::RDONLY)
211 .expect("missing bin directory");
212 current_task
213 .fs()
214 .chdir(locked, ¤t_task, bin.name.to_passive())
215 .expect("Failed to chdir");
216 assert_eq!("/bin", current_task.fs().cwd().path_escaping_chroot());
217
218 assert!(current_task.open_file(locked, "bin".into(), OpenFlags::RDONLY).is_err());
221
222 assert!(current_task.open_file(locked, "/bin".into(), OpenFlags::RDONLY).is_ok());
224
225 let previous_directory = current_task
226 .open_file(locked, "..".into(), OpenFlags::RDONLY)
227 .expect("failed to open ..")
228 .name
229 .to_passive();
230 current_task
231 .fs()
232 .chdir(locked, ¤t_task, previous_directory)
233 .expect("Failed to chdir");
234 assert_eq!("/", current_task.fs().cwd().path_escaping_chroot());
235
236 assert!(current_task.open_file(locked, "bin".into(), OpenFlags::RDONLY).is_ok());
238
239 let previous_directory = current_task
241 .open_file(locked, "..".into(), OpenFlags::RDONLY)
242 .expect("failed to open ..")
243 .name
244 .to_passive();
245 current_task
246 .fs()
247 .chdir(locked, ¤t_task, previous_directory)
248 .expect("Failed to chdir");
249 assert_eq!("/", current_task.fs().cwd().path_escaping_chroot());
250 assert!(current_task.open_file(locked, "bin".into(), OpenFlags::RDONLY).is_ok());
251 })
252 .await;
253 }
254}