2025-07-30 09:00:08 +00:00
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
|
|
|
|
|
//! Memfd Implementation.
|
|
|
|
|
|
|
|
|
|
use alloc::format;
|
2025-10-30 07:54:37 +00:00
|
|
|
use core::{
|
2025-11-19 07:20:25 +00:00
|
|
|
fmt::Display,
|
2025-10-30 07:54:37 +00:00
|
|
|
sync::atomic::{AtomicU32, Ordering},
|
|
|
|
|
time::Duration,
|
|
|
|
|
};
|
2025-07-30 09:00:08 +00:00
|
|
|
|
2025-11-05 15:15:40 +00:00
|
|
|
use align_ext::AlignExt;
|
2025-10-30 07:54:37 +00:00
|
|
|
use aster_block::bio::BioWaiter;
|
2025-11-11 09:37:26 +00:00
|
|
|
use aster_rights::Rights;
|
2025-07-30 09:00:08 +00:00
|
|
|
use inherit_methods_macro::inherit_methods;
|
2025-10-30 07:54:37 +00:00
|
|
|
use spin::Once;
|
2025-07-30 09:00:08 +00:00
|
|
|
|
2025-11-04 08:37:39 +00:00
|
|
|
use super::fs::RamInode;
|
2025-07-30 09:00:08 +00:00
|
|
|
use crate::{
|
|
|
|
|
events::IoEvents,
|
|
|
|
|
fs::{
|
2025-08-13 16:11:06 +00:00
|
|
|
file_handle::{FileLike, Mappable},
|
2025-11-19 07:20:25 +00:00
|
|
|
file_table::FdFlags,
|
2025-07-30 09:00:08 +00:00
|
|
|
inode_handle::{do_fallocate_util, do_resize_util, do_seek_util},
|
2025-11-17 15:38:28 +00:00
|
|
|
notify::FsEventPublisher,
|
2025-12-08 12:53:18 +00:00
|
|
|
path::{RESERVED_MOUNT_ID, check_open_util},
|
2025-11-04 08:37:39 +00:00
|
|
|
tmpfs::TmpFs,
|
2025-07-30 09:00:08 +00:00
|
|
|
utils::{
|
2025-12-08 12:53:18 +00:00
|
|
|
AccessMode, CachePage, CreationFlags, Extension, FallocMode, FileSystem, Inode,
|
|
|
|
|
InodeIo, InodeMode, InodeType, Metadata, OpenArgs, PageCacheBackend, SeekFrom,
|
2025-12-08 15:29:12 +00:00
|
|
|
StatusFlags, XattrName, XattrNamespace, XattrSetFlags, mkmod,
|
2025-07-30 09:00:08 +00:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
prelude::*,
|
|
|
|
|
process::{
|
|
|
|
|
Gid, Uid,
|
2025-12-08 12:53:18 +00:00
|
|
|
signal::{PollHandle, Pollable},
|
2025-07-30 09:00:08 +00:00
|
|
|
},
|
2025-12-04 15:06:55 +00:00
|
|
|
util::ioctl::RawIoctl,
|
2025-10-30 07:54:37 +00:00
|
|
|
vm::{perms::VmPerms, vmo::Vmo},
|
2025-07-30 09:00:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// Maximum file name length for `memfd_create`, excluding the final `\0` byte.
|
|
|
|
|
///
|
|
|
|
|
/// See <https://man7.org/linux/man-pages/man2/memfd_create.2.html>
|
|
|
|
|
pub const MAX_MEMFD_NAME_LEN: usize = 249;
|
|
|
|
|
|
2025-10-30 07:54:37 +00:00
|
|
|
pub struct MemfdInode {
|
|
|
|
|
inode: RamInode,
|
2025-07-30 09:00:08 +00:00
|
|
|
name: String,
|
2025-10-30 07:54:37 +00:00
|
|
|
seals: Mutex<FileSeals>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl MemfdInode {
|
2025-11-03 06:11:43 +00:00
|
|
|
pub(self) fn add_seals(&self, mut new_seals: FileSeals) -> Result<()> {
|
2025-10-30 07:54:37 +00:00
|
|
|
let mut seals = self.seals.lock();
|
|
|
|
|
|
|
|
|
|
if seals.contains(FileSeals::F_SEAL_SEAL) {
|
|
|
|
|
return_errno_with_message!(Errno::EPERM, "the file is sealed against sealing");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/mm/memfd.c#L262-L266>
|
|
|
|
|
if new_seals.contains(FileSeals::F_SEAL_EXEC)
|
2025-11-06 08:30:03 +00:00
|
|
|
&& self.mode().unwrap().intersects(mkmod!(a+x))
|
2025-10-30 07:54:37 +00:00
|
|
|
{
|
|
|
|
|
new_seals |= FileSeals::F_SEAL_SHRINK
|
|
|
|
|
| FileSeals::F_SEAL_GROW
|
|
|
|
|
| FileSeals::F_SEAL_WRITE
|
|
|
|
|
| FileSeals::F_SEAL_FUTURE_WRITE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if new_seals.contains(FileSeals::F_SEAL_WRITE) {
|
|
|
|
|
let page_cache = self.page_cache().unwrap();
|
2025-11-06 08:30:03 +00:00
|
|
|
page_cache.writable_mapping_status().deny()?;
|
2025-10-30 07:54:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*seals |= new_seals;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-03 06:11:43 +00:00
|
|
|
pub(self) fn get_seals(&self) -> FileSeals {
|
2025-10-30 07:54:37 +00:00
|
|
|
*self.seals.lock()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Checks whether writing to this memfd inode is allowed.
|
|
|
|
|
///
|
|
|
|
|
/// This method restricts the `may_perms` if needed.
|
|
|
|
|
pub fn check_writable(&self, perms: VmPerms, may_perms: &mut VmPerms) -> Result<()> {
|
|
|
|
|
let seals = self.seals.lock();
|
|
|
|
|
if seals.intersects(FileSeals::F_SEAL_WRITE | FileSeals::F_SEAL_FUTURE_WRITE) {
|
|
|
|
|
if perms.contains(VmPerms::WRITE) {
|
|
|
|
|
return_errno_with_message!(Errno::EPERM, "the file is sealed against writing");
|
|
|
|
|
}
|
|
|
|
|
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/mm/memfd.c#L356>
|
|
|
|
|
may_perms.remove(VmPerms::MAY_WRITE);
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2025-11-05 12:15:01 +00:00
|
|
|
|
|
|
|
|
pub fn name(&self) -> &str {
|
|
|
|
|
&self.name
|
|
|
|
|
}
|
2025-10-30 07:54:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inherit_methods(from = "self.inode")]
|
|
|
|
|
impl PageCacheBackend for MemfdInode {
|
|
|
|
|
fn read_page_async(&self, idx: usize, frame: &CachePage) -> Result<BioWaiter>;
|
|
|
|
|
fn write_page_async(&self, idx: usize, frame: &CachePage) -> Result<BioWaiter>;
|
|
|
|
|
fn npages(&self) -> usize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inherit_methods(from = "self.inode")]
|
2025-11-05 15:13:34 +00:00
|
|
|
impl InodeIo for MemfdInode {
|
|
|
|
|
fn read_at(
|
2025-10-30 07:54:37 +00:00
|
|
|
&self,
|
2025-11-05 15:13:34 +00:00
|
|
|
offset: usize,
|
|
|
|
|
writer: &mut VmWriter,
|
|
|
|
|
status_flags: StatusFlags,
|
|
|
|
|
) -> Result<usize>;
|
2025-10-30 07:54:37 +00:00
|
|
|
|
2025-11-05 15:13:34 +00:00
|
|
|
fn write_at(
|
|
|
|
|
&self,
|
|
|
|
|
offset: usize,
|
|
|
|
|
reader: &mut VmReader,
|
|
|
|
|
status_flags: StatusFlags,
|
|
|
|
|
) -> Result<usize> {
|
2025-10-30 07:54:37 +00:00
|
|
|
if !reader.has_remain() {
|
|
|
|
|
return Ok(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let seals = self.seals.lock();
|
|
|
|
|
if seals.intersects(FileSeals::F_SEAL_WRITE | FileSeals::F_SEAL_FUTURE_WRITE) {
|
|
|
|
|
return_errno_with_message!(Errno::EPERM, "the file is sealed against writing");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if seals.contains(FileSeals::F_SEAL_GROW) {
|
2025-11-05 15:15:40 +00:00
|
|
|
// For a memfd sealed with `F_SEAL_GROW`, if a write that would grow the file occurs,
|
|
|
|
|
// the entire write within the page containing the EOF is rejected. Writes before
|
|
|
|
|
// the EOF page are not affected.
|
|
|
|
|
//
|
|
|
|
|
// For detailed explanation, please see:
|
|
|
|
|
// <https://github.com/asterinas/asterinas/pull/2555#discussion_r2509179520>
|
|
|
|
|
//
|
|
|
|
|
// Reference:
|
|
|
|
|
// <https://elixir.bootlin.com/linux/v6.16.5/source/mm/shmem.c#L3309-L3310>
|
|
|
|
|
// <https://github.com/google/gvisor/blob/6db745970118635edec4c973f47df2363924d3a7/test/syscalls/linux/memfd.cc#L261-L280>
|
|
|
|
|
let old_size = self.inode.size();
|
2025-12-08 15:29:12 +00:00
|
|
|
let new_size = offset.saturating_add(reader.remain());
|
2025-11-05 15:15:40 +00:00
|
|
|
if new_size > old_size {
|
|
|
|
|
let eof_page = old_size.align_down(PAGE_SIZE);
|
|
|
|
|
if offset >= eof_page {
|
|
|
|
|
return_errno_with_message!(Errno::EPERM, "the file is sealed against growing");
|
|
|
|
|
} else {
|
|
|
|
|
reader.limit(eof_page - offset);
|
|
|
|
|
}
|
2025-10-30 07:54:37 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-05 15:13:34 +00:00
|
|
|
self.inode.write_at(offset, reader, status_flags)
|
2025-10-30 07:54:37 +00:00
|
|
|
}
|
2025-11-05 15:13:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inherit_methods(from = "self.inode")]
|
|
|
|
|
impl Inode for MemfdInode {
|
|
|
|
|
fn metadata(&self) -> Metadata;
|
|
|
|
|
fn size(&self) -> usize;
|
|
|
|
|
fn atime(&self) -> Duration;
|
|
|
|
|
fn set_atime(&self, time: Duration);
|
|
|
|
|
fn mtime(&self) -> Duration;
|
|
|
|
|
fn set_mtime(&self, time: Duration);
|
|
|
|
|
fn ctime(&self) -> Duration;
|
|
|
|
|
fn set_ctime(&self, time: Duration);
|
|
|
|
|
fn ino(&self) -> u64;
|
|
|
|
|
fn type_(&self) -> InodeType;
|
|
|
|
|
fn mode(&self) -> Result<InodeMode>;
|
|
|
|
|
fn owner(&self) -> Result<Uid>;
|
|
|
|
|
fn set_owner(&self, uid: Uid) -> Result<()>;
|
|
|
|
|
fn group(&self) -> Result<Gid>;
|
|
|
|
|
fn set_group(&self, gid: Gid) -> Result<()>;
|
|
|
|
|
fn page_cache(&self) -> Option<Arc<Vmo>>;
|
|
|
|
|
fn extension(&self) -> Option<&Extension>;
|
2025-11-17 15:38:28 +00:00
|
|
|
fn fs_event_publisher(&self) -> &FsEventPublisher;
|
2025-11-05 15:13:34 +00:00
|
|
|
fn set_xattr(
|
|
|
|
|
&self,
|
|
|
|
|
name: XattrName,
|
|
|
|
|
value_reader: &mut VmReader,
|
|
|
|
|
flags: XattrSetFlags,
|
|
|
|
|
) -> Result<()>;
|
|
|
|
|
fn get_xattr(&self, name: XattrName, value_writer: &mut VmWriter) -> Result<usize>;
|
|
|
|
|
fn list_xattr(&self, namespace: XattrNamespace, list_writer: &mut VmWriter) -> Result<usize>;
|
|
|
|
|
fn remove_xattr(&self, name: XattrName) -> Result<()>;
|
2025-10-30 07:54:37 +00:00
|
|
|
|
|
|
|
|
fn resize(&self, new_size: usize) -> Result<()> {
|
2025-10-30 07:54:37 +00:00
|
|
|
let seals = self.seals.lock();
|
2025-11-05 15:15:40 +00:00
|
|
|
let old_size = self.inode.size();
|
|
|
|
|
if seals.contains(FileSeals::F_SEAL_SHRINK) && new_size < old_size {
|
2025-10-30 07:54:37 +00:00
|
|
|
return_errno_with_message!(Errno::EPERM, "the file is sealed against shrinking");
|
|
|
|
|
}
|
2025-11-05 15:15:40 +00:00
|
|
|
if seals.contains(FileSeals::F_SEAL_GROW) && new_size > old_size {
|
2025-10-30 07:54:37 +00:00
|
|
|
return_errno_with_message!(Errno::EPERM, "the file is sealed against growing");
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-30 07:54:37 +00:00
|
|
|
self.inode.resize(new_size)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_mode(&self, mode: InodeMode) -> Result<()> {
|
2025-10-30 07:54:37 +00:00
|
|
|
let seals = self.seals.lock();
|
|
|
|
|
if seals.contains(FileSeals::F_SEAL_EXEC)
|
2025-11-06 08:30:03 +00:00
|
|
|
&& (self.mode().unwrap() ^ mode).intersects(mkmod!(a+x))
|
2025-10-30 07:54:37 +00:00
|
|
|
{
|
|
|
|
|
return_errno_with_message!(
|
|
|
|
|
Errno::EPERM,
|
|
|
|
|
"the file is sealed against modifying executable bits"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-30 07:54:37 +00:00
|
|
|
self.inode.set_mode(mode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn fallocate(&self, mode: FallocMode, offset: usize, len: usize) -> Result<()> {
|
2025-10-30 07:54:37 +00:00
|
|
|
let seals = self.seals.lock();
|
|
|
|
|
if seals.contains(FileSeals::F_SEAL_GROW) && offset + len > self.inode.size() {
|
|
|
|
|
return_errno_with_message!(Errno::EPERM, "the file is sealed against growing");
|
|
|
|
|
}
|
|
|
|
|
if seals.intersects(FileSeals::F_SEAL_WRITE | FileSeals::F_SEAL_FUTURE_WRITE)
|
|
|
|
|
&& mode == FallocMode::PunchHoleKeepSize
|
|
|
|
|
{
|
|
|
|
|
return_errno_with_message!(Errno::EPERM, "the file is sealed against writing");
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-30 07:54:37 +00:00
|
|
|
self.inode.fallocate(mode, offset, len)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn fs(&self) -> Arc<dyn FileSystem> {
|
2025-11-04 08:37:39 +00:00
|
|
|
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/mm/shmem.c#L3828-L3850>
|
|
|
|
|
static MEMFD_TMPFS: Once<Arc<TmpFs>> = Once::new();
|
|
|
|
|
MEMFD_TMPFS.call_once(TmpFs::new).clone()
|
2025-10-30 07:54:37 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct MemfdFile {
|
|
|
|
|
memfd_inode: Arc<dyn Inode>,
|
2025-07-30 09:00:08 +00:00
|
|
|
offset: Mutex<usize>,
|
|
|
|
|
status_flags: AtomicU32,
|
2025-11-11 09:37:26 +00:00
|
|
|
rights: Rights,
|
2025-07-30 09:00:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl MemfdFile {
|
2025-10-30 07:54:37 +00:00
|
|
|
pub fn new(name: &str, memfd_flags: MemfdFlags) -> Result<Self> {
|
2025-07-30 09:00:08 +00:00
|
|
|
if name.len() > MAX_MEMFD_NAME_LEN {
|
|
|
|
|
return_errno_with_message!(Errno::EINVAL, "MemfdManager: `name` is too long.");
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-30 07:54:37 +00:00
|
|
|
let name = format!("/memfd:{}", name);
|
|
|
|
|
|
|
|
|
|
let (allow_sealing, executable) = if memfd_flags.contains(MemfdFlags::MFD_NOEXEC_SEAL) {
|
|
|
|
|
(true, false)
|
|
|
|
|
} else {
|
|
|
|
|
(memfd_flags.contains(MemfdFlags::MFD_ALLOW_SEALING), true)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mode = if executable {
|
|
|
|
|
mkmod!(a+rwx)
|
|
|
|
|
} else {
|
|
|
|
|
mkmod!(a+rw)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let memfd_inode = Arc::new_cyclic(|weak_self| {
|
2025-11-03 06:11:43 +00:00
|
|
|
let ram_inode = RamInode::new_file_detached_in_memfd(
|
|
|
|
|
weak_self,
|
|
|
|
|
mode,
|
|
|
|
|
Uid::new_root(),
|
|
|
|
|
Gid::new_root(),
|
|
|
|
|
);
|
2025-10-30 07:54:37 +00:00
|
|
|
|
2025-10-30 07:54:37 +00:00
|
|
|
let mut seals = FileSeals::empty();
|
|
|
|
|
if !allow_sealing {
|
|
|
|
|
seals |= FileSeals::F_SEAL_SEAL;
|
|
|
|
|
}
|
|
|
|
|
if !executable {
|
|
|
|
|
seals |= FileSeals::F_SEAL_EXEC;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-30 07:54:37 +00:00
|
|
|
MemfdInode {
|
|
|
|
|
inode: ram_inode,
|
|
|
|
|
name,
|
2025-10-30 07:54:37 +00:00
|
|
|
seals: Mutex::new(seals),
|
2025-10-30 07:54:37 +00:00
|
|
|
}
|
|
|
|
|
});
|
2025-07-30 09:00:08 +00:00
|
|
|
|
|
|
|
|
Ok(Self {
|
2025-10-30 07:54:37 +00:00
|
|
|
memfd_inode,
|
2025-07-30 09:00:08 +00:00
|
|
|
offset: Mutex::new(0),
|
|
|
|
|
status_flags: AtomicU32::new(0),
|
2025-11-11 09:37:26 +00:00
|
|
|
rights: Rights::READ | Rights::WRITE,
|
2025-07-30 09:00:08 +00:00
|
|
|
})
|
|
|
|
|
}
|
2025-10-30 07:54:37 +00:00
|
|
|
|
2025-12-05 16:22:12 +00:00
|
|
|
pub fn open(inode: Arc<MemfdInode>, open_args: OpenArgs) -> Result<Self> {
|
2025-11-05 13:55:12 +00:00
|
|
|
let inode: Arc<dyn Inode> = inode;
|
2025-11-11 09:37:26 +00:00
|
|
|
let status_flags = open_args.status_flags;
|
|
|
|
|
let access_mode = open_args.access_mode;
|
|
|
|
|
|
|
|
|
|
if !status_flags.contains(StatusFlags::O_PATH) {
|
|
|
|
|
inode.check_permission(access_mode.into())?;
|
|
|
|
|
}
|
2025-11-05 13:55:12 +00:00
|
|
|
check_open_util(inode.as_ref(), &open_args)?;
|
|
|
|
|
|
2025-11-11 09:37:26 +00:00
|
|
|
if open_args.creation_flags.contains(CreationFlags::O_TRUNC)
|
|
|
|
|
&& !status_flags.contains(StatusFlags::O_PATH)
|
|
|
|
|
{
|
2025-11-05 13:55:12 +00:00
|
|
|
inode.resize(0)?;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-11 09:37:26 +00:00
|
|
|
let rights = if status_flags.contains(StatusFlags::O_PATH) {
|
|
|
|
|
Rights::empty()
|
|
|
|
|
} else {
|
|
|
|
|
access_mode.into()
|
|
|
|
|
};
|
|
|
|
|
|
2025-11-05 13:55:12 +00:00
|
|
|
Ok(Self {
|
|
|
|
|
memfd_inode: inode,
|
|
|
|
|
offset: Mutex::new(0),
|
|
|
|
|
status_flags: AtomicU32::new(open_args.status_flags.bits()),
|
2025-11-11 09:37:26 +00:00
|
|
|
rights,
|
2025-11-05 13:55:12 +00:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-30 07:54:37 +00:00
|
|
|
pub fn add_seals(&self, new_seals: FileSeals) -> Result<()> {
|
2025-11-11 09:37:26 +00:00
|
|
|
if self.rights.is_empty() {
|
|
|
|
|
return_errno_with_message!(Errno::EBADF, "the file is opened as a path");
|
|
|
|
|
}
|
|
|
|
|
if !self.rights.contains(Rights::WRITE) {
|
2025-10-30 07:54:37 +00:00
|
|
|
return_errno_with_message!(Errno::EPERM, "the file is not opened writable");
|
|
|
|
|
}
|
2025-11-11 09:37:26 +00:00
|
|
|
|
2025-10-30 07:54:37 +00:00
|
|
|
self.memfd_inode().add_seals(new_seals)
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-11 09:37:26 +00:00
|
|
|
pub fn get_seals(&self) -> Result<FileSeals> {
|
|
|
|
|
if self.rights.is_empty() {
|
|
|
|
|
return_errno_with_message!(Errno::EBADF, "the file is opened as a path");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(self.memfd_inode().get_seals())
|
2025-10-30 07:54:37 +00:00
|
|
|
}
|
|
|
|
|
|
2025-10-30 07:54:37 +00:00
|
|
|
fn memfd_inode(&self) -> &MemfdInode {
|
|
|
|
|
self.memfd_inode.downcast_ref::<MemfdInode>().unwrap()
|
|
|
|
|
}
|
2025-07-30 09:00:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Pollable for MemfdFile {
|
2025-11-05 15:13:34 +00:00
|
|
|
fn poll(&self, mask: IoEvents, _poller: Option<&mut PollHandle>) -> IoEvents {
|
|
|
|
|
let events = IoEvents::IN | IoEvents::OUT;
|
|
|
|
|
events & mask
|
2025-07-30 09:00:08 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FileLike for MemfdFile {
|
|
|
|
|
fn read(&self, writer: &mut VmWriter) -> Result<usize> {
|
|
|
|
|
let mut offset = self.offset.lock();
|
|
|
|
|
|
|
|
|
|
let len = self.read_at(*offset, writer)?;
|
|
|
|
|
*offset += len;
|
|
|
|
|
|
|
|
|
|
Ok(len)
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-11 09:37:26 +00:00
|
|
|
fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize> {
|
|
|
|
|
if !self.rights.contains(Rights::READ) {
|
|
|
|
|
return_errno_with_message!(Errno::EBADF, "the file is not opened readable");
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-05 15:13:34 +00:00
|
|
|
self.memfd_inode
|
|
|
|
|
.read_at(offset, writer, self.status_flags())
|
2025-11-11 09:37:26 +00:00
|
|
|
}
|
|
|
|
|
|
2025-07-30 09:00:08 +00:00
|
|
|
fn write(&self, reader: &mut VmReader) -> Result<usize> {
|
|
|
|
|
let mut offset = self.offset.lock();
|
|
|
|
|
|
|
|
|
|
if self.status_flags().contains(StatusFlags::O_APPEND) {
|
2025-11-05 15:13:34 +00:00
|
|
|
// FIXME: `O_APPEND` should ensure that new content is appended even if another process
|
|
|
|
|
// is writing to the file concurrently.
|
2025-10-30 07:54:37 +00:00
|
|
|
*offset = self.memfd_inode.size();
|
2025-07-30 09:00:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let len = self.write_at(*offset, reader)?;
|
|
|
|
|
*offset += len;
|
|
|
|
|
|
|
|
|
|
Ok(len)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn write_at(&self, mut offset: usize, reader: &mut VmReader) -> Result<usize> {
|
2025-11-11 09:37:26 +00:00
|
|
|
if !self.rights.contains(Rights::WRITE) {
|
|
|
|
|
return_errno_with_message!(Errno::EBADF, "the file is not opened writable");
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-05 15:13:34 +00:00
|
|
|
let status_flags = self.status_flags();
|
|
|
|
|
if status_flags.contains(StatusFlags::O_APPEND) {
|
|
|
|
|
// If the file has the `O_APPEND` flag, the offset is ignored.
|
|
|
|
|
// FIXME: `O_APPEND` should ensure that new content is appended even if another process
|
|
|
|
|
// is writing to the file concurrently.
|
2025-10-30 07:54:37 +00:00
|
|
|
offset = self.memfd_inode.size();
|
2025-07-30 09:00:08 +00:00
|
|
|
}
|
2025-11-05 15:13:34 +00:00
|
|
|
self.memfd_inode.write_at(offset, reader, status_flags)
|
2025-07-30 09:00:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn resize(&self, new_size: usize) -> Result<()> {
|
2025-11-11 09:37:26 +00:00
|
|
|
if self.rights.is_empty() {
|
|
|
|
|
return_errno_with_message!(Errno::EBADF, "the file is opened as a path");
|
|
|
|
|
}
|
|
|
|
|
if !self.rights.contains(Rights::WRITE) {
|
|
|
|
|
return_errno_with_message!(Errno::EINVAL, "the file is not opened writable");
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-06 08:30:03 +00:00
|
|
|
do_resize_util(self.memfd_inode.as_ref(), self.status_flags(), new_size)
|
2025-07-30 09:00:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn status_flags(&self) -> StatusFlags {
|
|
|
|
|
let bits = self.status_flags.load(Ordering::Relaxed);
|
|
|
|
|
StatusFlags::from_bits(bits).unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_status_flags(&self, new_status_flags: StatusFlags) -> Result<()> {
|
|
|
|
|
self.status_flags
|
|
|
|
|
.store(new_status_flags.bits(), Ordering::Relaxed);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn access_mode(&self) -> AccessMode {
|
2025-11-11 09:37:26 +00:00
|
|
|
self.rights.into()
|
2025-07-30 09:00:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn seek(&self, pos: SeekFrom) -> Result<usize> {
|
2025-11-11 09:37:26 +00:00
|
|
|
if self.rights.is_empty() {
|
|
|
|
|
return_errno_with_message!(Errno::EBADF, "the file is opened as a path");
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-07 02:07:49 +00:00
|
|
|
do_seek_util(&self.offset, pos, Some(self.memfd_inode.size()))
|
2025-07-30 09:00:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn fallocate(&self, mode: FallocMode, offset: usize, len: usize) -> Result<()> {
|
2025-11-11 09:37:26 +00:00
|
|
|
if !self.rights.contains(Rights::WRITE) {
|
|
|
|
|
return_errno_with_message!(Errno::EBADF, "the file is not opened writable");
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-06 08:30:03 +00:00
|
|
|
do_fallocate_util(
|
|
|
|
|
self.memfd_inode.as_ref(),
|
|
|
|
|
self.status_flags(),
|
|
|
|
|
mode,
|
|
|
|
|
offset,
|
|
|
|
|
len,
|
|
|
|
|
)
|
2025-07-30 09:00:08 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-13 16:11:06 +00:00
|
|
|
fn mappable(&self) -> Result<Mappable> {
|
2025-11-11 09:37:26 +00:00
|
|
|
if self.rights.is_empty() {
|
|
|
|
|
return_errno_with_message!(Errno::EBADF, "the file is opened as a path");
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-30 07:54:37 +00:00
|
|
|
Ok(Mappable::Inode(self.memfd_inode.clone()))
|
|
|
|
|
}
|
2025-11-04 09:34:20 +00:00
|
|
|
|
2025-12-04 15:06:55 +00:00
|
|
|
fn ioctl(&self, _raw_ioctl: RawIoctl) -> Result<i32> {
|
2025-11-11 09:37:26 +00:00
|
|
|
if self.rights.is_empty() {
|
|
|
|
|
return_errno_with_message!(Errno::EBADF, "the file is opened as a path");
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-05 15:13:34 +00:00
|
|
|
return_errno_with_message!(Errno::ENOTTY, "ioctl is not supported");
|
2025-11-11 09:37:26 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-04 09:34:20 +00:00
|
|
|
fn inode(&self) -> &Arc<dyn Inode> {
|
|
|
|
|
&self.memfd_inode
|
|
|
|
|
}
|
2025-11-19 07:20:25 +00:00
|
|
|
|
|
|
|
|
fn dump_proc_fdinfo(self: Arc<Self>, fd_flags: FdFlags) -> Box<dyn Display> {
|
|
|
|
|
struct FdInfo {
|
|
|
|
|
inner: Arc<MemfdFile>,
|
|
|
|
|
fd_flags: FdFlags,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for FdInfo {
|
|
|
|
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
|
|
|
let mut flags = self.inner.status_flags().bits() | self.inner.access_mode() as u32;
|
|
|
|
|
if self.fd_flags.contains(FdFlags::CLOEXEC) {
|
|
|
|
|
flags |= CreationFlags::O_CLOEXEC.bits();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writeln!(f, "pos:\t{}", *self.inner.offset.lock())?;
|
|
|
|
|
writeln!(f, "flags:\t0{:o}", flags)?;
|
|
|
|
|
writeln!(f, "mnt_id:\t{}", RESERVED_MOUNT_ID)?;
|
|
|
|
|
writeln!(f, "ino:\t{}", self.inner.inode().ino())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Box::new(FdInfo {
|
|
|
|
|
inner: self,
|
|
|
|
|
fd_flags,
|
|
|
|
|
})
|
|
|
|
|
}
|
2025-10-30 07:54:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bitflags! {
|
|
|
|
|
pub struct MemfdFlags: u32 {
|
|
|
|
|
/// Close on exec.
|
|
|
|
|
const MFD_CLOEXEC = 1 << 0;
|
|
|
|
|
/// Allow sealing operations on this file.
|
|
|
|
|
const MFD_ALLOW_SEALING = 1 << 1;
|
|
|
|
|
/// Create in the hugetlbfs.
|
|
|
|
|
const MFD_HUGETLB = 1 << 2;
|
|
|
|
|
/// Not executable and sealed to prevent changing to executable.
|
|
|
|
|
const MFD_NOEXEC_SEAL = 1 << 3;
|
|
|
|
|
/// Executable.
|
|
|
|
|
const MFD_EXEC = 1 << 4;
|
2025-07-30 09:00:08 +00:00
|
|
|
}
|
|
|
|
|
}
|
2025-10-30 07:54:37 +00:00
|
|
|
|
|
|
|
|
bitflags! {
|
|
|
|
|
pub struct FileSeals: u32 {
|
|
|
|
|
/// Prevent further seals from being set.
|
|
|
|
|
const F_SEAL_SEAL = 0x0001;
|
|
|
|
|
/// Prevent file from shrinking.
|
|
|
|
|
const F_SEAL_SHRINK = 0x0002;
|
|
|
|
|
/// Prevent file from growing.
|
|
|
|
|
const F_SEAL_GROW = 0x0004;
|
|
|
|
|
/// Prevent writes.
|
|
|
|
|
const F_SEAL_WRITE = 0x0008;
|
|
|
|
|
/// Prevent future writes while mapped.
|
|
|
|
|
const F_SEAL_FUTURE_WRITE = 0x0010;
|
|
|
|
|
/// Prevent chmod modifying exec bits.
|
|
|
|
|
const F_SEAL_EXEC = 0x0020;
|
|
|
|
|
}
|
|
|
|
|
}
|