diff --git a/liblbos/src/lib.rs b/liblbos/src/lib.rs index 4ec169c..13c5a0d 100644 --- a/liblbos/src/lib.rs +++ b/liblbos/src/lib.rs @@ -10,5 +10,12 @@ pub struct TaskSetup { pub epc: usize, pub ddi_first_addr: usize, pub ddi_size: usize, + pub environment: usize, pub wait_for_task_exit: bool, } + +#[repr(C)] +pub struct Environment { + pub current_directory_path: usize, + pub current_directory_path_len: usize, +} \ No newline at end of file diff --git a/liblbos/src/syscalls.rs b/liblbos/src/syscalls.rs index 9c9f772..c8c3210 100644 --- a/liblbos/src/syscalls.rs +++ b/liblbos/src/syscalls.rs @@ -65,6 +65,8 @@ pub enum SysCall { /// waits for a sent notification to be received /// (taskid) -> 0 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 InitKernel = 666 @@ -90,6 +92,7 @@ pub fn usize2sc(u: usize) -> SysCall { 11 => SysCall::WaitForNotification, 12 => SysCall::PendingNotifications, 13 => SysCall::WaitForNotifAck, + 14 => SysCall::EnvironmentPointer, 666 => SysCall::InitKernel, _ => 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); } +pub fn environment_pointer() -> usize { + syscall(SysCall::EnvironmentPointer, 0, 0, 0, 0, 0, 0) +} + pub fn init_kernel() { syscall(SysCall::InitKernel, 0, 0, 0, 0, 0, 0); } \ No newline at end of file diff --git a/src/arch/virt/mod.rs b/src/arch/virt/mod.rs index d007fd9..2c694d1 100644 --- a/src/arch/virt/mod.rs +++ b/src/arch/virt/mod.rs @@ -85,6 +85,7 @@ extern "C" fn _virt_init() -> ! { epc: ktask as usize, ddi_first_addr: 0, ddi_size: 0, + environment: 0, wait_for_task_exit: false, }); diff --git a/src/fs/fat32.rs b/src/fs/fat32.rs index 986356c..507e1e0 100644 --- a/src/fs/fat32.rs +++ b/src/fs/fat32.rs @@ -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 { let cluster = bpb.root_cluster as usize; Fat32DirectoryReader { diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 72850c9..b0a2c68 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -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 { #[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 { - path.make_ascii_uppercase(); +pub fn open_directory(fs: &FileSystem, path: &[u8], reader: &mut DirectoryReader) -> usize { + //path.make_ascii_uppercase(); if path[0] != b'/' { return 3; } @@ -104,6 +111,10 @@ pub fn open_directory(fs: &FileSystem, path: &mut [u8], reader: &mut DirectoryRe *reader = root_reader; 0 } 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 { name: [0; 12], record_type: 0, @@ -111,28 +122,67 @@ pub fn open_directory(fs: &FileSystem, path: &mut [u8], reader: &mut DirectoryRe total_size_bytes: 0, }; 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]; - loop { - if !read_one_record(fs, &mut current_reader, &mut record) { - return 1; + 'into_next_dir: loop { + if search_for == b"." { + // 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)]; - if record_name == search_for { - // 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 + loop { + if !read_one_record(fs, &mut current_reader, &mut record) { + return 1; + } + let record_name = &record.name[..record.name.iter().position(|&x| x == 0).unwrap_or(11)]; + if record_name == search_for { + // 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); - *reader = current_reader; - return 0; + target_stack[target_stack_idx] = record.target; + 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; } } } diff --git a/src/main.rs b/src/main.rs index 4129030..0ddbee8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -110,6 +110,7 @@ pub extern "C" fn ktask() -> ! { epc: entrypoint, ddi_first_addr: first_addr, ddi_size: size, + environment: 0, wait_for_task_exit: false, }); } else { diff --git a/src/trafficcontrol.rs b/src/trafficcontrol.rs index a9719cd..5b30512 100644 --- a/src/trafficcontrol.rs +++ b/src/trafficcontrol.rs @@ -84,6 +84,7 @@ pub fn handle_syscall( #[cfg(feature = "arch_virt")] let t = Some(Task { epc: unsafe { (*add).epc }, + environment: unsafe { (*add).environment }, want_exit: false, sp, wait: 0, @@ -319,6 +320,14 @@ pub fn handle_syscall( } 0 } + SysCall::EnvironmentPointer => { + let ci = tc.current; + if let Some(task) = tc.tasks[ci].as_ref() { + task.environment + } else { + 0 + } + } }, suspend, ) @@ -413,6 +422,7 @@ pub struct TrafficControl { pub struct Task { #[cfg(feature = "arch_virt")] pub epc: usize, + pub environment: usize, pub want_exit: bool, /// THE ORIGINAL STACK POINTER THAT THE TASK STARTED WITH pub sp: usize, diff --git a/turntable/src/main.rs b/turntable/src/main.rs index d670ae9..e0b6fb2 100644 --- a/turntable/src/main.rs +++ b/turntable/src/main.rs @@ -1,11 +1,17 @@ #![no_std] #![no_main] -use core::sync::atomic::{AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering}; use crate::terminal::{print, println}; use liblbos::fs::{DirectoryReader, FileSystem}; static NO_EXIT: AtomicBool = AtomicBool::new(false); +static CURRENT_DIR_PATH: AtomicPtr = 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] fn panic(info: &core::panic::PanicInfo) -> ! { @@ -13,14 +19,83 @@ fn panic(info: &core::panic::PanicInfo) -> ! { 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(); if liblbos::ktask::ktask_fsopen(&mut fs) != 0 { println("failed to open fs"); return; } 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"); return; } @@ -107,6 +182,7 @@ fn attempt_run_file(name: &str) { epc: 0, ddi_first_addr: 0, ddi_size: 0, + environment: 0, wait_for_task_exit: true, }; @@ -118,19 +194,26 @@ fn attempt_run_file(name: &str) { 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"); } -fn execute(cmd: &str) { +fn execute(cmd: &str, environment: &mut Environment<'_>) { print("\n"); let mut split = cmd.split_ascii_whitespace(); if let Some(cmd) = split.next() { match cmd { "ls" => { - lsdir(); + lsdir(environment); + } + "cd" => { + if let Some(path) = split.next() { + cd(path, environment); + } else { + println("cd requires a path"); + } } "exit" => { if NO_EXIT.load(Ordering::Relaxed) { @@ -168,6 +251,40 @@ extern "C" fn main() { let tid = liblbos::syscalls::current_task(); 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("turntable v"); const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -198,7 +315,8 @@ extern "C" fn main() { if *c == b'\r' { execute(unsafe { core::str::from_utf8_unchecked(&cmdbuf[0..cmdbuf_len - 1]) - }); + }, + &mut environment); cmdbuf_len = 0; print("> "); break;