Add ProcFS
This commit is contained in:
parent
8bfef4086f
commit
0b51d83811
@ -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;
|
||||
|
26
src/libos/src/fs/procfs/cpuinfo_inode.rs
Normal file
26
src/libos/src/fs/procfs/cpuinfo_inode.rs
Normal file
@ -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)
|
||||
}
|
28
src/libos/src/fs/procfs/meminfo_inode.rs
Normal file
28
src/libos/src/fs/procfs/meminfo_inode.rs
Normal file
@ -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())
|
||||
}
|
||||
}
|
143
src/libos/src/fs/procfs/mod.rs
Normal file
143
src/libos/src/fs/procfs/mod.rs
Normal file
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
193
src/libos/src/fs/procfs/pid_inode.rs
Normal file
193
src/libos/src/fs/procfs/pid_inode.rs
Normal file
@ -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())
|
||||
}
|
||||
}
|
75
src/libos/src/fs/procfs/proc_inode/dir.rs
Normal file
75
src/libos/src/fs/procfs/proc_inode/dir.rs
Normal file
@ -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
|
||||
}
|
||||
}
|
49
src/libos/src/fs/procfs/proc_inode/file.rs
Normal file
49
src/libos/src/fs/procfs/proc_inode/file.rs
Normal file
@ -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!();
|
||||
}
|
60
src/libos/src/fs/procfs/proc_inode/mod.rs
Normal file
60
src/libos/src/fs/procfs/proc_inode/mod.rs
Normal file
@ -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
|
||||
}
|
||||
};
|
||||
}
|
45
src/libos/src/fs/procfs/proc_inode/symlink.rs
Normal file
45
src/libos/src/fs/procfs/proc_inode/symlink.rs
Normal file
@ -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!();
|
||||
}
|
16
src/libos/src/fs/procfs/self_inode.rs
Normal file
16
src/libos/src/fs/procfs/self_inode.rs
Normal file
@ -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
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
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
|
||||
|
Loading…
Reference in New Issue
Block a user