starnix_core/vfs/
fs_context.rs1use crate::security;
6use crate::task::{CurrentTask, MountsWriteToken};
7use crate::vfs::{AccessCheck, ActiveNamespaceNode, Namespace, NamespaceNode};
8use fuchsia_rcu::RcuArc;
9use starnix_logging::log_trace;
10use starnix_sync::LockDepRwLock;
11use starnix_uapi::auth::CAP_SYS_CHROOT;
12use starnix_uapi::errno;
13use starnix_uapi::errors::Errno;
14use starnix_uapi::file_mode::FileMode;
15use std::sync::Arc;
16use std::sync::atomic::{AtomicUsize, Ordering};
17
18#[derive(Debug, Clone)]
22struct FsContextState {
23 namespace: Arc<Namespace>,
27
28 root: ActiveNamespaceNode,
33
34 cwd: ActiveNamespaceNode,
36
37 umask: FileMode,
39}
40
41impl FsContextState {
42 fn set_namespace(
43 &mut self,
44 new_ns: Arc<Namespace>,
45 mounts_guard: &MountsWriteToken,
46 ) -> Result<(), Errno> {
47 log_trace!("updating namespace");
48 let new_root = Namespace::translate_node(self.root.to_passive(), &new_ns, mounts_guard)
49 .ok_or_else(|| errno!(EINVAL))?;
50 let new_cwd = Namespace::translate_node(self.cwd.to_passive(), &new_ns, mounts_guard)
51 .ok_or_else(|| errno!(EINVAL))?;
52
53 mounts_guard.defer_drop(std::mem::replace(&mut self.root, new_root.into_active()));
55 mounts_guard.defer_drop(std::mem::replace(&mut self.cwd, new_cwd.into_active()));
56 mounts_guard.defer_drop(std::mem::replace(&mut self.namespace, new_ns));
57 log_trace!("namespace update succeeded");
58 Ok(())
59 }
60}
61
62#[derive(Debug)]
67pub struct FsContext {
68 state: LockDepRwLock<FsContextState, starnix_sync::FsContextStateLock>,
69
70 share_count: AtomicUsize,
75}
76
77#[derive(Debug)]
79pub struct SharedFsContext(RcuArc<FsContext>);
80
81impl SharedFsContext {
82 pub fn new(fs: Arc<FsContext>) -> Self {
84 fs.share_count.fetch_add(1, Ordering::Relaxed);
85 Self(RcuArc::new(Some(fs)))
86 }
87
88 pub fn upgrade(&self) -> Option<Arc<FsContext>> {
90 self.0.upgrade()
91 }
92
93 pub fn update(&self, current_task: &CurrentTask, fs: impl Into<Option<Arc<FsContext>>>) {
95 debug_assert!(std::ptr::eq(¤t_task.running_state().fs, self));
98 self.set(fs.into());
99 }
100
101 fn set(&self, fs: Option<Arc<FsContext>>) {
102 if let Some(ref fs) = fs {
103 fs.share_count.fetch_add(1, Ordering::Relaxed);
104 }
105 let old_fs = self.0.upgrade();
106 self.0.update(fs);
107 if let Some(old_fs) = old_fs {
108 let previous = old_fs.share_count.fetch_sub(1, Ordering::AcqRel);
109 debug_assert!(previous >= 1);
110 }
111 }
112}
113
114impl Drop for SharedFsContext {
115 fn drop(&mut self) {
116 self.set(None);
117 }
118}
119
120impl FsContext {
121 pub fn new(namespace: Arc<Namespace>) -> Arc<FsContext> {
126 let root = namespace.root();
127 Arc::new(FsContext {
128 state: FsContextState {
129 namespace,
130 root: root.clone().into_active(),
131 cwd: root.into_active(),
132 umask: FileMode::DEFAULT_UMASK,
133 }
134 .into(),
135 share_count: AtomicUsize::new(0),
136 })
137 }
138
139 pub fn fork(&self) -> Arc<FsContext> {
140 Arc::new(FsContext {
146 state: self.state.read().clone().into(),
147 share_count: AtomicUsize::new(0),
148 })
149 }
150
151 pub fn is_shared(&self) -> bool {
153 self.share_count.load(Ordering::Acquire) > 1
154 }
155
156 pub fn cwd(&self) -> NamespaceNode {
158 let state = self.state.read();
159 state.cwd.to_passive()
160 }
161
162 pub fn root(&self) -> NamespaceNode {
164 let state = self.state.read();
165 state.root.to_passive()
166 }
167
168 pub fn chdir(&self, current_task: &CurrentTask, name: NamespaceNode) -> Result<(), Errno> {
170 name.check_access(current_task, AccessCheck::for_chdir())?;
171 let mut state = self.state.write();
172 state.cwd = name.into_active();
173 Ok(())
174 }
175
176 pub fn chroot(&self, current_task: &CurrentTask, name: NamespaceNode) -> Result<(), Errno> {
178 name.check_access(current_task, AccessCheck::for_chroot()).map_err(|_| errno!(EACCES))?;
179 security::check_task_capable(current_task, CAP_SYS_CHROOT)?;
180
181 let mut state = self.state.write();
182 state.root = name.into_active();
183 Ok(())
184 }
185
186 pub fn umask(&self) -> FileMode {
187 self.state.read().umask
188 }
189
190 pub fn apply_umask(&self, mode: FileMode) -> FileMode {
191 let umask = self.state.read().umask;
192 mode & !umask
193 }
194
195 pub fn set_umask(&self, umask: FileMode) -> FileMode {
196 let mut state = self.state.write();
197 let old_umask = state.umask;
198
199 state.umask = umask & FileMode::from_bits(0o777);
205
206 old_umask
207 }
208
209 pub fn set_namespace(&self, new_ns: Arc<Namespace>) -> Result<(), Errno> {
210 let mut state = self.state.write();
211 let kernel = state.namespace.kernel();
212 let mounts_guard = kernel.mounts_lock();
213 state.set_namespace(new_ns, &mounts_guard)?;
214 Ok(())
215 }
216
217 pub fn replace_bootstrap_namespace(&self, current_task: &CurrentTask, new_ns: Arc<Namespace>) {
225 let mut state = self.state.write();
226 let root = new_ns.root();
227 let old_root = std::mem::replace(&mut state.root, root.clone().into_active());
228 let old_cwd = std::mem::replace(&mut state.cwd, root.into_active());
229 let old_ns = std::mem::replace(&mut state.namespace, new_ns);
230 drop(state);
231 drop((old_root, old_cwd));
232 old_ns.destroy_bootstrap();
233 current_task.trigger_delayed_releaser();
234 }
235
236 pub fn unshare_namespace(&self) {
237 let mut state = self.state.write();
238 let kernel = state.namespace.kernel();
239 let mounts_guard = kernel.mounts_lock();
240
241 let cloned = state.namespace.clone_namespace(&mounts_guard);
242 state
243 .set_namespace(cloned, &mounts_guard)
244 .expect("nodes should exist in the cloned namespace");
245 }
246
247 pub fn namespace(&self) -> Arc<Namespace> {
248 Arc::clone(&self.state.read().namespace)
249 }
250
251 pub fn update_root_after_pivot(&self, old_root: &NamespaceNode, new_root: &NamespaceNode) {
252 let mut state = self.state.write();
253 if state.root.to_passive() == *old_root {
254 state.root = new_root.clone().into_active();
255 }
256 if state.cwd.to_passive() == *old_root {
257 state.cwd = new_root.clone().into_active();
258 }
259 }
260}
261
262#[cfg(test)]
263mod test {
264 use crate::fs::tmpfs::TmpFs;
265 use crate::testing::{spawn_kernel_and_run, spawn_kernel_and_run_with_pkgfs};
266 use crate::vfs::{FsContext, Namespace};
267 use starnix_uapi::file_mode::FileMode;
268 use starnix_uapi::open_flags::OpenFlags;
269
270 #[::fuchsia::test]
271 async fn test_umask() {
272 spawn_kernel_and_run(async |current_task| {
273 let kernel = current_task.kernel();
274 let fs = FsContext::new(Namespace::new(TmpFs::new_fs(&kernel)));
275
276 assert_eq!(FileMode::from_bits(0o22), fs.set_umask(FileMode::from_bits(0o3020)));
277 assert_eq!(FileMode::from_bits(0o646), fs.apply_umask(FileMode::from_bits(0o666)));
278 assert_eq!(FileMode::from_bits(0o3646), fs.apply_umask(FileMode::from_bits(0o3666)));
279 assert_eq!(FileMode::from_bits(0o20), fs.set_umask(FileMode::from_bits(0o11)));
280 })
281 .await;
282 }
283
284 #[::fuchsia::test]
285 async fn test_chdir() {
286 spawn_kernel_and_run_with_pkgfs(async |current_task| {
287 assert_eq!("/", current_task.fs().cwd().path_escaping_chroot());
288
289 let bin = current_task
290 .open_file("bin".into(), OpenFlags::RDONLY)
291 .expect("missing bin directory");
292 current_task.fs().chdir(¤t_task, bin.name.to_passive()).expect("Failed to chdir");
293 assert_eq!("/bin", current_task.fs().cwd().path_escaping_chroot());
294
295 assert!(current_task.open_file("bin".into(), OpenFlags::RDONLY).is_err());
298
299 assert!(current_task.open_file("/bin".into(), OpenFlags::RDONLY).is_ok());
301
302 let previous_directory = current_task
303 .open_file("..".into(), OpenFlags::RDONLY)
304 .expect("failed to open ..")
305 .name
306 .to_passive();
307 current_task.fs().chdir(¤t_task, previous_directory).expect("Failed to chdir");
308 assert_eq!("/", current_task.fs().cwd().path_escaping_chroot());
309
310 assert!(current_task.open_file("bin".into(), OpenFlags::RDONLY).is_ok());
312
313 let previous_directory = current_task
315 .open_file("..".into(), OpenFlags::RDONLY)
316 .expect("failed to open ..")
317 .name
318 .to_passive();
319 current_task.fs().chdir(¤t_task, previous_directory).expect("Failed to chdir");
320 assert_eq!("/", current_task.fs().cwd().path_escaping_chroot());
321 assert!(current_task.open_file("bin".into(), OpenFlags::RDONLY).is_ok());
322 })
323 .await;
324 }
325}