handle directories

This commit is contained in:
husky 2025-09-09 09:17:42 -07:00
parent e004212c9c
commit 960de83bbc
8 changed files with 225 additions and 27 deletions

View file

@ -10,5 +10,12 @@ pub struct TaskSetup {
pub epc: usize, pub epc: usize,
pub ddi_first_addr: usize, pub ddi_first_addr: usize,
pub ddi_size: usize, pub ddi_size: usize,
pub environment: usize,
pub wait_for_task_exit: bool, pub wait_for_task_exit: bool,
} }
#[repr(C)]
pub struct Environment {
pub current_directory_path: usize,
pub current_directory_path_len: usize,
}

View file

@ -65,6 +65,8 @@ pub enum SysCall {
/// waits for a sent notification to be received /// waits for a sent notification to be received
/// (taskid) -> 0 /// (taskid) -> 0
WaitForNotifAck = 13, WaitForNotifAck = 13,
/// returns the environment pointer for the given task
EnvironmentPointer = 14,
/// initializes kernel, ONLY CALL ONCE! IN FACT, YOU PROBABLY NEVER NEED TO CALL THIS /// initializes kernel, ONLY CALL ONCE! IN FACT, YOU PROBABLY NEVER NEED TO CALL THIS
InitKernel = 666 InitKernel = 666
@ -90,6 +92,7 @@ pub fn usize2sc(u: usize) -> SysCall {
11 => SysCall::WaitForNotification, 11 => SysCall::WaitForNotification,
12 => SysCall::PendingNotifications, 12 => SysCall::PendingNotifications,
13 => SysCall::WaitForNotifAck, 13 => SysCall::WaitForNotifAck,
14 => SysCall::EnvironmentPointer,
666 => SysCall::InitKernel, 666 => SysCall::InitKernel,
_ => SysCall::NoAction, _ => SysCall::NoAction,
} }
@ -159,6 +162,10 @@ pub fn wait_for_notif_ack(taskid: u8) {
syscall(SysCall::WaitForNotifAck, taskid as usize, 0, 0, 0, 0, 0); syscall(SysCall::WaitForNotifAck, taskid as usize, 0, 0, 0, 0, 0);
} }
pub fn environment_pointer() -> usize {
syscall(SysCall::EnvironmentPointer, 0, 0, 0, 0, 0, 0)
}
pub fn init_kernel() { pub fn init_kernel() {
syscall(SysCall::InitKernel, 0, 0, 0, 0, 0, 0); syscall(SysCall::InitKernel, 0, 0, 0, 0, 0, 0);
} }

View file

@ -85,6 +85,7 @@ extern "C" fn _virt_init() -> ! {
epc: ktask as usize, epc: ktask as usize,
ddi_first_addr: 0, ddi_first_addr: 0,
ddi_size: 0, ddi_size: 0,
environment: 0,
wait_for_task_exit: false, wait_for_task_exit: false,
}); });

View file

