diff --git a/README.md b/README.md index b013caa7..fa19737d 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,10 @@ Occlum can be configured easily via a configuration file named `Occlum.json`, wh "type": "hostfs", "source": "." }, + { + "target": "/proc", + "type": "procfs" + }, { "target": "/tmp", "type": "sefs", diff --git a/demos/openvino/run_benchmark_on_occlum.sh b/demos/openvino/run_benchmark_on_occlum.sh index 15ad8ecd..9488dd54 100755 --- a/demos/openvino/run_benchmark_on_occlum.sh +++ b/demos/openvino/run_benchmark_on_occlum.sh @@ -28,8 +28,6 @@ cp $occlum_lib/libopencv_videoio.so.4.1 image/lib cp $occlum_lib/libz.so.1 image/lib [ -e $occlum_lib/libtbb.so ] && cp $occlum_lib/libtbb.so image/lib [ -e $occlum_lib/libtbbmalloc.so ] && cp $occlum_lib/libtbbmalloc.so image/lib -mkdir image/proc -cp /proc/cpuinfo image/proc mkdir image/model cp -r ../model/* image/model occlum build diff --git a/etc/template/Occlum.json b/etc/template/Occlum.json index 66074f38..8407e23d 100644 --- a/etc/template/Occlum.json +++ b/etc/template/Occlum.json @@ -52,6 +52,10 @@ "type": "hostfs", "source": "." }, + { + "target": "/proc", + "type": "procfs" + }, { "target": "/tmp", "type": "sefs", diff --git a/src/libos/src/config.rs b/src/libos/src/config.rs index 182cb978..82b3878d 100644 --- a/src/libos/src/config.rs +++ b/src/libos/src/config.rs @@ -126,6 +126,7 @@ pub enum ConfigMountFsType { TYPE_RAMFS, TYPE_UNIONFS, TYPE_DEVFS, + TYPE_PROCFS, } #[derive(Debug)] @@ -200,7 +201,7 @@ impl ConfigEnv { impl ConfigMount { fn from_input(input: &InputConfigMount) -> Result { - const ALL_FS_TYPES: [&str; 5] = ["sefs", "hostfs", "ramfs", "unionfs", "devfs"]; + const ALL_FS_TYPES: [&str; 6] = ["sefs", "hostfs", "ramfs", "unionfs", "devfs", "procfs"]; let type_ = match input.type_.as_str() { "sefs" => ConfigMountFsType::TYPE_SEFS, @@ -208,6 +209,7 @@ impl ConfigMount { "ramfs" => ConfigMountFsType::TYPE_RAMFS, "unionfs" => ConfigMountFsType::TYPE_UNIONFS, "devfs" => ConfigMountFsType::TYPE_DEVFS, + "procfs" => ConfigMountFsType::TYPE_PROCFS, _ => { return_errno!(EINVAL, "Unsupported file system type"); } diff --git a/src/libos/src/fs/file_ops/symlink.rs b/src/libos/src/fs/file_ops/symlink.rs index 7dd59c62..07b34571 100644 --- a/src/libos/src/fs/file_ops/symlink.rs +++ b/src/libos/src/fs/file_ops/symlink.rs @@ -5,35 +5,19 @@ pub fn do_readlinkat(fs_path: &FsPath, buf: &mut [u8]) -> Result { let path = fs_path.to_abs_path()?; let file_path = { - if path.as_str() == "/proc/self/exe" { - current!().process().exec_path().to_owned() - } else if path.starts_with("/proc/self/fd") { - let fd = path - .trim_start_matches("/proc/self/fd/") - .parse::() - .map_err(|_| errno!(EBADF, "Invalid file descriptor"))?; - let file_ref = current!().file(fd)?; - if let Ok(inode_file) = file_ref.as_inode_file() { - inode_file.get_abs_path().to_owned() - } else { - // TODO: support special device files - return_errno!(EINVAL, "not a normal file link") - } - } else { - let inode = { - let current = current!(); - let fs = current.fs().lock().unwrap(); - fs.lookup_inode_no_follow(&path)? - }; - if inode.metadata()?.type_ != FileType::SymLink { - return_errno!(EINVAL, "not a symbolic link"); - } - let mut content = vec![0u8; PATH_MAX]; - let len = inode.read_at(0, &mut content)?; - let path = std::str::from_utf8(&content[..len]) - .map_err(|_| errno!(EINVAL, "invalid symlink"))?; - String::from(path) + let inode = { + let current = current!(); + let fs = current.fs().lock().unwrap(); + fs.lookup_inode_no_follow(&path)? + }; + if inode.metadata()?.type_ != FileType::SymLink { + return_errno!(EINVAL, "not a symbolic link"); } + let mut content = vec![0u8; PATH_MAX]; + let len = inode.read_at(0, &mut content)?; + let path = + std::str::from_utf8(&content[..len]).map_err(|_| errno!(EINVAL, "invalid symlink"))?; + String::from(path) }; let len = file_path.len().min(buf.len()); buf[0..len].copy_from_slice(&file_path.as_bytes()[0..len]); diff --git a/src/libos/src/fs/mod.rs b/src/libos/src/fs/mod.rs index d37837ee..4c4ac6ab 100644 --- a/src/libos/src/fs/mod.rs +++ b/src/libos/src/fs/mod.rs @@ -39,6 +39,7 @@ mod host_fd; mod hostfs; mod inode_file; mod pipe; +mod procfs; mod rootfs; mod sefs; mod stdio; diff --git a/src/libos/src/fs/procfs/cpuinfo_inode.rs b/src/libos/src/fs/procfs/cpuinfo_inode.rs new file mode 100644 index 00000000..b10be675 --- /dev/null +++ b/src/libos/src/fs/procfs/cpuinfo_inode.rs @@ -0,0 +1,26 @@ +use super::*; +use std::untrusted::fs; + +pub struct CpuInfoINode; + +impl CpuInfoINode { + pub fn new() -> Arc { + Arc::new(File::new(Self)) + } +} + +impl ProcINode for CpuInfoINode { + fn generate_data_in_bytes(&self) -> vfs::Result> { + Ok(CPUINFO.to_vec()) + } +} + +lazy_static! { + static ref CPUINFO: Vec = get_untrusted_cpuinfo().unwrap(); +} + +fn get_untrusted_cpuinfo() -> Result> { + let cpuinfo = fs::read_to_string("/proc/cpuinfo")?.into_bytes(); + // TODO: do sanity check ? + Ok(cpuinfo) +} diff --git a/src/libos/src/fs/procfs/meminfo_inode.rs b/src/libos/src/fs/procfs/meminfo_inode.rs new file mode 100644 index 00000000..f12a8a9c --- /dev/null +++ b/src/libos/src/fs/procfs/meminfo_inode.rs @@ -0,0 +1,28 @@ +use super::*; +use crate::vm::USER_SPACE_VM_MANAGER; + +pub struct MemInfoINode; + +const KB: usize = 1024; + +impl MemInfoINode { + pub fn new() -> Arc { + Arc::new(File::new(Self)) + } +} + +impl ProcINode for MemInfoINode { + fn generate_data_in_bytes(&self) -> vfs::Result> { + let total_ram = USER_SPACE_VM_MANAGER.get_total_size(); + let free_ram = USER_SPACE_VM_MANAGER.get_free_size(); + Ok(format!( + "MemTotal: {} kB\n\ + MemFree: {} kB\n\ + MemAvailable: {} kB\n", + total_ram / KB, + free_ram / KB, + free_ram / KB, + ) + .into_bytes()) + } +} diff --git a/src/libos/src/fs/procfs/mod.rs b/src/libos/src/fs/procfs/mod.rs new file mode 100644 index 00000000..b454d08f --- /dev/null +++ b/src/libos/src/fs/procfs/mod.rs @@ -0,0 +1,143 @@ +use super::*; +use alloc::sync::{Arc, Weak}; +use rcore_fs::vfs; + +use crate::process::pid_t; + +use self::cpuinfo_inode::CpuInfoINode; +use self::meminfo_inode::MemInfoINode; +use self::pid_inode::LockedPidDirINode; +use self::proc_inode::{Dir, DirProcINode, File, ProcINode, SymLink}; +use self::self_inode::SelfSymINode; + +mod cpuinfo_inode; +mod meminfo_inode; +mod pid_inode; +mod proc_inode; +mod self_inode; + +/// Proc file system +pub struct ProcFS { + root: Arc>, + self_ref: Weak, +} + +impl FileSystem for ProcFS { + fn sync(&self) -> vfs::Result<()> { + Ok(()) + } + + fn root_inode(&self) -> Arc { + Arc::clone(&self.root) as _ + } + + fn info(&self) -> vfs::FsInfo { + vfs::FsInfo { + bsize: 0, + frsize: 0, + blocks: 0, + bfree: 0, + bavail: 0, + files: 0, + ffree: 0, + namemax: 0, + } + } +} + +impl ProcFS { + /// Create a new `ProcFS` + pub fn new() -> Arc { + let fs = { + let root = Arc::new(Dir::new(LockedProcRootINode(RwLock::new(ProcRootINode { + non_volatile_entries: HashMap::new(), + parent: Weak::default(), + this: Weak::default(), + })))); + ProcFS { + root, + self_ref: Weak::default(), + } + .wrap() + }; + fs.root.inner().init(&fs); + fs + } + + /// Wrap pure `ProcFS` with Arc + /// Used in constructors + fn wrap(self) -> Arc { + let fs = Arc::new(self); + let weak = Arc::downgrade(&fs); + let ptr = Arc::into_raw(fs) as *mut ProcFS; + unsafe { + (*ptr).self_ref = weak; + } + unsafe { Arc::from_raw(ptr) } + } +} + +struct LockedProcRootINode(RwLock); + +struct ProcRootINode { + non_volatile_entries: HashMap>, + this: Weak>, + parent: Weak>, +} + +impl LockedProcRootINode { + fn init(&self, fs: &Arc) { + let mut file = self.0.write().unwrap(); + file.this = Arc::downgrade(&fs.root); + file.parent = Arc::downgrade(&fs.root); + // Currently, we only init the 'cpuinfo', 'meminfo' and 'self' entry. + // TODO: Add more entries for root. + // All [pid] entries are lazy-initialized at the find() step. + let cpuinfo_inode = CpuInfoINode::new(); + file.non_volatile_entries + .insert(String::from("cpuinfo"), cpuinfo_inode); + let meminfo_inode = MemInfoINode::new(); + file.non_volatile_entries + .insert(String::from("meminfo"), meminfo_inode); + let self_inode = SelfSymINode::new(); + file.non_volatile_entries + .insert(String::from("self"), self_inode); + } +} + +impl DirProcINode for LockedProcRootINode { + fn find(&self, name: &str) -> vfs::Result> { + let file = self.0.read().unwrap(); + if name == "." { + return Ok(file.this.upgrade().unwrap()); + } + if name == ".." { + return Ok(file.parent.upgrade().unwrap()); + } + + if let Ok(pid) = name.parse::() { + let pid_inode = LockedPidDirINode::new(pid, file.this.upgrade().unwrap())?; + Ok(pid_inode) + } else if let Some(inode) = file.non_volatile_entries.get(name) { + Ok(Arc::clone(inode)) + } else { + Err(FsError::EntryNotFound) + } + } + + fn get_entry(&self, id: usize) -> vfs::Result { + match id { + 0 => Ok(String::from(".")), + 1 => Ok(String::from("..")), + i => { + let file = self.0.read().unwrap(); + if let Some(s) = file.non_volatile_entries.keys().nth(i - 2) { + Ok(s.to_string()) + } else { + // TODO: When to iterate the process table ? + Err(FsError::EntryNotFound) + } + } + } + } +} diff --git a/src/libos/src/fs/procfs/pid_inode.rs b/src/libos/src/fs/procfs/pid_inode.rs new file mode 100644 index 00000000..630356fe --- /dev/null +++ b/src/libos/src/fs/procfs/pid_inode.rs @@ -0,0 +1,193 @@ +use super::*; +use crate::process::table::get_process; +use crate::process::ProcessRef; + +pub struct LockedPidDirINode(RwLock); + +struct PidDirINode { + process_ref: ProcessRef, + this: Weak>, + parent: Arc, + entries: HashMap>, +} + +impl LockedPidDirINode { + pub fn new(pid: pid_t, parent: Arc) -> vfs::Result> { + let inode = Arc::new(Dir::new(Self(RwLock::new(PidDirINode { + process_ref: get_process(pid).map_err(|_| FsError::EntryNotFound)?, + this: Weak::default(), + parent: Arc::clone(&parent), + entries: HashMap::new(), + })))); + inode.inner().0.write().unwrap().this = Arc::downgrade(&inode); + inode.inner().init_entries()?; + Ok(inode) + } + + fn init_entries(&self) -> vfs::Result<()> { + let mut file = self.0.write().unwrap(); + // cmdline + let cmdline_inode = ProcCmdlineINode::new(&file.process_ref); + file.entries.insert(String::from("cmdline"), cmdline_inode); + // cwd + let cwd_inode = ProcCwdSymINode::new(&file.process_ref); + file.entries.insert(String::from("cwd"), cwd_inode); + // exe + let exe_inode = ProcExeSymINode::new(&file.process_ref); + file.entries.insert(String::from("exe"), exe_inode); + // fd + let fd_inode = LockedProcFdDirINode::new(&file.process_ref, file.this.upgrade().unwrap()); + file.entries.insert(String::from("fd"), fd_inode); + Ok(()) + } +} + +impl DirProcINode for LockedPidDirINode { + fn find(&self, name: &str) -> vfs::Result> { + let file = self.0.read().unwrap(); + if name == "." { + return Ok(file.this.upgrade().unwrap()); + } + if name == ".." { + return Ok(Arc::clone(&file.parent)); + } + if let Some(inode) = file.entries.get(name) { + Ok(Arc::clone(inode)) + } else { + Err(FsError::EntryNotFound) + } + } + + fn get_entry(&self, id: usize) -> vfs::Result { + match id { + 0 => Ok(String::from(".")), + 1 => Ok(String::from("..")), + i => { + let file = self.0.read().unwrap(); + if let Some(s) = file.entries.keys().nth(i - 2) { + Ok(s.to_string()) + } else { + Err(FsError::EntryNotFound) + } + } + } + } +} + +struct LockedProcFdDirINode(RwLock); + +struct ProcFdDirINode { + process_ref: ProcessRef, + this: Weak>, + parent: Arc, +} + +impl LockedProcFdDirINode { + pub fn new(process_ref: &ProcessRef, parent: Arc) -> Arc { + let inode = Arc::new(Dir::new(Self(RwLock::new(ProcFdDirINode { + process_ref: Arc::clone(process_ref), + this: Weak::default(), + parent: Arc::clone(&parent), + })))); + inode.inner().0.write().unwrap().this = Arc::downgrade(&inode); + inode + } +} + +impl DirProcINode for LockedProcFdDirINode { + fn find(&self, name: &str) -> vfs::Result> { + let file = self.0.read().unwrap(); + if name == "." { + return Ok(file.this.upgrade().unwrap()); + } + if name == ".." { + return Ok(Arc::clone(&file.parent)); + } + let fd = name + .parse::() + .map_err(|_| FsError::EntryNotFound)?; + let fd_inode = FdSymINode::new(&file.process_ref, fd)?; + Ok(fd_inode) + } + + fn get_entry(&self, id: usize) -> vfs::Result { + match id { + 0 => Ok(String::from(".")), + 1 => Ok(String::from("..")), + i => { + // TODO: When to iterate the file table ? + Err(FsError::EntryNotFound) + } + } + } +} + +pub struct ProcCmdlineINode(ProcessRef); + +impl ProcCmdlineINode { + pub fn new(process_ref: &ProcessRef) -> Arc { + Arc::new(File::new(Self(Arc::clone(process_ref)))) + } +} + +impl ProcINode for ProcCmdlineINode { + fn generate_data_in_bytes(&self) -> vfs::Result> { + Ok(self.0.exec_path().to_owned().into_bytes()) + } +} + +pub struct ProcExeSymINode(ProcessRef); + +impl ProcExeSymINode { + pub fn new(process_ref: &ProcessRef) -> Arc { + Arc::new(SymLink::new(Self(Arc::clone(process_ref)))) + } +} + +impl ProcINode for ProcExeSymINode { + fn generate_data_in_bytes(&self) -> vfs::Result> { + Ok(self.0.exec_path().to_owned().into_bytes()) + } +} + +pub struct ProcCwdSymINode(ProcessRef); + +impl ProcCwdSymINode { + pub fn new(process_ref: &ProcessRef) -> Arc { + Arc::new(SymLink::new(Self(Arc::clone(process_ref)))) + } +} + +impl ProcINode for ProcCwdSymINode { + fn generate_data_in_bytes(&self) -> vfs::Result> { + let main_thread = self.0.main_thread().ok_or(FsError::EntryNotFound)?; + let fs = main_thread.fs().lock().unwrap(); + Ok(fs.cwd().to_owned().into_bytes()) + } +} + +pub struct FdSymINode(FileRef); + +impl FdSymINode { + pub fn new(process_ref: &ProcessRef, fd: FileDesc) -> vfs::Result> { + let main_thread = process_ref.main_thread().ok_or(FsError::EntryNotFound)?; + let file_ref = main_thread.file(fd).map_err(|_| FsError::EntryNotFound)?; + Ok(Arc::new(SymLink::new(Self(Arc::clone(&file_ref))))) + } +} + +impl ProcINode for FdSymINode { + fn generate_data_in_bytes(&self) -> vfs::Result> { + let path = if let Ok(inode_file) = self.0.as_inode_file() { + inode_file.abs_path().to_owned() + } else { + // TODO: Support other file types + // For file descriptors for pipes and sockets, + // the content is: type:[inode]. + // For file descriptors that have no corresponding inode, + // the content is: anon_inode:[file-type] + return Err(FsError::EntryNotFound); + }; + Ok(path.into_bytes()) + } +} diff --git a/src/libos/src/fs/procfs/proc_inode/dir.rs b/src/libos/src/fs/procfs/proc_inode/dir.rs new file mode 100644 index 00000000..8fe63774 --- /dev/null +++ b/src/libos/src/fs/procfs/proc_inode/dir.rs @@ -0,0 +1,75 @@ +use super::*; + +pub struct Dir { + inner: T, +} + +impl Dir { + pub fn new(inner: T) -> Self { + Self { inner } + } + + pub fn inner(&self) -> &T { + &self.inner + } +} + +impl INode for Dir +where + T: DirProcINode + Sync + Send + 'static, +{ + fn read_at(&self, offset: usize, buf: &mut [u8]) -> vfs::Result { + Err(vfs::FsError::NotFile) + } + + fn write_at(&self, offset: usize, buf: &[u8]) -> vfs::Result { + Err(vfs::FsError::NotFile) + } + + fn poll(&self) -> vfs::Result { + Err(vfs::FsError::NotFile) + } + + fn metadata(&self) -> vfs::Result { + 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_: vfs::FileType::Dir, + mode: 0o555, + nlinks: 1, + uid: 0, + gid: 0, + rdev: 0, + }) + } + + fn set_metadata(&self, metadata: &Metadata) -> vfs::Result<()> { + Err(vfs::FsError::PermError) + } + + fn sync_all(&self) -> vfs::Result<()> { + Ok(()) + } + + fn sync_data(&self) -> vfs::Result<()> { + Ok(()) + } + + fn find(&self, name: &str) -> vfs::Result> { + self.inner().find(name) + } + + fn get_entry(&self, id: usize) -> vfs::Result { + self.inner().get_entry(id) + } + + fn as_any_ref(&self) -> &dyn Any { + self + } +} diff --git a/src/libos/src/fs/procfs/proc_inode/file.rs b/src/libos/src/fs/procfs/proc_inode/file.rs new file mode 100644 index 00000000..d07b2e76 --- /dev/null +++ b/src/libos/src/fs/procfs/proc_inode/file.rs @@ -0,0 +1,49 @@ +use super::*; + +pub struct File { + inner: T, +} + +impl File { + pub fn new(inner: T) -> Self { + Self { inner } + } + + pub fn inner(&self) -> &T { + &self.inner + } +} + +impl INode for File +where + T: ProcINode + Sync + Send + 'static, +{ + fn poll(&self) -> vfs::Result { + Ok(vfs::PollStatus { + read: true, + write: false, + error: false, + }) + } + + fn metadata(&self) -> vfs::Result { + 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_: vfs::FileType::File, + mode: 0o444, + nlinks: 1, + uid: 0, + gid: 0, + rdev: 0, + }) + } + + impl_inode_for_file_or_symlink!(); +} diff --git a/src/libos/src/fs/procfs/proc_inode/mod.rs b/src/libos/src/fs/procfs/proc_inode/mod.rs new file mode 100644 index 00000000..2d5a2ff4 --- /dev/null +++ b/src/libos/src/fs/procfs/proc_inode/mod.rs @@ -0,0 +1,60 @@ +use super::*; + +pub use self::dir::Dir; +pub use self::file::File; +pub use self::symlink::SymLink; + +mod dir; +mod file; +mod symlink; + +pub trait ProcINode { + fn generate_data_in_bytes(&self) -> vfs::Result>; +} + +pub trait DirProcINode { + fn find(&self, name: &str) -> vfs::Result>; + fn get_entry(&self, id: usize) -> vfs::Result; +} + +#[macro_export] +macro_rules! impl_inode_for_file_or_symlink { + () => { + fn read_at(&self, offset: usize, buf: &mut [u8]) -> vfs::Result { + let data = self.inner().generate_data_in_bytes()?; + let start = data.len().min(offset); + let end = data.len().min(offset + buf.len()); + let len = end - start; + buf[0..len].copy_from_slice(&data[start..end]); + Ok(len) + } + + fn write_at(&self, offset: usize, buf: &[u8]) -> vfs::Result { + Err(vfs::FsError::PermError) + } + + fn set_metadata(&self, metadata: &Metadata) -> vfs::Result<()> { + Err(vfs::FsError::PermError) + } + + fn sync_all(&self) -> vfs::Result<()> { + Ok(()) + } + + fn sync_data(&self) -> vfs::Result<()> { + Ok(()) + } + + fn find(&self, name: &str) -> vfs::Result> { + Err(FsError::NotDir) + } + + fn get_entry(&self, id: usize) -> vfs::Result { + Err(FsError::NotDir) + } + + fn as_any_ref(&self) -> &dyn Any { + self + } + }; +} diff --git a/src/libos/src/fs/procfs/proc_inode/symlink.rs b/src/libos/src/fs/procfs/proc_inode/symlink.rs new file mode 100644 index 00000000..be344895 --- /dev/null +++ b/src/libos/src/fs/procfs/proc_inode/symlink.rs @@ -0,0 +1,45 @@ +use super::*; + +pub struct SymLink { + inner: T, +} + +impl SymLink { + pub fn new(inner: T) -> Self { + Self { inner } + } + + pub fn inner(&self) -> &T { + &self.inner + } +} + +impl INode for SymLink +where + T: ProcINode + Sync + Send + 'static, +{ + fn poll(&self) -> vfs::Result { + Err(vfs::FsError::NotFile) + } + + fn metadata(&self) -> vfs::Result { + 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_: vfs::FileType::SymLink, + mode: 0o777, + nlinks: 1, + uid: 0, + gid: 0, + rdev: 0, + }) + } + + impl_inode_for_file_or_symlink!(); +} diff --git a/src/libos/src/fs/procfs/self_inode.rs b/src/libos/src/fs/procfs/self_inode.rs new file mode 100644 index 00000000..d613afd0 --- /dev/null +++ b/src/libos/src/fs/procfs/self_inode.rs @@ -0,0 +1,16 @@ +use super::*; +use crate::process::do_getpid; + +pub struct SelfSymINode; + +impl SelfSymINode { + pub fn new() -> Arc { + Arc::new(SymLink::new(Self)) + } +} + +impl ProcINode for SelfSymINode { + fn generate_data_in_bytes(&self) -> vfs::Result> { + Ok(do_getpid().unwrap().to_string().into_bytes()) + } +} diff --git a/src/libos/src/fs/rootfs.rs b/src/libos/src/fs/rootfs.rs index 8a953dbf..1a7bb53d 100644 --- a/src/libos/src/fs/rootfs.rs +++ b/src/libos/src/fs/rootfs.rs @@ -1,5 +1,6 @@ use super::dev_fs; use super::hostfs::HostFS; +use super::procfs::ProcFS; use super::sefs::{SgxStorage, SgxUuidProvider}; use super::*; use config::{ConfigMount, ConfigMountFsType}; @@ -149,6 +150,10 @@ fn mount_nonroot_fs_according_to(mount_config: &Vec, root: &MNode) let devfs = dev_fs::init_devfs()?; mount_fs_at(devfs, root, &mc.target)?; } + TYPE_PROCFS => { + let procfs = ProcFS::new(); + mount_fs_at(procfs, root, &mc.target)?; + } TYPE_UNIONFS => { return_errno!(EINVAL, "Cannot mount UnionFS at non-root path"); } diff --git a/test/Makefile b/test/Makefile index 1087a852..d0574f9c 100644 --- a/test/Makefile +++ b/test/Makefile @@ -18,7 +18,7 @@ TEST_DEPS := client data_sink TESTS ?= env empty hello_world malloc mmap file fs_perms getpid spawn sched pipe time \ truncate readdir mkdir open stat link symlink chmod chown tls pthread uname rlimit \ server server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group \ - ioctl fcntl eventfd emulate_syscall access signal sysinfo prctl rename + ioctl fcntl eventfd emulate_syscall access signal sysinfo prctl rename procfs # Benchmarks: need to be compiled and run by bench-% target BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput diff --git a/test/Occlum.json b/test/Occlum.json index e15c88c2..7f046816 100644 --- a/test/Occlum.json +++ b/test/Occlum.json @@ -55,6 +55,10 @@ "type": "hostfs", "source": "." }, + { + "target": "/proc", + "type": "procfs" + }, { "target": "/tmp", "type": "sefs", diff --git a/test/include/test_fs.h b/test/include/test_fs.h index 6b6ada31..df5b3b10 100644 --- a/test/include/test_fs.h +++ b/test/include/test_fs.h @@ -39,7 +39,7 @@ int fs_split_path(const char *path, char *dir_buf, char **dir_name, char *base_b } int fs_check_file_content(const char *path, const char *msg) { - char read_buf[128] = { 0 }; + char read_buf[PATH_MAX] = { 0 }; int fd = open(path, O_RDONLY); if (fd < 0) { diff --git a/test/procfs/Makefile b/test/procfs/Makefile new file mode 100644 index 00000000..9e1b6dec --- /dev/null +++ b/test/procfs/Makefile @@ -0,0 +1,5 @@ +include ../test_common.mk + +EXTRA_C_FLAGS := +EXTRA_LINK_FLAGS := +BIN_ARGS := diff --git a/test/procfs/main.c b/test/procfs/main.c new file mode 100644 index 00000000..cc200bee --- /dev/null +++ b/test/procfs/main.c @@ -0,0 +1,137 @@ +#include +#include +#include +#include +#include +#include "test_fs.h" + +// ============================================================================ +// Helper variable and function +// ============================================================================ + +const char **g_argv; + +static int test_readlink_from_procfs(const char *proc_inode, char *buf, int buf_size, + const char *expected_target) { + int n = readlink(proc_inode, buf, buf_size); + if (n < 0) { + THROW_ERROR("failed to readlink from %s", proc_inode); + } else if (n != strlen(expected_target)) { + THROW_ERROR("readlink from %s length is wrong", proc_inode); + } + if (strncmp(buf, expected_target, n) != 0) { + THROW_ERROR("check the result from %s failed", proc_inode); + } + + return 0; +} + +// ============================================================================ +// Test cases for procfs +// ============================================================================ + +static int test_readlink_from_proc_self_exe() { + char exe_buf[PATH_MAX] = { 0 }; + char absolute_path[PATH_MAX] = { 0 }; + const char *proc_exe = "/proc/self/exe"; + + int n = snprintf(absolute_path, sizeof(absolute_path), "/bin/%s", *g_argv); + if (n < 0) { + THROW_ERROR("failed to call snprintf"); + } + if (test_readlink_from_procfs(proc_exe, exe_buf, PATH_MAX, absolute_path) < 0) { + THROW_ERROR("failed to call test_readlink_from_procfs"); + } + + return 0; +} + +static int test_readlink_from_proc_self_cwd() { + char cwd_buf[PATH_MAX] = { 0 }; + const char *proc_cwd = "/proc/self/cwd"; + + if (test_readlink_from_procfs(proc_cwd, cwd_buf, PATH_MAX, "/") < 0) { + THROW_ERROR("failed to call test_readlink_from_procfs"); + } + if (chdir("/bin") < 0) { + THROW_ERROR("failed to chdir"); + } + if (test_readlink_from_procfs(proc_cwd, cwd_buf, PATH_MAX, "/bin") < 0) { + THROW_ERROR("failed to call test_readlink_from_procfs after chdir"); + } + if (chdir("/") < 0) { + THROW_ERROR("failed to chdir"); + } + + return 0; +} + +static int test_read_from_proc_self_cmdline() { + char absolute_path[PATH_MAX] = { 0 }; + const char *proc_cmdline = "/proc/self/cmdline"; + + int n = snprintf(absolute_path, sizeof(absolute_path), "/bin/%s", *g_argv); + if (n < 0) { + THROW_ERROR("failed to call snprintf"); + } + if (fs_check_file_content(proc_cmdline, absolute_path) < 0) { + THROW_ERROR("failed to check result in %s", proc_cmdline); + } + + return 0; +} + +static int test_read_from_proc_meminfo() { + char meminfo[1024] = { 0 }; + const char *proc_meminfo = "/proc/meminfo"; + + int fd = open(proc_meminfo, O_RDONLY); + if (fd < 0) { + THROW_ERROR("failed to open file: %s", proc_meminfo); + } + if (read(fd, meminfo, sizeof(meminfo)) < 0) { + THROW_ERROR("failed to read the meminfo"); + } + close(fd); + + return 0; +} + +static int test_read_from_proc_cpuinfo() { + char cpuinfo[1024] = { 0 }; + const char *proc_cpuinfo = "/proc/cpuinfo"; + int len; + + int fd = open(proc_cpuinfo, O_RDONLY); + if (fd < 0) { + THROW_ERROR("failed to open file: %s", proc_cpuinfo); + } + do { + len = read(fd, cpuinfo, sizeof(cpuinfo)); + if (len < 0) { + THROW_ERROR("failed to read the cpuinfo"); + } else if (len < sizeof(cpuinfo)) { + break; + } + } while (len == sizeof(cpuinfo)); + close(fd); + + return 0; +} + +// ============================================================================ +// Test suite main +// ============================================================================ + +static test_case_t test_cases[] = { + TEST_CASE(test_readlink_from_proc_self_exe), + TEST_CASE(test_readlink_from_proc_self_cwd), + TEST_CASE(test_read_from_proc_self_cmdline), + TEST_CASE(test_read_from_proc_meminfo), + TEST_CASE(test_read_from_proc_cpuinfo), +}; + +int main(int argc, const char *argv[]) { + g_argv = argv; + return test_suite_run(test_cases, ARRAY_SIZE(test_cases)); +} diff --git a/test/symlink/main.c b/test/symlink/main.c index a5799a08..18eaac4a 100644 --- a/test/symlink/main.c +++ b/test/symlink/main.c @@ -9,7 +9,6 @@ // ============================================================================ // Helper variable and function // ============================================================================ -const char **g_argv; static ssize_t get_path_by_fd(int fd, char *buf, ssize_t buf_len) { char proc_fd[64] = { 0 }; @@ -166,29 +165,6 @@ static int test_readlinkat() { return test_readlink_framework(__test_readlinkat); } -static int test_readlink_from_proc_self_exe() { - char exe_buf[128] = { 0 }; - char absolute_path[128] = { 0 }; - const char *proc_exe = "/proc/self/exe"; - ssize_t n; - - n = snprintf(absolute_path, sizeof(absolute_path), "/bin/%s", *g_argv); - if (n < 0) { - THROW_ERROR("failed to call snprintf"); - } - n = readlink(proc_exe, exe_buf, sizeof(exe_buf)); - if (n < 0) { - THROW_ERROR("failed to readlink from %s", proc_exe); - } else if (n != strlen(absolute_path)) { - THROW_ERROR("readlink from %s length is wrong", proc_exe); - } - if (strncmp(exe_buf, absolute_path, n) != 0) { - THROW_ERROR("check the absolute path from %s failed", proc_exe); - } - - return 0; -} - // ============================================================================ // Test cases for symlink // ============================================================================ @@ -441,7 +417,6 @@ static int test_create_file_from_symlink_to_relative_target() { static test_case_t test_cases[] = { TEST_CASE(test_readlink_from_proc_self_fd), TEST_CASE(test_realpath), - TEST_CASE(test_readlink_from_proc_self_exe), TEST_CASE(test_readlinkat), TEST_CASE(test_symlinkat), TEST_CASE(test_symlink_to_absolute_target), @@ -454,6 +429,5 @@ static test_case_t test_cases[] = { }; int main(int argc, const char *argv[]) { - g_argv = argv; return test_suite_run(test_cases, ARRAY_SIZE(test_cases)); } diff --git a/tools/gen_internal_conf/src/main.rs b/tools/gen_internal_conf/src/main.rs index 8b626d3a..ee14653a 100644 --- a/tools/gen_internal_conf/src/main.rs +++ b/tools/gen_internal_conf/src/main.rs @@ -251,6 +251,10 @@ fn gen_mount_config(occlum_conf_root_fs_mac: String) -> serde_json::Value { "type": "hostfs", "source": "." }, + { + "target": "/proc", + "type": "procfs" + }, { "target": "/tmp", "type": "sefs", @@ -280,7 +284,7 @@ fn gen_mount_config(occlum_conf_root_fs_mac: String) -> serde_json::Value { .pointer_mut("/mount/0/options/layers/1/source") .unwrap() = serde_json::Value::String(unionfs_run_source_path); *internal_mount_config - .pointer_mut("/mount/2/source") + .pointer_mut("/mount/3/source") .unwrap() = serde_json::Value::String(tmp_run_source_path); debug!("internal Occlum.json mount config:\n{:?}", internal_mount_config); diff --git a/tools/occlum b/tools/occlum index 3cf03222..da1c32d8 100755 --- a/tools/occlum +++ b/tools/occlum @@ -149,6 +149,7 @@ cmd_init() { mkdir -p image/host mkdir -p image/tmp mkdir -p image/dev + mkdir -p image/proc local occlum_glibc_lib=/opt/occlum/glibc/lib local cpu_lib=/sys/devices/system/cpu if [ -d "$occlum_glibc_lib" ]; then