master
Tianyu Gao 6 years ago
commit e3e0c4b465

@ -3,6 +3,7 @@ members = [
"rcore-fs", "rcore-fs",
"rcore-fs-sfs", "rcore-fs-sfs",
"rcore-fs-sefs", "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>"] authors = ["WangRunji <wangrunji0408@163.com>"]
edition = "2018" edition = "2018"
[features]
use_fuse = ["fuse"]
[dependencies] [dependencies]
time = "0.1" time = "0.1"
libc = "0.2" libc = "0.2"
log = "0.4" log = "0.4"
fuse = "0.3" fuse = { version = "0.3", optional = true }
structopt = "0.2" structopt = "0.2"
env_logger = "0.3" env_logger = "0.3"
git-version = "0.3"
rcore-fs = { path = "../rcore-fs", features = ["std"] } rcore-fs = { path = "../rcore-fs", features = ["std"] }
rcore-fs-sfs = { path = "../rcore-fs-sfs" } rcore-fs-sfs = { path = "../rcore-fs-sfs" }
rcore-fs-sefs = { path = "../rcore-fs-sefs", features = ["std"] } rcore-fs-sefs = { path = "../rcore-fs-sefs", features = ["std"] }

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

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

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

@ -5,7 +5,6 @@ use rcore_fs::vfs::FsError;
#[cfg(any(test, feature = "std"))] #[cfg(any(test, feature = "std"))]
pub use self::std_impl::*; pub use self::std_impl::*;
pub mod sgx_impl;
pub mod std_impl; pub mod std_impl;
/// A file stores a normal file or directory. /// 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<()> { fn set_len(&self, len: usize) -> DevResult<()> {
let mut file = self.lock(); let file = self.lock();
file.set_len(len as u64)?; file.set_len(len as u64)?;
Ok(()) Ok(())
} }
fn flush(&self) -> DevResult<()> { fn flush(&self) -> DevResult<()> {
let mut file = self.lock(); let file = self.lock();
file.sync_all()?; file.sync_all()?;
Ok(()) Ok(())
} }

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

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

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

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

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

