use std::cmp::{min, max}; use std::time::{SystemTime, UNIX_EPOCH}; use syscall::data::TimeSpec; 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; fn dup(&self) -> Result<Box<Resource<D>>>; 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, 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>; } pub struct DirResource { path: String, block: u64, data: Option<Vec<u8>>, seek: usize, uid: u32, } impl DirResource { pub fn new(path: String, block: u64, data: Option<Vec<u8>>, uid: u32) -> DirResource { DirResource { path: path, block: block, data: data, seek: 0, uid: uid, } } } impl<D: Disk> Resource<D> for DirResource { fn block(&self) -> u64 { self.block } fn dup(&self) -> Result<Box<Resource<D>>> { Ok(Box::new(DirResource { path: self.path.clone(), block: self.block, data: self.data.clone(), seek: self.seek, uid: self.uid })) } fn read(&mut self, buf: &mut [u8], _fs: &mut FileSystem<D>) -> Result<usize> { let data = self.data.as_ref().ok_or(Error::new(EISDIR))?; let mut i = 0; while i < buf.len() && self.seek < data.len() { buf[i] = data[self.seek]; i += 1; self.seek += 1; } Ok(i) } fn write(&mut self, _buf: &[u8], _fs: &mut FileSystem<D>) -> Result<usize> { Err(Error::new(EBADF)) } fn seek(&mut self, offset: usize, whence: usize, _fs: &mut FileSystem<D>) -> Result<usize> { let data = self.data.as_ref().ok_or(Error::new(EBADF))?; self.seek = match whence { SEEK_SET => max(0, min(data.len() as isize, offset as isize)) as usize, SEEK_CUR => max(0, min(data.len() as isize, self.seek as isize + offset as isize)) as usize, SEEK_END => max(0, min(data.len() as isize, data.len() as isize + offset as isize)) as usize, _ => return Err(Error::new(EINVAL)) }; 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)?; if node.1.uid == self.uid || self.uid == 0 { node.1.mode = (node.1.mode & ! MODE_PERM) | (mode & MODE_PERM); fs.write_at(node.0, &node.1)?; Ok(0) } else { Err(Error::new(EPERM)) } } fn fchown(&mut self, uid: u32, gid: u32, fs: &mut FileSystem<D>) -> Result<usize> { let mut node = fs.node(self.block)?; if node.1.uid == self.uid || self.uid == 0 { if uid as i32 != -1 { node.1.uid = uid; } if gid as i32 != -1 { node.1.gid = gid; } fs.write_at(node.0, &node.1)?; Ok(0) } else { Err(Error::new(EPERM)) } } fn fcntl(&mut self, _cmd: usize, _arg: usize) -> Result<usize> { Err(Error::new(EBADF)) } fn path(&self, buf: &mut [u8]) -> Result<usize> { let path = self.path.as_bytes(); let mut i = 0; while i < buf.len() && i < path.len() { buf[i] = path[i]; i += 1; } Ok(i) } fn stat(&self, stat: &mut Stat, fs: &mut FileSystem<D>) -> Result<usize> { let node = fs.node(self.block)?; *stat = Stat { st_dev: 0, // TODO st_ino: node.0, st_mode: node.1.mode, st_nlink: 1, st_uid: node.1.uid, st_gid: node.1.gid, st_size: fs.node_len(self.block)?, st_mtime: node.1.mtime, st_mtime_nsec: node.1.mtime_nsec, st_ctime: node.1.ctime, st_ctime_nsec: node.1.ctime_nsec, ..Default::default() }; Ok(0) } fn sync(&mut self, _maps: &mut Fmaps, _fs: &mut FileSystem<D>) -> Result<usize> { Err(Error::new(EBADF)) } fn truncate(&mut self, _len: usize, _fs: &mut FileSystem<D>) -> Result<usize> { Err(Error::new(EBADF)) } fn utimens(&mut self, _times: &[TimeSpec], _fs: &mut FileSystem<D>) -> Result<usize> { Err(Error::new(EBADF)) } } pub struct FileResource { path: String, block: u64, flags: usize, seek: u64, uid: u32, fmap: Option<(usize, FmapKey)> } impl FileResource { pub fn new(path: String, block: u64, flags: usize, seek: u64, uid: u32) -> FileResource { FileResource { path: path, block: block, 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 { fn block(&self) -> u64 { self.block } fn dup(&self) -> Result<Box<Resource<D>>> { Ok(Box::new(FileResource { path: self.path.clone(), block: self.block, flags: self.flags, seek: self.seek, uid: self.uid, fmap: None })) } fn read(&mut self, buf: &mut [u8], fs: &mut FileSystem<D>) -> Result<usize> { if self.flags & O_ACCMODE == O_RDWR || self.flags & O_ACCMODE == O_RDONLY { let count = fs.read_node(self.block, self.seek, buf)?; self.seek += count as u64; Ok(count) } else { Err(Error::new(EBADF)) } } fn write(&mut self, buf: &[u8], fs: &mut FileSystem<D>) -> Result<usize> { if self.flags & O_ACCMODE == O_RDWR || self.flags & O_ACCMODE == O_WRONLY { let mtime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); let count = fs.write_node(self.block, self.seek, buf, mtime.as_secs(), mtime.subsec_nanos())?; self.seek += count as u64; Ok(count) } else { Err(Error::new(EBADF)) } } fn seek(&mut self, offset: usize, whence: usize, fs: &mut FileSystem<D>) -> Result<usize> { let size = fs.node_len(self.block)?; self.seek = match whence { SEEK_SET => max(0, offset as i64) as u64, SEEK_CUR => max(0, self.seek as i64 + offset as i64) as u64, SEEK_END => max(0, size as i64 + offset as i64) as u64, _ => return Err(Error::new(EINVAL)) }; 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)?; if node.1.uid == self.uid || self.uid == 0 { node.1.mode = (node.1.mode & ! MODE_PERM) | (mode & MODE_PERM); fs.write_at(node.0, &node.1)?; Ok(0) } else { Err(Error::new(EPERM)) } } fn fchown(&mut self, uid: u32, gid: u32, fs: &mut FileSystem<D>) -> Result<usize> { let mut node = fs.node(self.block)?; if node.1.uid == self.uid || self.uid == 0 { if uid as i32 != -1 { node.1.uid = uid; } if gid as i32 != -1 { node.1.gid = gid; } fs.write_at(node.0, &node.1)?; Ok(0) } else { Err(Error::new(EPERM)) } } fn fcntl(&mut self, cmd: usize, arg: usize) -> Result<usize> { match cmd { F_GETFL => Ok(self.flags), F_SETFL => { self.flags = (self.flags & O_ACCMODE) | (arg & ! O_ACCMODE); Ok(0) }, _ => Err(Error::new(EINVAL)) } } fn path(&self, buf: &mut [u8]) -> Result<usize> { let path = self.path.as_bytes(); let mut i = 0; while i < buf.len() && i < path.len() { buf[i] = path[i]; i += 1; } Ok(i) } fn stat(&self, stat: &mut Stat, fs: &mut FileSystem<D>) -> Result<usize> { let node = fs.node(self.block)?; *stat = Stat { st_dev: 0, // TODO st_ino: node.0, st_mode: node.1.mode, st_nlink: 1, st_uid: node.1.uid, st_gid: node.1.gid, st_size: fs.node_len(self.block)?, st_mtime: node.1.mtime, st_mtime_nsec: node.1.mtime_nsec, st_ctime: node.1.ctime, st_ctime_nsec: node.1.ctime_nsec, ..Default::default() }; Ok(0) } fn sync(&mut self, maps: &mut Fmaps, fs: &mut FileSystem<D>) -> Result<usize> { self.sync_fmap(maps, fs)?; Ok(0) } fn truncate(&mut self, len: usize, fs: &mut FileSystem<D>) -> Result<usize> { if self.flags & O_ACCMODE == O_RDWR || self.flags & O_ACCMODE == O_WRONLY { fs.node_set_len(self.block, len as u64)?; Ok(0) } else { Err(Error::new(EBADF)) } } fn utimens(&mut self, times: &[TimeSpec], fs: &mut FileSystem<D>) -> Result<usize> { let mut node = fs.node(self.block)?; if node.1.uid == self.uid || self.uid == 0 { if let Some(mtime) = times.get(1) { node.1.mtime = mtime.tv_sec as u64; node.1.mtime_nsec = mtime.tv_nsec as u32; fs.write_at(node.0, &node.1)?; Ok(0) } else { Ok(0) } } else { Err(Error::new(EPERM)) } } }