Add getdents support for procfs's inode

This commit is contained in:
LI Qing 2021-07-27 18:31:14 +08:00 committed by Zongmin.Gu
parent 42bed8d338
commit dc37995bf0
11 changed files with 307 additions and 67 deletions

@ -94,6 +94,16 @@ impl FileTable {
} }
} }
pub fn fds(&self) -> Vec<FileDesc> {
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<FileRef> { pub fn get(&self, fd: FileDesc) -> Result<FileRef> {
let entry = self.get_entry(fd)?; let entry = self.get_entry(fd)?;
Ok(entry.file.clone()) Ok(entry.file.clone())

@ -3,6 +3,7 @@ use alloc::sync::{Arc, Weak};
use rcore_fs::vfs; use rcore_fs::vfs;
use crate::process::pid_t; use crate::process::pid_t;
use crate::process::table::get_all_processes;
use self::cpuinfo_inode::CpuInfoINode; use self::cpuinfo_inode::CpuInfoINode;
use self::meminfo_inode::MemInfoINode; use self::meminfo_inode::MemInfoINode;
@ -19,6 +20,11 @@ mod self_inode;
// Same with the procfs on Linux // Same with the procfs on Linux
const PROC_SUPER_MAGIC: usize = 0x9fa0; 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 /// Proc file system
pub struct ProcFS { pub struct ProcFS {
root: Arc<Dir<LockedProcRootINode>>, root: Arc<Dir<LockedProcRootINode>>,
@ -55,7 +61,6 @@ impl ProcFS {
let fs = { let fs = {
let root = Arc::new(Dir::new(LockedProcRootINode(RwLock::new(ProcRootINode { let root = Arc::new(Dir::new(LockedProcRootINode(RwLock::new(ProcRootINode {
non_volatile_entries: HashMap::new(), non_volatile_entries: HashMap::new(),
parent: Weak::default(),
this: Weak::default(), this: Weak::default(),
})))); }))));
ProcFS { ProcFS {
@ -86,14 +91,12 @@ struct LockedProcRootINode(RwLock<ProcRootINode>);
struct ProcRootINode { struct ProcRootINode {
non_volatile_entries: HashMap<String, Arc<dyn INode>>, non_volatile_entries: HashMap<String, Arc<dyn INode>>,
this: Weak<Dir<LockedProcRootINode>>, this: Weak<Dir<LockedProcRootINode>>,
parent: Weak<Dir<LockedProcRootINode>>,
} }
impl LockedProcRootINode { impl LockedProcRootINode {
fn init(&self, fs: &Arc<ProcFS>) { fn init(&self, fs: &Arc<ProcFS>) {
let mut file = self.0.write().unwrap(); let mut file = self.0.write().unwrap();
file.this = Arc::downgrade(&fs.root); file.this = Arc::downgrade(&fs.root);
file.parent = Arc::downgrade(&fs.root);
// Currently, we only init the 'cpuinfo', 'meminfo' and 'self' entry. // Currently, we only init the 'cpuinfo', 'meminfo' and 'self' entry.
// TODO: Add more entries for root. // TODO: Add more entries for root.
// All [pid] entries are lazy-initialized at the find() step. // All [pid] entries are lazy-initialized at the find() step.
@ -116,7 +119,7 @@ impl DirProcINode for LockedProcRootINode {
return Ok(file.this.upgrade().unwrap()); return Ok(file.this.upgrade().unwrap());
} }
if name == ".." { if name == ".." {
return Ok(file.parent.upgrade().unwrap()); return Ok(file.this.upgrade().unwrap());
} }
if let Ok(pid) = name.parse::<pid_t>() { if let Ok(pid) = name.parse::<pid_t>() {
@ -135,13 +138,61 @@ impl DirProcINode for LockedProcRootINode {
1 => Ok(String::from("..")), 1 => Ok(String::from("..")),
i => { i => {
let file = self.0.read().unwrap(); let file = self.0.read().unwrap();
if let Some(s) = file.non_volatile_entries.keys().nth(i - 2) { if let Some(name) = file.non_volatile_entries.keys().nth(i - 2) {
Ok(s.to_string()) Ok(name.to_owned())
} else { } else {
// TODO: When to iterate the process table ? let processes = get_all_processes();
Err(FsError::EntryNotFound) 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<usize> {
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)
}
} }

@ -70,14 +70,32 @@ impl DirProcINode for LockedPidDirINode {
1 => Ok(String::from("..")), 1 => Ok(String::from("..")),
i => { i => {
let file = self.0.read().unwrap(); let file = self.0.read().unwrap();
if let Some(s) = file.entries.keys().nth(i - 2) { let name = file
Ok(s.to_string()) .entries
} else { .keys()
Err(FsError::EntryNotFound) .nth(i - 2)
} .ok_or(FsError::EntryNotFound)?;
Ok(name.to_owned())
} }
} }
} }
fn iterate_entries(&self, mut ctx: &mut DirentWriterContext) -> vfs::Result<usize> {
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<ProcFdDirINode>); struct LockedProcFdDirINode(RwLock<ProcFdDirINode>);
@ -121,11 +139,47 @@ impl DirProcINode for LockedProcFdDirINode {
0 => Ok(String::from(".")), 0 => Ok(String::from(".")),
1 => Ok(String::from("..")), 1 => Ok(String::from("..")),
i => { i => {
// TODO: When to iterate the file table ? let file = self.0.read().unwrap();
Err(FsError::EntryNotFound) 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<usize> {
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); pub struct ProcCmdlineINode(ProcessRef);

@ -33,7 +33,7 @@ where
fn metadata(&self) -> vfs::Result<Metadata> { fn metadata(&self) -> vfs::Result<Metadata> {
Ok(Metadata { Ok(Metadata {
dev: 0, dev: 0,
inode: 0, inode: PROC_INO,
size: 0, size: 0,
blk_size: 0, blk_size: 0,
blocks: 0, blocks: 0,
@ -69,6 +69,10 @@ where
self.inner().get_entry(id) self.inner().get_entry(id)
} }
fn iterate_entries(&self, ctx: &mut DirentWriterContext) -> vfs::Result<usize> {
self.inner().iterate_entries(ctx)
}
fn as_any_ref(&self) -> &dyn Any { fn as_any_ref(&self) -> &dyn Any {
self self
} }

@ -29,7 +29,7 @@ where
fn metadata(&self) -> vfs::Result<Metadata> { fn metadata(&self) -> vfs::Result<Metadata> {
Ok(Metadata { Ok(Metadata {
dev: 0, dev: 0,
inode: 0, inode: PROC_INO,
size: 0, size: 0,
blk_size: 0, blk_size: 0,
blocks: 0, blocks: 0,

@ -15,6 +15,7 @@ pub trait ProcINode {
pub trait DirProcINode { pub trait DirProcINode {
fn find(&self, name: &str) -> vfs::Result<Arc<dyn INode>>; fn find(&self, name: &str) -> vfs::Result<Arc<dyn INode>>;
fn get_entry(&self, id: usize) -> vfs::Result<String>; fn get_entry(&self, id: usize) -> vfs::Result<String>;
fn iterate_entries(&self, ctx: &mut DirentWriterContext) -> vfs::Result<usize>;
} }
#[macro_export] #[macro_export]
@ -53,8 +54,65 @@ macro_rules! impl_inode_for_file_or_symlink {
Err(FsError::NotDir) Err(FsError::NotDir)
} }
fn iterate_entries(&self, ctx: &mut DirentWriterContext) -> vfs::Result<usize> {
Err(FsError::NotDir)
}
fn as_any_ref(&self) -> &dyn Any { fn as_any_ref(&self) -> &dyn Any {
self 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);
}
}
}
};
}

@ -25,7 +25,7 @@ where
fn metadata(&self) -> vfs::Result<Metadata> { fn metadata(&self) -> vfs::Result<Metadata> {
Ok(Metadata { Ok(Metadata {
dev: 0, dev: 0,
inode: 0, inode: PROC_INO,
size: 0, size: 0,
blk_size: 0, blk_size: 0,
blocks: 0, blocks: 0,

@ -6,6 +6,8 @@
#include <string.h> #include <string.h>
#include <libgen.h> #include <libgen.h>
#include <unistd.h> #include <unistd.h>
#include <dirent.h>
#include <stdbool.h>
#include "test.h" #include "test.h"
int fs_split_path(const char *path, char *dir_buf, char **dir_name, char *base_buf, 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; 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 */ #endif /* __TEST_FS_H */

@ -190,6 +190,57 @@ static int test_statfs() {
return 0; 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 // 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_meminfo),
TEST_CASE(test_read_from_proc_cpuinfo), TEST_CASE(test_read_from_proc_cpuinfo),
TEST_CASE(test_statfs), 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[]) { int main(int argc, const char *argv[]) {

@ -1,19 +1,17 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <dirent.h>
#include <stdbool.h> #include <stdbool.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include "test_fs.h" #include "test_fs.h"
// ============================================================================ // ============================================================================
// Helper function // The test case of readdir
// ============================================================================ // ============================================================================
#define NUM 9 static int test_readdir() {
static bool check_dir_entries(char entries[][256], int entry_cnt) { char expected_entries[9][NAME_MAX] = {
char *expected_entries[NUM] = {
"bin", "bin",
"dev", "dev",
"host", "host",
@ -24,52 +22,10 @@ static bool check_dir_entries(char entries[][256], int entry_cnt) {
"root", "root",
"tmp", "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;
}
// ============================================================================ if (check_readdir_with_expected_entries("/", expected_entries, 9) < 0) {
// 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)) {
THROW_ERROR("failed to check the result of readdir"); THROW_ERROR("failed to check the result of readdir");
} }
closedir(dirp);
return 0; return 0;
} }

@ -9,7 +9,6 @@
#include <stdio.h> #include <stdio.h>
#include <spawn.h> #include <spawn.h>
#include <string.h> #include <string.h>
#include <spawn.h>
#include "test.h" #include "test.h"