Add ProcFS

This commit is contained in:
LI Qing 2021-01-11 18:50:52 +08:00 committed by Tate, Hongliang Tian
parent 8bfef4086f
commit 0b51d83811
24 changed files with 818 additions and 60 deletions

@ -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",

@ -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

@ -52,6 +52,10 @@
"type": "hostfs",
"source": "."
},
{
"target": "/proc",
"type": "procfs"
},
{
"target": "/tmp",
"type": "sefs",

@ -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<ConfigMount> {
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");
}

@ -5,35 +5,19 @@ pub fn do_readlinkat(fs_path: &FsPath, buf: &mut [u8]) -> Result<usize> {
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::<FileDesc>()
.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]);

@ -39,6 +39,7 @@ mod host_fd;
mod hostfs;
mod inode_file;
mod pipe;
mod procfs;
mod rootfs;
mod sefs;
mod stdio;

@ -0,0 +1,26 @@
use super::*;
use std::untrusted::fs;
pub struct CpuInfoINode;
impl CpuInfoINode {
pub fn new() -> Arc<dyn INode> {
Arc::new(File::new(Self))
}
}
impl ProcINode for CpuInfoINode {
fn generate_data_in_bytes(&self) -> vfs::Result<Vec<u8>> {
Ok(CPUINFO.to_vec())
}
}
lazy_static! {
static ref CPUINFO: Vec<u8> = get_untrusted_cpuinfo().unwrap();
}
fn get_untrusted_cpuinfo() -> Result<Vec<u8>> {
let cpuinfo = fs::read_to_string("/proc/cpuinfo")?.into_bytes();
// TODO: do sanity check ?
Ok(cpuinfo)
}

@ -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<dyn INode> {
Arc::new(File::new(Self))
}
}
impl ProcINode for MemInfoINode {
fn generate_data_in_bytes(&self) -> vfs::Result<Vec<u8>> {
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())
}
}

