master
Tianyu Gao 6 years ago
commit e3e0c4b465

@ -3,6 +3,7 @@ members = [
"rcore-fs",
"rcore-fs-sfs",
"rcore-fs-sefs",
"rcore-fs-fuse"
"rcore-fs-fuse",
"rcore-fs-ext2"
]
exclude = ["sefs-fuse"]
exclude = ["sefs-fuse"]

@ -0,0 +1 @@
test.img

@ -0,0 +1,9 @@
[package]
name = "rcore-fs-ext2"
version = "0.1.0"
authors = ["Jiajie Chen <noc@jiegec.ac.cn>"]
edition = "2018"
[dependencies]
ext2 = { git = "https://github.com/rcore-os/ext2-rs" }
rcore-fs = { path = "../rcore-fs" }

Binary file not shown.

@ -0,0 +1,116 @@
#![cfg_attr(not(any(test, feature = "std")), no_std)]
#![feature(alloc)]
extern crate alloc;
extern crate ext2;
#[cfg(test)]
mod tests;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec;
use core::ops::Range;
use ext2::error::Error;
use ext2::fs::sync::Synced;
use ext2::fs::Ext2;
use ext2::sector::{Address, Size512};
use ext2::volume::size::Size;
use ext2::volume::{Volume, VolumeCommit, VolumeSlice};
use rcore_fs::dev::{DevError, Device};
use rcore_fs::vfs;
#[derive(Clone)]
struct Ext2Volume {
inner: Arc<Device>,
}
#[derive(Clone)]
pub struct Ext2FileSystem {
inner: Synced<Ext2<Size512, Ext2Volume>>,
volume: Ext2Volume,
}
/// A conversion between vfs::FsError and ext2::Error
#[derive(Debug)]
struct Ext2Error {
inner: Error,
}
impl core::convert::From<Ext2Error> for vfs::FsError {
fn from(err: Ext2Error) -> Self {
match err.inner {
_ => vfs::FsError::DeviceError,
}
}
}
impl core::convert::From<Ext2Error> for Error {
fn from(err: Ext2Error) -> Self {
err.inner
}
}
impl core::convert::From<Error> for Ext2Error {
fn from(err: Error) -> Self {
Ext2Error { inner: err }
}
}
impl core::convert::From<DevError> for Ext2Error {
fn from(_: DevError) -> Self {
Ext2Error {
inner: Error::Other(String::from("unknown")),
}
}
}
impl Ext2FileSystem {
pub fn open(device: Arc<Device>) -> vfs::Result<Arc<Self>> {
Ok(Self::open_internal(device)?)
}
fn open_internal(device: Arc<Device>) -> Result<Arc<Self>, Ext2Error> {
let volume = Ext2Volume { inner: device };
let fs = Synced::new(volume.clone())?;
Ok(Arc::new(Ext2FileSystem { inner: fs, volume }))
}
}
impl Volume<u8, Size512> for Ext2Volume {
type Error = Ext2Error;
fn size(&self) -> Size<Size512> {
Size::Unbounded
}
fn commit(&mut self, _slice: Option<VolumeCommit<u8, Size512>>) -> Result<(), Self::Error> {
unimplemented!()
}
unsafe fn slice_unchecked<'a>(
&'a self,
range: Range<Address<Size512>>,
) -> VolumeSlice<'a, u8, Size512> {
let index = range.start;
let len = range.end - range.start;
let mut vec = vec![0; len.into_index() as usize];
self.inner
.read_at(index.into_index() as usize, vec.as_mut_slice())
.unwrap();
VolumeSlice::new_owned(vec, index)
}
fn slice<'a>(
&'a self,
range: Range<Address<Size512>>,
) -> Result<VolumeSlice<'a, u8, Size512>, Self::Error> {
let index = range.start;
let len = range.end - range.start;
let mut vec = vec![0; len.into_index() as usize];
self.inner
.read_at(index.into_index() as usize, vec.as_mut_slice())?;
Ok(VolumeSlice::new_owned(vec, index))
}
}

@ -0,0 +1,21 @@
extern crate std;
use crate::*;
use std::fs::{self, OpenOptions};
use std::sync::Arc;
use std::sync::Mutex;
fn open_sample_file() -> Arc<Ext2FileSystem> {
fs::copy("ext2.img", "test.img").expect("failed to open ext2.img");
let file = OpenOptions::new()
.read(true)
.write(true)
.open("test.img")
.expect("failed to open test.img");
Ext2FileSystem::open(Arc::new(Mutex::new(file))).expect("failed to open Ext2")
}
#[test]
fn test_open() {
let _ = open_sample_file();
}

@ -4,13 +4,17 @@ version = "0.1.0"
authors = ["WangRunji <wangrunji0408@163.com>"]
edition = "2018"
[features]
use_fuse = ["fuse"]
[dependencies]
time = "0.1"
libc = "0.2"
log = "0.4"
fuse = "0.3"
fuse = { version = "0.3", optional = true }
structopt = "0.2"
env_logger = "0.3"
git-version = "0.3"
rcore-fs = { path = "../rcore-fs", features = ["std"] }
rcore-fs-sfs = { path = "../rcore-fs-sfs" }
rcore-fs-sefs = { path = "../rcore-fs-sefs", features = ["std"] }

