diff --git a/src/libos/src/fs/file.rs b/src/libos/src/fs/file.rs index b8241144..18259bc2 100644 --- a/src/libos/src/fs/file.rs +++ b/src/libos/src/fs/file.rs @@ -9,6 +9,11 @@ pub trait File: Debug + Sync + Send { fn readv<'a, 'b>(&self, bufs: &'a mut [&'b mut [u8]]) -> Result; fn writev<'a, 'b>(&self, bufs: &'a [&'b [u8]]) -> Result; fn seek(&self, pos: SeekFrom) -> Result; + fn metadata(&self) -> Result; + fn set_len(&self, len: u64) -> Result<(), Error>; + fn sync_all(&self) -> Result<(), Error>; + fn sync_data(&self) -> Result<(), Error>; + fn read_entry(&self) -> Result; } pub type FileRef = Arc>; @@ -72,6 +77,26 @@ impl File for SgxFile { let inner = inner_guard.borrow_mut(); inner.seek(pos) } + + fn metadata(&self) -> Result { + unimplemented!() + } + + fn set_len(&self, len: u64) -> Result<(), Error> { + unimplemented!() + } + + fn sync_all(&self) -> Result<(), Error> { + unimplemented!() + } + + fn sync_data(&self) -> Result<(), Error> { + unimplemented!() + } + + fn read_entry(&self) -> Result { + unimplemented!() + } } #[derive(Clone)] @@ -307,6 +332,26 @@ impl File for StdoutFile { fn seek(&self, seek_pos: SeekFrom) -> Result { Err(Error::new(Errno::ESPIPE, "Stdout does not support seek")) } + + fn metadata(&self) -> Result { + unimplemented!() + } + + fn set_len(&self, len: u64) -> Result<(), Error> { + unimplemented!() + } + + fn sync_all(&self) -> Result<(), Error> { + unimplemented!() + } + + fn sync_data(&self) -> Result<(), Error> { + unimplemented!() + } + + fn read_entry(&self) -> Result { + unimplemented!() + } } impl Debug for StdoutFile { @@ -376,6 +421,26 @@ impl File for StdinFile { fn writev<'a, 'b>(&self, bufs: &'a [&'b [u8]]) -> Result { Err(Error::new(Errno::EBADF, "Stdin does not support write")) } + + fn metadata(&self) -> Result { + unimplemented!() + } + + fn set_len(&self, len: u64) -> Result<(), Error> { + unimplemented!() + } + + fn sync_all(&self) -> Result<(), Error> { + unimplemented!() + } + + fn sync_data(&self) -> Result<(), Error> { + unimplemented!() + } + + fn read_entry(&self) -> Result { + unimplemented!() + } } impl Debug for StdinFile { diff --git a/src/libos/src/fs/inode_file.rs b/src/libos/src/fs/inode_file.rs index 5e559856..58ebeae6 100644 --- a/src/libos/src/fs/inode_file.rs +++ b/src/libos/src/fs/inode_file.rs @@ -70,6 +70,40 @@ impl File for INodeFile { }; Ok(*offset as i64) } + + fn metadata(&self) -> Result { + let metadata = self.inode.metadata()?; + Ok(metadata) + } + + fn set_len(&self, len: u64) -> Result<(), Error> { + if !self.options.write { + return Err(Error::new(EBADF, "File not writable. Can't set len.")); + } + self.inode.resize(len as usize)?; + Ok(()) + } + + fn sync_all(&self) -> Result<(), Error> { + self.inode.sync()?; + Ok(()) + } + + fn sync_data(&self) -> Result<(), Error> { + // TODO: add sync_data to VFS + self.inode.sync()?; + Ok(()) + } + + fn read_entry(&self) -> Result { + if !self.options.read { + return Err(Error::new(EBADF, "File not readable. Can't read entry.")); + } + let mut offset = self.offset.lock().unwrap(); + let name = self.inode.get_entry(*offset)?; + *offset += 1; + Ok(name) + } } impl INodeFile { diff --git a/src/libos/src/fs/mod.rs b/src/libos/src/fs/mod.rs index f7da3be2..0d55d17b 100644 --- a/src/libos/src/fs/mod.rs +++ b/src/libos/src/fs/mod.rs @@ -12,8 +12,9 @@ pub use self::file::{File, FileRef, SgxFile, StdinFile, StdoutFile}; pub use self::file_table::{FileDesc, FileTable}; pub use self::pipe::Pipe; pub use self::inode_file::{INodeFile, ROOT_INODE}; -use rcore_fs::vfs::{FsError, FileType, INode}; +use rcore_fs::vfs::{FsError, FileType, INode, Metadata, Timespec}; use self::inode_file::OpenOptions; +use process::Process; // TODO: use the type defined in Rust libc. // @@ -87,6 +88,31 @@ pub fn do_readv<'a, 'b>(fd: FileDesc, bufs: &'a mut [&'b mut [u8]]) -> Result Result { + warn!("stat is partial implemented as lstat"); + do_lstat(path) +} + +pub fn do_fstat(fd: u32) -> Result { + info!("fstat: fd: {}", fd); + let current_ref = process::get_current(); + let current_process = current_ref.lock().unwrap(); + let file_ref = current_process.get_files().get(fd)?; + let stat = Stat::from(file_ref.metadata()?); + // TODO: handle symlink + Ok(stat) +} + +pub fn do_lstat(path: &str) -> Result { + info!("lstat: path: {}", path); + + let current_ref = process::get_current(); + let current_process = current_ref.lock().unwrap(); + let inode = current_process.lookup_inode(&path)?; + let stat = Stat::from(inode.metadata()?); + Ok(stat) +} + pub fn do_lseek<'a, 'b>(fd: FileDesc, offset: SeekFrom) -> Result { let current_ref = process::get_current(); let current_process = current_ref.lock().unwrap(); @@ -94,6 +120,63 @@ pub fn do_lseek<'a, 'b>(fd: FileDesc, offset: SeekFrom) -> Result file_ref.seek(offset) } +pub fn do_fsync(fd: FileDesc) -> Result<(), Error> { + info!("fsync: fd: {}", fd); + let current_ref = process::get_current(); + let current_process = current_ref.lock().unwrap(); + let file_ref = current_process.get_files().get(fd)?; + file_ref.sync_all()?; + Ok(()) +} + +pub fn do_fdatasync(fd: FileDesc) -> Result<(), Error> { + info!("fdatasync: fd: {}", fd); + let current_ref = process::get_current(); + let current_process = current_ref.lock().unwrap(); + let file_ref = current_process.get_files().get(fd)?; + file_ref.sync_data()?; + Ok(()) +} + +pub fn do_truncate(path: &str, len: usize) -> Result<(), Error> { + info!("truncate: path: {:?}, len: {}", path, len); + let current_ref = process::get_current(); + let current_process = current_ref.lock().unwrap(); + current_process.lookup_inode(&path)?.resize(len)?; + Ok(()) +} + +pub fn do_ftruncate(fd: FileDesc, len: usize) -> Result<(), Error> { + info!("ftruncate: fd: {}, len: {}", fd, len); + let current_ref = process::get_current(); + let current_process = current_ref.lock().unwrap(); + let file_ref = current_process.get_files().get(fd)?; + file_ref.set_len(len as u64)?; + Ok(()) +} + +pub fn do_getdents64(fd: FileDesc, buf: &mut [u8]) -> Result { + info!("getdents64: fd: {}, buf: {:?}, buf_size: {}", fd, buf.as_ptr(), buf.len()); + let current_ref = process::get_current(); + let current_process = current_ref.lock().unwrap(); + let file_ref = current_process.get_files().get(fd)?; + let info = file_ref.metadata()?; + if info.type_ != FileType::Dir { + return Err(Error::new(ENOTDIR, "")); + } + let mut writer = unsafe { DirentBufWriter::new(buf) }; + loop { + let name = match file_ref.read_entry() { + Err(e) if e.errno == ENOENT => break, + r => r, + }?; + // TODO: get ino from dirent + let ok = writer.try_write(0, 0, &name); + if !ok { break; } + } + Ok(writer.written_size) +} + pub fn do_close(fd: FileDesc) -> Result<(), Error> { let current_ref = process::get_current(); let mut current_process = current_ref.lock().unwrap(); @@ -160,6 +243,14 @@ extern "C" { fn ocall_sync() -> sgx_status_t; } +impl Process { + fn lookup_inode(&self, path: &str) -> Result, Error> { + let cwd = self.get_exec_path().split_at(1).1; // skip start '/' + let inode = ROOT_INODE.lookup(cwd)?.lookup(path)?; + Ok(inode) + } +} + /// Split a `path` str to `(base_path, file_name)` fn split_path(path: &str) -> (&str, &str) { let mut split = path.trim_end_matches('/').rsplitn(2, '/'); @@ -206,3 +297,190 @@ impl OpenFlags { } } } + +#[derive(Debug)] +#[repr(packed)] // Don't use 'C'. Or its size will align up to 8 bytes. +pub struct LinuxDirent64 { + /// Inode number + ino: u64, + /// Offset to next structure + offset: u64, + /// Size of this dirent + reclen: u16, + /// File type + type_: u8, + /// Filename (null-terminated) + name: [u8; 0], +} + +struct DirentBufWriter<'a> { + buf: &'a mut [u8], + rest_size: usize, + written_size: usize, +} + +impl<'a> DirentBufWriter<'a> { + unsafe fn new(buf: &'a mut [u8]) -> Self { + let rest_size = buf.len(); + DirentBufWriter { + buf, + rest_size, + written_size: 0, + } + } + fn try_write(&mut self, inode: u64, type_: u8, name: &str) -> bool { + let len = ::core::mem::size_of::() + name.len() + 1; + let len = (len + 7) / 8 * 8; // align up + if self.rest_size < len { + return false; + } + let dent = LinuxDirent64 { + ino: inode, + offset: 0, + reclen: len as u16, + type_, + name: [], + }; + unsafe { + let ptr = self.buf.as_mut_ptr().add(self.written_size) as *mut LinuxDirent64; + ptr.write(dent); + let name_ptr = ptr.add(1) as _; + write_cstr(name_ptr, name); + } + self.rest_size -= len; + self.written_size += len; + true + } +} + +#[repr(C)] +pub struct Stat { + /// ID of device containing file + dev: u64, + /// inode number + ino: u64, + /// number of hard links + nlink: u64, + + /// file type and mode + mode: StatMode, + /// user ID of owner + uid: u32, + /// group ID of owner + gid: u32, + /// padding + _pad0: u32, + /// device ID (if special file) + rdev: u64, + /// total size, in bytes + size: u64, + /// blocksize for filesystem I/O + blksize: u64, + /// number of 512B blocks allocated + blocks: u64, + + /// last access time + atime: Timespec, + /// last modification time + mtime: Timespec, + /// last status change time + ctime: Timespec, +} + +bitflags! { + pub struct StatMode: u32 { + const NULL = 0; + /// Type + const TYPE_MASK = 0o170000; + /// FIFO + const FIFO = 0o010000; + /// character device + const CHAR = 0o020000; + /// directory + const DIR = 0o040000; + /// block device + const BLOCK = 0o060000; + /// ordinary regular file + const FILE = 0o100000; + /// symbolic link + const LINK = 0o120000; + /// socket + const SOCKET = 0o140000; + + /// Set-user-ID on execution. + const SET_UID = 0o4000; + /// Set-group-ID on execution. + const SET_GID = 0o2000; + + /// Read, write, execute/search by owner. + const OWNER_MASK = 0o700; + /// Read permission, owner. + const OWNER_READ = 0o400; + /// Write permission, owner. + const OWNER_WRITE = 0o200; + /// Execute/search permission, owner. + const OWNER_EXEC = 0o100; + + /// Read, write, execute/search by group. + const GROUP_MASK = 0o70; + /// Read permission, group. + const GROUP_READ = 0o40; + /// Write permission, group. + const GROUP_WRITE = 0o20; + /// Execute/search permission, group. + const GROUP_EXEC = 0o10; + + /// Read, write, execute/search by others. + const OTHER_MASK = 0o7; + /// Read permission, others. + const OTHER_READ = 0o4; + /// Write permission, others. + const OTHER_WRITE = 0o2; + /// Execute/search permission, others. + const OTHER_EXEC = 0o1; + } +} + +impl StatMode { + fn from_type_mode(type_: FileType, mode: u16) -> Self { + let type_ = match type_ { + FileType::File => StatMode::FILE, + FileType::Dir => StatMode::DIR, + FileType::SymLink => StatMode::LINK, + FileType::CharDevice => StatMode::CHAR, + FileType::BlockDevice => StatMode::BLOCK, + FileType::Socket => StatMode::SOCKET, + FileType::NamedPipe => StatMode::FIFO, + _ => StatMode::NULL, + }; + let mode = StatMode::from_bits_truncate(mode as u32); + type_ | mode + } +} + +impl From for Stat { + fn from(info: Metadata) -> Self { + Stat { + dev: info.dev as u64, + ino: info.inode as u64, + mode: StatMode::from_type_mode(info.type_, info.mode as u16), + nlink: info.nlinks as u64, + uid: info.uid as u32, + gid: info.gid as u32, + rdev: 0, + size: info.size as u64, + blksize: info.blk_size as u64, + blocks: info.blocks as u64, + atime: info.atime, + mtime: info.mtime, + ctime: info.ctime, + _pad0: 0 + } + } +} + +/// Write a Rust string to C string +pub unsafe fn write_cstr(ptr: *mut u8, s: &str) { + ptr.copy_from(s.as_ptr(), s.len()); + ptr.add(s.len()).write(0); +} diff --git a/src/libos/src/fs/pipe.rs b/src/libos/src/fs/pipe.rs index 62a10c45..75325e67 100644 --- a/src/libos/src/fs/pipe.rs +++ b/src/libos/src/fs/pipe.rs @@ -77,6 +77,26 @@ impl File for PipeReader { fn seek(&self, pos: SeekFrom) -> Result { Err(Error::new(Errno::ESPIPE, "Pipe does not support seek")) } + + fn metadata(&self) -> Result { + unimplemented!() + } + + fn set_len(&self, len: u64) -> Result<(), Error> { + unimplemented!() + } + + fn sync_all(&self) -> Result<(), Error> { + unimplemented!() + } + + fn sync_data(&self) -> Result<(), Error> { + unimplemented!() + } + + fn read_entry(&self) -> Result { + unimplemented!() + } } unsafe impl Send for PipeReader {} @@ -128,6 +148,26 @@ impl File for PipeWriter { fn seek(&self, seek_pos: SeekFrom) -> Result { Err(Error::new(Errno::ESPIPE, "Pipe does not support seek")) } + + fn metadata(&self) -> Result { + unimplemented!() + } + + fn set_len(&self, len: u64) -> Result<(), Error> { + unimplemented!() + } + + fn sync_all(&self) -> Result<(), Error> { + unimplemented!() + } + + fn sync_data(&self) -> Result<(), Error> { + unimplemented!() + } + + fn read_entry(&self) -> Result { + unimplemented!() + } } unsafe impl Send for PipeWriter {} diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index 76364bb7..bf60348f 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -34,7 +34,15 @@ pub extern "C" fn dispatch_syscall( SYS_write => do_write(arg0 as FileDesc, arg1 as *const u8, arg2 as usize), SYS_readv => do_readv(arg0 as FileDesc, arg1 as *mut iovec_t, arg2 as i32), SYS_writev => do_writev(arg0 as FileDesc, arg1 as *mut iovec_t, arg2 as i32), + SYS_stat => do_stat(arg0 as *const i8, arg1 as *mut fs::Stat), + SYS_fstat => do_fstat(arg0 as FileDesc, arg1 as *mut fs::Stat), + SYS_lstat => do_lstat(arg0 as *const i8, arg1 as *mut fs::Stat), SYS_lseek => do_lseek(arg0 as FileDesc, arg1 as off_t, arg2 as i32), + SYS_fsync => do_fsync(arg0 as FileDesc), + SYS_fdatasync => do_fdatasync(arg0 as FileDesc), + SYS_truncate => do_truncate(arg0 as *const i8, arg1 as usize), + SYS_ftruncate => do_ftruncate(arg0 as FileDesc, arg1 as usize), + SYS_getdents64 => do_getdents64(arg0 as FileDesc, arg1 as *mut u8, arg2 as usize), SYS_sync => do_sync(), SYS_getcwd => do_getcwd(arg0 as *mut u8, arg1 as usize), @@ -159,8 +167,8 @@ fn do_spawn( Ok(0) } -fn do_open(path_buf: *const i8, flags: u32, mode: u32) -> Result { - let path = unsafe { CStr::from_ptr(path_buf).to_string_lossy().into_owned() }; +fn do_open(path: *const i8, flags: u32, mode: u32) -> Result { + let path = clone_cstring_safely(path)?.to_string_lossy().into_owned(); let fd = fs::do_open(&path, flags, mode)?; Ok(fd as isize) } @@ -238,6 +246,32 @@ fn do_readv(fd: FileDesc, iov: *mut iovec_t, count: i32) -> Result Ok(len as isize) } +fn do_stat(path: *const i8, stat_buf: *mut fs::Stat) -> Result { + let path = clone_cstring_safely(path)?.to_string_lossy().into_owned(); + check_mut_ptr(stat_buf)?; + + let stat = fs::do_stat(&path)?; + unsafe { stat_buf.write(stat); } + Ok(0) +} + +fn do_fstat(fd: FileDesc, stat_buf: *mut fs::Stat) -> Result { + check_mut_ptr(stat_buf)?; + + let stat = fs::do_fstat(fd)?; + unsafe { stat_buf.write(stat); } + Ok(0) +} + +fn do_lstat(path: *const i8, stat_buf: *mut fs::Stat) -> Result { + let path = clone_cstring_safely(path)?.to_string_lossy().into_owned(); + check_mut_ptr(stat_buf)?; + + let stat = fs::do_lstat(&path)?; + unsafe { stat_buf.write(stat); } + Ok(0) +} + fn do_lseek(fd: FileDesc, offset: off_t, whence: i32) -> Result { let seek_from = match whence { 0 => { @@ -264,6 +298,36 @@ fn do_lseek(fd: FileDesc, offset: off_t, whence: i32) -> Result { Ok(offset as isize) } +fn do_fsync(fd: FileDesc) -> Result { + fs::do_fsync(fd)?; + Ok(0) +} + +fn do_fdatasync(fd: FileDesc) -> Result { + fs::do_fdatasync(fd)?; + Ok(0) +} + +fn do_truncate(path: *const i8, len: usize) -> Result { + let path = clone_cstring_safely(path)?.to_string_lossy().into_owned(); + fs::do_truncate(&path, len)?; + Ok(0) +} + +fn do_ftruncate(fd: FileDesc, len: usize) -> Result { + fs::do_ftruncate(fd, len)?; + Ok(0) +} + +fn do_getdents64(fd: FileDesc, buf: *mut u8, buf_size: usize) -> Result { + let safe_buf = { + check_mut_array(buf, buf_size)?; + unsafe { std::slice::from_raw_parts_mut(buf, buf_size) } + }; + let len = fs::do_getdents64(fd, safe_buf)?; + Ok(len as isize) +} + fn do_sync() -> Result { fs::do_sync()?; Ok(0)