Add DevFS for device files

This commit is contained in:
LI Qing 2021-01-07 17:21:07 +08:00 committed by Tate, Hongliang Tian
parent 1514be14fd
commit d6cd89f03b
22 changed files with 215 additions and 106 deletions

@ -162,6 +162,10 @@ Occlum can be configured easily via a configuration file named `Occlum.json`, wh
"options": { "options": {
"temporary": true "temporary": true
} }
},
{
"target": "/dev",
"type": "devfs"
} }
] ]
} }

@ -58,6 +58,10 @@
"options": { "options": {
"temporary": true "temporary": true
} }
},
{
"target": "/dev",
"type": "devfs"
} }
] ]
} }

10
src/libos/Cargo.lock generated

@ -13,6 +13,7 @@ dependencies = [
"log", "log",
"memoffset", "memoffset",
"rcore-fs", "rcore-fs",
"rcore-fs-devfs",
"rcore-fs-mountfs", "rcore-fs-mountfs",
"rcore-fs-ramfs", "rcore-fs-ramfs",
"rcore-fs-sefs", "rcore-fs-sefs",
@ -395,6 +396,15 @@ dependencies = [
"spin", "spin",
] ]
[[package]]
name = "rcore-fs-devfs"
version = "0.1.0"
dependencies = [
"log",
"rcore-fs",
"spin",
]
[[package]] [[package]]
name = "rcore-fs-mountfs" name = "rcore-fs-mountfs"
version = "0.1.0" version = "0.1.0"

@ -21,6 +21,7 @@ rcore-fs-sefs = { path = "../../deps/sefs/rcore-fs-sefs" }
rcore-fs-ramfs = { path = "../../deps/sefs/rcore-fs-ramfs" } rcore-fs-ramfs = { path = "../../deps/sefs/rcore-fs-ramfs" }
rcore-fs-mountfs = { path = "../../deps/sefs/rcore-fs-mountfs" } rcore-fs-mountfs = { path = "../../deps/sefs/rcore-fs-mountfs" }
rcore-fs-unionfs = { path = "../../deps/sefs/rcore-fs-unionfs" } 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 = { path = "../../deps/serde-sgx/serde", features = ["derive"] }
serde_json = { path = "../../deps/serde-json-sgx" } serde_json = { path = "../../deps/serde-json-sgx" }
memoffset = "0.6.1" memoffset = "0.6.1"

