diff --git a/src/lib.rs b/src/lib.rs index 0f1dba46e849984178f3ecc2e52fd733eeab8a72..20c6c98294c184bc8547850f18dcfa23cf1c5a41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ #![crate_name="redoxfs"] #![crate_type="lib"] -#![deny(warnings)] +//#![deny(warnings)] extern crate syscall; extern crate uuid; diff --git a/src/mount/redox/resource.rs b/src/mount/redox/resource.rs index 7d812bcce3532629c947c72d9aa5ae22c28197c9..50c3d2cd786c60b30eb8accef3aa6d262f3fb590 100644 --- a/src/mount/redox/resource.rs +++ b/src/mount/redox/resource.rs @@ -2,12 +2,13 @@ use std::cmp::{min, max}; use std::time::{SystemTime, UNIX_EPOCH}; use syscall::data::TimeSpec; -use syscall::error::{Error, Result, EBADF, EINVAL, EISDIR, EPERM}; +use syscall::error::{Error, Result, EBADF, EBUSY, EINVAL, EISDIR, EPERM}; use syscall::flag::{O_ACCMODE, O_RDONLY, O_WRONLY, O_RDWR, F_GETFL, F_SETFL, MODE_PERM}; use syscall::{Stat, SEEK_SET, SEEK_CUR, SEEK_END}; use disk::Disk; use filesystem::FileSystem; +use super::scheme::{Fmaps, FmapKey, FmapValue}; pub trait Resource<D: Disk> { fn block(&self) -> u64; @@ -15,12 +16,14 @@ pub trait Resource<D: Disk> { fn read(&mut self, buf: &mut [u8], fs: &mut FileSystem<D>) -> Result<usize>; fn write(&mut self, buf: &[u8], fs: &mut FileSystem<D>) -> Result<usize>; fn seek(&mut self, offset: usize, whence: usize, fs: &mut FileSystem<D>) -> Result<usize>; + fn fmap(&mut self, offset: usize, size: usize, maps: &mut Fmaps, fs: &mut FileSystem<D>) -> Result<usize>; + fn funmap(&mut self, maps: &mut Fmaps, fs: &mut FileSystem<D>) -> Result<usize>; fn fchmod(&mut self, mode: u16, fs: &mut FileSystem<D>) -> Result<usize>; fn fchown(&mut self, uid: u32, gid: u32, fs: &mut FileSystem<D>) -> Result<usize>; fn fcntl(&mut self, cmd: usize, arg: usize) -> Result<usize>; fn path(&self, buf: &mut [u8]) -> Result<usize>; fn stat(&self, _stat: &mut Stat, fs: &mut FileSystem<D>) -> Result<usize>; - fn sync(&mut self) -> Result<usize>; + fn sync(&mut self, maps: &mut Fmaps, fs: &mut FileSystem<D>) -> Result<usize>; fn truncate(&mut self, len: usize, fs: &mut FileSystem<D>) -> Result<usize>; fn utimens(&mut self, times: &[TimeSpec], fs: &mut FileSystem<D>) -> Result<usize>; } @@ -87,6 +90,13 @@ impl<D: Disk> Resource<D> for DirResource { Ok(self.seek) } + fn fmap(&mut self, _offset: usize, _size: usize, _maps: &mut Fmaps, _fs: &mut FileSystem<D>) -> Result<usize> { + Err(Error::new(EBADF)) + } + fn funmap(&mut self, _maps: &mut Fmaps, _fs: &mut FileSystem<D>) -> Result<usize> { + Err(Error::new(EBADF)) + } + fn fchmod(&mut self, mode: u16, fs: &mut FileSystem<D>) -> Result<usize> { let mut node = fs.node(self.block)?; @@ -158,7 +168,7 @@ impl<D: Disk> Resource<D> for DirResource { Ok(0) } - fn sync(&mut self) -> Result<usize> { + fn sync(&mut self, _maps: &mut Fmaps, _fs: &mut FileSystem<D>) -> Result<usize> { Err(Error::new(EBADF)) } @@ -177,6 +187,7 @@ pub struct FileResource { flags: usize, seek: u64, uid: u32, + fmap: Option<(usize, FmapKey)> } impl FileResource { @@ -187,8 +198,31 @@ impl FileResource { flags: flags, seek: seek, uid: uid, + fmap: None } } + + fn sync_fmap<D: Disk>(&mut self, maps: &mut Fmaps, fs: &mut FileSystem<D>) -> Result<()> { + if let Some((i, key_exact)) = self.fmap.as_ref() { + let (_, value) = maps.index(*i).as_mut().expect("mapping dropped while still referenced"); + // Minimum out of our size and the original file size + let actual_size = value.actual_size.min(key_exact.size); + + let mut count = 0; + while count < actual_size { + let mtime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + match fs.write_node(self.block, key_exact.offset as u64 + count as u64, &value.buffer[count..actual_size], + mtime.as_secs(), mtime.subsec_nanos())? { + 0 => { + eprintln!("Fmap failed to write whole buffer, encountered EOF early."); + break; + } + n => count += n, + } + } + } + Ok(()) + } } impl<D: Disk> Resource<D> for FileResource { @@ -203,6 +237,7 @@ impl<D: Disk> Resource<D> for FileResource { flags: self.flags, seek: self.seek, uid: self.uid, + fmap: None })) } @@ -240,6 +275,71 @@ impl<D: Disk> Resource<D> for FileResource { Ok(self.seek as usize) } + fn fmap(&mut self, offset: usize, size: usize, maps: &mut Fmaps, fs: &mut FileSystem<D>) -> Result<usize> { + if self.flags & O_ACCMODE == O_RDWR { + let key_exact = FmapKey { + block: self.block, + offset, + size + }; + + let i = match maps.find_compatible(&key_exact) { + Ok((i, (key_existing, value))) => { + value.refcount += 1; + self.fmap = Some((i, key_exact)); + return Ok(value.buffer.as_ptr() as usize + (key_exact.offset - key_existing.offset)) + }, + Err(None) => { + // This is bad! + // We reached the limit of maps, and we can't reallocate + // because that would invalidate stuff. + // Sorry, nothing personal :( + return Err(Error::new(EBUSY)) + }, + Err(Some(i)) => { + // Can't do stuff in here because lifetime issues + i + } + }; + let key_round = key_exact.round(); + + let mut content = vec![0; key_round.size]; + let mut count = 0; + while count < key_round.size { + match fs.read_node(self.block, key_round.offset as u64 + count as u64, &mut content[count..])? { + 0 => break, + n => count += n + } + } + + let value = maps.insert(i, key_round, FmapValue { + buffer: content, + actual_size: count, + refcount: 1 + }); + + self.fmap = Some((i, key_exact)); + Ok(value.buffer.as_ptr() as usize + (key_exact.offset - key_round.offset)) + } else { + Err(Error::new(EBADF)) + } + } + fn funmap(&mut self, maps: &mut Fmaps, fs: &mut FileSystem<D>) -> Result<usize> { + self.sync_fmap(maps, fs)?; + if let Some((i, _)) = self.fmap.as_ref() { + let value = maps.index(*i); + let clear = { + let (_, value) = value.as_mut().expect("mapping dropped while still referenced"); + value.refcount -= 1; + value.refcount == 0 + }; + if clear { + *value = None; + } + } + Ok(0) + } + fn fchmod(&mut self, mode: u16, fs: &mut FileSystem<D>) -> Result<usize> { let mut node = fs.node(self.block)?; @@ -318,7 +418,8 @@ impl<D: Disk> Resource<D> for FileResource { Ok(0) } - fn sync(&mut self) -> Result<usize> { + fn sync(&mut self, maps: &mut Fmaps, fs: &mut FileSystem<D>) -> Result<usize> { + self.sync_fmap(maps, fs)?; Ok(0) } diff --git a/src/mount/redox/scheme.rs b/src/mount/redox/scheme.rs index 966b56501a68b6d0b5198fe2270a62728809d9d8..a5ce3be516ae36613e5c3f7249401793a2f2ca52 100644 --- a/src/mount/redox/scheme.rs +++ b/src/mount/redox/scheme.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; use std::collections::BTreeMap; +use std::result::Result as StdResult; use std::str; use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::{SystemTime, UNIX_EPOCH}; @@ -17,11 +18,81 @@ use node::Node; use super::resource::{Resource, DirResource, FileResource}; use super::spin::Mutex; +/// The size to round offset/len up to. +/// This ensures more fmaps can share the same memory even with different parameters. +const PAGE_SIZE: usize = 4096; +/// The max amount of fmaps that can be held simultaneously. +/// This restriction is here because we can under no circumstances reallocate, +/// that would invalidate previous mappings. +const FMAP_AMOUNT: usize = 1024; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct FmapKey { + pub block: u64, + pub offset: usize, + pub size: usize +} +impl FmapKey { + pub fn round(&self) -> FmapKey { + let remainder = self.size % PAGE_SIZE; + FmapKey { + block: self.block, + offset: self.offset - self.offset % PAGE_SIZE, + size: if remainder == 0 { self.size } else { self.size - remainder + PAGE_SIZE } + } + } + pub fn is_compatible(&self, other: &FmapKey) -> bool { + self.block == other.block + && self.offset <= other.offset + && self.offset + self.size >= other.offset + other.size + } +} +#[derive(Clone)] +pub struct FmapValue { + pub buffer: Vec<u8>, + /// The actual file length. Syncing only writes &buffer[..actual_size]. + pub actual_size: usize, + pub refcount: usize +} + +// NOTE: This can NOT reallocate. That would invalidate previous mappings. +pub struct Fmaps(Vec<Option<(FmapKey, FmapValue)>>); +impl Default for Fmaps { + fn default() -> Fmaps { + Fmaps(vec![None; FMAP_AMOUNT]) + } +} + +impl Fmaps { + pub fn find_compatible(&mut self, key: &FmapKey) -> StdResult<(usize, &mut (FmapKey, FmapValue)), Option<usize>> { + let mut first_empty = None; + for (i, entry) in self.0.iter_mut().enumerate() { + match entry { + None if first_empty.is_none() => first_empty = Some(i), + Some(entry) if entry.0.is_compatible(key) => return Ok((i, entry)), + _ => () + } + } + Err(first_empty) + } + pub fn index(&mut self, index: usize) -> &mut Option<(FmapKey, FmapValue)> { + &mut self.0[index] + } + pub fn insert(&mut self, index: usize, key: FmapKey, value: FmapValue) -> &mut FmapValue { + let elem = &mut self.0[index]; + assert!(elem.is_none()); + *elem = Some((key, value)); + + &mut elem.as_mut().unwrap().1 + } +} + pub struct FileScheme<D: Disk> { name: String, fs: RefCell<FileSystem<D>>, next_id: AtomicUsize, - files: Mutex<BTreeMap<usize, Box<Resource<D>>>> + files: Mutex<BTreeMap<usize, Box<Resource<D>>>>, + fmaps: Mutex<Fmaps> } impl<D: Disk> FileScheme<D> { @@ -30,7 +101,8 @@ impl<D: Disk> FileScheme<D> { name: name, fs: RefCell::new(fs), next_id: AtomicUsize::new(1), - files: Mutex::new(BTreeMap::new()) + files: Mutex::new(BTreeMap::new()), + fmaps: Mutex::new(Fmaps::default()) } } @@ -651,7 +723,7 @@ impl<D: Disk> Scheme for FileScheme<D> { // println!("Fsync {}", id); let mut files = self.files.lock(); if let Some(file) = files.get_mut(&id) { - file.sync() + file.sync(&mut self.fmaps.lock(), &mut self.fs.borrow_mut()) } else { Err(Error::new(EBADF)) } @@ -677,10 +749,21 @@ impl<D: Disk> Scheme for FileScheme<D> { } } + fn fmap(&self, id: usize, offset: usize, size: usize) -> Result<usize> { + // println!("Fmap {}, {}, {}", id, offset, size); + let mut files = self.files.lock(); + if let Some(file) = files.get_mut(&id) { + file.fmap(offset, size, &mut self.fmaps.lock(), &mut self.fs.borrow_mut()) + } else { + Err(Error::new(EBADF)) + } + } + fn close(&self, id: usize) -> Result<usize> { // println!("Close {}", id); let mut files = self.files.lock(); - if files.remove(&id).is_some() { + if let Some(mut file) = files.remove(&id) { + let _ = file.funmap(&mut self.fmaps.lock(), &mut self.fs.borrow_mut()); Ok(0) } else { Err(Error::new(EBADF))