@ -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<Dir<LockedProcRootINode>>,
self_ref: Weak<ProcFS>,
}
impl FileSystem for ProcFS {
fn sync(&self) -> vfs::Result<()> {
Ok(())
}
fn root_inode(&self) -> Arc<dyn INode> {
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<ProcFS> {
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<ProcFS> {
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<ProcRootINode>);
struct ProcRootINode {
non_volatile_entries: HashMap<String, Arc<dyn INode>>,
this: Weak<Dir<LockedProcRootINode>>,
parent: Weak<Dir<LockedProcRootINode>>,
}
impl LockedProcRootINode {
fn init(&self, fs: &Arc<ProcFS>) {
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<Arc<dyn INode>> {
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::<pid_t>() {
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<String> {
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)
}
}
}
}
}

@ -0,0 +1,193 @@
use super::*;
use crate::process::table::get_process;
use crate::process::ProcessRef;
pub struct LockedPidDirINode(RwLock<PidDirINode>);
struct PidDirINode {
process_ref: ProcessRef,
this: Weak<Dir<LockedPidDirINode>>,
parent: Arc<dyn INode>,
entries: HashMap<String, Arc<dyn INode>>,
}
impl LockedPidDirINode {
pub fn new(pid: pid_t, parent: Arc<dyn INode>) -> vfs::Result<Arc<dyn INode>> {
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<Arc<dyn INode>> {
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<String> {
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<ProcFdDirINode>);
struct ProcFdDirINode {
process_ref: ProcessRef,
this: Weak<Dir<LockedProcFdDirINode>>,
parent: Arc<dyn INode>,
}
impl LockedProcFdDirINode {
pub fn new(process_ref: &ProcessRef, parent: Arc<dyn INode>) -> Arc<dyn INode> {
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<Arc<dyn INode>> {
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::<FileDesc>()
.map_err(|_| FsError::EntryNotFound)?;
let fd_inode = FdSymINode::new(&file.process_ref, fd)?;
Ok(fd_inode)
}
fn get_entry(&self, id: usize) -> vfs::Result<String> {
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<dyn INode> {
Arc::new(File::new(Self(Arc::clone(process_ref))))
}
}
impl ProcINode for ProcCmdlineINode {
fn generate_data_in_bytes(&self) -> vfs::Result<Vec<u8>> {
Ok(self.0.exec_path().to_owned().into_bytes())
}
}
pub struct ProcExeSymINode(ProcessRef);
impl ProcExeSymINode {
pub fn new(process_ref: &ProcessRef) -> Arc<dyn INode> {
Arc::new(SymLink::new(Self(Arc::clone(process_ref))))
}
}
impl ProcINode for ProcExeSymINode {
fn generate_data_in_bytes(&self) -> vfs::Result<Vec<u8>> {
Ok(self.0.exec_path().to_owned().into_bytes())
}
}
pub struct ProcCwdSymINode(ProcessRef);
impl ProcCwdSymINode {
pub fn new(process_ref: &ProcessRef) -> Arc<dyn INode> {
Arc::new(SymLink::new(Self(Arc::clone(process_ref))))
}
}
impl ProcINode for ProcCwdSymINode {
fn generate_data_in_bytes(&self) -> vfs::Result<Vec<u8>> {
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<Arc<dyn INode>> {
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<Vec<u8>> {
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())
}
}

@ -0,0 +1,75 @@
use super::*;
pub struct Dir<T: DirProcINode> {
inner: T,
}
impl<T: DirProcINode> Dir<T> {
pub fn new(inner: T) -> Self {
Self { inner }
}
pub fn inner(&self) -> &T {
&self.inner
}
}
impl<T> INode for Dir<T>
where
T: DirProcINode + Sync + Send + 'static,
{
fn read_at(&self, offset: usize, buf: &mut [u8]) -> vfs::Result<usize> {
Err(vfs::FsError::NotFile)
}
fn write_at(&self, offset: usize, buf: &[u8]) -> vfs::Result<usize> {
Err(vfs::FsError::NotFile)
}
fn poll(&self) -> vfs::Result<vfs::PollStatus> {
Err(vfs::FsError::NotFile)
}
fn metadata(&self) -> vfs::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_: 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<Arc<dyn INode>> {
self.inner().find(name)
}
fn get_entry(&self, id: usize) -> vfs::Result<String> {
self.inner().get_entry(id)
}
fn as_any_ref(&self) -> &dyn Any {
self
}
}

@ -0,0 +1,49 @@
use super::*;
pub struct File<T: ProcINode> {
inner: T,
}
impl<T: ProcINode> File<T> {
pub fn new(inner: T) -> Self {
Self { inner }
}
pub fn inner(&self) -> &T {
&self.inner
}
}
impl<T> INode for File<T>
where
T: ProcINode + Sync + Send + 'static,
{
fn poll(&self) -> vfs::Result<vfs::PollStatus> {
Ok(vfs::PollStatus {
read: true,
write: false,
error: false,
})
}
fn metadata(&self) -> vfs::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_: vfs::FileType::File,
mode: 0o444,
nlinks: 1,
uid: 0,
gid: 0,
rdev: 0,
})
}
impl_inode_for_file_or_symlink!();
}

@ -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<Vec<u8>>;
}
pub trait DirProcINode {
fn find(&self, name: &str) -> vfs::Result<Arc<dyn INode>>;
fn get_entry(&self, id: usize) -> vfs::Result<String>;
}
#[macro_export]
macro_rules! impl_inode_for_file_or_symlink {
() => {
fn read_at(&self, offset: usize, buf: &mut [u8]) -> vfs::Result<usize> {
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<usize> {
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<Arc<dyn INode>> {
Err(FsError::NotDir)
}
fn get_entry(&self, id: usize) -> vfs::Result<String> {
Err(FsError::NotDir)
}
fn as_any_ref(&self) -> &dyn Any {
self
}
};
}

@ -0,0 +1,45 @@
use super::*;
pub struct SymLink<T: ProcINode> {
inner: T,
}
impl<T: ProcINode> SymLink<T> {
pub fn new(inner: T) -> Self {
Self { inner }
}
pub fn inner(&self) -> &T {
&self.inner
}
}
impl<T> INode for SymLink<T>
where
T: ProcINode + Sync + Send + 'static,
{
fn poll(&self) -> vfs::Result<vfs::PollStatus> {
Err(vfs::FsError::NotFile)
}
fn metadata(&self) -> vfs::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_: vfs::FileType::SymLink,
mode: 0o777,
nlinks: 1,
uid: 0,
gid: 0,
rdev: 0,
})
}
impl_inode_for_file_or_symlink!();
}

@ -0,0 +1,16 @@
use super::*;
use crate::process::do_getpid;
pub struct SelfSymINode;
impl SelfSymINode {
pub fn new() -> Arc<dyn INode> {
Arc::new(SymLink::new(Self))
}
}
impl ProcINode for SelfSymINode {
fn generate_data_in_bytes(&self) -> vfs::Result<Vec<u8>> {
Ok(do_getpid().unwrap().to_string().into_bytes())
}
}

@ -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<ConfigMount>, 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");
}

@ -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

@ -55,6 +55,10 @@
"type": "hostfs",
"source": "."
},
{
"target": "/proc",
"type": "procfs"
},
{
"target": "/tmp",
"type": "sefs",

@ -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) {

5
test/procfs/Makefile Normal file

@ -0,0 +1,5 @@
include ../test_common.mk
EXTRA_C_FLAGS :=
EXTRA_LINK_FLAGS :=
BIN_ARGS :=

137
test/procfs/main.c Normal file

@ -0,0 +1,137 @@
#include <sys/types.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <errno.h>
#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));
}

@ -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));
}

@ -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);

@ -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