occlum/src/libos/src/fs/file.rs
Tate, Hongliang Tian 1a365de08f Introduce the new error handling mechanism
* Fast error diagnosing (e.g., backtrace and code location)
* First-class POSIX errno (e.g., every error has an errno)
* Zero-overhead abstraction (e.g., no heap allocation for simple errors)
* Ergonomic grammar (e.g., providing convenient macros)
2019-10-14 03:50:20 +00:00

508 lines
14 KiB
Rust

use super::*;
use std;
use std::borrow::BorrowMut;
use std::fmt;
use std::io::SeekFrom;
pub trait File: Debug + Sync + Send + Any {
fn read(&self, buf: &mut [u8]) -> Result<usize>;
fn write(&self, buf: &[u8]) -> Result<usize>;
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize>;
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize>;
fn readv(&self, bufs: &mut [&mut [u8]]) -> Result<usize>;
fn writev(&self, bufs: &[&[u8]]) -> Result<usize>;
fn seek(&self, pos: SeekFrom) -> Result<off_t>;
fn metadata(&self) -> Result<Metadata>;
fn set_len(&self, len: u64) -> Result<()>;
fn sync_all(&self) -> Result<()>;
fn sync_data(&self) -> Result<()>;
fn read_entry(&self) -> Result<String>;
fn as_any(&self) -> &Any;
}
pub type FileRef = Arc<Box<File>>;
#[derive(Debug)]
#[repr(C)]
pub struct SgxFile {
inner: SgxMutex<SgxFileInner>,
}
impl SgxFile {
pub fn new(
file: Arc<SgxMutex<fs_impl::SgxFile>>,
is_readable: bool,
is_writable: bool,
is_append: bool,
) -> Result<SgxFile> {
if !is_readable && !is_writable {
return_errno!(EINVAL, "Invalid permissions");
}
Ok(SgxFile {
inner: SgxMutex::new(SgxFileInner {
pos: 0 as usize,
file: file,
is_readable,
is_writable,
is_append,
}),
})
}
}
impl File for SgxFile {
fn read(&self, buf: &mut [u8]) -> Result<usize> {
let mut inner_guard = self.inner.lock().unwrap();
let inner = inner_guard.borrow_mut();
inner.read(buf)
}
fn write(&self, buf: &[u8]) -> Result<usize> {
let mut inner_guard = self.inner.lock().unwrap();
let inner = inner_guard.borrow_mut();
inner.write(buf)
}
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
let mut inner_guard = self.inner.lock().unwrap();
let inner = inner_guard.borrow_mut();
inner.seek(SeekFrom::Start(offset as u64))?;
inner.read(buf)
}
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
let mut inner_guard = self.inner.lock().unwrap();
let inner = inner_guard.borrow_mut();
inner.seek(SeekFrom::Start(offset as u64))?;
inner.write(buf)
}
fn readv(&self, bufs: &mut [&mut [u8]]) -> Result<usize> {
let mut inner_guard = self.inner.lock().unwrap();
let inner = inner_guard.borrow_mut();
inner.readv(bufs)
}
fn writev(&self, bufs: &[&[u8]]) -> Result<usize> {
let mut inner_guard = self.inner.lock().unwrap();
let inner = inner_guard.borrow_mut();
inner.writev(bufs)
}
fn seek(&self, pos: SeekFrom) -> Result<off_t> {
let mut inner_guard = self.inner.lock().unwrap();
let inner = inner_guard.borrow_mut();
inner.seek(pos)
}
fn metadata(&self) -> Result<Metadata> {
unimplemented!()
}
fn set_len(&self, len: u64) -> Result<()> {
unimplemented!()
}
fn sync_all(&self) -> Result<()> {
unimplemented!()
}
fn sync_data(&self) -> Result<()> {
unimplemented!()
}
fn read_entry(&self) -> Result<String> {
unimplemented!()
}
fn as_any(&self) -> &Any {
self
}
}
#[derive(Clone)]
#[repr(C)]
struct SgxFileInner {
// perms: FilePerms,
pos: usize,
file: Arc<SgxMutex<fs_impl::SgxFile>>,
is_readable: bool,
is_writable: bool,
is_append: bool,
}
impl SgxFileInner {
pub fn write(&mut self, buf: &[u8]) -> Result<usize> {
if !self.is_writable {
return_errno!(EINVAL, "File not writable");
}
let mut file_guard = self.file.lock().unwrap();
let file = file_guard.borrow_mut();
let seek_pos = if !self.is_append {
SeekFrom::Start(self.pos as u64)
} else {
SeekFrom::End(0)
};
// TODO: recover from error
file.seek(seek_pos).map_err(|e| errno!(e))?;
let write_len = { file.write(buf).map_err(|e| errno!(e))? };
if !self.is_append {
self.pos += write_len;
}
Ok(write_len)
}
pub fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
if !self.is_readable {
return_errno!(EINVAL, "File not readable");
}
let mut file_guard = self.file.lock().unwrap();
let file = file_guard.borrow_mut();
let seek_pos = SeekFrom::Start(self.pos as u64);
file.seek(seek_pos).map_err(|e| errno!(e))?;
let read_len = { file.read(buf).map_err(|e| errno!(e))? };
self.pos += read_len;
Ok(read_len)
}
pub fn seek(&mut self, pos: SeekFrom) -> Result<off_t> {
let mut file_guard = self.file.lock().unwrap();
let file = file_guard.borrow_mut();
let pos = match pos {
SeekFrom::Start(absolute_offset) => pos,
SeekFrom::End(relative_offset) => pos,
SeekFrom::Current(relative_offset) => {
if relative_offset >= 0 {
SeekFrom::Start((self.pos + relative_offset as usize) as u64)
} else {
let backward_offset = (-relative_offset) as usize;
if self.pos < backward_offset {
// underflow
return_errno!(EINVAL, "Invalid seek position");
}
SeekFrom::Start((self.pos - backward_offset) as u64)
}
}
};
self.pos = file.seek(pos).map_err(|e| errno!(e))? as usize;
Ok(self.pos as off_t)
}
pub fn writev(&mut self, bufs: &[&[u8]]) -> Result<usize> {
if !self.is_writable {
return_errno!(EINVAL, "File not writable");
}
let mut file_guard = self.file.lock().unwrap();
let file = file_guard.borrow_mut();
let seek_pos = if !self.is_append {
SeekFrom::Start(self.pos as u64)
} else {
SeekFrom::End(0)
};
file.seek(seek_pos).map_err(|e| errno!(e))?;
let mut total_bytes = 0;
for buf in bufs {
match file.write(buf) {
Ok(this_bytes) => {
total_bytes += this_bytes;
if this_bytes < buf.len() {
break;
}
}
Err(e) => {
match total_bytes {
// a complete failure
0 => return_errno!(EINVAL, "Failed to write"),
// a partially failure
_ => break,
}
}
}
}
self.pos += total_bytes;
Ok(total_bytes)
}
fn readv(&mut self, bufs: &mut [&mut [u8]]) -> Result<usize> {
if !self.is_readable {
return_errno!(EINVAL, "File not readable");
}
let mut file_guard = self.file.lock().unwrap();
let file = file_guard.borrow_mut();
let seek_pos = SeekFrom::Start(self.pos as u64);
file.seek(seek_pos).map_err(|e| errno!(e))?;
let mut total_bytes = 0;
for buf in bufs {
match file.read(buf) {
Ok(this_bytes) => {
total_bytes += this_bytes;
if this_bytes < buf.len() {
break;
}
}
Err(e) => {
match total_bytes {
// a complete failure
0 => return_errno!(EINVAL, "Failed to write"),
// a partially failure
_ => break,
}
}
}
}
self.pos += total_bytes;
Ok(total_bytes)
}
}
unsafe impl Send for SgxFileInner {}
unsafe impl Sync for SgxFileInner {}
impl Debug for SgxFileInner {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "SgxFileInner {{ pos: {}, file: ??? }}", self.pos)
}
}
pub struct StdoutFile {
inner: std::io::Stdout,
}
impl StdoutFile {
pub fn new() -> StdoutFile {
StdoutFile {
inner: std::io::stdout(),
}
}
}
impl File for StdoutFile {
fn read(&self, buf: &mut [u8]) -> Result<usize> {
return_errno!(EBADF, "Stdout does not support read")
}
fn write(&self, buf: &[u8]) -> Result<usize> {
let write_len = { self.inner.lock().write(buf).map_err(|e| errno!(e))? };
Ok(write_len)
}
fn read_at(&self, _offset: usize, buf: &mut [u8]) -> Result<usize> {
self.read(buf)
}
fn write_at(&self, _offset: usize, buf: &[u8]) -> Result<usize> {
self.write(buf)
}
fn readv(&self, bufs: &mut [&mut [u8]]) -> Result<usize> {
return_errno!(EBADF, "Stdout does not support read")
}
fn writev(&self, bufs: &[&[u8]]) -> Result<usize> {
let mut guard = self.inner.lock();
let mut total_bytes = 0;
for buf in bufs {
match guard.write(buf) {
Ok(this_len) => {
total_bytes += this_len;
if this_len < buf.len() {
break;
}
}
Err(e) => {
match total_bytes {
// a complete failure
0 => return_errno!(EINVAL, "Failed to write"),
// a partially failure
_ => break,
}
}
}
}
Ok(total_bytes)
}
fn seek(&self, seek_pos: SeekFrom) -> Result<off_t> {
return_errno!(ESPIPE, "Stdout does not support seek")
}
fn metadata(&self) -> Result<Metadata> {
Ok(Metadata {
dev: 0,
inode: 0,
size: 0,
blk_size: 0,
blocks: 0,
atime: Timespec { sec: 0, nsec: 0 },
mtime: Timespec { sec: 0, nsec: 0 },
ctime: Timespec { sec: 0, nsec: 0 },
type_: FileType::CharDevice,
mode: 0,
nlinks: 0,
uid: 0,
gid: 0,
rdev: 0,
})
}
fn set_len(&self, _len: u64) -> Result<()> {
return_errno!(EINVAL, "Stdout does not support set_len")
}
fn sync_all(&self) -> Result<()> {
self.sync_data()
}
fn sync_data(&self) -> Result<()> {
self.inner.lock().flush()?;
Ok(())
}
fn read_entry(&self) -> Result<String> {
return_errno!(ENOTDIR, "Stdout does not support read_entry")
}
fn as_any(&self) -> &Any {
self
}
}
impl Debug for StdoutFile {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "StdoutFile")
}
}
unsafe impl Send for StdoutFile {}
unsafe impl Sync for StdoutFile {}
pub struct StdinFile {
inner: std::io::Stdin,
}
impl StdinFile {
pub fn new() -> StdinFile {
StdinFile {
inner: std::io::stdin(),
}
}
}
impl File for StdinFile {
fn read(&self, buf: &mut [u8]) -> Result<usize> {
let read_len = { self.inner.lock().read(buf).map_err(|e| errno!(e))? };
Ok(read_len)
}
fn write(&self, buf: &[u8]) -> Result<usize> {
return_errno!(EBADF, "Stdin does not support write")
}
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
unimplemented!()
}
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
unimplemented!()
}
fn readv(&self, bufs: &mut [&mut [u8]]) -> Result<usize> {
let mut guard = self.inner.lock();
let mut total_bytes = 0;
for buf in bufs {
match guard.read(buf) {
Ok(this_len) => {
total_bytes += this_len;
if this_len < buf.len() {
break;
}
}
Err(e) => {
match total_bytes {
// a complete failure
0 => return_errno!(EINVAL, "Failed to write"),
// a partially failure
_ => break,
}
}
}
}
Ok(total_bytes)
}
fn writev(&self, bufs: &[&[u8]]) -> Result<usize> {
return_errno!(EBADF, "Stdin does not support write")
}
fn seek(&self, pos: SeekFrom) -> Result<off_t> {
return_errno!(ESPIPE, "Stdin does not support seek")
}
fn metadata(&self) -> Result<Metadata> {
Ok(Metadata {
dev: 0,
inode: 0,
size: 0,
blk_size: 0,
blocks: 0,
atime: Timespec { sec: 0, nsec: 0 },
mtime: Timespec { sec: 0, nsec: 0 },
ctime: Timespec { sec: 0, nsec: 0 },
type_: FileType::CharDevice,
mode: 0,
nlinks: 0,
uid: 0,
gid: 0,
rdev: 0,
})
}
fn set_len(&self, _len: u64) -> Result<()> {
return_errno!(EINVAL, "Stdin does not support set_len")
}
fn sync_all(&self) -> Result<()> {
self.sync_data()
}
fn sync_data(&self) -> Result<()> {
Ok(())
}
fn read_entry(&self) -> Result<String> {
return_errno!(ENOTDIR, "Stdin does not support read_entry")
}
fn as_any(&self) -> &Any {
self
}
}
impl Debug for StdinFile {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "StdinFile")
}
}
unsafe impl Send for StdinFile {}
unsafe impl Sync for StdinFile {}