@ -125,6 +125,7 @@ pub enum ConfigMountFsType {
TYPE_HOSTFS, TYPE_HOSTFS,
TYPE_RAMFS, TYPE_RAMFS,
TYPE_UNIONFS, TYPE_UNIONFS,
TYPE_DEVFS,
} }
#[derive(Debug)] #[derive(Debug)]
@ -199,13 +200,14 @@ impl ConfigEnv {
impl ConfigMount { impl ConfigMount {
fn from_input(input: &InputConfigMount) -> Result<ConfigMount> { fn from_input(input: &InputConfigMount) -> Result<ConfigMount> {
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() { let type_ = match input.type_.as_str() {
"sefs" => ConfigMountFsType::TYPE_SEFS, "sefs" => ConfigMountFsType::TYPE_SEFS,
"hostfs" => ConfigMountFsType::TYPE_HOSTFS, "hostfs" => ConfigMountFsType::TYPE_HOSTFS,
"ramfs" => ConfigMountFsType::TYPE_RAMFS, "ramfs" => ConfigMountFsType::TYPE_RAMFS,
"unionfs" => ConfigMountFsType::TYPE_UNIONFS, "unionfs" => ConfigMountFsType::TYPE_UNIONFS,
"devfs" => ConfigMountFsType::TYPE_DEVFS,
_ => { _ => {
return_errno!(EINVAL, "Unsupported file system type"); return_errno!(EINVAL, "Unsupported file system type");
} }

@ -3,24 +3,43 @@ use super::*;
#[derive(Debug)] #[derive(Debug)]
pub struct DevNull; pub struct DevNull;
impl File for DevNull { impl INode for DevNull {
fn write(&self, _buf: &[u8]) -> Result<usize> { fn read_at(&self, offset: usize, buf: &mut [u8]) -> vfs::Result<usize> {
Ok(_buf.len()) Ok(0)
} }
fn write_at(&self, _offset: usize, _buf: &[u8]) -> Result<usize> { fn write_at(&self, offset: usize, buf: &[u8]) -> vfs::Result<usize> {
Ok(_buf.len()) Ok(buf.len())
} }
fn writev(&self, bufs: &[&[u8]]) -> Result<usize> { fn poll(&self) -> vfs::Result<vfs::PollStatus> {
Ok(bufs.iter().map(|buf| buf.len()).sum()) Ok(vfs::PollStatus {
read: true,
write: true,
error: false,
})
} }
fn poll_new(&self) -> IoEvents { fn metadata(&self) -> vfs::Result<Metadata> {
IoEvents::IN 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 self
} }
} }

@ -1,42 +1,30 @@
use super::*; use super::*;
use crate::net::PollEventFlags;
use crate::util::random; use crate::util::random;
#[derive(Debug)] #[derive(Debug)]
pub struct DevRandom; pub struct DevRandom;
impl File for DevRandom { impl INode for DevRandom {
fn read(&self, _buf: &mut [u8]) -> Result<usize> { fn read_at(&self, offset: usize, buf: &mut [u8]) -> vfs::Result<usize> {
random::get_random(_buf)?; random::get_random(buf).map_err(|_| FsError::Again)?;
Ok(_buf.len()) Ok(buf.len())
} }
fn read_at(&self, _offset: usize, _buf: &mut [u8]) -> Result<usize> { fn write_at(&self, offset: usize, buf: &[u8]) -> vfs::Result<usize> {
self.read(_buf) Err(FsError::PermError)
} }
fn readv(&self, bufs: &mut [&mut [u8]]) -> Result<usize> { fn poll(&self) -> vfs::Result<vfs::PollStatus> {
let mut total_nbytes = 0; Ok(vfs::PollStatus {
for buf in bufs { read: true,
match self.read(buf) { write: false,
Ok(this_nbytes) => { error: false,
total_nbytes += this_nbytes; })
}
Err(e) => {
if total_nbytes > 0 {
break;
} else {
return Err(e);
}
}
}
}
Ok(total_nbytes)
} }
fn metadata(&self) -> Result<Metadata> { fn metadata(&self) -> vfs::Result<Metadata> {
Ok(Metadata { Ok(Metadata {
dev: 0, dev: 1,
inode: 0, inode: 0,
size: 0, size: 0,
blk_size: 0, blk_size: 0,
@ -44,36 +32,16 @@ impl File for DevRandom {
atime: Timespec { sec: 0, nsec: 0 }, atime: Timespec { sec: 0, nsec: 0 },
mtime: Timespec { sec: 0, nsec: 0 }, mtime: Timespec { sec: 0, nsec: 0 },
ctime: Timespec { sec: 0, nsec: 0 }, ctime: Timespec { sec: 0, nsec: 0 },
type_: FileType::CharDevice, type_: vfs::FileType::CharDevice,
mode: (FileMode::S_IRUSR | FileMode::S_IRGRP | FileMode::S_IROTH).bits(), mode: 0o444,
nlinks: 0, nlinks: 1,
uid: 0, uid: 0,
gid: 0, gid: 0,
rdev: 0, rdev: 0,
}) })
} }
fn poll(&self) -> Result<(PollEventFlags)> { fn as_any_ref(&self) -> &dyn Any {
Ok(PollEventFlags::POLLIN)
}
fn poll_new(&self) -> IoEvents {
IoEvents::IN
}
fn as_any(&self) -> &dyn Any {
self 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::<DevRandom>()
.ok_or_else(|| errno!(EBADF, "not random device"))
}
}

@ -15,7 +15,54 @@ extern "C" {
#[derive(Debug)] #[derive(Debug)]
pub struct DevSgx; pub struct DevSgx;
impl File for DevSgx { impl INode for DevSgx {
fn read_at(&self, offset: usize, buf: &mut [u8]) -> vfs::Result<usize> {
Err(FsError::PermError)
}
fn write_at(&self, offset: usize, buf: &[u8]) -> vfs::Result<usize> {
Err(FsError::PermError)
}
fn poll(&self) -> vfs::Result<vfs::PollStatus> {
Err(FsError::PermError)
}
fn metadata(&self) -> vfs::Result<Metadata> {
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<i32> { fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<i32> {
let nonbuiltin_cmd = match cmd { let nonbuiltin_cmd = match cmd {
IoctlCmd::NonBuiltin(nonbuiltin_cmd) => nonbuiltin_cmd, IoctlCmd::NonBuiltin(nonbuiltin_cmd) => nonbuiltin_cmd,
@ -180,14 +227,6 @@ impl File for DevSgx {
} }
Ok(0) Ok(0)
} }
fn poll_new(&self) -> IoEvents {
IoEvents::IN
}
fn as_any(&self) -> &dyn Any {
self
}
} }
lazy_static! { lazy_static! {

@ -3,31 +3,46 @@ use super::*;
#[derive(Debug)] #[derive(Debug)]
pub struct DevZero; pub struct DevZero;
impl File for DevZero { impl INode for DevZero {
fn read(&self, _buf: &mut [u8]) -> Result<usize> { fn read_at(&self, offset: usize, buf: &mut [u8]) -> vfs::Result<usize> {
for b in _buf.iter_mut() { for b in buf.iter_mut() {
*b = 0; *b = 0;
} }
Ok(_buf.len()) Ok(buf.len())
} }
fn read_at(&self, _offset: usize, _buf: &mut [u8]) -> Result<usize> { fn write_at(&self, offset: usize, buf: &[u8]) -> vfs::Result<usize> {
self.read(_buf) Ok(buf.len())
} }
fn readv(&self, bufs: &mut [&mut [u8]]) -> Result<usize> { fn poll(&self) -> vfs::Result<vfs::PollStatus> {
let mut total_nbytes = 0; Ok(vfs::PollStatus {
for buf in bufs { read: true,
total_nbytes += self.read(buf)?; write: true,
} error: false,
Ok(total_nbytes) })
} }
fn poll_new(&self) -> IoEvents { fn metadata(&self) -> vfs::Result<Metadata> {
IoEvents::IN 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 self
} }
} }

@ -1,11 +1,30 @@
use super::*; use super::*;
use rcore_fs::vfs;
use rcore_fs_devfs::DevFS;
pub use self::dev_null::DevNull; use self::dev_null::DevNull;
pub use self::dev_random::{AsDevRandom, DevRandom}; use self::dev_random::DevRandom;
pub use self::dev_sgx::DevSgx; use self::dev_sgx::DevSgx;
pub use self::dev_zero::DevZero; use self::dev_zero::DevZero;
mod dev_null; mod dev_null;
mod dev_random; mod dev_random;
mod dev_sgx; mod dev_sgx;
mod dev_zero; mod dev_zero;
/// API to initialize the DevFS
pub fn init_devfs() -> Result<Arc<DevFS>> {
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)
}

@ -25,6 +25,20 @@ impl IoEvents {
Self::from_bits_truncate(raw) 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 { fn contains_unrecognizable_bits(raw: u32) -> bool {
// Help to detect four valid but mostly useless flags that we do not // Help to detect four valid but mostly useless flags that we do not
// handle, yet: POLLRDNORM, POLLRDBAND, POLLWRNORM, annd POLLWRBAND. // handle, yet: POLLRDNORM, POLLRDBAND, POLLWRNORM, annd POLLWRBAND.

@ -1,4 +1,3 @@
use super::dev_fs::{DevNull, DevRandom, DevSgx, DevZero};
use super::*; use super::*;
use process::Process; use process::Process;

@ -1,4 +1,3 @@
use super::dev_fs::{DevNull, DevRandom, DevSgx, DevZero};
/// Present a per-process view of FS. /// Present a per-process view of FS.
use super::*; use super::*;
@ -40,18 +39,6 @@ impl FsView {
/// Open a file on the process. But DO NOT add it to file table. /// 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<Arc<dyn File>> { pub fn open_file(&self, path: &str, flags: u32, mode: u32) -> Result<Arc<dyn File>> {
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 creation_flags = CreationFlags::from_bits_truncate(flags);
let inode = if creation_flags.no_follow_symlink() { let inode = if creation_flags.no_follow_symlink() {
match self.lookup_inode_no_follow(path) { match self.lookup_inode_no_follow(path) {

@ -1,4 +1,5 @@
use super::*; use super::*;
use crate::net::PollEventFlags;
use rcore_fs_sefs::dev::SefsMac; use rcore_fs_sefs::dev::SefsMac;
pub struct INodeFile { pub struct INodeFile {
@ -196,6 +197,20 @@ impl File for INodeFile {
Ok(()) Ok(())
} }
fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<i32> {
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 { fn as_any(&self) -> &dyn Any {
self self
} }

@ -10,7 +10,6 @@ use std::mem::MaybeUninit;
use std::path::Path; use std::path::Path;
use untrusted::{SliceAsMutPtrAndLen, SliceAsPtrAndLen}; use untrusted::{SliceAsMutPtrAndLen, SliceAsPtrAndLen};
pub use self::dev_fs::AsDevRandom;
pub use self::event_file::{AsEvent, EventCreationFlags, EventFile}; pub use self::event_file::{AsEvent, EventCreationFlags, EventFile};
pub use self::events::{AtomicIoEvents, IoEvents, IoNotifier}; pub use self::events::{AtomicIoEvents, IoEvents, IoNotifier};
pub use self::file::{File, FileRef}; pub use self::file::{File, FileRef};

@ -1,3 +1,4 @@
use super::dev_fs;
use super::hostfs::HostFS; use super::hostfs::HostFS;
use super::sefs::{SgxStorage, SgxUuidProvider}; use super::sefs::{SgxStorage, SgxUuidProvider};
use super::*; use super::*;
@ -144,6 +145,10 @@ fn mount_nonroot_fs_according_to(mount_config: &Vec<ConfigMount>, root: &MNode)
let ramfs = RamFS::new(); let ramfs = RamFS::new();
mount_fs_at(ramfs, root, &mc.target)?; mount_fs_at(ramfs, root, &mc.target)?;
} }
TYPE_DEVFS => {
let devfs = dev_fs::init_devfs()?;
mount_fs_at(devfs, root, &mc.target)?;
}
TYPE_UNIONFS => { TYPE_UNIONFS => {
return_errno!(EINVAL, "Cannot mount UnionFS at non-root path"); return_errno!(EINVAL, "Cannot mount UnionFS at non-root path");
} }

@ -38,6 +38,7 @@ extern crate lazy_static;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
extern crate rcore_fs; extern crate rcore_fs;
extern crate rcore_fs_devfs;
extern crate rcore_fs_mountfs; extern crate rcore_fs_mountfs;
extern crate rcore_fs_ramfs; extern crate rcore_fs_ramfs;
extern crate rcore_fs_sefs; extern crate rcore_fs_sefs;

@ -15,7 +15,7 @@ pub use self::poll::{do_poll, PollEvent, PollEventFlags};
pub use self::poll_new::{do_poll_new, PollFd}; pub use self::poll_new::{do_poll_new, PollFd};
pub use self::select::{do_select, FdSetExt}; 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::any::Any;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::fmt; use std::fmt;

@ -93,7 +93,6 @@ pub fn do_poll(pollfds: &mut [PollEvent], timeout: *mut timeval_t) -> Result<usi
if file_ref.as_unix_socket().is_ok() if file_ref.as_unix_socket().is_ok()
|| file_ref.as_pipe_reader().is_ok() || file_ref.as_pipe_reader().is_ok()
|| file_ref.as_pipe_writer().is_ok() || file_ref.as_pipe_writer().is_ok()
|| file_ref.as_dev_random().is_ok()
{ {
let events = file_ref.poll()?; let events = file_ref.poll()?;
debug!("polled events are {:?}", events); debug!("polled events are {:?}", events);

@ -61,6 +61,10 @@
"options": { "options": {
"temporary": true "temporary": true
} }
},
{
"target": "/dev",
"type": "devfs"
} }
] ]
} }

@ -258,6 +258,10 @@ fn gen_mount_config(occlum_conf_root_fs_mac: String) -> serde_json::Value {
"options": { "options": {
"temporary": true "temporary": true
} }
},
{
"target": "/dev",
"type": "devfs"
} }
] ]
}); });

@ -148,6 +148,7 @@ cmd_init() {
mkdir -p image/root mkdir -p image/root
mkdir -p image/host mkdir -p image/host
mkdir -p image/tmp mkdir -p image/tmp
mkdir -p image/dev
local occlum_glibc_lib=/opt/occlum/glibc/lib local occlum_glibc_lib=/opt/occlum/glibc/lib
local cpu_lib=/sys/devices/system/cpu local cpu_lib=/sys/devices/system/cpu
if [ -d "$occlum_glibc_lib" ]; then if [ -d "$occlum_glibc_lib" ]; then