diff --git a/src/libos/src/file.rs b/src/libos/src/file.rs index 367f114f..1fbcb82f 100644 --- a/src/libos/src/file.rs +++ b/src/libos/src/file.rs @@ -3,14 +3,13 @@ use {std}; use std::{fmt}; use std::sgxfs as fs_impl; -use std::io::{Read, Write, Seek, SeekFrom}; pub trait File : Debug + Sync + Send { fn read(&self, buf: &mut [u8]) -> Result; fn write(&self, buf: &[u8]) -> Result; fn readv<'a, 'b>(&self, bufs: &'a mut [&'b mut [u8]]) -> Result; fn writev<'a, 'b>(&self, bufs: &'a [&'b [u8]]) -> Result; - //pub seek(&mut self, ) -> Result; + fn seek(&self, pos: SeekFrom) -> Result; } pub type FileRef = Arc>; @@ -23,9 +22,14 @@ pub struct SgxFile { impl SgxFile { pub fn new(file: Arc>, - is_readable: bool, is_writable: bool, is_append: bool) -> SgxFile + is_readable: bool, is_writable: bool, is_append: bool) + -> Result { - SgxFile { + if !is_readable && !is_writable { + return Err(Error::new(Errno::EINVAL, "Invalid permissions")); + } + + Ok(SgxFile { inner: SgxMutex::new(SgxFileInner { pos: 0 as usize, file: file, @@ -33,7 +37,7 @@ impl SgxFile { is_writable, is_append, }), - } + }) } } @@ -61,6 +65,12 @@ impl File for SgxFile { let inner = inner_guard.borrow_mut(); inner.writev(bufs) } + + fn seek(&self, pos: SeekFrom) -> Result { + let mut inner_guard = self.inner.lock().unwrap(); + let inner = inner_guard.borrow_mut(); + inner.seek(pos) + } } #[derive(Clone)] @@ -76,10 +86,18 @@ struct SgxFileInner { impl SgxFileInner { pub fn write(&mut self, buf: &[u8]) -> Result { + if !self.is_writable { + return Err(Error::new(Errno::EINVAL, "File not writable")); + } + let mut file_guard = self.file.lock().unwrap(); let file = file_guard.borrow_mut(); - let seek_pos = SeekFrom::Start(self.pos as u64); + 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| Error::new(Errno::EINVAL, "Failed to seek to a position"))?; @@ -89,11 +107,17 @@ impl SgxFileInner { |e| Error::new(Errno::EINVAL, "Failed to write"))? }; - self.pos += write_len; + if !self.is_append { + self.pos += write_len; + } Ok(write_len) } pub fn read(&mut self, buf: &mut [u8]) -> Result { + if !self.is_readable { + return Err(Error::new(Errno::EINVAL, "File not readable")); + } + let mut file_guard = self.file.lock().unwrap(); let file = file_guard.borrow_mut(); @@ -110,11 +134,50 @@ impl SgxFileInner { Ok(read_len) } - pub fn writev<'a, 'b>(&mut self, bufs: &'a [&'b [u8]]) -> Result { + pub fn seek(&mut self, pos: SeekFrom) -> Result { let mut file_guard = self.file.lock().unwrap(); let file = file_guard.borrow_mut(); - let seek_pos = SeekFrom::Start(self.pos as u64); + 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 Err(Error::new(Errno::EINVAL, + "Invalid seek position")); + } + SeekFrom::Start((self.pos - backward_offset) as u64) + } + } + }; + + self.pos = file.seek(pos).map_err( + |e| Error::new(Errno::EINVAL, "Failed to seek to a position"))? as usize; + Ok(self.pos as off_t) + } + + pub fn writev<'a, 'b>(&mut self, bufs: &'a [&'b [u8]]) -> Result { + if !self.is_writable { + return Err(Error::new(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| Error::new(Errno::EINVAL, "Failed to seek to a position"))?; @@ -143,6 +206,10 @@ impl SgxFileInner { } fn readv<'a, 'b>(&mut self, bufs: &'a mut [&'b mut [u8]]) -> Result { + if !self.is_readable { + return Err(Error::new(Errno::EINVAL, "File not readable")); + } + let mut file_guard = self.file.lock().unwrap(); let file = file_guard.borrow_mut(); @@ -233,7 +300,11 @@ impl File for StdoutFile { } fn readv<'a, 'b>(&self, bufs: &'a mut [&'b mut [u8]]) -> Result { - Err(Error::new(Errno::EBADF, "Stdout does not support reading")) + Err(Error::new(Errno::EBADF, "Stdout does not support read")) + } + + fn seek(&self, seek_pos: SeekFrom) -> Result { + Err(Error::new(Errno::ESPIPE, "Stdout does not support seek")) } } @@ -268,7 +339,11 @@ impl File for StdinFile { } fn write(&self, buf: &[u8]) -> Result { - Err(Error::new(Errno::EBADF, "Stdin does not support reading")) + Err(Error::new(Errno::EBADF, "Stdin does not support write")) + } + + fn seek(&self, pos: SeekFrom) -> Result { + Err(Error::new(Errno::ESPIPE, "Stdin does not support seek")) } fn readv<'a, 'b>(&self, bufs: &'a mut [&'b mut [u8]]) -> Result { diff --git a/src/libos/src/fs.rs b/src/libos/src/fs.rs index 18c60b16..f22e8e06 100644 --- a/src/libos/src/fs.rs +++ b/src/libos/src/fs.rs @@ -12,6 +12,13 @@ pub const O_CREAT : u32 = 0x00000040; pub const O_TRUNC : u32 = 0x00000200; pub const O_APPEND : u32 = 0x00000400; +// TODO: use the type defined in Rust libc. +// +// However, off_t is defined as i64 in the current Rust SGX SDK, which is +// wrong (see issue https://github.com/baidu/rust-sgx-sdk/issues/46) +#[allow(non_camel_case_types)] +pub type off_t = i64; + pub fn do_open(path: &str, flags: u32, mode: u32) -> Result { let open_options = { let mut open_options = fs_impl::OpenOptions::new(); @@ -29,6 +36,7 @@ pub fn do_open(path: &str, flags: u32, mode: u32) -> Result { let mut sgx_file = { let key : sgx_key_128bit_t = [0 as uint8_t; 16]; + // TODO: what if two processes open the same underlying SGX file? let sgx_file = open_options.open_ex(path, &key) .map_err(|e| (Errno::ENOENT, "Failed to open the SGX-protected file") )?; Arc::new(SgxMutex::new(sgx_file)) @@ -38,7 +46,7 @@ pub fn do_open(path: &str, flags: u32, mode: u32) -> Result { let is_writable = (flags & O_WRONLY != 0) || (flags & O_RDWR != 0); let is_append = (flags & O_APPEND != 0); let file_ref : Arc> = Arc::new(Box::new( - SgxFile::new(sgx_file, is_readable, is_writable, is_append))); + SgxFile::new(sgx_file, is_readable, is_writable, is_append)?)); let current_ref = process::get_current(); let mut current_process = current_ref.lock().unwrap(); @@ -79,6 +87,14 @@ pub fn do_readv<'a, 'b>(fd: FileDesc, bufs: &'a mut [&'b mut [u8]]) -> Result(fd: FileDesc, offset: SeekFrom) -> Result { + let current_ref = process::get_current(); + let current_process = current_ref.lock().unwrap(); + let file_ref = current_process.file_table.get(fd) + .ok_or_else(|| Error::new(Errno::EBADF, "Invalid file descriptor [do_lseek]"))?; + file_ref.seek(offset) +} + pub fn do_close(fd: FileDesc) -> Result<(), Error> { let current_ref = process::get_current(); let mut current_process = current_ref.lock().unwrap(); diff --git a/src/libos/src/prelude.rs b/src/libos/src/prelude.rs index 108a5729..89892c29 100644 --- a/src/libos/src/prelude.rs +++ b/src/libos/src/prelude.rs @@ -15,6 +15,9 @@ pub use std::vec::Vec; pub use std::string::{String}; pub use std::collections::{HashMap, VecDeque}; pub use std::fmt::{Debug, Display}; +pub use std::io::{Read, Write, Seek, SeekFrom}; pub use errno::Error as Error; pub use errno::Errno; + +pub use fs::off_t; diff --git a/src/libos/src/syscall.h b/src/libos/src/syscall.h index 7a240a1e..2f7d8458 100644 --- a/src/libos/src/syscall.h +++ b/src/libos/src/syscall.h @@ -16,6 +16,7 @@ extern ssize_t occlum_read(int fd, void* buf, size_t size); extern ssize_t occlum_write(int fd, const void* buf, size_t size); extern ssize_t occlum_readv(int fd, struct iovec* iov, int count); extern ssize_t occlum_writev(int fd, const struct iovec* iov, int count); +extern off_t occlum_lseek(int fd, off_t offset, int whence); extern int occlum_spawn(int* child_pid, const char* path, const char** argv, const char** envp); diff --git a/src/libos/src/syscall.rs b/src/libos/src/syscall.rs index ea94cead..63416c8a 100644 --- a/src/libos/src/syscall.rs +++ b/src/libos/src/syscall.rs @@ -11,7 +11,6 @@ pub struct iovec_t { len: size_t, } - fn check_ptr_from_user(user_ptr: *const T) -> Result<(), Error> { Ok(()) } @@ -133,6 +132,33 @@ fn do_readv(fd: c_int, iov: *mut iovec_t, count: c_int) } +pub fn do_lseek(fd: c_int, offset: off_t, whence: c_int) -> Result +{ + + let fd = fd as file_table::FileDesc; + + let seek_from = match whence { + 0 => { // SEEK_SET + if offset < 0 { + return Err(Error::new(Errno::EINVAL, "Invalid offset")); + } + SeekFrom::Start(offset as u64) + } + 1 => { // SEEK_CUR + SeekFrom::Current(offset) + } + 2 => { // SEEK_END + SeekFrom::End(offset) + } + _ => { + return Err(Error::new(Errno::EINVAL, "Invalid whence")); + } + }; + + fs::do_lseek(fd, seek_from) +} + + #[no_mangle] pub extern "C" fn occlum_open(path_buf: * const c_char, flags: c_int, mode: c_int) -> c_int { let path = unsafe { @@ -208,6 +234,17 @@ pub extern "C" fn occlum_writev(fd: c_int, iov: * const iovec_t, count: c_int) - } } +#[no_mangle] +pub extern "C" fn occlum_lseek(fd: c_int, offset: off_t, whence: c_int) -> off_t { + match do_lseek(fd, offset, whence) { + Ok(ret) => { + ret + }, + Err(e) => { + -1 as off_t // this special value indicates error + } + } +} #[no_mangle] pub extern "C" fn occlum_getpid() -> c_uint diff --git a/src/libos/src/syscall_entry.c b/src/libos/src/syscall_entry.c index 3efe20ef..0b2301d9 100644 --- a/src/libos/src/syscall_entry.c +++ b/src/libos/src/syscall_entry.c @@ -54,6 +54,13 @@ long dispatch_syscall(int num, long arg0, long arg1, long arg2, long arg3, long ret = occlum_readv(fd, iov, count); break; } + case SYS_lseek: { + DECL_SYSCALL_ARG(int, fd, arg0); + DECL_SYSCALL_ARG(off_t, offset, arg1); + DECL_SYSCALL_ARG(int, whence, arg2); + ret = occlum_lseek(fd, offset, whence); + break; + } case SYS_spawn: { DECL_SYSCALL_ARG(int*, child_pid, arg0); DECL_SYSCALL_ARG(const char*, path, arg1);