Add getdents support for procfs's inode
This commit is contained in:
parent
42bed8d338
commit
dc37995bf0
@ -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> {
|
||||
let entry = self.get_entry(fd)?;
|
||||
Ok(entry.file.clone())
|
||||
|
@ -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<Dir<LockedProcRootINode>>,
|
||||
@ -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<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.
|
||||
@ -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::<pid_t>() {
|
||||
@ -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<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("..")),
|
||||
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<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>);
|
||||
@ -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<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);
|
||||
|
@ -33,7 +33,7 @@ where
|
||||
fn metadata(&self) -> vfs::Result<Metadata> {
|
||||
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<usize> {
|
||||
self.inner().iterate_entries(ctx)
|
||||
}
|
||||
|
||||
fn as_any_ref(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ where
|
||||
fn metadata(&self) -> vfs::Result<Metadata> {
|
||||
Ok(Metadata {
|
||||
dev: 0,
|
||||
inode: 0,
|
||||
inode: PROC_INO,
|
||||
size: 0,
|
||||
blk_size: 0,
|
||||
blocks: 0,
|
||||
|
@ -15,6 +15,7 @@ pub trait ProcINode {
|
||||
pub trait DirProcINode {
|
||||
fn find(&self, name: &str) -> vfs::Result<Arc<dyn INode>>;
|
||||
fn get_entry(&self, id: usize) -> vfs::Result<String>;
|
||||
fn iterate_entries(&self, ctx: &mut DirentWriterContext) -> vfs::Result<usize>;
|
||||
}
|
||||
|
||||
#[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<usize> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ where
|
||||
fn metadata(&self) -> vfs::Result<Metadata> {
|
||||
Ok(Metadata {
|
||||
dev: 0,
|
||||
inode: 0,
|
||||
inode: PROC_INO,
|
||||
size: 0,
|
||||
blk_size: 0,
|
||||
blocks: 0,
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <string.h>
|
||||
#include <libgen.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <stdbool.h>
|
||||
#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 */
|
||||
|
@ -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[]) {
|
||||
|
@ -1,19 +1,17 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <dirent.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include <stdio.h>
|
||||
#include <spawn.h>
|
||||
#include <string.h>
|
||||
#include <spawn.h>
|
||||
|
||||
#include "test.h"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user