@ -28,6 +28,12 @@ impl VfsFuse {
nsec: time.nsec,
}
}
fn trans_time_r(time: Timespec) -> vfs::Timespec {
vfs::Timespec {
sec: time.sec,
nsec: time.nsec,
}
}
fn trans_attr(info: vfs::Metadata) -> FileAttr {
FileAttr {
ino: info.inode as u64,
@ -40,8 +46,8 @@ impl VfsFuse {
kind: Self::trans_type(info.type_),
perm: info.mode,
nlink: info.nlinks as u32,
uid: info.uid as u32,
gid: info.gid as u32,
uid: 501, // info.uid as u32,
gid: 20, // info.gid as u32,
rdev: 0,
flags: 0,
}
@ -59,20 +65,19 @@ impl VfsFuse {
}
fn trans_error(err: vfs::FsError) -> i32 {
use libc::*;
use vfs::FsError::*;
match err {
NotSupported => ENOSYS,
EntryNotFound => ENOENT,
EntryExist => EEXIST,
IsDir => EISDIR,
NotFile => EISDIR,
NotDir => ENOTDIR,
NotSameFs => EXDEV,
InvalidParam => EINVAL,
NoDeviceSpace => ENOSPC,
DirRemoved => ENOENT,
DirNotEmpty => ENOTEMPTY,
WrongFs => EINVAL,
vfs::FsError::NotSupported => ENOSYS,
vfs::FsError::EntryNotFound => ENOENT,
vfs::FsError::EntryExist => EEXIST,
vfs::FsError::IsDir => EISDIR,
vfs::FsError::NotFile => EISDIR,
vfs::FsError::NotDir => ENOTDIR,
vfs::FsError::NotSameFs => EXDEV,
vfs::FsError::InvalidParam => EINVAL,
vfs::FsError::NoDeviceSpace => ENOSPC,
vfs::FsError::DirRemoved => ENOENT,
vfs::FsError::DirNotEmpty => ENOTEMPTY,
vfs::FsError::WrongFs => EINVAL,
_ => EINVAL,
}
}
@ -118,6 +123,48 @@ impl Filesystem for VfsFuse {
reply.attr(&TTL, &attr);
}
fn setattr(
&mut self,
_req: &Request,
ino: u64,
mode: Option<u32>,
uid: Option<u32>,
gid: Option<u32>,
size: Option<u64>,
atime: Option<Timespec>,
mtime: Option<Timespec>,
_fh: Option<u64>,
_crtime: Option<Timespec>,
_chgtime: Option<Timespec>,
_bkuptime: Option<Timespec>,
_flags: Option<u32>,
reply: ReplyAttr,
) {
let inode = try_vfs!(reply, self.get_inode(ino));
if let Some(size) = size {
try_vfs!(reply, inode.resize(size as usize));
}
let mut info = try_vfs!(reply, inode.metadata());
if let Some(mode) = mode {
info.mode = mode as u16;
}
if let Some(uid) = uid {
info.uid = uid as usize;
}
if let Some(gid) = gid {
info.gid = gid as usize;
}
if let Some(atime) = atime {
info.atime = Self::trans_time_r(atime);
}
if let Some(mtime) = mtime {
info.mtime = Self::trans_time_r(mtime);
}
try_vfs!(reply, inode.set_metadata(&info));
let attr = Self::trans_attr(info);
reply.attr(&TTL, &attr);
}
fn mknod(
&mut self,
_req: &Request,
@ -141,6 +188,7 @@ impl Filesystem for VfsFuse {
let inode = try_vfs!(reply, self.get_inode(parent));
let target = try_vfs!(reply, inode.create(name, vfs::FileType::Dir, mode));
let info = try_vfs!(reply, target.metadata());
self.inodes.insert(info.inode, target);
let attr = Self::trans_attr(info);
reply.entry(&TTL, &attr, 0);
}
@ -246,10 +294,12 @@ impl Filesystem for VfsFuse {
mut reply: ReplyDirectory,
) {
let inode = try_vfs!(reply, self.get_inode(ino));
let info = try_vfs!(reply, inode.metadata());
let count = info.size;
for i in offset as usize..count {
let name = inode.get_entry(i).unwrap();
for i in offset as usize.. {
let name = match inode.get_entry(i) {
Ok(name) => name,
Err(vfs::FsError::EntryNotFound) => break,
e @ _ => try_vfs!(reply, e),
};
let inode = try_vfs!(reply, inode.find(name.as_str()));
let info = try_vfs!(reply, inode.metadata());
let kind = Self::trans_type(info.type_);

@ -1,2 +1,3 @@
#[cfg(feature = "use_fuse")]
pub mod fuse;
pub mod zip;

@ -6,11 +6,14 @@ use structopt::StructOpt;
use rcore_fs::dev::std_impl::StdTimeProvider;
use rcore_fs::vfs::FileSystem;
#[cfg(feature = "use_fuse")]
use rcore_fs_fuse::fuse::VfsFuse;
use rcore_fs_fuse::zip::{unzip_dir, zip_dir};
use rcore_fs_sefs as sefs;
use rcore_fs_sfs as sfs;
use git_version::git_version;
#[derive(Debug, StructOpt)]
struct Opt {
/// Command
@ -41,8 +44,12 @@ enum Cmd {
Unzip,
/// Mount <image> to <dir>
#[cfg(feature = "use_fuse")]
#[structopt(name = "mount")]
Mount,
#[structopt(name = "git-version")]
GitVersion,
}
fn main() {
@ -51,9 +58,14 @@ fn main() {
// open or create
let create = match opt.cmd {
#[cfg(feature = "use_fuse")]
Cmd::Mount => !opt.image.is_dir() && !opt.image.is_file(),
Cmd::Zip => true,
Cmd::Unzip => false,
Cmd::GitVersion => {
println!("{}", git_version!());
return;
}
};
let fs: Arc<FileSystem> = match opt.fs.as_str() {
@ -67,7 +79,8 @@ fn main() {
let device = Mutex::new(file);
const MAX_SPACE: usize = 0x1000 * 0x1000 * 1024; // 1G
match create {
true => sfs::SimpleFileSystem::create(Arc::new(device), MAX_SPACE),
true => sfs::SimpleFileSystem::create(Arc::new(device), MAX_SPACE)
.expect("failed to create sfs"),
false => sfs::SimpleFileSystem::open(Arc::new(device)).expect("failed to open sfs"),
}
}
@ -84,6 +97,7 @@ fn main() {
_ => panic!("unsupported file system"),
};
match opt.cmd {
#[cfg(feature = "use_fuse")]
Cmd::Mount => {
fuse::mount(VfsFuse::new(fs), &opt.dir, &[]).expect("failed to mount fs");
}
@ -94,5 +108,6 @@ fn main() {
std::fs::create_dir(&opt.dir).expect("failed to create dir");
unzip_dir(&opt.dir, fs.root_inode()).expect("failed to unzip fs");
}
Cmd::GitVersion => unreachable!(),
}
}

@ -4,6 +4,7 @@ use std::io::{Read, Write};
use std::mem::uninitialized;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::str;
use std::sync::Arc;
use rcore_fs::vfs::{FileType, INode};
@ -67,6 +68,11 @@ pub fn unzip_dir(path: &Path, inode: Arc<INode>) -> Result<(), Box<Error>> {
fs::create_dir(&path)?;
unzip_dir(path.as_path(), inode)?;
}
FileType::SymLink => {
let mut buf: [u8; BUF_SIZE] = unsafe { uninitialized() };
let len = inode.read_at(0, buf.as_mut())?;
std::os::unix::fs::symlink(str::from_utf8(&buf[..len]).unwrap(), path)?;
}
_ => panic!("unsupported file type"),
}
}

@ -5,7 +5,6 @@ use rcore_fs::vfs::FsError;
#[cfg(any(test, feature = "std"))]
pub use self::std_impl::*;
pub mod sgx_impl;
pub mod std_impl;
/// A file stores a normal file or directory.

@ -1,115 +0,0 @@
#![cfg(feature = "sgx")]
use std::boxed::Box;
use std::io::{Read, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};
use std::sgxfs::{remove, OpenOptions, SgxFile as File};
use std::sync::SgxMutex as Mutex;
use std::time::{SystemTime, UNIX_EPOCH};
use std::untrusted::time::SystemTimeEx; // FIXME: use trusted ime
use rcore_fs::dev::TimeProvider;
use rcore_fs::vfs::Timespec;
use super::{DevResult, DeviceError};
pub struct SgxStorage {
path: PathBuf,
}
impl SgxStorage {
pub fn new(path: impl AsRef<Path>) -> Self {
// assert!(path.as_ref().is_dir());
SgxStorage {
path: path.as_ref().to_path_buf(),
}
}
}
impl super::Storage for SgxStorage {
fn open(&self, file_id: usize) -> DevResult<Box<super::File>> {
let mut path = self.path.to_path_buf();
path.push(format!("{}", file_id));
// TODO: key
let key = [0u8; 16];
let file = OpenOptions::new()
.read(true)
.update(true)
.open_ex(path, &key)?;
Ok(Box::new(LockedFile(Mutex::new(file))))
}
fn create(&self, file_id: usize) -> DevResult<Box<super::File>> {
let mut path = self.path.to_path_buf();
path.push(format!("{}", file_id));
// TODO: key
let key = [0u8; 16];
let file = OpenOptions::new()
.write(true)
.update(true)
.open_ex(path, &key)?;
Ok(Box::new(LockedFile(Mutex::new(file))))
}
fn remove(&self, file_id: usize) -> DevResult<()> {
let mut path = self.path.to_path_buf();
path.push(format!("{}", file_id));
remove(path)?;
Ok(())
}
}
impl From<std::io::Error> for DeviceError {
fn from(e: std::io::Error) -> Self {
println!("{:?}", e);
panic!("{:?}", e);
DeviceError
}
}
pub struct LockedFile(Mutex<File>);
// `sgx_tstd::sgxfs::SgxFile` not impl Send ...
unsafe impl Send for LockedFile {}
unsafe impl Sync for LockedFile {}
impl super::File for LockedFile {
fn read_at(&self, buf: &mut [u8], offset: usize) -> DevResult<usize> {
let mut file = self.0.lock().unwrap();
let offset = offset as u64;
file.seek(SeekFrom::Start(offset))?;
let len = file.read(buf)?;
Ok(len)
}
fn write_at(&self, buf: &[u8], offset: usize) -> DevResult<usize> {
let mut file = self.0.lock().unwrap();
let offset = offset as u64;
file.seek(SeekFrom::Start(offset))?;
let len = file.write(buf)?;
Ok(len)
}
fn set_len(&self, len: usize) -> DevResult<()> {
// NOTE: do nothing ??
Ok(())
}
fn flush(&self) -> DevResult<()> {
let mut file = self.0.lock().unwrap();
file.flush()?;
Ok(())
}
}
pub struct SgxTimeProvider;
impl TimeProvider for SgxTimeProvider {
fn current_time(&self) -> Timespec {
let duration = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
Timespec {
sec: duration.as_secs() as i64,
nsec: duration.subsec_nanos() as i32,
}
}
}

@ -77,13 +77,13 @@ impl super::File for Mutex<File> {
}
fn set_len(&self, len: usize) -> DevResult<()> {
let mut file = self.lock();
let file = self.lock();
file.set_len(len as u64)?;
Ok(())
}
fn flush(&self) -> DevResult<()> {
let mut file = self.lock();
let file = self.lock();
file.sync_all()?;
Ok(())
}

@ -2,9 +2,6 @@
#![feature(alloc)]
extern crate alloc;
#[cfg(feature = "sgx")]
#[macro_use]
extern crate sgx_tstd as std;
use alloc::{
boxed::Box,
@ -18,10 +15,10 @@ use core::fmt::{Debug, Error, Formatter};
use core::mem::uninitialized;
use bitvec::BitVec;
use log::*;
use rcore_fs::dev::TimeProvider;
use rcore_fs::dirty::Dirty;
use rcore_fs::vfs::{self, FileSystem, FsError, INode, Timespec};
//use log::*;
use spin::RwLock;
use self::dev::*;
@ -146,19 +143,28 @@ impl INodeImpl {
impl vfs::INode for INodeImpl {
fn read_at(&self, offset: usize, buf: &mut [u8]) -> vfs::Result<usize> {
if self.disk_inode.read().type_ != FileType::File {
let type_ = self.disk_inode.read().type_;
if type_ != FileType::File && type_ != FileType::SymLink {
return Err(FsError::NotFile);
}
let len = self.file.read_at(buf, offset)?;
Ok(len)
}
fn write_at(&self, offset: usize, buf: &[u8]) -> vfs::Result<usize> {
if self.disk_inode.read().type_ != FileType::File {
let type_ = self.disk_inode.read().type_;
if type_ != FileType::File && type_ != FileType::SymLink {
return Err(FsError::NotFile);
}
let len = self.file.write_at(buf, offset)?;
Ok(len)
}
fn poll(&self) -> vfs::Result<vfs::PollStatus> {
Ok(vfs::PollStatus {
read: true,
write: true,
error: false,
})
}
/// the size returned here is logical size(entry num for directory), not the disk space used.
fn metadata(&self) -> vfs::Result<vfs::Metadata> {
let disk_inode = self.disk_inode.read();
@ -166,7 +172,7 @@ impl vfs::INode for INodeImpl {
dev: 0,
inode: self.id,
size: match disk_inode.type_ {
FileType::File => disk_inode.size as usize,
FileType::File | FileType::SymLink => disk_inode.size as usize,
FileType::Dir => disk_inode.blocks as usize,
_ => panic!("Unknown file type"),
},
@ -191,13 +197,16 @@ impl vfs::INode for INodeImpl {
blk_size: 0x1000,
})
}
fn chmod(&self, mode: u16) -> vfs::Result<()> {
fn set_metadata(&self, metadata: &vfs::Metadata) -> vfs::Result<()> {
let mut disk_inode = self.disk_inode.write();
disk_inode.mode = mode;
disk_inode.mode = metadata.mode;
disk_inode.uid = metadata.uid as u16;
disk_inode.gid = metadata.gid as u8;
disk_inode.atime = metadata.atime.sec as u32;
disk_inode.mtime = metadata.mtime.sec as u32;
disk_inode.ctime = metadata.ctime.sec as u32;
Ok(())
}
fn sync_all(&self) -> vfs::Result<()> {
let mut disk_inode = self.disk_inode.write();
if disk_inode.dirty() {
@ -206,13 +215,16 @@ impl vfs::INode for INodeImpl {
.write_block(self.id, disk_inode.as_buf())?;
disk_inode.sync();
}
self.sync_data()?;
Ok(())
}
fn sync_data(&self) -> vfs::Result<()> {
self.sync_all()
self.file.flush()?;
Ok(())
}
fn resize(&self, len: usize) -> vfs::Result<()> {
if self.disk_inode.read().type_ != FileType::File {
let type_ = self.disk_inode.read().type_;
if type_ != FileType::File && type_ != FileType::SymLink {
return Err(FsError::NotFile);
}
self.file.set_len(len)?;
@ -223,6 +235,7 @@ impl vfs::INode for INodeImpl {
let type_ = match type_ {
vfs::FileType::File => FileType::File,
vfs::FileType::Dir => FileType::Dir,
vfs::FileType::SymLink => FileType::SymLink,
_ => return Err(vfs::FsError::InvalidParam),
};
let info = self.metadata()?;
@ -402,6 +415,9 @@ impl vfs::INode for INodeImpl {
let entry = self.file.read_direntry(id)?;
Ok(String::from(entry.name.as_ref()))
}
fn io_control(&self, _cmd: u32, _data: usize) -> vfs::Result<()> {
Err(FsError::NotSupported)
}
fn fs(&self) -> Arc<vfs::FileSystem> {
self.fs.clone()
}
@ -423,7 +439,7 @@ impl Drop for INodeImpl {
}
}
/// Simple Encrypt File System
/// Simple Encrypted File System
pub struct SEFS {
/// on-disk superblock
super_block: RwLock<Dirty<SuperBlock>>,
@ -452,11 +468,23 @@ impl SEFS {
if !super_block.check() {
return Err(FsError::WrongFs);
}
let free_map = meta_file.load_struct::<[u8; BLKSIZE]>(BLKN_FREEMAP)?;
// load free map
let mut free_map = BitVec::with_capacity(BLKBITS * super_block.groups as usize);
unsafe {
free_map.set_len(BLKBITS * super_block.groups as usize);
}
for i in 0..super_block.groups as usize {
let block_id = Self::get_freemap_block_id_of_group(i);
meta_file.read_block(
block_id,
&mut free_map.as_mut()[BLKSIZE * i..BLKSIZE * (i + 1)],
)?;
}
Ok(SEFS {
super_block: RwLock::new(Dirty::new(super_block)),
free_map: RwLock::new(Dirty::new(BitVec::from(free_map.as_ref()))),
free_map: RwLock::new(Dirty::new(free_map)),
inodes: RwLock::new(BTreeMap::new()),
device,
meta_file,
@ -476,6 +504,7 @@ impl SEFS {
magic: MAGIC,
blocks: blocks as u32,
unused_blocks: blocks as u32 - 2,
groups: 1,
};
let free_map = {
let mut bitset = BitVec::with_capacity(BLKBITS);
@ -526,15 +555,23 @@ impl SEFS {
/// Allocate a block, return block id
fn alloc_block(&self) -> Option<usize> {
let mut free_map = self.free_map.write();
let id = free_map.alloc();
if let Some(block_id) = id {
let mut super_block = self.super_block.write();
if super_block.unused_blocks == 0 {
free_map.set(block_id, true);
return None;
}
super_block.unused_blocks -= 1; // will not underflow
}
let mut super_block = self.super_block.write();
let id = free_map.alloc().or_else(|| {
// allocate a new group
let new_group_id = super_block.groups as usize;
super_block.groups += 1;
super_block.blocks += BLKBITS as u32;
super_block.unused_blocks += BLKBITS as u32 - 1;
self.meta_file
.set_len(super_block.groups as usize * BLKBITS * BLKSIZE)
.expect("failed to extend meta file");
free_map.extend(core::iter::repeat(true).take(BLKBITS));
free_map.set(Self::get_freemap_block_id_of_group(new_group_id), false);
// allocate block again
free_map.alloc()
});
assert!(id.is_some(), "allocate block should always success");
super_block.unused_blocks -= 1;
id
}
/// Free a block
@ -609,29 +646,39 @@ impl SEFS {
inodes.remove(&id);
}
}
fn get_freemap_block_id_of_group(group_id: usize) -> usize {
BLKBITS * group_id + BLKN_FREEMAP
}
}
impl vfs::FileSystem for SEFS {
/// Write back super block if dirty
fn sync(&self) -> vfs::Result<()> {
// sync super_block
let mut super_block = self.super_block.write();
if super_block.dirty() {
self.meta_file
.write_all_at(super_block.as_buf(), BLKSIZE * BLKN_SUPER)?;
super_block.sync();
}
// sync free_map
let mut free_map = self.free_map.write();
if free_map.dirty() {
self.meta_file
.write_all_at(free_map.as_buf(), BLKSIZE * BLKN_FREEMAP)?;
for i in 0..super_block.groups as usize {
let slice = &free_map.as_ref()[BLKSIZE * i..BLKSIZE * (i + 1)];
self.meta_file
.write_all_at(slice, BLKSIZE * Self::get_freemap_block_id_of_group(i))?;
}
free_map.sync();
}
// sync all INodes
self.flush_weak_inodes();
for inode in self.inodes.read().values() {
if let Some(inode) = inode.upgrade() {
inode.sync_all()?;
}
}
self.meta_file.flush()?;
Ok(())
}
@ -657,8 +704,7 @@ impl vfs::FileSystem for SEFS {
impl Drop for SEFS {
/// Auto sync when drop
fn drop(&mut self) {
self.sync()
.expect("Failed to sync when dropping the SimpleFileSystem");
self.sync().expect("Failed to sync when dropping the SEFS");
}
}
@ -693,6 +739,7 @@ impl From<FileType> for vfs::FileType {
match t {
FileType::File => vfs::FileType::File,
FileType::Dir => vfs::FileType::Dir,
FileType::SymLink => vfs::FileType::SymLink,
_ => panic!("unknown file type"),
}
}

@ -16,6 +16,8 @@ pub struct SuperBlock {
pub blocks: u32,
/// number of unused blocks in fs
pub unused_blocks: u32,
/// number of block groups
pub groups: u32,
}
/// On-disk inode
@ -111,12 +113,8 @@ pub const MAGIC: u32 = 0x2f8dbe2a;
pub const BLKSIZE: usize = 1usize << BLKSIZE_LOG2;
/// log2( size of block )
pub const BLKSIZE_LOG2: u8 = 7;
/// max length of information
pub const MAX_INFO_LEN: usize = 31;
/// max length of filename
pub const MAX_FNAME_LEN: usize = 255;
/// max file size (128M)
pub const MAX_FILE_SIZE: usize = 1024 * 1024 * 128;
/// block the superblock lives in
pub const BLKN_SUPER: BlockId = 0;
/// location of the root dir inode
@ -125,10 +123,6 @@ pub const BLKN_ROOT: BlockId = 2;
pub const BLKN_FREEMAP: BlockId = 1;
/// number of bits in a block
pub const BLKBITS: usize = BLKSIZE * 8;
/// size of one entry
pub const ENTRY_SIZE: usize = 4;
/// number of entries in a block
pub const BLK_NENTRY: usize = BLKSIZE / ENTRY_SIZE;
/// size of a dirent used in the size field
pub const DIRENT_SIZE: usize = 260;
@ -139,7 +133,7 @@ pub enum FileType {
Invalid = 0,
File = 1,
Dir = 2,
Link = 3,
SymLink = 3,
}
const_assert!(o1; size_of::<SuperBlock>() <= BLKSIZE);

@ -10,9 +10,9 @@ use alloc::{
collections::BTreeMap,
string::String,
sync::{Arc, Weak},
vec::Vec,
vec,
boxed::Box
vec::Vec,
};
use core::any::Any;
use core::fmt::{Debug, Error, Formatter};
@ -36,33 +36,34 @@ trait DeviceExt: Device {
fn read_block(&self, id: BlockId, offset: usize, buf: &mut [u8]) -> vfs::Result<()> {
debug_assert!(offset + buf.len() <= BLKSIZE);
match self.read_at(id * BLKSIZE + offset, buf) {
Some(len) if len == buf.len() => Ok(()),
Ok(len) if len == buf.len() => Ok(()),
_ => panic!("cannot read block {} offset {} from device", id, offset),
}
}
fn write_block(&self, id: BlockId, offset: usize, buf: &[u8]) -> vfs::Result<()> {
debug_assert!(offset + buf.len() <= BLKSIZE);
match self.write_at(id * BLKSIZE + offset, buf) {
Some(len) if len == buf.len() => Ok(()),
Ok(len) if len == buf.len() => Ok(()),
_ => panic!("cannot write block {} offset {} to device", id, offset),
}
}
/// Load struct `T` from given block in device
fn load_struct<T: AsBuf>(&self, id: BlockId) -> vfs::Result<T> {
let mut s: T = unsafe { uninitialized() };
self.read_block(id, 0, s.as_buf_mut()).map(|_| s)
self.read_block(id, 0, s.as_buf_mut())?;
Ok(s)
}
}
impl DeviceExt for Device {}
/// inode for sfs
/// INode for SFS
pub struct INodeImpl {
/// inode number
/// INode number
id: INodeId,
/// on-disk inode
/// On-disk INode
disk_inode: RwLock<Dirty<DiskINode>>,
/// Weak reference to SFS, used by almost all operations
/// Reference to SFS, used by almost all operations
fs: Arc<SimpleFileSystem>,
device_inode_id: usize
}
@ -154,13 +155,8 @@ impl INodeImpl {
}
/// Only for Dir
fn get_file_inode_and_entry_id(&self, name: &str) -> Option<(INodeId, usize)> {
(0..self.disk_inode.read().blocks)
.map(|i| {
let mut entry: DiskEntry = unsafe { uninitialized() };
self._read_at(i as usize * BLKSIZE, entry.as_buf_mut())
.unwrap();
(entry, i)
})
(0..self.disk_inode.read().size as usize / DIRENT_SIZE)
.map(|i| (self.read_direntry(i as usize).unwrap(), i))
.find(|(entry, _)| entry.name.as_ref() == name)
.map(|(entry, id)| (entry.id as INodeId, id as usize))
}
@ -170,40 +166,50 @@ impl INodeImpl {
}
/// Init dir content. Insert 2 init entries.
/// This do not init nlinks, please modify the nlinks in the invoker.
fn init_dir_entry(&self, parent: INodeId) -> vfs::Result<()> {
fn init_direntry(&self, parent: INodeId) -> vfs::Result<()> {
// Insert entries: '.' '..'
self._resize(BLKSIZE * 2)?;
self._write_at(
BLKSIZE * 1,
DiskEntry {
id: parent as u32,
name: Str256::from(".."),
}
.as_buf(),
)
.unwrap();
self._write_at(
BLKSIZE * 0,
DiskEntry {
self._resize(DIRENT_SIZE * 2)?;
self.write_direntry(
0,
&DiskEntry {
id: self.id as u32,
name: Str256::from("."),
}
.as_buf(),
)
.unwrap();
},
)?;
self.write_direntry(
1,
&DiskEntry {
id: parent as u32,
name: Str256::from(".."),
},
)?;
Ok(())
}
fn read_direntry(&self, id: usize) -> vfs::Result<DiskEntry> {
let mut direntry: DiskEntry = unsafe { uninitialized() };
self._read_at(DIRENT_SIZE * id, direntry.as_buf_mut())?;
Ok(direntry)
}
fn write_direntry(&self, id: usize, direntry: &DiskEntry) -> vfs::Result<()> {
self._write_at(DIRENT_SIZE * id, direntry.as_buf())?;
Ok(())
}
fn append_direntry(&self, direntry: &DiskEntry) -> vfs::Result<()> {
let size = self.disk_inode.read().size as usize;
let dirent_count = size / DIRENT_SIZE;
self._resize(size + DIRENT_SIZE)?;
self.write_direntry(dirent_count, direntry)?;
Ok(())
}
/// remove a page in middle of file and insert the last page here, useful for dirent remove
/// remove a direntry in middle of file and insert the last one here, useful for direntry remove
/// should be only used in unlink
fn remove_dirent_page(&self, id: usize) -> vfs::Result<()> {
debug_assert!(id < self.disk_inode.read().blocks as usize);
let to_remove = self.get_disk_block_id(id)?;
let current_last = self.get_disk_block_id(self.disk_inode.read().blocks as usize - 1)?;
self.set_disk_block_id(id, current_last)?;
self.disk_inode.write().blocks -= 1;
let new_size = self.disk_inode.read().blocks as usize * BLKSIZE;
self._set_size(new_size);
self.fs.free_block(to_remove);
fn remove_direntry(&self, id: usize) -> vfs::Result<()> {
let size = self.disk_inode.read().size as usize;
let dirent_count = size / DIRENT_SIZE;
debug_assert!(id < dirent_count);
let last_dirent = self.read_direntry(dirent_count - 1)?;
self.write_direntry(id, &last_dirent)?;
self._resize(size - DIRENT_SIZE)?;
Ok(())
}
/// Resize content size, no matter what type it is.
@ -215,50 +221,53 @@ impl INodeImpl {
if blocks > MAX_NBLOCK_DOUBLE_INDIRECT as u32 {
return Err(FsError::InvalidParam);
}
use core::cmp::{Ord, Ordering};
use core::cmp::Ordering;
let old_blocks = self.disk_inode.read().blocks;
match blocks.cmp(&old_blocks) {
Ordering::Equal => {} // Do nothing
Ordering::Equal => {
self.disk_inode.write().size = len as u32;
}
Ordering::Greater => {
{
let mut disk_inode = self.disk_inode.write();
disk_inode.blocks = blocks;
// allocate indirect block if needed
if old_blocks < MAX_NBLOCK_DIRECT as u32 && blocks >= MAX_NBLOCK_DIRECT as u32 {
disk_inode.indirect = self.fs.alloc_block().expect("no space") as u32;
let mut disk_inode = self.disk_inode.write();
disk_inode.blocks = blocks;
// allocate indirect block if needed
if old_blocks < MAX_NBLOCK_DIRECT as u32 && blocks >= MAX_NBLOCK_DIRECT as u32 {
disk_inode.indirect = self.fs.alloc_block().expect("no space") as u32;
}
// allocate double indirect block if needed
if blocks >= MAX_NBLOCK_INDIRECT as u32 {
if disk_inode.db_indirect == 0 {
disk_inode.db_indirect = self.fs.alloc_block().expect("no space") as u32;
}
// allocate double indirect block if needed
if blocks >= MAX_NBLOCK_INDIRECT as u32 {
if disk_inode.db_indirect == 0 {
disk_inode.db_indirect = self.fs.alloc_block().expect("no space") as u32;
}
let indirect_begin = {
if (old_blocks as usize) < MAX_NBLOCK_INDIRECT {
0
} else {
(old_blocks as usize - MAX_NBLOCK_INDIRECT) / BLK_NENTRY + 1
}
};
let indirect_end = (blocks as usize - MAX_NBLOCK_INDIRECT) / BLK_NENTRY + 1;
for i in indirect_begin..indirect_end {
let indirect = self.fs.alloc_block().expect("no space") as u32;
self.fs.device.write_block(
disk_inode.db_indirect as usize,
ENTRY_SIZE * i,
indirect.as_buf(),
)?;
let indirect_begin = {
if (old_blocks as usize) < MAX_NBLOCK_INDIRECT {
0
} else {
(old_blocks as usize - MAX_NBLOCK_INDIRECT) / BLK_NENTRY + 1
}
};
let indirect_end = (blocks as usize - MAX_NBLOCK_INDIRECT) / BLK_NENTRY + 1;
for i in indirect_begin..indirect_end {
let indirect = self.fs.alloc_block().expect("no space") as u32;
self.fs.device.write_block(
disk_inode.db_indirect as usize,
ENTRY_SIZE * i,
indirect.as_buf(),
)?;
}
}
drop(disk_inode);
// allocate extra blocks
for i in old_blocks..blocks {
let disk_block_id = self.fs.alloc_block().expect("no space");
self.set_disk_block_id(i as usize, disk_block_id)?;
}
// clean up
let old_size = self._size();
self._set_size(len);
self._clean_at(old_size, len).unwrap();
let mut disk_inode = self.disk_inode.write();
let old_size = disk_inode.size as usize;
disk_inode.size = len as u32;
drop(disk_inode);
self._clean_at(old_size, len)?;
}
Ordering::Less => {
// free extra blocks
@ -268,7 +277,9 @@ impl INodeImpl {
}
let mut disk_inode = self.disk_inode.write();
// free indirect block if needed
if blocks < MAX_NBLOCK_DIRECT as u32 && disk_inode.blocks >= MAX_NBLOCK_DIRECT as u32 {
if blocks < MAX_NBLOCK_DIRECT as u32
&& disk_inode.blocks >= MAX_NBLOCK_DIRECT as u32
{
self.fs.free_block(disk_inode.indirect as usize);
disk_inode.indirect = 0;
}
@ -281,7 +292,8 @@ impl INodeImpl {
(blocks as usize - MAX_NBLOCK_INDIRECT) / BLK_NENTRY + 1
}
};
let indirect_end = (disk_inode.blocks as usize - MAX_NBLOCK_INDIRECT) / BLK_NENTRY + 1;
let indirect_end =
(disk_inode.blocks as usize - MAX_NBLOCK_INDIRECT) / BLK_NENTRY + 1;
for i in indirect_begin..indirect_end {
let mut indirect: u32 = 0;
self.fs.device.read_block(
@ -299,38 +311,18 @@ impl INodeImpl {
}
}
disk_inode.blocks = blocks;
disk_inode.size = len as u32;
}
}
self._set_size(len);
Ok(())
}
/// Get the actual size of this inode,
/// since size in inode for dir is not real size
fn _size(&self) -> usize {
let disk_inode = self.disk_inode.read();
match disk_inode.type_ {
FileType::Dir => disk_inode.blocks as usize * BLKSIZE,
FileType::File | FileType::SymLink => disk_inode.size as usize,
_ => panic!("Unknown file type"),
}
}
/// Set the ucore compat size of this inode,
/// Size in inode for dir is size of entries
fn _set_size(&self, len: usize) {
let mut disk_inode = self.disk_inode.write();
disk_inode.size = match disk_inode.type_ {
FileType::Dir => disk_inode.blocks as usize * DIRENT_SIZE,
FileType::File | FileType::SymLink => len,
_ => panic!("Unknown file type"),
} as u32
}
// Note: the _\w*_at method always return begin>size?0:begin<end?0:(min(size,end)-begin) when success
/// Read/Write content, no matter what type it is
fn _io_at<F>(&self, begin: usize, end: usize, mut f: F) -> vfs::Result<usize>
where
F: FnMut(&Arc<Device>, &BlockRange, usize) -> vfs::Result<()>,
{
let size = self._size();
let size = self.disk_inode.read().size as usize;
let iter = BlockIter {
begin: size.min(begin),
end: size.min(end),
@ -445,6 +437,13 @@ impl vfs::INode for INodeImpl {
_ => Err(FsError::NotFile)
}
}
fn poll(&self) -> vfs::Result<vfs::PollStatus> {
Ok(vfs::PollStatus {
read: true,
write: true,
error: false,
})
}
/// the size returned here is logical size(entry num for directory), not the disk space used.
fn metadata(&self) -> vfs::Result<vfs::Metadata> {
let disk_inode = self.disk_inode.read();
@ -458,6 +457,7 @@ impl vfs::INode for INodeImpl {
FileType::BlockDevice => 0,
_ => panic!("Unknown file type"),
},
size: disk_inode.size as usize,
mode: 0o777,
type_: vfs::FileType::from(disk_inode.type_.clone()),
blocks: disk_inode.blocks as usize,
@ -470,12 +470,10 @@ impl vfs::INode for INodeImpl {
blk_size: BLKSIZE,
})
}
fn chmod(&self, _mode: u16) -> vfs::Result<()> {
fn set_metadata(&self, _metadata: &vfs::Metadata) -> vfs::Result<()> {
// No-op for sfs
Ok(())
}
fn sync_all(&self) -> vfs::Result<()> {
let mut disk_inode = self.disk_inode.write();
if disk_inode.dirty() {
@ -520,13 +518,10 @@ impl vfs::INode for INodeImpl {
};
// Write new entry
let entry = DiskEntry {
self.append_direntry(&DiskEntry {
id: inode.id as u32,
name: Str256::from(name),
};
let old_size = self._size();
self._resize(old_size + BLKSIZE)?;
self._write_at(old_size, entry.as_buf()).unwrap();
})?;
inode.nlinks_inc();
if type_ == vfs::FileType::Dir {
inode.nlinks_inc(); //for .
@ -535,6 +530,33 @@ impl vfs::INode for INodeImpl {
Ok(inode)
}
fn link(&self, name: &str, other: &Arc<INode>) -> vfs::Result<()> {
let info = self.metadata()?;
if info.type_ != vfs::FileType::Dir {
return Err(FsError::NotDir);
}
if info.nlinks <= 0 {
return Err(FsError::DirRemoved);
}
if !self.get_file_inode_id(name).is_none() {
return Err(FsError::EntryExist);
}
let child = other
.downcast_ref::<INodeImpl>()
.ok_or(FsError::NotSameFs)?;
if !Arc::ptr_eq(&self.fs, &child.fs) {
return Err(FsError::NotSameFs);
}
if child.metadata()?.type_ == vfs::FileType::Dir {
return Err(FsError::IsDir);
}
self.append_direntry(&DiskEntry {
id: child.id as u32,
name: Str256::from(name),
})?;
child.nlinks_inc();
Ok(())
}
fn unlink(&self, name: &str) -> vfs::Result<()> {
let info = self.metadata()?;
if info.type_ != vfs::FileType::Dir {
@ -558,8 +580,7 @@ impl vfs::INode for INodeImpl {
let type_ = inode.disk_inode.read().type_;
if type_ == FileType::Dir {
// only . and ..
assert!(inode.disk_inode.read().blocks >= 2);
if inode.disk_inode.read().blocks > 2 {
if inode.disk_inode.read().size as usize / DIRENT_SIZE > 2 {
return Err(FsError::DirNotEmpty);
}
}
@ -568,40 +589,10 @@ impl vfs::INode for INodeImpl {
inode.nlinks_dec(); //for .
self.nlinks_dec(); //for ..
}
self.remove_dirent_page(entry_id)?;
self.remove_direntry(entry_id)?;
Ok(())
}
fn link(&self, name: &str, other: &Arc<INode>) -> vfs::Result<()> {
let info = self.metadata()?;
if info.type_ != vfs::FileType::Dir {
return Err(FsError::NotDir);
}
if info.nlinks <= 0 {
return Err(FsError::DirRemoved);
}
if !self.get_file_inode_id(name).is_none() {
return Err(FsError::EntryExist);
}
let child = other
.downcast_ref::<INodeImpl>()
.ok_or(FsError::NotSameFs)?;
if !Arc::ptr_eq(&self.fs, &child.fs) {
return Err(FsError::NotSameFs);
}
if child.metadata()?.type_ == vfs::FileType::Dir {
return Err(FsError::IsDir);
}
let entry = DiskEntry {
id: child.id as u32,
name: Str256::from(name),
};
let old_size = self._size();
self._resize(old_size + BLKSIZE)?;
self._write_at(old_size, entry.as_buf()).unwrap();
child.nlinks_inc();
Ok(())
}
fn move_(&self, old_name: &str, target: &Arc<INode>, new_name: &str) -> vfs::Result<()> {
let info = self.metadata()?;
if info.type_ != vfs::FileType::Dir {
@ -639,31 +630,27 @@ impl vfs::INode for INodeImpl {
.ok_or(FsError::EntryNotFound)?;
if info.inode == dest_info.inode {
// rename: in place modify name
let mut entry: DiskEntry = unsafe { uninitialized() };
let entry_pos = entry_id as usize * BLKSIZE;
self._read_at(entry_pos, entry.as_buf_mut()).unwrap();
entry.name = Str256::from(new_name);
self._write_at(entry_pos, entry.as_buf()).unwrap();
self.write_direntry(
entry_id,
&DiskEntry {
id: inode_id as u32,
name: Str256::from(new_name),
},
)?;
} else {
// move
let inode = self.fs.get_inode(inode_id);
let entry = DiskEntry {
dest.append_direntry(&DiskEntry {
id: inode_id as u32,
name: Str256::from(new_name),
};
let old_size = dest._size();
dest._resize(old_size + BLKSIZE)?;
dest._write_at(old_size, entry.as_buf()).unwrap();
self.remove_dirent_page(entry_id)?;
})?;
self.remove_direntry(entry_id)?;
let inode = self.fs.get_inode(inode_id);
if inode.metadata()?.type_ == vfs::FileType::Dir {
self.nlinks_dec();
dest.nlinks_inc();
}
}
Ok(())
}
fn find(&self, name: &str) -> vfs::Result<Arc<vfs::INode>> {
@ -678,14 +665,15 @@ impl vfs::INode for INodeImpl {
if self.disk_inode.read().type_ != FileType::Dir {
return Err(FsError::NotDir);
}
if id >= self.disk_inode.read().blocks as usize {
if id >= self.disk_inode.read().size as usize / DIRENT_SIZE {
return Err(FsError::EntryNotFound);
};
let mut entry: DiskEntry = unsafe { uninitialized() };
self._read_at(id as usize * BLKSIZE, entry.as_buf_mut())
.unwrap();
let entry = self.read_direntry(id)?;
Ok(String::from(entry.name.as_ref()))
}
fn io_control(&self, _cmd: u32, _data: usize) -> vfs::Result<()> {
Err(FsError::NotSupported)
}
fn fs(&self) -> Arc<vfs::FileSystem> {
self.fs.clone()
}
@ -746,13 +734,17 @@ pub struct SimpleFileSystem {
impl SimpleFileSystem {
/// Load SFS from device
pub fn open(device: Arc<Device>) -> vfs::Result<Arc<Self>> {
let super_block = device.load_struct::<SuperBlock>(BLKN_SUPER).unwrap();
let super_block = device.load_struct::<SuperBlock>(BLKN_SUPER)?;
if !super_block.check() {
return Err(FsError::WrongFs);
}
let mut freemap_disk = vec![0u8; BLKSIZE * super_block.freemap_blocks as usize];
for i in 0..super_block.freemap_blocks as usize {
device.read_block(BLKN_FREEMAP + i, 0, &mut freemap_disk[i * BLKSIZE..(i+1) *BLKSIZE])?;
device.read_block(
BLKN_FREEMAP + i,
0,
&mut freemap_disk[i * BLKSIZE..(i + 1) * BLKSIZE],
)?;
}
Ok(SimpleFileSystem {
@ -766,7 +758,7 @@ impl SimpleFileSystem {
.wrap())
}
/// Create a new SFS on blank disk
pub fn create(device: Arc<Device>, space: usize) -> Arc<Self> {
pub fn create(device: Arc<Device>, space: usize) -> vfs::Result<Arc<Self>> {
let blocks = (space + BLKSIZE - 1) / BLKSIZE;
let freemap_blocks = (space + BLKBITS * BLKSIZE - 1) / BLKBITS / BLKSIZE;
assert!(blocks >= 16, "space too small");
@ -799,12 +791,12 @@ impl SimpleFileSystem {
// Init root INode
let root = sfs._new_inode(BLKN_ROOT, Dirty::new_dirty(DiskINode::new_dir()));
root.init_dir_entry(BLKN_ROOT).unwrap();
root.init_direntry(BLKN_ROOT)?;
root.nlinks_inc(); //for .
root.nlinks_inc(); //for ..(root's parent is itself)
root.sync_all().unwrap();
root.sync_all()?;
sfs
Ok(sfs)
}
/// Wrap pure SimpleFileSystem with Arc
/// Used in constructors
@ -897,7 +889,7 @@ impl SimpleFileSystem {
let id = self.alloc_block().ok_or(FsError::NoDeviceSpace)?;
let disk_inode = Dirty::new_dirty(DiskINode::new_dir());
let inode = self._new_inode(id, disk_inode);
inode.init_dir_entry(parent)?;
inode.init_direntry(parent)?;
Ok(inode)
}
/// Create a new INode chardevice
@ -926,17 +918,17 @@ impl vfs::FileSystem for SimpleFileSystem {
let mut super_block = self.super_block.write();
if super_block.dirty() {
self.device
.write_at(BLKSIZE * BLKN_SUPER, super_block.as_buf())
.unwrap();
.write_at(BLKSIZE * BLKN_SUPER, super_block.as_buf())?;
super_block.sync();
}
let mut free_map = self.free_map.write();
if free_map.dirty() {
let data = free_map.as_buf();
for i in 0..super_block.freemap_blocks as usize {
self.device
.write_at(BLKSIZE * (BLKN_FREEMAP + i), &data[i * BLKSIZE..(i+1) * BLKSIZE])
.unwrap();
self.device.write_at(
BLKSIZE * (BLKN_FREEMAP + i),
&data[i * BLKSIZE..(i + 1) * BLKSIZE],
)?;
}
free_map.sync();
}
@ -946,6 +938,7 @@ impl vfs::FileSystem for SimpleFileSystem {
inode.sync_all()?;
}
}
self.device.sync()?;
Ok(())
}

@ -205,7 +205,7 @@ pub type INodeId = BlockId;
pub const NODEVICE: usize = 100;
/// magic number for sfs
pub const MAGIC: u32 = 0x2f8dbe2a;
pub const MAGIC: u32 = 0x2f8dbe2b;
/// size of block
pub const BLKSIZE: usize = 1usize << BLKSIZE_LOG2;
/// log2( size of block )
@ -234,7 +234,7 @@ pub const ENTRY_SIZE: usize = 4;
/// number of entries in a block
pub const BLK_NENTRY: usize = BLKSIZE / ENTRY_SIZE;
/// size of a dirent used in the size field
pub const DIRENT_SIZE: usize = MAX_FNAME_LEN + 1;
pub const DIRENT_SIZE: usize = MAX_FNAME_LEN + 1 + ENTRY_SIZE;
/// max number of blocks with direct blocks
pub const MAX_NBLOCK_DIRECT: usize = NDIRECT;
/// max number of blocks with indirect blocks

@ -20,6 +20,7 @@ fn _open_sample_file() -> Arc<SimpleFileSystem> {
fn _create_new_sfs() -> Arc<SimpleFileSystem> {
let file = tempfile::tempfile().expect("failed to create file");
SimpleFileSystem::create(Arc::new(Mutex::new(file)), 32 * 4096 * 4096)
.expect("failed to create SFS")
}
#[test]
@ -43,7 +44,7 @@ fn create_file() -> Result<()> {
assert_eq!(
file1.metadata()?,
Metadata {
inode: 8,
inode: 7,
size: 0,
type_: FileType::File,
mode: 0o777,
@ -165,11 +166,26 @@ fn create_then_lookup() -> Result<()> {
"failed to find dir1/file2 by weird relative"
);
assert!(root.lookup("./dir1/../file2").is_err(), "found non-existent file");
assert!(root.lookup("./dir1/../file3").is_err(), "found non-existent file");
assert!(root.lookup("/dir1/../dir1/../file3").is_err(), "found non-existent file");
assert!(root.lookup("/dir1/../../../dir1/../file3").is_err(), "found non-existent file");
assert!(root.lookup("/").unwrap().lookup("dir1/../file2").is_err(), "found non-existent file");
assert!(
root.lookup("./dir1/../file2").is_err(),
"found non-existent file"
);
assert!(
root.lookup("./dir1/../file3").is_err(),
"found non-existent file"
);
assert!(
root.lookup("/dir1/../dir1/../file3").is_err(),
"found non-existent file"
);
assert!(
root.lookup("/dir1/../../../dir1/../file3").is_err(),
"found non-existent file"
);
assert!(
root.lookup("/").unwrap().lookup("dir1/../file2").is_err(),
"found non-existent file"
);
sfs.sync()?;
Ok(())

@ -4,6 +4,9 @@ version = "0.1.0"
authors = ["WangRunji <wangrunji0408@163.com>", "Ben Pig Chu <benpichu@gmail.com>"]
edition = "2018"
[dependencies]
spin = "0.5"
[dev-dependencies]
tempfile = "3"

@ -0,0 +1,164 @@
//! A naive LRU cache layer for `BlockDevice`
use super::*;
use alloc::{vec, vec::Vec};
use spin::{Mutex, MutexGuard};
pub struct BlockCache<T: BlockDevice> {
device: T,
bufs: Vec<Mutex<Buf>>,
lru: Mutex<LRU>,
}
struct Buf {
status: BufStatus,
data: Vec<u8>,
}
enum BufStatus {
/// buffer is unused
Unused,
/// buffer has been read from disk
Valid(BlockId),
/// buffer needs to be written to disk
Dirty(BlockId),
}
impl<T: BlockDevice> BlockCache<T> {
pub fn new(device: T, capacity: usize) -> Self {
let mut bufs = Vec::new();
bufs.resize_with(capacity, || {
Mutex::new(Buf {
status: BufStatus::Unused,
data: vec![0; 1 << T::BLOCK_SIZE_LOG2 as usize],
})
});
let lru = Mutex::new(LRU::new(capacity));
BlockCache { device, bufs, lru }
}
/// Get a buffer for `block_id` with any status
fn get_buf(&self, block_id: BlockId) -> MutexGuard<Buf> {
let (i, buf) = self._get_buf(block_id);
self.lru.lock().visit(i);
buf
}
fn _get_buf(&self, block_id: BlockId) -> (usize, MutexGuard<Buf>) {
for (i, buf) in self.bufs.iter().enumerate() {
if let Some(lock) = buf.try_lock() {
match lock.status {
BufStatus::Valid(id) if id == block_id => return (i, lock),
BufStatus::Dirty(id) if id == block_id => return (i, lock),
_ => {}
}
}
}
self.get_unused()
}
/// Get an unused buffer
fn get_unused(&self) -> (usize, MutexGuard<Buf>) {
for (i, buf) in self.bufs.iter().enumerate() {
if let Some(lock) = buf.try_lock() {
if let BufStatus::Unused = lock.status {
return (i, lock);
}
}
}
let victim_id = self.lru.lock().victim();
let mut victim = self.bufs[victim_id].lock();
self.write_back(&mut victim).expect("failed to write back");
victim.status = BufStatus::Unused;
(victim_id, victim)
}
/// Write back data if buffer is dirty
fn write_back(&self, buf: &mut Buf) -> Result<()> {
if let BufStatus::Dirty(block_id) = buf.status {
self.device.write_at(block_id, &buf.data)?;
buf.status = BufStatus::Valid(block_id);
}
Ok(())
}
}
impl<T: BlockDevice> Drop for BlockCache<T> {
fn drop(&mut self) {
BlockDevice::sync(self).expect("failed to sync");
}
}
impl<T: BlockDevice> BlockDevice for BlockCache<T> {
const BLOCK_SIZE_LOG2: u8 = T::BLOCK_SIZE_LOG2;
fn read_at(&self, block_id: BlockId, buffer: &mut [u8]) -> Result<()> {
let mut buf = self.get_buf(block_id);
match buf.status {
BufStatus::Unused => {
// read from device
self.device.read_at(block_id, &mut buf.data)?;
buf.status = BufStatus::Valid(block_id);
}
_ => {}
}
let len = 1 << Self::BLOCK_SIZE_LOG2 as usize;
buffer[..len].copy_from_slice(&buf.data);
Ok(())
}
fn write_at(&self, block_id: BlockId, buffer: &[u8]) -> Result<()> {
let mut buf = self.get_buf(block_id);
buf.status = BufStatus::Dirty(block_id);
let len = 1 << Self::BLOCK_SIZE_LOG2 as usize;
buf.data.copy_from_slice(&buffer[..len]);
Ok(())
}
fn sync(&self) -> Result<()> {
for buf in self.bufs.iter() {
self.write_back(&mut buf.lock())?;
}
self.device.sync()?;
Ok(())
}
}
/// Doubly circular linked list LRU manager
struct LRU {
prev: Vec<usize>,
next: Vec<usize>,
}
impl LRU {
fn new(size: usize) -> Self {
LRU {
prev: (size - 1..size).chain(0..size - 1).collect(),
next: (1..size).chain(0..1).collect(),
}
}
/// Visit element `id`, move it to head.
fn visit(&mut self, id: usize) {
if id == 0 || id >= self.prev.len() {
return;
}
self._list_remove(id);
self._list_insert_head(id);
}
/// Get a victim at tail.
fn victim(&self) -> usize {
self.prev[0]
}
fn _list_remove(&mut self, id: usize) {
let prev = self.prev[id];
let next = self.next[id];
self.prev[next] = prev;
self.next[prev] = next;
}
fn _list_insert_head(&mut self, id: usize) {
let head = self.next[0];
self.prev[id] = 0;
self.next[id] = head;
self.next[0] = id;
self.prev[head] = id;
}
}

@ -1,6 +1,7 @@
use crate::util::*;
use crate::vfs::Timespec;
pub mod block_cache;
pub mod std_impl;
/// A current time provider
@ -9,30 +10,40 @@ pub trait TimeProvider: Send + Sync {
}
/// Interface for FS to read & write
/// TODO: use std::io::{Read, Write}
pub trait Device: Send + Sync {
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Option<usize>;
fn write_at(&self, offset: usize, buf: &[u8]) -> Option<usize>;
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize>;
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize>;
fn sync(&self) -> Result<()>;
}
/// Device which can only R/W in blocks
pub trait BlockDevice: Send + Sync {
const BLOCK_SIZE_LOG2: u8;
fn read_at(&self, block_id: usize, buf: &mut [u8]) -> bool;
fn write_at(&self, block_id: usize, buf: &[u8]) -> bool;
fn read_at(&self, block_id: BlockId, buf: &mut [u8]) -> Result<()>;
fn write_at(&self, block_id: BlockId, buf: &[u8]) -> Result<()>;
fn sync(&self) -> Result<()>;
}
/// The error type for device.
#[derive(Debug, PartialEq, Eq)]
pub struct DevError;
/// A specialized `Result` type for device.
pub type Result<T> = core::result::Result<T, DevError>;
pub type BlockId = usize;
macro_rules! try0 {
($len:expr, $res:expr) => {
if !$res {
return Some($len);
if $res.is_err() {
return Ok($len);
}
};
}
/// Helper functions to R/W BlockDevice in bytes
impl<T: BlockDevice> Device for T {
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Option<usize> {
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
let iter = BlockIter {
begin: offset,
end: offset + buf.len(),
@ -48,18 +59,18 @@ impl<T: BlockDevice> Device for T {
try0!(len, BlockDevice::read_at(self, range.block, buf));
} else {
use core::mem::uninitialized;
let mut block_buf: [u8; 4096] = unsafe { uninitialized() };
assert!(Self::BLOCK_SIZE_LOG2 <= 12);
let mut block_buf: [u8; 1 << 10] = unsafe { uninitialized() };
assert!(Self::BLOCK_SIZE_LOG2 <= 10);
// Read to local buf first
try0!(len, BlockDevice::read_at(self, range.block, &mut block_buf));
// Copy to target buf then
buf.copy_from_slice(&mut block_buf[range.begin..range.end]);
}
}
Some(buf.len())
Ok(buf.len())
}
fn write_at(&self, offset: usize, buf: &[u8]) -> Option<usize> {
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
let iter = BlockIter {
begin: offset,
end: offset + buf.len(),
@ -75,8 +86,8 @@ impl<T: BlockDevice> Device for T {
try0!(len, BlockDevice::write_at(self, range.block, buf));
} else {
use core::mem::uninitialized;
let mut block_buf: [u8; 4096] = unsafe { uninitialized() };
assert!(Self::BLOCK_SIZE_LOG2 <= 12);
let mut block_buf: [u8; 1 << 10] = unsafe { uninitialized() };
assert!(Self::BLOCK_SIZE_LOG2 <= 10);
// Read to local buf first
try0!(len, BlockDevice::read_at(self, range.block, &mut block_buf));
// Write to local buf
@ -85,7 +96,11 @@ impl<T: BlockDevice> Device for T {
try0!(len, BlockDevice::write_at(self, range.block, &block_buf));
}
}
Some(buf.len())
Ok(buf.len())
}
fn sync(&self) -> Result<()> {
BlockDevice::sync(self)
}
}
@ -96,21 +111,24 @@ mod test {
impl BlockDevice for Mutex<[u8; 16]> {
const BLOCK_SIZE_LOG2: u8 = 2;
fn read_at(&self, block_id: usize, buf: &mut [u8]) -> bool {
fn read_at(&self, block_id: BlockId, buf: &mut [u8]) -> Result<()> {
if block_id >= 4 {
return false;
return Err(DevError);
}
let begin = block_id << 2;
buf[..4].copy_from_slice(&mut self.lock().unwrap()[begin..begin + 4]);
true
Ok(())
}
fn write_at(&self, block_id: usize, buf: &[u8]) -> bool {
fn write_at(&self, block_id: BlockId, buf: &[u8]) -> Result<()> {
if block_id >= 4 {
return false;
return Err(DevError);
}
let begin = block_id << 2;
self.lock().unwrap()[begin..begin + 4].copy_from_slice(&buf[..4]);
true
Ok(())
}
fn sync(&self) -> Result<()> {
Ok(())
}
}
@ -122,17 +140,17 @@ mod test {
// all inside
let ret = Device::read_at(&buf, 3, &mut res);
assert_eq!(ret, Some(6));
assert_eq!(ret, Ok(6));
assert_eq!(res, [3, 4, 5, 6, 7, 8]);
// partly inside
let ret = Device::read_at(&buf, 11, &mut res);
assert_eq!(ret, Some(5));
assert_eq!(ret, Ok(5));
assert_eq!(res, [11, 12, 13, 14, 15, 8]);
// all outside
let ret = Device::read_at(&buf, 16, &mut res);
assert_eq!(ret, Some(0));
assert_eq!(ret, Ok(0));
assert_eq!(res, [11, 12, 13, 14, 15, 8]);
}
@ -143,7 +161,7 @@ mod test {
// all inside
let ret = Device::write_at(&buf, 3, &res);
assert_eq!(ret, Some(6));
assert_eq!(ret, Ok(6));
assert_eq!(
*buf.lock().unwrap(),
[0, 0, 0, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0]
@ -151,7 +169,7 @@ mod test {
// partly inside
let ret = Device::write_at(&buf, 11, &res);
assert_eq!(ret, Some(5));
assert_eq!(ret, Ok(5));
assert_eq!(
*buf.lock().unwrap(),
[0, 0, 0, 3, 4, 5, 6, 7, 8, 0, 0, 3, 4, 5, 6, 7]
@ -159,7 +177,7 @@ mod test {
// all outside
let ret = Device::write_at(&buf, 16, &res);
assert_eq!(ret, Some(0));
assert_eq!(ret, Ok(0));
assert_eq!(
*buf.lock().unwrap(),
[0, 0, 0, 3, 4, 5, 6, 7, 8, 0, 0, 3, 4, 5, 6, 7]

@ -1,29 +1,33 @@
#![cfg(any(test, feature = "std"))]
use std::fs::File;
use std::io::{Read, Seek, SeekFrom, Write};
use std::io::{Error, Read, Seek, SeekFrom, Write};
use std::sync::Mutex;
use std::time::{SystemTime, UNIX_EPOCH};
use super::*;
impl Device for Mutex<File> {
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Option<usize> {
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
let offset = offset as u64;
let mut file = self.lock().unwrap();
match file.seek(SeekFrom::Start(offset)) {
Ok(real_offset) if real_offset == offset => file.read(buf).ok(),
_ => None,
}
file.seek(SeekFrom::Start(offset))?;
let len = file.read(buf)?;
Ok(len)
}
fn write_at(&self, offset: usize, buf: &[u8]) -> Option<usize> {
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
let offset = offset as u64;
let mut file = self.lock().unwrap();
match file.seek(SeekFrom::Start(offset)) {
Ok(real_offset) if real_offset == offset => file.write(buf).ok(),
_ => None,
}
file.seek(SeekFrom::Start(offset))?;
let len = file.write(buf)?;
Ok(len)
}
fn sync(&self) -> Result<()> {
let file = self.lock().unwrap();
file.sync_all()?;
Ok(())
}
}
@ -38,3 +42,9 @@ impl TimeProvider for StdTimeProvider {
}
}
}
impl From<Error> for DevError {
fn from(_: Error) -> Self {
DevError
}
}

@ -1,50 +1,84 @@
use crate::dev::DevError;
use alloc::{string::String, sync::Arc, vec::Vec};
use core::any::Any;
use core::fmt;
use core::result;
use core::str;
/// Abstract operations on a inode.
/// Abstract file system object such as file or directory.
pub trait INode: Any + Sync + Send {
/// Read bytes at `offset` into `buf`, return the number of bytes read.
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize>;
/// Write bytes at `offset` from `buf`, return the number of bytes written.
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize>;
/// Poll the events, return a bitmap of events.
fn poll(&self) -> Result<PollStatus>;
/// Get metadata of the INode
fn metadata(&self) -> Result<Metadata>;
fn chmod(&self, mode: u16) -> Result<()>;
/// Set metadata of the INode
fn set_metadata(&self, metadata: &Metadata) -> Result<()>;
/// Sync all data and metadata
fn sync_all(&self) -> Result<()>;
/// Sync data (not include metadata)
fn sync_data(&self) -> Result<()>;
/// Resize the file
fn resize(&self, len: usize) -> Result<()>;
/// Create a new INode in the directory
fn create(&self, name: &str, type_: FileType, mode: u32) -> Result<Arc<INode>>;
fn unlink(&self, name: &str) -> Result<()>;
/// user of the vfs api should call borrow_mut by itself
/// Create a hard link `name` to `other`
fn link(&self, name: &str, other: &Arc<INode>) -> Result<()>;
/// Delete a hard link `name`
fn unlink(&self, name: &str) -> Result<()>;
/// Move INode `self/old_name` to `target/new_name`.
/// If `target` equals `self`, do rename.
fn move_(&self, old_name: &str, target: &Arc<INode>, new_name: &str) -> Result<()>;
/// lookup with only one layer
/// Find the INode `name` in the directory
fn find(&self, name: &str) -> Result<Arc<INode>>;
/// like list()[id]
/// only get one item in list, often faster than list
/// Get the name of directory entry
fn get_entry(&self, id: usize) -> Result<String>;
// fn io_ctrl(&mut self, op: u32, data: &[u8]) -> Result<()>;
/// Control device
fn io_control(&self, cmd: u32, data: usize) -> Result<()>;
/// Get the file system of the INode
fn fs(&self) -> Arc<FileSystem>;
/// this is used to implement dynamics cast
/// simply return self in the implement of the function
/// This is used to implement dynamics cast.
/// Simply return self in the implement of the function.
fn as_any_ref(&self) -> &Any;
fn ioctl(&self, request: u32, data: *mut u8) -> result::Result<(), IOCTLError>;
}
impl INode {
/// Downcast the INode to specific struct
pub fn downcast_ref<T: INode>(&self) -> Option<&T> {
self.as_any_ref().downcast_ref::<T>()
}
/// Get all directory entries as a Vec
pub fn list(&self) -> Result<Vec<String>> {
let info = self.metadata()?;
if info.type_ != FileType::Dir {
return Err(FsError::NotDir);
}
(0..info.size).map(|i| self.get_entry(i)).collect()
Ok((0..)
.map(|i| self.get_entry(i))
.take_while(|result| result.is_ok())
.filter_map(|result| result.ok())
.collect())
}
/// Lookup path from current INode, and do not follow symlinks
@ -81,34 +115,27 @@ impl INode {
rest_path = String::from(&rest_path[pos + 1..]);
}
};
match result.find(&name) {
Err(error) => return Err(error),
Ok(inode) => {
// Handle symlink
if inode.metadata()?.type_ == FileType::SymLink && follow_times > 0 {
follow_times -= 1;
let mut content = [0u8; 256];
let len = inode.read_at(0, &mut content)?;
if let Ok(path) = str::from_utf8(&content[..len]) {
// result remains unchanged
rest_path = {
let mut new_path = String::from(path);
if let Some('/') = new_path.chars().last() {
new_path += &rest_path;
} else {
new_path += "/";
new_path += &rest_path;
}
new_path
};
} else {
return Err(FsError::NotDir);
}
let inode = result.find(&name)?;
// Handle symlink
if inode.metadata()?.type_ == FileType::SymLink && follow_times > 0 {
follow_times -= 1;
let mut content = [0u8; 256];
let len = inode.read_at(0, &mut content)?;
let path = str::from_utf8(&content[..len]).map_err(|_| FsError::NotDir)?;
// result remains unchanged
rest_path = {
let mut new_path = String::from(path);
if let Some('/') = new_path.chars().last() {
new_path += &rest_path;
} else {
result = inode
new_path += "/";
new_path += &rest_path;
}
}
};
new_path
};
} else {
result = inode
}
}
Ok(result)
}
@ -122,6 +149,13 @@ pub enum IOCTLError {
NotCharDevice = 25, // ENOTTY
}
#[derive(Debug, Default)]
pub struct PollStatus {
pub read: bool,
pub write: bool,
pub error: bool,
}
/// Metadata of INode
///
/// Ref: [http://pubs.opengroup.org/onlinepubs/009604499/basedefs/sys/stat.h.html]
@ -168,7 +202,7 @@ pub struct Timespec {
pub nsec: i32,
}
#[derive(Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum FileType {
File,
Dir,
@ -228,14 +262,25 @@ impl fmt::Display for FsError {
}
}
impl From<DevError> for FsError {
fn from(_: DevError) -> Self {
FsError::DeviceError
}
}
#[cfg(any(test, feature = "std"))]
impl std::error::Error for FsError {}
pub type Result<T> = result::Result<T, FsError>;
/// Abstract filesystem
/// Abstract file system
pub trait FileSystem: Sync {
/// Sync all data to the storage
fn sync(&self) -> Result<()>;
/// Get the root INode of the file system
fn root_inode(&self) -> Arc<INode>;
/// Get the file system information
fn info(&self) -> FsInfo;
}

@ -45,7 +45,6 @@ use std::io::{Read, Seek, SeekFrom, Write};
use std::path::PathBuf;
use std::sgxfs::{OpenOptions, SgxFile};
use std::sync::{SgxMutex as Mutex, SgxRwLock as RwLock};
use std::vec::Vec;
use sgx_types::sgx_key_128bit_t;
#[no_mangle]
@ -147,7 +146,16 @@ pub extern "C" fn ecall_file_set_len(fd: usize, len: usize) -> i32 {
let mut rest_len = len - current_len;
while rest_len != 0 {
let l = rest_len.min(0x1000);
try_io!(file.write(&ZEROS[..l]));
let ret = file.write(&ZEROS[..l]);
if let Err(e) = ret {
if e.raw_os_error().unwrap() == 12 {
warn!("Error 12: \"Cannot allocate memory\". Clear cache and try again.");
try_io!(file.clear_cache());
try_io!(file.write(&ZEROS[..l]));
} else {
try_io!(Err(e));
}
}
rest_len -= l;
}
// NOTE: Don't try to write a large slice at once.

Loading…
Cancel
Save