Skip to content

Commit 8168661

Browse files
committed
Added vfs::readdir syscall and fixed UB in the syscall crate
1 parent f598cb8 commit 8168661

File tree

6 files changed

+213
-16
lines changed

6 files changed

+213
-16
lines changed

kernel/src/syscall/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub enum Syscall {
3636
VfsRmdir = 19,
3737
VfsTruncate = 20,
3838
VfsStat = 21,
39+
VfsReaddir = 22,
3940
}
4041

4142
impl Syscall {
@@ -66,6 +67,7 @@ impl Syscall {
6667
19 => Some(Self::VfsRmdir),
6768
20 => Some(Self::VfsTruncate),
6869
21 => Some(Self::VfsStat),
70+
22 => Some(Self::VfsReaddir),
6971
_ => None,
7072
}
7173
}
@@ -105,6 +107,7 @@ fn syscall(id: usize, a: usize, b: usize, c: usize, d: usize, e: usize) -> isize
105107
Some(Syscall::VfsRmdir) => vfs::rmdir(a).map_err(Into::into),
106108
Some(Syscall::VfsTruncate) => vfs::truncate(a, b).map_err(Into::into),
107109
Some(Syscall::VfsStat) => vfs::stat(a, b).map_err(Into::into),
110+
Some(Syscall::VfsReaddir) => vfs::readdir(a, b).map_err(Into::into),
108111
None => Err(-1), // NoSuchSyscall,
109112
};
110113

kernel/src/syscall/vfs.rs

+127-10
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,7 @@ impl From<RmdirError> for isize {
838838
}
839839

