feature: simple ls(1p)

This commit is contained in:
Mark Poliakov 2021-11-23 17:55:58 +02:00
parent a7d89158cb
commit 564d10e1be
15 changed files with 380 additions and 82 deletions

View File

@ -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) \

View File

@ -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 {

View File

@ -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(())
}
}

View File

@ -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(())
}
}

View File

@ -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();

View File

@ -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] {

View File

@ -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 {

View 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)
}
}
}

View File

@ -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)

View File

@ -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!(),
}
}

View File

@ -12,6 +12,7 @@ pub enum SystemCall {
Ioctl = 6,
Select = 7,
Access = 8,
ReadDirectory = 9,
// Process manipulation
Fork = 32,
Clone = 33,

View File

@ -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())
)
})
}

View File

@ -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(())
}
}

View File

@ -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
View 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
}