Offset-based file mmap.
This commit is contained in:
+52
-31
@@ -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<u32, FileMmapInfo>;
|
||||
|
||||
pub trait Resource<D: Disk> {
|
||||
fn parent_ptr_opt(&self) -> Option<TreePtr<Node>>;
|
||||
|
||||
@@ -31,9 +34,9 @@ pub trait Resource<D: Disk> {
|
||||
|
||||
fn seek(&mut self, offset: isize, whence: usize, tx: &mut Transaction<D>) -> Result<isize>;
|
||||
|
||||
fn fmap(&mut self, flags: MapFlags, size: usize, offset: u64, tx: &mut Transaction<D>) -> Result<usize>;
|
||||
fn fmap(&mut self, fmaps: &mut Fmaps, flags: MapFlags, size: usize, offset: u64, tx: &mut Transaction<D>) -> Result<usize>;
|
||||
|
||||
fn funmap(&mut self, offset: u64, size: usize, tx: &mut Transaction<D>) -> Result<usize>;
|
||||
fn funmap(&mut self, fmaps: &mut Fmaps, offset: u64, size: usize, tx: &mut Transaction<D>) -> Result<usize>;
|
||||
|
||||
fn fchmod(&mut self, mode: u16, tx: &mut Transaction<D>) -> Result<usize> {
|
||||
let mut node = tx.read_tree(self.node_ptr())?;
|
||||
@@ -114,7 +117,7 @@ pub trait Resource<D: Disk> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn sync(&mut self, tx: &mut Transaction<D>) -> Result<usize>;
|
||||
fn sync(&mut self, fmaps: &mut Fmaps, tx: &mut Transaction<D>) -> Result<usize>;
|
||||
|
||||
fn truncate(&mut self, len: usize, tx: &mut Transaction<D>) -> Result<usize>;
|
||||
|
||||
@@ -205,10 +208,10 @@ impl<D: Disk> Resource<D> for DirResource {
|
||||
Ok(self.seek)
|
||||
}
|
||||
|
||||
fn fmap(&mut self, _flags: MapFlags, _size: usize, _offset: u64, _tx: &mut Transaction<D>) -> Result<usize> {
|
||||
fn fmap(&mut self, _fmaps: &mut Fmaps, _flags: MapFlags, _size: usize, _offset: u64, _tx: &mut Transaction<D>) -> Result<usize> {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
fn funmap(&mut self, _offset: u64, _size: usize, _tx: &mut Transaction<D>) -> Result<usize> {
|
||||
fn funmap(&mut self, _fmaps: &mut Fmaps, _offset: u64, _size: usize, _tx: &mut Transaction<D>) -> Result<usize> {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
|
||||
@@ -220,7 +223,7 @@ impl<D: Disk> Resource<D> for DirResource {
|
||||
&self.path
|
||||
}
|
||||
|
||||
fn sync(&mut self, _tx: &mut Transaction<D>) -> Result<usize> {
|
||||
fn sync(&mut self, _fmaps: &mut Fmaps, _tx: &mut Transaction<D>) -> Result<usize> {
|
||||
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<Fmap>,
|
||||
ranges: RangeTree<Fmap>,
|
||||
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<D: Disk> Resource<D> for FileResource {
|
||||
flags: self.flags,
|
||||
seek: self.seek,
|
||||
uid: self.uid,
|
||||
fmaps: RangeTree::new(),
|
||||
base: core::ptr::null_mut(),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -412,7 +423,7 @@ impl<D: Disk> Resource<D> for FileResource {
|
||||
Ok(self.seek)
|
||||
}
|
||||
|
||||
fn fmap(&mut self, flags: MapFlags, unaligned_size: usize, offset: u64, tx: &mut Transaction<D>) -> Result<usize> {
|
||||
fn fmap(&mut self, fmaps: &mut Fmaps, flags: MapFlags, unaligned_size: usize, offset: u64, tx: &mut Transaction<D>) -> Result<usize> {
|
||||
//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<D: Disk> Resource<D> 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<D: Disk> Resource<D> 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<D: Disk> Resource<D> 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<D>) -> Result<usize> {
|
||||
fn funmap(&mut self, fmaps: &mut Fmaps, offset: u64, size: usize, tx: &mut Transaction<D>) -> Result<usize> {
|
||||
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::<Vec<_>>());
|
||||
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<D: Disk> Resource<D> for FileResource {
|
||||
&self.path
|
||||
}
|
||||
|
||||
fn sync(&mut self, tx: &mut Transaction<D>) -> Result<usize> {
|
||||
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<D>) -> Result<usize> {
|
||||
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<D: Disk> Resource<D> 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()
|
||||
);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+26
-32
@@ -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<D: Disk> {
|
||||
fs: FileSystem<D>,
|
||||
next_id: AtomicUsize,
|
||||
files: BTreeMap<usize, Box<dyn Resource<D>>>,
|
||||
fmap: BTreeMap<u64, usize>,
|
||||
fmap: super::resource::Fmaps,
|
||||
}
|
||||
|
||||
impl<D: Disk> FileScheme<D> {
|
||||
pub fn new(name: String, fs: FileSystem<D>) -> FileScheme<D> {
|
||||
FileScheme {
|
||||
name: name,
|
||||
fs: fs,
|
||||
name,
|
||||
fs,
|
||||
next_id: AtomicUsize::new(1),
|
||||
files: BTreeMap::new(),
|
||||
fmap: BTreeMap::new(),
|
||||
@@ -400,6 +401,8 @@ impl<D: Disk> SchemeMut for FileScheme<D> {
|
||||
}
|
||||
}
|
||||
};
|
||||
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<D: Disk> SchemeMut for FileScheme<D> {
|
||||
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<D: Disk> SchemeMut for FileScheme<D> {
|
||||
|
||||
fn fsync(&mut self, id: usize) -> Result<usize> {
|
||||
// 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<usize> {
|
||||
@@ -780,40 +783,31 @@ impl<D: Disk> SchemeMut for FileScheme<D> {
|
||||
}
|
||||
}
|
||||
|
||||
fn mmap_prep(&mut self, id: usize, flags: MapFlags, size: usize, offset: u64) -> Result<usize> {
|
||||
fn mmap_prep(&mut self, id: usize, offset: u64, size: usize, flags: MapFlags) -> Result<usize> {
|
||||
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<usize> {
|
||||
fn munmap(&mut self, id: usize, offset: u64, size: usize, flags: MunmapFlags) -> Result<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user