@ -4,6 +4,9 @@ version = "0.1.0"
authors = ["WangRunji <wangrunji0408@163.com>", "Ben Pig Chu <benpichu@gmail.com>"] authors = ["WangRunji <wangrunji0408@163.com>", "Ben Pig Chu <benpichu@gmail.com>"]
edition = "2018" edition = "2018"
[dependencies]
spin = "0.5"
[dev-dependencies] [dev-dependencies]
tempfile = "3" 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::util::*;
use crate::vfs::Timespec; use crate::vfs::Timespec;
pub mod block_cache;
pub mod std_impl; pub mod std_impl;
/// A current time provider /// A current time provider
@ -9,30 +10,40 @@ pub trait TimeProvider: Send + Sync {
} }
/// Interface for FS to read & write /// Interface for FS to read & write
/// TODO: use std::io::{Read, Write}
pub trait Device: Send + Sync { pub trait Device: Send + Sync {
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Option<usize>; fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize>;
fn write_at(&self, offset: usize, buf: &[u8]) -> Option<usize>; fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize>;
fn sync(&self) -> Result<()>;
} }
/// Device which can only R/W in blocks /// Device which can only R/W in blocks
pub trait BlockDevice: Send + Sync { pub trait BlockDevice: Send + Sync {
const BLOCK_SIZE_LOG2: u8; const BLOCK_SIZE_LOG2: u8;
fn read_at(&self, block_id: usize, buf: &mut [u8]) -> bool; fn read_at(&self, block_id: BlockId, buf: &mut [u8]) -> Result<()>;
fn write_at(&self, block_id: usize, buf: &[u8]) -> bool; 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 { macro_rules! try0 {
($len:expr, $res:expr) => { ($len:expr, $res:expr) => {
if !$res { if $res.is_err() {
return Some($len); return Ok($len);
} }
}; };
} }
/// Helper functions to R/W BlockDevice in bytes /// Helper functions to R/W BlockDevice in bytes
impl<T: BlockDevice> Device for T { 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 { let iter = BlockIter {
begin: offset, begin: offset,
end: offset + buf.len(), end: offset + buf.len(),
@ -48,18 +59,18 @@ impl<T: BlockDevice> Device for T {
try0!(len, BlockDevice::read_at(self, range.block, buf)); try0!(len, BlockDevice::read_at(self, range.block, buf));
} else { } else {
use core::mem::uninitialized; use core::mem::uninitialized;
let mut block_buf: [u8; 4096] = unsafe { uninitialized() }; let mut block_buf: [u8; 1 << 10] = unsafe { uninitialized() };
assert!(Self::BLOCK_SIZE_LOG2 <= 12); assert!(Self::BLOCK_SIZE_LOG2 <= 10);
// Read to local buf first // Read to local buf first
try0!(len, BlockDevice::read_at(self, range.block, &mut block_buf)); try0!(len, BlockDevice::read_at(self, range.block, &mut block_buf));
// Copy to target buf then // Copy to target buf then
buf.copy_from_slice(&mut block_buf[range.begin..range.end]); 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 { let iter = BlockIter {
begin: offset, begin: offset,
end: offset + buf.len(), end: offset + buf.len(),
@ -75,8 +86,8 @@ impl<T: BlockDevice> Device for T {
try0!(len, BlockDevice::write_at(self, range.block, buf)); try0!(len, BlockDevice::write_at(self, range.block, buf));
} else { } else {
use core::mem::uninitialized; use core::mem::uninitialized;
let mut block_buf: [u8; 4096] = unsafe { uninitialized() }; let mut block_buf: [u8; 1 << 10] = unsafe { uninitialized() };
assert!(Self::BLOCK_SIZE_LOG2 <= 12); assert!(Self::BLOCK_SIZE_LOG2 <= 10);
// Read to local buf first // Read to local buf first
try0!(len, BlockDevice::read_at(self, range.block, &mut block_buf)); try0!(len, BlockDevice::read_at(self, range.block, &mut block_buf));
// Write to local 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)); 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]> { impl BlockDevice for Mutex<[u8; 16]> {
const BLOCK_SIZE_LOG2: u8 = 2; 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 { if block_id >= 4 {
return false; return Err(DevError);
} }
let begin = block_id << 2; let begin = block_id << 2;
buf[..4].copy_from_slice(&mut self.lock().unwrap()[begin..begin + 4]); 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 { if block_id >= 4 {
return false; return Err(DevError);
} }
let begin = block_id << 2; let begin = block_id << 2;
self.lock().unwrap()[begin..begin + 4].copy_from_slice(&buf[..4]); 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 // all inside
let ret = Device::read_at(&buf, 3, &mut res); 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]); assert_eq!(res, [3, 4, 5, 6, 7, 8]);
// partly inside // partly inside
let ret = Device::read_at(&buf, 11, &mut res); 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]); assert_eq!(res, [11, 12, 13, 14, 15, 8]);
// all outside // all outside
let ret = Device::read_at(&buf, 16, &mut res); 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]); assert_eq!(res, [11, 12, 13, 14, 15, 8]);
} }
@ -143,7 +161,7 @@ mod test {
// all inside // all inside
let ret = Device::write_at(&buf, 3, &res); let ret = Device::write_at(&buf, 3, &res);
assert_eq!(ret, Some(6)); assert_eq!(ret, Ok(6));
assert_eq!( assert_eq!(
*buf.lock().unwrap(), *buf.lock().unwrap(),
[0, 0, 0, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0] [0, 0, 0, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0]
@ -151,7 +169,7 @@ mod test {
// partly inside // partly inside
let ret = Device::write_at(&buf, 11, &res); let ret = Device::write_at(&buf, 11, &res);
assert_eq!(ret, Some(5)); assert_eq!(ret, Ok(5));
assert_eq!( assert_eq!(
*buf.lock().unwrap(), *buf.lock().unwrap(),
[0, 0, 0, 3, 4, 5, 6, 7, 8, 0, 0, 3, 4, 5, 6, 7] [0, 0, 0, 3, 4, 5, 6, 7, 8, 0, 0, 3, 4, 5, 6, 7]
@ -159,7 +177,7 @@ mod test {
// all outside // all outside
let ret = Device::write_at(&buf, 16, &res); let ret = Device::write_at(&buf, 16, &res);
assert_eq!(ret, Some(0)); assert_eq!(ret, Ok(0));
assert_eq!( assert_eq!(
*buf.lock().unwrap(), *buf.lock().unwrap(),
[0, 0, 0, 3, 4, 5, 6, 7, 8, 0, 0, 3, 4, 5, 6, 7] [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"))] #![cfg(any(test, feature = "std"))]
use std::fs::File; 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::sync::Mutex;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use super::*; use super::*;
impl Device for Mutex<File> { 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 offset = offset as u64;
let mut file = self.lock().unwrap(); let mut file = self.lock().unwrap();
match file.seek(SeekFrom::Start(offset)) { file.seek(SeekFrom::Start(offset))?;
Ok(real_offset) if real_offset == offset => file.read(buf).ok(), let len = file.read(buf)?;
_ => None, 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 offset = offset as u64;
let mut file = self.lock().unwrap(); let mut file = self.lock().unwrap();
match file.seek(SeekFrom::Start(offset)) { file.seek(SeekFrom::Start(offset))?;
Ok(real_offset) if real_offset == offset => file.write(buf).ok(), let len = file.write(buf)?;
_ => None, 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 alloc::{string::String, sync::Arc, vec::Vec};
use core::any::Any; use core::any::Any;
use core::fmt; use core::fmt;
use core::result; use core::result;
use core::str; use core::str;
/// Abstract operations on a inode. /// Abstract file system object such as file or directory.
pub trait INode: Any + Sync + Send { 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>; 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>; 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 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 /// Sync all data and metadata
fn sync_all(&self) -> Result<()>; fn sync_all(&self) -> Result<()>;
/// Sync data (not include metadata) /// Sync data (not include metadata)
fn sync_data(&self) -> Result<()>; fn sync_data(&self) -> Result<()>;
/// Resize the file
fn resize(&self, len: usize) -> Result<()>; 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 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<()>; 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`. /// Move INode `self/old_name` to `target/new_name`.
/// If `target` equals `self`, do rename. /// If `target` equals `self`, do rename.
fn move_(&self, old_name: &str, target: &Arc<INode>, new_name: &str) -> Result<()>; 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>>; 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 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>; 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 as_any_ref(&self) -> &Any;
fn ioctl(&self, request: u32, data: *mut u8) -> result::Result<(), IOCTLError>; fn ioctl(&self, request: u32, data: *mut u8) -> result::Result<(), IOCTLError>;
} }
impl INode { impl INode {
/// Downcast the INode to specific struct
pub fn downcast_ref<T: INode>(&self) -> Option<&T> { pub fn downcast_ref<T: INode>(&self) -> Option<&T> {
self.as_any_ref().downcast_ref::<T>() self.as_any_ref().downcast_ref::<T>()
} }
/// Get all directory entries as a Vec
pub fn list(&self) -> Result<Vec<String>> { pub fn list(&self) -> Result<Vec<String>> {
let info = self.metadata()?; let info = self.metadata()?;
if info.type_ != FileType::Dir { if info.type_ != FileType::Dir {
return Err(FsError::NotDir); 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 /// Lookup path from current INode, and do not follow symlinks
@ -81,34 +115,27 @@ impl INode {
rest_path = String::from(&rest_path[pos + 1..]); rest_path = String::from(&rest_path[pos + 1..]);
} }
}; };
match result.find(&name) { let inode = result.find(&name)?;
Err(error) => return Err(error), // Handle symlink
Ok(inode) => { if inode.metadata()?.type_ == FileType::SymLink && follow_times > 0 {
// Handle symlink follow_times -= 1;
if inode.metadata()?.type_ == FileType::SymLink && follow_times > 0 { let mut content = [0u8; 256];
follow_times -= 1; let len = inode.read_at(0, &mut content)?;
let mut content = [0u8; 256]; let path = str::from_utf8(&content[..len]).map_err(|_| FsError::NotDir)?;
let len = inode.read_at(0, &mut content)?; // result remains unchanged
if let Ok(path) = str::from_utf8(&content[..len]) { rest_path = {
// result remains unchanged let mut new_path = String::from(path);
rest_path = { if let Some('/') = new_path.chars().last() {
let mut new_path = String::from(path); new_path += &rest_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);
}
} else { } else {
result = inode new_path += "/";
new_path += &rest_path;
} }
} new_path
}; };
} else {
result = inode
}
} }
Ok(result) Ok(result)
} }
@ -122,6 +149,13 @@ pub enum IOCTLError {
NotCharDevice = 25, // ENOTTY NotCharDevice = 25, // ENOTTY
} }
#[derive(Debug, Default)]
pub struct PollStatus {
pub read: bool,
pub write: bool,
pub error: bool,
}
/// Metadata of INode /// Metadata of INode
/// ///
/// Ref: [http://pubs.opengroup.org/onlinepubs/009604499/basedefs/sys/stat.h.html] /// Ref: [http://pubs.opengroup.org/onlinepubs/009604499/basedefs/sys/stat.h.html]
@ -168,7 +202,7 @@ pub struct Timespec {
pub nsec: i32, pub nsec: i32,
} }
#[derive(Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum FileType { pub enum FileType {
File, File,
Dir, 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"))] #[cfg(any(test, feature = "std"))]
impl std::error::Error for FsError {} impl std::error::Error for FsError {}
pub type Result<T> = result::Result<T, FsError>; pub type Result<T> = result::Result<T, FsError>;
/// Abstract filesystem /// Abstract file system
pub trait FileSystem: Sync { pub trait FileSystem: Sync {
/// Sync all data to the storage
fn sync(&self) -> Result<()>; fn sync(&self) -> Result<()>;
/// Get the root INode of the file system
fn root_inode(&self) -> Arc<INode>; fn root_inode(&self) -> Arc<INode>;
/// Get the file system information
fn info(&self) -> FsInfo; fn info(&self) -> FsInfo;
} }

@ -45,7 +45,6 @@ use std::io::{Read, Seek, SeekFrom, Write};
use std::path::PathBuf; use std::path::PathBuf;
use std::sgxfs::{OpenOptions, SgxFile}; use std::sgxfs::{OpenOptions, SgxFile};
use std::sync::{SgxMutex as Mutex, SgxRwLock as RwLock}; use std::sync::{SgxMutex as Mutex, SgxRwLock as RwLock};
use std::vec::Vec;
use sgx_types::sgx_key_128bit_t; use sgx_types::sgx_key_128bit_t;
#[no_mangle] #[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; let mut rest_len = len - current_len;
while rest_len != 0 { while rest_len != 0 {
let l = rest_len.min(0x1000); 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; rest_len -= l;
} }
// NOTE: Don't try to write a large slice at once. // NOTE: Don't try to write a large slice at once.

Loading…
Cancel
Save