diff --git a/README.md b/README.md index 0e8d3662..b013caa7 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,10 @@ Occlum can be configured easily via a configuration file named `Occlum.json`, wh "options": { "temporary": true } + }, + { + "target": "/dev", + "type": "devfs" } ] } diff --git a/etc/template/Occlum.json b/etc/template/Occlum.json index 919d17d2..66074f38 100644 --- a/etc/template/Occlum.json +++ b/etc/template/Occlum.json @@ -58,6 +58,10 @@ "options": { "temporary": true } + }, + { + "target": "/dev", + "type": "devfs" } ] } diff --git a/src/libos/Cargo.lock b/src/libos/Cargo.lock index 77d2443a..e32d885b 100644 --- a/src/libos/Cargo.lock +++ b/src/libos/Cargo.lock @@ -13,6 +13,7 @@ dependencies = [ "log", "memoffset", "rcore-fs", + "rcore-fs-devfs", "rcore-fs-mountfs", "rcore-fs-ramfs", "rcore-fs-sefs", @@ -395,6 +396,15 @@ dependencies = [ "spin", ] +[[package]] +name = "rcore-fs-devfs" +version = "0.1.0" +dependencies = [ + "log", + "rcore-fs", + "spin", +] + [[package]] name = "rcore-fs-mountfs" version = "0.1.0" diff --git a/src/libos/Cargo.toml b/src/libos/Cargo.toml index bfaedb3e..c210e106 100644 --- a/src/libos/Cargo.toml +++ b/src/libos/Cargo.toml @@ -21,6 +21,7 @@ rcore-fs-sefs = { path = "../../deps/sefs/rcore-fs-sefs" } rcore-fs-ramfs = { path = "../../deps/sefs/rcore-fs-ramfs" } rcore-fs-mountfs = { path = "../../deps/sefs/rcore-fs-mountfs" } rcore-fs-unionfs = { path = "../../deps/sefs/rcore-fs-unionfs" } +rcore-fs-devfs = { path = "../../deps/sefs/rcore-fs-devfs" } serde = { path = "../../deps/serde-sgx/serde", features = ["derive"] } serde_json = { path = "../../deps/serde-json-sgx" } memoffset = "0.6.1" diff --git a/src/libos/src/config.rs b/src/libos/src/config.rs index 5b32f1ee..182cb978 100644 --- a/src/libos/src/config.rs +++ b/src/libos/src/config.rs @@ -125,6 +125,7 @@ pub enum ConfigMountFsType { TYPE_HOSTFS, TYPE_RAMFS, TYPE_UNIONFS, + TYPE_DEVFS, } #[derive(Debug)] @@ -199,13 +200,14 @@ impl ConfigEnv { impl ConfigMount { fn from_input(input: &InputConfigMount) -> Result { - const ALL_FS_TYPES: [&str; 4] = ["sefs", "hostfs", "ramfs", "unionfs"]; + const ALL_FS_TYPES: [&str; 5] = ["sefs", "hostfs", "ramfs", "unionfs", "devfs"]; let type_ = match input.type_.as_str() { "sefs" => ConfigMountFsType::TYPE_SEFS, "hostfs" => ConfigMountFsType::TYPE_HOSTFS, "ramfs" => ConfigMountFsType::TYPE_RAMFS, "unionfs" => ConfigMountFsType::TYPE_UNIONFS, + "devfs" => ConfigMountFsType::TYPE_DEVFS, _ => { return_errno!(EINVAL, "Unsupported file system type"); } diff --git a/src/libos/src/fs/dev_fs/dev_null.rs b/src/libos/src/fs/dev_fs/dev_null.rs index 1de217fd..aa81c72e 100644 --- a/src/libos/src/fs/dev_fs/dev_null.rs +++ b/src/libos/src/fs/dev_fs/dev_null.rs @@ -3,24 +3,43 @@ use super::*; #[derive(Debug)] pub struct DevNull; -impl File for DevNull { - fn write(&self, _buf: &[u8]) -> Result { - Ok(_buf.len()) +impl INode for DevNull { + fn read_at(&self, offset: usize, buf: &mut [u8]) -> vfs::Result { + Ok(0) } - fn write_at(&self, _offset: usize, _buf: &[u8]) -> Result { - Ok(_buf.len()) + fn write_at(&self, offset: usize, buf: &[u8]) -> vfs::Result { + Ok(buf.len()) } - fn writev(&self, bufs: &[&[u8]]) -> Result { - Ok(bufs.iter().map(|buf| buf.len()).sum()) + fn poll(&self) -> vfs::Result { + Ok(vfs::PollStatus { + read: true, + write: true, + error: false, + }) } - fn poll_new(&self) -> IoEvents { - IoEvents::IN + fn metadata(&self) -> vfs::Result { + Ok(Metadata { + dev: 1, + 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_: vfs::FileType::CharDevice, + mode: 0o666, + nlinks: 1, + uid: 0, + gid: 0, + rdev: 0, + }) } - fn as_any(&self) -> &dyn Any { + fn as_any_ref(&self) -> &dyn Any { self } } diff --git a/src/libos/src/fs/dev_fs/dev_random.rs b/src/libos/src/fs/dev_fs/dev_random.rs index 4ccb1870..79bc3b6a 100644 --- a/src/libos/src/fs/dev_fs/dev_random.rs +++ b/src/libos/src/fs/dev_fs/dev_random.rs @@ -1,42 +1,30 @@ use super::*; -use crate::net::PollEventFlags; use crate::util::random; #[derive(Debug)] pub struct DevRandom; -impl File for DevRandom { - fn read(&self, _buf: &mut [u8]) -> Result { - random::get_random(_buf)?; - Ok(_buf.len()) +impl INode for DevRandom { + fn read_at(&self, offset: usize, buf: &mut [u8]) -> vfs::Result { + random::get_random(buf).map_err(|_| FsError::Again)?; + Ok(buf.len()) } - fn read_at(&self, _offset: usize, _buf: &mut [u8]) -> Result { - self.read(_buf) + fn write_at(&self, offset: usize, buf: &[u8]) -> vfs::Result { + Err(FsError::PermError) } - fn readv(&self, bufs: &mut [&mut [u8]]) -> Result { - let mut total_nbytes = 0; - for buf in bufs { - match self.read(buf) { - Ok(this_nbytes) => { - total_nbytes += this_nbytes; - } - Err(e) => { - if total_nbytes > 0 { - break; - } else { - return Err(e); - } - } - } - } - Ok(total_nbytes) + fn poll(&self) -> vfs::Result { + Ok(vfs::PollStatus { + read: true, + write: false, + error: false, + }) } - fn metadata(&self) -> Result { + fn metadata(&self) -> vfs::Result { Ok(Metadata { - dev: 0, + dev: 1, inode: 0, size: 0, blk_size: 0, @@ -44,36 +32,16 @@ impl File for DevRandom { atime: Timespec { sec: 0, nsec: 0 }, mtime: Timespec { sec: 0, nsec: 0 }, ctime: Timespec { sec: 0, nsec: 0 }, - type_: FileType::CharDevice, - mode: (FileMode::S_IRUSR | FileMode::S_IRGRP | FileMode::S_IROTH).bits(), - nlinks: 0, + type_: vfs::FileType::CharDevice, + mode: 0o444, + nlinks: 1, uid: 0, gid: 0, rdev: 0, }) } - fn poll(&self) -> Result<(PollEventFlags)> { - Ok(PollEventFlags::POLLIN) - } - - fn poll_new(&self) -> IoEvents { - IoEvents::IN - } - - fn as_any(&self) -> &dyn Any { + fn as_any_ref(&self) -> &dyn Any { self } } - -pub trait AsDevRandom { - fn as_dev_random(&self) -> Result<&DevRandom>; -} - -impl AsDevRandom for FileRef { - fn as_dev_random(&self) -> Result<&DevRandom> { - self.as_any() - .downcast_ref::() - .ok_or_else(|| errno!(EBADF, "not random device")) - } -} diff --git a/src/libos/src/fs/dev_fs/dev_sgx/mod.rs b/src/libos/src/fs/dev_fs/dev_sgx/mod.rs index 3ead3cdb..ca9edca2 100644 --- a/src/libos/src/fs/dev_fs/dev_sgx/mod.rs +++ b/src/libos/src/fs/dev_fs/dev_sgx/mod.rs @@ -15,7 +15,54 @@ extern "C" { #[derive(Debug)] pub struct DevSgx; -impl File for DevSgx { +impl INode for DevSgx { + fn read_at(&self, offset: usize, buf: &mut [u8]) -> vfs::Result { + Err(FsError::PermError) + } + + fn write_at(&self, offset: usize, buf: &[u8]) -> vfs::Result { + Err(FsError::PermError) + } + + fn poll(&self) -> vfs::Result { + Err(FsError::PermError) + } + + fn metadata(&self) -> vfs::Result { + Ok(Metadata { + dev: 1, + 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_: vfs::FileType::CharDevice, + mode: 0o666, + nlinks: 1, + uid: 0, + gid: 0, + rdev: 0, + }) + } + + fn io_control(&self, _cmd: u32, _data: usize) -> vfs::Result<()> { + let mut ioctl_cmd = + unsafe { IoctlCmd::new(_cmd, _data as *mut u8).map_err(|_| FsError::InvalidParam)? }; + self.ioctl(&mut ioctl_cmd).map_err(|e| { + error!("{}", e.backtrace()); + FsError::IOCTLError + })?; + Ok(()) + } + + fn as_any_ref(&self) -> &dyn Any { + self + } +} + +impl DevSgx { fn ioctl(&self, cmd: &mut IoctlCmd) -> Result { let nonbuiltin_cmd = match cmd { IoctlCmd::NonBuiltin(nonbuiltin_cmd) => nonbuiltin_cmd, @@ -180,14 +227,6 @@ impl File for DevSgx { } Ok(0) } - - fn poll_new(&self) -> IoEvents { - IoEvents::IN - } - - fn as_any(&self) -> &dyn Any { - self - } } lazy_static! { diff --git a/src/libos/src/fs/dev_fs/dev_zero.rs b/src/libos/src/fs/dev_fs/dev_zero.rs index 19d0459d..5f77c8f8 100644 --- a/src/libos/src/fs/dev_fs/dev_zero.rs +++ b/src/libos/src/fs/dev_fs/dev_zero.rs @@ -3,31 +3,46 @@ use super::*; #[derive(Debug)] pub struct DevZero; -impl File for DevZero { - fn read(&self, _buf: &mut [u8]) -> Result { - for b in _buf.iter_mut() { +impl INode for DevZero { + fn read_at(&self, offset: usize, buf: &mut [u8]) -> vfs::Result { + for b in buf.iter_mut() { *b = 0; } - Ok(_buf.len()) + Ok(buf.len()) } - fn read_at(&self, _offset: usize, _buf: &mut [u8]) -> Result { - self.read(_buf) + fn write_at(&self, offset: usize, buf: &[u8]) -> vfs::Result { + Ok(buf.len()) } - fn readv(&self, bufs: &mut [&mut [u8]]) -> Result { - let mut total_nbytes = 0; - for buf in bufs { - total_nbytes += self.read(buf)?; - } - Ok(total_nbytes) + fn poll(&self) -> vfs::Result { + Ok(vfs::PollStatus { + read: true, + write: true, + error: false, + }) } - fn poll_new(&self) -> IoEvents { - IoEvents::IN + fn metadata(&self) -> vfs::Result { + Ok(Metadata { + dev: 1, + 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_: vfs::FileType::CharDevice, + mode: 0o666, + nlinks: 1, + uid: 0, + gid: 0, + rdev: 0, + }) } - fn as_any(&self) -> &dyn Any { + fn as_any_ref(&self) -> &dyn Any { self } } diff --git a/src/libos/src/fs/dev_fs/mod.rs b/src/libos/src/fs/dev_fs/mod.rs index 8ef85bb7..a25e07e5 100644 --- a/src/libos/src/fs/dev_fs/mod.rs +++ b/src/libos/src/fs/dev_fs/mod.rs @@ -1,11 +1,30 @@ use super::*; +use rcore_fs::vfs; +use rcore_fs_devfs::DevFS; -pub use self::dev_null::DevNull; -pub use self::dev_random::{AsDevRandom, DevRandom}; -pub use self::dev_sgx::DevSgx; -pub use self::dev_zero::DevZero; +use self::dev_null::DevNull; +use self::dev_random::DevRandom; +use self::dev_sgx::DevSgx; +use self::dev_zero::DevZero; mod dev_null; mod dev_random; mod dev_sgx; mod dev_zero; + +/// API to initialize the DevFS +pub fn init_devfs() -> Result> { + let devfs = DevFS::new(); + let dev_null = Arc::new(DevNull) as _; + devfs.add("null", dev_null)?; + let dev_zero = Arc::new(DevZero) as _; + devfs.add("zero", dev_zero)?; + let dev_random = Arc::new(DevRandom) as _; + devfs.add("random", Arc::clone(&dev_random))?; + devfs.add("urandom", Arc::clone(&dev_random))?; + devfs.add("arandom", Arc::clone(&dev_random))?; + let dev_sgx = Arc::new(DevSgx) as _; + devfs.add("sgx", dev_sgx)?; + // TODO: Add stdio(stdin, stdout, stderr) into DevFS + Ok(devfs) +} diff --git a/src/libos/src/fs/events.rs b/src/libos/src/fs/events.rs index 37417c6e..575306d2 100644 --- a/src/libos/src/fs/events.rs +++ b/src/libos/src/fs/events.rs @@ -25,6 +25,20 @@ impl IoEvents { Self::from_bits_truncate(raw) } + pub fn from_poll_status(poll_status: &crate::rcore_fs::vfs::PollStatus) -> Self { + if poll_status.error { + return Self::ERR; + } + let mut events = Self::empty(); + if poll_status.read { + events |= Self::IN + } + if poll_status.write { + events |= Self::OUT + } + events + } + fn contains_unrecognizable_bits(raw: u32) -> bool { // Help to detect four valid but mostly useless flags that we do not // handle, yet: POLLRDNORM, POLLRDBAND, POLLWRNORM, annd POLLWRBAND. diff --git a/src/libos/src/fs/file_ops/mod.rs b/src/libos/src/fs/file_ops/mod.rs index 8f28820e..6b6ad8ac 100644 --- a/src/libos/src/fs/file_ops/mod.rs +++ b/src/libos/src/fs/file_ops/mod.rs @@ -1,4 +1,3 @@ -use super::dev_fs::{DevNull, DevRandom, DevSgx, DevZero}; use super::*; use process::Process; diff --git a/src/libos/src/fs/fs_view.rs b/src/libos/src/fs/fs_view.rs index f34f385e..f9c0d09b 100644 --- a/src/libos/src/fs/fs_view.rs +++ b/src/libos/src/fs/fs_view.rs @@ -1,4 +1,3 @@ -use super::dev_fs::{DevNull, DevRandom, DevSgx, DevZero}; /// Present a per-process view of FS. use super::*; @@ -40,18 +39,6 @@ impl FsView { /// Open a file on the process. But DO NOT add it to file table. pub fn open_file(&self, path: &str, flags: u32, mode: u32) -> Result> { - if path == "/dev/null" { - return Ok(Arc::new(DevNull)); - } - if path == "/dev/zero" { - return Ok(Arc::new(DevZero)); - } - if path == "/dev/random" || path == "/dev/urandom" || path == "/dev/arandom" { - return Ok(Arc::new(DevRandom)); - } - if path == "/dev/sgx" { - return Ok(Arc::new(DevSgx)); - } let creation_flags = CreationFlags::from_bits_truncate(flags); let inode = if creation_flags.no_follow_symlink() { match self.lookup_inode_no_follow(path) { diff --git a/src/libos/src/fs/inode_file.rs b/src/libos/src/fs/inode_file.rs index 76182ed5..5c064247 100644 --- a/src/libos/src/fs/inode_file.rs +++ b/src/libos/src/fs/inode_file.rs @@ -1,4 +1,5 @@ use super::*; +use crate::net::PollEventFlags; use rcore_fs_sefs::dev::SefsMac; pub struct INodeFile { @@ -196,6 +197,20 @@ impl File for INodeFile { Ok(()) } + fn ioctl(&self, cmd: &mut IoctlCmd) -> Result { + let cmd_num = cmd.cmd_num(); + let cmd_argp = cmd.arg_ptr() as usize; + self.inode.io_control(cmd_num, cmd_argp)?; + Ok(0) + } + + fn poll_new(&self) -> IoEvents { + match self.inode.poll() { + Ok(poll_status) => IoEvents::from_poll_status(&poll_status), + Err(_) => IoEvents::empty(), + } + } + fn as_any(&self) -> &dyn Any { self } diff --git a/src/libos/src/fs/mod.rs b/src/libos/src/fs/mod.rs index 871e2b9a..d37837ee 100644 --- a/src/libos/src/fs/mod.rs +++ b/src/libos/src/fs/mod.rs @@ -10,7 +10,6 @@ use std::mem::MaybeUninit; use std::path::Path; use untrusted::{SliceAsMutPtrAndLen, SliceAsPtrAndLen}; -pub use self::dev_fs::AsDevRandom; pub use self::event_file::{AsEvent, EventCreationFlags, EventFile}; pub use self::events::{AtomicIoEvents, IoEvents, IoNotifier}; pub use self::file::{File, FileRef}; diff --git a/src/libos/src/fs/rootfs.rs b/src/libos/src/fs/rootfs.rs index 1672dae6..8a953dbf 100644 --- a/src/libos/src/fs/rootfs.rs +++ b/src/libos/src/fs/rootfs.rs @@ -1,3 +1,4 @@ +use super::dev_fs; use super::hostfs::HostFS; use super::sefs::{SgxStorage, SgxUuidProvider}; use super::*; @@ -144,6 +145,10 @@ fn mount_nonroot_fs_according_to(mount_config: &Vec, root: &MNode) let ramfs = RamFS::new(); mount_fs_at(ramfs, root, &mc.target)?; } + TYPE_DEVFS => { + let devfs = dev_fs::init_devfs()?; + mount_fs_at(devfs, root, &mc.target)?; + } TYPE_UNIONFS => { return_errno!(EINVAL, "Cannot mount UnionFS at non-root path"); } diff --git a/src/libos/src/lib.rs b/src/libos/src/lib.rs index 5a2b6a03..5c42189b 100644 --- a/src/libos/src/lib.rs +++ b/src/libos/src/lib.rs @@ -38,6 +38,7 @@ extern crate lazy_static; #[macro_use] extern crate log; extern crate rcore_fs; +extern crate rcore_fs_devfs; extern crate rcore_fs_mountfs; extern crate rcore_fs_ramfs; extern crate rcore_fs_sefs; diff --git a/src/libos/src/net/io_multiplexing/mod.rs b/src/libos/src/net/io_multiplexing/mod.rs index b04b1d1a..86e54dc0 100644 --- a/src/libos/src/net/io_multiplexing/mod.rs +++ b/src/libos/src/net/io_multiplexing/mod.rs @@ -15,7 +15,7 @@ pub use self::poll::{do_poll, PollEvent, PollEventFlags}; pub use self::poll_new::{do_poll_new, PollFd}; pub use self::select::{do_select, FdSetExt}; -use fs::{AsDevRandom, AsEvent, CreationFlags, File, FileDesc, FileRef, HostFd, PipeType}; +use fs::{AsEvent, AsINodeFile, CreationFlags, File, FileDesc, FileRef, HostFd, PipeType}; use std::any::Any; use std::convert::TryFrom; use std::fmt; diff --git a/src/libos/src/net/io_multiplexing/poll.rs b/src/libos/src/net/io_multiplexing/poll.rs index 61777bd4..60f6b184 100644 --- a/src/libos/src/net/io_multiplexing/poll.rs +++ b/src/libos/src/net/io_multiplexing/poll.rs @@ -93,7 +93,6 @@ pub fn do_poll(pollfds: &mut [PollEvent], timeout: *mut timeval_t) -> Result serde_json::Value { "options": { "temporary": true } + }, + { + "target": "/dev", + "type": "devfs" } ] }); diff --git a/tools/occlum b/tools/occlum index f4cf5f07..3cf03222 100755 --- a/tools/occlum +++ b/tools/occlum @@ -148,6 +148,7 @@ cmd_init() { mkdir -p image/root mkdir -p image/host mkdir -p image/tmp + mkdir -p image/dev local occlum_glibc_lib=/opt/occlum/glibc/lib local cpu_lib=/sys/devices/system/cpu if [ -d "$occlum_glibc_lib" ]; then