1use crate::mm::PAGE_SIZE;
6use crate::task::{CurrentTask, Kernel};
7use crate::vfs::memory_directory::MemoryDirectoryFile;
8use crate::vfs::{
9 CacheMode, FileOps, FileSystem, FileSystemHandle, FileSystemOps, FileSystemOptions, FsNode,
10 FsNodeHandle, FsNodeInfo, FsNodeOps, FsStr, MemoryRegularNode, MemoryXattrStorage,
11 RenameContext, SymlinkNode, XattrStorage as _, fs_args, fs_node_impl_not_dir,
12 fs_node_impl_xattr_delegate,
13};
14use starnix_logging::{log_warn, track_stub};
15use starnix_types::vfs::default_statfs;
16use starnix_uapi::auth::FsCred;
17use starnix_uapi::device_id::DeviceId;
18use starnix_uapi::errors::Errno;
19use starnix_uapi::file_mode::{FileMode, mode};
20use starnix_uapi::open_flags::OpenFlags;
21use starnix_uapi::seal_flags::SealFlags;
22use starnix_uapi::{TMPFS_MAGIC, error, gid_t, statfs, uid_t};
23use std::sync::Arc;
24use std::sync::atomic::{AtomicU32, Ordering};
25
26pub struct TmpFs {
27 name: &'static FsStr,
28 casefold: bool,
29}
30
31impl FileSystemOps for Arc<TmpFs> {
32 fn statfs(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<statfs, Errno> {
33 Ok(statfs {
34 f_blocks: 0x100000000,
36 f_bavail: 0x100000000,
37 f_bfree: 0x100000000,
38 ..default_statfs(TMPFS_MAGIC)
39 })
40 }
41 fn name(&self) -> &'static FsStr {
42 self.name
43 }
44
45 fn has_casefold_support(&self) -> bool {
46 self.casefold
47 }
48
49 fn rename(
50 &self,
51 _fs: &FileSystem,
52 _current_task: &CurrentTask,
53 context: &mut RenameContext<'_>,
54 _old_name: &FsStr,
55 _new_name: &FsStr,
56 ) -> Result<(), Errno> {
57 fn child_count(node: &FsNodeHandle) -> &AtomicU32 {
58 &node.downcast_ops::<TmpFsDirectory>().unwrap().child_count
63 }
64 let replaced = context.replaced.map(|r| &r.node);
65 let renamed_is_dir = context.renamed_is_dir();
66 let replaced_is_dir = context.replaced_is_dir();
67
68 if let Some(replaced) = replaced {
69 if replaced_is_dir {
70 if child_count(replaced).load(Ordering::Acquire) != 0 {
72 return error!(ENOTEMPTY);
73 }
74 }
75 }
76
77 let has_replaced = replaced.is_some();
78
79 {
83 let old_parent = &context.old_parent().node;
84 let new_parent = &context.new_parent().node;
85 let same_parent = Arc::ptr_eq(old_parent, new_parent);
86
87 if same_parent {
88 if has_replaced {
90 child_count(old_parent).fetch_sub(1, Ordering::Release);
91 }
92 } else {
93 child_count(old_parent).fetch_sub(1, Ordering::Release);
96 if !has_replaced {
97 child_count(new_parent).fetch_add(1, Ordering::Release);
98 }
99 }
100 }
101
102 let (old_parent_info, mut new_parent_info) = context.parent_infos_mut();
105 if let Some(new_info) = new_parent_info.as_deref_mut() {
106 if renamed_is_dir {
107 old_parent_info.link_count -= 1;
108 new_info.link_count += 1;
109 }
110 if replaced_is_dir {
111 new_info.link_count -= 1;
112 }
113 } else if replaced_is_dir {
114 old_parent_info.link_count -= 1;
115 }
116 Ok(())
117 }
118
119 fn exchange(
120 &self,
121 _fs: &FileSystem,
122 _current_task: &CurrentTask,
123 context: &mut RenameContext<'_>,
124 _name1: &FsStr,
125 _name2: &FsStr,
126 ) -> Result<(), Errno> {
127 let is_dir1 = context.renamed_is_dir();
128 let is_dir2 = context.replaced_is_dir();
129 let (parent1_info, mut parent2_info) = context.parent_infos_mut();
130 if let Some(parent2_info) = parent2_info.as_deref_mut() {
131 if is_dir1 != is_dir2 {
132 if is_dir1 {
133 parent1_info.link_count -= 1;
134 parent2_info.link_count += 1;
135 } else {
136 parent1_info.link_count += 1;
137 parent2_info.link_count -= 1;
138 }
139 }
140 }
141
142 Ok(())
143 }
144}
145
146pub fn tmp_fs(
147 current_task: &CurrentTask,
148 options: FileSystemOptions,
149) -> Result<FileSystemHandle, Errno> {
150 TmpFs::new_fs_with_options(¤t_task.kernel(), options)
151}
152
153pub fn register_selinux_test_suite_ramfs(kernel: &Kernel) {
155 let registry = kernel.expando.get::<crate::vfs::fs_registry::FsRegistry>();
156 registry.register(
157 b"ramfs".into(),
158 starnix_uapi::fs_type::FileSystemTypeFlags::empty(),
159 |current_task, options| {
160 TmpFs::new_fs_with_options_and_name(current_task.kernel(), options, "ramfs".into())
161 },
162 );
163}
164
165impl TmpFs {
166 pub fn new_fs(kernel: &Kernel) -> FileSystemHandle {
167 Self::new_fs_with_options(kernel, Default::default()).expect("empty options cannot fail")
168 }
169
170 pub fn new_fs_with_name(kernel: &Kernel, name: &'static FsStr) -> FileSystemHandle {
171 Self::new_fs_with_options_and_name(kernel, Default::default(), name)
172 .expect("empty options cannot fail")
173 }
174
175 pub fn new_fs_with_options(
176 kernel: &Kernel,
177 options: FileSystemOptions,
178 ) -> Result<FileSystemHandle, Errno> {
179 Self::new_fs_with_options_and_name(kernel, options, "tmpfs".into())
180 }
181
182 fn new_fs_with_options_and_name(
183 kernel: &Kernel,
184 options: FileSystemOptions,
185 name: &'static FsStr,
186 ) -> Result<FileSystemHandle, Errno> {
187 let mut mount_options = options.params.clone();
188 let mode = if let Some(mode) = mount_options.remove(b"mode") {
189 FileMode::from_string(mode.as_ref())?
190 } else {
191 mode!(IFDIR, 0o1777)
192 };
193 let uid = if let Some(uid) = mount_options.remove(b"uid") {
194 fs_args::parse::<uid_t>(uid.as_ref())?
195 } else {
196 0
197 };
198 let gid = if let Some(gid) = mount_options.remove(b"gid") {
199 fs_args::parse::<gid_t>(gid.as_ref())?
200 } else {
201 0
202 };
203 let casefold = mount_options.remove(b"casefold").is_some();
204 let fs = FileSystem::new(
205 kernel,
206 CacheMode::Permanent,
207 Arc::new(TmpFs { name, casefold }),
208 options,
209 )?;
210 let root_ino = fs.allocate_ino();
211 let mut info = FsNodeInfo::new(mode!(IFDIR, 0o1777), FsCred { uid, gid });
212 info.chmod(mode);
213 fs.create_root_with_info(root_ino, TmpFsDirectory::new(), info);
214
215 if !mount_options.is_empty() {
216 track_stub!(
217 TODO("https://fxbug.dev/322873419"),
218 "unknown tmpfs options, see logs for strings"
219 );
220 log_warn!("Unknown tmpfs options: {}", mount_options);
221 }
222
223 Ok(fs)
224 }
225}
226
227struct TmpFsDirectory {
228 xattrs: MemoryXattrStorage,
229 child_count: AtomicU32,
235}
236
237impl TmpFsDirectory {
238 fn new() -> Self {
239 Self { xattrs: MemoryXattrStorage::default(), child_count: AtomicU32::new(0) }
240 }
241}
242
243fn create_child_node(
244 parent: &FsNode,
245 mode: FileMode,
246 dev: DeviceId,
247 owner: FsCred,
248) -> Result<FsNodeHandle, Errno> {
249 let ops: Box<dyn FsNodeOps> = match mode.fmt() {
250 FileMode::IFREG => Box::new(MemoryRegularNode::new()?),
251 FileMode::IFIFO | FileMode::IFBLK | FileMode::IFCHR | FileMode::IFSOCK => {
252 Box::new(TmpFsSpecialNode::new())
253 }
254 _ => return error!(EACCES),
255 };
256 let mut info = FsNodeInfo::new(mode, owner);
257 info.rdev = dev;
258 info.blksize = *PAGE_SIZE as usize;
260 let child = parent.fs().create_node_and_allocate_node_id(ops, info);
261 if mode.fmt() == FileMode::IFREG {
262 child.write_guard_state.lock().enable_sealing(SealFlags::SEAL);
264 }
265 Ok(child)
266}
267
268impl FsNodeOps for TmpFsDirectory {
269 fs_node_impl_xattr_delegate!(self, self.xattrs);
270
271 fn create_file_ops(
272 &self,
273 _node: &FsNode,
274 _current_task: &CurrentTask,
275 _flags: OpenFlags,
276 ) -> Result<Box<dyn FileOps>, Errno> {
277 Ok(Box::new(MemoryDirectoryFile::new()))
278 }
279
280 fn mkdir(
281 &self,
282 node: &FsNode,
283 _current_task: &CurrentTask,
284 _name: &FsStr,
285 mode: FileMode,
286 owner: FsCred,
287 ) -> Result<FsNodeHandle, Errno> {
288 node.update_info(|info| {
289 info.link_count += 1;
290 });
291 self.child_count.fetch_add(1, Ordering::Release);
292 let mut info = FsNodeInfo::new(mode, owner);
293 info.casefold = node.info().casefold;
294 Ok(node.fs().create_node_and_allocate_node_id(TmpFsDirectory::new(), info))
295 }
296
297 fn mknod(
298 &self,
299 node: &FsNode,
300 _current_task: &CurrentTask,
301 _name: &FsStr,
302 mode: FileMode,
303 dev: DeviceId,
304 owner: FsCred,
305 ) -> Result<FsNodeHandle, Errno> {
306 let child = create_child_node(node, mode, dev, owner)?;
307 self.child_count.fetch_add(1, Ordering::Release);
308 Ok(child)
309 }
310
311 fn create_symlink(
312 &self,
313 node: &FsNode,
314 _current_task: &CurrentTask,
315 _name: &FsStr,
316 target: &FsStr,
317 owner: FsCred,
318 ) -> Result<FsNodeHandle, Errno> {
319 self.child_count.fetch_add(1, Ordering::Release);
320 let (link, info) = SymlinkNode::new(target, owner);
321 Ok(node.fs().create_node_and_allocate_node_id(link, info))
322 }
323
324 fn create_tmpfile(
325 &self,
326 node: &FsNode,
327 _current_task: &CurrentTask,
328 mode: FileMode,
329 owner: FsCred,
330 ) -> Result<FsNodeHandle, Errno> {
331 assert!(mode.is_reg());
332 create_child_node(node, mode, DeviceId::NONE, owner)
333 }
334
335 fn link(
336 &self,
337 _node: &FsNode,
338 _current_task: &CurrentTask,
339 _name: &FsStr,
340 child: &FsNodeHandle,
341 ) -> Result<(), Errno> {
342 child.update_info(|info| {
343 info.link_count += 1;
344 });
345 self.child_count.fetch_add(1, Ordering::Release);
346 Ok(())
347 }
348
349 fn unlink(
350 &self,
351 node: &FsNode,
352 _current_task: &CurrentTask,
353 _name: &FsStr,
354 child_to_unlink: &FsNodeHandle,
355 ) -> Result<(), Errno> {
356 if child_to_unlink.is_dir() {
357 let child_count =
362 &child_to_unlink.downcast_ops::<TmpFsDirectory>().unwrap().child_count;
363 if child_count.load(Ordering::Acquire) != 0 {
364 return error!(ENOTEMPTY);
365 }
366
367 node.update_info(|info| {
368 info.link_count -= 1;
369 });
370 }
371 child_to_unlink.update_info(|info| {
372 info.link_count -= 1;
373 });
374 self.child_count.fetch_sub(1, Ordering::Release);
375 Ok(())
376 }
377}
378
379struct TmpFsSpecialNode {
380 xattrs: MemoryXattrStorage,
381}
382
383impl TmpFsSpecialNode {
384 pub fn new() -> Self {
385 Self { xattrs: MemoryXattrStorage::default() }
386 }
387}
388
389impl FsNodeOps for TmpFsSpecialNode {
390 fs_node_impl_not_dir!();
391 fs_node_impl_xattr_delegate!(self, self.xattrs);
392
393 fn create_file_ops(
394 &self,
395 _node: &FsNode,
396 _current_task: &CurrentTask,
397 _flags: OpenFlags,
398 ) -> Result<Box<dyn FileOps>, Errno> {
399 unreachable!("Special nodes cannot be opened.");
400 }
401}
402
403#[cfg(test)]
404mod test {
405 use super::*;
406 use crate::testing::spawn_kernel_and_run;
407 use crate::vfs::buffers::{VecInputBuffer, VecOutputBuffer};
408 use crate::vfs::fs_args::MountParams;
409 use crate::vfs::{DirectoryMode, FdNumber, UnlinkKind};
410 use starnix_uapi::errno;
411 use starnix_uapi::mount_flags::FileSystemFlags;
412 use starnix_uapi::vfs::ResolveFlags;
413 use zerocopy::IntoBytes;
414
415 #[::fuchsia::test]
416 async fn test_tmpfs() {
417 spawn_kernel_and_run(async |current_task| {
418 let kernel = current_task.kernel();
419 let fs = TmpFs::new_fs(&kernel);
420 let root = fs.root();
421 let usr = root.create_dir(¤t_task, "usr".into()).unwrap();
422 let _etc = root.create_dir(¤t_task, "etc".into()).unwrap();
423 let _usr_bin = usr.create_dir(¤t_task, "bin".into()).unwrap();
424 let mut names = root.copy_child_names();
425 names.sort();
426 assert!(names.iter().eq(["etc", "usr"].iter()));
427 })
428 .await;
429 }
430
431 #[::fuchsia::test]
432 async fn test_write_read() {
433 spawn_kernel_and_run(async |current_task| {
434 let path = "test.bin";
435 let _file = current_task
436 .fs()
437 .root()
438 .create_node(¤t_task, path.into(), mode!(IFREG, 0o777), DeviceId::NONE)
439 .unwrap();
440
441 let wr_file = current_task.open_file(path.into(), OpenFlags::RDWR).unwrap();
442
443 let test_seq = 0..10000u16;
444 let test_vec = test_seq.collect::<Vec<_>>();
445 let test_bytes = test_vec.as_slice().as_bytes();
446
447 let written =
448 wr_file.write(¤t_task, &mut VecInputBuffer::new(test_bytes)).unwrap();
449 assert_eq!(written, test_bytes.len());
450
451 let mut read_buffer = VecOutputBuffer::new(test_bytes.len() + 1);
452 let read = wr_file.read_at(¤t_task, 0, &mut read_buffer).unwrap();
453 assert_eq!(read, test_bytes.len());
454 assert_eq!(test_bytes, read_buffer.data());
455 })
456 .await;
457 }
458
459 #[::fuchsia::test]
460 async fn test_read_past_eof() {
461 spawn_kernel_and_run(async |current_task| {
462 let path = "test.bin";
464 let _file = current_task
465 .fs()
466 .root()
467 .create_node(¤t_task, path.into(), mode!(IFREG, 0o777), DeviceId::NONE)
468 .unwrap();
469 let rd_file = current_task.open_file(path.into(), OpenFlags::RDONLY).unwrap();
470
471 let buffer_size = 0x10000;
473 let mut output_buffer = VecOutputBuffer::new(buffer_size);
474 let test_offset = 100;
475 let result = rd_file.read_at(¤t_task, test_offset, &mut output_buffer).unwrap();
476 assert_eq!(result, 0);
477 })
478 .await;
479 }
480
481 #[::fuchsia::test]
482 async fn test_permissions() {
483 spawn_kernel_and_run(async |current_task| {
484 let path = "test.bin";
485 let file = current_task
486 .open_file_at(
487 FdNumber::AT_FDCWD,
488 path.into(),
489 OpenFlags::CREAT | OpenFlags::RDONLY,
490 FileMode::from_bits(0o777),
491 ResolveFlags::empty(),
492 )
493 .expect("failed to create file");
494 assert_eq!(
495 0,
496 file.read(¤t_task, &mut VecOutputBuffer::new(0)).expect("failed to read")
497 );
498
499 assert!(file.write(¤t_task, &mut VecInputBuffer::new(&[])).is_err());
500
501 let file = current_task
502 .open_file_at(
503 FdNumber::AT_FDCWD,
504 path.into(),
505 OpenFlags::WRONLY,
506 FileMode::EMPTY,
507 ResolveFlags::empty(),
508 )
509 .expect("failed to open file WRONLY");
510
511 assert!(file.read(¤t_task, &mut VecOutputBuffer::new(0)).is_err());
512
513 assert_eq!(
514 0,
515 file.write(¤t_task, &mut VecInputBuffer::new(&[])).expect("failed to write")
516 );
517
518 let file = current_task
519 .open_file_at(
520 FdNumber::AT_FDCWD,
521 path.into(),
522 OpenFlags::RDWR,
523 FileMode::EMPTY,
524 ResolveFlags::empty(),
525 )
526 .expect("failed to open file RDWR");
527
528 assert_eq!(
529 0,
530 file.read(¤t_task, &mut VecOutputBuffer::new(0)).expect("failed to read")
531 );
532
533 assert_eq!(
534 0,
535 file.write(¤t_task, &mut VecInputBuffer::new(&[])).expect("failed to write")
536 );
537 })
538 .await;
539 }
540
541 #[::fuchsia::test]
542 async fn test_persistence() {
543 spawn_kernel_and_run(async |current_task| {
544 {
545 let root = ¤t_task.fs().root().entry;
546 let usr =
547 root.create_dir(¤t_task, "usr".into()).expect("failed to create usr");
548 root.create_dir(¤t_task, "etc".into()).expect("failed to create usr/etc");
549 usr.create_dir(¤t_task, "bin".into()).expect("failed to create usr/bin");
550 }
551
552 current_task
555 .open_file("/usr/bin".into(), OpenFlags::RDONLY | OpenFlags::DIRECTORY)
556 .expect("failed to open /usr/bin");
557 assert_eq!(
558 errno!(ENOENT),
559 current_task.open_file("/usr/bin/test.txt".into(), OpenFlags::RDWR).unwrap_err()
560 );
561 current_task
562 .open_file_at(
563 FdNumber::AT_FDCWD,
564 "/usr/bin/test.txt".into(),
565 OpenFlags::RDWR | OpenFlags::CREAT,
566 FileMode::from_bits(0o777),
567 ResolveFlags::empty(),
568 )
569 .expect("failed to create test.txt");
570 let txt = current_task
571 .open_file("/usr/bin/test.txt".into(), OpenFlags::RDWR)
572 .expect("failed to open test.txt");
573
574 let usr_bin = current_task
575 .open_file("/usr/bin".into(), OpenFlags::RDONLY)
576 .expect("failed to open /usr/bin");
577 usr_bin
578 .name
579 .unlink(
580 ¤t_task,
581 "test.txt".into(),
582 UnlinkKind::NonDirectory,
583 DirectoryMode::AllowAny,
584 )
585 .expect("failed to unlink test.text");
586 assert_eq!(
587 errno!(ENOENT),
588 current_task.open_file("/usr/bin/test.txt".into(), OpenFlags::RDWR).unwrap_err()
589 );
590 assert_eq!(
591 errno!(ENOENT),
592 usr_bin
593 .name
594 .unlink(
595 ¤t_task,
596 "test.txt".into(),
597 UnlinkKind::NonDirectory,
598 DirectoryMode::AllowAny,
599 )
600 .unwrap_err()
601 );
602
603 assert_eq!(
604 0,
605 txt.read(¤t_task, &mut VecOutputBuffer::new(0)).expect("failed to read")
606 );
607 std::mem::drop(txt);
608 assert_eq!(
609 errno!(ENOENT),
610 current_task.open_file("/usr/bin/test.txt".into(), OpenFlags::RDWR).unwrap_err()
611 );
612 std::mem::drop(usr_bin);
613
614 let usr = current_task
615 .open_file("/usr".into(), OpenFlags::RDONLY)
616 .expect("failed to open /usr");
617 assert_eq!(
618 errno!(ENOENT),
619 current_task.open_file("/usr/foo".into(), OpenFlags::RDONLY).unwrap_err()
620 );
621 usr.name
622 .unlink(¤t_task, "bin".into(), UnlinkKind::Directory, DirectoryMode::AllowAny)
623 .expect("failed to unlink /usr/bin");
624 })
625 .await;
626 }
627
628 #[::fuchsia::test]
629 async fn test_data() {
630 spawn_kernel_and_run(async |current_task| {
631 let kernel = current_task.kernel();
632 let fs = TmpFs::new_fs_with_options(
633 &kernel,
634 FileSystemOptions {
635 source: Default::default(),
636 flags: FileSystemFlags::empty().into(),
637 params: MountParams::parse(b"mode=0123,uid=42,gid=84".into())
638 .expect("parsed correctly"),
639 },
640 )
641 .expect("new_fs");
642 let info = fs.root().node.info();
643 assert_eq!(info.mode, mode!(IFDIR, 0o123));
644 assert_eq!(info.uid, 42);
645 assert_eq!(info.gid, 84);
646 })
647 .await;
648 }
649}