feature: simple ls(1p)
This commit is contained in:
parent
a7d89158cb
commit
564d10e1be
1
Makefile
1
Makefile
@ -96,6 +96,7 @@ initrd:
|
||||
cp target/$(ARCH)-osdev5/$(PROFILE)/init $(O)/rootfs/init
|
||||
cp target/$(ARCH)-osdev5/$(PROFILE)/shell $(O)/rootfs/bin
|
||||
cp target/$(ARCH)-osdev5/$(PROFILE)/fuzzy $(O)/rootfs/bin
|
||||
cp target/$(ARCH)-osdev5/$(PROFILE)/ls $(O)/rootfs/bin
|
||||
cd $(O)/rootfs && tar cf ../initrd.img `find -type f -printf "%P\n"`
|
||||
ifeq ($(MACH),orangepi3)
|
||||
$(MKIMAGE) \
|
||||
|
@ -94,6 +94,18 @@ fn impl_inode_fn<T: ToTokens>(name: &str, behavior: T) -> ImplItem {
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"readdir" => quote! {
|
||||
fn readdir(
|
||||
&mut self,
|
||||
_node: VnodeRef,
|
||||
_pos: usize,
|
||||
_entries: &mut [libsys::stat::DirectoryEntry]
|
||||
) ->
|
||||
Result<usize, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
_ => panic!("TODO implement {:?}", name),
|
||||
})
|
||||
}
|
||||
@ -126,6 +138,7 @@ pub fn auto_inode(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
missing.insert("size".to_string());
|
||||
missing.insert("ioctl".to_string());
|
||||
missing.insert("is_ready".to_string());
|
||||
missing.insert("readdir".to_string());
|
||||
|
||||
for item in &impl_item.items {
|
||||
match item {
|
||||
|
@ -1,6 +1,9 @@
|
||||
use crate::{BlockAllocator, Bvec, FileInode};
|
||||
use alloc::boxed::Box;
|
||||
use libsys::{error::Errno, stat::Stat};
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
stat::{DirectoryEntry, OpenFlags, Stat},
|
||||
};
|
||||
use vfs::{Vnode, VnodeImpl, VnodeKind, VnodeRef};
|
||||
|
||||
pub struct DirInode<A: BlockAllocator + Copy + 'static> {
|
||||
@ -15,7 +18,7 @@ impl<A: BlockAllocator + Copy + 'static> VnodeImpl for DirInode<A> {
|
||||
name: &str,
|
||||
kind: VnodeKind,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
let vnode = Vnode::new(name, kind, Vnode::SEEKABLE);
|
||||
let vnode = Vnode::new(name, kind, Vnode::SEEKABLE | Vnode::CACHE_READDIR);
|
||||
match kind {
|
||||
VnodeKind::Directory => vnode.set_data(Box::new(DirInode { alloc: self.alloc })),
|
||||
VnodeKind::Regular => vnode.set_data(Box::new(FileInode::new(Bvec::new(self.alloc)))),
|
||||
@ -32,7 +35,11 @@ impl<A: BlockAllocator + Copy + 'static> VnodeImpl for DirInode<A> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stat(&mut self, _at: VnodeRef, _stat: &mut Stat) -> Result<(), Errno> {
|
||||
fn stat(&mut self, node: VnodeRef, stat: &mut Stat) -> Result<(), Errno> {
|
||||
let props = node.props();
|
||||
stat.size = 0;
|
||||
stat.blksize = 4096;
|
||||
stat.mode = props.mode;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -35,10 +35,11 @@ impl<'a, A: BlockAllocator + Copy + 'static> VnodeImpl for FileInode<'a, A> {
|
||||
Ok(self.data.size())
|
||||
}
|
||||
|
||||
fn stat(&mut self, _node: VnodeRef, stat: &mut Stat) -> Result<(), Errno> {
|
||||
fn stat(&mut self, node: VnodeRef, stat: &mut Stat) -> Result<(), Errno> {
|
||||
let props = node.props();
|
||||
stat.size = self.data.size() as u64;
|
||||
stat.blksize = 4096;
|
||||
stat.mode = 0o755;
|
||||
stat.mode = props.mode;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ impl<A: BlockAllocator + Copy + 'static> Ramfs<A> {
|
||||
|
||||
fn create_node_initial(self: Rc<Self>, name: &str, tar: &Tar) -> VnodeRef {
|
||||
let kind = tar.node_kind();
|
||||
let node = Vnode::new(name, kind, Vnode::SEEKABLE);
|
||||
let node = Vnode::new(name, kind, Vnode::SEEKABLE | Vnode::CACHE_READDIR);
|
||||
node.props_mut().mode = tar.mode();
|
||||
node.set_fs(self.clone());
|
||||
match kind {
|
||||
@ -113,7 +113,7 @@ impl<A: BlockAllocator + Copy + 'static> Ramfs<A> {
|
||||
}
|
||||
|
||||
unsafe fn load_tar(self: Rc<Self>, base: *const u8, size: usize) -> Result<VnodeRef, Errno> {
|
||||
let root = Vnode::new("", VnodeKind::Directory, Vnode::SEEKABLE);
|
||||
let root = Vnode::new("", VnodeKind::Directory, Vnode::SEEKABLE | Vnode::CACHE_READDIR);
|
||||
root.set_fs(self.clone());
|
||||
root.set_data(Box::new(DirInode::new(self.alloc)));
|
||||
root.props_mut().mode = FileMode::default_dir();
|
||||
|
@ -82,7 +82,12 @@ impl Tar {
|
||||
}
|
||||
|
||||
pub fn mode(&self) -> FileMode {
|
||||
FileMode::from_bits(from_octal(&self.mode) as u32).unwrap()
|
||||
let t = match self.node_kind() {
|
||||
VnodeKind::Regular => FileMode::S_IFREG,
|
||||
VnodeKind::Directory => FileMode::S_IFDIR,
|
||||
_ => todo!()
|
||||
};
|
||||
FileMode::from_bits(from_octal(&self.mode) as u32).unwrap() | t
|
||||
}
|
||||
|
||||
pub fn data(&self) -> &[u8] {
|
||||
|
@ -1,9 +1,10 @@
|
||||
use crate::{VnodeKind, VnodeRef};
|
||||
use crate::{VnodeKind, VnodeRef, Vnode};
|
||||
use alloc::rc::Rc;
|
||||
use core::cell::RefCell;
|
||||
use core::cmp::min;
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
stat::DirectoryEntry,
|
||||
traits::{Read, Seek, SeekDir, Write},
|
||||
};
|
||||
|
||||
@ -97,6 +98,9 @@ impl File {
|
||||
/// File has to be closed on execve() calls
|
||||
pub const CLOEXEC: u32 = 1 << 2;
|
||||
|
||||
pub const POS_CACHE_DOT: usize = usize::MAX - 1;
|
||||
pub const POS_CACHE_DOT_DOT: usize = usize::MAX;
|
||||
|
||||
/// Constructs a new file handle for a regular file
|
||||
pub fn normal(vnode: VnodeRef, pos: usize, flags: u32) -> FileRef {
|
||||
Rc::new(RefCell::new(Self {
|
||||
@ -125,6 +129,60 @@ impl File {
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cache_readdir(inner: &mut NormalFile, entries: &mut [DirectoryEntry]) -> Result<usize, Errno> {
|
||||
let mut count = entries.len();
|
||||
let mut offset = 0usize;
|
||||
|
||||
if inner.pos == Self::POS_CACHE_DOT {
|
||||
if count == 0 {
|
||||
return Ok(offset);
|
||||
}
|
||||
|
||||
entries[offset] = DirectoryEntry::from_str(".");
|
||||
inner.pos = Self::POS_CACHE_DOT_DOT;
|
||||
|
||||
offset += 1;
|
||||
count -= 1;
|
||||
}
|
||||
|
||||
if inner.pos == Self::POS_CACHE_DOT_DOT {
|
||||
if count == 0 {
|
||||
return Ok(offset);
|
||||
}
|
||||
|
||||
entries[offset] = DirectoryEntry::from_str("..");
|
||||
inner.pos = 0;
|
||||
|
||||
offset += 1;
|
||||
count -= 1;
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return Ok(offset);
|
||||
}
|
||||
|
||||
let count = inner.vnode.for_each_entry(inner.pos, count, |i, e| {
|
||||
entries[offset + i] = DirectoryEntry::from_str(e.name());
|
||||
});
|
||||
inner.pos += count;
|
||||
Ok(offset + count)
|
||||
}
|
||||
|
||||
pub fn readdir(&mut self, entries: &mut [DirectoryEntry]) -> Result<usize, Errno> {
|
||||
match &mut self.inner {
|
||||
FileInner::Normal(inner) => {
|
||||
assert_eq!(inner.vnode.kind(), VnodeKind::Directory);
|
||||
|
||||
if inner.vnode.flags() & Vnode::CACHE_READDIR != 0 {
|
||||
Self::cache_readdir(inner, entries)
|
||||
} else {
|
||||
todo!();
|
||||
}
|
||||
},
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for File {
|
||||
|
@ -1,11 +1,11 @@
|
||||
use crate::{Ioctx, File, FileRef, Filesystem};
|
||||
use crate::{File, FileRef, Filesystem, Ioctx};
|
||||
use alloc::{borrow::ToOwned, boxed::Box, rc::Rc, string::String, vec::Vec};
|
||||
use core::cell::{RefCell, RefMut};
|
||||
use core::cell::{RefCell, RefMut, Ref};
|
||||
use core::fmt;
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
ioctl::IoctlCmd,
|
||||
stat::{AccessMode, FileMode, OpenFlags, Stat},
|
||||
stat::{AccessMode, DirectoryEntry, FileMode, OpenFlags, Stat},
|
||||
};
|
||||
|
||||
/// Convenience type alias for [Rc<Vnode>]
|
||||
@ -74,6 +74,13 @@ pub trait VnodeImpl {
|
||||
/// Resizes the file storage if necessary.
|
||||
fn write(&mut self, node: VnodeRef, pos: usize, data: &[u8]) -> Result<usize, Errno>;
|
||||
|
||||
fn readdir(
|
||||
&mut self,
|
||||
node: VnodeRef,
|
||||
pos: usize,
|
||||
data: &mut [DirectoryEntry],
|
||||
) -> Result<usize, Errno>;
|
||||
|
||||
/// Retrieves file status
|
||||
fn stat(&mut self, node: VnodeRef, stat: &mut Stat) -> Result<(), Errno>;
|
||||
|
||||
@ -97,6 +104,8 @@ impl Vnode {
|
||||
/// be seeked to arbitrary offsets
|
||||
pub const SEEKABLE: u32 = 1 << 0;
|
||||
|
||||
pub const CACHE_READDIR: u32 = 1 << 1;
|
||||
|
||||
/// Constructs a new [Vnode], wrapping it in [Rc]. The resulting node
|
||||
/// then needs to have [Vnode::set_data()] called on it to be usable.
|
||||
pub fn new(name: &str, kind: VnodeKind, flags: u32) -> VnodeRef {
|
||||
@ -127,6 +136,11 @@ impl Vnode {
|
||||
self.props.borrow_mut()
|
||||
}
|
||||
|
||||
/// Returns a borrowed reference to cached file properties
|
||||
pub fn props(&self) -> Ref<VnodeProps> {
|
||||
self.props.borrow()
|
||||
}
|
||||
|
||||
/// Sets an associated [VnodeImpl] for the [Vnode]
|
||||
pub fn set_data(&self, data: Box<dyn VnodeImpl>) {
|
||||
*self.data.borrow_mut() = Some(data);
|
||||
@ -163,6 +177,11 @@ impl Vnode {
|
||||
self.kind
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn flags(&self) -> u32 {
|
||||
self.flags
|
||||
}
|
||||
|
||||
// Tree operations
|
||||
|
||||
/// Attaches `child` vnode to `self` in in-memory tree. NOTE: does not
|
||||
@ -235,6 +254,29 @@ impl Vnode {
|
||||
.cloned()
|
||||
}
|
||||
|
||||
pub(crate) fn for_each_entry<F: FnMut(usize, &VnodeRef)>(
|
||||
&self,
|
||||
offset: usize,
|
||||
limit: usize,
|
||||
mut f: F,
|
||||
) -> usize {
|
||||
assert!(self.is_directory());
|
||||
let mut count = 0;
|
||||
for (index, item) in self
|
||||
.tree
|
||||
.borrow()
|
||||
.children
|
||||
.iter()
|
||||
.skip(offset)
|
||||
.take(limit)
|
||||
.enumerate()
|
||||
{
|
||||
f(index, item);
|
||||
count += 1;
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
/// Looks up a child `name` in `self`. Will first try looking up a cached
|
||||
/// vnode and will load it from disk if it's missing.
|
||||
pub fn lookup_or_load(self: &VnodeRef, name: &str) -> Result<VnodeRef, Errno> {
|
||||
@ -308,35 +350,55 @@ impl Vnode {
|
||||
|
||||
/// Opens a vnode for access
|
||||
pub fn open(self: &VnodeRef, flags: OpenFlags) -> Result<FileRef, Errno> {
|
||||
if self.kind == VnodeKind::Directory {
|
||||
return Err(Errno::IsADirectory);
|
||||
let mut open_flags = 0;
|
||||
if flags.contains(OpenFlags::O_DIRECTORY) {
|
||||
if self.kind != VnodeKind::Directory {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
if flags & OpenFlags::O_ACCESS != OpenFlags::O_RDONLY {
|
||||
return Err(Errno::IsADirectory);
|
||||
}
|
||||
|
||||
open_flags = File::READ;
|
||||
} else {
|
||||
if self.kind == VnodeKind::Directory {
|
||||
return Err(Errno::IsADirectory);
|
||||
}
|
||||
|
||||
match flags & OpenFlags::O_ACCESS {
|
||||
OpenFlags::O_RDONLY => open_flags |= File::READ,
|
||||
OpenFlags::O_WRONLY => open_flags |= File::WRITE,
|
||||
OpenFlags::O_RDWR => open_flags |= File::READ | File::WRITE,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
let mut open_flags = 0;
|
||||
match flags & OpenFlags::O_ACCESS {
|
||||
OpenFlags::O_RDONLY => open_flags |= File::READ,
|
||||
OpenFlags::O_WRONLY => open_flags |= File::WRITE,
|
||||
OpenFlags::O_RDWR => open_flags |= File::READ | File::WRITE,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
if flags.contains(OpenFlags::O_CLOEXEC) {
|
||||
open_flags |= File::CLOEXEC;
|
||||
}
|
||||
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
let pos = data.open(self.clone(), flags)?;
|
||||
Ok(File::normal(self.clone(), pos, open_flags))
|
||||
if self.kind == VnodeKind::Directory && self.flags & Vnode::CACHE_READDIR != 0 {
|
||||
Ok(File::normal(self.clone(), File::POS_CACHE_DOT, open_flags))
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
let pos = data.open(self.clone(), flags)?;
|
||||
Ok(File::normal(self.clone(), pos, open_flags))
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Closes a vnode
|
||||
pub fn close(self: &VnodeRef) -> Result<(), Errno> {
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
data.close(self.clone())
|
||||
if self.kind == VnodeKind::Directory && self.flags & Vnode::CACHE_READDIR != 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
data.close(self.clone())
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,6 +62,19 @@ pub fn struct_mut<'a, T>(base: usize) -> Result<&'a mut T, Errno> {
|
||||
Ok(unsafe { &mut *(bytes.as_mut_ptr() as *mut T) })
|
||||
}
|
||||
|
||||
pub fn struct_buf_mut<'a, T>(base: usize, count: usize) -> Result<&'a mut [T], Errno> {
|
||||
let layout = Layout::array::<T>(count).unwrap();
|
||||
if base % layout.align() != 0 {
|
||||
invalid_memory!(
|
||||
"Structure pointer is misaligned: base={:#x}, expected {:?}",
|
||||
base,
|
||||
layout
|
||||
);
|
||||
}
|
||||
let bytes = buf_mut(base, layout.size())?;
|
||||
Ok(unsafe { core::slice::from_raw_parts_mut(bytes.as_mut_ptr() as *mut T, count) })
|
||||
}
|
||||
|
||||
pub fn option_struct_ref<'a, T>(base: usize) -> Result<Option<&'a T>, Errno> {
|
||||
if base == 0 {
|
||||
Ok(None)
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
use crate::arch::{machine, platform::exception::ExceptionFrame};
|
||||
use crate::debug::Level;
|
||||
use crate::proc::{self, elf, wait, Process, ProcessIo, Thread};
|
||||
use crate::dev::timer::TimestampSource;
|
||||
use crate::proc::{self, elf, wait, Process, ProcessIo, Thread};
|
||||
use core::mem::size_of;
|
||||
use core::ops::DerefMut;
|
||||
use core::time::Duration;
|
||||
@ -13,7 +13,9 @@ use libsys::{
|
||||
ioctl::IoctlCmd,
|
||||
proc::{ExitCode, Pid},
|
||||
signal::{Signal, SignalDestination},
|
||||
stat::{FdSet, AccessMode, FileDescriptor, FileMode, OpenFlags, Stat, AT_EMPTY_PATH},
|
||||
stat::{
|
||||
AccessMode, DirectoryEntry, FdSet, FileDescriptor, FileMode, OpenFlags, Stat, AT_EMPTY_PATH,
|
||||
},
|
||||
traits::{Read, Write},
|
||||
};
|
||||
use vfs::VnodeRef;
|
||||
@ -62,7 +64,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
let buf = arg::buf_mut(args[1], args[2])?;
|
||||
|
||||
io.file(fd)?.borrow_mut().read(buf)
|
||||
},
|
||||
}
|
||||
SystemCall::Write => {
|
||||
let proc = Process::current();
|
||||
let fd = FileDescriptor::from(args[0] as u32);
|
||||
@ -70,7 +72,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
let buf = arg::buf_ref(args[1], args[2])?;
|
||||
|
||||
io.file(fd)?.borrow_mut().write(buf)
|
||||
},
|
||||
}
|
||||
SystemCall::Open => {
|
||||
let at_fd = FileDescriptor::from_i32(args[0] as i32)?;
|
||||
let path = arg::str_ref(args[1], args[2])?;
|
||||
@ -88,7 +90,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
|
||||
let file = io.ioctx().open(at, path, mode, opts)?;
|
||||
Ok(u32::from(io.place_file(file)?) as usize)
|
||||
},
|
||||
}
|
||||
SystemCall::Close => {
|
||||
let proc = Process::current();
|
||||
let mut io = proc.io.lock();
|
||||
@ -96,7 +98,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
|
||||
io.close_file(fd)?;
|
||||
Ok(0)
|
||||
},
|
||||
}
|
||||
SystemCall::FileStatus => {
|
||||
let at_fd = FileDescriptor::from_i32(args[0] as i32)?;
|
||||
let filename = arg::str_ref(args[1], args[2])?;
|
||||
@ -107,7 +109,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
let mut io = proc.io.lock();
|
||||
find_at_node(&mut io, at_fd, filename, flags & AT_EMPTY_PATH != 0)?.stat(buf)?;
|
||||
Ok(0)
|
||||
},
|
||||
}
|
||||
SystemCall::Ioctl => {
|
||||
let fd = FileDescriptor::from(args[0] as u32);
|
||||
let cmd = IoctlCmd::try_from(args[1] as u32)?;
|
||||
@ -117,7 +119,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
|
||||
let node = io.file(fd)?.borrow().node().ok_or(Errno::InvalidFile)?;
|
||||
node.ioctl(cmd, args[2], args[3])
|
||||
},
|
||||
}
|
||||
SystemCall::Select => {
|
||||
let rfds = arg::option_struct_mut::<FdSet>(args[0])?;
|
||||
let wfds = arg::option_struct_mut::<FdSet>(args[1])?;
|
||||
@ -128,7 +130,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
};
|
||||
|
||||
wait::select(Thread::current(), rfds, wfds, timeout)
|
||||
},
|
||||
}
|
||||
SystemCall::Access => {
|
||||
let at_fd = FileDescriptor::from_i32(args[0] as i32)?;
|
||||
let path = arg::str_ref(args[1], args[2])?;
|
||||
@ -138,9 +140,18 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
let proc = Process::current();
|
||||
let mut io = proc.io.lock();
|
||||
|
||||
find_at_node(&mut io, at_fd, path, flags & AT_EMPTY_PATH != 0)?.check_access(io.ioctx(), mode)?;
|
||||
find_at_node(&mut io, at_fd, path, flags & AT_EMPTY_PATH != 0)?
|
||||
.check_access(io.ioctx(), mode)?;
|
||||
Ok(0)
|
||||
},
|
||||
}
|
||||
SystemCall::ReadDirectory => {
|
||||
let proc = Process::current();
|
||||
let fd = FileDescriptor::from(args[0] as u32);
|
||||
let mut io = proc.io.lock();
|
||||
let buf = arg::struct_buf_mut::<DirectoryEntry>(args[1], args[2])?;
|
||||
|
||||
io.file(fd)?.borrow_mut().readdir(buf)
|
||||
}
|
||||
|
||||
// Process
|
||||
SystemCall::Clone => {
|
||||
@ -151,7 +162,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
Process::current()
|
||||
.new_user_thread(entry, stack, arg)
|
||||
.map(|e| e as usize)
|
||||
},
|
||||
}
|
||||
SystemCall::Exec => {
|
||||
let node = {
|
||||
let proc = Process::current();
|
||||
@ -165,7 +176,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
let file = node.open(OpenFlags::O_RDONLY)?;
|
||||
Process::execve(move |space| elf::load_elf(space, file), 0).unwrap();
|
||||
panic!();
|
||||
},
|
||||
}
|
||||
SystemCall::Exit => {
|
||||
let status = ExitCode::from(args[0] as i32);
|
||||
let flags = args[1];
|
||||
@ -177,7 +188,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
}
|
||||
|
||||
unreachable!();
|
||||
},
|
||||
}
|
||||
SystemCall::WaitPid => {
|
||||
// TODO special "pid" values
|
||||
let pid = unsafe { Pid::from_raw(args[0] as u32) };
|
||||
@ -190,17 +201,15 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
}
|
||||
e => e.map(|e| i32::from(e) as usize),
|
||||
}
|
||||
},
|
||||
}
|
||||
SystemCall::WaitTid => {
|
||||
let tid = args[0] as u32;
|
||||
|
||||
match Thread::waittid(tid) {
|
||||
Ok(_) => {
|
||||
Ok(0)
|
||||
},
|
||||
Ok(_) => Ok(0),
|
||||
_ => todo!(),
|
||||
}
|
||||
},
|
||||
}
|
||||
SystemCall::GetPid => Ok(Process::current().id().value() as usize),
|
||||
SystemCall::GetTid => Ok(Thread::current().id() as usize),
|
||||
SystemCall::Sleep => {
|
||||
@ -214,15 +223,15 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
}
|
||||
}
|
||||
res.map(|_| 0)
|
||||
},
|
||||
}
|
||||
SystemCall::SetSignalEntry => {
|
||||
Thread::current().set_signal_entry(args[0], args[1]);
|
||||
Ok(0)
|
||||
},
|
||||
}
|
||||
SystemCall::SignalReturn => {
|
||||
Thread::current().return_from_signal();
|
||||
unreachable!();
|
||||
},
|
||||
}
|
||||
SystemCall::SendSignal => {
|
||||
let target = SignalDestination::from(args[0] as isize);
|
||||
let signal = Signal::try_from(args[1] as u32)?;
|
||||
@ -235,11 +244,11 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
_ => todo!(),
|
||||
};
|
||||
Ok(0)
|
||||
},
|
||||
}
|
||||
SystemCall::Yield => {
|
||||
proc::switch();
|
||||
Ok(0)
|
||||
},
|
||||
}
|
||||
SystemCall::GetSid => {
|
||||
// TODO handle kernel processes here?
|
||||
let pid = args[0] as u32;
|
||||
@ -250,13 +259,13 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
let pid = unsafe { Pid::from_raw(pid) };
|
||||
let proc = Process::get(pid).ok_or(Errno::DoesNotExist)?;
|
||||
if proc.sid() != current.sid() {
|
||||
return Err(Errno::PermissionDenied)
|
||||
return Err(Errno::PermissionDenied);
|
||||
}
|
||||
proc
|
||||
};
|
||||
|
||||
Ok(proc.sid().value() as usize)
|
||||
},
|
||||
}
|
||||
SystemCall::GetPgid => {
|
||||
// TODO handle kernel processes here?
|
||||
let pid = args[0] as u32;
|
||||
@ -269,10 +278,8 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
};
|
||||
|
||||
Ok(proc.pgid().value() as usize)
|
||||
},
|
||||
SystemCall::GetPpid => {
|
||||
Ok(Process::current().ppid().unwrap().value() as usize)
|
||||
},
|
||||
}
|
||||
SystemCall::GetPpid => Ok(Process::current().ppid().unwrap().value() as usize),
|
||||
SystemCall::SetSid => {
|
||||
let proc = Process::current();
|
||||
let mut io = proc.io.lock();
|
||||
@ -282,17 +289,13 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
}
|
||||
|
||||
todo!();
|
||||
},
|
||||
}
|
||||
SystemCall::SetPgid => {
|
||||
let pid = args[0] as u32;
|
||||
let pgid = args[1] as u32;
|
||||
|
||||
let current = Process::current();
|
||||
let proc = if pid == 0 {
|
||||
current
|
||||
} else {
|
||||
todo!()
|
||||
};
|
||||
let proc = if pid == 0 { current } else { todo!() };
|
||||
|
||||
if pgid == 0 {
|
||||
proc.set_pgid(proc.id());
|
||||
@ -301,13 +304,13 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
}
|
||||
|
||||
Ok(proc.pgid().value() as usize)
|
||||
},
|
||||
}
|
||||
|
||||
// System
|
||||
SystemCall::GetCpuTime => {
|
||||
let time = machine::local_timer().timestamp()?;
|
||||
Ok(time.as_nanos() as usize)
|
||||
},
|
||||
}
|
||||
|
||||
// Debugging
|
||||
SystemCall::DebugTrace => {
|
||||
@ -316,9 +319,9 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
print!(Level::Debug, "{}", buf);
|
||||
println!(Level::Debug, "");
|
||||
Ok(args[1])
|
||||
},
|
||||
}
|
||||
|
||||
// Handled elsewhere
|
||||
SystemCall::Fork => unreachable!()
|
||||
SystemCall::Fork => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ pub enum SystemCall {
|
||||
Ioctl = 6,
|
||||
Select = 7,
|
||||
Access = 8,
|
||||
ReadDirectory = 9,
|
||||
// Process manipulation
|
||||
Fork = 32,
|
||||
Clone = 33,
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
ioctl::IoctlCmd,
|
||||
proc::{ExitCode, Pid},
|
||||
signal::{Signal, SignalDestination},
|
||||
stat::{AccessMode, FdSet, FileDescriptor, FileMode, OpenFlags, Stat},
|
||||
stat::{AccessMode, DirectoryEntry, FdSet, FileDescriptor, FileMode, OpenFlags, Stat},
|
||||
};
|
||||
use core::time::Duration;
|
||||
|
||||
@ -366,6 +366,20 @@ pub fn sys_getpgid(pid: Pid) -> Result<Pid, Errno> {
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sys_setpgid(pid: Pid, pgid: Pid) -> Result<Pid, Errno> {
|
||||
Errno::from_syscall(unsafe { syscall!(SystemCall::SetPgid, argn!(pid.value()), argn!(pgid.value())) }).map(|e| unsafe { Pid::from_raw(e as u32) })
|
||||
Errno::from_syscall(unsafe {
|
||||
syscall!(SystemCall::SetPgid, argn!(pid.value()), argn!(pgid.value()))
|
||||
})
|
||||
.map(|e| unsafe { Pid::from_raw(e as u32) })
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sys_readdir(fd: FileDescriptor, buf: &mut [DirectoryEntry]) -> Result<usize, Errno> {
|
||||
Errno::from_syscall(unsafe {
|
||||
syscall!(
|
||||
SystemCall::ReadDirectory,
|
||||
argn!(u32::from(fd)),
|
||||
argp!(buf.as_mut_ptr()),
|
||||
argn!(buf.len())
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use core::fmt;
|
||||
use crate::error::Errno;
|
||||
use core::fmt;
|
||||
|
||||
const AT_FDCWD: i32 = -2;
|
||||
pub const AT_EMPTY_PATH: u32 = 1 << 16;
|
||||
@ -14,11 +14,16 @@ bitflags! {
|
||||
const O_CREAT = 1 << 4;
|
||||
const O_EXEC = 1 << 5;
|
||||
const O_CLOEXEC = 1 << 6;
|
||||
const O_DIRECTORY = 1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct FileMode: u32 {
|
||||
const FILE_TYPE = 0xF << 12;
|
||||
const S_IFREG = 0x8 << 12;
|
||||
const S_IFDIR = 0x4 << 12;
|
||||
|
||||
const USER_READ = 1 << 8;
|
||||
const USER_WRITE = 1 << 7;
|
||||
const USER_EXEC = 1 << 6;
|
||||
@ -42,26 +47,57 @@ bitflags! {
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FdSet {
|
||||
bits: [u64; 2]
|
||||
bits: [u64; 2],
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct FileDescriptor(u32);
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct DirectoryEntry {
|
||||
name: [u8; 64],
|
||||
}
|
||||
|
||||
struct FdSetIter<'a> {
|
||||
idx: u32,
|
||||
set: &'a FdSet
|
||||
set: &'a FdSet,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[repr(C)]
|
||||
pub struct Stat {
|
||||
pub mode: u32,
|
||||
pub mode: FileMode,
|
||||
pub size: u64,
|
||||
pub blksize: u32,
|
||||
}
|
||||
|
||||
impl DirectoryEntry {
|
||||
pub const fn empty() -> Self {
|
||||
Self { name: [0; 64] }
|
||||
}
|
||||
|
||||
pub fn from_str(i: &str) -> DirectoryEntry {
|
||||
let mut res = DirectoryEntry { name: [0; 64] };
|
||||
let bytes = i.as_bytes();
|
||||
res.name[..bytes.len()].copy_from_slice(bytes);
|
||||
res
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
let zero = self.name.iter().position(|&c| c == 0).unwrap();
|
||||
core::str::from_utf8(&self.name[..zero]).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for DirectoryEntry {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("DirectoryEntry")
|
||||
.field("name", &self.as_str())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl FdSet {
|
||||
pub const fn empty() -> Self {
|
||||
Self { bits: [0; 2] }
|
||||
@ -93,10 +129,7 @@ impl FdSet {
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = FileDescriptor> + '_ {
|
||||
FdSetIter {
|
||||
idx: 0,
|
||||
set: self
|
||||
}
|
||||
FdSetIter { idx: 0, set: self }
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,13 +164,51 @@ impl fmt::Debug for FdSet {
|
||||
|
||||
impl FileMode {
|
||||
/// Returns default permission set for directories
|
||||
pub const fn default_dir() -> Self {
|
||||
unsafe { Self::from_bits_unchecked(0o755) }
|
||||
pub fn default_dir() -> Self {
|
||||
unsafe { Self::from_bits_unchecked(0o755) | Self::S_IFDIR }
|
||||
}
|
||||
|
||||
/// Returns default permission set for regular files
|
||||
pub const fn default_reg() -> Self {
|
||||
unsafe { Self::from_bits_unchecked(0o644) }
|
||||
pub fn default_reg() -> Self {
|
||||
unsafe { Self::from_bits_unchecked(0o644) | Self::S_IFREG }
|
||||
}
|
||||
}
|
||||
|
||||
fn choose<T>(q: bool, a: T, b: T) -> T {
|
||||
if q { a } else { b }
|
||||
}
|
||||
|
||||
impl Default for FileMode {
|
||||
fn default() -> Self {
|
||||
unsafe { Self::from_bits_unchecked(0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FileMode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}{}{}{}{}{}{}{}{}{}",
|
||||
// File type
|
||||
match *self & Self::FILE_TYPE {
|
||||
Self::S_IFDIR => 'd',
|
||||
Self::S_IFREG => '-',
|
||||
_ => '?'
|
||||
},
|
||||
// User
|
||||
choose(self.contains(Self::USER_READ), 'r', '-'),
|
||||
choose(self.contains(Self::USER_WRITE), 'w', '-'),
|
||||
choose(self.contains(Self::USER_EXEC), 'x', '-'),
|
||||
// Group
|
||||
choose(self.contains(Self::GROUP_READ), 'r', '-'),
|
||||
choose(self.contains(Self::GROUP_WRITE), 'w', '-'),
|
||||
choose(self.contains(Self::GROUP_EXEC), 'x', '-'),
|
||||
// Other
|
||||
choose(self.contains(Self::OTHER_READ), 'r', '-'),
|
||||
choose(self.contains(Self::OTHER_WRITE), 'w', '-'),
|
||||
choose(self.contains(Self::OTHER_EXEC), 'x', '-'),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,10 @@ path = "src/shell/main.rs"
|
||||
name = "fuzzy"
|
||||
path = "src/fuzzy/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "ls"
|
||||
path = "src/ls/main.rs"
|
||||
|
||||
[dependencies]
|
||||
libusr = { path = "../libusr" }
|
||||
lazy_static = { version = "*", features = ["spin_no_std"] }
|
||||
|
45
user/src/ls/main.rs
Normal file
45
user/src/ls/main.rs
Normal file
@ -0,0 +1,45 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate libusr;
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
|
||||
use libusr::sys::{sys_readdir, sys_openat, sys_close, sys_fstatat, stat::{FileMode, OpenFlags, DirectoryEntry, Stat}};
|
||||
use alloc::{string::String, borrow::ToOwned};
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
let mut buffer = [DirectoryEntry::empty(); 16];
|
||||
let mut stat = Stat::default();
|
||||
let mut data = vec![];
|
||||
|
||||
let fd = sys_openat(None, "/", FileMode::default_dir(), OpenFlags::O_DIRECTORY | OpenFlags::O_RDONLY).unwrap();
|
||||
|
||||
loop {
|
||||
let count = sys_readdir(fd, &mut buffer).unwrap();
|
||||
if count == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
buffer.iter().take(count).for_each(|e| data.push(e.as_str().to_owned()));
|
||||
}
|
||||
|
||||
data.sort();
|
||||
|
||||
data.iter().for_each(|item| {
|
||||
let stat = sys_fstatat(Some(fd), item, &mut stat, 0).map(|_| &stat);
|
||||
if let Ok(stat) = stat {
|
||||
print!("{} ", stat.mode);
|
||||
} else {
|
||||
print!("?????????? ");
|
||||
}
|
||||
println!("{}", item);
|
||||
});
|
||||
|
||||
sys_close(fd).unwrap();
|
||||
|
||||
|
||||
0
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user