starnix_core/vfs/
fs_context.rs1use crate::security;
6use crate::task::{CurrentTask, MountsWriteToken};
7use crate::vfs::{ActiveNamespaceNode, CheckAccessReason, Namespace, NamespaceNode};
8use starnix_logging::log_trace;
9use starnix_sync::LockDepRwLock;
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(
41 &mut self,
42 new_ns: Arc<Namespace>,
43 mounts_guard: &MountsWriteToken,
44 ) -> Result<(), Errno> {
45 log_trace!("updating namespace");
46 let new_root = Namespace::translate_node(self.root.to_passive(), &new_ns, mounts_guard)
47 .ok_or_else(|| errno!(EINVAL))?;
48 let new_cwd = Namespace::translate_node(self.cwd.to_passive(), &new_ns, mounts_guard)
49 .ok_or_else(|| errno!(EINVAL))?;
50
51 mounts_guard.defer_drop(std::mem::replace(&mut self.root, new_root.into_active()));
53 mounts_guard.defer_drop(std::mem::replace(&mut self.cwd, new_cwd.into_active()));
54 mounts_guard.defer_drop(std::mem::replace(&mut self.namespace, new_ns));
55 log_trace!("namespace update succeeded");
56 Ok(())
57 }
58}
59
60#[derive(Debug)]
65pub struct FsContext {
66 state: LockDepRwLock<FsContextState, starnix_sync::FsContextStateLock>,
67}
68
69impl FsContext {
70 pub fn new(namespace: Arc<Namespace>) -> Arc<FsContext> {
75 let root = namespace.root();
76 Arc::new(FsContext {
77 state: FsContextState {
78 namespace,
79 root: root.clone().into_active(),
80 cwd: root.into_active(),
81 umask: FileMode::DEFAULT_UMASK,
82 }
83 .into(),
84 })
85 }
86
87 pub fn fork(&self) -> Arc<FsContext> {
88 Arc::new(FsContext { state: self.state.read().clone().into() })
94 }
95
96 pub fn cwd(&self) -> NamespaceNode {
98 let state = self.state.read();
99 state.cwd.to_passive()
100 }
101
102 pub fn root(&self) -> NamespaceNode {
104 let state = self.state.read();
105 state.root.to_passive()
106 }
107
108 pub fn chdir(&self, current_task: &CurrentTask, name: NamespaceNode) -> Result<(), Errno> {
110 name.check_access(current_task, Access::EXEC, CheckAccessReason::Chdir)?;
111 let mut state = self.state.write();
112 state.cwd = name.into_active();
113 Ok(())
114 }
115
116 pub fn chroot(&self, current_task: &CurrentTask, name: NamespaceNode) -> Result<(), Errno> {
118 name.check_access(current_task, Access::EXEC, CheckAccessReason::Chroot)
119 .map_err(|_| errno!(EACCES))?;
120 security::check_task_capable(current_task, CAP_SYS_CHROOT)?;
121
122 let mut state = self.state.write();
123 state.root = name.into_active();
124 Ok(())
125 }
126
127 pub fn umask(&self) -> FileMode {
128 self.state.read().umask
129 }
130
131 pub fn apply_umask(&self, mode: FileMode) -> FileMode {
132 let umask = self.state.read().umask;
133 mode & !umask
134 }
135
136 pub fn set_umask(&self, umask: FileMode) -> FileMode {
137 let mut state = self.state.write();
138 let old_umask = state.umask;
139
140 state.umask = umask & FileMode::from_bits(0o777);
146
147 old_umask
148 }
149
150 pub fn set_namespace(&self, new_ns: Arc<Namespace>) -> Result<(), Errno> {
151 let mut state = self.state.write();
152 let kernel = state.namespace.kernel();
153 let mounts_guard = kernel.mounts_lock();
154 state.set_namespace(new_ns, &mounts_guard)?;
155 Ok(())
156 }
157
158 pub fn unshare_namespace(&self) {
159 let mut state = self.state.write();
160 let kernel = state.namespace.kernel();
161 let mounts_guard = kernel.mounts_lock();
162
163 let cloned = state.namespace.clone_namespace(&mounts_guard);
164 state
165 .set_namespace(cloned, &mounts_guard)
166 .expect("nodes should exist in the cloned namespace");
167 }
168
169 pub fn namespace(&self) -> Arc<Namespace> {
170 Arc::clone(&self.state.read().namespace)
171 }
172}
173
174#[cfg(test)]
175mod test {
176 use crate::fs::tmpfs::TmpFs;
177 use crate::testing::{spawn_kernel_and_run, spawn_kernel_and_run_with_pkgfs};
178 use crate::vfs::{FsContext, Namespace};
179 use starnix_uapi::file_mode::FileMode;
180 use starnix_uapi::open_flags::OpenFlags;
181
182 #[::fuchsia::test]
183 async fn test_umask() {
184 spawn_kernel_and_run(async |current_task| {
185 let kernel = current_task.kernel();
186 let fs = FsContext::new(Namespace::new(TmpFs::new_fs(&kernel)));
187
188 assert_eq!(FileMode::from_bits(0o22), fs.set_umask(FileMode::from_bits(0o3020)));
189 assert_eq!(FileMode::from_bits(0o646), fs.apply_umask(FileMode::from_bits(0o666)));
190 assert_eq!(FileMode::from_bits(0o3646), fs.apply_umask(FileMode::from_bits(0o3666)));
191 assert_eq!(FileMode::from_bits(0o20), fs.set_umask(FileMode::from_bits(0o11)));
192 })
193 .await;
194 }
195
196 #[::fuchsia::test]
197 async fn test_chdir() {
198 spawn_kernel_and_run_with_pkgfs(async |current_task| {
199 assert_eq!("/", current_task.fs().cwd().path_escaping_chroot());
200
201 let bin = current_task
202 .open_file("bin".into(), OpenFlags::RDONLY)
203 .expect("missing bin directory");
204 current_task.fs().chdir(¤t_task, bin.name.to_passive()).expect("Failed to chdir");
205 assert_eq!("/bin", current_task.fs().cwd().path_escaping_chroot());
206
207 assert!(current_task.open_file("bin".into(), OpenFlags::RDONLY).is_err());
210
211 assert!(current_task.open_file("/bin".into(), OpenFlags::RDONLY).is_ok());
213
214 let previous_directory = current_task
215 .open_file("..".into(), OpenFlags::RDONLY)
216 .expect("failed to open ..")
217 .name
218 .to_passive();
219 current_task.fs().chdir(¤t_task, previous_directory).expect("Failed to chdir");
220 assert_eq!("/", current_task.fs().cwd().path_escaping_chroot());
221
222 assert!(current_task.open_file("bin".into(), OpenFlags::RDONLY).is_ok());
224
225 let previous_directory = current_task
227 .open_file("..".into(), OpenFlags::RDONLY)
228 .expect("failed to open ..")
229 .name
230 .to_passive();
231 current_task.fs().chdir(¤t_task, previous_directory).expect("Failed to chdir");
232 assert_eq!("/", current_task.fs().cwd().path_escaping_chroot());
233 assert!(current_task.open_file("bin".into(), OpenFlags::RDONLY).is_ok());
234 })
235 .await;
236 }
237}