diff --git a/src/libos/src/fs/file_table.rs b/src/libos/src/fs/file_table.rs index df9cae80..d719ad5c 100644 --- a/src/libos/src/fs/file_table.rs +++ b/src/libos/src/fs/file_table.rs @@ -94,6 +94,16 @@ impl FileTable { } } + pub fn fds(&self) -> Vec { + let table = &self.table; + table + .iter() + .enumerate() + .filter(|(_, opt)| opt.is_some()) + .map(|(idx, _)| idx as FileDesc) + .collect() + } + pub fn get(&self, fd: FileDesc) -> Result { let entry = self.get_entry(fd)?; Ok(entry.file.clone()) diff --git a/src/libos/src/fs/procfs/mod.rs b/src/libos/src/fs/procfs/mod.rs index 12be004f..1f298984 100644 --- a/src/libos/src/fs/procfs/mod.rs +++ b/src/libos/src/fs/procfs/mod.rs @@ -3,6 +3,7 @@ use alloc::sync::{Arc, Weak}; use rcore_fs::vfs; use crate::process::pid_t; +use crate::process::table::get_all_processes; use self::cpuinfo_inode::CpuInfoINode; use self::meminfo_inode::MemInfoINode; @@ -19,6 +20,11 @@ mod self_inode; // Same with the procfs on Linux const PROC_SUPER_MAGIC: usize = 0x9fa0; +// Use the same inode number for all the inodes in procfs, the value is +// arbitrarily chosen, and it should not be zero. +// TODO: Assign different inode numbers for different inodes +pub const PROC_INO: usize = 0x63fd_40e5; + /// Proc file system pub struct ProcFS { root: Arc>, @@ -55,7 +61,6 @@ impl 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 { @@ -86,14 +91,12 @@ 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. @@ -116,7 +119,7 @@ impl DirProcINode for LockedProcRootINode { return Ok(file.this.upgrade().unwrap()); } if name == ".." { - return Ok(file.parent.upgrade().unwrap()); + return Ok(file.this.upgrade().unwrap()); } if let Ok(pid) = name.parse::() { @@ -135,13 +138,61 @@ impl DirProcINode for LockedProcRootINode { 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()) + if let Some(name) = file.non_volatile_entries.keys().nth(i - 2) { + Ok(name.to_owned()) } else { - // TODO: When to iterate the process table ? - Err(FsError::EntryNotFound) + let processes = get_all_processes(); + let prior_entries_len = 2 + file.non_volatile_entries.len(); + let process = processes + .iter() + .nth(i - prior_entries_len) + .ok_or(FsError::EntryNotFound)?; + Ok(process.pid().to_string()) } } } } + + fn iterate_entries(&self, mut ctx: &mut DirentWriterContext) -> vfs::Result { + let file = self.0.read().unwrap(); + let mut total_written_len = 0; + let idx = ctx.pos(); + + // Write first two special entries + if idx == 0 { + let this_inode = file.this.upgrade().unwrap(); + write_inode_entry!(&mut ctx, ".", &this_inode, &mut total_written_len); + } + if idx <= 1 { + let parent_inode = file.this.upgrade().unwrap(); + write_inode_entry!(&mut ctx, "..", &parent_inode, &mut total_written_len); + } + + // Write the non volatile entries + let skipped = if idx < 2 { 0 } else { idx - 2 }; + for (name, inode) in file.non_volatile_entries.iter().skip(skipped) { + write_inode_entry!(&mut ctx, name, inode, &mut total_written_len); + } + + // Write the pid entries + let skipped = { + let prior_entries_len = 2 + file.non_volatile_entries.len(); + if idx < prior_entries_len { + 0 + } else { + idx - prior_entries_len + } + }; + for process in get_all_processes().iter().skip(skipped) { + write_entry!( + &mut ctx, + &process.pid().to_string(), + PROC_INO, + vfs::FileType::Dir, + &mut total_written_len + ); + } + + Ok(total_written_len) + } } diff --git a/src/libos/src/fs/procfs/pid_inode.rs b/src/libos/src/fs/procfs/pid_inode.rs index 30a94b6d..045890ff 100644 --- a/src/libos/src/fs/procfs/pid_inode.rs +++ b/src/libos/src/fs/procfs/pid_inode.rs @@ -70,14 +70,32 @@ impl DirProcINode for LockedPidDirINode { 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) - } + let name = file + .entries + .keys() + .nth(i - 2) + .ok_or(FsError::EntryNotFound)?; + Ok(name.to_owned()) } } } + + fn iterate_entries(&self, mut ctx: &mut DirentWriterContext) -> vfs::Result { + let file = self.0.read().unwrap(); + let mut total_written_len = 0; + let idx = ctx.pos(); + + // Write first two special entries + write_first_two_entries!(idx, &mut ctx, &file, &mut total_written_len); + + // Write the normal entries + let skipped = if idx < 2 { 0 } else { idx - 2 }; + for (name, inode) in file.entries.iter().skip(skipped) { + write_inode_entry!(&mut ctx, name, inode, &mut total_written_len); + } + + Ok(total_written_len) + } } struct LockedProcFdDirINode(RwLock); @@ -121,11 +139,47 @@ impl DirProcINode for LockedProcFdDirINode { 0 => Ok(String::from(".")), 1 => Ok(String::from("..")), i => { - // TODO: When to iterate the file table ? - Err(FsError::EntryNotFound) + let file = self.0.read().unwrap(); + let main_thread = file + .process_ref + .main_thread() + .ok_or(FsError::EntryNotFound)?; + let fds = main_thread.files().lock().unwrap().fds(); + let fd = fds.iter().nth(i - 2).ok_or(FsError::EntryNotFound)?; + Ok(fd.to_string()) } } } + + fn iterate_entries(&self, mut ctx: &mut DirentWriterContext) -> vfs::Result { + let file = self.0.read().unwrap(); + let mut total_written_len = 0; + let idx = ctx.pos(); + + // Write first two special entries + write_first_two_entries!(idx, &mut ctx, &file, &mut total_written_len); + + // Write the fd entries + let skipped = if idx < 2 { 0 } else { idx - 2 }; + let main_thread = match file.process_ref.main_thread() { + Some(main_thread) => main_thread, + None => { + return Ok(total_written_len); + } + }; + let fds = main_thread.files().lock().unwrap().fds(); + for fd in fds.iter().skip(skipped) { + write_entry!( + &mut ctx, + &fd.to_string(), + PROC_INO, + vfs::FileType::SymLink, + &mut total_written_len + ); + } + + Ok(total_written_len) + } } pub struct ProcCmdlineINode(ProcessRef); diff --git a/src/libos/src/fs/procfs/proc_inode/dir.rs b/src/libos/src/fs/procfs/proc_inode/dir.rs index 8fe63774..5b504e65 100644 --- a/src/libos/src/fs/procfs/proc_inode/dir.rs +++ b/src/libos/src/fs/procfs/proc_inode/dir.rs @@ -33,7 +33,7 @@ where fn metadata(&self) -> vfs::Result { Ok(Metadata { dev: 0, - inode: 0, + inode: PROC_INO, size: 0, blk_size: 0, blocks: 0, @@ -69,6 +69,10 @@ where self.inner().get_entry(id) } + fn iterate_entries(&self, ctx: &mut DirentWriterContext) -> vfs::Result { + self.inner().iterate_entries(ctx) + } + 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 index d07b2e76..e2fab56c 100644 --- a/src/libos/src/fs/procfs/proc_inode/file.rs +++ b/src/libos/src/fs/procfs/proc_inode/file.rs @@ -29,7 +29,7 @@ where fn metadata(&self) -> vfs::Result { Ok(Metadata { dev: 0, - inode: 0, + inode: PROC_INO, size: 0, blk_size: 0, blocks: 0, diff --git a/src/libos/src/fs/procfs/proc_inode/mod.rs b/src/libos/src/fs/procfs/proc_inode/mod.rs index 2d5a2ff4..131110b6 100644 --- a/src/libos/src/fs/procfs/proc_inode/mod.rs +++ b/src/libos/src/fs/procfs/proc_inode/mod.rs @@ -15,6 +15,7 @@ pub trait ProcINode { pub trait DirProcINode { fn find(&self, name: &str) -> vfs::Result>; fn get_entry(&self, id: usize) -> vfs::Result; + fn iterate_entries(&self, ctx: &mut DirentWriterContext) -> vfs::Result; } #[macro_export] @@ -53,8 +54,65 @@ macro_rules! impl_inode_for_file_or_symlink { Err(FsError::NotDir) } + fn iterate_entries(&self, ctx: &mut DirentWriterContext) -> vfs::Result { + Err(FsError::NotDir) + } + fn as_any_ref(&self) -> &dyn Any { self } }; } + +#[macro_export] +macro_rules! write_first_two_entries { + ($idx: expr, $ctx:expr, $file:expr, $total_written:expr) => { + let idx = $idx; + let file = $file; + + if idx == 0 { + let this_inode = file.this.upgrade().unwrap(); + write_inode_entry!($ctx, ".", &this_inode, $total_written); + } + if idx <= 1 { + write_inode_entry!($ctx, "..", &file.parent, $total_written); + } + }; +} + +#[macro_export] +macro_rules! write_inode_entry { + ($ctx:expr, $name:expr, $inode:expr, $total_written:expr) => { + let ctx = $ctx; + let name = $name; + let ino = $inode.metadata()?.inode; + let type_ = $inode.metadata()?.type_; + let total_written = $total_written; + + write_entry!(ctx, name, ino, type_, total_written); + }; +} + +#[macro_export] +macro_rules! write_entry { + ($ctx:expr, $name:expr, $ino:expr, $type_:expr, $total_written:expr) => { + let ctx = $ctx; + let name = $name; + let ino = $ino; + let type_ = $type_; + let total_written = $total_written; + + match ctx.write_entry(name, ino as u64, type_) { + Ok(written_len) => { + *total_written += written_len; + } + Err(e) => { + if *total_written == 0 { + return Err(e); + } else { + return Ok(*total_written); + } + } + } + }; +} diff --git a/src/libos/src/fs/procfs/proc_inode/symlink.rs b/src/libos/src/fs/procfs/proc_inode/symlink.rs index be344895..7df91671 100644 --- a/src/libos/src/fs/procfs/proc_inode/symlink.rs +++ b/src/libos/src/fs/procfs/proc_inode/symlink.rs @@ -25,7 +25,7 @@ where fn metadata(&self) -> vfs::Result { Ok(Metadata { dev: 0, - inode: 0, + inode: PROC_INO, size: 0, blk_size: 0, blocks: 0, diff --git a/test/include/test_fs.h b/test/include/test_fs.h index 491dd379..641693d4 100644 --- a/test/include/test_fs.h +++ b/test/include/test_fs.h @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include "test.h" int fs_split_path(const char *path, char *dir_buf, char **dir_name, char *base_buf, @@ -95,4 +97,56 @@ int check_file_with_repeated_bytes(int fd, size_t len, int expected_byte_val) { return 0; } +bool check_dir_entries(char entries[][NAME_MAX], int entry_cnt, + char expected_entries[][NAME_MAX], int expected_cnt) { + for (int i = 0; i < expected_cnt; i++) { + bool found = false; + for (int j = 0; j < entry_cnt; j++) { + if (strncmp(expected_entries[i], entries[j], strlen(expected_entries[i])) == 0) { + found = true; + break; + } + } + if (!found) { + printf("can't find: %s\n", expected_entries[i]); + return false; + } + } + return true; +} + +int check_readdir_with_expected_entries(const char *dir_path, + char expected_entries[][NAME_MAX], + int expected_cnt) { + struct dirent *dp; + DIR *dirp; + char entries[128][NAME_MAX] = { 0 }; + + dirp = opendir(dir_path); + if (dirp == NULL) { + THROW_ERROR("failed to open directory"); + } + + int entry_cnt = 0; + while (1) { + errno = 0; + dp = readdir(dirp); + if (dp == NULL) { + if (errno != 0) { + THROW_ERROR("failed to call readdir"); + } + break; + } + memcpy(entries[entry_cnt], dp->d_name, strlen(dp->d_name)); + ++entry_cnt; + } + + if (!check_dir_entries(entries, entry_cnt, expected_entries, expected_cnt)) { + THROW_ERROR("failed to check the result of readdir"); + } + + closedir(dirp); + return 0; +} + #endif /* __TEST_FS_H */ diff --git a/test/procfs/main.c b/test/procfs/main.c index c3e9e288..4de32ab8 100644 --- a/test/procfs/main.c +++ b/test/procfs/main.c @@ -190,6 +190,57 @@ static int test_statfs() { return 0; } +static int test_readdir_root() { + const char *root = "/proc"; + char pid[NAME_MAX] = { 0 }; + snprintf(pid, sizeof(pid), "%d", getpid()); + char expected_entries[4][NAME_MAX] = { + "self", + "meminfo", + "cpuinfo", + { *pid }, + }; + + if (check_readdir_with_expected_entries(root, expected_entries, 4) < 0) { + THROW_ERROR("failed to test readdir %s", root); + } + + return 0; +} + +static int test_readdir_self() { + const char *self = "/proc/self"; + char expected_entries[6][NAME_MAX] = { + "exe", + "cwd", + "root", + "fd", + "comm", + "cmdline", + }; + + if (check_readdir_with_expected_entries(self, expected_entries, 6) < 0) { + THROW_ERROR("failed to test readdir %s", self); + } + + return 0; +} + +static int test_readdir_self_fd() { + const char *self_fd = "/proc/self/fd"; + char expected_entries[3][NAME_MAX] = { + "0", + "1", + "2", + }; + + if (check_readdir_with_expected_entries(self_fd, expected_entries, 3) < 0) { + THROW_ERROR("failed to test readdir %s", self_fd); + } + + return 0; +} + // ============================================================================ // Test suite main // ============================================================================ @@ -204,6 +255,9 @@ static test_case_t test_cases[] = { TEST_CASE(test_read_from_proc_meminfo), TEST_CASE(test_read_from_proc_cpuinfo), TEST_CASE(test_statfs), + TEST_CASE(test_readdir_root), + TEST_CASE(test_readdir_self), + TEST_CASE(test_readdir_self_fd), }; int main(int argc, const char *argv[]) { diff --git a/test/readdir/main.c b/test/readdir/main.c index a6dab88f..cbef735a 100644 --- a/test/readdir/main.c +++ b/test/readdir/main.c @@ -1,19 +1,17 @@ #include #include #include -#include #include #include #include #include "test_fs.h" // ============================================================================ -// Helper function +// The test case of readdir // ============================================================================ -#define NUM 9 -static bool check_dir_entries(char entries[][256], int entry_cnt) { - char *expected_entries[NUM] = { +static int test_readdir() { + char expected_entries[9][NAME_MAX] = { "bin", "dev", "host", @@ -24,52 +22,10 @@ static bool check_dir_entries(char entries[][256], int entry_cnt) { "root", "tmp", }; - for (int i = 0; i < NUM; i++) { - bool find_entry = false; - for (int j = 0; j < entry_cnt; j++) { - if (strncmp(expected_entries[i], entries[j], strlen(expected_entries[i])) == 0) { - find_entry = true; - break; - } - } - if (!find_entry) { - return false; - } - } - return true; -} -// ============================================================================ -// The test case of readdir -// ============================================================================ - -static int test_readdir() { - struct dirent *dp; - DIR *dirp; - char entries[32][256] = { 0 }; - - dirp = opendir("/"); - if (dirp == NULL) { - THROW_ERROR("failed to open directory"); - } - int entry_cnt = 0; - while (1) { - errno = 0; - dp = readdir(dirp); - if (dp == NULL) { - if (errno != 0) { - closedir(dirp); - THROW_ERROR("failed to call readdir"); - } - break; - } - strncpy(entries[entry_cnt], dp->d_name, 256); - ++entry_cnt; - } - if (!check_dir_entries(entries, entry_cnt)) { + if (check_readdir_with_expected_entries("/", expected_entries, 9) < 0) { THROW_ERROR("failed to check the result of readdir"); } - closedir(dirp); return 0; } diff --git a/test/unix_socket/main.c b/test/unix_socket/main.c index bab423ed..34b748b9 100644 --- a/test/unix_socket/main.c +++ b/test/unix_socket/main.c @@ -9,7 +9,6 @@ #include #include #include -#include #include "test.h"