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