starnix_core/vfs/
fs_context.rs1use crate::security;
6use crate::task::{CurrentTask, MountsWriteToken};
7use crate::vfs::{AccessCheck, ActiveNamespaceNode, 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::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, AccessCheck::for_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, AccessCheck::for_chroot()).map_err(|_| errno!(EACCES))?;
119 security::check_task_capable(current_task, CAP_SYS_CHROOT)?;
120
121 let mut state = self.state.write();
122 state.root = name.into_active();
123 Ok(())
124 }
125
126 pub fn umask(&self) -> FileMode {
127 self.state.read().umask
128 }
129
130 pub fn apply_umask(&self, mode: FileMode) -> FileMode {
131 let umask = self.state.read().umask;
132 mode & !umask
133 }
134
135 pub fn set_umask(&self, umask: FileMode) -> FileMode {
136 let mut state = self.state.write();
137 let old_umask = state.umask;
138
139 state.umask = umask & FileMode::from_bits(0o777);
145
146 old_umask
147 }
148
149 pub fn set_namespace(&self, new_ns: Arc<Namespace>) -> Result<(), Errno> {
150 let mut state = self.state.write();
151 let kernel = state.namespace.kernel();
152 let mounts_guard = kernel.mounts_lock();
153 state.set_namespace(new_ns, &mounts_guard)?;
154 Ok(())
155 }
156
157 pub fn unshare_namespace(&self) {
158 let mut state = self.state.write();
159 let kernel = state.namespace.kernel();
160 let mounts_guard = kernel.mounts_lock();
161
162 let cloned = state.namespace.clone_namespace(&mounts_guard);
163 state
164 .set_namespace(cloned, &mounts_guard)
165 .expect("nodes should exist in the cloned namespace");
166 }
167
168 pub fn namespace(&self) -> Arc<Namespace> {
169 Arc::clone(&self.state.read().namespace)
170 }
171
172 pub fn update_root_after_pivot(&self, old_root: &NamespaceNode, new_root: &NamespaceNode) {
173 let mut state = self.state.write();
174 if state.root.to_passive() == *old_root {
175 state.root = new_root.clone().into_active();
176 }
177 if state.cwd.to_passive() == *old_root {
178 state.cwd = new_root.clone().into_active();
179 }
180 }
181}
182
183#[cfg(test)]
184mod test {
185 use crate::fs::tmpfs::TmpFs;
186 use crate::testing::{spawn_kernel_and_run, spawn_kernel_and_run_with_pkgfs};
187 use crate::vfs::{FsContext, Namespace};
188 use starnix_uapi::file_mode::FileMode;
189 use starnix_uapi::open_flags::OpenFlags;
190
191 #[::fuchsia::test]
192 async fn test_umask() {
193 spawn_kernel_and_run(async |current_task| {
194 let kernel = current_task.kernel();
195 let fs = FsContext::new(Namespace::new(TmpFs::new_fs(&kernel)));
196
197 assert_eq!(FileMode::from_bits(0o22), fs.set_umask(FileMode::from_bits(0o3020)));
198 assert_eq!(FileMode::from_bits(0o646), fs.apply_umask(FileMode::from_bits(0o666)));
199 assert_eq!(FileMode::from_bits(0o3646), fs.apply_umask(FileMode::from_bits(0o3666)));
200 assert_eq!(FileMode::from_bits(0o20), fs.set_umask(FileMode::from_bits(0o11)));
201 })
202 .await;
203 }
204
205 #[::fuchsia::test]
206 async fn test_chdir() {
207 spawn_kernel_and_run_with_pkgfs(async |current_task| {
208 assert_eq!("/", current_task.fs().cwd().path_escaping_chroot());
209
210 let bin = current_task
211 .open_file("bin".into(), OpenFlags::RDONLY)
212 .expect("missing bin directory");
213 current_task.fs().chdir(¤t_task, bin.name.to_passive()).expect("Failed to chdir");
214 assert_eq!("/bin", current_task.fs().cwd().path_escaping_chroot());
215
216 assert!(current_task.open_file("bin".into(), OpenFlags::RDONLY).is_err());
219
220 assert!(current_task.open_file("/bin".into(), OpenFlags::RDONLY).is_ok());
222
223 let previous_directory = current_task
224 .open_file("..".into(), OpenFlags::RDONLY)
225 .expect("failed to open ..")
226 .name
227 .to_passive();
228 current_task.fs().chdir(¤t_task, previous_directory).expect("Failed to chdir");
229 assert_eq!("/", current_task.fs().cwd().path_escaping_chroot());
230
231 assert!(current_task.open_file("bin".into(), OpenFlags::RDONLY).is_ok());
233
234 let previous_directory = current_task
236 .open_file("..".into(), OpenFlags::RDONLY)
237 .expect("failed to open ..")
238 .name
239 .to_passive();
240 current_task.fs().chdir(¤t_task, previous_directory).expect("Failed to chdir");
241 assert_eq!("/", current_task.fs().cwd().path_escaping_chroot());
242 assert!(current_task.open_file("bin".into(), OpenFlags::RDONLY).is_ok());
243 })
244 .await;
245 }
246}