From ae1f15a4490a38fd4e26361c860f9236f1a9c618 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 26 Jul 2023 16:06:53 +0200 Subject: [PATCH] Offset-based file mmap. --- src/mount/redox/resource.rs | 83 +++++++++++++++++++++++-------------- src/mount/redox/scheme.rs | 58 ++++++++++++-------------- 2 files changed, 78 insertions(+), 63 deletions(-) diff --git a/src/mount/redox/resource.rs b/src/mount/redox/resource.rs index 3bbfff96df..2d9d8e7ca6 100644 --- a/src/mount/redox/resource.rs +++ b/src/mount/redox/resource.rs @@ -2,9 +2,10 @@ use std::cmp::{max, min}; use std::slice; use std::time::{SystemTime, UNIX_EPOCH}; +use alloc::collections::BTreeMap; use range_tree::RangeTree; -use syscall::{MAP_PRIVATE, PAGE_SIZE}; +use syscall::{MAP_PRIVATE, PAGE_SIZE, EBADFD}; use syscall::data::{Map, Stat, TimeSpec}; use syscall::error::{Error, Result, EBADF, EINVAL, EISDIR, ENOMEM, EPERM}; use syscall::flag::{ @@ -14,6 +15,8 @@ use syscall::flag::{ use crate::{Disk, Node, Transaction, TreePtr}; +pub type Fmaps = BTreeMap; + pub trait Resource { fn parent_ptr_opt(&self) -> Option>; @@ -31,9 +34,9 @@ pub trait Resource { fn seek(&mut self, offset: isize, whence: usize, tx: &mut Transaction) -> Result; - fn fmap(&mut self, flags: MapFlags, size: usize, offset: u64, tx: &mut Transaction) -> Result; + fn fmap(&mut self, fmaps: &mut Fmaps, flags: MapFlags, size: usize, offset: u64, tx: &mut Transaction) -> Result; - fn funmap(&mut self, offset: u64, size: usize, tx: &mut Transaction) -> Result; + fn funmap(&mut self, fmaps: &mut Fmaps, offset: u64, size: usize, tx: &mut Transaction) -> Result; fn fchmod(&mut self, mode: u16, tx: &mut Transaction) -> Result { let mut node = tx.read_tree(self.node_ptr())?; @@ -114,7 +117,7 @@ pub trait Resource { Ok(0) } - fn sync(&mut self, tx: &mut Transaction) -> Result; + fn sync(&mut self, fmaps: &mut Fmaps, tx: &mut Transaction) -> Result; fn truncate(&mut self, len: usize, tx: &mut Transaction) -> Result; @@ -205,10 +208,10 @@ impl Resource for DirResource { Ok(self.seek) } - fn fmap(&mut self, _flags: MapFlags, _size: usize, _offset: u64, _tx: &mut Transaction) -> Result { + fn fmap(&mut self, _fmaps: &mut Fmaps, _flags: MapFlags, _size: usize, _offset: u64, _tx: &mut Transaction) -> Result { Err(Error::new(EBADF)) } - fn funmap(&mut self, _offset: u64, _size: usize, _tx: &mut Transaction) -> Result { + fn funmap(&mut self, _fmaps: &mut Fmaps, _offset: u64, _size: usize, _tx: &mut Transaction) -> Result { Err(Error::new(EBADF)) } @@ -220,7 +223,7 @@ impl Resource for DirResource { &self.path } - fn sync(&mut self, _tx: &mut Transaction) -> Result { + fn sync(&mut self, _fmaps: &mut Fmaps, _tx: &mut Transaction) -> Result { Err(Error::new(EBADF)) } @@ -306,8 +309,20 @@ pub struct FileResource { flags: usize, seek: isize, uid: u32, +} +pub struct FileMmapInfo { base: *mut u8, - fmaps: RangeTree, + ranges: RangeTree, + pub open_fds: usize, +} +impl Default for FileMmapInfo { + fn default() -> Self { + Self { + base: core::ptr::null_mut(), + ranges: RangeTree::new(), + open_fds: 0, + } + } } impl FileResource { @@ -324,9 +339,7 @@ impl FileResource { node_ptr, flags, seek: 0, - base: core::ptr::null_mut(), uid, - fmaps: RangeTree::new(), } } } @@ -352,8 +365,6 @@ impl Resource for FileResource { flags: self.flags, seek: self.seek, uid: self.uid, - fmaps: RangeTree::new(), - base: core::ptr::null_mut(), })) } @@ -412,7 +423,7 @@ impl Resource for FileResource { Ok(self.seek) } - fn fmap(&mut self, flags: MapFlags, unaligned_size: usize, offset: u64, tx: &mut Transaction) -> Result { + fn fmap(&mut self, fmaps: &mut Fmaps, flags: MapFlags, unaligned_size: usize, offset: u64, tx: &mut Transaction) -> Result { //dbg!(&self.fmaps); let accmode = self.flags & O_ACCMODE; if flags.contains(PROT_READ) && !(accmode == O_RDWR || accmode == O_RDONLY) { @@ -428,10 +439,14 @@ impl Resource for FileResource { // program can always map anonymous RW-, read from a file, then remap as R-E. But it might // be usable as a hint, prohibiting direct executable mmaps at least. - let max_offset = self.fmaps.end(); + // TODO: Pass entry directory to Resource trait functions, since the node_ptr can be + // obtained by the caller. + let fmap_info = fmaps.get_mut(&self.node_ptr.id()).ok_or(Error::new(EBADFD))?; + + let max_offset = fmap_info.ranges.end(); if offset + aligned_size as u64 > max_offset { - if self.base.is_null() { - self.base = unsafe { + if fmap_info.base.is_null() { + fmap_info.base = unsafe { syscall::fmap(!0, &Map { size: offset as usize + aligned_size, // PRIVATE/SHARED doesn't matter once the pages are passed in the fmap @@ -462,13 +477,13 @@ impl Resource for FileResource { } } - self.base = unsafe { - syscall::syscall5(syscall::SYS_MREMAP, self.base as usize, common_size, 0, common_size, syscall::MremapFlags::empty().bits())? as *mut u8 + fmap_info.base = unsafe { + syscall::syscall5(syscall::SYS_MREMAP, fmap_info.base as usize, common_size, 0, common_size, syscall::MremapFlags::empty().bits())? as *mut u8 }; } } - let affected_fmaps = self.fmaps.remove_and_unused(offset..offset + aligned_size as u64); + let affected_fmaps = fmap_info.ranges.remove_and_unused(offset..offset + aligned_size as u64); for (range, v_opt) in affected_fmaps { //dbg!(&range); @@ -476,32 +491,34 @@ impl Resource for FileResource { fmap.rc += 1; fmap.flags |= flags; - self.fmaps.insert(range.start, range.end - range.start, fmap); + fmap_info.ranges.insert(range.start, range.end - range.start, fmap); } else { - let map = unsafe { Fmap::new(self.node_ptr, flags, unaligned_size, offset, self.base, tx)? }; - self.fmaps.insert(offset, aligned_size as u64, map); + let map = unsafe { Fmap::new(self.node_ptr, flags, unaligned_size, offset, fmap_info.base, tx)? }; + fmap_info.ranges.insert(offset, aligned_size as u64, map); } } //dbg!(&self.fmaps); - Ok(self.base as usize + offset as usize) + Ok(fmap_info.base as usize + offset as usize) } - fn funmap(&mut self, offset: u64, size: usize, tx: &mut Transaction) -> Result { + fn funmap(&mut self, fmaps: &mut Fmaps, offset: u64, size: usize, tx: &mut Transaction) -> Result { + let fmap_info = fmaps.get_mut(&self.node_ptr.id()).ok_or(Error::new(EBADFD))?; + //dbg!(&self.fmaps); //dbg!(self.fmaps.conflicts(offset..offset + size as u64).collect::>()); - let mut affected_fmaps = self.fmaps.remove(offset..offset + size as u64); + let mut affected_fmaps = fmap_info.ranges.remove(offset..offset + size as u64); for (range, mut fmap) in affected_fmaps { fmap.rc = fmap.rc.checked_sub(1).unwrap(); //log::info!("SYNCING {}..{}", range.start, range.end); unsafe { - fmap.sync(self.node_ptr, self.base, range.start, (range.end - range.start) as usize, tx)?; + fmap.sync(self.node_ptr, fmap_info.base, range.start, (range.end - range.start) as usize, tx)?; } if fmap.rc > 0 { - self.fmaps.insert(range.start, range.end - range.start, fmap); + fmap_info.ranges.insert(range.start, range.end - range.start, fmap); } } //dbg!(&self.fmaps); @@ -524,10 +541,12 @@ impl Resource for FileResource { &self.path } - fn sync(&mut self, tx: &mut Transaction) -> Result { - for (range, fmap) in self.fmaps.iter_mut() { - unsafe { - fmap.sync(self.node_ptr, self.base, range.start, (range.end - range.start) as usize, tx)?; + fn sync(&mut self, fmaps: &mut Fmaps, tx: &mut Transaction) -> Result { + if let Some(fmap_info) = fmaps.get_mut(&self.node_ptr.id()) { + for (range, fmap) in fmap_info.ranges.iter_mut() { + unsafe { + fmap.sync(self.node_ptr, fmap_info.base, range.start, (range.end - range.start) as usize, tx)?; + } } } @@ -583,6 +602,7 @@ impl Resource for FileResource { impl Drop for FileResource { fn drop(&mut self) { + /* if !self.fmaps.is_empty() { eprintln!( "redoxfs: file {} still has {} fmaps!", @@ -590,6 +610,7 @@ impl Drop for FileResource { self.fmaps.len() ); } + */ } } diff --git a/src/mount/redox/scheme.rs b/src/mount/redox/scheme.rs index 660812ce97..89fd88d3d8 100644 --- a/src/mount/redox/scheme.rs +++ b/src/mount/redox/scheme.rs @@ -3,6 +3,7 @@ use std::str; use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::{SystemTime, UNIX_EPOCH}; +use syscall::{EBADFD, MunmapFlags}; use syscall::data::{Map, Stat, StatVfs, TimeSpec}; use syscall::error::{ Error, Result, EACCES, EBADF, EBUSY, EEXIST, EINVAL, EISDIR, ELOOP, ENOENT, ENOTDIR, ENOTEMPTY, @@ -23,14 +24,14 @@ pub struct FileScheme { fs: FileSystem, next_id: AtomicUsize, files: BTreeMap>>, - fmap: BTreeMap, + fmap: super::resource::Fmaps, } impl FileScheme { pub fn new(name: String, fs: FileSystem) -> FileScheme { FileScheme { - name: name, - fs: fs, + name, + fs, next_id: AtomicUsize::new(1), files: BTreeMap::new(), fmap: BTreeMap::new(), @@ -400,6 +401,8 @@ impl SchemeMut for FileScheme { } } }; + self.fmap.entry(resource.node_ptr().id()).or_insert_with(Default::default).open_fds += 1; + let id = self.next_id.fetch_add(1, Ordering::SeqCst); self.files.insert(id, resource); @@ -533,6 +536,7 @@ impl SchemeMut for FileScheme { return Err(Error::new(EBADF)); }; + self.fmap.get_mut(&resource.node_ptr().id()).ok_or(Error::new(EBADFD))?.open_fds += 1; let id = self.next_id.fetch_add(1, Ordering::SeqCst); self.files.insert(id, resource); @@ -755,11 +759,10 @@ impl SchemeMut for FileScheme { fn fsync(&mut self, id: usize) -> Result { // println!("Fsync {}", id); - if let Some(file) = self.files.get_mut(&id) { - self.fs.tx(|tx| file.sync(tx)) - } else { - Err(Error::new(EBADF)) - } + let file = self.files.get_mut(&id).ok_or(Error::new(EBADF))?; + let fmaps = &mut self.fmap; + + self.fs.tx(|tx| file.sync(fmaps, tx)) } fn ftruncate(&mut self, id: usize, len: usize) -> Result { @@ -780,40 +783,31 @@ impl SchemeMut for FileScheme { } } - fn mmap_prep(&mut self, id: usize, flags: MapFlags, size: usize, offset: u64) -> Result { + fn mmap_prep(&mut self, id: usize, offset: u64, size: usize, flags: MapFlags) -> Result { println!("Mmap {}, {:?} {} {}", id, flags, size, offset); let file = self.files.get_mut(&id).ok_or(Error::new(EBADF))?; + let fmaps = &mut self.fmap; - self.fs.tx(|tx| file.fmap(flags, size, offset, tx)) + self.fs.tx(|tx| file.fmap(fmaps, flags, size, offset, tx)) } - fn munmap(&mut self, id: usize, size: usize, offset: u64) -> Result { + fn munmap(&mut self, id: usize, offset: u64, size: usize, flags: MunmapFlags) -> Result { println!("Munmap {}, {} {}", id, size, offset); let file = self.files.get_mut(&id).ok_or(Error::new(EINVAL))?; + let fmaps = &mut self.fmap; - self.fs.tx(|tx| file.funmap(offset, size, tx)) + self.fs.tx(|tx| file.funmap(fmaps, offset, size, tx)) } - /* - //TODO: implement (length is ignored!) - fn funmap(&mut self, address: usize, length: usize) -> Result { - println!("redoxfs: funmap 0x{:X}, {}", address, length); - if let Some(id) = self.fmap.remove(&address) { - if let Some(file) = self.files.get_mut(&id) { - self.fs.tx(|tx| file.funmap(address, tx)) - } else { - Err(Error::new(EINVAL)) - } - } else { - Err(Error::new(EINVAL)) - } - }*/ - fn close(&mut self, id: usize) -> Result { // println!("Close {}", id); - if self.files.remove(&id).is_some() { - Ok(0) - } else { - Err(Error::new(EBADF)) - } + let file = self.files.remove(&id).ok_or(Error::new(EBADF))?; + let file_info = self.fmap.get_mut(&file.node_ptr().id()).ok_or(Error::new(EBADFD))?; + + file_info.open_fds = file_info.open_fds.checked_sub(1).expect("open_fds not tracked correctly"); + + // TODO: If open_fds reaches zero and there are no hardlinks (directory entries) to any + // particular inode, remove that inode here. + + Ok(0) } }