diff --git a/src/libos/src/fs/hostfs.rs b/src/libos/src/fs/hostfs.rs new file mode 100644 index 00000000..37b7f154 --- /dev/null +++ b/src/libos/src/fs/hostfs.rs @@ -0,0 +1,283 @@ +use alloc::string::String; +use alloc::sync::{Arc, Weak}; +use core::any::Any; +use rcore_fs::vfs::*; +use std::io::{Read, Seek, SeekFrom, Write}; +use std::path::{Path, PathBuf}; +use std::sync::{SgxMutex as Mutex, SgxMutexGuard as MutexGuard}; +use std::untrusted::fs; +use std::untrusted::path::PathEx; + +/// Untrusted file system at host +pub struct HostFS { + path: PathBuf, + self_ref: Weak, +} + +/// INode for `HostFS` +pub struct HNode { + path: PathBuf, + file: Mutex>, + fs: Arc, +} + +impl FileSystem for HostFS { + fn sync(&self) -> Result<()> { + unimplemented!() + } + + fn root_inode(&self) -> Arc { + Arc::new(HNode { + path: self.path.clone(), + file: Mutex::new(None), + fs: self.self_ref.upgrade().unwrap(), + }) + } + + fn info(&self) -> FsInfo { + unimplemented!() + } +} + +impl HostFS { + /// Create a new `HostFS` from host `path` + pub fn new(path: impl AsRef) -> Arc { + HostFS { + path: path.as_ref().to_path_buf(), + self_ref: Weak::default(), + } + .wrap() + } + + /// Wrap pure `HostFS` with Arc + /// Used in constructors + fn wrap(self) -> Arc { + // Create an Arc, make a Weak from it, then put it into the struct. + // It's a little tricky. + let fs = Arc::new(self); + let weak = Arc::downgrade(&fs); + let ptr = Arc::into_raw(fs) as *mut Self; + unsafe { + (*ptr).self_ref = weak; + } + unsafe { Arc::from_raw(ptr) } + } +} + +// workaround for unable to `impl From for FsError` +macro_rules! try_std { + ($ret: expr) => { + $ret.map_err(|e| e.into_fs_error())? + }; +} + +impl INode for HNode { + fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result { + let mut guard = self.open_file()?; + let file = guard.as_mut().unwrap(); + try_std!(file.seek(SeekFrom::Start(offset as u64))); + let len = try_std!(file.read(buf)); + Ok(len) + } + + fn write_at(&self, offset: usize, buf: &[u8]) -> Result { + let mut guard = self.open_file()?; + let file = guard.as_mut().unwrap(); + try_std!(file.seek(SeekFrom::Start(offset as u64))); + let len = try_std!(file.write(buf)); + Ok(len) + } + + fn poll(&self) -> Result { + unimplemented!() + } + + fn metadata(&self) -> Result { + let metadata = try_std!(self.path.metadata()); + Ok(metadata.into_fs_metadata()) + } + + fn set_metadata(&self, metadata: &Metadata) -> Result<()> { + unimplemented!() + } + + fn sync_all(&self) -> Result<()> { + unimplemented!() + } + + fn sync_data(&self) -> Result<()> { + unimplemented!() + } + + fn resize(&self, len: usize) -> Result<()> { + unimplemented!() + } + + fn create(&self, name: &str, type_: FileType, mode: u32) -> Result> { + let new_path = self.path.join(name); + if new_path.exists() { + return Err(FsError::EntryExist); + } + match type_ { + FileType::File => { + try_std!(fs::File::create(&new_path)); + } + _ => unimplemented!("only support creating files in HostFS"), + } + Ok(Arc::new(HNode { + path: new_path, + file: Mutex::new(None), + fs: self.fs.clone(), + })) + } + + fn link(&self, name: &str, other: &Arc) -> Result<()> { + let other = other.downcast_ref::().ok_or(FsError::NotSameFs)?; + try_std!(fs::hard_link(&other.path, &self.path.join(name))); + Ok(()) + } + + fn unlink(&self, name: &str) -> Result<()> { + let new_path = self.path.join(name); + if new_path.is_file() { + try_std!(fs::remove_file(new_path)); + } else if new_path.is_dir() { + unimplemented!("no remove_dir in sgx_std?") + // fs::remove_dir(new_path)?; + } else { + return Err(FsError::EntryNotFound); + } + Ok(()) + } + + fn move_(&self, old_name: &str, target: &Arc, new_name: &str) -> Result<()> { + unimplemented!() + } + + fn find(&self, name: &str) -> Result> { + let new_path = self.path.join(name); + if !new_path.exists() { + return Err(FsError::EntryNotFound); + } + Ok(Arc::new(HNode { + path: new_path, + file: Mutex::new(None), + fs: self.fs.clone(), + })) + } + + fn get_entry(&self, id: usize) -> Result { + if !self.path.is_dir() { + return Err(FsError::NotDir); + } + unimplemented!("no read_dir in sgx_std?") + // FIXME: read_dir + + // self.path + // .read_dir() + // .map_err(|_| FsError::NotDir)? + // .nth(id) + // .map_err(|_| FsError::EntryNotFound)? + // .file_name() + // .into_string() + // .map_err(|_| FsError::InvalidParam) + } + + fn io_control(&self, cmd: u32, data: usize) -> Result<()> { + unimplemented!() + } + + fn fs(&self) -> Arc { + self.fs.clone() + } + + fn as_any_ref(&self) -> &Any { + self + } +} + +impl HNode { + /// Ensure to open the file and store a `File` into `self.file`, + /// return the `MutexGuard`. + /// If the type of `self.path` is not file, then return Err + fn open_file(&self) -> Result>> { + if !self.path.exists() { + return Err(FsError::EntryNotFound); + } + if !self.path.is_file() { + return Err(FsError::NotFile); + } + let mut maybe_file = self.file.lock().unwrap(); + if maybe_file.is_none() { + let file = try_std!(fs::OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(&self.path)); + *maybe_file = Some(file); + } + Ok(maybe_file) + } +} + +trait IntoFsError { + fn into_fs_error(self) -> FsError; +} + +impl IntoFsError for std::io::Error { + fn into_fs_error(self) -> FsError { + use std::io::ErrorKind; + match self.kind() { + ErrorKind::NotFound => FsError::EntryNotFound, + ErrorKind::AlreadyExists => FsError::EntryExist, + ErrorKind::WouldBlock => FsError::Again, + ErrorKind::InvalidInput => FsError::InvalidParam, + ErrorKind::InvalidData => FsError::InvalidParam, + _ => FsError::NotSupported, + } + } +} + +trait IntoFsMetadata { + fn into_fs_metadata(self) -> Metadata; +} + +impl IntoFsMetadata for fs::Metadata { + fn into_fs_metadata(self) -> Metadata { + use libc; + use std::os::fs::MetadataExt; + Metadata { + dev: self.st_dev() as usize, + inode: self.st_ino() as usize, + size: self.st_size() as usize, + blk_size: self.st_blksize() as usize, + blocks: self.st_blocks() as usize, + atime: Timespec { + sec: self.st_atime(), + nsec: self.st_atime_nsec() as i32, + }, + mtime: Timespec { + sec: self.st_mtime(), + nsec: self.st_mtime_nsec() as i32, + }, + ctime: Timespec { + sec: self.st_ctime(), + nsec: self.st_ctime_nsec() as i32, + }, + type_: match self.st_mode() & 0xf000 { + libc::S_IFCHR => FileType::CharDevice, + libc::S_IFBLK => FileType::BlockDevice, + libc::S_IFDIR => FileType::Dir, + libc::S_IFREG => FileType::File, + libc::S_IFLNK => FileType::SymLink, + libc::S_IFSOCK => FileType::Socket, + _ => unimplemented!("unknown file type"), + }, + mode: self.st_mode() as u16 & 0o777, + nlinks: self.st_nlink() as usize, + uid: self.st_uid() as usize, + gid: self.st_gid() as usize, + rdev: self.st_rdev() as usize, + } + } +} diff --git a/src/libos/src/fs/inode_file.rs b/src/libos/src/fs/inode_file.rs index 60414c88..765921fe 100644 --- a/src/libos/src/fs/inode_file.rs +++ b/src/libos/src/fs/inode_file.rs @@ -1,9 +1,10 @@ use rcore_fs::vfs::{FileSystem, FsError, INode}; -use rcore_fs_sefs::SEFS; -use rcore_fs_ramfs::RamFS; use rcore_fs_mountfs::MountFS; +use rcore_fs_ramfs::RamFS; +use rcore_fs_sefs::SEFS; use std::fmt; +use super::hostfs::HostFS; use super::sgx_impl::SgxStorage; use super::*; @@ -13,8 +14,6 @@ lazy_static! { // ramfs as rootfs let rootfs = MountFS::new(RamFS::new()); let root = rootfs.root_inode(); - let bin = root.create("test", FileType::Dir, 0o777) - .expect("failed to mkdir: /test"); // sefs let device = Box::new(SgxStorage::new("sefs")); @@ -22,9 +21,20 @@ lazy_static! { .expect("failed to open SEFS"); // mount sefs at /test + let bin = root.create("test", FileType::Dir, 0o777) + .expect("failed to mkdir: /test"); bin.mount(sefs) .expect("failed to mount SEFS"); + // HostFS + let hostfs = HostFS::new("."); + + // mount HostFS at /host + let host = root.create("host", FileType::Dir, 0o777) + .expect("failed to mkdir: /host"); + host.mount(hostfs) + .expect("failed to mount HostFS"); + root }; } diff --git a/src/libos/src/fs/mod.rs b/src/libos/src/fs/mod.rs index 560554a1..c42df998 100644 --- a/src/libos/src/fs/mod.rs +++ b/src/libos/src/fs/mod.rs @@ -22,6 +22,7 @@ use std::mem::uninitialized; mod access; mod file; mod file_table; +mod hostfs; mod inode_file; mod io_multiplexing; mod null; diff --git a/test/Makefile b/test/Makefile index 7023b082..857acef2 100644 --- a/test/Makefile +++ b/test/Makefile @@ -4,7 +4,7 @@ PROJECT_DIR := $(realpath $(CUR_DIR)/../) # Dependencies: need to be compiled but not to run by any Makefile target TEST_DEPS := dev_null # Tests: need to be compiled and run by test-% target -TESTS := empty argv hello_world malloc mmap file getpid spawn pipe time truncate readdir mkdir link tls pthread uname rlimit client server server_epoll unix_socket cout cpuid rdtsc +TESTS := empty argv hello_world malloc mmap file getpid spawn pipe time truncate readdir mkdir link tls pthread uname rlimit client server server_epoll unix_socket cout hostfs cpuid rdtsc # Benchmarks: need to be compiled and run by bench-% target BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput diff --git a/test/hostfs/Makefile b/test/hostfs/Makefile new file mode 100644 index 00000000..9e1b6dec --- /dev/null +++ b/test/hostfs/Makefile @@ -0,0 +1,5 @@ +include ../test_common.mk + +EXTRA_C_FLAGS := +EXTRA_LINK_FLAGS := +BIN_ARGS := diff --git a/test/hostfs/main.c b/test/hostfs/main.c new file mode 100644 index 00000000..3ce7c9f1 --- /dev/null +++ b/test/hostfs/main.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include +#include +#include + +int main(int argc, const char* argv[]) { + int fd, len; + char read_buf[128] = {0}; + + // read + if ((fd = open("/host/hostfs/sample.txt", O_RDONLY)) < 0) { + printf("ERROR: failed to open a file for read\n"); + return -1; + } + if ((len = read(fd, read_buf, sizeof(read_buf) - 1)) <= 0) { + printf("ERROR: failed to read from the file\n"); + return -1; + } + close(fd); + + if (strcmp("HostFS works!", read_buf) != 0) { + printf("ERROR: the message read from the file is not expected\n"); + return -1; + } + printf("Read file from hostfs successfully!\n"); + + // write + if ((fd = open("/host/hostfs/test_write.txt", O_WRONLY | O_CREAT)) < 0) { + printf("ERROR: failed to open a file for write\n"); + return -1; + } + const char WRITE_STR[] = "Write to hostfs successfully!"; + if ((len = write(fd, WRITE_STR, sizeof(WRITE_STR))) <= 0) { + printf("ERROR: failed to write to the file\n"); + return -1; + } + close(fd); + + printf("Write file to hostfs finished. Please check its content.\n"); + return 0; +} diff --git a/test/hostfs/sample.txt b/test/hostfs/sample.txt new file mode 100644 index 00000000..750bc261 --- /dev/null +++ b/test/hostfs/sample.txt @@ -0,0 +1 @@ +HostFS works! \ No newline at end of file