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> {
|
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"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user