WIP: Use improved mmap interface.

This commit is contained in:
4lDO2
2023-07-20 16:58:58 +02:00
parent e7e0e3ce8d
commit 5db0da52e2
6 changed files with 236 additions and 118 deletions
+6 -2
View File
@@ -1,3 +1,5 @@
#![feature(int_roundings)]
extern crate libc;
#[cfg(target_os = "redox")]
@@ -90,14 +92,16 @@ fn bootloader_password() -> Option<Vec<u8>> {
let mut password = Vec::with_capacity(size);
unsafe {
let password_map = syscall::physmap(addr, size, syscall::PhysmapFlags::empty())
let aligned_size = size.next_multiple_of(syscall::PAGE_SIZE);
let password_map = syscall::physmap(addr, aligned_size, syscall::PhysmapFlags::empty())
.expect("failed to map REDOXFS_PASSWORD");
for i in 0..size {
password.push(*((password_map + i) as *const u8));
}
let _ = syscall::physunmap(password_map);
let _ = syscall::funmap(password_map, aligned_size);
}
Some(password)
}
+2
View File
@@ -4,6 +4,8 @@
// Used often in generating redox_syscall errors
#![allow(clippy::or_fun_call)]
#![feature(int_roundings)]
extern crate alloc;
use core::sync::atomic::AtomicUsize;
+164 -64
View File
@@ -1,8 +1,10 @@
use std::cmp::{max, min};
use std::collections::BTreeMap;
use std::slice;
use std::time::{SystemTime, UNIX_EPOCH};
use range_tree::RangeTree;
use syscall::{MAP_PRIVATE, PAGE_SIZE};
use syscall::data::{Map, Stat, TimeSpec};
use syscall::error::{Error, Result, EBADF, EINVAL, EISDIR, ENOMEM, EPERM};
use syscall::flag::{
@@ -29,9 +31,9 @@ pub trait Resource<D: Disk> {
fn seek(&mut self, offset: isize, whence: usize, tx: &mut Transaction<D>) -> Result<isize>;
fn fmap(&mut self, map: &Map, tx: &mut Transaction<D>) -> Result<usize>;
fn fmap(&mut self, flags: MapFlags, size: usize, offset: u64, tx: &mut Transaction<D>) -> Result<usize>;
fn funmap(&mut self, address: usize, tx: &mut Transaction<D>) -> Result<usize>;
fn funmap(&mut self, 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())?;
@@ -203,10 +205,10 @@ impl<D: Disk> Resource<D> for DirResource {
Ok(self.seek)
}
fn fmap(&mut self, _map: &Map, _tx: &mut Transaction<D>) -> Result<usize> {
fn fmap(&mut self, _flags: MapFlags, _size: usize, _offset: u64, _tx: &mut Transaction<D>) -> Result<usize> {
Err(Error::new(EBADF))
}
fn funmap(&mut self, _address: usize, _tx: &mut Transaction<D>) -> Result<usize> {
fn funmap(&mut self, _offset: u64, _size: usize, _tx: &mut Transaction<D>) -> Result<usize> {
Err(Error::new(EBADF))
}
@@ -231,68 +233,64 @@ impl<D: Disk> Resource<D> for DirResource {
}
}
#[derive(Debug)]
pub struct Fmap {
node_ptr: TreePtr<Node>,
offset: usize,
rc: usize,
flags: MapFlags,
data: &'static mut [u8],
last_page_tail: u16,
}
impl Fmap {
pub unsafe fn new<D: Disk>(
node_ptr: TreePtr<Node>,
map: &Map,
flags: MapFlags,
unaligned_size: usize,
offset: u64,
base: *mut u8,
tx: &mut Transaction<D>,
) -> Result<Self> {
extern "C" {
fn memalign(align: usize, size: usize) -> *mut u8;
fn free(ptr: *mut u8);
}
// Memory provided to fmap must be page aligned and sized
let align = 4096;
let address = memalign(align, ((map.size + align - 1) / align) * align);
if address.is_null() {
return Err(Error::new(ENOMEM));
}
let aligned_size = unaligned_size.next_multiple_of(syscall::PAGE_SIZE);
let address = base.add(offset as usize);
println!("ADDR {:p}", address);
// Read buffer from disk
let atime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let buf = slice::from_raw_parts_mut(address, map.size);
let buf = slice::from_raw_parts_mut(address, unaligned_size);
let count = match tx.read_node(
node_ptr,
map.offset as u64,
offset,
buf,
atime.as_secs(),
atime.subsec_nanos(),
) {
Ok(ok) => ok,
Err(err) => {
free(address);
let _ = syscall::funmap(address as usize, aligned_size);
return Err(err);
}
};
// Make sure remaining data is zeroed
for i in count..buf.len() {
buf[i] = 0;
}
buf[count..].fill(0_u8);
Ok(Self {
node_ptr,
offset: map.offset,
flags: map.flags,
data: buf,
rc: 1,
flags,
last_page_tail: (unaligned_size % PAGE_SIZE) as u16,
})
}
pub fn sync<D: Disk>(&mut self, tx: &mut Transaction<D>) -> Result<()> {
pub unsafe fn sync<D: Disk>(&mut self, node_ptr: TreePtr<Node>, base: *mut u8, offset: u64, size: usize, tx: &mut Transaction<D>) -> Result<()> {
if self.flags & PROT_WRITE == PROT_WRITE {
let mtime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
tx.write_node(
self.node_ptr,
self.offset as u64,
&self.data,
node_ptr,
offset,
unsafe { core::slice::from_raw_parts(base.add(offset as usize), size) },
mtime.as_secs(),
mtime.subsec_nanos(),
)?;
@@ -301,18 +299,6 @@ impl Fmap {
}
}
impl Drop for Fmap {
fn drop(&mut self) {
unsafe {
extern "C" {
fn free(ptr: *mut u8);
}
free(self.data.as_mut_ptr());
}
}
}
pub struct FileResource {
path: String,
parent_ptr_opt: Option<TreePtr<Node>>,
@@ -320,7 +306,8 @@ pub struct FileResource {
flags: usize,
seek: isize,
uid: u32,
fmaps: BTreeMap<usize, Fmap>,
base: *mut u8,
fmaps: RangeTree<Fmap>,
}
impl FileResource {
@@ -337,8 +324,9 @@ impl FileResource {
node_ptr,
flags,
seek: 0,
base: core::ptr::null_mut(),
uid,
fmaps: BTreeMap::new(),
fmaps: RangeTree::new(),
}
}
}
@@ -364,7 +352,8 @@ impl<D: Disk> Resource<D> for FileResource {
flags: self.flags,
seek: self.seek,
uid: self.uid,
fmaps: BTreeMap::new(),
fmaps: RangeTree::new(),
base: core::ptr::null_mut(),
}))
}
@@ -423,30 +412,101 @@ impl<D: Disk> Resource<D> for FileResource {
Ok(self.seek)
}
fn fmap(&mut self, map: &Map, tx: &mut Transaction<D>) -> Result<usize> {
fn fmap(&mut self, flags: MapFlags, unaligned_size: usize, offset: u64, tx: &mut Transaction<D>) -> Result<usize> {
//dbg!(&self.fmaps);
let accmode = self.flags & O_ACCMODE;
if map.flags.contains(PROT_READ) && !(accmode == O_RDWR || accmode == O_RDONLY) {
if flags.contains(PROT_READ) && !(accmode == O_RDWR || accmode == O_RDONLY) {
return Err(Error::new(EBADF));
}
if map.flags.contains(PROT_WRITE) && !(accmode == O_RDWR || accmode == O_WRONLY) {
if flags.contains(PROT_WRITE) && !(accmode == O_RDWR || accmode == O_WRONLY) {
return Err(Error::new(EBADF));
}
//TODO: PROT_EXEC?
let map = unsafe { Fmap::new(self.node_ptr, map, tx)? };
let address = map.data.as_ptr() as usize;
self.fmaps.insert(address, map);
Ok(address)
let aligned_size = unaligned_size.next_multiple_of(PAGE_SIZE);
// TODO: PROT_EXEC? It is however unenforcable without restricting anonymous mmap, since a
// 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();
if offset + aligned_size as u64 > max_offset {
if self.base.is_null() {
self.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
// handler.
flags: MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE,
offset: 0,
address: 0,
})? as *mut u8
};
} else {
let new_size = (offset as usize + aligned_size).next_multiple_of(PAGE_SIZE);
let old_size = max_offset as usize;
let common_size = std::cmp::min(new_size, old_size);
if new_size > old_size {
unsafe {
syscall::fmap(!0, &Map {
size: new_size - old_size,
flags: MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE,
offset: old_size,
address: 0,
})?;
}
} else if old_size > new_size {
unsafe {
syscall::funmap(old_size, old_size - new_size)?;
}
}
self.base = unsafe {
syscall::syscall5(syscall::SYS_MREMAP, self.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);
for (range, v_opt) in affected_fmaps {
//dbg!(&range);
if let Some(mut fmap) = v_opt {
fmap.rc += 1;
fmap.flags |= flags;
self.fmaps.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);
}
}
//dbg!(&self.fmaps);
Ok(self.base as usize + offset as usize)
}
fn funmap(&mut self, address: usize, tx: &mut Transaction<D>) -> Result<usize> {
if let Some(mut fmap) = self.fmaps.remove(&address) {
fmap.sync(tx)?;
fn funmap(&mut self, offset: u64, size: usize, tx: &mut Transaction<D>) -> Result<usize> {
//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);
Ok(0)
} else {
Err(Error::new(EINVAL))
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)?;
}
if fmap.rc > 0 {
self.fmaps.insert(range.start, range.end - range.start, fmap);
}
}
//dbg!(&self.fmaps);
Ok(0)
}
fn fcntl(&mut self, cmd: usize, arg: usize) -> Result<usize> {
@@ -465,8 +525,10 @@ impl<D: Disk> Resource<D> for FileResource {
}
fn sync(&mut self, tx: &mut Transaction<D>) -> Result<usize> {
for fmap in self.fmaps.values_mut() {
fmap.sync(tx)?;
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)?;
}
}
Ok(0)
@@ -530,3 +592,41 @@ impl Drop for FileResource {
}
}
}
impl range_tree::Value for Fmap {
type K = u64;
fn try_merge_forward(self, other: &Self) -> core::result::Result<Self, Self> {
if self.rc == other.rc && self.flags == other.flags && self.last_page_tail == 0 {
Ok(self)
} else {
Err(self)
}
}
fn try_merge_backwards(self, other: &Self) -> core::result::Result<Self, Self> {
if self.rc == other.rc && self.flags == other.flags && other.last_page_tail == 0 {
Ok(self)
} else {
Err(self)
}
}
fn split(self, prev_range: Option<core::ops::Range<Self::K>>, range: core::ops::Range<Self::K>, next_range: Option<core::ops::Range<Self::K>>) -> (Option<Self>, Self, Option<Self>) {
(
prev_range.map(|range| Fmap {
rc: self.rc,
flags: self.flags,
last_page_tail: 0,
}),
Fmap {
rc: self.rc,
flags: self.flags,
last_page_tail: if next_range.is_none() { self.last_page_tail } else { 0 },
},
next_range.map(|range| Fmap {
rc: self.rc,
flags: self.flags,
last_page_tail: self.last_page_tail,
}),
)
}
}
+16 -25
View File
@@ -9,8 +9,8 @@ use syscall::error::{
EPERM, EXDEV,
};
use syscall::flag::{
EventFlags, MODE_PERM, O_ACCMODE, O_CREAT, O_DIRECTORY, O_EXCL, O_NOFOLLOW, O_RDONLY, O_RDWR,
O_STAT, O_SYMLINK, O_TRUNC, O_WRONLY,
EventFlags, MapFlags, MODE_PERM, O_ACCMODE, O_CREAT, O_DIRECTORY, O_EXCL, O_NOFOLLOW, O_RDONLY,
O_RDWR, O_STAT, O_SYMLINK, O_TRUNC, O_WRONLY,
};
use syscall::scheme::SchemeMut;
@@ -23,7 +23,7 @@ pub struct FileScheme<D: Disk> {
fs: FileSystem<D>,
next_id: AtomicUsize,
files: BTreeMap<usize, Box<dyn Resource<D>>>,
fmap: BTreeMap<usize, usize>,
fmap: BTreeMap<u64, usize>,
}
impl<D: Disk> FileScheme<D> {
@@ -780,29 +780,20 @@ impl<D: Disk> SchemeMut for FileScheme<D> {
}
}
fn fmap(&mut self, id: usize, map: &Map) -> Result<usize> {
// println!("Fmap {}, {:?}", id, map);
if let Some(file) = self.files.get_mut(&id) {
let address = self.fs.tx(|tx| file.fmap(map, tx))?;
self.fmap.insert(address, id);
Ok(address)
} else {
Err(Error::new(EBADF))
}
}
fn funmap_old(&mut self, address: usize) -> Result<usize> {
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 mmap_prep(&mut self, id: usize, flags: MapFlags, size: usize, offset: u64) -> Result<usize> {
println!("Mmap {}, {:?} {} {}", id, flags, size, offset);
let file = self.files.get_mut(&id).ok_or(Error::new(EBADF))?;
self.fs.tx(|tx| file.fmap(flags, size, offset, tx))
}
fn munmap(&mut self, id: usize, size: usize, offset: u64) -> Result<usize> {
println!("Munmap {}, {} {}", id, size, offset);
let file = self.files.get_mut(&id).ok_or(Error::new(EINVAL))?;
self.fs.tx(|tx| file.funmap(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);
@@ -815,7 +806,7 @@ impl<D: Disk> SchemeMut for FileScheme<D> {
} else {
Err(Error::new(EINVAL))
}
}
}*/
fn close(&mut self, id: usize) -> Result<usize> {
// println!("Close {}", id);