1use crate::fs::tmpfs::TmpFs;
6use crate::mm::memory::MemoryObject;
7use crate::mm::{PAGE_SIZE, ProtectionFlags, VMEX_RESOURCE};
8use crate::security;
9use crate::signals::{SignalInfo, send_standard_signal};
10use crate::task::CurrentTask;
11use crate::vfs::buffers::{InputBuffer, OutputBuffer};
12use crate::vfs::{
13 Anon, AppendLockWriteGuard, DirEntry, FallocMode, FileHandle, FileObject, FileOps,
14 FileSystemHandle, FsNode, FsNodeFlags, FsNodeInfo, FsNodeLinkBehavior, FsNodeOps, FsString,
15 MAX_LFS_FILESIZE, MemoryXattrStorage, Mount, MountInfo, NamespaceNode, OpenAccessCheck,
16 WhatToMount, XattrStorage as _, anon_fs, fileops_impl_noop_sync, fs_node_impl_not_dir,
17 fs_node_impl_xattr_delegate,
18};
19use linux_uapi::{ASHMEM_GET_SIZE, ASHMEM_SET_SIZE};
20use once_cell::sync::OnceCell;
21use starnix_logging::{impossible_error, track_stub};
22use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
23use starnix_types::math::round_up_to_system_page_size;
24use starnix_uapi::errors::{EFBIG, Errno};
25use starnix_uapi::file_mode::mode;
26use starnix_uapi::open_flags::OpenFlags;
27use starnix_uapi::resource_limits::Resource;
28use starnix_uapi::seal_flags::SealFlags;
29use starnix_uapi::signals::SIGXFSZ;
30use starnix_uapi::{errno, error};
31use std::sync::Arc;
32
33pub struct MemoryRegularNode {
34 memory: Arc<MemoryObject>,
36 read_only_memory: OnceCell<Arc<MemoryObject>>,
38 read_exec_memory: OnceCell<Arc<MemoryObject>>,
40 write_exec_memory: OnceCell<Arc<MemoryObject>>,
42 xattrs: MemoryXattrStorage,
43}
44
45impl MemoryRegularNode {
46 pub fn new() -> Result<Self, Errno> {
48 let vmo =
49 zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, 0).map_err(|_| errno!(ENOMEM))?;
50 Ok(Self {
51 memory: Arc::new(MemoryObject::from(vmo).with_zx_name(b"starnix:vfs")),
52 read_only_memory: OnceCell::new(),
53 read_exec_memory: OnceCell::new(),
54 write_exec_memory: OnceCell::new(),
55 xattrs: MemoryXattrStorage::default(),
56 })
57 }
58
59 pub fn from_memory(memory: Arc<MemoryObject>) -> Self {
63 Self {
64 memory,
65 read_only_memory: OnceCell::new(),
66 read_exec_memory: OnceCell::new(),
67 write_exec_memory: OnceCell::new(),
68 xattrs: MemoryXattrStorage::default(),
69 }
70 }
71
72 fn read_only_memory(&self) -> Result<&Arc<MemoryObject>, Errno> {
75 self.read_only_memory.get_or_try_init(|| {
76 self.memory
77 .duplicate_handle(zx::Rights::VMO_DEFAULT - zx::Rights::WRITE)
78 .map(Arc::new)
79 .map_err(|_e| errno!(EIO))
80 })
81 }
82
83 fn exec_memory(&self, writable: bool) -> Result<Arc<MemoryObject>, Errno> {
87 if writable {
88 self.write_exec_memory.get_or_try_init(|| {
89 self.memory
90 .duplicate_handle(zx::Rights::VMO_DEFAULT | zx::Rights::RESIZE)
91 .map_err(|_e| errno!(EIO))?
92 .replace_as_executable(&VMEX_RESOURCE)
93 .map(Arc::new)
94 .map_err(impossible_error)
95 })
96 } else {
97 self.read_exec_memory.get_or_try_init(|| {
98 self.read_only_memory()?
99 .duplicate_handle(zx::Rights::SAME_RIGHTS)
100 .and_then(|m| m.replace_as_executable(&VMEX_RESOURCE))
101 .map(Arc::new)
102 .map_err(impossible_error)
103 })
104 }
105 .cloned()
106 }
107}
108
109impl FsNodeOps for MemoryRegularNode {
110 fs_node_impl_not_dir!();
111 fs_node_impl_xattr_delegate!(self, self.xattrs);
112
113 fn initial_info(&self, info: &mut FsNodeInfo) {
114 info.size = self.memory.get_content_size() as usize;
115 }
116
117 fn create_file_ops(
118 &self,
119 node: &FsNode,
120 _current_task: &CurrentTask,
121 flags: OpenFlags,
122 ) -> Result<Box<dyn FileOps>, Errno> {
123 if flags.contains(OpenFlags::TRUNC) {
124 node.write_guard_state.lock().check_no_seal(SealFlags::SHRINK)?;
126 }
127
128 let scoped_memory = if flags.can_read() && !flags.can_write() {
130 self.read_only_memory()?.clone()
131 } else {
132 let mut desired_rights = zx::Rights::VMO_DEFAULT | zx::Rights::RESIZE;
133 if !flags.can_read() {
134 desired_rights.remove(zx::Rights::READ);
135 }
136 if !flags.can_write() {
137 desired_rights.remove(zx::Rights::WRITE | zx::Rights::RESIZE);
138 }
139 Arc::new(self.memory.duplicate_handle(desired_rights).map_err(|_e| errno!(EIO))?)
140 };
141 let file_object = MemoryRegularFile::new(scoped_memory);
142
143 Ok(Box::new(file_object))
144 }
145
146 fn truncate(
147 &self,
148 _guard: &AppendLockWriteGuard<'_>,
149 node: &FsNode,
150 _current_task: &CurrentTask,
151 length: u64,
152 ) -> Result<(), Errno> {
153 let length = length as usize;
154
155 node.update_info(|info| {
156 if info.size == length {
157 return Ok(());
159 }
160
161 let state = node.write_guard_state.lock();
164
165 if info.size > length {
166 state.check_no_seal(SealFlags::SHRINK)?;
168 } else {
169 state.check_no_seal(SealFlags::GROW)?;
171 }
172
173 let memory_size = update_memory_file_size(&self.memory, info, length)?;
174 info.size = length;
175
176 if memory_size > length {
178 self.memory
179 .op_range(zx::VmoOp::ZERO, length as u64, (memory_size - length) as u64)
180 .map_err(impossible_error)?;
181 }
182
183 Ok(())
184 })
185 }
186
187 fn allocate(
188 &self,
189 _guard: &AppendLockWriteGuard<'_>,
190 node: &FsNode,
191 _current_task: &CurrentTask,
192 mode: FallocMode,
193 offset: u64,
194 length: u64,
195 ) -> Result<(), Errno> {
196 match mode {
197 FallocMode::PunchHole => {
198 let info = node.info();
201
202 let state = node.write_guard_state.lock();
204 state.check_no_seal(SealFlags::WRITE | SealFlags::FUTURE_WRITE)?;
205
206 let mut end = offset.checked_add(length).ok_or_else(|| errno!(EINVAL))? as usize;
207
208 let memory_size = info.blksize * info.blocks;
209 if offset as usize >= memory_size {
210 return Ok(());
211 }
212
213 if end >= info.size {
216 end = memory_size;
217 }
218
219 self.memory
220 .op_range(zx::VmoOp::ZERO, offset, end as u64 - offset)
221 .map_err(impossible_error)?;
222
223 Ok(())
224 }
225
226 FallocMode::Allocate { keep_size } => {
227 node.update_info(|info| {
228 let new_size = (offset + length) as usize;
229 if new_size > info.size {
230 let state = node.write_guard_state.lock();
233 state.check_no_seal(SealFlags::GROW)?;
234
235 update_memory_file_size(&self.memory, info, new_size)?;
236
237 if !keep_size {
238 info.size = new_size;
239 }
240 }
241 Ok(())
242 })
243 }
244
245 _ => error!(EOPNOTSUPP),
246 }
247 }
248}
249
250pub struct MemoryRegularFile {
251 pub memory: Arc<MemoryObject>,
252}
253
254impl MemoryRegularFile {
255 pub fn new(memory: Arc<MemoryObject>) -> Self {
257 MemoryRegularFile { memory }
258 }
259
260 pub fn new_private_file_with_ops(
263 current_task: &CurrentTask,
264 ops: Box<dyn FileOps>,
265 size: usize,
266 flags: OpenFlags,
267 name: &'static str,
268 ) -> FileHandle {
269 let mut info = FsNodeInfo::new(mode!(IFREG, 0o600), current_task.current_fscred());
270 info.size = size;
271 let fs = anon_fs(current_task.kernel());
272 let node = fs.create_node_with_flags(None, Anon {}, info, FsNodeFlags::IS_PRIVATE);
273 security::fs_node_init_anon(current_task, &node, name)
274 .expect("Private anon_inode creation cannot fail");
275 let name = NamespaceNode::new_anonymous(DirEntry::new(node, None, name.into()));
276 FileObject::new(current_task, ops, name, flags)
277 .expect("FileObject::new on private anon_inode cannot fail")
278 }
279
280 pub fn new_private_file(
283 current_task: &CurrentTask,
284 memory: Arc<MemoryObject>,
285 flags: OpenFlags,
286 name: &'static str,
287 ) -> FileHandle {
288 let size = memory.get_size() as usize;
289 Self::new_private_file_with_ops(
290 current_task,
291 Box::new(Self::new(memory)),
292 size,
293 flags,
294 name,
295 )
296 }
297
298 pub fn read(
299 memory: &Arc<MemoryObject>,
300 file: &FileObject,
301 offset: usize,
302 data: &mut dyn OutputBuffer,
303 ) -> Result<usize, Errno> {
304 let actual = {
305 let info = file.node().info();
306 let file_length = info.size;
307 let want_read = data.available();
308 if offset < file_length {
309 let to_read =
310 if file_length < offset + want_read { file_length - offset } else { want_read };
311 let buf =
312 memory.read_to_vec(offset as u64, to_read as u64).map_err(|_| errno!(EIO))?;
313 drop(info);
314 data.write_all(&buf[..])?;
315 to_read
316 } else {
317 0
318 }
319 };
320 Ok(actual)
321 }
322
323 pub fn write(
324 memory: &Arc<MemoryObject>,
325 file: &FileObject,
326 current_task: &CurrentTask,
327 offset: usize,
328 data: &mut dyn InputBuffer,
329 ) -> Result<usize, Errno> {
330 let mut want_write = data.available();
331 let buf = data.peek_all()?;
332
333 let result = file.node().update_info(|info| {
334 let mut write_end = offset + want_write;
335 let mut update_content_size = false;
336
337 let state = file.name.entry.node.write_guard_state.lock();
340
341 if want_write != 0 {
343 state.check_no_seal(SealFlags::WRITE | SealFlags::FUTURE_WRITE)?;
344 }
345
346 if write_end > info.size {
348 if let Err(e) = state.check_no_seal(SealFlags::GROW) {
350 if offset >= info.size {
351 return Err(e);
354 } else if info.size == info.storage_size() {
355 write_end = info.size;
358 want_write = write_end - offset;
359 } else {
360 let eof_page_start = info.storage_size() - (*PAGE_SIZE as usize);
362
363 if offset >= eof_page_start {
364 return Err(e);
367 }
368
369 write_end = eof_page_start;
371 want_write = write_end - offset;
372 }
373 }
374 }
375
376 let fsize_limit = current_task.thread_group().get_rlimit(Resource::FSIZE) as usize;
378 if write_end > fsize_limit {
379 if offset >= fsize_limit {
380 return error!(EFBIG);
382 }
383
384 write_end = fsize_limit;
386 want_write = write_end - offset;
387 }
388
389 if write_end > info.size {
390 if write_end > info.storage_size() {
391 update_memory_file_size(memory, info, write_end)?;
392 }
393 update_content_size = true;
394 }
395 memory.write(&buf[..want_write], offset as u64).map_err(|_| errno!(EIO))?;
396
397 if update_content_size {
398 info.size = write_end;
399 }
400 data.advance(want_write)?;
401 Ok(want_write)
402 });
403 if let Err(ref e) = result
404 && *e == EFBIG
405 {
406 send_standard_signal(current_task, SignalInfo::kernel(SIGXFSZ));
409 }
410 result
411 }
412
413 pub fn get_memory(
414 memory: &Arc<MemoryObject>,
415 file: &FileObject,
416 _current_task: &CurrentTask,
417 prot: ProtectionFlags,
418 ) -> Result<Arc<MemoryObject>, Errno> {
419 if prot.contains(ProtectionFlags::READ) && !file.can_read() {
425 return error!(EACCES);
426 }
427 if prot.contains(ProtectionFlags::WRITE) && !file.can_write() {
428 return error!(EACCES);
429 }
430 let mut memory = Arc::clone(memory);
431 if prot.contains(ProtectionFlags::EXEC) {
432 if file.can_read()
437 && let Some(node) = file.node().downcast_ops::<MemoryRegularNode>()
438 {
439 return node.exec_memory(file.can_write());
440 }
441 memory = Arc::new(
442 memory
443 .duplicate_handle(zx::Rights::SAME_RIGHTS)
444 .map_err(impossible_error)?
445 .replace_as_executable(&VMEX_RESOURCE)
446 .map_err(impossible_error)?,
447 );
448 }
449 Ok(memory)
450 }
451}
452
453#[macro_export]
454macro_rules! fileops_impl_memory {
455 ($self:ident, $memory:expr) => {
456 $crate::fileops_impl_seekable!();
457
458 fn read(
459 &$self,
460 file: &$crate::vfs::FileObject,
461 _current_task: &$crate::task::CurrentTask,
462 offset: usize,
463 data: &mut dyn $crate::vfs::buffers::OutputBuffer,
464 ) -> Result<usize, starnix_uapi::errors::Errno> {
465 $crate::vfs::MemoryRegularFile::read($memory, file, offset, data)
466 }
467
468 fn write(
469 &$self,
470 file: &$crate::vfs::FileObject,
471 current_task: &$crate::task::CurrentTask,
472 offset: usize,
473 data: &mut dyn $crate::vfs::buffers::InputBuffer,
474 ) -> Result<usize, starnix_uapi::errors::Errno> {
475 $crate::vfs::MemoryRegularFile::write($memory, file, current_task, offset, data)
476 }
477
478 fn get_memory(
479 &$self,
480 file: &$crate::vfs::FileObject,
481 current_task: &$crate::task::CurrentTask,
482 _length: Option<usize>,
483 prot: $crate::mm::ProtectionFlags,
484 ) -> Result<Arc<$crate::mm::memory::MemoryObject>, starnix_uapi::errors::Errno> {
485 $crate::vfs::MemoryRegularFile::get_memory($memory, file, current_task, prot)
486 }
487 }
488}
489pub use fileops_impl_memory;
490
491impl FileOps for MemoryRegularFile {
492 fileops_impl_memory!(self, &self.memory);
493 fileops_impl_noop_sync!();
494
495 fn readahead(
496 &self,
497 _file: &FileObject,
498 _current_task: &CurrentTask,
499 _offset: usize,
500 _length: usize,
501 ) -> Result<(), Errno> {
502 track_stub!(TODO("https://fxbug.dev/42082608"), "paged VMO readahead");
503 Ok(())
504 }
505
506 fn ioctl(
507 &self,
508 _file: &FileObject,
509 _current_task: &CurrentTask,
510 request: u32,
511 arg: SyscallArg,
512 ) -> Result<SyscallResult, Errno> {
513 match request {
514 ASHMEM_GET_SIZE => {
515 track_stub!(TODO("https://fxbug.dev/389102161"), "ashmem get_size on memfd");
516 Ok(self.memory.get_size().into())
517 }
518 ASHMEM_SET_SIZE => {
519 track_stub!(TODO("https://fxbug.dev/389102161"), "ashmem set_size on memfd");
520 self.memory.set_size(arg.into()).map_err(|_| errno!(EINVAL))?;
521 Ok(SUCCESS)
522 }
523 _ => error!(ENOTTY),
524 }
525 }
526}
527
528pub fn new_memfd(
529 current_task: &CurrentTask,
530 mut name: FsString,
531 seals: SealFlags,
532 flags: OpenFlags,
533) -> Result<FileHandle, Errno> {
534 struct MemFdTmpfs {
535 tmpfs: FileSystemHandle,
536 mount: Arc<Mount>,
537 }
538
539 let fs = current_task.kernel().expando.get_or_init(|| {
540 let tmpfs = TmpFs::new_fs(current_task.kernel());
541 security::file_system_resolve_security(¤t_task, &tmpfs).expect("resolve fs security");
542 let mounts_guard = current_task.kernel().mounts_lock();
543 let mount = Mount::new(&mounts_guard, WhatToMount::Fs(tmpfs.clone()), Default::default())
544 .expect("create new tempfs mount for memfd");
545 MemFdTmpfs { tmpfs, mount }
546 });
547
548 let fs_node = current_task.override_creds(
552 security::creds_start_internal_operation(current_task),
553 || {
554 fs.tmpfs.root().node.create_tmpfile(
555 current_task,
556 &MountInfo::detached(),
557 mode!(IFREG, 0o600),
558 current_task.current_fscred(),
559 FsNodeLinkBehavior::Disallowed,
560 )
561 },
562 )?;
563
564 security::fs_node_init_anon(current_task, &fs_node, "[memfd]")?;
566
567 fs_node.write_guard_state.lock().enable_sealing(seals);
568
569 let mut local_name = FsString::from("memfd:");
572 local_name.append(&mut name);
573 let dir_entry = DirEntry::new_deleted(fs_node, Some(fs.tmpfs.root().clone()), local_name);
574 security::fs_node_init_with_dentry(current_task, &dir_entry)?;
575
576 let name = NamespaceNode::new(fs.mount.clone(), dir_entry);
577 name.open(current_task, OpenAccessCheck::skip(flags))
578}
579
580fn update_memory_file_size(
582 memory: &MemoryObject,
583 node_info: &mut FsNodeInfo,
584 requested_size: usize,
585) -> Result<usize, Errno> {
586 assert!(requested_size <= MAX_LFS_FILESIZE);
587 let size = round_up_to_system_page_size(requested_size)?;
588 memory.set_size(size as u64).map_err(|status| match status {
589 zx::Status::NO_MEMORY => errno!(ENOMEM),
590 zx::Status::OUT_OF_RANGE => errno!(ENOMEM),
591 _ => impossible_error(status),
592 })?;
593 node_info.blocks = size / node_info.blksize;
594 Ok(size)
595}
596
597#[cfg(test)]
598mod test {
599 use super::*;
600 use crate::testing::spawn_kernel_and_run;
601 use crate::vfs::{LookupContext, Namespace};
602 use starnix_uapi::device_id::DeviceId;
603
604 #[::fuchsia::test]
605 async fn test_get_memory_cached_across_opens_and_exec() {
606 spawn_kernel_and_run(async |current_task| {
607 let fs = TmpFs::new_fs(current_task.kernel());
608 let ns = Namespace::new(fs);
609 let root = ns.root();
610
611 root.create_node(current_task, "file".into(), mode!(IFREG, 0o755), DeviceId::NONE)
612 .expect("create_node failed");
613
614 let mut context = LookupContext::default();
615 let file_node =
616 root.lookup_child(current_task, &mut context, "file".into()).expect("lookup");
617
618 let file1 = file_node.open(current_task, OpenFlags::RDONLY).expect("open 1 failed");
619 let ro_mem1 = file1
620 .get_memory(current_task, None, ProtectionFlags::READ)
621 .expect("get_memory READ 1 failed");
622 let rx_mem1 = file1
623 .get_memory(current_task, None, ProtectionFlags::READ | ProtectionFlags::EXEC)
624 .expect("get_memory READ|EXEC 1 failed");
625 assert!(rx_mem1.get_rights().contains(zx::Rights::EXECUTE));
626 assert!(!rx_mem1.get_rights().contains(zx::Rights::WRITE));
627
628 let file2 = file_node.open(current_task, OpenFlags::RDONLY).expect("open 2 failed");
629 let ro_mem2 = file2
630 .get_memory(current_task, None, ProtectionFlags::READ)
631 .expect("get_memory READ 2 failed");
632 let rx_mem2 = file2
633 .get_memory(current_task, None, ProtectionFlags::READ | ProtectionFlags::EXEC)
634 .expect("get_memory READ|EXEC 2 failed");
635 assert!(Arc::ptr_eq(&ro_mem1, &ro_mem2));
636 assert!(Arc::ptr_eq(&rx_mem1, &rx_mem2));
637
638 let file_rw1 = file_node.open(current_task, OpenFlags::RDWR).expect("open rw1 failed");
640 let rwx_mem1 = file_rw1
641 .get_memory(
642 current_task,
643 None,
644 ProtectionFlags::READ | ProtectionFlags::WRITE | ProtectionFlags::EXEC,
645 )
646 .expect("get_memory READ|WRITE|EXEC 1 failed");
647 assert!(rwx_mem1.get_rights().contains(
648 zx::Rights::READ | zx::Rights::WRITE | zx::Rights::RESIZE | zx::Rights::EXECUTE
649 ));
650 assert!(!Arc::ptr_eq(&rwx_mem1, &rx_mem1));
651
652 let file_rw2 = file_node.open(current_task, OpenFlags::RDWR).expect("open rw2 failed");
653 let rwx_mem2 = file_rw2
654 .get_memory(current_task, None, ProtectionFlags::READ | ProtectionFlags::EXEC)
655 .expect("get_memory READ|EXEC rw2 failed");
656 assert!(Arc::ptr_eq(&rwx_mem1, &rwx_mem2));
657 })
658 .await;
659 }
660
661 #[::fuchsia::test]
662 async fn test_get_memory_write_only_exec_does_not_pollute_cache() {
663 spawn_kernel_and_run(async |current_task| {
664 let fs = TmpFs::new_fs(current_task.kernel());
665 let ns = Namespace::new(fs);
666 let root = ns.root();
667
668 root.create_node(current_task, "file".into(), mode!(IFREG, 0o755), DeviceId::NONE)
669 .expect("create_node failed");
670
671 let mut context = LookupContext::default();
672 let file_node =
673 root.lookup_child(current_task, &mut context, "file".into()).expect("lookup");
674
675 let file_wo = file_node.open(current_task, OpenFlags::WRONLY).expect("open wo failed");
677 let wx_mem = file_wo
678 .get_memory(current_task, None, ProtectionFlags::WRITE | ProtectionFlags::EXEC)
679 .expect("get_memory WRITE|EXEC failed");
680 assert!(wx_mem.get_rights().contains(zx::Rights::WRITE | zx::Rights::EXECUTE));
681 assert!(!wx_mem.get_rights().contains(zx::Rights::READ));
682
683 let file_rw = file_node.open(current_task, OpenFlags::RDWR).expect("open rw failed");
685 let rwx_mem = file_rw
686 .get_memory(
687 current_task,
688 None,
689 ProtectionFlags::READ | ProtectionFlags::WRITE | ProtectionFlags::EXEC,
690 )
691 .expect("get_memory READ|WRITE|EXEC failed");
692 assert!(!Arc::ptr_eq(&wx_mem, &rwx_mem));
693 assert!(rwx_mem.get_rights().contains(
694 zx::Rights::READ | zx::Rights::WRITE | zx::Rights::RESIZE | zx::Rights::EXECUTE
695 ));
696
697 let file_ro = file_node.open(current_task, OpenFlags::RDONLY).expect("open ro failed");
699 let rx_mem = file_ro
700 .get_memory(current_task, None, ProtectionFlags::READ | ProtectionFlags::EXEC)
701 .expect("get_memory READ|EXEC failed");
702 assert!(!Arc::ptr_eq(&wx_mem, &rx_mem));
703 assert!(!Arc::ptr_eq(&rwx_mem, &rx_mem));
704 assert!(rx_mem.get_rights().contains(zx::Rights::READ | zx::Rights::EXECUTE));
705 assert!(!rx_mem.get_rights().contains(zx::Rights::WRITE));
706 })
707 .await;
708 }
709}