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 AppendLockWriteGuard, DirEntry, FallocMode, FileHandle, FileObject, FileOps, FileSystemHandle,
14 FsNode, FsNodeInfo, FsNodeLinkBehavior, FsNodeOps, FsString, MAX_LFS_FILESIZE,
15 MemoryXattrStorage, Mount, MountInfo, NamespaceNode, WhatToMount, XattrStorage as _,
16 fileops_impl_noop_sync, fs_node_impl_not_dir, fs_node_impl_xattr_delegate,
17};
18use linux_uapi::{ASHMEM_GET_SIZE, ASHMEM_SET_SIZE};
19use starnix_logging::{impossible_error, track_stub};
20use starnix_sync::{FileOpsCore, Locked, Unlocked};
21use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
22use starnix_types::math::round_up_to_system_page_size;
23use starnix_uapi::errors::{EFBIG, Errno};
24use starnix_uapi::file_mode::{AccessCheck, mode};
25use starnix_uapi::open_flags::OpenFlags;
26use starnix_uapi::resource_limits::Resource;
27use starnix_uapi::seal_flags::SealFlags;
28use starnix_uapi::signals::SIGXFSZ;
29use starnix_uapi::{errno, error};
30use std::sync::Arc;
31
32pub struct MemoryRegularNode {
33 memory: Arc<MemoryObject>,
35 xattrs: MemoryXattrStorage,
36}
37
38impl MemoryRegularNode {
39 pub fn new() -> Result<Self, Errno> {
41 let vmo =
42 zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, 0).map_err(|_| errno!(ENOMEM))?;
43 Ok(Self {
44 memory: Arc::new(MemoryObject::from(vmo).with_zx_name(b"starnix:vfs")),
45 xattrs: MemoryXattrStorage::default(),
46 })
47 }
48
49 pub fn from_memory(memory: Arc<MemoryObject>) -> Self {
53 Self { memory, xattrs: MemoryXattrStorage::default() }
54 }
55}
56
57impl FsNodeOps for MemoryRegularNode {
58 fs_node_impl_not_dir!();
59 fs_node_impl_xattr_delegate!(self, self.xattrs);
60
61 fn initial_info(&self, info: &mut FsNodeInfo) {
62 info.size = self.memory.get_content_size() as usize;
63 }
64
65 fn create_file_ops(
66 &self,
67 _locked: &mut Locked<FileOpsCore>,
68 node: &FsNode,
69 _current_task: &CurrentTask,
70 flags: OpenFlags,
71 ) -> Result<Box<dyn FileOps>, Errno> {
72 if flags.contains(OpenFlags::TRUNC) {
73 node.write_guard_state.lock().check_no_seal(SealFlags::SHRINK)?;
75 }
76
77 let mut desired_rights = zx::Rights::VMO_DEFAULT | zx::Rights::RESIZE;
79 if !flags.can_read() {
80 desired_rights.remove(zx::Rights::READ);
81 }
82 if !flags.can_write() {
83 desired_rights.remove(zx::Rights::WRITE | zx::Rights::RESIZE);
84 }
85 let scoped_memory =
86 Arc::new(self.memory.duplicate_handle(desired_rights).map_err(|_e| errno!(EIO))?);
87 let file_object = MemoryRegularFile::new(scoped_memory);
88
89 Ok(Box::new(file_object))
90 }
91
92 fn truncate(
93 &self,
94 _locked: &mut Locked<FileOpsCore>,
95 _guard: &AppendLockWriteGuard<'_>,
96 node: &FsNode,
97 _current_task: &CurrentTask,
98 length: u64,
99 ) -> Result<(), Errno> {
100 let length = length as usize;
101
102 node.update_info(|info| {
103 if info.size == length {
104 return Ok(());
106 }
107
108 let state = node.write_guard_state.lock();
111
112 if info.size > length {
113 state.check_no_seal(SealFlags::SHRINK)?;
115 } else {
116 state.check_no_seal(SealFlags::GROW)?;
118 }
119
120 let memory_size = update_memory_file_size(&self.memory, info, length)?;
121 info.size = length;
122
123 if memory_size > length {
125 self.memory
126 .op_range(zx::VmoOp::ZERO, length as u64, (memory_size - length) as u64)
127 .map_err(impossible_error)?;
128 }
129
130 Ok(())
131 })
132 }
133
134 fn allocate(
135 &self,
136 _locked: &mut Locked<FileOpsCore>,
137 _guard: &AppendLockWriteGuard<'_>,
138 node: &FsNode,
139 _current_task: &CurrentTask,
140 mode: FallocMode,
141 offset: u64,
142 length: u64,
143 ) -> Result<(), Errno> {
144 match mode {
145 FallocMode::PunchHole => {
146 let info = node.info();
149
150 let state = node.write_guard_state.lock();
152 state.check_no_seal(SealFlags::WRITE | SealFlags::FUTURE_WRITE)?;
153
154 let mut end = offset.checked_add(length).ok_or_else(|| errno!(EINVAL))? as usize;
155
156 let memory_size = info.blksize * info.blocks;
157 if offset as usize >= memory_size {
158 return Ok(());
159 }
160
161 if end >= info.size {
164 end = memory_size;
165 }
166
167 self.memory
168 .op_range(zx::VmoOp::ZERO, offset, end as u64 - offset)
169 .map_err(impossible_error)?;
170
171 Ok(())
172 }
173
174 FallocMode::Allocate { keep_size } => {
175 node.update_info(|info| {
176 let new_size = (offset + length) as usize;
177 if new_size > info.size {
178 let state = node.write_guard_state.lock();
181 state.check_no_seal(SealFlags::GROW)?;
182
183 update_memory_file_size(&self.memory, info, new_size)?;
184
185 if !keep_size {
186 info.size = new_size;
187 }
188 }
189 Ok(())
190 })
191 }
192
193 _ => error!(EOPNOTSUPP),
194 }
195 }
196}
197
198pub struct MemoryRegularFile {
199 pub memory: Arc<MemoryObject>,
200}
201
202impl MemoryRegularFile {
203 pub fn new(memory: Arc<MemoryObject>) -> Self {
205 MemoryRegularFile { memory }
206 }
207}
208
209impl MemoryRegularFile {
210 pub fn read(
211 memory: &Arc<MemoryObject>,
212 file: &FileObject,
213 offset: usize,
214 data: &mut dyn OutputBuffer,
215 ) -> Result<usize, Errno> {
216 let actual = {
217 let info = file.node().info();
218 let file_length = info.size;
219 let want_read = data.available();
220 if offset < file_length {
221 let to_read =
222 if file_length < offset + want_read { file_length - offset } else { want_read };
223 let buf =
224 memory.read_to_vec(offset as u64, to_read as u64).map_err(|_| errno!(EIO))?;
225 drop(info);
226 data.write_all(&buf[..])?;
227 to_read
228 } else {
229 0
230 }
231 };
232 Ok(actual)
233 }
234
235 pub fn write(
236 locked: &mut Locked<FileOpsCore>,
237 memory: &Arc<MemoryObject>,
238 file: &FileObject,
239 current_task: &CurrentTask,
240 offset: usize,
241 data: &mut dyn InputBuffer,
242 ) -> Result<usize, Errno> {
243 let mut want_write = data.available();
244 let buf = data.peek_all()?;
245
246 let result = file.node().update_info(|info| {
247 let mut write_end = offset + want_write;
248 let mut update_content_size = false;
249
250 let state = file.name.entry.node.write_guard_state.lock();
253
254 if want_write != 0 {
256 state.check_no_seal(SealFlags::WRITE | SealFlags::FUTURE_WRITE)?;
257 }
258
259 if write_end > info.size {
261 if let Err(e) = state.check_no_seal(SealFlags::GROW) {
263 if offset >= info.size {
264 return Err(e);
267 } else if info.size == info.storage_size() {
268 write_end = info.size;
271 want_write = write_end - offset;
272 } else {
273 let eof_page_start = info.storage_size() - (*PAGE_SIZE as usize);
275
276 if offset >= eof_page_start {
277 return Err(e);
280 }
281
282 write_end = eof_page_start;
284 want_write = write_end - offset;
285 }
286 }
287 }
288
289 let fsize_limit =
291 current_task.thread_group().get_rlimit(locked, Resource::FSIZE) as usize;
292 if write_end > fsize_limit {
293 if offset >= fsize_limit {
294 return error!(EFBIG);
296 }
297
298 write_end = fsize_limit;
300 want_write = write_end - offset;
301 }
302
303 if write_end > info.size {
304 if write_end > info.storage_size() {
305 update_memory_file_size(memory, info, write_end)?;
306 }
307 update_content_size = true;
308 }
309 memory.write(&buf[..want_write], offset as u64).map_err(|_| errno!(EIO))?;
310
311 if update_content_size {
312 info.size = write_end;
313 }
314 data.advance(want_write)?;
315 Ok(want_write)
316 });
317 if let Err(ref e) = result
318 && *e == EFBIG
319 {
320 send_standard_signal(locked, current_task, SignalInfo::kernel(SIGXFSZ));
323 }
324 result
325 }
326
327 pub fn get_memory(
328 memory: &Arc<MemoryObject>,
329 file: &FileObject,
330 _current_task: &CurrentTask,
331 prot: ProtectionFlags,
332 ) -> Result<Arc<MemoryObject>, Errno> {
333 if prot.contains(ProtectionFlags::READ) && !file.can_read() {
339 return error!(EACCES);
340 }
341 if prot.contains(ProtectionFlags::WRITE) && !file.can_write() {
342 return error!(EACCES);
343 }
344 let mut memory = Arc::clone(memory);
345 if prot.contains(ProtectionFlags::EXEC) {
346 memory = Arc::new(
347 memory
348 .duplicate_handle(zx::Rights::SAME_RIGHTS)
349 .map_err(impossible_error)?
350 .replace_as_executable(&VMEX_RESOURCE)
351 .map_err(impossible_error)?,
352 );
353 }
354 Ok(memory)
355 }
356}
357
358#[macro_export]
359macro_rules! fileops_impl_memory {
360 ($self:ident, $memory:expr) => {
361 $crate::fileops_impl_seekable!();
362
363 fn read(
364 &$self,
365 _locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
366 file: &$crate::vfs::FileObject,
367 _current_task: &$crate::task::CurrentTask,
368 offset: usize,
369 data: &mut dyn $crate::vfs::buffers::OutputBuffer,
370 ) -> Result<usize, starnix_uapi::errors::Errno> {
371 $crate::vfs::MemoryRegularFile::read($memory, file, offset, data)
372 }
373
374 fn write(
375 &$self,
376 locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
377 file: &$crate::vfs::FileObject,
378 current_task: &$crate::task::CurrentTask,
379 offset: usize,
380 data: &mut dyn $crate::vfs::buffers::InputBuffer,
381 ) -> Result<usize, starnix_uapi::errors::Errno> {
382 $crate::vfs::MemoryRegularFile::write(locked, $memory, file, current_task, offset, data)
383 }
384
385 fn get_memory(
386 &$self,
387 _locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
388 file: &$crate::vfs::FileObject,
389 current_task: &$crate::task::CurrentTask,
390 _length: Option<usize>,
391 prot: $crate::mm::ProtectionFlags,
392 ) -> Result<Arc<$crate::mm::memory::MemoryObject>, starnix_uapi::errors::Errno> {
393 $crate::vfs::MemoryRegularFile::get_memory($memory, file, current_task, prot)
394 }
395 }
396}
397pub use fileops_impl_memory;
398
399impl FileOps for MemoryRegularFile {
400 fileops_impl_memory!(self, &self.memory);
401 fileops_impl_noop_sync!();
402
403 fn readahead(
404 &self,
405 _file: &FileObject,
406 _current_task: &CurrentTask,
407 _offset: usize,
408 _length: usize,
409 ) -> Result<(), Errno> {
410 track_stub!(TODO("https://fxbug.dev/42082608"), "paged VMO readahead");
411 Ok(())
412 }
413
414 fn ioctl(
415 &self,
416 _locked: &mut Locked<Unlocked>,
417 _file: &FileObject,
418 _current_task: &CurrentTask,
419 request: u32,
420 arg: SyscallArg,
421 ) -> Result<SyscallResult, Errno> {
422 match request {
423 ASHMEM_GET_SIZE => {
424 track_stub!(TODO("https://fxbug.dev/389102161"), "ashmem get_size on memfd");
425 Ok(self.memory.get_size().into())
426 }
427 ASHMEM_SET_SIZE => {
428 track_stub!(TODO("https://fxbug.dev/389102161"), "ashmem set_size on memfd");
429 self.memory.set_size(arg.into()).map_err(|_| errno!(EINVAL))?;
430 Ok(SUCCESS)
431 }
432 _ => error!(ENOTTY),
433 }
434 }
435}
436
437pub fn new_memfd(
438 locked: &mut Locked<Unlocked>,
439 current_task: &CurrentTask,
440 mut name: FsString,
441 seals: SealFlags,
442 flags: OpenFlags,
443) -> Result<FileHandle, Errno> {
444 struct MemFdTmpfs {
445 tmpfs: FileSystemHandle,
446 mount: Arc<Mount>,
447 }
448
449 let fs = current_task.kernel().expando.get_or_init(|| {
450 let tmpfs = TmpFs::new_fs(locked, current_task.kernel());
451 security::file_system_resolve_security(locked, ¤t_task, &tmpfs)
452 .expect("resolve fs security");
453 let mounts_guard = current_task.kernel().mounts_lock.lock();
454 let mount = Mount::new(&mounts_guard, WhatToMount::Fs(tmpfs.clone()), Default::default());
455 MemFdTmpfs { tmpfs, mount }
456 });
457
458 let fs_node = current_task.override_creds(
462 security::creds_start_internal_operation(current_task),
463 || {
464 fs.tmpfs.root().node.create_tmpfile(
465 locked,
466 current_task,
467 &MountInfo::detached(),
468 mode!(IFREG, 0o600),
469 current_task.current_fscred(),
470 FsNodeLinkBehavior::Disallowed,
471 )
472 },
473 )?;
474
475 security::fs_node_init_anon(current_task, &fs_node, "[memfd]")?;
477
478 fs_node.write_guard_state.lock().enable_sealing(seals);
479
480 let mut local_name = FsString::from("memfd:");
483 local_name.append(&mut name);
484 let dir_entry = DirEntry::new_deleted(fs_node, Some(fs.tmpfs.root().clone()), local_name);
485 security::fs_node_init_with_dentry(locked, current_task, &dir_entry)?;
486
487 let name = NamespaceNode::new(fs.mount.clone(), dir_entry);
488 name.open(locked, current_task, flags, AccessCheck::skip())
489}
490
491fn update_memory_file_size(
493 memory: &MemoryObject,
494 node_info: &mut FsNodeInfo,
495 requested_size: usize,
496) -> Result<usize, Errno> {
497 assert!(requested_size <= MAX_LFS_FILESIZE);
498 let size = round_up_to_system_page_size(requested_size)?;
499 memory.set_size(size as u64).map_err(|status| match status {
500 zx::Status::NO_MEMORY => errno!(ENOMEM),
501 zx::Status::OUT_OF_RANGE => errno!(ENOMEM),
502 _ => impossible_error(status),
503 })?;
504 node_info.blocks = size / node_info.blksize;
505 Ok(size)
506}