diff --git a/context/context.rs b/context/context.rs index 6b4f7c7..43bdc61 100644 --- a/context/context.rs +++ b/context/context.rs @@ -33,6 +33,8 @@ pub struct Context { pub heap: Option, /// User stack pub stack: Option, + /// The current working directory + pub cwd: Arc>>, /// The open files in the scheme pub files: Arc>>> } @@ -49,10 +51,46 @@ impl Context { image: Vec::new(), heap: None, stack: None, + cwd: Arc::new(Mutex::new(Vec::new())), files: Arc::new(Mutex::new(Vec::new())) } } + pub fn canonicalize(&self, path: &[u8]) -> Vec { + if path.iter().position(|&b| b == b':').is_none() { + let cwd = self.cwd.lock(); + if path == b"." { + cwd.clone() + } else if path == b".." { + cwd[..cwd[..cwd.len() - 1] + .iter().rposition(|&b| b == b'/') + .map_or(cwd.len(), |i| i + 1)] + .to_vec() + } else if path.starts_with(b"./") { + let mut canon = cwd.clone(); + canon.extend_from_slice(&path[2..]); + canon + } else if path.starts_with(b"../") { + let mut canon = cwd[..cwd[..cwd.len() - 1] + .iter().rposition(|&b| b == b'/') + .map_or(cwd.len(), |i| i + 1)] + .to_vec(); + canon.extend_from_slice(&path[3..]); + canon + } else if path.starts_with(b"/") { + let mut canon = cwd[..cwd.iter().position(|&b| b == b':').map_or(1, |i| i + 1)].to_vec(); + canon.extend_from_slice(&path); + canon + } else { + let mut canon = cwd.clone(); + canon.extend_from_slice(&path); + canon + } + } else { + path.to_vec() + } + } + /// Add a file to the lowest available slot. /// Return the file descriptor number or None if no slot was found pub fn add_file(&mut self, file: File) -> Option { diff --git a/lib.rs b/lib.rs index dce4bce..e9eec63 100644 --- a/lib.rs +++ b/lib.rs @@ -128,6 +128,8 @@ pub fn cpu_id() -> usize { } pub extern fn userspace_init() { + assert_eq!(syscall::chdir(b"initfs:"), Ok(0)); + assert_eq!(syscall::open(b"debug:", 0), Ok(0)); assert_eq!(syscall::open(b"debug:", 0), Ok(1)); assert_eq!(syscall::open(b"debug:", 0), Ok(2)); diff --git a/scheme/initfs.rs b/scheme/initfs.rs index d947b5a..82e5f35 100644 --- a/scheme/initfs.rs +++ b/scheme/initfs.rs @@ -19,8 +19,9 @@ impl InitFsScheme { let mut files: BTreeMap<&'static [u8], &'static [u8]> = BTreeMap::new(); files.insert(b"bin/init", include_bytes!("../../build/userspace/init")); + files.insert(b"bin/ion", include_bytes!("../../build/userspace/ion")); files.insert(b"bin/pcid", include_bytes!("../../build/userspace/pcid")); - files.insert(b"etc/init.rc", b"echo testing\ninitfs:bin/pcid\n"); + files.insert(b"etc/init.rc", b"echo testing\ninitfs:bin/pcid\ninitfs:bin/ion"); InitFsScheme { next_id: 0, diff --git a/syscall/call.rs b/syscall/call.rs index e62b27d..e4a7e92 100644 --- a/syscall/call.rs +++ b/syscall/call.rs @@ -19,6 +19,8 @@ pub enum Call { WaitPid = 7, /// Execute syscall Exec = 11, + /// Change working directory + ChDir = 12, /// Get process ID GetPid = 20, /// Duplicate file descriptor @@ -30,12 +32,15 @@ pub enum Call { /// Clone process Clone = 120, /// Yield to scheduler - SchedYield = 158 + SchedYield = 158, + /// Get process working directory + GetCwd = 183 } /// Convert numbers to calls /// See http://syscalls.kernelgrok.com/ impl Call { + //TODO: Return Option pub fn from(number: usize) -> Result { match number { 1 => Ok(Call::Exit), @@ -45,12 +50,14 @@ impl Call { 6 => Ok(Call::Close), 7 => Ok(Call::WaitPid), 11 => Ok(Call::Exec), + 12 => Ok(Call::ChDir), 20 => Ok(Call::GetPid), 41 => Ok(Call::Dup), 45 => Ok(Call::Brk), 110 => Ok(Call::Iopl), 120 => Ok(Call::Clone), 158 => Ok(Call::SchedYield), + 183 => Ok(Call::GetCwd), _ => Err(Error::NoCall) } } diff --git a/syscall/fs.rs b/syscall/fs.rs index ddb4f8a..acf457e 100644 --- a/syscall/fs.rs +++ b/syscall/fs.rs @@ -5,6 +5,28 @@ use scheme; use super::{Error, Result}; +pub fn chdir(path: &[u8]) -> Result { + let contexts = context::contexts(); + let context_lock = contexts.current().ok_or(Error::NoProcess)?; + let context = context_lock.read(); + let canonical = context.canonicalize(path); + *context.cwd.lock() = canonical; + Ok(0) +} + +pub fn getcwd(buf: &mut [u8]) -> Result { + let contexts = context::contexts(); + let context_lock = contexts.current().ok_or(Error::NoProcess)?; + let context = context_lock.read(); + let cwd = context.cwd.lock(); + let mut i = 0; + while i < buf.len() && i < cwd.len() { + buf[i] = cwd[i]; + i += 1; + } + Ok(i) +} + /// Read syscall pub fn read(fd: usize, buf: &mut [u8]) -> Result { let file = { diff --git a/syscall/mod.rs b/syscall/mod.rs index f27d009..c6ab11d 100644 --- a/syscall/mod.rs +++ b/syscall/mod.rs @@ -34,12 +34,14 @@ pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize Call::Close => close(b), Call::WaitPid => waitpid(b, c, d), Call::Exec => exec(validate_slice(b as *const u8, c)?, validate_slice(d as *const [usize; 2], e)?), + Call::ChDir => chdir(validate_slice(b as *const u8, c)?), Call::GetPid => getpid(), Call::Dup => dup(b), Call::Brk => brk(b), Call::Iopl => iopl(b), Call::Clone => clone(b, stack), - Call::SchedYield => sched_yield() + Call::SchedYield => sched_yield(), + Call::GetCwd => getcwd(validate_slice_mut(b as *mut u8, c)?) }, Err(err) => { println!("Unknown syscall {}", a); diff --git a/syscall/process.rs b/syscall/process.rs index fb21a70..182c5f9 100644 --- a/syscall/process.rs +++ b/syscall/process.rs @@ -1,6 +1,7 @@ ///! Process syscalls use alloc::arc::Arc; +use collections::Vec; use core::mem; use core::str; use spin::Mutex; @@ -53,7 +54,9 @@ pub const CLONE_FILES: usize = 0x400; pub const CLONE_VFORK: usize = 0x4000; pub fn clone(flags: usize, stack_base: usize) -> Result { //TODO: Copy on write? - println!("Clone {:X}: {:X}", flags, stack_base); + + // vfork not supported + assert!(flags & CLONE_VFORK == 0); let ppid; let pid; @@ -64,7 +67,8 @@ pub fn clone(flags: usize, stack_base: usize) -> Result { let mut image = vec![]; let mut heap_option = None; let mut stack_option = None; - let mut files = Arc::new(Mutex::new(vec![])); + let cwd; + let files; // Copy from old process { @@ -159,9 +163,16 @@ pub fn clone(flags: usize, stack_base: usize) -> Result { stack_option = Some(new_stack); } + if flags & CLONE_FS == CLONE_FS { + cwd = context.cwd.clone(); + } else { + cwd = Arc::new(Mutex::new(context.cwd.lock().clone())); + } + if flags & CLONE_FILES == CLONE_FILES { files = context.files.clone(); } else { + let mut files_vec = Vec::new(); for (fd, file_option) in context.files.lock().iter().enumerate() { if let Some(file) = *file_option { let result = { @@ -172,16 +183,17 @@ pub fn clone(flags: usize, stack_base: usize) -> Result { }; match result { Ok(new_number) => { - files.lock().push(Some(context::file::File { scheme: file.scheme, number: new_number })); + files_vec.push(Some(context::file::File { scheme: file.scheme, number: new_number })); }, Err(err) => { println!("clone: failed to dup {}: {:?}", fd, err); } } } else { - files.lock().push(None); + files_vec.push(None); } } + files = Arc::new(Mutex::new(files_vec)); } } @@ -289,6 +301,8 @@ pub fn clone(flags: usize, stack_base: usize) -> Result { context.stack = Some(stack); } + context.cwd = cwd; + context.files = files; context.arch.set_page_table(unsafe { new_table.address() }); @@ -322,7 +336,6 @@ pub fn exec(path: &[u8], _args: &[[usize; 2]]) -> Result { //TODO: Use args //TODO: Unmap previous mappings //TODO: Drop data vec - println!("Exec {}", unsafe { str::from_utf8_unchecked(path) }); let file = syscall::open(path, 0)?; let mut data = vec![];