@ -176,6 +176,10 @@ pub fn reader_from_cluster(bpb: &BPBUseful, cluster: usize) -> Fat32FileReader {
} }
} }
pub fn root_target(bpb: &BPBUseful) -> u32 {
bpb.root_cluster as u32
}
pub fn root_reader(bpb: &BPBUseful) -> Fat32DirectoryReader { pub fn root_reader(bpb: &BPBUseful) -> Fat32DirectoryReader {
let cluster = bpb.root_cluster as usize; let cluster = bpb.root_cluster as usize;
Fat32DirectoryReader { Fat32DirectoryReader {

View file

@ -38,6 +38,13 @@ pub fn open_fs(fs: &mut FileSystem) -> bool {
} }
} }
pub fn root_target(fs: &FileSystem) -> u32 {
#[cfg(feature = "fs_fat32")]
{
fat32::root_target(fs)
}
}
pub fn root_reader(fs: &FileSystem) -> DirectoryReader { pub fn root_reader(fs: &FileSystem) -> DirectoryReader {
#[cfg(feature = "fs_fat32")] #[cfg(feature = "fs_fat32")]
{ {
@ -94,8 +101,8 @@ pub fn read_one_record(fs: &FileSystem, reader: &mut DirectoryReader, record: &m
} }
} }
pub fn open_directory(fs: &FileSystem, path: &mut [u8], reader: &mut DirectoryReader) -> usize { pub fn open_directory(fs: &FileSystem, path: &[u8], reader: &mut DirectoryReader) -> usize {
path.make_ascii_uppercase(); //path.make_ascii_uppercase();
if path[0] != b'/' { if path[0] != b'/' {
return 3; return 3;
} }
@ -104,6 +111,10 @@ pub fn open_directory(fs: &FileSystem, path: &mut [u8], reader: &mut DirectoryRe
*reader = root_reader; *reader = root_reader;
0 0
} else { } else {
let mut target_stack = [0u32; 128]; // todo: make this actually dynamic
target_stack[0] = root_target(fs);
let mut target_stack_idx = 1;
let mut record = Record { let mut record = Record {
name: [0; 12], name: [0; 12],
record_type: 0, record_type: 0,
@ -111,28 +122,67 @@ pub fn open_directory(fs: &FileSystem, path: &mut [u8], reader: &mut DirectoryRe
total_size_bytes: 0, total_size_bytes: 0,
}; };
let mut current_reader = root_reader; let mut current_reader = root_reader;
let mut next_path_start = path[1..].iter().position(|&x| x == b'/').unwrap_or(path.len()); let mut next_path_start = path[1..].iter().position(|&x| x == b'/').unwrap_or(path.len()) + 1;
let mut search_for = &path[1..next_path_start]; let mut search_for = &path[1..next_path_start];
loop { 'into_next_dir: loop {
if !read_one_record(fs, &mut current_reader, &mut record) { if search_for == b"." {
return 1; // do nothing and skip this part of the path
let new_path_start = next_path_start + 1;
if new_path_start >= path.len() {
// we're at the end of the path
*reader = current_reader;
return 0;
}
next_path_start = path[new_path_start..].iter().position(|&x| x == b'/').unwrap_or(path.len()) + new_path_start;
search_for = &path[new_path_start..next_path_start];
target_stack[target_stack_idx] = record.target;
target_stack_idx += 1;
continue 'into_next_dir;
} else if search_for == b".." {
// go back up
if target_stack_idx < 2 {
directory_reader(fs, &mut current_reader, target_stack[0]);
*reader = current_reader;
return 0;
}
target_stack_idx -= 2; // skip the current directory and the one before that
let prev_target = target_stack[target_stack_idx];
let new_path_start = next_path_start + 1;
if new_path_start >= path.len() {
// we're at the end of the path
directory_reader(fs, &mut current_reader, prev_target);
*reader = current_reader;
return 0;
}
next_path_start = path[new_path_start..].iter().position(|&x| x == b'/').unwrap_or(path.len()) + new_path_start;
search_for = &path[new_path_start..next_path_start];
directory_reader(fs, &mut current_reader, prev_target);
continue 'into_next_dir;
} }
let record_name = &record.name[..record.name.iter().position(|&x| x == 0).unwrap_or(11)]; loop {
if record_name == search_for { if !read_one_record(fs, &mut current_reader, &mut record) {
// this is the next record we want return 1;
if record.record_type == RecordType::Directory as u8 { }
let new_path_start = next_path_start + 1; // skip the slash let record_name = &record.name[..record.name.iter().position(|&x| x == 0).unwrap_or(11)];
if new_path_start >= path.len() { if record_name == search_for {
// we're at the end of the path // this is the next record we want
if record.record_type == RecordType::Directory as u8 {
let new_path_start = next_path_start + 1; // skip the slash
if new_path_start >= path.len() {
// we're at the end of the path
directory_reader(fs, &mut current_reader, record.target);
*reader = current_reader;
return 0;
}
next_path_start = path[new_path_start..].iter().position(|&x| x == b'/').unwrap_or(path.len()) + new_path_start;
search_for = &path[new_path_start..next_path_start];
directory_reader(fs, &mut current_reader, record.target); directory_reader(fs, &mut current_reader, record.target);
*reader = current_reader; target_stack[target_stack_idx] = record.target;
return 0; target_stack_idx += 1;
continue 'into_next_dir;
} else {
return 2;
} }
next_path_start = path[new_path_start..].iter().position(|&x| x == b'/').unwrap_or(path.len()) + new_path_start;
search_for = &path[next_path_start..next_path_start];
directory_reader(fs, &mut current_reader, record.target);
} else {
return 2;
} }
} }
} }

View file

@ -110,6 +110,7 @@ pub extern "C" fn ktask() -> ! {
epc: entrypoint, epc: entrypoint,
ddi_first_addr: first_addr, ddi_first_addr: first_addr,
ddi_size: size, ddi_size: size,
environment: 0,
wait_for_task_exit: false, wait_for_task_exit: false,
}); });
} else { } else {

View file

@ -84,6 +84,7 @@ pub fn handle_syscall(
#[cfg(feature = "arch_virt")] #[cfg(feature = "arch_virt")]
let t = Some(Task { let t = Some(Task {
epc: unsafe { (*add).epc }, epc: unsafe { (*add).epc },
environment: unsafe { (*add).environment },
want_exit: false, want_exit: false,
sp, sp,
wait: 0, wait: 0,
@ -319,6 +320,14 @@ pub fn handle_syscall(
} }
0 0
} }
SysCall::EnvironmentPointer => {
let ci = tc.current;
if let Some(task) = tc.tasks[ci].as_ref() {
task.environment
} else {
0
}
}
}, },
suspend, suspend,
) )
@ -413,6 +422,7 @@ pub struct TrafficControl {
pub struct Task { pub struct Task {
#[cfg(feature = "arch_virt")] #[cfg(feature = "arch_virt")]
pub epc: usize, pub epc: usize,
pub environment: usize,
pub want_exit: bool, pub want_exit: bool,
/// THE ORIGINAL STACK POINTER THAT THE TASK STARTED WITH /// THE ORIGINAL STACK POINTER THAT THE TASK STARTED WITH
pub sp: usize, pub sp: usize,

View file

@ -1,11 +1,17 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
use core::sync::atomic::{AtomicBool, Ordering}; use core::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering};
use crate::terminal::{print, println}; use crate::terminal::{print, println};
use liblbos::fs::{DirectoryReader, FileSystem}; use liblbos::fs::{DirectoryReader, FileSystem};
static NO_EXIT: AtomicBool = AtomicBool::new(false); static NO_EXIT: AtomicBool = AtomicBool::new(false);
static CURRENT_DIR_PATH: AtomicPtr<u8> = AtomicPtr::new(0 as *mut _);
static CURRENT_DIR_PATH_LEN: AtomicUsize = AtomicUsize::new(0);
struct Environment<'a> {
pub current_directory_path: &'a [u8],
}
#[panic_handler] #[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! { fn panic(info: &core::panic::PanicInfo) -> ! {
@ -13,14 +19,83 @@ fn panic(info: &core::panic::PanicInfo) -> ! {
loop {} loop {}
} }
fn lsdir() { fn cd(new_path: &str, environment: &mut Environment<'_>) {
let old_path = environment.current_directory_path;
let abs_new_path = if new_path.starts_with('/') {
// absolute path, replaces old path
let new_path_buf = liblbos::syscalls::alloc_blocks(new_path.len().div_ceil(512));
let new_path_buf = unsafe { core::slice::from_raw_parts_mut(new_path_buf as *mut _, new_path.len()) };
// copy
{
for (i, &x) in new_path.as_bytes().iter().enumerate() {
new_path_buf[i] = x;
}
}
new_path_buf
} else {
// todo: ".." is currently handled on the ktask level, however...
// todo: we should evaluate this and instead modify the absolute path to remove the ".."
// todo: otherwise, we're bound to end up with a path like "/a/b/../../c" which is not nice
// relative path, append
let new_path_len = old_path.len() + new_path.len() + 1;
let new_path_buf = liblbos::syscalls::alloc_blocks(new_path_len.div_ceil(512));
let new_path_buf = unsafe { core::slice::from_raw_parts_mut(new_path_buf as *mut _, new_path_len) };
// copy old path
{
for (i, &x) in old_path.iter().enumerate() {
new_path_buf[i] = x;
}
}
// copy new path
{
for (i, &x) in new_path.as_bytes().iter().enumerate() {
new_path_buf[old_path.len() + i] = x;
}
}
// add separator
new_path_buf[old_path.len() + new_path.len()] = b'/';
new_path_buf
};
// check to make sure the new path is valid
{
let mut fs = FileSystem::empty();
if liblbos::ktask::ktask_fsopen(&mut fs) != 0 {
println("failed to open fs");
// free
liblbos::syscalls::free_blocks(abs_new_path.as_ptr() as usize, abs_new_path.len().div_ceil(512));
return;
}
let mut dir = DirectoryReader::empty();
if liblbos::ktask::ktask_fsopendir(&fs, &mut dir, abs_new_path) != 0 {
println("invalid dir");
// free
liblbos::syscalls::free_blocks(abs_new_path.as_ptr() as usize, abs_new_path.len().div_ceil(512));
return;
}
}
// it is valid, update everything
{
// free old path
liblbos::syscalls::free_blocks(CURRENT_DIR_PATH.load(Ordering::Relaxed) as usize, CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed).div_ceil(512));
}
CURRENT_DIR_PATH.store(abs_new_path.as_ptr() as *mut _, Ordering::Relaxed);
CURRENT_DIR_PATH_LEN.store(abs_new_path.len(), Ordering::Relaxed);
environment.current_directory_path = unsafe {
core::slice::from_raw_parts_mut(CURRENT_DIR_PATH.load(Ordering::Relaxed) as *mut _, CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed))
};
}
fn lsdir(env: &Environment<'_>) {
let mut fs = FileSystem::empty(); let mut fs = FileSystem::empty();
if liblbos::ktask::ktask_fsopen(&mut fs) != 0 { if liblbos::ktask::ktask_fsopen(&mut fs) != 0 {
println("failed to open fs"); println("failed to open fs");
return; return;
} }
let mut dir = DirectoryReader::empty(); let mut dir = DirectoryReader::empty();
if liblbos::ktask::ktask_fsopendir(&fs, &mut dir, b"/") != 0 { if liblbos::ktask::ktask_fsopendir(&fs, &mut dir, env.current_directory_path) != 0 {
println("failed to open dir"); println("failed to open dir");
return; return;
} }
@ -107,6 +182,7 @@ fn attempt_run_file(name: &str) {
epc: 0, epc: 0,
ddi_first_addr: 0, ddi_first_addr: 0,
ddi_size: 0, ddi_size: 0,
environment: 0,
wait_for_task_exit: true, wait_for_task_exit: true,
}; };
@ -118,19 +194,26 @@ fn attempt_run_file(name: &str) {
liblbos::syscalls::free_blocks(buf, size.div_ceil(512)); liblbos::syscalls::free_blocks(buf, size.div_ceil(512));
let task_id = liblbos::syscalls::create_task(task_setup); liblbos::syscalls::create_task(task_setup);
println("\ntask exited\n"); println("\ntask exited\n");
} }
fn execute(cmd: &str) { fn execute(cmd: &str, environment: &mut Environment<'_>) {
print("\n"); print("\n");
let mut split = cmd.split_ascii_whitespace(); let mut split = cmd.split_ascii_whitespace();
if let Some(cmd) = split.next() { if let Some(cmd) = split.next() {
match cmd { match cmd {
"ls" => { "ls" => {
lsdir(); lsdir(environment);
}
"cd" => {
if let Some(path) = split.next() {
cd(path, environment);
} else {
println("cd requires a path");
}
} }
"exit" => { "exit" => {
if NO_EXIT.load(Ordering::Relaxed) { if NO_EXIT.load(Ordering::Relaxed) {
@ -168,6 +251,40 @@ extern "C" fn main() {
let tid = liblbos::syscalls::current_task(); let tid = liblbos::syscalls::current_task();
NO_EXIT.store(tid == 1, Ordering::Relaxed); NO_EXIT.store(tid == 1, Ordering::Relaxed);
let environment = liblbos::syscalls::environment_pointer();
let mut environment = if environment == 0 {
// no environment, construct it ourself
static DEFAULT_CURRENT_DIRECTORY: &[u8] = b"/";
let environment = Environment {
current_directory_path: DEFAULT_CURRENT_DIRECTORY,
};
environment
} else {
let env = unsafe { &mut *(environment as *mut liblbos::Environment) };
let current_directory_path = unsafe { core::slice::from_raw_parts(env.current_directory_path as *const u8, env.current_directory_path_len) };
let environment = Environment {
current_directory_path,
};
environment
};
// replace the current path with one that we allocate ourself, so that we can modify it later
{
let path = environment.current_directory_path;
let len = environment.current_directory_path.len();
CURRENT_DIR_PATH.store(liblbos::syscalls::alloc_blocks(len.div_ceil(512)) as *mut u8, Ordering::Relaxed);
CURRENT_DIR_PATH_LEN.store(len, Ordering::Relaxed);
{
// copy
let dest = unsafe {
core::slice::from_raw_parts_mut(CURRENT_DIR_PATH.load(Ordering::Relaxed) as *mut _, len)
};
for (i, &x) in path.iter().enumerate() {
dest[i] = x;
}
}
environment.current_directory_path = unsafe { core::slice::from_raw_parts_mut(CURRENT_DIR_PATH.load(Ordering::Relaxed) as *mut u8, CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed)) };
}
print("\n\n"); print("\n\n");
print("turntable v"); print("turntable v");
const VERSION: &str = env!("CARGO_PKG_VERSION"); const VERSION: &str = env!("CARGO_PKG_VERSION");
@ -198,7 +315,8 @@ extern "C" fn main() {
if *c == b'\r' { if *c == b'\r' {
execute(unsafe { execute(unsafe {
core::str::from_utf8_unchecked(&cmdbuf[0..cmdbuf_len - 1]) core::str::from_utf8_unchecked(&cmdbuf[0..cmdbuf_len - 1])
}); },
&mut environment);
cmdbuf_len = 0; cmdbuf_len = 0;
print("> "); print("> ");
break; break;