diff --git a/etc/syscalls_linux_aarch64.md b/etc/syscalls_linux_aarch64.md index 66b3275..7905488 100644 --- a/etc/syscalls_linux_aarch64.md +++ b/etc/syscalls_linux_aarch64.md @@ -51,7 +51,7 @@ | 0x30 (48) | faccessat | (int dfd, const char *filename, int mode) | __arm64_sys_faccessat | true | | 0x31 (49) | chdir | (const char *filename) | __arm64_sys_chdir | true | | 0x32 (50) | fchdir | (unsigned int fd) | __arm64_sys_fchdir | false | -| 0x33 (51) | chroot | (const char *filename) | __arm64_sys_chroot | false | +| 0x33 (51) | chroot | (const char *filename) | __arm64_sys_chroot | true | | 0x34 (52) | fchmod | (unsigned int fd, umode_t mode) | __arm64_sys_fchmod | false | | 0x35 (53) | fchmodat | (int dfd, const char *filename, umode_t mode) | __arm64_sys_fchmodat | false | | 0x36 (54) | fchownat | (int dfd, const char *filename, uid_t user, gid_t group, int flag) | __arm64_sys_fchownat | false | diff --git a/src/arch/arm64/exceptions/syscall.rs b/src/arch/arm64/exceptions/syscall.rs index 3a8956e..28009b7 100644 --- a/src/arch/arm64/exceptions/syscall.rs +++ b/src/arch/arm64/exceptions/syscall.rs @@ -1,3 +1,4 @@ +use crate::fs::syscalls::chdir::sys_chroot; use crate::fs::syscalls::trunc::sys_ftruncate; use crate::kernel::power::sys_reboot; use crate::kernel::rand::sys_getrandom; @@ -100,6 +101,7 @@ pub async fn handle_syscall() { 0x2e => sys_ftruncate(arg1.into(), arg2 as _).await, 0x30 => sys_faccessat(arg1.into(), TUA::from_value(arg2 as _), arg3 as _).await, 0x31 => sys_chdir(TUA::from_value(arg1 as _)).await, + 0x33 => sys_chroot(TUA::from_value(arg1 as _)).await, 0x38 => { sys_openat( arg1.into(), diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 2af62b0..cb11224 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -11,6 +11,7 @@ use open_file::OpenFile; use reg::RegFile; use crate::drivers::{DM, Driver}; +use crate::process::Task; use crate::sync::SpinLock; use alloc::vec::Vec; @@ -169,7 +170,52 @@ impl VFS { /// Resolves a path string to an Inode, starting from a given root for /// relative paths. - pub async fn resolve_path(&self, path: &Path, root: Arc) -> Result> { + pub async fn resolve_path( + &self, + path: &Path, + root: Arc, + task: Arc, + ) -> Result> { + let mut current_inode = if path.is_absolute() { + task.root.lock_save_irq().0.clone() // use the task's root inode, in case a custom chroot was set + } else { + root + }; + + for component in path.components() { + // Before looking up the component, check if the current inode is a + // mount point. If so, traverse into the mounted filesystem's root. + if let Some(mount_root) = self + .state + .lock_save_irq() + .get_mount_root(¤t_inode.id()) + { + current_inode = mount_root; + } + + // Delegate the lookup to the underlying filesystem. + current_inode = current_inode.lookup(component).await?; + } + + // After the final lookup, check if the destination is itself a mount point. + if let Some(mount_root) = self + .state + .lock_save_irq() + .get_mount_root(¤t_inode.id()) + { + current_inode = mount_root; + } + + Ok(current_inode) + } + + /// Resolves a path string to an Inode, starting from a given root for + /// relative paths, and using the filesystem root inode for absolute paths. + pub async fn resolve_path_absolute( + &self, + path: &Path, + root: Arc, + ) -> Result> { let mut current_inode = if path.is_absolute() { self.root_inode .lock_save_irq() @@ -218,9 +264,10 @@ impl VFS { flags: OpenFlags, root: Arc, mode: FilePermissions, + task: Arc, ) -> Result> { // Attempt to resolve the full path first. - let resolve_result = self.resolve_path(path, root.clone()).await; + let resolve_result = self.resolve_path(path, root.clone(), task.clone()).await; let target_inode = match resolve_result { // The file/directory exists. @@ -243,7 +290,7 @@ impl VFS { // (cwd or dirfd) as the parent directory. let file_name = path.file_name().ok_or(FsError::InvalidInput)?; let parent_inode = if let Some(parent_path) = path.parent() { - self.resolve_path(parent_path, root.clone()).await? + self.resolve_path(parent_path, root.clone(), task).await? } else { root.clone() }; @@ -324,9 +371,10 @@ impl VFS { path: &Path, root: Arc, mode: FilePermissions, + task: Arc, ) -> Result<()> { // Try to resolve the target directory first. - match self.resolve_path(path, root.clone()).await { + match self.resolve_path(path, root.clone(), task.clone()).await { // The path already exists, this is an error. Ok(_) => Err(FsError::AlreadyExists.into()), @@ -339,7 +387,7 @@ impl VFS { // component (e.g., \"foo\"), treat the provided `root` // directory (AT_FDCWD / cwd / dirfd) as the parent. let parent_inode = if let Some(parent_path) = path.parent() { - self.resolve_path(parent_path, root.clone()).await? + self.resolve_path(parent_path, root.clone(), task).await? } else { root.clone() }; @@ -362,9 +410,15 @@ impl VFS { } } - pub async fn unlink(&self, path: &Path, root: Arc, remove_dir: bool) -> Result<()> { + pub async fn unlink( + &self, + path: &Path, + root: Arc, + remove_dir: bool, + task: Arc, + ) -> Result<()> { // First, resolve the target inode so we can inspect its type. - let target_inode = self.resolve_path(path, root.clone()).await?; + let target_inode = self.resolve_path(path, root.clone(), task.clone()).await?; let attr = target_inode.getattr().await?; @@ -382,7 +436,7 @@ impl VFS { // Determine the parent directory inode in which to perform the unlink. let parent_inode = if let Some(parent_path) = path.parent() { - self.resolve_path(parent_path, root.clone()).await? + self.resolve_path(parent_path, root.clone(), task).await? } else { root.clone() }; diff --git a/src/fs/syscalls/at/access.rs b/src/fs/syscalls/at/access.rs index f6838e8..8c79560 100644 --- a/src/fs/syscalls/at/access.rs +++ b/src/fs/syscalls/at/access.rs @@ -14,10 +14,11 @@ pub async fn sys_faccessat(dirfd: Fd, path: TUA, mode: i32) -> Result, mode: i32, flags: i32) -> Result { let mut buf = [0; 1024]; + let task = current_task(); let access_mode = AccessMode::from_bits_retain(mode); let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?); let start_node = resolve_at_start_node(dirfd, path).await?; - let node = VFS.resolve_path(path, start_node).await?; + let node = VFS.resolve_path(path, start_node, task.clone()).await?; let at_flags = AtFlags::from_bits_retain(flags); // If mode is F_OK (value 0), the check is for the file's existence. @@ -26,7 +27,6 @@ pub async fn sys_faccessat2(dirfd: Fd, path: TUA, mode: i32, flags: i32) return Ok(0); } - let task = current_task(); let attrs = node.getattr().await?; let creds = task.creds.lock_save_irq(); diff --git a/src/fs/syscalls/at/mkdir.rs b/src/fs/syscalls/at/mkdir.rs index eeaebde..078aa38 100644 --- a/src/fs/syscalls/at/mkdir.rs +++ b/src/fs/syscalls/at/mkdir.rs @@ -1,3 +1,4 @@ +use crate::current_task; use crate::fs::VFS; use crate::fs::syscalls::at::resolve_at_start_node; use crate::memory::uaccess::cstr::UserCStr; @@ -14,10 +15,11 @@ pub async fn sys_mkdirat( ) -> libkernel::error::Result { let mut buf = [0; 1024]; + let task = current_task(); let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?); let start_node = resolve_at_start_node(dirfd, path).await?; let mode = FilePermissions::from_bits_retain(mode); - VFS.mkdir(path, start_node, mode).await?; + VFS.mkdir(path, start_node, mode, task.clone()).await?; Ok(0) } diff --git a/src/fs/syscalls/at/open.rs b/src/fs/syscalls/at/open.rs index 525e447..67577d2 100644 --- a/src/fs/syscalls/at/open.rs +++ b/src/fs/syscalls/at/open.rs @@ -11,14 +11,17 @@ use super::resolve_at_start_node; pub async fn sys_openat(dirfd: Fd, path: TUA, flags: u32, mode: u16) -> Result { let mut buf = [0; 1024]; + let task = current_task(); let flags = OpenFlags::from_bits_truncate(flags); let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?); let start_node = resolve_at_start_node(dirfd, path).await?; let mode = FilePermissions::from_bits_retain(mode); - let file = VFS.open(path, flags, start_node, mode).await?; + let file = VFS + .open(path, flags, start_node, mode, task.clone()) + .await?; - let fd = current_task().fd_table.lock_save_irq().insert(file)?; + let fd = task.fd_table.lock_save_irq().insert(file)?; Ok(fd.as_raw() as _) } diff --git a/src/fs/syscalls/at/stat.rs b/src/fs/syscalls/at/stat.rs index ed0a764..57e389a 100644 --- a/src/fs/syscalls/at/stat.rs +++ b/src/fs/syscalls/at/stat.rs @@ -1,4 +1,5 @@ use crate::{ + current_task, fs::{VFS, syscalls::at::resolve_at_start_node}, memory::uaccess::{UserCopyable, copy_to_user, cstr::UserCStr}, process::fd_table::Fd, @@ -84,11 +85,12 @@ pub async fn sys_newfstatat( ) -> Result { let mut buf = [0; 1024]; + let task = current_task(); let _flags = AtFlags::from_bits_truncate(flags); let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?); let start_node = resolve_at_start_node(dirfd, path).await?; - let node = VFS.resolve_path(path, start_node).await?; + let node = VFS.resolve_path(path, start_node, task.clone()).await?; let attr = node.getattr().await?; diff --git a/src/fs/syscalls/at/unlink.rs b/src/fs/syscalls/at/unlink.rs index 1475767..2280509 100644 --- a/src/fs/syscalls/at/unlink.rs +++ b/src/fs/syscalls/at/unlink.rs @@ -3,6 +3,7 @@ use core::ffi::c_char; use libkernel::{error::Result, fs::path::Path, memory::address::TUA}; use crate::{ + current_task, fs::{VFS, syscalls::at::resolve_at_start_node}, memory::uaccess::cstr::UserCStr, process::fd_table::Fd, @@ -21,12 +22,15 @@ pub async fn sys_unlinkat(dirfd: Fd, path: TUA, flags: u32) -> Result) -> Result { let current_path = task.cwd.lock_save_irq().0.clone(); let new_path = task.cwd.lock_save_irq().1.join(path); - let node = VFS.resolve_path(path, current_path).await?; + let node = VFS.resolve_path(path, current_path, task.clone()).await?; *task.cwd.lock_save_irq() = (node, new_path); Ok(0) } + +pub async fn sys_chroot(path: TUA) -> Result { + let mut buf = [0; 1024]; + + let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?); + let task = current_task(); + let current_path = task.root.lock_save_irq().0.clone(); + let new_path = task.root.lock_save_irq().1.join(path); + + let node = VFS.resolve_path(path, current_path, task.clone()).await?; + + *task.root.lock_save_irq() = (node, new_path); + + Ok(0) +} diff --git a/src/main.rs b/src/main.rs index eac6fc7..2af3225 100644 --- a/src/main.rs +++ b/src/main.rs @@ -107,7 +107,7 @@ async fn launch_init(opts: KOptions) { // Process all automounts. for (path, fs) in opts.automounts.iter() { let mount_point = VFS - .resolve_path(path, VFS.root_inode()) + .resolve_path_absolute(path, VFS.root_inode()) .await .unwrap_or_else(|e| panic!("Could not find automount path: {}. {e}", path.as_str())); @@ -117,7 +117,7 @@ async fn launch_init(opts: KOptions) { } let inode = VFS - .resolve_path(&init, VFS.root_inode()) + .resolve_path_absolute(&init, VFS.root_inode()) .await .expect("Unable to find init"); @@ -127,8 +127,9 @@ async fn launch_init(opts: KOptions) { assert!(task.process.tgid.is_init()); // Now that the root fs has been mounted, set the real root inode as the - // cwd. + // cwd and root. *task.cwd.lock_save_irq() = (VFS.root_inode(), PathBuf::new()); + *task.root.lock_save_irq() = (VFS.root_inode(), PathBuf::new()); let console = VFS .open( @@ -136,6 +137,7 @@ async fn launch_init(opts: KOptions) { OpenFlags::O_RDWR, VFS.root_inode(), FilePermissions::empty(), + task.clone(), ) .await .expect("Could not open console for init process"); diff --git a/src/process/clone.rs b/src/process/clone.rs index 8dced7c..ef65397 100644 --- a/src/process/clone.rs +++ b/src/process/clone.rs @@ -105,6 +105,12 @@ pub async fn sys_clone( Arc::new(SpinLock::new(current_task.cwd.lock_save_irq().clone())) }; + let root = if flags.contains(CloneFlags::CLONE_FS) { + current_task.root.clone() + } else { + Arc::new(SpinLock::new(current_task.root.lock_save_irq().clone())) + }; + let creds = current_task.creds.lock_save_irq().clone(); let mut user_ctx = *current_task.ctx.lock_save_irq().user(); @@ -120,6 +126,7 @@ pub async fn sys_clone( vm, fd_table: files, cwd, + root, creds: SpinLock::new(creds), ctx: SpinLock::new(Context::from_user_ctx(user_ctx)), priority: current_task.priority, diff --git a/src/process/exec.rs b/src/process/exec.rs index 97fb9a3..0260ec5 100644 --- a/src/process/exec.rs +++ b/src/process/exec.rs @@ -276,8 +276,11 @@ pub async fn sys_execve( usr_env = usr_env.add_objs(1); } + let task = current_task(); let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?); - let inode = VFS.resolve_path(path, VFS.root_inode()).await?; + let inode = VFS + .resolve_path(path, VFS.root_inode(), task.clone()) + .await?; kernel_exec(inode, argv, envp).await?; diff --git a/src/process/mod.rs b/src/process/mod.rs index fcb00f3..ac928dd 100644 --- a/src/process/mod.rs +++ b/src/process/mod.rs @@ -168,6 +168,7 @@ pub struct Task { pub process: Arc, pub vm: Arc>, pub cwd: Arc, PathBuf)>>, + pub root: Arc, PathBuf)>>, pub creds: SpinLock, pub fd_table: Arc>, pub ctx: SpinLock, @@ -201,6 +202,7 @@ impl Task { state: Arc::new(SpinLock::new(TaskState::Runnable)), priority: i8::MIN, cwd: Arc::new(SpinLock::new((Arc::new(DummyInode {}), PathBuf::new()))), + root: Arc::new(SpinLock::new((Arc::new(DummyInode {}), PathBuf::new()))), creds: SpinLock::new(Credentials::new_root()), ctx: SpinLock::new(Context::from_user_ctx(user_ctx)), vm: Arc::new(SpinLock::new(vm)), @@ -222,6 +224,7 @@ impl Task { process: ThreadGroupBuilder::new(Tgid::init()).build(), state: Arc::new(SpinLock::new(TaskState::Runnable)), cwd: Arc::new(SpinLock::new((Arc::new(DummyInode {}), PathBuf::new()))), + root: Arc::new(SpinLock::new((Arc::new(DummyInode {}), PathBuf::new()))), creds: SpinLock::new(Credentials::new_root()), vm: Arc::new(SpinLock::new( ProcessVM::empty().expect("Could not create init process's VM"), diff --git a/usertest/src/main.rs b/usertest/src/main.rs index 1a1ccd4..2030e17 100644 --- a/usertest/src/main.rs +++ b/usertest/src/main.rs @@ -54,6 +54,24 @@ fn test_chdir() { println!(" OK"); } +fn test_chroot() { + print!("Testing chroot syscall ..."); + let file = "/bin/busybox"; + let c_file = std::ffi::CString::new(file).unwrap(); + let path = std::ffi::CString::new("/dev").unwrap(); + unsafe { + if libc::chroot(path.as_ptr()) != 0 { + panic!("chroot failed"); + } else { + let fd = libc::open(c_file.as_ptr(), libc::O_RDONLY); + if fd != -1 { + panic!("chroot failed"); + } + } + } + println!(" OK"); +} + fn test_fork() { print!("Testing fork syscall ..."); unsafe { @@ -173,6 +191,7 @@ fn main() { run_test(test_opendir); run_test(test_readdir); run_test(test_chdir); + run_test(test_chroot); run_test(test_fork); run_test(test_read); run_test(test_write);