Remove `Rights` from VMO

This commit is contained in:
Wang Siyuan 2025-10-28 08:50:57 +00:00 committed by Tate, Hongliang Tian
parent 77ef9e0368
commit 89e759894f
23 changed files with 163 additions and 624 deletions

View File

@ -6,7 +6,6 @@
use core::ops::Range;
use align_ext::AlignExt;
use aster_rights::Full;
use bitvec::prelude::*;
use super::{
@ -37,7 +36,7 @@ pub(super) struct ExfatBitmap {
impl ExfatBitmap {
pub(super) fn load(
fs_weak: Weak<ExfatFs>,
root_page_cache: Vmo<Full>,
root_page_cache: &Vmo,
root_chain: ExfatChain,
) -> Result<Self> {
let dentry_iterator = ExfatDentryIterator::new(root_page_cache, 0, None)?;

View File

@ -2,7 +2,6 @@
use core::{fmt::Display, ops::Range};
use aster_rights::Full;
use ostd::mm::VmIo;
use super::{
@ -278,7 +277,7 @@ impl ExfatDentrySet {
Self::new(dentries, false)
}
pub(super) fn read_from(page_cache: Vmo<Full>, offset: usize) -> Result<Self> {
pub(super) fn read_from(page_cache: &Vmo, offset: usize) -> Result<Self> {
let mut iter = ExfatDentryIterator::new(page_cache, offset, None)?;
let primary_dentry_result = iter.next();
@ -439,17 +438,17 @@ impl Checksum for ExfatDentrySet {
}
}
pub(super) struct ExfatDentryIterator {
pub(super) struct ExfatDentryIterator<'a> {
/// The dentry position in current inode.
entry: u32,
/// The page cache of the iterated inode.
page_cache: Vmo<Full>,
page_cache: &'a Vmo,
/// Remaining size that can be iterated. If none, iterate through the whole cluster chain.
size: Option<usize>,
}
impl ExfatDentryIterator {
pub fn new(page_cache: Vmo<Full>, offset: usize, size: Option<usize>) -> Result<Self> {
impl<'a> ExfatDentryIterator<'a> {
pub fn new(page_cache: &'a Vmo, offset: usize, size: Option<usize>) -> Result<Self> {
if size.is_some() && size.unwrap() % DENTRY_SIZE != 0 {
return_errno_with_message!(Errno::EINVAL, "remaining size unaligned to dentry size")
}
@ -466,7 +465,7 @@ impl ExfatDentryIterator {
}
}
impl Iterator for ExfatDentryIterator {
impl Iterator for ExfatDentryIterator<'_> {
type Item = Result<ExfatDentry>;
fn next(&mut self) -> Option<Self::Item> {

View File

@ -98,17 +98,12 @@ impl ExfatFs {
let root = ExfatInode::build_root_inode(weak_fs.clone(), root_chain.clone())?;
let upcase_table = ExfatUpcaseTable::load(
weak_fs.clone(),
root.page_cache().unwrap(),
root_chain.clone(),
)?;
let root_page_cache = root.page_cache().unwrap();
let bitmap = ExfatBitmap::load(
weak_fs.clone(),
root.page_cache().unwrap(),
root_chain.clone(),
)?;
let upcase_table =
ExfatUpcaseTable::load(weak_fs.clone(), &root_page_cache, root_chain.clone())?;
let bitmap = ExfatBitmap::load(weak_fs.clone(), &root_page_cache, root_chain.clone())?;
*exfat_fs.bitmap.lock() = bitmap;
*exfat_fs.upcase_table.lock() = upcase_table;

View File

@ -12,7 +12,6 @@ use aster_block::{
id::{Bid, BlockId},
BLOCK_SIZE,
};
use aster_rights::Full;
use ostd::mm::{io_util::HasVmReaderWriter, Segment, VmIo};
use super::{
@ -247,7 +246,7 @@ impl ExfatInodeInner {
return Ok((0, 0));
}
let iterator = ExfatDentryIterator::new(self.page_cache.pages().dup(), 0, Some(self.size))?;
let iterator = ExfatDentryIterator::new(self.page_cache.pages(), 0, Some(self.size))?;
let mut sub_inodes = 0;
let mut sub_dirs = 0;
for dentry_result in iterator {
@ -314,7 +313,7 @@ impl ExfatInodeInner {
// Need to read the latest dentry set from parent inode.
let mut dentry_set =
ExfatDentrySet::read_from(page_cache.dup(), self.dentry_entry as usize * DENTRY_SIZE)?;
ExfatDentrySet::read_from(&page_cache, self.dentry_entry as usize * DENTRY_SIZE)?;
let mut file_dentry = dentry_set.get_file_dentry();
let mut stream_dentry = dentry_set.get_stream_dentry();
@ -376,7 +375,7 @@ impl ExfatInodeInner {
let fs = self.fs();
let cluster_size = fs.cluster_size();
let mut iter = ExfatDentryIterator::new(self.page_cache.pages().dup(), offset, None)?;
let mut iter = ExfatDentryIterator::new(self.page_cache.pages(), offset, None)?;
let mut dir_read = 0;
let mut current_off = offset;
@ -828,7 +827,7 @@ impl ExfatInode {
let inner = self.inner.upread();
let dentry_iterator =
ExfatDentryIterator::new(inner.page_cache.pages().dup(), 0, Some(inner.size))?;
ExfatDentryIterator::new(inner.page_cache.pages(), 0, Some(inner.size))?;
let mut contiguous_unused = 0;
let mut entry_id = 0;
@ -1225,8 +1224,8 @@ impl Inode for ExfatInode {
self.inner.read().fs()
}
fn page_cache(&self) -> Option<Vmo<Full>> {
Some(self.inner.read().page_cache.pages().dup())
fn page_cache(&self) -> Option<Arc<Vmo>> {
Some(self.inner.read().page_cache.pages().clone())
}
fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize> {

View File

@ -4,7 +4,6 @@
#![expect(unused_variables)]
use align_ext::AlignExt;
use aster_rights::Full;
use super::{
constants::UNICODE_SIZE,
@ -33,7 +32,7 @@ impl ExfatUpcaseTable {
pub(super) fn load(
fs_weak: Weak<ExfatFs>,
root_page_cache: Vmo<Full>,
root_page_cache: &Vmo,
root_chain: ExfatChain,
) -> Result<Self> {
let dentry_iterator = ExfatDentryIterator::new(root_page_cache, 0, None)?;

View File

@ -4,8 +4,6 @@
use core::time::Duration;
use aster_rights::Full;
use crate::{
fs::{
ext2::{FilePerm, Inode as Ext2Inode},
@ -91,7 +89,7 @@ impl Inode for Ext2Inode {
Ok(())
}
fn page_cache(&self) -> Option<Vmo<Full>> {
fn page_cache(&self) -> Option<Arc<Vmo>> {
Some(self.page_cache())
}

View File

@ -82,8 +82,8 @@ impl Inode {
self.fs.upgrade().unwrap()
}
pub fn page_cache(&self) -> Vmo<Full> {
self.inner.read().page_cache.pages().dup()
pub fn page_cache(&self) -> Arc<Vmo> {
self.inner.read().page_cache.pages().clone()
}
pub fn metadata(&self) -> Metadata {

View File

@ -11,7 +11,6 @@ pub(super) use aster_block::{
id::Bid,
BlockDevice, BLOCK_SIZE,
};
pub(super) use aster_rights::Full;
pub(super) use ostd::{
mm::{Frame, FrameAllocOptions, Segment, USegment, VmIo},
sync::{RwMutex, RwMutexReadGuard, RwMutexWriteGuard},

View File

@ -10,7 +10,6 @@ use core::{
use align_ext::AlignExt;
use aster_block::BLOCK_SIZE;
use aster_rights::Full;
use hashbrown::HashSet;
use inherit_methods_macro::inherit_methods;
use ostd::{
@ -445,7 +444,7 @@ impl OverlayInode {
self.type_
}
pub fn page_cache(&self) -> Option<Vmo<Full>> {
pub fn page_cache(&self) -> Option<Arc<Vmo>> {
let _ = self.get_top_valid_inode().page_cache()?;
// Do copy-up for the potential memory mapping operations
let upper = self.build_upper_recursively_if_needed().unwrap();
@ -928,7 +927,7 @@ impl Inode for OverlayInode {
fn set_mtime(&self, time: Duration);
fn ctime(&self) -> Duration;
fn set_ctime(&self, time: Duration);
fn page_cache(&self) -> Option<Vmo<Full>>;
fn page_cache(&self) -> Option<Arc<Vmo>>;
fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize>;
fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize>;
fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result<usize>;

View File

@ -7,11 +7,10 @@ use core::{
use align_ext::AlignExt;
use aster_block::bio::BioWaiter;
use aster_rights::Full;
use aster_util::slot_vec::SlotVec;
use hashbrown::HashMap;
use ostd::{
mm::{io_util::HasVmReaderWriter, HasSize, VmIo},
mm::{io_util::HasVmReaderWriter, HasSize},
sync::{PreemptDisabled, RwLockWriteGuard},
};
@ -529,10 +528,10 @@ impl PageCacheBackend for RamInode {
}
impl Inode for RamInode {
fn page_cache(&self) -> Option<Vmo<Full>> {
fn page_cache(&self) -> Option<Arc<Vmo>> {
self.inner
.as_file()
.map(|page_cache| page_cache.pages().dup())
.map(|page_cache| page_cache.pages().clone())
}
fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize> {

View File

@ -4,7 +4,6 @@
use core::{any::TypeId, time::Duration};
use aster_rights::Full;
use core2::io::{Error as IoError, ErrorKind as IoErrorKind, Result as IoResult, Write};
use ostd::task::Task;
@ -292,7 +291,7 @@ pub trait Inode: Any + Sync + Send {
fn set_ctime(&self, time: Duration);
fn page_cache(&self) -> Option<Vmo<Full>> {
fn page_cache(&self) -> Option<Arc<Vmo>> {
None
}

View File

@ -9,7 +9,6 @@ use core::{
use align_ext::AlignExt;
use aster_block::bio::{BioStatus, BioWaiter};
use aster_rights::Full;
use lru::LruCache;
use ostd::{
impl_untyped_frame_meta_for,
@ -22,7 +21,7 @@ use crate::{
};
pub struct PageCache {
pages: Vmo<Full>,
pages: Arc<Vmo>,
manager: Arc<PageCacheManager>,
}
@ -30,7 +29,7 @@ impl PageCache {
/// Creates an empty size page cache associated with a new backend.
pub fn new(backend: Weak<dyn PageCacheBackend>) -> Result<Self> {
let manager = Arc::new(PageCacheManager::new(backend));
let pages = VmoOptions::<Full>::new(0)
let pages = VmoOptions::new(0)
.flags(VmoFlags::RESIZABLE)
.pager(manager.clone())
.alloc()?;
@ -43,7 +42,7 @@ impl PageCache {
/// This size usually corresponds to the size of the backend.
pub fn with_capacity(capacity: usize, backend: Weak<dyn PageCacheBackend>) -> Result<Self> {
let manager = Arc::new(PageCacheManager::new(backend));
let pages = VmoOptions::<Full>::new(capacity)
let pages = VmoOptions::new(capacity)
.flags(VmoFlags::RESIZABLE)
.pager(manager.clone())
.alloc()?;
@ -51,9 +50,7 @@ impl PageCache {
}
/// Returns the Vmo object.
// TODO: The capability is too highrestrict it to eliminate the possibility of misuse.
// For example, the `resize` api should be forbidded.
pub fn pages(&self) -> &Vmo<Full> {
pub fn pages(&self) -> &Arc<Vmo> {
&self.pages
}

View File

@ -348,7 +348,7 @@ impl<KInode: SysTreeInodeTy + Send + Sync + 'static> Inode for KInode {
unimplemented!("fs() method should be implemented by the concrete inode type");
}
default fn page_cache(&self) -> Option<crate::vm::vmo::Vmo<aster_rights::Full>> {
default fn page_cache(&self) -> Option<Arc<crate::vm::vmo::Vmo>> {
None
}

View File

@ -19,7 +19,6 @@ use core::{
};
use align_ext::AlignExt;
use aster_rights::Full;
use ostd::mm::{VmIo, MAX_USERSPACE_VADDR};
use self::aux_vec::{AuxKey, AuxVec};
@ -29,7 +28,7 @@ use crate::{
vm::{
perms::VmPerms,
vmar::Vmar,
vmo::{Vmo, VmoOptions, VmoRightsOp},
vmo::{Vmo, VmoOptions},
},
};
@ -173,7 +172,7 @@ impl InitStack {
self.set_uninitialized();
let vmo = {
let vmo_options = VmoOptions::<Full>::new(self.max_size);
let vmo_options = VmoOptions::new(self.max_size);
vmo_options.alloc()?
};
let vmar_map_options = {
@ -182,7 +181,7 @@ impl InitStack {
debug_assert!(map_addr % PAGE_SIZE == 0);
vmar.new_map(self.max_size, perms)?
.offset(map_addr)
.vmo(vmo.dup().to_dyn())
.vmo(vmo.clone())
};
vmar_map_options.build()?;
@ -231,7 +230,7 @@ impl InitStack {
/// A writer to initialize the content of an `InitStack`.
struct InitStackWriter<'a> {
pos: &'a AtomicUsize,
vmo: Vmo<Full>,
vmo: Arc<Vmo>,
argv: Vec<CString>,
envp: Vec<CString>,
auxvec: AuxVec,

View File

@ -20,12 +20,7 @@ use crate::{
},
prelude::*,
process::process_vm::{AuxKey, AuxVec},
vm::{
perms::VmPerms,
util::duplicate_frame,
vmar::Vmar,
vmo::{CommitFlags, VmoRightsOp},
},
vm::{perms::VmPerms, util::duplicate_frame, vmar::Vmar, vmo::CommitFlags},
};
/// Loads elf to the process VMAR.
@ -324,14 +319,10 @@ fn map_segment_vmo(
debug_assert!(file_offset % PAGE_SIZE == virtual_addr % PAGE_SIZE);
let segment_vmo = {
let inode = elf_file.inode();
inode
.page_cache()
.ok_or(Error::with_message(
Errno::ENOENT,
"executable has no page cache",
))?
.to_dyn()
.dup()?
inode.page_cache().ok_or(Error::with_message(
Errno::ENOENT,
"executable has no page cache",
))?
};
let total_map_size = {
@ -352,7 +343,7 @@ fn map_segment_vmo(
if segment_size != 0 {
let mut vm_map_options = vmar
.new_map(segment_size, perms)?
.vmo(segment_vmo.dup()?)
.vmo(segment_vmo.clone())
.vmo_offset(segment_offset)
.can_overwrite(true);
vm_map_options = vm_map_options.offset(offset).handle_page_faults_around();
@ -498,7 +489,7 @@ fn map_vdso_to_vmar(vmar: &Vmar) -> Option<Vaddr> {
let options = vmar
.new_map(VDSO_VMO_LAYOUT.size, VmPerms::empty())
.unwrap()
.vmo(vdso_vmo.dup().unwrap());
.vmo(vdso_vmo);
let vdso_vmo_base = options.build().unwrap();
let vdso_data_base = vdso_vmo_base + VDSO_VMO_LAYOUT.data_segment_offset;

View File

@ -3,7 +3,6 @@
//! This mod defines mmap flags and the handler to syscall mmap
use align_ext::AlignExt;
use aster_rights::Rights;
use super::SyscallReturn;
use crate::{
@ -116,7 +115,7 @@ fn do_sys_mmap(
// Anonymous shared mapping should share the same memory pages.
if option.typ() == MMapType::Shared {
let shared_vmo = {
let vmo_options: VmoOptions<Rights> = VmoOptions::new(len);
let vmo_options = VmoOptions::new(len);
vmo_options.alloc()?
};
options = options.vmo(shared_vmo);

View File

@ -15,7 +15,6 @@
use alloc::sync::Arc;
use core::{mem::ManuallyDrop, time::Duration};
use aster_rights::Rights;
use aster_time::{read_monotonic_time, Instant};
use aster_util::coeff::Coeff;
use ostd::{
@ -238,7 +237,7 @@ impl Vdso {
vdso_data.init();
let (vdso_vmo, data_frame) = {
let vmo_options = VmoOptions::<Rights>::new(VDSO_VMO_LAYOUT.size);
let vmo_options = VmoOptions::new(VDSO_VMO_LAYOUT.size);
let vdso_vmo = vmo_options.alloc().unwrap();
// Write vDSO data to vDSO VMO.
vdso_vmo
@ -259,7 +258,7 @@ impl Vdso {
Self {
data: SpinLock::new(vdso_data),
vmo: Arc::new(vdso_vmo),
vmo: vdso_vmo,
data_frame,
}
}

View File

@ -8,7 +8,6 @@ mod vm_mapping;
use core::{array, num::NonZeroUsize, ops::Range};
use align_ext::AlignExt;
use aster_rights::Rights;
use aster_util::per_cpu_counter::PerCpuCounter;
use ostd::{
cpu::CpuId,
@ -32,10 +31,7 @@ use crate::{
prelude::*,
process::{Process, ProcessVm, ResourceType},
thread::exception::PageFaultInfo,
vm::{
perms::VmPerms,
vmo::{Vmo, VmoRightsOp},
},
vm::{perms::VmPerms, vmo::Vmo},
};
/// Virtual Memory Address Regions (VMARs) are a type of capability that manages
@ -71,13 +67,12 @@ impl Vmar {
/// # Examples
///
/// ```
/// use aster_rights::Rights;
/// use ostd::mm::PAGE_SIZE;
///
/// use crate::vm::{perms::VmPerms, vmar::Vmar, vmo::VmoOptions};
///
/// let vmar = Vmar::new();
/// let vmo = VmoOptions::<Rights>::new(10 * PAGE_SIZE).alloc().unwrap();
/// let vmo = VmoOptions::new(10 * PAGE_SIZE).alloc().unwrap();
/// let target_vaddr = 0x1234000;
/// let real_vaddr = vmar
/// // Create a 4 * PAGE_SIZE bytes, read-only mapping
@ -95,7 +90,7 @@ impl Vmar {
/// ```
///
/// For more details on the available options, see `VmarMapOptions`.
pub fn new_map(&self, size: usize, perms: VmPerms) -> Result<VmarMapOptions<Rights>> {
pub fn new_map(&self, size: usize, perms: VmPerms) -> Result<VmarMapOptions> {
Ok(VmarMapOptions::new(self, size, perms))
}
@ -221,7 +216,7 @@ impl Vmar {
let base = vm_mapping.map_to_addr();
// Clone the `VmMapping` to the new VMAR.
let new_mapping = vm_mapping.new_fork()?;
let new_mapping = vm_mapping.new_fork();
new_inner.insert_without_try_merge(new_mapping);
// Protect the mapping and copy to the new page table for COW.
@ -416,7 +411,7 @@ impl Vmar {
old_mapping
};
// Note that we have ensured that `new_size >= old_size` at the beginning.
let new_mapping = old_mapping.clone_for_remap_at(new_range.start).unwrap();
let new_mapping = old_mapping.clone_for_remap_at(new_range.start);
inner.insert_try_merge(new_mapping.enlarge(new_size - old_size));
let preempt_guard = disable_preempt();
@ -953,9 +948,9 @@ fn cow_copy_pt(src: &mut CursorMut<'_>, dst: &mut CursorMut<'_>, size: usize) ->
/// Options for creating a new mapping. The mapping is not allowed to overlap
/// with any child VMARs. And unless specified otherwise, it is not allowed
/// to overlap with any existing mapping, either.
pub struct VmarMapOptions<'a, R> {
pub struct VmarMapOptions<'a> {
parent: &'a Vmar,
vmo: Option<Vmo<R>>,
vmo: Option<Arc<Vmo>>,
mappable: Option<Mappable>,
perms: VmPerms,
may_perms: VmPerms,
@ -970,13 +965,9 @@ pub struct VmarMapOptions<'a, R> {
handle_page_faults_around: bool,
}
impl<'a, R> VmarMapOptions<'a, R> {
/// Creates a default set of options with the VMO and the memory access
impl<'a> VmarMapOptions<'a> {
/// Creates a default set of options with the size and the memory access
/// permissions.
///
/// The VMO must have access rights that correspond to the memory
/// access permissions. For example, if `perms` contains `VmPerms::Write`,
/// then `vmo.rights()` should contain `Rights::WRITE`.
pub fn new(parent: &'a Vmar, size: usize, perms: VmPerms) -> Self {
Self {
parent,
@ -1026,7 +1017,7 @@ impl<'a, R> VmarMapOptions<'a, R> {
/// # Panics
///
/// This function panics if a [`Mappable`] is already provided.
pub fn vmo(mut self, vmo: Vmo<R>) -> Self {
pub fn vmo(mut self, vmo: Arc<Vmo>) -> Self {
if self.mappable.is_some() {
panic!("Cannot set `vmo` when `mappable` is already set");
}
@ -1098,9 +1089,7 @@ impl<'a, R> VmarMapOptions<'a, R> {
self.handle_page_faults_around = true;
self
}
}
impl VmarMapOptions<'_, Rights> {
/// Binds memory to map based on the [`Mappable`] enum.
///
/// This method accepts file-specific details, like a page cache (inode)
@ -1119,24 +1108,14 @@ impl VmarMapOptions<'_, Rights> {
// Verify whether the page cache inode is valid.
if let Mappable::Inode(ref inode) = mappable {
self.vmo = Some(
inode
.page_cache()
.expect("Map an inode without page cache")
.to_dyn(),
);
self.vmo = Some(inode.page_cache().expect("Map an inode without page cache"));
}
self.mappable = Some(mappable);
self
}
}
impl<R> VmarMapOptions<'_, R>
where
Vmo<R>: VmoRightsOp,
{
/// Creates the mapping and adds it to the parent VMAR.
///
/// All options will be checked at this point.
@ -1206,15 +1185,14 @@ where
Mappable::Inode(inode_handle) => {
// Since `Mappable::Inode` is provided, it is
// reasonable to assume that the VMO is provided.
let mapped_mem =
MappedMemory::Vmo(MappedVmo::new(vmo.unwrap().to_dyn(), vmo_offset));
let mapped_mem = MappedMemory::Vmo(MappedVmo::new(vmo.unwrap(), vmo_offset));
(mapped_mem, Some(inode_handle), None)
}
Mappable::IoMem(iomem) => (MappedMemory::Device, None, Some(iomem)),
}
} else if let Some(vmo) = vmo {
(
MappedMemory::Vmo(MappedVmo::new(vmo.to_dyn(), vmo_offset)),
MappedMemory::Vmo(MappedVmo::new(vmo, vmo_offset)),
None,
None,
)
@ -1271,11 +1249,10 @@ where
return_errno_with_message!(Errno::EINVAL, "invalid offset");
}
}
self.check_perms()?;
Ok(())
self.check_perms()
}
/// Checks whether the permissions of the mapping is subset of vmo rights.
/// Checks whether the permissions of the mapping is valid.
fn check_perms(&self) -> Result<()> {
if !VmPerms::ALL_MAY_PERMS.contains(self.may_perms)
|| !VmPerms::ALL_PERMS.contains(self.perms)
@ -1284,14 +1261,7 @@ where
}
let vm_perms = self.perms | self.may_perms;
vm_perms.check()?;
let Some(vmo) = &self.vmo else {
return Ok(());
};
let perm_rights = Rights::from(vm_perms);
vmo.check_rights(perm_rights)
vm_perms.check()
}
}

View File

@ -111,18 +111,18 @@ impl VmMapping {
}
}
pub(super) fn new_fork(&self) -> Result<VmMapping> {
Ok(VmMapping {
mapped_mem: self.mapped_mem.dup()?,
pub(super) fn new_fork(&self) -> VmMapping {
VmMapping {
mapped_mem: self.mapped_mem.dup(),
inode: self.inode.clone(),
..*self
})
}
}
pub(super) fn clone_for_remap_at(&self, va: Vaddr) -> Result<VmMapping> {
let mut vm_mapping = self.new_fork()?;
pub(super) fn clone_for_remap_at(&self, va: Vaddr) -> VmMapping {
let mut vm_mapping = self.new_fork();
vm_mapping.map_to_addr = va;
Ok(vm_mapping)
vm_mapping
}
/// Returns the mapping's start address.
@ -506,13 +506,11 @@ impl VmMapping {
debug_assert!(self.map_to_addr < at && at < self.map_end());
debug_assert!(at % PAGE_SIZE == 0);
let (l_mapped_mem, r_mapped_mem) = match &self.mapped_mem {
let (l_mapped_mem, r_mapped_mem) = match self.mapped_mem {
MappedMemory::Vmo(vmo) => {
let at_offset = vmo.offset() + (at - self.map_to_addr);
(
MappedMemory::Vmo(MappedVmo::new(vmo.vmo().dup()?, vmo.offset())),
MappedMemory::Vmo(MappedVmo::new(vmo.vmo().dup()?, at_offset)),
)
let r_mapped_vmo = MappedVmo::new(vmo.vmo().clone(), at_offset);
(MappedMemory::Vmo(vmo), MappedMemory::Vmo(r_mapped_vmo))
}
MappedMemory::Anonymous => {
// For anonymous mappings, we create new anonymous mappings for the split parts
@ -683,12 +681,12 @@ pub(super) enum MappedMemory {
impl MappedMemory {
/// Duplicates the mapped memory capability.
pub(super) fn dup(&self) -> Result<Self> {
Ok(match self {
pub(super) fn dup(&self) -> Self {
match self {
MappedMemory::Anonymous => MappedMemory::Anonymous,
MappedMemory::Vmo(v) => MappedMemory::Vmo(v.dup()?),
MappedMemory::Vmo(v) => MappedMemory::Vmo(v.dup()),
MappedMemory::Device => MappedMemory::Device,
})
}
}
}
@ -696,14 +694,14 @@ impl MappedMemory {
/// that need to be provided to mappings from the VMO.
#[derive(Debug)]
pub(super) struct MappedVmo {
vmo: Vmo,
vmo: Arc<Vmo>,
/// Represents the mapped offset in the VMO for the mapping.
offset: usize,
}
impl MappedVmo {
/// Creates a `MappedVmo` used for the mapping.
pub(super) fn new(vmo: Vmo, offset: usize) -> Self {
pub(super) fn new(vmo: Arc<Vmo>, offset: usize) -> Self {
Self { vmo, offset }
}
@ -758,7 +756,7 @@ impl MappedVmo {
}
/// Gets a reference to the underlying VMO.
pub fn vmo(&self) -> &Vmo {
pub fn vmo(&self) -> &Arc<Vmo> {
&self.vmo
}
@ -768,11 +766,11 @@ impl MappedVmo {
}
/// Duplicates the capability.
pub fn dup(&self) -> Result<Self> {
Ok(Self {
vmo: self.vmo.dup()?,
pub fn dup(&self) -> Self {
Self {
vmo: self.vmo.clone(),
offset: self.offset,
})
}
}
}
@ -802,13 +800,13 @@ fn try_merge(left: &VmMapping, right: &VmMapping) -> Option<VmMapping> {
let l_vmo = l_vmo_obj.vmo();
let r_vmo = r_vmo_obj.vmo();
if Arc::ptr_eq(&l_vmo.0, &r_vmo.0) {
if Arc::ptr_eq(l_vmo, r_vmo) {
let is_offset_contiguous =
l_vmo_obj.offset() + left.map_size() == r_vmo_obj.offset();
if !is_offset_contiguous {
return None;
}
MappedMemory::Vmo(l_vmo_obj.dup().ok()?)
MappedMemory::Vmo(l_vmo_obj.dup())
} else {
return None;
}

View File

@ -1,174 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
use core::ops::Range;
use aster_rights::{Rights, TRights};
use ostd::mm::{UFrame, VmIo, VmIoFill};
use super::{CommitFlags, Vmo, VmoCommitError, VmoRightsOp};
use crate::prelude::*;
impl Vmo<Rights> {
/// Commits a page at specific offset.
///
/// If the commit operation needs to perform I/O, it will return a [`VmoCommitError::NeedIo`].
///
/// # Access rights
///
/// The method requires the Write right.
pub fn try_commit_page(&self, offset: usize) -> core::result::Result<UFrame, VmoCommitError> {
self.check_rights(Rights::WRITE)?;
self.0.try_commit_page(offset)
}
/// Commits a page at a specific page index.
///
/// This method may involve I/O operations if the VMO needs to fetch
/// a page from the underlying page cache.
///
/// # Access rights
///
/// The method requires the Write right.
pub fn commit_on(&self, page_idx: usize, commit_flags: CommitFlags) -> Result<UFrame> {
self.check_rights(Rights::WRITE)?;
self.0.commit_on(page_idx, commit_flags)
}
/// Traverses the indices within a specified range of a VMO sequentially.
///
/// For each index position, you have the option to commit the page as well as
/// perform other operations.
///
/// Once a commit operation needs to perform I/O, it will return a [`VmoCommitError::NeedIo`].
///
/// # Access rights
///
/// The method requires the Write right.
pub(in crate::vm) fn try_operate_on_range<F>(
&self,
range: &Range<usize>,
operate: F,
) -> core::result::Result<(), VmoCommitError>
where
F: FnMut(
&mut dyn FnMut() -> core::result::Result<UFrame, VmoCommitError>,
) -> core::result::Result<(), VmoCommitError>,
{
self.check_rights(Rights::WRITE)?;
self.0.try_operate_on_range(range, operate)
}
/// Decommits the pages specified in the range (in bytes).
///
/// The range must be within the size of the VMO.
///
/// The start and end addresses will be rounded down and up to page boundaries.
///
/// # Access rights
///
/// The method requires the Write right.
pub fn decommit(&self, range: Range<usize>) -> Result<()> {
self.check_rights(Rights::WRITE)?;
self.0.decommit(range)
}
/// Resizes the VMO by giving a new size.
///
/// The VMO must be resizable.
///
/// The new size will be rounded up to page boundaries.
///
/// # Access rights
///
/// The method requires the Write right.
pub fn resize(&self, new_size: usize) -> Result<()> {
self.check_rights(Rights::WRITE)?;
self.0.resize(new_size)
}
/// Clears the specified range by writing zeros.
///
/// # Access rights
///
/// The method requires the Write right.
pub fn clear(&self, range: Range<usize>) -> Result<()> {
self.check_rights(Rights::WRITE)?;
self.0.clear(range)
}
/// Duplicates the capability.
///
/// # Access rights
///
/// The method requires the Dup right.
pub fn dup(&self) -> Result<Self> {
self.check_rights(Rights::DUP)?;
Ok(Self(self.0.clone(), self.1))
}
/// Replaces the page at the `page_idx` in the VMO with the input `page`.
///
/// # Access rights
///
/// The method requires the Write right.
pub fn replace(&self, page: UFrame, page_idx: usize) -> Result<()> {
self.check_rights(Rights::WRITE)?;
self.0.replace(page, page_idx)
}
/// Restricts the access rights given the mask.
pub fn restrict(mut self, mask: Rights) -> Self {
self.1 |= mask;
self
}
/// Converts to a static capability.
#[expect(clippy::wrong_self_convention)]
pub fn to_static<R1: TRights>(self) -> Result<Vmo<R1>> {
self.check_rights(Rights::from_bits(R1::BITS).ok_or(Error::new(Errno::EINVAL))?)?;
Ok(Vmo(self.0, R1::new()))
}
}
impl VmIo for Vmo<Rights> {
fn read(&self, offset: usize, writer: &mut VmWriter) -> ostd::Result<()> {
self.check_rights(Rights::READ)?;
self.0.read(offset, writer)?;
Ok(())
}
fn write(&self, offset: usize, reader: &mut VmReader) -> ostd::Result<()> {
self.check_rights(Rights::WRITE)?;
self.0.write(offset, reader)?;
Ok(())
}
}
impl VmIoFill for Vmo<Rights> {
fn fill_zeros(
&self,
offset: usize,
len: usize,
) -> core::result::Result<(), (ostd::Error, usize)> {
// TODO: Support efficient `fill_zeros()`.
for i in 0..len {
match self.write_slice(offset + i, &[0u8]) {
Ok(()) => continue,
Err(err) => return Err((err, i)),
}
}
Ok(())
}
}
impl VmoRightsOp for Vmo<Rights> {
fn rights(&self) -> Rights {
self.1
}
/// Converts to a dynamic capability.
fn to_dyn(self) -> Vmo<Rights> {
let rights = self.rights();
Vmo(self.0, rights)
}
}

View File

@ -11,19 +11,18 @@ use core::{
};
use align_ext::AlignExt;
use aster_rights::Rights;
use ostd::{
mm::{io_util::HasVmReaderWriter, FrameAllocOptions, UFrame, VmReader, VmWriter},
mm::{
io_util::HasVmReaderWriter, FrameAllocOptions, UFrame, VmIo, VmIoFill, VmReader, VmWriter,
},
task::disable_preempt,
};
use xarray::{Cursor, LockedXArray, XArray};
use crate::prelude::*;
mod dyn_cap;
mod options;
mod pager;
mod static_cap;
pub use options::VmoOptions;
pub use pager::Pager;
@ -31,14 +30,17 @@ pub use pager::Pager;
/// Virtual Memory Objects (VMOs) are a type of capability that represents a
/// range of memory pages.
///
/// Broadly speaking, there are two types of VMO:
/// 1. File-backed VMO: the VMO backed by a file and resides in the page cache,
/// which includes a [`Pager`] to provide it with actual pages.
/// 2. Anonymous VMO: the VMO without a file backup, which does not have a `Pager`.
///
/// # Features
///
/// * **I/O interface.** A VMO provides read and write methods to access the
/// memory pages that it contain.
/// * **On-demand paging.** The memory pages of a VMO (except for _contiguous_
/// VMOs) are allocated lazily when the page is first accessed.
/// * **Access control.** As capabilities, VMOs restrict the
/// accessible range of memory and the allowed I/O operations.
/// * **Device driver support.** If specified upon creation, VMOs will be
/// backed by physically contiguous memory pages starting at a target address.
/// * **File system support.** By default, a VMO's memory pages are initially
@ -47,22 +49,6 @@ pub use pager::Pager;
/// With this pager mechanism, file systems can easily implement page caches
/// with VMOs by attaching the VMOs to pagers backed by inodes.
///
/// # Capabilities
///
/// As a capability, each VMO is associated with a set of access rights,
/// whose semantics are explained below.
///
/// * The Dup right allows duplicating a VMO and creating children out of
/// a VMO.
/// * The Read, Write, Exec rights allow creating memory mappings with
/// readable, writable, and executable access permissions, respectively.
/// * The Read and Write rights allow the VMO to be read from and written to
/// directly.
/// * The Write right allows resizing a resizable VMO.
///
/// VMOs are implemented with two flavors of capabilities:
/// the dynamic one (`Vmo<Rights>`) and the static one (`Vmo<R: TRights>).
///
/// # Examples
///
/// For creating root VMOs, see [`VmoOptions`].
@ -74,27 +60,28 @@ pub use pager::Pager;
/// Compared with `UFrame`,
/// `Vmo` is easier to use (by offering more powerful APIs) and
/// harder to misuse (thanks to its nature of being capability).
#[derive(Debug)]
pub struct Vmo<R = Rights>(pub(super) Arc<Vmo_>, R);
pub struct Vmo {
pager: Option<Arc<dyn Pager>>,
/// Flags
flags: VmoFlags,
/// The virtual pages where the VMO resides.
pages: XArray<UFrame>,
/// The size of the VMO.
///
/// Note: This size may not necessarily match the size of the `pages`, but it is
/// required here that modifications to the size can only be made after locking
/// the [`XArray`] in the `pages` field. Therefore, the size read after locking the
/// `pages` will be the latest size.
size: AtomicUsize,
}
/// Functions exist both for static capbility and dynamic capability
pub trait VmoRightsOp {
/// Returns the access rights.
fn rights(&self) -> Rights;
/// Checks whether current rights meet the input `rights`.
fn check_rights(&self, rights: Rights) -> Result<()> {
if self.rights().contains(rights) {
Ok(())
} else {
return_errno_with_message!(Errno::EACCES, "VMO rights are insufficient");
}
impl Debug for Vmo {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Vmo")
.field("flags", &self.flags)
.field("size", &self.size())
.finish()
}
/// Converts to a dynamic capability.
fn to_dyn(self) -> Vmo<Rights>
where
Self: Sized;
}
bitflags! {
@ -136,36 +123,6 @@ impl From<ostd::Error> for VmoCommitError {
}
}
/// `Vmo_` is the structure that actually manages the content of VMO.
///
/// Broadly speaking, there are two types of VMO:
/// 1. File-backed VMO: the VMO backed by a file and resides in the page cache,
/// which includes a [`Pager`] to provide it with actual pages.
/// 2. Anonymous VMO: the VMO without a file backup, which does not have a `Pager`.
pub(super) struct Vmo_ {
pager: Option<Arc<dyn Pager>>,
/// Flags
flags: VmoFlags,
/// The virtual pages where the VMO resides.
pages: XArray<UFrame>,
/// The size of the VMO.
///
/// Note: This size may not necessarily match the size of the `pages`, but it is
/// required here that modifications to the size can only be made after locking
/// the [`XArray`] in the `pages` field. Therefore, the size read after locking the
/// `pages` will be the latest size.
size: AtomicUsize,
}
impl Debug for Vmo_ {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Vmo_")
.field("flags", &self.flags)
.field("size", &self.size())
.finish()
}
}
bitflags! {
/// Commit Flags.
pub struct CommitFlags: u8 {
@ -181,7 +138,7 @@ impl CommitFlags {
}
}
impl Vmo_ {
impl Vmo {
/// Prepares a new `UFrame` for the target index in pages, returns this new frame.
///
/// This operation may involve I/O operations if the VMO is backed by a pager.
@ -324,6 +281,10 @@ impl Vmo_ {
}
/// Decommits a range of pages in the VMO.
///
/// The range must be within the size of the VMO.
///
/// The start and end addresses will be rounded down and up to page boundaries.
pub fn decommit(&self, range: Range<usize>) -> Result<()> {
let locked_pages = self.pages.lock();
if range.end > self.size() {
@ -401,7 +362,7 @@ impl Vmo_ {
Ok(())
}
/// Clears the target range in current VMO.
/// Clears the target range in current VMO by writing zeros.
pub fn clear(&self, range: Range<usize>) -> Result<()> {
let buffer = vec![0u8; range.end - range.start];
let mut reader = VmReader::from(buffer.as_slice()).to_fallible();
@ -415,6 +376,10 @@ impl Vmo_ {
}
/// Resizes current VMO to target size.
///
/// The VMO must be resizable.
///
/// The new size will be rounded up to page boundaries.
pub fn resize(&self, new_size: usize) -> Result<()> {
assert!(self.flags.contains(VmoFlags::RESIZABLE));
let new_size = new_size.align_up(PAGE_SIZE);
@ -473,6 +438,7 @@ impl Vmo_ {
self.flags
}
/// Replaces the page at the `page_idx` in the VMO with the input `page`.
fn replace(&self, page: UFrame, page_idx: usize) -> Result<()> {
let mut locked_pages = self.pages.lock();
if page_idx >= self.size() / PAGE_SIZE {
@ -484,15 +450,32 @@ impl Vmo_ {
}
}
impl<R> Vmo<R> {
/// Returns the size (in bytes) of a VMO.
pub fn size(&self) -> usize {
self.0.size()
impl VmIo for Vmo {
fn read(&self, offset: usize, writer: &mut VmWriter) -> ostd::Result<()> {
self.read(offset, writer)?;
Ok(())
}
/// Returns the flags of a VMO.
pub fn flags(&self) -> VmoFlags {
self.0.flags()
fn write(&self, offset: usize, reader: &mut VmReader) -> ostd::Result<()> {
self.write(offset, reader)?;
Ok(())
}
}
impl VmIoFill for Vmo {
fn fill_zeros(
&self,
offset: usize,
len: usize,
) -> core::result::Result<(), (ostd::Error, usize)> {
// TODO: Support efficient `fill_zeros()`.
for i in 0..len {
match self.write_slice(offset + i, &[0u8]) {
Ok(()) => continue,
Err(err) => return Err((err, i)),
}
}
Ok(())
}
}

View File

@ -1,24 +1,21 @@
// SPDX-License-Identifier: MPL-2.0
#![expect(unused_variables)]
//! Options for allocating root and child VMOs.
use core::sync::atomic::AtomicUsize;
use align_ext::AlignExt;
use aster_rights::{Rights, TRightSet, TRights};
use ostd::mm::{FrameAllocOptions, UFrame, USegment};
use xarray::XArray;
use super::{Pager, Vmo, VmoFlags};
use crate::{prelude::*, vm::vmo::Vmo_};
use crate::prelude::*;
/// Options for allocating a root VMO.
///
/// # Examples
///
/// Creating a VMO as a _dynamic_ capability with full access rights:
/// Creating a VMO:
/// ```
/// use aster_nix::vm::{PAGE_SIZE, VmoOptions};
///
@ -27,16 +24,6 @@ use crate::{prelude::*, vm::vmo::Vmo_};
/// .unwrap();
/// ```
///
/// Creating a VMO as a _static_ capability with all access rights:
/// ```
/// use aster_nix::prelude::*;
/// use aster_nix::vm::{PAGE_SIZE, VmoOptions};
///
/// let vmo = VmoOptions::<Full>::new(PAGE_SIZE)
/// .alloc()
/// .unwrap();
/// ```
///
/// Creating a resizable VMO backed by 10 memory pages that may not be
/// physically contiguous:
///
@ -48,14 +35,13 @@ use crate::{prelude::*, vm::vmo::Vmo_};
/// .alloc()
/// .unwrap();
/// ```
pub struct VmoOptions<R = Rights> {
pub struct VmoOptions {
size: usize,
flags: VmoFlags,
rights: Option<R>,
pager: Option<Arc<dyn Pager>>,
}
impl<R> VmoOptions<R> {
impl VmoOptions {
/// Creates a default set of options with the specified size of the VMO
/// (in bytes).
///
@ -64,7 +50,6 @@ impl<R> VmoOptions<R> {
Self {
size,
flags: VmoFlags::empty(),
rights: None,
pager: None,
}
}
@ -86,44 +71,21 @@ impl<R> VmoOptions<R> {
}
}
impl VmoOptions<Rights> {
impl VmoOptions {
/// Allocates the VMO according to the specified options.
///
/// # Access rights
///
/// The VMO is initially assigned full access rights.
pub fn alloc(self) -> Result<Vmo<Rights>> {
pub fn alloc(self) -> Result<Arc<Vmo>> {
let VmoOptions {
size, flags, pager, ..
} = self;
let vmo_ = alloc_vmo_(size, flags, pager)?;
Ok(Vmo(Arc::new(vmo_), Rights::all()))
let vmo = alloc_vmo(size, flags, pager)?;
Ok(Arc::new(vmo))
}
}
impl<R: TRights> VmoOptions<TRightSet<R>> {
/// Allocates the VMO according to the specified options.
///
/// # Access rights
///
/// The VMO is initially assigned the access rights represented
/// by `R: TRights`.
pub fn alloc(self) -> Result<Vmo<TRightSet<R>>> {
let VmoOptions {
size,
flags,
rights,
pager,
} = self;
let vmo_ = alloc_vmo_(size, flags, pager)?;
Ok(Vmo(Arc::new(vmo_), TRightSet(R::new())))
}
}
fn alloc_vmo_(size: usize, flags: VmoFlags, pager: Option<Arc<dyn Pager>>) -> Result<Vmo_> {
fn alloc_vmo(size: usize, flags: VmoFlags, pager: Option<Arc<dyn Pager>>) -> Result<Vmo> {
let size = size.align_up(PAGE_SIZE);
let pages = committed_pages_if_continuous(flags, size)?;
Ok(Vmo_ {
Ok(Vmo {
pager,
flags,
pages,
@ -153,14 +115,13 @@ fn committed_pages_if_continuous(flags: VmoFlags, size: usize) -> Result<XArray<
#[cfg(ktest)]
mod test {
use aster_rights::Full;
use ostd::{mm::VmIo, prelude::*};
use super::*;
#[ktest]
fn alloc_vmo() {
let vmo = VmoOptions::<Full>::new(PAGE_SIZE).alloc().unwrap();
let vmo = VmoOptions::new(PAGE_SIZE).alloc().unwrap();
assert_eq!(vmo.size(), PAGE_SIZE);
// the vmo is zeroed once allocated
assert_eq!(vmo.read_val::<usize>(0).unwrap(), 0);
@ -168,7 +129,7 @@ mod test {
#[ktest]
fn alloc_continuous_vmo() {
let vmo = VmoOptions::<Full>::new(10 * PAGE_SIZE)
let vmo = VmoOptions::new(10 * PAGE_SIZE)
.flags(VmoFlags::CONTIGUOUS)
.alloc()
.unwrap();
@ -177,7 +138,7 @@ mod test {
#[ktest]
fn write_and_read() {
let vmo = VmoOptions::<Full>::new(PAGE_SIZE).alloc().unwrap();
let vmo = VmoOptions::new(PAGE_SIZE).alloc().unwrap();
let val = 42u8;
// write val
vmo.write_val(111, &val).unwrap();
@ -191,7 +152,7 @@ mod test {
#[ktest]
fn resize() {
let vmo = VmoOptions::<Full>::new(PAGE_SIZE)
let vmo = VmoOptions::new(PAGE_SIZE)
.flags(VmoFlags::RESIZABLE)
.alloc()
.unwrap();

View File

@ -1,169 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
use core::ops::Range;
use aster_rights::{Dup, Rights, TRightSet, TRights, Write};
use aster_rights_proc::require;
use ostd::mm::{UFrame, VmIo, VmIoFill};
use super::{CommitFlags, Vmo, VmoCommitError, VmoRightsOp};
use crate::prelude::*;
impl<R: TRights> Vmo<TRightSet<R>> {
/// Commits a page at specific offset.
///
/// If the commit operation needs to perform I/O, it will return a [`VmoCommitError::NeedIo`].
///
/// # Access rights
///
/// The method requires the Write right.
#[require(R > Write)]
pub fn try_commit_page(&self, offset: usize) -> core::result::Result<UFrame, VmoCommitError> {
self.check_rights(Rights::WRITE)?;
self.0.try_commit_page(offset)
}
/// Commits a page at a specific page index.
///
/// This method may involve I/O operations if the VMO needs to fetch
/// a page from the underlying page cache.
///
/// # Access rights
///
/// The method requires the Write right.
#[require(R > Write)]
pub fn commit_on(&self, page_idx: usize, commit_flags: CommitFlags) -> Result<UFrame> {
self.0.commit_on(page_idx, commit_flags)
}
/// Traverses the indices within a specified range of a VMO sequentially.
///
/// For each index position, you have the option to commit the page as well as
/// perform other operations.
///
/// Once a commit operation needs to perform I/O, it will return a [`VmoCommitError::NeedIo`].
///
/// # Access rights
///
/// The method requires the Write right.
#[require(R > Write)]
pub(in crate::vm) fn try_operate_on_range<F>(
&self,
range: &Range<usize>,
operate: F,
) -> core::result::Result<(), VmoCommitError>
where
F: FnMut(
&mut dyn FnMut() -> core::result::Result<UFrame, VmoCommitError>,
) -> core::result::Result<(), VmoCommitError>,
{
self.0.try_operate_on_range(range, operate)
}
/// Decommits the pages specified in the range (in bytes).
///
/// The range must be within the size of the VMO.
///
/// The start and end addresses will be rounded down and up to page boundaries.
///
/// # Access rights
///
/// The method requires the Write right.
#[require(R > Write)]
pub fn decommit(&self, range: Range<usize>) -> Result<()> {
self.0.decommit(range)
}
/// Resize the VMO by giving a new size.
///
/// The VMO must be resizable.
///
/// The new size will be rounded up to page boundaries.
///
/// # Access rights
///
/// The method requires the Write right.
#[require(R > Write)]
pub fn resize(&self, new_size: usize) -> Result<()> {
self.0.resize(new_size)
}
/// Clear the specified range by writing zeros.
///
/// # Access rights
///
/// The method requires the Write right.
#[require(R > Write)]
pub fn clear(&self, range: Range<usize>) -> Result<()> {
self.0.clear(range)
}
/// Duplicate the capability.
///
/// # Access rights
///
/// The method requires the Dup right.
#[require(R > Dup)]
pub fn dup(&self) -> Self {
Vmo(self.0.clone(), self.1)
}
/// Replaces the page at the `page_idx` in the VMO with the input `page`.
///
/// # Access rights
///
/// The method requires the Write right.
#[require(R > Write)]
pub fn replace(&self, page: UFrame, page_idx: usize) -> Result<()> {
self.0.replace(page, page_idx)
}
/// Strict the access rights.
#[require(R > R1)]
pub fn restrict<R1: TRights>(self) -> Vmo<TRightSet<R1>> {
Vmo(self.0, TRightSet(R1::new()))
}
}
impl<R: TRights> VmIo for Vmo<TRightSet<R>> {
fn read(&self, offset: usize, writer: &mut VmWriter) -> ostd::Result<()> {
self.check_rights(Rights::READ)?;
self.0.read(offset, writer)?;
Ok(())
}
fn write(&self, offset: usize, reader: &mut VmReader) -> ostd::Result<()> {
self.check_rights(Rights::WRITE)?;
self.0.write(offset, reader)?;
Ok(())
}
}
impl<R: TRights> VmIoFill for Vmo<TRightSet<R>> {
fn fill_zeros(
&self,
offset: usize,
len: usize,
) -> core::result::Result<(), (ostd::Error, usize)> {
// TODO: Support efficient `fill_zeros()`.
for i in 0..len {
match self.write_slice(offset + i, &[0u8]) {
Ok(()) => continue,
Err(err) => return Err((err, i)),
}
}
Ok(())
}
}
impl<R: TRights> VmoRightsOp for Vmo<TRightSet<R>> {
fn rights(&self) -> Rights {
Rights::from_bits(R::BITS).unwrap()
}
/// Converts to a dynamic capability.
fn to_dyn(self) -> Vmo<Rights> {
let rights = self.rights();
Vmo(self.0, rights)
}
}