840840
/// Truncate a file to the given length.
841-
///
841+
///
842842
/// # Errors
843843
/// See [`TruncateError`] for more details.
844844
pub fn truncate(path: usize, len: usize) -> Result<usize, TruncateError> {
@@ -922,9 +922,7 @@ impl From<user::string::FetchError> for TruncateError {
922922

923923
impl From<vfs::inode::TruncateError> for TruncateError {
924924
fn from(error: vfs::inode::TruncateError) -> Self {
925-
match error {
926-
927-
}
925+
match error {}
928926
}
929927
}
930928

@@ -950,7 +948,7 @@ pub struct Stat {
950948

951949
/// Number of hard links
952950
pub nlink: u64,
953-
951+
954952
/// Unix timestamp of the last access
955953
pub atime: Timespec,
956954

@@ -961,6 +959,10 @@ pub struct Stat {
961959
pub ctime: Timespec,
962960
}
963961

962+
/// Get information about a file.
963+
///
964+
/// # Errors
965+
/// See [`StatError`] for more details.
964966
pub fn stat(path: usize, stat: usize) -> Result<usize, StatError> {
965967
let ptr = user::Pointer::<SyscallString>::from_usize(path).ok_or(StatError::BadAddress)?;
966968
let path = user::String::from_raw_ptr(&ptr)
@@ -978,14 +980,23 @@ pub fn stat(path: usize, stat: usize) -> Result<usize, StatError> {
978980
let inode = dentry.inode();
979981
let state = inode.state.lock();
980982
let stat = Stat {
981-
dev: 0, // TODO
982-
kind: 0, // TODO
983+
dev: 0, // TODO
984+
kind: 0, // TODO
983985
ino: inode.id.0,
984986
size: state.size as u64,
985987
nlink: state.links,
986-
atime: Timespec { seconds: state.access_time.0.0, nanoseconds: 0 },
987-
ctime: Timespec { seconds: state.access_time.0.0, nanoseconds: 0 },
988-
mtime: Timespec { seconds: state.access_time.0.0, nanoseconds: 0 },
988+
atime: Timespec {
989+
seconds: state.access_time.0 .0,
990+
nanoseconds: 0,
991+
},
992+
ctime: Timespec {
993+
seconds: state.access_time.0 .0,
994+
nanoseconds: 0,
995+
},
996+
mtime: Timespec {
997+
seconds: state.access_time.0 .0,
998+
nanoseconds: 0,
999+
},
9891000
};
9901001

9911002
unsafe {
@@ -1053,3 +1064,109 @@ impl From<StatError> for isize {
10531064
-(error as isize)
10541065
}
10551066
}
1067+
1068+
#[repr(C)]
1069+
pub struct Dirent {
1070+
pub ino: u64,
1071+
pub kind: u16,
1072+
pub name_len: u16,
1073+
pub name: [u8; vfs::name::Name::MAX_LEN],
1074+
}
1075+
1076+
impl Dirent {
1077+
pub const UNKNOWN: u16 = 0;
1078+
pub const REGULAR: u16 = 1;
1079+
pub const DIRECTORY: u16 = 2;
1080+
pub const CHAR_DEVICE: u16 = 3;
1081+
pub const BLOCK_DEVICE: u16 = 4;
1082+
1083+
#[must_use]
1084+
pub const fn convert_inode_type(kind: vfs::dirent::Kind) -> u16 {
1085+
match kind {
1086+
vfs::dirent::Kind::File => Self::REGULAR,
1087+
vfs::dirent::Kind::Directory => Self::DIRECTORY,
1088+
vfs::dirent::Kind::CharDevice => Self::CHAR_DEVICE,
1089+
vfs::dirent::Kind::BlockDevice => Self::BLOCK_DEVICE,
1090+
}
1091+
}
1092+
}
1093+
1094+
1095+
/// Read a directory entry from the directory descriptor `fd` into the
1096+
/// buffer `dirent` from the current position of the directory.
1097+
///
1098+
/// # Errors
1099+
/// See [`ReaddirError`] for more details.
1100+
pub fn readdir(fd: usize, dirent: usize) -> Result<usize, ReaddirError> {
1101+
let current_task = SCHEDULER.current_task();
1102+
let file = current_task
1103+
.files()
1104+
.lock()
1105+
.get(vfs::fd::Descriptor(fd))
1106+
.ok_or(ReaddirError::InvalidFileDescriptor)?
1107+
.clone();
1108+
1109+
let ptr = user::Pointer::<Dirent>::from_usize(dirent).ok_or(ReaddirError::BadAddress)?;
1110+
1111+
// Check that the file was opened for reading
1112+
if !file.open_flags.contains(vfs::file::OpenFlags::READ) {
1113+
return Err(ReaddirError::NotReadable);
1114+
}
1115+
1116+
let dirent = file
1117+
.as_directory()
1118+
.ok_or(ReaddirError::NotADirectory)?
1119+
.readdir(&file, file.state.lock().offset)?;
1120+
1121+
file.state.lock().offset.0 += 1;
1122+
1123+
unsafe {
1124+
#[allow(clippy::cast_possible_truncation)]
1125+
user::Object::write(&ptr, &Dirent {
1126+
ino: dirent.inode.0,
1127+
kind: Dirent::convert_inode_type(dirent.kind),
1128+
name_len: dirent.name.len() as u16,
1129+
name: dirent.name.as_bytes().try_into().unwrap_or([0; 255]),
1130+
});
1131+
}
1132+
Ok(0)
1133+
}
1134+
1135+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1136+
#[repr(usize)]
1137+
pub enum ReaddirError {
1138+
/// The syscall number is invalid.
1139+
NoSuchSyscall = 1,
1140+
1141+
/// The file descriptor is invalid
1142+
InvalidFileDescriptor,
1143+
1144+
/// The path passed as an argument
1145+
BadAddress,
1146+
1147+
/// The descriptor is not a directory
1148+
NotADirectory,
1149+
1150+
/// The directory is not readable
1151+
NotReadable,
1152+
1153+
/// The directory has no entries remaning
1154+
EndOfDirectory,
1155+
1156+
/// An unknown error occurred
1157+
UnknownError,
1158+
}
1159+
1160+
impl From<vfs::file::ReaddirError> for ReaddirError {
1161+
fn from(error: vfs::file::ReaddirError) -> Self {
1162+
match error {
1163+
vfs::file::ReaddirError::EndOfDirectory => ReaddirError::EndOfDirectory,
1164+
}
1165+
}
1166+
}
1167+
1168+
impl From<ReaddirError> for isize {
1169+
fn from(error: ReaddirError) -> Self {
1170+
-(error as isize)
1171+
}
1172+
}

kernel/src/vfs/name.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
pub struct Name(String);
44

55
impl Name {
6-
const MAX_LEN: usize = 255;
6+
pub const MAX_LEN: usize = 255;
77

88
/// Creates a new name from a string.
99
///

user/shell/src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,5 @@ fn main() {
6262
println!("stat directory /test");
6363
let stat = syscall::vfs::stat("/test").expect("stat failed");
6464
println!("stat inode: {}", stat.ino);
65-
println!("stat size: {}", stat.size);
65+
println!("stat size: {}", stat.size);
6666
}

user/syscall/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ pub enum Syscall {
7979
VfsRmdir = 19,
8080
VfsTruncate = 20,
8181
VfsStat = 21,
82+
VfsReaddir = 22,
8283
}
8384

8485
pub fn syscall_return(code: usize) -> Result<usize, Errno> {

user/syscall/src/vfs.rs

+80-4
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub struct Stat {
3333

3434
/// Number of hard links
3535
pub nlink: u64,
36-
36+
3737
/// Unix timestamp of the last access
3838
pub atime: clock::Timespec,
3939

@@ -44,6 +44,22 @@ pub struct Stat {
4444
pub ctime: clock::Timespec,
4545
}
4646

47+
#[repr(C)]
48+
pub struct Dirent {
49+
pub ino: u64,
50+
pub kind: u16,
51+
pub name_len: u16,
52+
pub name: [u8; 256],
53+
}
54+
55+
impl Dirent {
56+
pub const UNKNOWN: u16 = 0;
57+
pub const REGULAR: u16 = 1;
58+
pub const DIRECTORY: u16 = 2;
59+
pub const CHAR_DEVICE: u16 = 3;
60+
pub const BLOCK_DEVICE: u16 = 4;
61+
}
62+
4763
/// Errors that can occur during the `open` syscall.
4864
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
4965
#[repr(usize)]
@@ -466,6 +482,41 @@ impl From<Errno> for StatError {
466482
}
467483
}
468484

485+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
486+
#[repr(usize)]
487+
pub enum ReaddirError {
488+
/// The syscall number is invalid.
489+
NoSuchSyscall = 1,
490+
491+
/// The file descriptor is invalid
492+
InvalidFileDescriptor,
493+
494+
/// The path passed as an argument
495+
BadAddress,
496+
497+
/// The descriptor is not a directory
498+
NotADirectory,
499+
500+
/// The directory is not readable
501+
NotReadable,
502+
503+
/// The directory has no entries remaning
504+
EndOfDirectory,
505+
506+
/// An unknown error occurred
507+
UnknownError,
508+
}
509+
510+
impl From<Errno> for ReaddirError {
511+
fn from(error: Errno) -> Self {
512+
if error.code() > -(Self::UnknownError as isize) {
513+
unsafe { core::mem::transmute(error) }
514+
} else {
515+
Self::UnknownError
516+
}
517+
}
518+
}
519+
469520
/// Open a file and return a file descriptor that can be used to refer to it.
470521
///
471522
/// # Errors
@@ -671,7 +722,7 @@ pub fn truncate(path: &str, lenght: usize) -> Result<(), TruncateError> {
671722

672723
pub fn stat(path: &str) -> Result<Stat, TruncateError> {
673724
let str = SyscallString::from(path);
674-
let stat = Stat {
725+
let mut stat = Stat {
675726
dev: 0,
676727
ino: 0,
677728
size: 0,
@@ -690,15 +741,15 @@ pub fn stat(path: &str) -> Result<Stat, TruncateError> {
690741
nanoseconds: 0,
691742
},
692743
};
693-
744+
694745
let ret;
695746

696747
unsafe {
697748
core::arch::asm!(
698749
"syscall",
699750
in("rax") Syscall::VfsStat as u64,
700751
in("rsi") &str as *const _ as u64,
701-
in("rdx") &stat as *const _ as u64,
752+
in("rdx") &mut stat as *mut _ as u64,
702753
lateout("rax") ret,
703754
);
704755
}
@@ -708,3 +759,28 @@ pub fn stat(path: &str) -> Result<Stat, TruncateError> {
708759
Ok(_) => Ok(stat),
709760
}
710761
}
762+
763+
pub fn readdir(fd: &FileDescriptor) -> Result<Dirent, ReaddirError> {
764+
let mut dirent = Dirent {
765+
ino: 0,
766+
kind: 0,
767+
name_len: 0,
768+
name: [0; 256],
769+
};
770+
let ret;
771+
772+
unsafe {
773+
core::arch::asm!(
774+
"syscall",
775+
in("rax") Syscall::VfsReaddir as u64,
776+
in("rsi") fd.0 as u64,
777+
in("rdx") &mut dirent as *mut _ as u64,
778+
lateout("rax") ret,
779+
);
780+
}
781+
782+
match syscall_return(ret) {
783+
Err(errno) => Err(ReaddirError::from(errno)),
784+
Ok(_) => Ok(dirent),
785+
}
786+
}

0 commit comments

Comments
 (0)