From ebff6b7924a22c4c672e2185a74848b0c2ade284 Mon Sep 17 00:00:00 2001 From: Alice Wen <40227173+Yaxuan-w@users.noreply.github.com> Date: Tue, 1 Oct 2024 13:21:00 -0400 Subject: [PATCH 1/3] Porting newest version fdtables and Synchronize the progress of porting test suite (#14) * change to latest fdtables * using newest fdtables * mmap debug * mmap debug * mmap debug * porting test suite * porting test suite * Add more detailed comments * Fix directory cleanup issue in ut_lind_fs_mmap_invalid_flags_both test case (#16) * fix: ut_lind_fs_chdir_removeddir * fix: add syscalls * fix: revert changes * fix: testing subdir1 * fix: testing without i32 * fix: revert to mkdir * fix: test with if * fix: revert * debug: added print * debug: added print * fix: print * fix: revert println * feat: return invalid * fix: revert removed return * feat: update rm subdir1 subdir2 * refactor: update comments * fix: test case ut-lind-fs-fchdir-invalid-args * Fix out-of-range file descriptor error in `getdents` syscall (#17) * Fix: out-of-range file descriptor handling in getdents syscall * fix: update comment * Update src/fdtables/dashmapvecglobal.rs Co-authored-by: Justin Cappos --------- Co-authored-by: lind Co-authored-by: Justin Cappos * Fix `getcwd_syscall` to Handle Invalid Arguments and Correctly Set `errno` (#24) * fix: test case ut_lind_fs_getcwd_invalid_args * fix: PR comments --------- Co-authored-by: lind * Fix mmap test case to handle invalid flags scenario correctly (#18) * Fix: mmap test to validate EINVAL for invalid flags * fix: PR comments --------- Co-authored-by: lind * Fix Test `ut_lind_fs_pread_from_directory` from Directory to Return Correct Error (#27) * fix: test case ut_lind_fs_pread_from_directory * fix: add rmdir syscall --------- Co-authored-by: lind * fix: test case ut-lind-fs-write-to-directory (#19) Co-authored-by: lind * fix: ut-lind-fs-simple (#21) Co-authored-by: lind * Fix `ut_lind_fs_rmdir_nowriteperm_parent_dir` rmdir Nowrite Permission Handling and Recursive Directory Cleanup (#28) * fix: test case ut_lind_fs_rmdir_nowriteperm_parent_dir * feat: added cleanup --------- Co-authored-by: lind * Fix ut lind fs mkdir nonexisting directory (#25) * fix: test case ut_lind_fs_mkdir_nonexisting_directory * feat: added rmdir_recursive_syscall * fix: rm unecessary function * feat: updated comment * feat: updated values --------- Co-authored-by: lind * Fix EISDIR Error in `ut_lind_fs_getdents_bufsize_too_small` Test by Adjusting Directory Open Flags (#29) * fix: test case ut_lind_fs_getdents_bufsize_too_small * fix: rmdir for clean env --------- Co-authored-by: lind * Fix EEXIST Error in `ut_lind_fs_close_directory` Test by Removing Existing Directories Before Creation (#31) * fix: test case ut_lind_fs_close_directory * feat: use rmdir --------- Co-authored-by: lind * Fix EEXIST Error in `ut_lind_fs_dir_multiple` Test by Removing Existing Directories Before Creation (#32) * fix: test case ut_lind_fs_dir_multiple * feat: updated to rmdir --------- Co-authored-by: lind * Fix EEXIST Error in `ut_lind_fs_dir_mode` Test by Removing Existing Directories Before Creation (#30) * fix: test case ut_lind_fs_dir_mode * fix: cleanup the enviornment --------- Co-authored-by: lind * Fix EEXIST Error in `ut_lind_fs_unlink_directory` (#34) * fix: test case ut_lind_fs_rmdir_nonexist_dir (#36) * fix: test case ut_lind_fs_rmdir_nonempty_dir (#37) * fix: test case ut_lind_fs_link_directory (#38) * fix: test case ut_lind_fs_read_from_directory (#39) * fix: test case ut_lind_fs_rename (#41) * Fix in `ut_lind_fs_tmp_file_test` ensure /tmp Directory Is Properly Set Up and Cleaned (#35) * fix: test case ut_lind_fs_read_from_chardev_file (#44) Co-authored-by: lind --------- Co-authored-by: Chinmay Shringi <31031919+ChinmayShringi@users.noreply.github.com> Co-authored-by: lind Co-authored-by: Nicholas Renner Co-authored-by: Justin Cappos --- src/example_grates/commonconstants.rs | 104 - src/example_grates/dashmaparrayglobal.rs | 728 -- src/example_grates/dashmapvecglobal.rs | 969 --- src/example_grates/imfs_grate.rs | 218 - src/example_grates/imp_grate.rs | 242 - src/example_grates/lindfs_grate.rs | 215 - src/example_grates/log_grate.rs | 40 - src/example_grates/route_grate.rs | 147 - src/fdtables/commonconstants.rs | 123 + src/fdtables/current_impl | 2 + src/fdtables/dashmaparrayglobal.rs | 981 +++ src/fdtables/dashmapvecglobal.rs | 999 +++ src/{example_grates => fdtables}/mod.rs | 2 +- .../muthashmaxglobal.rs | 490 +- src/{example_grates => fdtables}/threei.rs | 70 +- .../vanillaglobal.rs | 510 +- src/lib.rs | 2 +- src/safeposix/dispatcher.rs | 219 +- src/safeposix/syscalls/fs_calls.rs | 779 +-- src/safeposix/syscalls/fs_constants.rs | 28 +- src/safeposix/syscalls/net_calls.rs | 930 ++- src/safeposix/syscalls/sys_calls.rs | 15 +- src/tests/fs_tests.rs | 5881 +++++++++++++---- src/tests/ipc_tests.rs | 975 ++- src/tests/mod.rs | 92 +- src/tests/networking_tests.rs | 5854 +++++++++++++++- src/tests/sys_tests.rs | 138 + 27 files changed, 14577 insertions(+), 6176 deletions(-) delete mode 100644 src/example_grates/commonconstants.rs delete mode 100644 src/example_grates/dashmaparrayglobal.rs delete mode 100644 src/example_grates/dashmapvecglobal.rs delete mode 100644 src/example_grates/imfs_grate.rs delete mode 100644 src/example_grates/imp_grate.rs delete mode 100644 src/example_grates/lindfs_grate.rs delete mode 100644 src/example_grates/log_grate.rs delete mode 100644 src/example_grates/route_grate.rs create mode 100644 src/fdtables/commonconstants.rs create mode 100644 src/fdtables/current_impl create mode 100644 src/fdtables/dashmaparrayglobal.rs create mode 100644 src/fdtables/dashmapvecglobal.rs rename src/{example_grates => fdtables}/mod.rs (90%) rename src/{example_grates => fdtables}/muthashmaxglobal.rs (59%) rename src/{example_grates => fdtables}/threei.rs (82%) rename src/{example_grates => fdtables}/vanillaglobal.rs (57%) create mode 100644 src/tests/sys_tests.rs diff --git a/src/example_grates/commonconstants.rs b/src/example_grates/commonconstants.rs deleted file mode 100644 index f6022646..00000000 --- a/src/example_grates/commonconstants.rs +++ /dev/null @@ -1,104 +0,0 @@ -// This file exists to make it easier to vary a single file of constants -// instead of editing each implementation... - -/// Per-process maximum number of fds... -pub const FD_PER_PROCESS_MAX: u64 = 1024; -// pub const FD_PER_PROCESS_MAX: u64 = 16; - -/// Use this to indicate there isn't a real fd backing an item -pub const NO_REAL_FD: u64 = 0xff_abcd_ef01; - -/// Use to indicate this is an EPOLLFD -pub const EPOLLFD: u64 = 0xff_abcd_ef02; - -/// Use this to indicate that a FD is invalid... Usually an error will be -/// returned instead, but this is needed for rare cases like poll. -pub const INVALID_FD: u64 = 0xff_abcd_ef00; - -// These are the values we look up with at the end... -// #[doc = include_str!("../docs/fdtableentry.md")] -#[derive(Clone, Copy, Debug, PartialEq)] -/// This is a table entry, looked up by virtual fd. -pub struct FDTableEntry { - /// underlying fd (may be a virtual fd below us or a kernel fd). In - /// some cases is also `NO_REAL_FD` or EPOLLFD to indicate it isn't backed - /// by an underlying fd. - pub realfd: u64, - /// Should I close this on exec? [/`empty_fds_for_exec`] - pub should_cloexec: bool, - /// Used for `NO_REAL_FD` and EPOLLFD types to store extra info. User - /// defined data can be added here. - pub optionalinfo: u64, -} - -#[allow(non_snake_case)] -/// A function used when registering close handlers which does nothing... -pub const fn NULL_FUNC(_: u64) {} - -// BUG / TODO: Use this in some sane way... -#[allow(dead_code)] -/// Global maximum number of fds... (checks may not be implemented) -pub const TOTAL_FD_MAX: u64 = 4096; - -// replicating these constants here so this can compile on systems other than -// Linux... Copied from Rust's libc. -// copied from libc -// pub const EPOLL_CTL_ADD: i32 = 1; -// /// copied from libc -// pub const EPOLL_CTL_MOD: i32 = 2; -// /// copied from libc -// pub const EPOLL_CTL_DEL: i32 = 3; - -// #[allow(non_camel_case_types)] -// /// i32 copied from libc. used in EPOLL event flags even though events are u32 -// pub type c_int = i32; - -// /// copied from libc -// pub const EPOLLIN: c_int = 0x1; -// /// copied from libc -// pub const EPOLLPRI: c_int = 0x2; -// /// copied from libc -// pub const EPOLLOUT: c_int = 0x4; -// /// copied from libc -// pub const EPOLLERR: c_int = 0x8; -// /// copied from libc -// pub const EPOLLHUP: c_int = 0x10; -// /// copied from libc -// pub const EPOLLRDNORM: c_int = 0x40; -// /// copied from libc -// pub const EPOLLRDBAND: c_int = 0x80; -// /// copied from libc -// pub const EPOLLWRNORM: c_int = 0x100; -// /// copied from libc -// pub const EPOLLWRBAND: c_int = 0x200; -// /// copied from libc -// pub const EPOLLMSG: c_int = 0x400; -// /// copied from libc -// pub const EPOLLRDHUP: c_int = 0x2000; -// /// copied from libc -// pub const EPOLLEXCLUSIVE: c_int = 0x1000_0000; -// /// copied from libc -// pub const EPOLLWAKEUP: c_int = 0x2000_0000; -// /// copied from libc -// pub const EPOLLONESHOT: c_int = 0x4000_0000; -// // Turning this on here because we copied from Rust's libc and I assume they -// // intended this... -// #[allow(overflowing_literals)] -// /// copied from libc -// pub const EPOLLET: c_int = 0x8000_0000; - -// use libc::epoll_event; -// // Note, I'm not using libc's version because this isn't defined on Windows -// // or Mac. Hence, I can't compile, etc. on those systems. Of course any -// // system actually running epoll, will need to be on Mac, but that doesn't mean -// // we can't parse those calls. -// #[allow(non_camel_case_types)] -// #[derive(Clone, Debug)] -// /// matches libc in Rust. Copied exactly. -// pub struct epoll_event { -// /// copied from libc. Event types to look at. -// pub events: u32, // So weird that this is a u32, while the constants -// // defined to work with it are i32s... -// /// copied from libc. Not used. -// pub u64: u64, -// } \ No newline at end of file diff --git a/src/example_grates/dashmaparrayglobal.rs b/src/example_grates/dashmaparrayglobal.rs deleted file mode 100644 index 63281dc8..00000000 --- a/src/example_grates/dashmaparrayglobal.rs +++ /dev/null @@ -1,728 +0,0 @@ -// DashMap;FD_PER_PROCESSS_MAX]> Space is ~24KB -// per cage w/ 1024 fds?!? -// Static DashMap. Let's see if having the FDTableEntries be a static -// array is any faster... - - -use crate::safeposix::cage; -use crate::safeposix::syscalls::fs_calls::*; - -use super::threei; - -use dashmap::DashMap; - -use lazy_static::lazy_static; - -use std::collections::HashMap; - -use std::sync::Mutex; - -// This uses a Dashmap (for cages) with an array of FDTableEntry items. - -// Get constants about the fd table sizes, etc. -pub use super::commonconstants::*; - -// algorithm name. Need not be listed. Used in benchmarking output -#[doc(hidden)] -pub const ALGONAME: &str = "DashMapArrayGlobal"; - -// These are the values we look up with at the end... -// #[doc = include_str!("../docs/fdtableentry.md")] -#[derive(Clone, Copy, Debug, PartialEq,Eq, Hash)] -pub struct FDTableEntry { - pub realfd: u64, // underlying fd (may be a virtual fd below us or - // a kernel fd) - pub should_cloexec: bool, // should I close this when exec is called? - pub optionalinfo: u64, // user specified / controlled data -} - -// It's fairly easy to check the fd count on a per-process basis (I just check -// when I would add a new fd). -// -// BUG: I will ignore the total limit for now. I would ideally do this on -// every creation, close, fork, etc. but it's a PITA to track this. - -// We will raise a panic anywhere we receive an unknown cageid. This frankly -// should not be possible and indicates some sort of internal error in our -// code. However, it is expected we could receive an invalid file descriptor -// when a cage makes a call. - -// In order to store this information, I'm going to use a DashMap which -// has keys of (cageid:u64) and values that are an array of FD_PER_PROCESS_MAX -// Option items. -// -// - -// This lets me initialize the code as a global. -lazy_static! { - - #[derive(Debug)] - static ref FDTABLE: DashMap;FD_PER_PROCESS_MAX as usize]> = { - let m = DashMap::new(); - // Insert a cage so that I have something to fork / test later, if need - // be. Otherwise, I'm not sure how I get this started. I think this - // should be invalid from a 3i standpoint, etc. Could this mask an - // error in the future? - // m.insert(threei::TESTING_CAGEID,[Option::None;FD_PER_PROCESS_MAX as usize]); - m - }; -} - -lazy_static! { - // This is needed for close and similar functionality. I need track the - // number of times a realfd is open - #[derive(Debug)] - static ref REALFDCOUNT: DashMap = { - DashMap::new() - }; - -} - -// Internal helper to hold the close handlers... -struct CloseHandlers { - intermediate_handler: fn(u64), - final_handler: fn(u64), - unreal_handler: fn(u64), -} - -// Seems sort of like a constant... I'm not sure if this is bad form or not... -// #[allow(non_snake_case)] -// pub fn NULL_FUNC(_:u64) { } - -lazy_static! { - // This holds the user registered handlers they want to have called when - // a close occurs. I did this rather than return messy data structures - // from the close, exec, and exit handlers because it seemed cleaner... - #[derive(Debug)] - static ref CLOSEHANDLERTABLE: Mutex = { - let c = CloseHandlers { - intermediate_handler:NULL_FUNC, - final_handler:kernel_close, - unreal_handler:NULL_FUNC, - }; - Mutex::new(c) - }; -} - -// #[doc = include_str!("../docs/init_empty_cage.md")] -pub fn init_empty_cage(cageid: u64) { - - if FDTABLE.contains_key(&cageid) { - panic!("Known cageid in fdtable access"); - } - - FDTABLE.insert(cageid,[Option::None;FD_PER_PROCESS_MAX as usize]); -} - -// #[doc = include_str!("../docs/translate_virtual_fd.md")] -pub fn translate_virtual_fd(cageid: u64, virtualfd: u64) -> Result { - - // They should not be able to pass a new cage I don't know. I should - // always have a table for each cage because each new cage is added at fork - // time - if !FDTABLE.contains_key(&cageid) { - panic!("Unknown cageid in fdtable access"); - } - - return match FDTABLE.get(&cageid).unwrap()[virtualfd as usize] { - Some(tableentry) => Ok(tableentry.realfd), - None => Err(threei::Errno::EBADFD as u64), - }; -} - - -// This is fairly slow if I just iterate sequentially through numbers. -// However there are not that many to choose from. I could pop from a list -// or a set as well... Likely the best solution is to keep a count of the -// largest fd handed out and to just use this until you wrap. This will be -// super fast for a normal cage and will be correct in the weird case. -// Right now, I'll just implement the slow path and will speed this up -// later, if needed. -// #[doc = include_str!("../docs/get_unused_virtual_fd.md")] -pub fn get_unused_virtual_fd( - cageid: u64, - realfd: u64, - should_cloexec: bool, - optionalinfo: u64, -) -> Result { - - if !FDTABLE.contains_key(&cageid) { - panic!("Unknown cageid in fdtable access"); - } - // Set up the entry so it has the right info... - // Note, a HashMap stores its data on the heap! No need to box it... - // https://doc.rust-lang.org/book/ch08-03-hash-maps.html#creating-a-new-hash-map - let myentry = FDTableEntry { - realfd, - should_cloexec, - optionalinfo, - }; - - let mut myfdarray = FDTABLE.get_mut(&cageid).unwrap(); - - // Check the fds in order. - for fdcandidate in 0..FD_PER_PROCESS_MAX { - // FIXME: This is likely very slow. Should do something smarter... - if myfdarray[fdcandidate as usize].is_none() { - // I just checked. Should not be there... - myfdarray[fdcandidate as usize] = Some(myentry); - _increment_realfd(realfd); - return Ok(fdcandidate); - } - } - - // I must have checked all fds and failed to find one open. Fail! - Err(threei::Errno::EMFILE as u64) -} - -// This is used for things like dup2, which need a specific fd... -// If the requested_virtualfd is used, I close it... -// #[doc = include_str!("../docs/get_specific_virtual_fd.md")] -pub fn get_specific_virtual_fd( - cageid: u64, - requested_virtualfd: u64, - realfd: u64, - should_cloexec: bool, - optionalinfo: u64, -) -> Result<(), threei::RetVal> { - - if !FDTABLE.contains_key(&cageid) { - panic!("Unknown cageid in fdtable access"); - } - - // If you ask for a FD number that is too large, I'm going to reject it. - // Note that, I need to use the FD_PER_PROCESS_MAX setting because this - // is also how I'm tracking how many values you have open. If this - // changed, then these constants could be decoupled... - if requested_virtualfd > FD_PER_PROCESS_MAX { - return Err(threei::Errno::EBADF as u64); - } - - // Set up the entry so it has the right info... - // Note, a HashMap stores its data on the heap! No need to box it... - // https://doc.rust-lang.org/book/ch08-03-hash-maps.html#creating-a-new-hash-map - let myentry = FDTableEntry { - realfd, - should_cloexec, - optionalinfo, - }; - - // I moved this up so that if I decrement the same realfd, it calls - // the intermediate handler instead of the final one. - _increment_realfd(realfd); - if let Some(entry) = FDTABLE.get(&cageid).unwrap()[requested_virtualfd as usize] { - if entry.realfd != NO_REAL_FD { - _decrement_realfd(entry.realfd); - } - else { - // Let their code know this has been closed... - let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); - (closehandlers.unreal_handler)(entry.optionalinfo); - } - } - - // always add the new entry - FDTABLE.get_mut(&cageid).unwrap()[requested_virtualfd as usize] = Some(myentry); - Ok(()) -} - -// We're just setting a flag here, so this should be pretty straightforward. -// #[doc = include_str!("../docs/set_cloexec.md")] -pub fn set_cloexec(cageid: u64, virtualfd: u64, is_cloexec: bool) -> Result<(), threei::RetVal> { - - if !FDTABLE.contains_key(&cageid) { - panic!("Unknown cageid in fdtable access"); - } - - // return EBADFD, if the fd is missing... - if FDTABLE.get(&cageid).unwrap()[virtualfd as usize].is_none() { - return Err(threei::Errno::EBADFD as u64); - } - // Set the is_cloexec flag - FDTABLE.get_mut(&cageid).unwrap()[virtualfd as usize].as_mut().unwrap().should_cloexec = is_cloexec; - Ok(()) -} - -// Super easy, just return the optionalinfo field... -// #[doc = include_str!("../docs/get_optionalinfo.md")] -pub fn get_optionalinfo(cageid: u64, virtualfd: u64) -> Result { - if !FDTABLE.contains_key(&cageid) { - panic!("Unknown cageid in fdtable access"); - } - - return match FDTABLE.get(&cageid).unwrap()[virtualfd as usize] { - Some(tableentry) => Ok(tableentry.optionalinfo), - None => Err(threei::Errno::EBADFD as u64), - }; -} - -// We're setting an opaque value here. This should be pretty straightforward. -// #[doc = include_str!("../docs/set_optionalinfo.md")] -pub fn set_optionalinfo( - cageid: u64, - virtualfd: u64, - optionalinfo: u64, -) -> Result<(), threei::RetVal> { - - if !FDTABLE.contains_key(&cageid) { - panic!("Unknown cageid in fdtable access"); - } - - // return EBADFD, if the fd is missing... - if FDTABLE.get(&cageid).unwrap()[virtualfd as usize].is_none() { - return Err(threei::Errno::EBADFD as u64); - } - - // Set optionalinfo or return EBADFD, if that's missing... - FDTABLE.get_mut(&cageid).unwrap()[virtualfd as usize].as_mut().unwrap().optionalinfo = optionalinfo; - Ok(()) -} - -// Helper function used for fork... Copies an fdtable for another process -// #[doc = include_str!("../docs/copy_fdtable_for_cage.md")] -pub fn copy_fdtable_for_cage(srccageid: u64, newcageid: u64) -> Result<(), threei::Errno> { - - if !FDTABLE.contains_key(&srccageid) { - panic!("Unknown srccageid in fdtable access"); - } - if FDTABLE.contains_key(&newcageid) { - panic!("Known newcageid in fdtable access"); - } - - // Insert a copy and ensure it didn't exist... - // BUG: Is this a copy!?! Am I passing a ref to the same thing!?!?!? - let hmcopy = *FDTABLE.get(&srccageid).unwrap(); - - // Increment copied items - for entry in FDTABLE.get(&srccageid).unwrap().iter() { - if entry.is_some() { - let thisrealfd = entry.unwrap().realfd; - if thisrealfd != NO_REAL_FD { - _increment_realfd(thisrealfd); - } - } - } - - assert!(FDTABLE.insert(newcageid, hmcopy).is_none()); - Ok(()) - // I'm not going to bother to check the number of fds used overall yet... - // Err(threei::Errno::EMFILE as u64), -} - -// This is mostly used in handling exit, etc. Returns the HashMap -// for the cage. -// #[doc = include_str!("../docs/remove_cage_from_fdtable.md")] -pub fn remove_cage_from_fdtable(cageid: u64) { - - if !FDTABLE.contains_key(&cageid) { - panic!("Unknown cageid in fdtable access"); - } - - - let myfdarray = FDTABLE.get(&cageid).unwrap(); - for item in 0..FD_PER_PROCESS_MAX as usize { - if myfdarray[item].is_some() { - let therealfd = myfdarray[item].unwrap().realfd; - if therealfd != NO_REAL_FD { - _decrement_realfd(therealfd); - } - else{ - // Let their code know this has been closed... - let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); - (closehandlers.unreal_handler)(myfdarray[item].unwrap().optionalinfo); - } - } - } - // I need to do this or else I'll try to double claim the lock and - // deadlock... - drop(myfdarray); - - FDTABLE.remove(&cageid); - -} - -// This removes all fds with the should_cloexec flag set. They are returned -// in a new hashmap... -// #[doc = include_str!("../docs/empty_fds_for_exec.md")] -pub fn empty_fds_for_exec(cageid: u64) { - - if !FDTABLE.contains_key(&cageid) { - panic!("Unknown cageid in fdtable access"); - } - - let mut myfdarray = FDTABLE.get_mut(&cageid).unwrap(); - for item in 0..FD_PER_PROCESS_MAX as usize { - if myfdarray[item].is_some() && myfdarray[item].unwrap().should_cloexec { - let therealfd = myfdarray[item].unwrap().realfd; - if therealfd != NO_REAL_FD { - _decrement_realfd(therealfd); - } - else{ - // Let their code know this has been closed... - let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); - (closehandlers.unreal_handler)(myfdarray[item].unwrap().optionalinfo); - } - myfdarray[item] = None; - } - } - -} - -// Returns the HashMap returns a copy of the fdtable for a cage. Useful -// helper function for a caller that needs to examine the table. Likely could -// be more efficient by letting the caller borrow this... -// #[doc = include_str!("../docs/return_fdtable_copy.md")] -pub fn return_fdtable_copy(cageid: u64) -> HashMap { - - if !FDTABLE.contains_key(&cageid) { - panic!("Unknown cageid in fdtable access"); - } - - let mut myhashmap = HashMap::new(); - - let myfdarray = FDTABLE.get(&cageid).unwrap(); - for item in 0..FD_PER_PROCESS_MAX as usize { - if myfdarray[item].is_some() { - myhashmap.insert(item as u64,myfdarray[item].unwrap()); - } - } - myhashmap -} - - - -/******************* CLOSE SPECIFIC FUNCTIONALITY *******************/ - -// Helper for close. Returns a tuple of realfd, number of references -// remaining. -// #[doc = include_str!("../docs/close_virtualfd.md")] -pub fn close_virtualfd(cageid:u64, virtfd:u64) -> Result<(),threei::RetVal> { - if !FDTABLE.contains_key(&cageid) { - panic!("Unknown cageid in fdtable access"); - } - - let mut myfdarray = FDTABLE.get_mut(&cageid).unwrap(); - - - if myfdarray[virtfd as usize].is_some() { - let therealfd = myfdarray[virtfd as usize].unwrap().realfd; - - if therealfd == NO_REAL_FD { - // Let their code know this has been closed... - let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); - (closehandlers.unreal_handler)(myfdarray[virtfd as usize].unwrap().optionalinfo); - // Zero out this entry... - myfdarray[virtfd as usize] = None; - return Ok(()); - } - _decrement_realfd(therealfd); - // Zero out this entry... - myfdarray[virtfd as usize] = None; - return Ok(()); - } - Err(threei::Errno::EBADFD as u64) -} - - -// Register a series of helpers to be called for close. Can be called -// multiple times to override the older helpers. -// #[doc = include_str!("../docs/register_close_handlers.md")] -pub fn register_close_handlers(intermediate_handler: fn(u64), final_handler: fn(u64), unreal_handler: fn(u64)) { - // Unlock the table and set the handlers... - let mut closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); - closehandlers.intermediate_handler = intermediate_handler; - closehandlers.final_handler = final_handler; - closehandlers.unreal_handler = unreal_handler; -} - - -// Helpers to track the count of times each realfd is used -#[doc(hidden)] -fn _decrement_realfd(realfd:u64) -> u64 { - if realfd == NO_REAL_FD { - panic!("Called _decrement_realfd with NO_REAL_FD"); - } - - let newcount:u64 = REALFDCOUNT.get(&realfd).unwrap().value() - 1; - let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); - if newcount > 0 { - (closehandlers.intermediate_handler)(realfd); - REALFDCOUNT.insert(realfd,newcount); - } - else{ - (closehandlers.final_handler)(realfd); - } - newcount -} - -// Helpers to track the count of times each realfd is used -#[doc(hidden)] -fn _increment_realfd(realfd:u64) -> u64 { - if realfd == NO_REAL_FD { - return 0 - } - - // Get a mutable reference to the entry so we can update it. - return match REALFDCOUNT.get_mut(&realfd) { - Some(mut count) => { - *count += 1; - *count - } - None => { - REALFDCOUNT.insert(realfd, 1); - 1 - } - } -} - - - -/*************** Code for handling select() ****************/ - -use libc::fd_set; -use std::collections::HashSet; -use std::cmp; -use std::mem; - -// Helper to get an empty fd_set. Helper function to isolate unsafe code, -// etc. -pub fn _init_fd_set() -> fd_set { - let raw_fd_set:fd_set; - unsafe { - let mut this_fd_set = mem::MaybeUninit::::uninit(); - libc::FD_ZERO(this_fd_set.as_mut_ptr()); - raw_fd_set = this_fd_set.assume_init() - } - raw_fd_set -} - -// Helper to get a null pointer. -pub fn _get_null_fd_set() -> fd_set { - //unsafe{ptr::null_mut()} - // BUG!!! Need to fix this later. - _init_fd_set() -} - -pub fn _fd_set(fd:u64, thisfdset:&mut fd_set) { - unsafe{libc::FD_SET(fd as i32,thisfdset)} -} - -pub fn _fd_isset(fd:u64, thisfdset:&fd_set) -> bool { - unsafe{libc::FD_ISSET(fd as i32,thisfdset)} -} - -// Computes the bitmodifications and returns a (maxnfds, unrealset) tuple... -fn _do_bitmods(myfdarray:[Option;FD_PER_PROCESS_MAX as usize], nfds:u64, infdset: fd_set, thisfdset: &mut fd_set, mappingtable: &mut HashMap) -> Result<(u64,HashSet<(u64,u64)>),threei::RetVal> { - let mut unrealhashset:HashSet<(u64,u64)> = HashSet::new(); - // Iterate through the infdset and set those values as is appropriate - let mut highestpos = 0; - - // Clippy is somehow missing how pos is using bit. - #[allow(clippy::needless_range_loop)] - for bit in 0..nfds as usize { - let pos = bit as u64; - if _fd_isset(pos,&infdset) { - if let Some(entry) = myfdarray[bit] { - if entry.realfd == NO_REAL_FD { - unrealhashset.insert((pos,entry.optionalinfo)); - } - else { - mappingtable.insert(entry.realfd, pos); - _fd_set(entry.realfd,thisfdset); - // I add one because select expects nfds to be the max+1 - highestpos = cmp::max(highestpos, entry.realfd+1); - } - } - else { - return Err(threei::Errno::EINVAL as u64); - } - } - } - Ok((highestpos,unrealhashset)) -} - -// helper to call before calling select beneath you. Translates your virtfds -// to realfds. -// See: https://man7.org/linux/man-pages/man2/select.2.html for details / -// corner cases about the arguments. - -// I hate doing these, but don't know how to make this interface better... -#[allow(clippy::type_complexity)] -#[allow(clippy::too_many_arguments)] -// #[doc = include_str!("../docs/get_real_bitmasks_for_select.md")] -pub fn get_real_bitmasks_for_select(cageid:u64, nfds:u64, readbits:Option, writebits:Option, exceptbits:Option) -> Result<(u64, fd_set, fd_set, fd_set, [HashSet<(u64,u64)>;3], HashMap),threei::RetVal> { - - if nfds >= FD_PER_PROCESS_MAX { - return Err(threei::Errno::EINVAL as u64); - } - - if !FDTABLE.contains_key(&cageid) { - panic!("Unknown cageid in fdtable access"); - } - - let mut unrealarray:[HashSet<(u64,u64)>;3] = [HashSet::new(),HashSet::new(),HashSet::new()]; - let mut mappingtable:HashMap = HashMap::new(); - let mut newnfds = 0; - - // dashmaps are lockless, but usually I would grab a lock on the fdtable - // here... - let binding = FDTABLE.get(&cageid).unwrap(); - let thefdvec = *binding.value(); - - // putting results in a vec was the cleanest way I found to do this.. - let mut resultvec = Vec::new(); - - for (unrealoffset, inset) in [readbits,writebits, exceptbits].into_iter().enumerate() { - match inset { - Some(virtualbits) => { - let mut retset = _init_fd_set(); - let (thisnfds,myunrealhashset) = _do_bitmods(thefdvec,nfds,*virtualbits, &mut retset,&mut mappingtable)?; - resultvec.push(retset); - newnfds = cmp::max(thisnfds, newnfds); - unrealarray[unrealoffset] = myunrealhashset; - } - None => { - // This item is null. No unreal items - // BUG: Need to actually return null! - resultvec.push(_get_null_fd_set()); - unrealarray[unrealoffset] = HashSet::new(); - } - } - } - - Ok((newnfds, resultvec[0], resultvec[1], resultvec[2], unrealarray, mappingtable)) - -} - - -// helper to call after calling select beneath you. returns the fd_sets you -// need for your return from a select call and the number of unique flags -// set... - -// I hate doing these, but don't know how to make this interface better... -#[allow(clippy::type_complexity)] -#[allow(clippy::too_many_arguments)] -// #[doc = include_str!("../docs/get_virtual_bitmasks_from_select_result.md")] -pub fn get_virtual_bitmasks_from_select_result(nfds:u64, readbits:fd_set, writebits:fd_set, exceptbits:fd_set,unrealreadset:HashSet, unrealwriteset:HashSet, unrealexceptset:HashSet, mappingtable:HashMap) -> Result<(u64, fd_set, fd_set, fd_set),threei::RetVal> { - - // Note, I don't need the cage_id here because I have the mappingtable... - - if nfds >= FD_PER_PROCESS_MAX { - panic!("This shouldn't be possible because we shouldn't have returned this previously") - } - - let mut flagsset = 0; - let mut retvec = Vec::new(); - - for (inset,unrealset) in [(readbits,unrealreadset), (writebits,unrealwriteset), (exceptbits,unrealexceptset)] { - let mut retbits = _init_fd_set(); - for bit in 0..nfds as usize { - let pos = bit as u64; - if _fd_isset(pos,&inset)&& !_fd_isset(*mappingtable.get(&pos).unwrap(),&retbits) { - flagsset+=1; - _fd_set(*mappingtable.get(&pos).unwrap(),&mut retbits); - } - } - for virtfd in unrealset { - if !_fd_isset(virtfd,&retbits) { - flagsset+=1; - _fd_set(virtfd,&mut retbits); - } - } - retvec.push(retbits); - } - - Ok((flagsset,retvec[0],retvec[1],retvec[2])) - -} - - - -/********************** POLL SPECIFIC FUNCTIONS **********************/ - -// helper to call before calling poll beneath you. replaces the fds in -// the poll struct with virtual versions and returns the items you need -// to check yourself... -#[allow(clippy::type_complexity)] -// #[doc = include_str!("../docs/convert_virtualfds_to_real.md")] -pub fn convert_virtualfds_to_real(cageid:u64, virtualfds:Vec) -> (Vec, Vec<(u64,u64)>, Vec, HashMap) { - - if !FDTABLE.contains_key(&cageid) { - panic!("Unknown cageid in fdtable access"); - } - - let mut unrealvec = Vec::new(); - let mut realvec = Vec::new(); - let mut invalidvec = Vec::new(); - let thefdarray = *FDTABLE.get(&cageid).unwrap(); - let mut mappingtable:HashMap = HashMap::new(); - - // BUG?: I'm ignoring the fact that virtualfds can show up multiple times. - // I'm not sure this actually matters, but I didn't think hard about it. - for virtfd in virtualfds { - match thefdarray[virtfd as usize] { - Some(entry) => { - // always append the value here. NO_REAL_FD will be added - // in the appropriate places to tell them to handle those calls - // themself. - realvec.push(entry.realfd); - if entry.realfd == NO_REAL_FD { - unrealvec.push((virtfd,entry.optionalinfo)); - } - else{ - mappingtable.insert(entry.realfd, virtfd); - } - } - None => { - // Add this because they need to handle it if POLLNVAL is set. - // An exception should not be raised!!! - realvec.push(INVALID_FD); - invalidvec.push(virtfd); - } - } - } - - (realvec, unrealvec, invalidvec, mappingtable) -} - - - -// helper to call after calling poll. replaces the realfds the vector -// with virtual ones... -// #[doc = include_str!("../docs/convert_realfds_back_to_virtual.md")] -pub fn convert_realfds_back_to_virtual(realfds:Vec, mappingtable:HashMap) -> Vec { - - // I don't care what cage was used, and don't need to lock anything... - // I have the mappingtable! - - let mut virtvec = Vec::new(); - - for realfd in realfds { - virtvec.push(*mappingtable.get(&realfd).unwrap()); - } - - virtvec -} - - - - - - -/********************** TESTING HELPER FUNCTION **********************/ - -#[doc(hidden)] -// Helper to initialize / empty out state so we can test with a clean system... -// This is only used in tests, thus is hidden... -pub fn refresh() { - FDTABLE.clear(); - FDTABLE.insert(threei::TESTING_CAGEID,[Option::None;FD_PER_PROCESS_MAX as usize]); - let mut closehandlers = CLOSEHANDLERTABLE.lock().unwrap_or_else(|e| { - CLOSEHANDLERTABLE.clear_poison(); - e.into_inner() - }); - closehandlers.intermediate_handler = NULL_FUNC; - closehandlers.final_handler = NULL_FUNC; - closehandlers.unreal_handler = NULL_FUNC; - // Note, it doesn't seem that Dashmaps can be poisoned... -} \ No newline at end of file diff --git a/src/example_grates/dashmapvecglobal.rs b/src/example_grates/dashmapvecglobal.rs deleted file mode 100644 index 1db56c54..00000000 --- a/src/example_grates/dashmapvecglobal.rs +++ /dev/null @@ -1,969 +0,0 @@ -// DashMap;FD_PER_PROCESSS_MAX])> Space is -// ~30KB per cage w/ 1024 fds?!? -// Static DashMap. Let's see if having the FDTableEntries be in a Vector -// matters... - -use crate::safeposix::cage; -use crate::safeposix::syscalls::fs_calls::*; - -use std::io::Write; -use std::io; - -use super::threei; - - -use dashmap::DashMap; - -use lazy_static::lazy_static; -use serde::de::value; - -use std::collections::HashMap; - -use std::sync::Mutex; - -// This uses a Dashmap (for cages) with an array of FDTableEntry items. - -// Get constants about the fd table sizes, etc. -pub use super::commonconstants::*; - -// algorithm name. Need not be listed. Used in benchmarking output -#[doc(hidden)] -pub const ALGONAME: &str = "DashMapVecGlobal"; - -// It's fairly easy to check the fd count on a per-process basis (I just check -// when I would add a new fd). -// -// BUG: I will ignore the total limit for now. I would ideally do this on -// every creation, close, fork, etc. but it's a PITA to track this. - -// We will raise a panic anywhere we receive an unknown cageid. This frankly -// should not be possible and indicates some sort of internal error in our -// code. However, it is expected we could receive an invalid file descriptor -// when a cage makes a call. - -// In order to store this information, I'm going to use a DashMap which -// has keys of (cageid:u64) and values that are an array of FD_PER_PROCESS_MAX -// Option items. -// -// - -// This lets me initialize the code as a global. -lazy_static! { - - #[derive(Debug)] - pub static ref FDTABLE: DashMap>> = { - let m = DashMap::new(); - // Insert a cage so that I have something to fork / test later, if need - // be. Otherwise, I'm not sure how I get this started. I think this - // should be invalid from a 3i standpoint, etc. Could this mask an - // error in the future? - // m.insert(threei::TESTING_CAGEID,vec!(Option::None;FD_PER_PROCESS_MAX as usize)); - m - }; -} - -lazy_static! { - // This is needed for close and similar functionality. I need track the - // number of times a realfd is open - #[derive(Debug)] - pub static ref REALFDCOUNT: DashMap = { - DashMap::new() - }; - -} - -// Internal helper to hold the close handlers... -struct CloseHandlers { - intermediate: fn(u64), - last: fn(u64), - unreal: fn(u64), -} - -// #[doc = include_str!("../docs/init_empty_cage.md")] -pub fn init_empty_cage(cageid: u64) { - - assert!(!FDTABLE.contains_key(&cageid),"Known cageid in fdtable access"); - - FDTABLE.insert(cageid,vec!(Option::None;FD_PER_PROCESS_MAX as usize)); -} - -// #[doc = include_str!("../docs/translate_virtual_fd.md")] -pub fn translate_virtual_fd(cageid: u64, virtualfd: u64) -> Result { - - // They should not be able to pass a new cage I don't know. I should - // always have a table for each cage because each new cage is added at fork - // time - assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); - - return match FDTABLE.get(&cageid).unwrap()[virtualfd as usize] { - Some(tableentry) => Ok(tableentry.realfd), - None => Err(threei::Errno::EBADFD as u64), - }; -} - - -// This is fairly slow if I just iterate sequentially through numbers. -// However there are not that many to choose from. I could pop from a list -// or a set as well... Likely the best solution is to keep a count of the -// largest fd handed out and to just use this until you wrap. This will be -// super fast for a normal cage and will be correct in the weird case. -// Right now, I'll just implement the slow path and will speed this up -// later, if needed. -// #[doc = include_str!("../docs/get_unused_virtual_fd.md")] -pub fn get_unused_virtual_fd( - cageid: u64, - realfd: u64, - should_cloexec: bool, - optionalinfo: u64, -) -> Result { - - assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); - // Set up the entry so it has the right info... - // Note, a HashMap stores its data on the heap! No need to box it... - // https://doc.rust-lang.org/book/ch08-03-hash-maps.html#creating-a-new-hash-map - let myentry = FDTableEntry { - realfd, - should_cloexec, - optionalinfo, - }; - - let mut myfdrow = FDTABLE.get_mut(&cageid).unwrap(); - - // Check the fds in order. - for fdcandidate in 0..FD_PER_PROCESS_MAX { - // FIXME: This is likely very slow. Should do something smarter... - if myfdrow[fdcandidate as usize].is_none() { - // I just checked. Should not be there... - myfdrow[fdcandidate as usize] = Some(myentry); - _increment_realfd(realfd); - return Ok(fdcandidate); - } - } - - // I must have checked all fds and failed to find one open. Fail! - Err(threei::Errno::EMFILE as u64) -} - -// This is used for things like dup2, which need a specific fd... -// If the requested_virtualfd is used, I close it... -// #[doc = include_str!("../docs/get_specific_virtual_fd.md")] -pub fn get_specific_virtual_fd( - cageid: u64, - requested_virtualfd: u64, - realfd: u64, - should_cloexec: bool, - optionalinfo: u64, -) -> Result<(), threei::RetVal> { - - assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); - - // If you ask for a FD number that is too large, I'm going to reject it. - // Note that, I need to use the FD_PER_PROCESS_MAX setting because this - // is also how I'm tracking how many values you have open. If this - // changed, then these constants could be decoupled... - if requested_virtualfd > FD_PER_PROCESS_MAX { - return Err(threei::Errno::EBADF as u64); - } - - // Set up the entry so it has the right info... - // Note, a HashMap stores its data on the heap! No need to box it... - // https://doc.rust-lang.org/book/ch08-03-hash-maps.html#creating-a-new-hash-map - let myentry = FDTableEntry { - realfd, - should_cloexec, - optionalinfo, - }; - - // I moved this up so that if I decrement the same realfd, it calls - // the intermediate handler instead of the last one. - _increment_realfd(realfd); - let myoptionentry = FDTABLE.get(&cageid).unwrap()[requested_virtualfd as usize]; - // always add the new entry. I'm duping this first, before I close - // the old one because I need to ensure I've cleaned up state correctly - // before calling the close handlers... - FDTABLE.get_mut(&cageid).unwrap()[requested_virtualfd as usize] = Some(myentry); - - // Close the old entry, if I need to... - if let Some(entry) = myoptionentry { - - if entry.realfd == NO_REAL_FD { - // Let their code know this has been closed... - let unrealclosehandler = CLOSEHANDLERTABLE.lock().unwrap().unreal; - (unrealclosehandler)(entry.optionalinfo); - } - else { - _decrement_realfd(entry.realfd); - } - } - - Ok(()) -} - -// We're just setting a flag here, so this should be pretty straightforward. -// #[doc = include_str!("../docs/set_cloexec.md")] -pub fn set_cloexec(cageid: u64, virtualfd: u64, is_cloexec: bool) -> Result<(), threei::RetVal> { - - assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); - - // return EBADFD, if the fd is missing... - if FDTABLE.get(&cageid).unwrap()[virtualfd as usize].is_none() { - return Err(threei::Errno::EBADFD as u64); - } - // Set the is_cloexec flag - FDTABLE.get_mut(&cageid).unwrap()[virtualfd as usize].as_mut().unwrap().should_cloexec = is_cloexec; - Ok(()) -} - -// Super easy, just return the optionalinfo field... -// #[doc = include_str!("../docs/get_optionalinfo.md")] -pub fn get_optionalinfo(cageid: u64, virtualfd: u64) -> Result { - assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); - - return match FDTABLE.get(&cageid).unwrap()[virtualfd as usize] { - Some(tableentry) => Ok(tableentry.optionalinfo), - None => Err(threei::Errno::EBADFD as u64), - }; -} - -// We're setting an opaque value here. This should be pretty straightforward. -// #[doc = include_str!("../docs/set_optionalinfo.md")] -pub fn set_optionalinfo( - cageid: u64, - virtualfd: u64, - optionalinfo: u64, -) -> Result<(), threei::RetVal> { - - assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); - - // return EBADFD, if the fd is missing... - if FDTABLE.get(&cageid).unwrap()[virtualfd as usize].is_none() { - return Err(threei::Errno::EBADFD as u64); - } - - // Set optionalinfo or return EBADFD, if that's missing... - FDTABLE.get_mut(&cageid).unwrap()[virtualfd as usize].as_mut().unwrap().optionalinfo = optionalinfo; - Ok(()) -} - -// Helper function used for fork... Copies an fdtable for another process -// #[doc = include_str!("../docs/copy_fdtable_for_cage.md")] -pub fn copy_fdtable_for_cage(srccageid: u64, newcageid: u64) -> Result<(), threei::Errno> { - - assert!(FDTABLE.contains_key(&srccageid),"Unknown cageid in fdtable access"); - assert!(!FDTABLE.contains_key(&newcageid),"Known cageid in fdtable access"); - - // Insert a copy and ensure it didn't exist... - let hmcopy = FDTABLE.get(&srccageid).unwrap().clone(); - - // Increment copied items - for entry in FDTABLE.get(&srccageid).unwrap().iter() { - if entry.is_some() { - let thisrealfd = entry.unwrap().realfd; - if thisrealfd != NO_REAL_FD { - _increment_realfd(thisrealfd); - } - } - } - - assert!(FDTABLE.insert(newcageid, hmcopy).is_none()); - Ok(()) - // I'm not going to bother to check the number of fds used overall yet... - // Err(threei::Errno::EMFILE as u64), -} - -// This is mostly used in handling exit, etc. Returns the HashMap -// for the cage. -// #[doc = include_str!("../docs/remove_cage_from_fdtable.md")] -pub fn remove_cage_from_fdtable(cageid: u64) { - - assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); - - - // remove the item first and then we clean up and call their close - // handlers. - let myfdrow = FDTABLE.remove(&cageid).unwrap().1; - - for entry in myfdrow { - if entry.is_some() { - let therealfd = entry.unwrap().realfd; - if therealfd == NO_REAL_FD { - // Let their code know this has been closed... - let unrealclosehandler = CLOSEHANDLERTABLE.lock().unwrap().unreal; - (unrealclosehandler)(entry.unwrap().optionalinfo); - } - else{ - _decrement_realfd(therealfd); - } - } - } - -} - -// This removes all fds with the should_cloexec flag set. They are returned -// in a new hashmap... -// #[doc = include_str!("../docs/empty_fds_for_exec.md")] -pub fn empty_fds_for_exec(cageid: u64) { - - assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); - - let mut myfdrow = FDTABLE.get_mut(&cageid).unwrap(); - // I need to call all the close handlers at the end. So I need to - // get vector of them to do the operation on... - let mut closevec = Vec::new(); - - for item in 0..FD_PER_PROCESS_MAX as usize { - if myfdrow[item].is_some() && myfdrow[item].unwrap().should_cloexec { - let therealfd = myfdrow[item].unwrap().realfd; - let optionalinfo = myfdrow[item].unwrap().optionalinfo; - // handle this in a moment... - closevec.push((therealfd,optionalinfo)); - - // Always zero out the row before calling their handler - myfdrow[item] = None; - } - } - - // Need to drop the lock, before calling the handlers. - drop(myfdrow); - - // Now, we can call the close handlers! - for (therealfd,optionalinfo) in closevec { - if therealfd == NO_REAL_FD { - // Let their code know this has been closed... - let unrealclosehandler = CLOSEHANDLERTABLE.lock().unwrap().unreal; - (unrealclosehandler)(optionalinfo); - } - else{ - _decrement_realfd(therealfd); - } - } - -} - -// Returns the HashMap returns a copy of the fdtable for a cage. Useful -// helper function for a caller that needs to examine the table. Likely could -// be more efficient by letting the caller borrow this... -// #[doc = include_str!("../docs/return_fdtable_copy.md")] -#[must_use] // must use the return value if you call it. -pub fn return_fdtable_copy(cageid: u64) -> HashMap { - - assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); - - let mut myhashmap = HashMap::new(); - - let myfdrow = FDTABLE.get(&cageid).unwrap(); - for item in 0..FD_PER_PROCESS_MAX as usize { - if myfdrow[item].is_some() { - myhashmap.insert(item as u64,myfdrow[item].unwrap()); - } - } - myhashmap -} - - - -/******************* CLOSE SPECIFIC FUNCTIONALITY *******************/ - -lazy_static! { - // This holds the user registered handlers they want to have called when - // a close occurs. I did this rather than return messy data structures - // from the close, exec, and exit handlers because it seemed cleaner... - #[derive(Debug)] - static ref CLOSEHANDLERTABLE: Mutex = { - let c = CloseHandlers { - intermediate:NULL_FUNC, - last:NULL_FUNC, - unreal:NULL_FUNC, - }; - Mutex::new(c) - }; -} - - -// Helper for close. Returns a tuple of realfd, number of references -// remaining. -// #[doc = include_str!("../docs/close_virtualfd.md")] -pub fn close_virtualfd(cageid:u64, virtfd:u64) -> Result<(),threei::RetVal> { - - assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); - - // cloning this so I don't hold a lock and deadlock close handlers - let mut myfdrow = FDTABLE.get_mut(&cageid).unwrap().clone(); - - - if myfdrow[virtfd as usize].is_some() { - let therealfd = myfdrow[virtfd as usize].unwrap().realfd; - - if therealfd == NO_REAL_FD { - // Let their code know this has been closed... - let unrealclosehandler = CLOSEHANDLERTABLE.lock().unwrap().unreal; - let optionalinfo = myfdrow[virtfd as usize].unwrap().optionalinfo; - - // Zero out this entry... - myfdrow[virtfd as usize] = None; - - // call the handler last... - (unrealclosehandler)(optionalinfo); - return Ok(()); - } - // Zero out this entry... - myfdrow[virtfd as usize] = None; - - FDTABLE.insert(cageid, myfdrow.clone()); - - // always _decrement last as it may call the user handler... - _decrement_realfd(therealfd); - return Ok(()); - } - Err(threei::Errno::EBADFD as u64) -} - - -// Register a series of helpers to be called for close. Can be called -// multiple times to override the older helpers. -// #[doc = include_str!("../docs/register_close_handlers.md")] -pub fn register_close_handlers(intermediate: fn(u64), last: fn(u64), unreal: fn(u64)) { - // Unlock the table and set the handlers... - // TODO: I made a serious attempt to try to keep closehandlers that call - // close, etc. from deadlocking the system. More testing, etc. is needed. - let mut closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); - closehandlers.intermediate = intermediate; - closehandlers.last = last; - closehandlers.unreal = unreal; -} - - -// Helpers to track the count of times each realfd is used -#[doc(hidden)] -fn _decrement_realfd(realfd:u64) -> u64 { - - assert!(realfd != NO_REAL_FD, "Called _decrement_realfd with NO_REAL_FD"); - - // let newcount:u64 = REALFDCOUNT.get(&realfd).unwrap().value() - 1; - let newcount = match REALFDCOUNT.get(&realfd) { - Some(refvalue) => refvalue.value() - 1, - None => { - // println!("[FDTABLE] realfd: {:?}", realfd); - // println!("[FDTABLE] REALFDCOUNT:"); - // io::stdout().flush().unwrap(); - // for entry in REALFDCOUNT.iter() { - // let key = entry.key(); - // let value = entry.value(); - // println!("realfd: {:?}, Value: {:?}", key, value); - // io::stdout().flush().unwrap(); - // } - // println!("[FDTABLE] FDTABLE: "); - // io::stdout().flush().unwrap(); - // for entry in FDTABLE.iter() { - // let key = entry.key(); - // let value = entry.value(); - // let non_none_values: Vec<&FDTableEntry> = value.iter().filter_map(|v| v.as_ref()).collect(); - // println!("cageid: {:?}, Value: {:?}", key, non_none_values); - // io::stdout().flush().unwrap(); - // } - // io::stdout().flush().unwrap(); - panic!(); - } - }; - - // Doing this to release the lock so I can call it recursively... - let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); - let intermediateclosehandler = closehandlers.intermediate; - let lastclosehandler = closehandlers.last; - // release the lock... - drop(closehandlers); - if newcount > 0 { - // Update before calling their close handler in case they do operations - // inside the close handler which create / close fds... - REALFDCOUNT.insert(realfd,newcount); - (intermediateclosehandler)(realfd); - } - else{ - // Remove before calling their close handler in case they do operations - // inside the close handler which create / close fds... - REALFDCOUNT.remove(&realfd); - (lastclosehandler)(realfd); - } - newcount -} - -// Helpers to track the count of times each realfd is used -#[doc(hidden)] -fn _increment_realfd(realfd:u64) -> u64 { - if realfd == NO_REAL_FD { - return 0 - } - - // Get a mutable reference to the entry so we can update it. - return if let Some(mut count) = REALFDCOUNT.get_mut(&realfd) { - *count += 1; - *count - } else { - REALFDCOUNT.insert(realfd, 1); - 1 - } -} - - - -/*************** Code for handling select() ****************/ - -use libc::fd_set; -use std::collections::HashSet; -use std::cmp; -use std::mem; - -// Helper to get an empty fd_set. Helper function to isolate unsafe code, -// etc. -#[doc(hidden)] -#[must_use] // must use the return value if you call it. -pub fn _init_fd_set() -> fd_set { - let raw_fd_set:fd_set; - unsafe { - let mut this_fd_set = mem::MaybeUninit::::uninit(); - libc::FD_ZERO(this_fd_set.as_mut_ptr()); - raw_fd_set = this_fd_set.assume_init(); - } - raw_fd_set -} - -#[doc(hidden)] -pub fn _fd_set(fd:u64, thisfdset:&mut fd_set) { - unsafe{libc::FD_SET(fd as i32,thisfdset)} -} - -#[doc(hidden)] -#[must_use] // must use the return value if you call it. -pub fn _fd_isset(fd:u64, thisfdset:&fd_set) -> bool { - unsafe{libc::FD_ISSET(fd as i32,thisfdset)} -} - -// Computes the bitmodifications and returns a (maxnfds, unrealset) tuple... -#[doc(hidden)] -fn _do_bitmods(myfdrow:&[Option], nfds:u64, infdset: fd_set, thisfdset: &mut fd_set, mappingtable: &mut HashMap) -> Result<(u64,HashSet<(u64,u64)>),threei::RetVal> { - let mut unrealhashset:HashSet<(u64,u64)> = HashSet::new(); - // Iterate through the infdset and set those values as is appropriate - let mut highestpos = 0; - - // Clippy is somehow missing how pos is using bit. - #[allow(clippy::needless_range_loop)] - for bit in 0..nfds as usize { - let pos = bit as u64; - if _fd_isset(pos,&infdset) { - if let Some(entry) = myfdrow[bit] { - if entry.realfd == NO_REAL_FD { - unrealhashset.insert((pos,entry.optionalinfo)); - } - else { - mappingtable.insert(entry.realfd, pos); - _fd_set(entry.realfd,thisfdset); - // I add one because select expects nfds to be the max+1 - highestpos = cmp::max(highestpos, entry.realfd+1); - } - } - else { - return Err(threei::Errno::EINVAL as u64); - } - } - } - Ok((highestpos,unrealhashset)) -} - -// helper to call before calling select beneath you. Translates your virtfds -// to realfds. -// See: https://man7.org/linux/man-pages/man2/select.2.html for details / -// corner cases about the arguments. - -// I hate doing these, but don't know how to make this interface better... -#[allow(clippy::type_complexity)] -#[allow(clippy::too_many_arguments)] -// #[doc = include_str!("../docs/get_real_bitmasks_for_select.md")] -pub fn get_real_bitmasks_for_select(cageid:u64, nfds:u64, readbits:Option, writebits:Option, exceptbits:Option) -> Result<(u64, Option, Option, Option, [HashSet<(u64,u64)>;3], HashMap),threei::RetVal> { - - if nfds >= FD_PER_PROCESS_MAX { - return Err(threei::Errno::EINVAL as u64); - } - - assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); - - let mut unrealarray:[HashSet<(u64,u64)>;3] = [HashSet::new(),HashSet::new(),HashSet::new()]; - let mut mappingtable:HashMap = HashMap::new(); - let mut newnfds = 0; - - // dashmaps are lockless, but usually I would grab a lock on the fdtable - // here... - let binding = FDTABLE.get(&cageid).unwrap(); - let thefdrow = binding.value().clone(); - - // putting results in a vec was the cleanest way I found to do this.. - let mut resultvec = Vec::new(); - - for (unrealoffset, inset) in [readbits,writebits, exceptbits].into_iter().enumerate() { - #[allow(clippy::single_match_else)] // I find this clearer - match inset { - Some(virtualbits) => { - let mut retset = _init_fd_set(); - let (thisnfds,myunrealhashset) = _do_bitmods(&thefdrow,nfds,*virtualbits, &mut retset,&mut mappingtable)?; - resultvec.push(Some(retset)); - newnfds = cmp::max(thisnfds, newnfds); - unrealarray[unrealoffset] = myunrealhashset; - } - None => { - // This item is null. No unreal items - resultvec.push(None); - unrealarray[unrealoffset] = HashSet::new(); - } - } - } - - Ok((newnfds, resultvec[0], resultvec[1], resultvec[2], unrealarray, mappingtable)) - -} - - -// helper to call after calling select beneath you. returns the fd_sets you -// need for your return from a select call and the number of unique flags -// set... - -// I hate doing these, but don't know how to make this interface better... -#[allow(clippy::type_complexity)] -#[allow(clippy::too_many_arguments)] -// I given them the hashmap, so don't need flexibility in what they return... -#[allow(clippy::implicit_hasher)] -// #[doc = include_str!("../docs/get_virtual_bitmasks_from_select_result.md")] -pub fn get_virtual_bitmasks_from_select_result(nfds:u64, readbits:Option, writebits:Option, exceptbits:Option,unrealreadset:HashSet, unrealwriteset:HashSet, unrealexceptset:HashSet, mappingtable:&HashMap) -> Result<(u64, Option, Option, Option),threei::RetVal> { - - // Note, I don't need the cage_id here because I have the mappingtable... - - assert!(nfds < FD_PER_PROCESS_MAX,"This shouldn't be possible because we shouldn't have returned this previously"); - - let mut flagsset = 0; - let mut retvec = Vec::new(); - - for (insetoption,unrealset) in [(readbits,unrealreadset), (writebits,unrealwriteset), (exceptbits,unrealexceptset)] { - // If I don't have any data, just return None (NULL) and skip... - if insetoption.is_none()&&unrealset.is_empty() { - retvec.push(None); - continue; - } - - let mut retbits = _init_fd_set(); - if let Some(inset) = insetoption { - for bit in 0..nfds as usize { - let pos = bit as u64; - if _fd_isset(pos,&inset)&& !_fd_isset(*mappingtable.get(&pos).unwrap(),&retbits) { - flagsset+=1; - _fd_set(*mappingtable.get(&pos).unwrap(),&mut retbits); - } - } - } - for virtfd in unrealset { - if !_fd_isset(virtfd,&retbits) { - flagsset+=1; - _fd_set(virtfd,&mut retbits); - } - } - retvec.push(Some(retbits)); - } - - Ok((flagsset,retvec[0],retvec[1],retvec[2])) - -} - - - -/********************** POLL SPECIFIC FUNCTIONS **********************/ - -// helper to call before calling poll beneath you. replaces the fds in -// the poll struct with virtual versions and returns the items you need -// to check yourself... -#[allow(clippy::type_complexity)] -// #[doc = include_str!("../docs/convert_virtualfds_to_real.md")] -#[must_use] // must use the return value if you call it. -pub fn convert_virtualfds_to_real(cageid:u64, virtualfds:Vec) -> (Vec, Vec<(u64,u64)>, Vec, HashMap) { - - assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); - - let mut unrealvec = Vec::new(); - let mut realvec = Vec::new(); - let mut invalidvec = Vec::new(); -// I can't do this because it doesn't implement the copy trait... -// let thefdrow = *FDTABLE.get(&cageid).unwrap(); - let binding = FDTABLE.get(&cageid).unwrap(); - let thefdrow = binding.value().clone(); - let mut mappingtable:HashMap = HashMap::new(); - - // BUG?: I'm ignoring the fact that virtualfds can show up multiple times. - // I'm not sure this actually matters, but I didn't think hard about it. - for virtfd in virtualfds { - if let Some(entry) = thefdrow[virtfd as usize] { - // always append the value here. NO_REAL_FD will be added - // in the appropriate places to tell them to handle those calls - // themself. - realvec.push(entry.realfd); - if entry.realfd == NO_REAL_FD { - unrealvec.push((virtfd,entry.optionalinfo)); - } - else{ - mappingtable.insert(entry.realfd, virtfd); - } - } - else { - // Add this because they need to handle it if POLLNVAL is set. - // An exception should not be raised!!! - realvec.push(INVALID_FD); - invalidvec.push(virtfd); - } - } - - (realvec, unrealvec, invalidvec, mappingtable) -} - - - -// helper to call after calling poll. replaces the realfds the vector -// with virtual ones... -// #[doc = include_str!("../docs/convert_realfds_back_to_virtual.md")] -// I given them the hashmap, so don't need flexibility in what they return... -#[allow(clippy::implicit_hasher)] -#[must_use] // must use the return value if you call it. -pub fn convert_realfds_back_to_virtual(realfds:Vec, mappingtable:&HashMap) -> Vec { - - // I don't care what cage was used, and don't need to lock anything... - // I have the mappingtable! - - let mut virtvec = Vec::new(); - - for realfd in realfds { - virtvec.push(*mappingtable.get(&realfd).unwrap()); - } - - virtvec -} - - - -/********************** EPOLL SPECIFIC FUNCTIONS **********************/ - - -// Okay this adds a big wrinkle, epollfds. The reason these are complex is -// multi-fold: -// 1) they themselves are file descriptors and take up a slot. -// 2) a epollfd can point to any number of other fds -// 3) an epollfd can point to epollfds, which can point to other epollfds, etc. -// and possibly cause a loop to occur (which is an error) -// 4) an epollfd can point to a mix of virtual and realfds. -// -// My thinking is this is handled as similarly to poll as possible. We push -// off the problem of understanding what the event types are to the implementer -// of the library. -// -// In my view, epoll_wait() is quite simple to support. One basically just -// keeps a list of virtual fds for this epollfd and their corresponding event -// types, which they may need to poll themselves. After this, they handle the -// call. -// -// epoll_create makes a new fd type, which really is unfortunate. To this -// point, I haven't had to care about anything except in-memory fds (unreal), -// and doing the virtual <-> real mappings. The caller can decide whether to -// create an underlying epollfd when this is called, when epoll_ctl is called -// to add a realfd, etc. -// -// epoll_ctl is complex, but really has the same fundamental problem as -// epoll_create: the epollfd. -// -// What if I just ignore the epollfd problem by just making another table -// for epoll information? Then what I do is set the realfd to EPOLLFD and -// have optionalinfo point into the epollfd table. If I do this, then when -// epoll_create is called, if it contains realfds, those need to be passed -// down to the underlying epoll_create. Similarly, when epoll_ctl is called, -// we either modify our data or return the realfd... -// -// Interestingly, this actually would be just as easy to build on top of the -// fdtables library as into it. -// -// Each epollfd will have some virtual fds associated with it. Each of those -// will have an event mask. So I'll have a mutex around an EPollTable struct. -// This contains the next available entry and an epollhashmap. -// I use a hashmap here to better support removing and modifying items. - -// Note, I'm defining a bunch of symbols myself because libc doesn't import -// them on systems that don't support epoll and I want to be able to build -// the code anywhere. See commonconstants.rs for more info. - -// Design notes: I'm not adding realfds. I return them when you do a epoll_ctl -// operation that tries to add them. So, I only have unrealfds in my epoll -// structures. - -// TODO: I don't clean up this table yet. I probably should when the last -// reference to a fd is closed, but this bookkeeping seems excessive at this -// time... -// #[derive(Clone, Debug)] -// struct EPollTable { -// highestneverusedentry: u64, // Never resets (even after close). Used to -// // let us quickly get an unused entry -// thisepolltable: HashMap>, // the epollentry -> -// // virtfd -> -// // event map -// realfdtable: HashMap, // the epollentry -> realfd map. I need -// // this because the realfd field in the -// // main data structure is EPOLLFD -// } - -// lazy_static! { - -// #[derive(Debug)] -// static ref EPOLLTABLE: Mutex = { -// let newetable = HashMap::new(); -// let newrealfdtable = HashMap::new(); -// let m = EPollTable { -// highestneverusedentry:0, -// thisepolltable:newetable, -// realfdtable:newrealfdtable, -// }; -// Mutex::new(m) -// }; -// } - - -// // #[doc = include_str!("../docs/epoll_create_helper.md")] -// pub fn epoll_create_helper(cageid:u64, realfd:u64, should_cloexec:bool) -> Result { - -// let mut ept = EPOLLTABLE.lock().unwrap(); - -// // I'll use my other functions to make this easier. -// // return the same errno (EMFile), if we get one -// let newepollfd = get_unused_virtual_fd(cageid, EPOLLFD, should_cloexec, ept.highestneverusedentry)?; - -// let newentry = ept.highestneverusedentry; -// ept.highestneverusedentry+=1; -// // add in my realfd. -// ept.realfdtable.insert(newentry,realfd); -// // if it errored out above that is okay. I haven't changed any state yet. -// ept.thisepolltable.insert(newentry, HashMap::new()); -// Ok(newepollfd) - -// } - - -// // #[doc = include_str!("../docs/try_epoll_ctl.md")] -// pub fn try_epoll_ctl(cageid:u64, epfd:u64, op:i32, virtfd:u64, event:epoll_event) -> Result<(u64,u64),threei::RetVal> { - -// assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); - -// if epfd == virtfd { -// return Err(threei::Errno::EINVAL as u64); -// } - -// // Is the epfd ok? -// let epollentrynum:u64 = match FDTABLE.get(&cageid).unwrap()[epfd as usize] { -// None => { -// return Err(threei::Errno::EBADF as u64); -// }, -// // Do I need to have EPOLLFDs here too? -// Some(tableentry) => { -// if tableentry.realfd != EPOLLFD { -// return Err(threei::Errno::EINVAL as u64); -// } -// tableentry.optionalinfo -// }, -// }; - -// // Okay, I know which table entry and verified the virtfd... - -// let mut epttable = EPOLLTABLE.lock().unwrap(); -// let realepollfd = *epttable.realfdtable.get(&epollentrynum).unwrap(); -// let eptentry = epttable.thisepolltable.get_mut(&epollentrynum).unwrap(); - -// // check if the virtfd is real and error... -// // I don't care about its contents except to ensure it isn't real... -// if let Some(tableentry) = FDTABLE.get(&cageid).unwrap()[virtfd as usize] { -// // Do I need to have EPOLLFDs here too? -// if tableentry.realfd != NO_REAL_FD { -// // Return realfds because the caller should handle them instead -// // I only track unrealfds. -// if tableentry.realfd == EPOLLFD { -// // BUG: How should I be doing this, really!?! -// println!("epollfds acting on epollfds is not supported!"); -// } -// return Ok((realepollfd,tableentry.realfd)); -// } -// } -// else { -// return Err(threei::Errno::EBADF as u64); -// } - -// // okay, virtfd is real... - -// match op { -// EPOLL_CTL_ADD => { -// if eptentry.contains_key(&virtfd) { -// return Err(threei::Errno::EEXIST as u64); -// } -// // BUG: Need to check for ELOOP here... - -// eptentry.insert(virtfd, event); -// }, -// EPOLL_CTL_MOD => { -// if !eptentry.contains_key(&virtfd) { -// return Err(threei::Errno::ENOENT as u64); -// } -// eptentry.insert(virtfd, event); -// }, -// EPOLL_CTL_DEL => { -// if !eptentry.contains_key(&virtfd) { -// return Err(threei::Errno::ENOENT as u64); -// } -// eptentry.remove(&virtfd); -// }, -// _ => { -// return Err(threei::Errno::EINVAL as u64); -// }, -// }; -// Ok((realepollfd,NO_REAL_FD)) -// } - - -// // #[doc = include_str!("../docs/get_epoll_wait_data.md")] -// pub fn get_epoll_wait_data(cageid:u64, epfd:u64) -> Result<(u64,HashMap),threei::RetVal> { - -// assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); - -// // Note that because I don't track realfds or deal with epollfds, I just -// // return the epolltable... -// let epollentrynum:u64 = match FDTABLE.get(&cageid).unwrap()[epfd as usize] { -// None => { -// return Err(threei::Errno::EBADF as u64); -// }, -// // Do I need to have EPOLLFDs here too? -// Some(tableentry) => { -// if tableentry.realfd != EPOLLFD { -// return Err(threei::Errno::EINVAL as u64); -// } -// tableentry.optionalinfo -// }, -// }; - -// let epttable = EPOLLTABLE.lock().unwrap(); -// Ok((*epttable.realfdtable.get(&epollentrynum).unwrap(),epttable.thisepolltable[&epollentrynum].clone())) -// } - - - -// /********************** TESTING HELPER FUNCTION **********************/ - -// #[doc(hidden)] -// // Helper to initialize / empty out state so we can test with a clean system... -// // This is only used in tests, thus is hidden... -// pub fn refresh() { -// FDTABLE.clear(); -// FDTABLE.insert(threei::TESTING_CAGEID,vec![Option::None;FD_PER_PROCESS_MAX as usize]); -// let mut closehandlers = CLOSEHANDLERTABLE.lock().unwrap_or_else(|e| { -// CLOSEHANDLERTABLE.clear_poison(); -// e.into_inner() -// }); -// closehandlers.intermediate = NULL_FUNC; -// closehandlers.last = NULL_FUNC; -// closehandlers.unreal = NULL_FUNC; -// // Note, it doesn't seem that Dashmaps can be poisoned... -// } \ No newline at end of file diff --git a/src/example_grates/imfs_grate.rs b/src/example_grates/imfs_grate.rs deleted file mode 100644 index 9864a432..00000000 --- a/src/example_grates/imfs_grate.rs +++ /dev/null @@ -1,218 +0,0 @@ -// Example grates -// -// handle file requests internally (need inode / dir, fd table) -// ex: in memory file system, network file system, separate /tmp per-process, -// lindfs -// -// note: -// it's slightly odd I need a fd table here. I guess it is custom to the -// grate though. -// - -// ******************************************************************* -// *** This file contains the sketch of Lind's in memory fs *** -// *** THIS CODE WILL NOT RUN! IT IS MEANT TO ILLUSTRATE *** -// *** MY THINKING ABOUT HOW SOMETHING LIKE THIS WOULD *** -// *** WORK. WE WILL MAKE A WORKING GRATE ONCE WE *** -// *** UNDERSTAND THE INTERFACES AND CHALLENGES BETTER. *** -// ******************************************************************* - - - - -use lind_encasement; -use lind_3i; -use lind_3i::constants::*; - -// I'm going to include this, which will use fdtable extensively... -use virt_fsmetadata; - -/**************** virt_fsmetadata selected exerpts *****************/ - -pub struct data_storage_interface { - pub fn create_item(item_id:u64); - pub fn open_item(item_id:u64); - pub fn close_item(item_id:u64); - pub fn store_data(targetcage: u64, item_id:u64, position:u64; data: *[u8], length:u64) -> Result<...>; - pub fn read_data(targetcage: u64, item_id:u64, position:u64; data: *[u8], amount:u64) -> Result<...>; - // could implement or add others... - pub fn log_action(...); - - -}; - -// I'm assuming that we always know all of the metadata because we virtualize -// it. This means we know the size of every file, etc. - -/*** All the inode, data structure code goes here... ***/ - - - -// We hook in the handlers for read, write, etc. -pub fn setup_interposition(calls_to_integrate:data_storage_interface) { - // The encasement library will help w/ syscall replacements for my children - let mut syscall_replacements: Vec = Vec::new(); - - // I handle all open calls, etc... - syscall_replacements.push(repltable{syscallid:PIPE_SYSID, func:open_syscall}); - - // Let's handle read and write... - syscall_replacements.push(repltable{syscallid:READ_SYSID, func:read_syscall}); - syscall_replacements.push(repltable{syscallid:WRITE_SYSID, func:write_syscall}); - - syscall_replacements.push(repltable{syscallid:MMAP_SYSID, func:mmap_syscall}); - // And so on until we have all the file system calls... - - //... until finally - lind_encasement.replace_syscalls(syscall_replacements); -} - -// I'm assuming that how the above calls are used is quite self-evident. -// In essence, we just have the existing interface for most things, but the -// above extracts out the file storage. - -// This gets overridden... As would related calls like munmap() -pub fn mmap_syscall() { - ... -} - - -pub fn initialize_fs_metadata() { - // I'll either make this fresh or read it in here... - ... -} - - -// The code here keeps a fdtable, inode information, directory information, -// etc, much the same way as the existing code. When we open something (which -// is not open), we call open_item - - - - -/**************** Unique code for this implementation ***************/ - -pub fn init_imfs() { - // Create a global data structure which allocates in memory blocks to store - // the things it needs to persist. -} - - -pub fn my_create_item(item_id:u64) { - // create an entry in the item table. The value stored is the fd. - - if item_id in itemtable { - panic!("Item already open"); - } - // Ensure we are creating this item or panic! - itemtable[item_id] = _make_block_list(); -} - - -pub fn my_store_data(targetcageid: u64, item_id:u64, position:u64; data: *[u8], length:u64) -> Result<...> { - - // This is my custom way to handle storing metadata... - let mut source_data_pos = data; - - let currentlength = length; - // loops through while getting and/or locating blocks - while currentlength > 0 { - let blockptr,allowedamount = _alloc_or_get_block_address(itemtable[item_id],position,currentlength); - lind_3i.read_from_cage(targetcageid, source_data_pos, blockptr, allowedamount); - currentlength -= allowedamount; - source_data_pos += allowedamount; - - } - return length; -} - - -pub fn my_read_data(targetcageid: u64, item_id:u64, position:u64; data: *[u8], amount:u64) -> Result<...> { - // This is my custom way to handle reading in data... - - let mut writedatapos = data; - // Walk through the blocks and use - for blockpos,length in _get_block_location_iter(item_id,position,amount) { - lind_3i.write_to_cage(targetcageid,blockpos, writedatapos,length); - writedatapos +=length; - } - - // I always return it all (?virt_metadata tracks the length?) - return amount; -} - - -// I'm going to interpose here, so I need to have the full interface... -pub fn my_mmap_syscall(cageid: u64, targetcageid: u64, callid: u64, addr: u64, length: u64, prot: u64, flags: u64, fd: u64, offset: u64) -> u64 { - // *mmap(void addr[.length], size_t length, int prot, int flags, int fd, off_t offset); - - // do I need to call their code to set anything up, like file descriptors? - - - if ! MAP_ANONYMOUS & flags { - // If this is file-backed, I would loop through and allocate the - // blocks, much like with my_store_data(...). - - } - else { // MAP_ANONYMOUS - // I probably don't understand all the nuances of ANONYMOUS mapping, - // but I'm assuming I can just zero out the blocks of memory... - if flags & MAP_PRIVATE { - // if not shared, likely okay to just do nothing but zero the - // memory... - return...; - } - // I'm not sure how to handle MAP_SHARED. Likely need to set up the - // shared memory mapping on fork... - // However, we don't currently handle this in lindfs... - } - - - // Now I loop through and allocated shared memory for each block so that - // it is mapped from my cache into the cage's memory... - -} - -// munmap is similar, but easier. Just removes memory mappings... - - -// We probably need to do something on fork so that we can handle mmap -// correctly... -pub fn my_fork_syscall(cageid: u64, targetcageid: u64, callid: u64, addr: u64, length: u64, prot: u64, flags: u64, fd: u64, offset: u64) -> u64 { - - // Need to handle MAP_SHARED mappings here, so they are mapped shared - // into the child -} - - - - -fn main() { - // Setup my item table... - init_item_table(); - - // Have the virtual_fs initialize itself... - virt_metadata.initialize_fs_metadata(); - - // Register my calls - let mycalls = virt_metadata::data_storage_interface { - create_item: my_create_item, - open_item: virt_metadata::NOOP, - close_item: virt_metadata::NOOP, - store_data: my_store_data, - read_data: my_read_data, - // could implement or add others... - log_action:virt_metadata::DEFAULT, - // Don't persist metadata, since it's an imfs... - store_directories:false, - store_inodes:false, - }; - - - // need to replace calls with the right ones for our children - virt_metadata.setup_interposition(mycalls); - - // Need to instantiate child cage(s) here... - lind_encasement.initialize_children_and_consume_thread(); - // Never reaches here! -} diff --git a/src/example_grates/imp_grate.rs b/src/example_grates/imp_grate.rs deleted file mode 100644 index 77fd1e57..00000000 --- a/src/example_grates/imp_grate.rs +++ /dev/null @@ -1,242 +0,0 @@ - -// -// custom handle non-file requests internally (fd table) -// ex: in memory pipes, loopback sockets, unix domain sockets, etc. -// -// note: -// How do I reserve the fd I need here? Do I have a call to reserve a fd? -// If so, I likely need a way to close or release it as well... -// I also should keep a fd table to see how to handle this... -// - -// ******************************************************************* -// *** This file contains the sketch of an in-memory pipe handler *** -// *** THIS CODE WILL NOT RUN! IT IS MEANT TO ILLUSTRATE *** -// *** MY THINKING ABOUT HOW SOMETHING LIKE THIS WOULD *** -// *** WORK. WE WILL MAKE A WORKING GRATE ONCE WE *** -// *** UNDERSTAND THE INTERFACES AND CHALLENGES BETTER. *** -// ******************************************************************* - -use fdtable; -use lind_3i; -use lind_3i::constants::*; -use lind_encasement; - - -// Initialize the circular buffers for the in memory pipes -def init_circular_buffer_table() { - // do whatever initialization I need to for the circular buffers... -} - -// Get a new, unused pipe number... -fn _get_pipe_num() { -} - -// handle pipe creation -pub fn my_pipe_syscall(cageid: u64, targetcageid: u64, callid: u64, pipefds: u64, _: u64, _: u64, _: u64, _: u64, _: u64) -> u64 { - // int pipe(int pipefd[2]); - - let real_read_fd = lind_3i::reserve_fd(targetcageid,None); - let real_write_fd = lind_3i::reserve_fd(targetcageid,None); - // size of a file descriptor in bytes... - const FD_SIZE = 4; - - // Adds an entry to the table. The CLOEXEC flag is needed so we know - // if we need to clobber this on exec or not. - let mypipenum = _get_pipe_num(); - let virt_read_pipe_fd =fdtable::get_unused_virtual_fd(targetcageid,real_read_fd,false,mypipenum); - let virt_write_pipe_fd = fdtable::get_unused_virtual_fd(targetcageid,real_write_fd,false,mypipenum); - - // Writing the fds back to the child... - lind_3i::write_to_cage(targetcageid,pipefds,virt_read_pipe_fd,FD_SIZE); - pipefds += FD_SIZE; - lind_3i::write_to_cage(targetcageid,pipefds,virt_write_pipe_fd,FD_SIZE); - - // No need to call below me... - - // Return success! - return 0; - -} - -pub fn my_read_syscall(cageid: u64, targetcageid: u64, callid: u64, fd: u64, buf: u64, count: u64, _: u64, _: u64, _: u64) -> u64 { - // ssize_t read(int fd, void buf[.count], size_t count); - - - /***** Read from the circular buffer, block, etc. here... *****/ - lind_3i::write_to_cage(targetcageid,buf,local_buffer,count); - - - return count; - - /**** End specific pipe logic */ - -} - -pub fn my_write_syscall(cageid: u64, targetcageid: u64, callid: u64, fd: u64, buf: u64, count: u64, _: u64, _: u64, _: u64) -> u64 { - // ssize_t write(int fd, const void buf[.count], size_t count); - - - /***** Write to the circular buffer, block, etc. here... *****/ - lind_3i::read_from_cage(targetcageid,local_buffer,buf,count); - - - return count; - - /**** End specific pipe logic */ - -} - - -pub fn my_close_syscall(cageid: u64, targetcageid: u64, callid: u64, fd: u64, buf: u64, count: u64, _: u64, _: u64, _: u64) -> u64 { - // int close(int fd); - - /***** clean up the buffer, if needed ****/ - - return 0; - - /**** End specific pipe logic */ - -} - -pub fn my_select_helper(targetcageid: u64, callid: u64, fdvec: Vec) -> Vec { - // Select, poll, etc. are a bit of a mess. Maybe it makes sense to have - // a helper instead? - - /* call with a vector of file descriptors and returns an vector of fds - * with an enum with a statue for each POLLERR, POLLHUP, POLLIN, POLLOUT - * etc. - */ - - - return ...; - - /**** End specific pipe logic */ - -} - - - - - - - - - - - - -/************** Example pseudocode from the fdtable code *****************/ - -pub fn fork_syscall(cageid: u64, targetcageid: u64, callid: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64, arg6: u64) -> u64 { - // int fork(); - let newcageid= lind_encasement::handle_fork(targetcageid); - - // Make a copy of the parent's FD table... - _dup_row(targetcageid,newcageid); - return newcageid; - -} - -pub fn exec_syscall(cageid: u64, targetcageid: u64, callid: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64, arg6: u64) -> u64 { - - // close fds that were close on exec... - _handle_cloexec(targetcageid); - - return MAKE_SYSCALL(targetcageid, callid, arg1, arg2, arg3, arg4, arg5, arg6); - -} - -// dup, etc. are handlede here as well... - - -// This is the basic logic for all of the system calls that fdtable handles. -// It decides where to route things... -fn _route_syscall_or_calldown(cageid: u64, targetcageid: u64, callid: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64, arg6: u64) -> u64 { - // close(fd); - - if _fd_table_in(targetcageid,arg1) { - // Call handler just calls the appropriate system call function from - // the table... - return call_handler(cageid,targetcageid, callid, arg1,arg2,arg3,arg4,arg5,arg6) - else { - // Call beneath us... - return MAKE_SYSCALL(targetcageid, callid, arg1, arg2, arg3, arg4, arg5, arg6); - } - -} - -pub fn boilerplate_init_for_fdtables(Vec ,enum handler) { - - // Let fdtable handle these (and likely I need to add others... This - // most likely would be handled for me via a function call to initialize - // these at the same time instead of me doing it individually... - syscall_replacements::push(repltable{syscallid:FORK_SYSID, func:fdtable::fork_syscall}); - syscall_replacements::push(repltable{syscallid:EXEC_SYSID, func:fdtable::exec_syscall}); - syscall_replacements::push(repltable{syscallid:DUP_SYSID, func:fdtable::dup_syscall}); - syscall_replacements::push(repltable{syscallid:DUP2_SYSID, func:fdtable::dup2_syscall}); - - // Ignore open because we don't care about files... If we handled named - // pipes, we would add open... -// syscall_replacements::push(repltable{syscallid:OPEN_SYSID, func:lind_encasement::DEFAULT}); - if handler == CALLDOWN { - add _route_syscall_or_calldown to essentially all calls... - - } - -} - -/*************************** MAIN *******************************/ - - - -// This sets up the calls so that the right things are interposed... -fn setup_interposition() { - // The encasement library will help w/ syscall replacements for my children - let mut syscall_replacements: Vec = Vec::new(); - - // This sets up basic handlers, and says unknown fds should call to the - // grate below... This is useful when we only want to intercept some calls - // but do want to pass others through. - boilerplate_init_for_fdtables(syscall_replacements,CALLDOWN); - - // I handle all pipe calls in my own code... - syscall_replacements::push(repltable{syscallid:PIPE_SYSID, func:my_pipe_syscall}); - - // Let's handle read and write... - syscall_replacements::push(repltable{syscallid:READ_SYSID, func:fdtable::read_syscall}); - // add my handler for read. Will only be called on fds I added to the - // table. Other FDs will call below... - fdtable::add_handler(syscallid::READ_SYSID, my_read_syscall); - syscall_replacements::push(repltable{syscallid:WRITE_SYSID, func:fdtable::write_syscall}); - fdtable::add_handler(syscallid::WRITE_SYSID, my_write_syscall); - - // add my handler for close. Only called on my fds - syscall_replacements::push(repltable{syscallid:CLOSE_SYSID, func:fdtable::close_syscall}); - fdtable::add_handler(syscallid::CLOSE_SYSID, my_close_syscall); - - - // This handles select, poll, etc. in a clean way. Only called for my fds - fdtable::add_select_helper(my_select_helper); - - - lind_encasement::replace_syscalls(syscall_replacements); - -} - - -fn main() { - // do my setup... Where does this live? Rust doesn't like globals, - // but I need to share this... - fdtable::get_new_table(); - - // If I'm setting up circular buffers, do it here... - init_circular_buffer_table(); - - // need to replace calls with the right ones for our children - setup_interposition(); - - // Need to instantiate child cage(s) here... - lind_encasement::initialize_children_and_consume_thread(); - // Never reaches here! -} diff --git a/src/example_grates/lindfs_grate.rs b/src/example_grates/lindfs_grate.rs deleted file mode 100644 index f00acfe4..00000000 --- a/src/example_grates/lindfs_grate.rs +++ /dev/null @@ -1,215 +0,0 @@ -// Example grates -// -// handle file requests internally (need inode / dir, fd table) -// ex: in memory file system, network file system, separate /tmp per-process, -// lindfs -// -// note: -// it's slightly odd I need a fd table here. I guess it is custom to the -// grate though. -// - -// ******************************************************************* -// *** This file contains the sketch of Lind's safeposix_rust fs *** -// *** THIS CODE WILL NOT RUN! IT IS MEANT TO ILLUSTRATE *** -// *** MY THINKING ABOUT HOW SOMETHING LIKE THIS WOULD *** -// *** WORK. WE WILL MAKE A WORKING GRATE ONCE WE *** -// *** UNDERSTAND THE INTERFACES AND CHALLENGES BETTER. *** -// ******************************************************************* - - -use lind_encasement; -use lind_3i; -use lind_3i::constants::*; - -// I'm going to include this, which will use fdtable extensively... -use virt_fsmetadata; - -/**************** virt_fsmetadata selected exerpts *****************/ - -pub struct data_storage_interface { - pub fn create_item(item_id:u64); - pub fn open_item(item_id:u64); - pub fn close_item(item_id:u64); - pub fn store_data(targetcage: u64, item_id:u64, position:u64; data: *[u8], length:u64) -> Result<...>; - pub fn read_data(targetcage: u64, item_id:u64, position:u64; data: *[u8], amount:u64) -> Result<...>; - // could implement or add others... - pub fn log_action(...); - - -}; - -// I'm assuming that we always know all of the metadata because we virtualize -// it. This means we know the size of every file, etc. - -/*** All the inode, data structure code goes here... ***/ - - - -// We hook in the handlers for read, write, etc. -pub fn setup_interposition(calls_to_integrate:data_storage_interface) { - // The encasement library will help w/ syscall replacements for my children - let mut syscall_replacements: Vec = Vec::new(); - - // I handle all open calls, etc... - syscall_replacements.push(repltable{syscallid:PIPE_SYSID, func:open_syscall}); - - // Let's handle read and write... - syscall_replacements.push(repltable{syscallid:READ_SYSID, func:read_syscall}); - syscall_replacements.push(repltable{syscallid:WRITE_SYSID, func:write_syscall}); - - syscall_replacements.push(repltable{syscallid:MMAP_SYSID, func:mmap_syscall}); - // And so on until we have all the file system calls... - - //... until finally - lind_encasement.replace_syscalls(syscall_replacements); -} - -// I'm assuming that how the above calls are used is quite self-evident. -// In essence, we just have the existing interface for most things, but the -// above extracts out the file storage. - -// NOTE: mmap in safeposix_rust just does some error checking and then calls -// down. We can do the same here! -pub fn mmap_syscall() { - ... -} - - -pub fn initialize_fs_metadata() { - // I'll either make this fresh or read it in here... - ... -} - - -// The code here keeps a fdtable, inode information, directory information, -// etc, much the same way as the existing code. When we open something (which -// is not open), we call open_item - - - - -/**************** Unique code for this implementation ***************/ - -pub fn init_item_table() { - // Create a global table of items here. In this implementation, this - // will map item_ids to fds of mine. -} - - -pub fn my_create_item(item_id:u64) { - // create an entry in the item table. The value stored is the fd. - - if item_id in itemtable { - panic!("Item already open"); - } - // Ensure we are creating this item or panic! - itemtable[item_id] = open("linddata.{item_id}",O_CREAT|O_EXCL|O_RDWR,0o600).unwrap(); -} - - -pub fn my_open_item(item_id:u64) { - // create an entry in the item table. The value stored is the fd. - - if item_id in itemtable { - panic!("Item already open"); - } - // Ensure we're opening an existing item or panic! - itemtable[item_id] = open("linddata.{item_id}",O_RDWR,0o600).unwrap(); - -} - -pub fn my_close_item(item_id:u64) { - // create an entry in the item table. The value stored is the fd. - - if !item_id in itemtable { - panic!("Item is not open"); - } - close(itemtable[item_id]) - -} - -pub fn my_store_data(targetcage: u64, item_id:u64, position:u64; data: *[u8], length:u64) -> Result<...> { - // This is my custom way to handle storing data... - - // I should never be past the end of the file since the virt_fs knows - // the position and has checked it... - - let fd = itemtable[item_id]; - - // I need to lock this... - { - - let last_pos = virt_fsmetadata.get_item_id_entry(item_id).position; - - if position != last_pos { - lseek(fd, position); - } - // BUG: This fd is this cage's fd, while the data pointer is from the - // target cage. This means that 3i needs to be able to support a mix - // of cage associations for different arguments... BUG BUG BUG - let amt_written = MAKE_SYSCALL(targetcage, WRITE_SYSID, fd, data, length).unwrap(); - // do error handling, likely panic, since no error should occur... - } - return amt_written; // To be used to update the position... - -} - - -pub fn my_read_data(targetcage: u64, item_id:u64, position:u64; data: *[u8], amount:u64) -> Result<...> { - // This is my custom way to handle reading in data... - - // Once again, I should never be past the end of the file, etc. since the - // virt_fs knows the position and has checked it... - - let fd = itemtable[item_id]; - - // I need to lock this... - { - - let last_pos = virt_fsmetadata.get_item_id_entry(item_id).position; - - if position != last_pos { - lseek(fd, position); - } - // BUG: This fd is the grate's fd, while the data pointer is from the - // target cage. This means that 3i needs to be able to support a mix - // of cage associations for different arguments... BUG BUG BUG - let amt_read = MAKE_SYSCALL(targetcage, READ_SYSID, fd, data, amount).unwrap(); - // do error handling, likely panic, since no error should occur... - } - return amt_read; // Update virt_metadata position... - -} - - - -fn main() { - // Setup my item table... - init_item_table(); - - // Have the virtual_fs initialize itself... - virt_metadata.initialize_fs_metadata(); - - // Register my calls - let mycalls = virt_metadata::data_storage_interface { - create_item: my_create_item, - open_item: my_open_item, - close_item: my_close_item, - store_data: my_store_data, - read_data: my_read_data, - // could implement or add others... - log_action:virt_metadata::DEFAULT, - // persist metadata out into items. - store_directories:true, - store_inodes:true, - }; - - - // need to replace calls with the right ones for our children - virt_metadata.setup_interposition(mycalls); - - // Need to instantiate child cage(s) here... - lind_encasement.initialize_children_and_consume_thread(); - // Never reaches here! -} diff --git a/src/example_grates/log_grate.rs b/src/example_grates/log_grate.rs deleted file mode 100644 index d504f035..00000000 --- a/src/example_grates/log_grate.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Example grates -// -// pass through -// ex: filter network calls by dest or file accesses by path, measure API -// usage / do strace equivalent. -// -// note: -// With the ability to set the target cage ID, I don't need to do much to -// support multi-tenancy. I can just use the targetcageid to do this. -// -// - - -// This does the heavy lifting (such as there is any...) -use lind_encasement; - -// This file contains a basic, example system call handler that just prints -// out the arguments - -pub fn log_syscall(cageid: u64, targetcageid: u64, callid: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64, arg6: u64) -> u64 { - // do whatever we want to log here... - println!("{cageid}, {targetcageid}, {callid}, {arg1}, {arg2}, {arg3}, {arg4}, {arg5}, {arg6}"); - - // Note, we don’t need to ensure the cageid can do an op - // on the targetcage because 3i checks that for us. Nice! - - // ...and make the original system call! - return MAKE_SYSCALL(targetcageid, callid, arg1, cleaned_up_ptr_arg, count_as_u64, arg4, arg5, arg6); -} - - -fn main() { - - // Replace all calls with the one given here... - lind_encasement.replace_all_syscalls(log_syscall); - - // instantiate child cage(s) as is needed here... - lind_encasement.initialize_children_and_consume_thread(); - // Never reaches here! -} diff --git a/src/example_grates/route_grate.rs b/src/example_grates/route_grate.rs deleted file mode 100644 index 150ddfac..00000000 --- a/src/example_grates/route_grate.rs +++ /dev/null @@ -1,147 +0,0 @@ -// Example grates -// -// -// route requests to different grates -// ex: /tmp to imfs + others to lower level fs, etc. -// -// We need a better API to support this example. -// -// - Encasement libary supports a way to save and restore sets of system -// calls. Grate A is instantiated with the pure API. A dummy child -// is forked from it and that API is saved. Grate B is instantiated -// with the pure API. A dummy child if forked from grate B and its -// API is also saved. The filter grate is forked with the pure API -// and the APIs from the two dummy children are provided. Note that -// the filter grate must return its cageID / PID from fork for each -// grate if it wants to make calls of its own to those grates. -// -// - - -// ******************************************************************* -// *** This file contains the sketch of a filtering handler which *** -// *** divides up the calls that are made between different grates *** -// *** THIS CODE WILL NOT RUN! IT IS MEANT TO ILLUSTRATE *** -// *** MY THINKING ABOUT HOW SOMETHING LIKE THIS WOULD *** -// *** WORK. WE WILL MAKE A WORKING GRATE ONCE WE *** -// *** UNDERSTAND THE INTERFACES AND CHALLENGES BETTER. *** -// ******************************************************************* - - -// I'm realizing this likely needs its own library. The reason is that -// there is one tedious part: separating out the grate to route a request -// to by system call. So, really, you just want to decide once for every -// creation of a fd, where to assign it, and thereafter all the calls for -// it should end up in the right place... Manually annotating write, send, -// recv, fcntl, etc. is a pain in the butt. Especially calls like mmap, -// select, etc. which put the fd in a weird spot or do other weird things... -use multigratelib; -use lind_3i; -use lind_3i::constants::*; -use lind_encasement; - - -/*********************** some multigratelib code *************************/ - - -pub gratemap: HashMap; - - -// This adds entries to the gratemap so that they can be called later -// by the other system calls. -pub fn initialize_grates(grates: Vec,interface:...) { - - - for gratename in grates { - - // create the dummy child, snatch its API, and stick it in the - // gratemap. This lets us use it later. - gratemap[gratename] = lind_encasement.dummy_initialize_grate_and_return_syscalls(gratename,interface); - } - -} - -// For each individual call, I check to see where the fd is from and then I -// route to this grate. These can be private functions... -fn fcntl_handler(cageid: u64, targetcageid: u64, callid: u64, fd: u64, cmd: u64, arg3: u64, arg4: u64, arg5: u64, arg6: u64) -> u64 { - //int fcntl(int fd, int cmd, ...); - - // panic if doesn't exist... - let gratename = fdtable.get_entry(targetcageid,fd).extradata; - - return makesyscall(gratename,targetcageid,callid,fd,cmd,arg3,arg4,arg5,arg6); -} - -// Above is just an example. There would be one for each call with a fd... -// Or, at least there is one for every call with a fd in a specific position. -// Some calls like dup, etc. also need special handling. - - -//***************** Code the grate author needs to write ******************* - - -pub fn my_open_syscall(cageid: u64, targetcageid: u64, callid: u64, pathname: u64, flags: u64, mode: u64, _: u64, _: u64, _: u64) -> u64 { - // int open(const char *pathname, int flags, mode_t mode */ - // - - // the max filename length is supposed to be 4096 - let mut localfn_buffer = vec!([0u8,4096]); - - lind_3i.icstrncpy(MY_CAGE_ID, localfn_buffer, targetcageid, pathname,4096); - - // I do my filtering here... - if abs_path_santize(pathname).startswith("/tmp/") { - let myfd = multigratelib::makesyscall("grate_a.rs",targetcageid,OPEN_SYSIDpathname,flags,mode); - // All future calls with this fd, go to this grate... - multigratelib::filterfd("grate_a.rs",targetcageid,myfd); - return myfd; - } - else { - let myfd = multigratelib::makesyscall("grate_b.rs",targetcageid,OPEN_SYSID,pathname,flags,mode); - // All future calls with this fd, go to this grate... - multigratelib::filterfd("grate_b.rs",targetcageid,myfd); - return myfd; - } - -} - - - - -// This sets up interposition so that calls go the right place... -fn setup_interposition() { - // This sets up the basic handlers and says unknown fds should return - // EBADFD. This is useful when all fds should be known to us. - multigratelib::add_handler(syscallid::OPEN_SYSID, my_open_syscall); - - // You can also always route a call to a specific grate without needing - // to write a function - multigratelib::always_handle_with_grate(syscallid::PIPE_SYSID, "grate_a.rs"); - - // You can also just block a certain way to crate FDs... - multigratelib::block_call(syscallid::SOCKET_SYSID); - - lind_encasement.replace_syscalls(syscall_replacements); - -} - - -fn main() { - - // Initialize these two grates and don't change the interface for them - // by setting None (just use my interface) - multigratelib::initialize_grates(["grate_A.rs","grate_B.rs",None); - // The above is equivalent to calling the function two times (each with - // one of the grates). If we want to have different interfaces for them, - // we can do it that way. - - // If I want to have things handled with my API, I could also pass - // the empty string "" as an option. - // For example: multigratelib::initialize_grates(["",None); - - - - // Now, onto the real children! - lind_encasement::initialize_children_and_consume_thread(); - // Never reaches here! -} diff --git a/src/fdtables/commonconstants.rs b/src/fdtables/commonconstants.rs new file mode 100644 index 00000000..dbbff897 --- /dev/null +++ b/src/fdtables/commonconstants.rs @@ -0,0 +1,123 @@ +// This file exists to make it easier to vary a single file of constants +// instead of editing each implementation... + +/// Per-process maximum number of fds... +pub const FD_PER_PROCESS_MAX: u64 = 1024; + +// /// Use this to indicate there isn't a real fd backing an item +//pub const NO_REAL_FD: u64 = 0xff_abcd_ef01; + +// /// Use to indicate this is an EPOLLFD +// pub const EPOLLFD: u64 = 0xff_abcd_ef02; + +/// All FDKIND values defined by the user must be below this value. +pub const FDT_KINDMAX: u32 = 0xff00_0000; + +/// Use this to indicate that a FD is invalid... Usually an error will be +/// returned instead, but this is needed for rare cases like poll. +pub const FDT_INVALID_FD: u32 = 0xff00_0001; + +/// Use to indicate this is an EPOLLFD (an internal kind of fd) +pub const FDT_KINDEPOLL: u32 = 0xff00_0002; + +// These are the values we look up with at the end... +// #[doc = include_str!("../docs/fdtableentry.md")] +#[derive(Clone, Copy, Hash, Debug, PartialEq, Eq)] +/// This is a table entry, looked up by virtual fd. +pub struct FDTableEntry { + /// This is the kind of fd which it is. These are user defined values + /// so they can track what this means. Appropriate for storing information + /// which is global to all virtual fds that track this. + /// values over FDKINDMAX are reserved. + pub fdkind: u32, + /// underlying fd (could be the real, kernel fd below us or could be + /// some indicator of what table entry a virtual fd has. It is up to + /// the implementer to decide how to use this. + pub underfd: u64, + /// Should I close this on exec? Needed so fdtabls can implement + /// [/`empty_fds_for_exec`] + pub should_cloexec: bool, + /// Used to store fd specific extra information, such as flags or similar + /// which may differ for different 'dup'ed copies of a fd. Whatever + /// the user desires may be placed here. + pub perfdinfo: u64, +} + +#[allow(non_snake_case)] +/// A function used when registering close handlers which does nothing... +/// It is the default if no close handlers are defined +pub const fn NULL_FUNC(_: FDTableEntry, _: u64) {} + +// BUG / TODO: Use this in some sane way... +#[allow(dead_code)] +/// Global maximum number of fds... (checks may not be implemented) +pub const TOTAL_FD_MAX: u64 = 4096; + +// replicating these constants here so this can compile on systems other than +// Linux... Copied from Rust's libc. +/// copied from libc +pub const EPOLL_CTL_ADD: i32 = 1; +/// copied from libc +pub const EPOLL_CTL_MOD: i32 = 2; +/// copied from libc +pub const EPOLL_CTL_DEL: i32 = 3; + +#[allow(non_camel_case_types)] +/// i32 copied from libc. used in EPOLL event flags even though events are u32 +pub type c_int = i32; + +/// copied from libc +pub const EPOLLIN: c_int = 0x1; +/// copied from libc +pub const EPOLLPRI: c_int = 0x2; +/// copied from libc +pub const EPOLLOUT: c_int = 0x4; +/// copied from libc +pub const EPOLLERR: c_int = 0x8; +/// copied from libc +pub const EPOLLHUP: c_int = 0x10; +/// copied from libc +pub const EPOLLRDNORM: c_int = 0x40; +/// copied from libc +pub const EPOLLRDBAND: c_int = 0x80; +/// copied from libc +pub const EPOLLWRNORM: c_int = 0x100; +/// copied from libc +pub const EPOLLWRBAND: c_int = 0x200; +/// copied from libc +pub const EPOLLMSG: c_int = 0x400; +/// copied from libc +pub const EPOLLRDHUP: c_int = 0x2000; +/// copied from libc +pub const EPOLLEXCLUSIVE: c_int = 0x1000_0000; +/// copied from libc +pub const EPOLLWAKEUP: c_int = 0x2000_0000; +/// copied from libc +pub const EPOLLONESHOT: c_int = 0x4000_0000; +// Turning this on here because we copied from Rust's libc and I assume they +// intended this... +// `0x8000_0000` is a hexadecimal literal representing a 32-bit integer value +// in rust and works for unsigned int. But it will cause warning in compilation +// stage for signed int (ie `c_int`). +// Since the value is copied from Rust's libc and is meant to match certain +// system constants (such as EPOLLET), this case is by design. +// Use #[allow(overflowing_literals)] to ensure our code compiles without warnings. +#[allow(overflowing_literals)] +/// copied from libc +pub const EPOLLET: c_int = 0x8000_0000; + +// use libc::epoll_event; +// Note, I'm not using libc's version because this isn't defined on Windows +// or Mac. Hence, I can't compile, etc. on those systems. Of course any +// system actually running epoll, will need to be on Mac, but that doesn't mean +// we can't parse those calls. +#[allow(non_camel_case_types)] +#[derive(Clone, Debug)] +/// matches libc in Rust. Copied exactly. +pub struct epoll_event { + /// copied from libc. Event types to look at. + pub events: u32, // So weird that this is a u32, while the constants + // defined to work with it are i32s... + /// copied from libc. Not used. + pub u64: u64, +} diff --git a/src/fdtables/current_impl b/src/fdtables/current_impl new file mode 100644 index 00000000..51f2083b --- /dev/null +++ b/src/fdtables/current_impl @@ -0,0 +1,2 @@ +mod dashmaparrayglobal; +pub use crate::dashmaparrayglobal::*; diff --git a/src/fdtables/dashmaparrayglobal.rs b/src/fdtables/dashmaparrayglobal.rs new file mode 100644 index 00000000..b3a936e9 --- /dev/null +++ b/src/fdtables/dashmaparrayglobal.rs @@ -0,0 +1,981 @@ +// DashMap;FD_PER_PROCESSS_MAX]> Space is ~24KB +// per cage w/ 1024 fds?!? +// Static DashMap. Let's see if having the FDTableEntries be a static +// array is any faster... + +use crate::fdtables::threei; +use crate::safeposix::cage; + +use dashmap::DashMap; + +use lazy_static::lazy_static; + +use std::collections::HashMap; + +use std::sync::Mutex; + +// This uses a Dashmap (for cages) with an array of FDTableEntry items. + +// Get constants about the fd table sizes, etc. +pub use super::commonconstants::*; + +// algorithm name. Need not be listed. Used in benchmarking output +// #[doc(hidden)] +pub const ALGONAME: &str = "DashMapArrayGlobal"; + +// It's fairly easy to check the fd count on a per-process basis (I just check +// when I would add a new fd). +// +// TODO: I will ignore the total limit for now. I would ideally do this on +// every creation, close, fork, etc. but it's a PITA to track this. + +// We will raise a panic anywhere we receive an unknown cageid. This frankly +// should not be possible and indicates some sort of internal error in our +// code. However, other issues, such as an invalid file descriptor when a +// cage makes a call, will be handled by returning the appropriate errno. + +// In order to store this information, I'm going to use a DashMap which +// has keys of (cageid:u64) and values that are an array of FD_PER_PROCESS_MAX +// Option items. +// +// + +// This lets me initialize the code as a global. +lazy_static! { + + #[derive(Debug)] + pub static ref FDTABLE: DashMap;FD_PER_PROCESS_MAX as usize]> = { + let m = DashMap::new(); + // Insert a cage so that I have something to fork / test later, if need + // be. Otherwise, I'm not sure how I get this started. I think this + // should be invalid from a 3i standpoint, etc. Could this mask an + // error in the future? + // m.insert(threei::TESTING_CAGEID,[Option::None;FD_PER_PROCESS_MAX as usize]); + m + }; +} + +lazy_static! { + // This is needed for close and similar functionality. I need track the + // number of times a realfd is open. Note that this is across cages in + // order to enable a library to have situations where two cages have the + // same fd open. The (fdkind,underfd) tuple is the key and the number of + // times it appears is the value. If it reaches 0, the entry is removed. + #[derive(Debug)] + static ref FDCOUNT: DashMap<(u32,u64), u64> = { + DashMap::new() + }; + +} + +// #[doc = include_str!("../docs/init_empty_cage.md")] +pub fn init_empty_cage(cageid: u64) { + + assert!(!FDTABLE.contains_key(&cageid),"Known cageid in fdtable access"); + + FDTABLE.insert(cageid,[Option::None;FD_PER_PROCESS_MAX as usize]); +} + +// #[doc = include_str!("../docs/translate_virtual_fd.md")] +pub fn translate_virtual_fd(cageid: u64, virtualfd: u64) -> Result { + + // They should not be able to pass a new cage I don't know. I should + // always have a table for each cage because each new cage is added at fork + // time + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + return match FDTABLE.get(&cageid).unwrap()[virtualfd as usize] { + Some(tableentry) => Ok(tableentry), + None => Err(threei::Errno::EBADFD as u64), + }; +} + + +// This is fairly slow if I just iterate sequentially through numbers. +// However there are not that many to choose from. I could pop from a list +// or a set as well... Likely the best solution is to keep a count of the +// largest fd handed out and to just use this until you wrap. This will be +// super fast for a normal cage and will be correct in the weird case. +// Right now, I'll just implement the slow path and will speed this up +// later, if needed. +// #[doc = include_str!("../docs/get_unused_virtual_fd.md")] +pub fn get_unused_virtual_fd( + cageid: u64, + fdkind: u32, + underfd: u64, + should_cloexec: bool, + perfdinfo: u64, +) -> Result { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + // Set up the entry so it has the right info... + // Note, a HashMap stores its data on the heap! No need to box it... + // https://doc.rust-lang.org/book/ch08-03-hash-maps.html#creating-a-new-hash-map + let myentry = FDTableEntry { + fdkind, + underfd, + should_cloexec, + perfdinfo, + }; + + let mut myfdrow = FDTABLE.get_mut(&cageid).unwrap(); + + // Check the fds in order. + for fdcandidate in 0..FD_PER_PROCESS_MAX { + // FIXME: This is likely very slow. Should do something smarter... + if myfdrow[fdcandidate as usize].is_none() { + // I just checked. Should not be there... + myfdrow[fdcandidate as usize] = Some(myentry); + _increment_fdcount(myentry); + return Ok(fdcandidate); + } + } + + // I must have checked all fds and failed to find one open. Fail! + Err(threei::Errno::EMFILE as u64) +} + +// This is used for things like dup2, which need a specific fd... +// If the requested_virtualfd is used, I close it... +// #[doc = include_str!("../docs/get_specific_virtual_fd.md")] +pub fn get_specific_virtual_fd( + cageid: u64, + requested_virtualfd: u64, + fdkind: u32, + underfd: u64, + should_cloexec: bool, + perfdinfo: u64, +) -> Result<(), threei::RetVal> { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + // If you ask for a FD number that is too large, I'm going to reject it. + // Note that, I need to use the FD_PER_PROCESS_MAX setting because this + // is also how I'm tracking how many values you have open. If this + // changed, then these constants could be decoupled... + if requested_virtualfd > FD_PER_PROCESS_MAX { + return Err(threei::Errno::EBADF as u64); + } + + // Set up the entry so it has the right info... + // Note, a HashMap stores its data on the heap! No need to box it... + // https://doc.rust-lang.org/book/ch08-03-hash-maps.html#creating-a-new-hash-map + let myentry = FDTableEntry { + fdkind, + underfd, + should_cloexec, + perfdinfo, + }; + + // This is before the FDTABLE action, so if I decrement the same fd, it + // calls the intermediate handler instead of the last one. + _increment_fdcount(myentry); + let myoptionentry = FDTABLE.get(&cageid).unwrap()[requested_virtualfd as usize]; + // always add the new entry. I'm doing this first, before I close + // the old one because I need to ensure I've cleaned up state correctly + // before calling the close handlers... + FDTABLE.get_mut(&cageid).unwrap()[requested_virtualfd as usize] = Some(myentry); + + // Update the fdcount / close the old entry, if existed + if let Some(entry) = myoptionentry { + _decrement_fdcount(entry); + } + + Ok(()) +} + +// We're just setting a flag here, so this should be pretty straightforward. +// #[doc = include_str!("../docs/set_cloexec.md")] +pub fn set_cloexec(cageid: u64, virtualfd: u64, is_cloexec: bool) -> Result<(), threei::RetVal> { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + // return EBADFD, if the fd is missing... + if FDTABLE.get(&cageid).unwrap()[virtualfd as usize].is_none() { + return Err(threei::Errno::EBADFD as u64); + } + // Set the is_cloexec flag + FDTABLE.get_mut(&cageid).unwrap()[virtualfd as usize].as_mut().unwrap().should_cloexec = is_cloexec; + Ok(()) +} + +// We're setting an opaque value here. This should be pretty straightforward. +// #[doc = include_str!("../docs/set_perfdinfo.md")] +pub fn set_perfdinfo( + cageid: u64, + virtualfd: u64, + perfdinfo: u64, +) -> Result<(), threei::RetVal> { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + // return EBADFD, if the fd is missing... + if FDTABLE.get(&cageid).unwrap()[virtualfd as usize].is_none() { + return Err(threei::Errno::EBADFD as u64); + } + + // Set optionalinfo or return EBADFD, if that's missing... + FDTABLE.get_mut(&cageid).unwrap()[virtualfd as usize].as_mut().unwrap().perfdinfo = perfdinfo; + Ok(()) +} + +// Helper function used for fork... Copies an fdtable for another process +// #[doc = include_str!("../docs/copy_fdtable_for_cage.md")] +pub fn copy_fdtable_for_cage(srccageid: u64, newcageid: u64) -> Result<(), threei::Errno> { + + assert!(FDTABLE.contains_key(&srccageid),"Unknown cageid in fdtable access"); + assert!(!FDTABLE.contains_key(&newcageid),"Known cageid in fdtable access"); + + // Insert a copy and ensure it didn't exist... + // I've checked this should be a copy, not a ref to the same thing. + let hmcopy = *FDTABLE.get(&srccageid).unwrap(); + + // Increment copied items + for entry in FDTABLE.get(&srccageid).unwrap().iter() { + if entry.is_some() { + _increment_fdcount(entry.unwrap()); + } + } + + assert!(FDTABLE.insert(newcageid, hmcopy).is_none()); + + // I'm not going to bother to check the number of fds used overall yet... + // Err(threei::Errno::EMFILE as u64), + Ok(()) +} + +// This is mostly used in handling exit, etc. Returns the HashMap +// for the cage. +// #[doc = include_str!("../docs/remove_cage_from_fdtable.md")] +pub fn remove_cage_from_fdtable(cageid: u64) { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + + // remove the item first and then we clean up and call their close + // handlers. + let myfdrow = FDTABLE.remove(&cageid).unwrap().1; + + // Take only the Some items in here (clippy suggested) + for entry in myfdrow.into_iter().flatten() { + _decrement_fdcount(*entry); + } + +} + +// This removes all fds with the should_cloexec flag set. They are returned +// in a new hashmap... +// #[doc = include_str!("../docs/empty_fds_for_exec.md")] +pub fn empty_fds_for_exec(cageid: u64) { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + let mut myfdrow = FDTABLE.get_mut(&cageid).unwrap(); + // I need to call all the close handlers at the end. So I need to + // get vector of them to do the operation on... + let mut closevec = Vec::new(); + + for item in 0..FD_PER_PROCESS_MAX as usize { + if myfdrow[item].is_some() && myfdrow[item].unwrap().should_cloexec { + // handle this in a moment... + closevec.push(myfdrow[item].unwrap()); + + // Always zero out the row before calling their handler + myfdrow[item] = None; + } + } + + // Need to drop the lock, before calling the handlers. + drop(myfdrow); + + // Now, we can call the close handlers! + for entry in closevec { + _decrement_fdcount(entry); + } + +} + +// Returns the HashMap returns a copy of the fdtable for a cage. Useful +// helper function for a caller that needs to examine the table. Likely could +// be more efficient by letting the caller borrow this... +// #[doc = include_str!("../docs/return_fdtable_copy.md")] +#[must_use] // must use the return value if you call it. +pub fn return_fdtable_copy(cageid: u64) -> HashMap { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + let mut myhashmap = HashMap::new(); + + let myfdrow = FDTABLE.get(&cageid).unwrap(); + for item in 0..FD_PER_PROCESS_MAX as usize { + if myfdrow[item].is_some() { + myhashmap.insert(item as u64,myfdrow[item].unwrap()); + } + } + myhashmap +} + + + +/******************* CLOSE SPECIFIC FUNCTIONALITY *******************/ + +// These indicate what functions should be called upon a virtualfd closing. +// The handler which is called depends on number of (fdkind,underfd) tuples +// that are used across *all instances managed by this library including in +// other cages*. +struct CloseHandlers { + // Called when close is called, but at least one (fdkind,underfd) + // reference still remains. Called with (fdkind,underfd,count) + intermediate: fn(FDTableEntry,u64), + // Called when close is called, but at least one (fdkind,underfd) + // reference still remains. Called with (fdkind,underfd,0) + last: fn(FDTableEntry,u64), +} + + +lazy_static! { + // This holds the user registered handlers they want to have called when + // a close occurs. I did this rather than return messy data structures + // from the close, exec, and exit handlers because it seemed cleaner... + #[derive(Debug)] + static ref CLOSEHANDLERTABLE: Mutex> = { + Mutex::new(HashMap::new()) + }; +} + + +// #[doc = include_str!("../docs/close_virtualfd.md")] +pub fn close_virtualfd(cageid:u64, virtfd:u64) -> Result<(),threei::RetVal> { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + // derefing this so I don't hold a lock and deadlock close handlers + let mut myfdrow = *FDTABLE.get_mut(&cageid).unwrap(); + + + if myfdrow[virtfd as usize].is_some() { + let entry = myfdrow[virtfd as usize]; + + // Zero out this entry before calling the close handler... + myfdrow[virtfd as usize] = None; + + FDTABLE.insert(cageid, myfdrow.clone()); + + // always _decrement last as it may call the user handler... + _decrement_fdcount(entry.unwrap()); + return Ok(()); + } + Err(threei::Errno::EBADFD as u64) +} + + +// Register a series of helpers to be called for close. Can be called +// multiple times to override the older helpers. +// #[doc = include_str!("../docs/register_close_handlers.md")] +pub fn register_close_handlers(fdkind:u32, intermediate: fn(FDTableEntry,u64), last: fn(FDTableEntry,u64)) { + // Unlock the table and set the handlers... + let mut closehandlertable = CLOSEHANDLERTABLE.lock().unwrap(); + let closehandler = CloseHandlers { + intermediate, + last, + }; + // overwrite whatever is in there... + closehandlertable.insert(fdkind,closehandler); +} + + +// Helpers to track the count of times each (fdkind,underfd) is used +// #[doc(hidden)] +fn _decrement_fdcount(entry:FDTableEntry) { + + let mytuple = (entry.fdkind, entry.underfd); + + let newcount:u64 = FDCOUNT.get(&mytuple).unwrap().value() - 1; + + let intermediatech; + let lastch; + // Doing this to release the lock so I can call it recursively... + let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + if let Some(closehandlerentry) = closehandlers.get(&entry.fdkind) { + intermediatech = closehandlerentry.intermediate; + lastch = closehandlerentry.last; + } + else { + // TODO: If at any future point, I wanted to add a "default" handler + // for all fdkind values, I would add it here... + intermediatech = NULL_FUNC; + lastch = NULL_FUNC; + } + // release the lock... + drop(closehandlers); + + if newcount > 0 { + // Update before calling their close handler in case they do operations + // inside the close handler which create / close fds... + FDCOUNT.insert(mytuple,newcount); + (intermediatech)(entry,newcount); + } + else{ + // Remove before calling their close handler in case they do operations + // inside the close handler which create / close fds... + FDCOUNT.remove(&mytuple); + (lastch)(entry,0); + } +} + +// Helpers to track the count of times each realfd is used +// #[doc(hidden)] +fn _increment_fdcount(entry:FDTableEntry) { + + let mytuple = (entry.fdkind, entry.underfd); + + // Get a mutable reference to the entry so we can update it. + if let Some(mut count) = FDCOUNT.get_mut(&mytuple) { + *count += 1; + } else { + FDCOUNT.insert(mytuple, 1); + } +} + + + +/*************** Code for handling select() ****************/ + +use libc::fd_set; +use std::collections::HashSet; +use std::cmp; +use std::mem; + +// Helper to get an empty fd_set. Helper function to isolate unsafe code, +// etc. +// #[doc(hidden)] +#[must_use] // must use the return value if you call it. +pub fn _init_fd_set() -> fd_set { + let raw_fd_set:fd_set; + unsafe { + let mut this_fd_set = mem::MaybeUninit::::uninit(); + libc::FD_ZERO(this_fd_set.as_mut_ptr()); + raw_fd_set = this_fd_set.assume_init(); + } + raw_fd_set +} + +// #[doc(hidden)] +pub fn _fd_set(fd:u64, thisfdset:&mut fd_set) { + unsafe{libc::FD_SET(fd as i32,thisfdset)} +} + +// #[doc(hidden)] +#[must_use] // must use the return value if you call it. +pub fn _fd_isset(fd:u64, thisfdset:&fd_set) -> bool { + unsafe{libc::FD_ISSET(fd as i32,thisfdset)} +} + + + +// This is a helper that just does a single type (r/w/e) and returns: +// bithashmap: HashMap +// unhandledhashmap: HashMap> +// mappingtable: HashMap +// +// With this we trivially build the whole function... + +// helper to call before calling select beneath you. Translates your virtfds +// into a bitmask you may use for select. +// See: https://man7.org/linux/man-pages/man2/select.2.html for details / +// corner cases about the arguments. +// + +// I hate doing this, but don't know how to make this interface better... +#[allow(clippy::type_complexity)] +#[allow(clippy::implicit_hasher)] +// #[doc = include_str!("../docs/get_bitmask_for_select.md")] +pub fn get_bitmask_for_select(cageid:u64, nfds:u64, bits:Option, fdkinds:&HashSet) -> Result<(HashMap, HashMap>, HashMap<(u32,u64),u64>),threei::RetVal> { + + if nfds >= FD_PER_PROCESS_MAX { + return Err(threei::Errno::EINVAL as u64); + } + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + // The three things I will return... + let mut retbittable:HashMap = HashMap::new(); + let mut retunparsedtable:HashMap> = HashMap::new(); + let mut mappingtable:HashMap<(u32,u64),u64> = HashMap::new(); + + // If we were asked to do this on nothing, return empty mappings... + if bits.is_none() { + return Ok((retbittable, retunparsedtable, mappingtable)); + } + + let infdset = bits.unwrap(); + + // dashmaps are lockless, but usually I would grab a lock on the fdtable + // here... + let binding = FDTABLE.get(&cageid).unwrap(); + let myfdrow = *binding.value(); + + // Clippy is somehow missing how the virtualfd is being used throughout + // here. It's not just a range value + #[allow(clippy::needless_range_loop)] + // iterate through the set bits... + for bit in 0..nfds as usize { + let pos = bit as u64; + if _fd_isset(pos,&infdset) { + if let Some(entry) = myfdrow[bit] { + + // I like to do the shorter case first rather than having + // it later. + #[allow(clippy::if_not_else)] + // Which return set do I go in? + if !fdkinds.contains(&entry.fdkind) { + // Is unparsed... Clippy's suggestion to insert if missing + retunparsedtable.entry(entry.fdkind).or_default(); + retunparsedtable.get_mut(&entry.fdkind).unwrap().insert(entry); + } + else { + + let startingnfds; + let mut startingfdset; + + // Either initialize it or use what exists + if retbittable.contains_key(&entry.fdkind) { + (startingnfds, startingfdset) = *retbittable.get(&entry.fdkind).unwrap(); + } + else{ + startingnfds = 1; + // I don't init this above because a fd_set is a large + // data structure and would be costly. + startingfdset = _init_fd_set(); + } + + // Update the table and the nfds + _fd_set(entry.underfd,&mut startingfdset); + let newnfds = cmp::max(startingnfds, entry.underfd+1); + + // and update the mappingtable to have the bit from the + // original fd... + mappingtable.insert((entry.fdkind,entry.underfd),pos); + + // insert the item + retbittable.insert(entry.fdkind,(newnfds,startingfdset)); + } + } + else { + return Err(threei::Errno::EBADF as u64); + } + } + } + Ok((retbittable, retunparsedtable, mappingtable)) + +} + + +#[allow(clippy::type_complexity)] +#[allow(clippy::implicit_hasher)] +// #[doc = include_str!("../docs/prepare_bitmasks_for_select.md")] +pub fn prepare_bitmasks_for_select(cageid:u64, nfds:u64, rbits:Option, wbits:Option, ebits:Option, fdkinds:&HashSet) -> Result<([HashMap;3], [HashMap>;3], HashMap<(u32,u64),u64>),threei::RetVal> { + // This is a pretty simple function. Calls get_bitmask_for_select + // repeatedly and combines the results... + // [HashSet<(u64,u64)>;3] + + // return the error, if need be + let rresult = get_bitmask_for_select(cageid, nfds, rbits, fdkinds)?; + let wresult = get_bitmask_for_select(cageid, nfds, wbits, fdkinds)?; + let eresult = get_bitmask_for_select(cageid, nfds, ebits, fdkinds)?; + + let mut mappingtable = rresult.2; + mappingtable.extend(wresult.2); + mappingtable.extend(eresult.2); + + Ok(([rresult.0,wresult.0,eresult.0],[rresult.1,wresult.1,eresult.1],mappingtable)) + +} + + +// helper to call after calling select beneath you. returns the fd_set you +// need for your return from a select call and the number of unique flags +// set... + +// I hate doing these, but don't know how to make this interface better... +//#[allow(clippy::type_complexity)] +//#[allow(clippy::too_many_arguments)] +// I given them the hashmap, so don't need flexibility in what they return... +#[allow(clippy::implicit_hasher)] +#[must_use] // must use the return value if you call it. +// #[doc = include_str!("../docs/get_one_virtual_bitmask_from_select_result.md")] +pub fn get_one_virtual_bitmask_from_select_result(fdkind:u32, nfds:u64, bits:Option, unprocessedset:HashSet, startingbits:Option,mappingtable:&HashMap<(u32,u64),u64>) -> (u64, Option) { + + // Note, I don't need the cage_id here because I have the mappingtable... + + assert!(nfds < FD_PER_PROCESS_MAX,"This shouldn't be possible because we shouldn't have returned this previously"); + + let mut flagsset = 0; + + if bits.is_none() && unprocessedset.is_empty() { + return (flagsset,None); + } + + // I probably should pass a reference to startingbits to avoid copying the + // bit structure... + let mut retbits = match startingbits { + Some(val) => val, + None => _init_fd_set(), + }; + + if let Some(inset) = bits { + for bit in 0..nfds as usize { + let pos = bit as u64; + if _fd_isset(pos,&inset)&& !_fd_isset(*mappingtable.get(&(fdkind,pos)).unwrap(),&retbits) { + flagsset+=1; + _fd_set(*mappingtable.get(&(fdkind,pos)).unwrap(),&mut retbits); + } + } + } + for virtfd in unprocessedset { + if !_fd_isset(virtfd,&retbits) { + flagsset+=1; + _fd_set(virtfd,&mut retbits); + } + } + + (flagsset,Some(retbits)) + +} + + + +/********************** POLL SPECIFIC FUNCTIONS **********************/ + +// helper to call before calling poll beneath you. replaces the fds in +// the poll struct with virtual versions and returns the items you need +// to check yourself... +#[allow(clippy::implicit_hasher)] +#[allow(clippy::type_complexity)] +// #[doc = include_str!("../docs/convert_virtualfds_for_poll.md")] +#[must_use] // must use the return value if you call it. +pub fn convert_virtualfds_for_poll(cageid:u64, virtualfds:HashSet) -> (HashMap>, HashMap<(u32,u64),u64>) { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + let thefdrow = *FDTABLE.get(&cageid).unwrap(); + let mut mappingtable:HashMap<(u32,u64),u64> = HashMap::new(); + let mut rethashmap:HashMap> = HashMap::new(); + + + // BUG?: I'm ignoring the fact that virtualfds can show up multiple times. + // I'm not sure this actually matters, but I didn't think hard about it. + for virtfd in virtualfds { + if let Some(entry) = thefdrow[virtfd as usize] { + // Insert an empty HashSet, if needed + rethashmap.entry(entry.fdkind).or_default(); + mappingtable.entry((entry.fdkind,entry.underfd)).or_default(); + + rethashmap.get_mut(&entry.fdkind).unwrap().insert((virtfd,entry)); + mappingtable.insert((entry.fdkind,entry.underfd), virtfd); + } + else { + let myentry = FDTableEntry { + fdkind:FDT_INVALID_FD, + underfd:virtfd, + should_cloexec:false, + perfdinfo:u64::from(FDT_INVALID_FD), + }; + + // Insert an empty HashSet, if needed + rethashmap.entry(FDT_INVALID_FD).or_default(); + mappingtable.entry((FDT_INVALID_FD,virtfd)).or_default(); + + rethashmap.get_mut(&FDT_INVALID_FD).unwrap().insert((virtfd,myentry)); + // Add this because they need to handle it if POLLNVAL is set. + // An exception should not be raised!!! + + // I will add this to the mapping table, because I do think they + // may want to raise an exception, etc. based upon this and signal + // back. I am setting the underfd to be the virtfd, so I can + // reverse this process, if multiple entries like this occur. + mappingtable.insert((FDT_INVALID_FD,virtfd), virtfd); + } + } + + (rethashmap, mappingtable) +} + + + +// helper to call after calling poll. replaces the realfds the vector +// with virtual ones... +// #[doc = include_str!("../docs/convert_poll_result_back_to_virtual.md")] +// I give them the hashmap, so don't need flexibility in what they return... +#[allow(clippy::implicit_hasher)] +#[must_use] // must use the return value if you call it. +pub fn convert_poll_result_back_to_virtual(fdkind:u32,underfd:u64, mappingtable:&HashMap<(u32,u64),u64>) -> Option { + + // I don't care what cage was used, and don't need to lock anything... + // I have the mappingtable! + + // Should this even be a function? + mappingtable.get(&(fdkind,underfd)).copied() +} + + + +/********************** EPOLL SPECIFIC FUNCTIONS **********************/ + + +// Supporting epollfds is done by a fdkind which is not set by the user. +// There are a few complexities here: +// 1) an epollfd gets a virtual file descriptor +// 2) a epollfd can point to any number of other fds of different kinds +// 3) an epollfd can point to epollfds, which can point to other epollfds, etc. +// and possibly cause a loop to occur (which is an error) +// +// My thinking is this is handled as similarly to poll as possible. We push +// off the problem of understanding what the event types are to the implementer +// of the library. +// +// In my view, epoll_wait() is quite simple to support. One basically just +// keeps a list of virtual fds for this epollfd and their corresponding event +// types, which they may need to poll themselves. After this, they handle the +// call. +// +// epoll_ctl is complex, but really has the same fundamental problem as +// epoll_create: the epollfd. +// +// I'll create a new fdkind for epoll. When epoll_create is called, the +// caller can decide which fdkinds need to be passed down to the underlying +// epoll_create call(s). Similarly, when epoll_ctl is called, one either +// handles the call internally or uses the underfd for the fdkind... +// +// Interestingly, this actually would be just as easy to build on top of the +// fdtables library as into it. +// +// Each epollfd will have some virtual fds associated with it. Each of those +// will have an event mask. So I'll have a mutex around an EPollTable struct. +// This contains the next available entry and an epollhashmap. +// I use a hashmap here to better support removing and modifying items. + +// Note, I'm defining a bunch of symbols myself because libc doesn't import +// them on systems that don't support epoll and I want to be able to build +// the code anywhere. See commonconstants.rs for more info. + + +// Okay, so the basic structure is like this: +// 1) epoll_create_helper sets up an epollfd. You will need to have a unique +// underfd for each fdkind below you, if you want to call down. +// 2) my API should track all of the fdkinds where there isn't an underlying +// epollfd +// 3) epoll_ctl / epoll_wait will work on whichever is appropriate +// +// A hashmap of: +// HashMap for tracking how to call down. +// HashMap> seems to make the most sense for the other +// descriptors. + + +// a structure that exists for each epoll descriptor to track the underfd(s) +// and parts the user will handle +#[derive(Clone, Debug, Default)] +struct EPollDescriptorInfo { + // I didn't combine thewe two hashmaps into one because they are used + // separately and the resulting value type would be too messy... + + underfdhashmap: HashMap, // The underfd for a specific fdkind. + // Used only when an epoll call will + // call down beneath it. + userhandledhashmap: HashMap>, + // This has all of the things the user + // will virtualize and handle. The key + // is the fdkind. +} + +// TODO: I don't clean up this table yet. I probably should when the last +// reference to a fd is closed, but this bookkeeping seems excessive at this +// time... +#[derive(Clone, Debug)] +struct EPollTable { + highestneverusedentry: u64, // Never resets (even after close). Used to + // let us quickly get an unused entry + thisepolltable: HashMap, +} + +lazy_static! { + + #[derive(Debug)] + static ref EPOLLTABLE: Mutex = { + let newetable = HashMap::new(); + let m = EPollTable { + highestneverusedentry:0, + thisepolltable:newetable, + }; + Mutex::new(m) + }; +} + +fn _get_epoll_entrynum_or_error(cageid:u64, epfd:u64) -> Result { + // Is the epfd ok? + match FDTABLE.get(&cageid).unwrap()[epfd as usize] { + None => { + Err(threei::Errno::EBADF as u64) + }, + Some(tableentry) => { + // You must call this on an epoll fd + if tableentry.fdkind == FDT_KINDEPOLL { + Ok(tableentry.underfd) + } + else { + Err(threei::Errno::EINVAL as u64) + } + }, + } +} + + +// #[doc = include_str!("../docs/epoll_create_empty.md")] +pub fn epoll_create_empty(cageid:u64, should_cloexec:bool) -> Result { + + let mut ept = EPOLLTABLE.lock().unwrap(); + + // return the same errno (EMFile), if we get one + let newepollfd = get_unused_virtual_fd(cageid, FDT_KINDEPOLL, ept.highestneverusedentry, should_cloexec, 0)?; + + let newentrynum = ept.highestneverusedentry; + ept.highestneverusedentry+=1; + + // Create a new entry with empty values + ept.thisepolltable.entry(newentrynum).or_default(); + Ok(newepollfd) + +} + +// #[doc = include_str!("../docs/epoll_add_underfd.md")] +pub fn epoll_add_underfd(cageid:u64, virtepollfd:u64, fdkind:u32, underfd:u64) -> Result<(),threei::RetVal> { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + let mut ept = EPOLLTABLE.lock().unwrap(); + + // get this or error out... + let epentrynum = _get_epoll_entrynum_or_error(cageid, virtepollfd)?; + + let myhm = &mut ept.thisepolltable.get_mut(&epentrynum).unwrap().underfdhashmap; + + assert!(!myhm.contains_key(&fdkind),"Adding duplicate underfd to epollfd"); + + myhm.insert(fdkind,underfd); + + Ok(()) + +} + + +// #[doc = include_str!("../docs/epoll_get_underfd_hashmap.md")] +pub fn epoll_get_underfd_hashmap(cageid:u64, virtepollfd:u64) -> Result,threei::RetVal> { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + let ept = EPOLLTABLE.lock().unwrap(); + + // get this or error out... + let epentrynum = _get_epoll_entrynum_or_error(cageid, virtepollfd)?; + + Ok(ept.thisepolltable.get(&epentrynum).unwrap().underfdhashmap.clone()) + +} + + + +// #[doc = include_str!("../docs/virtualize_epoll_ctl.md")] +pub fn virtualize_epoll_ctl(cageid:u64, epfd:u64, op:i32, virtfd:u64, event:epoll_event) -> Result<(),threei::RetVal> { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + if epfd == virtfd { + return Err(threei::Errno::EINVAL as u64); + } + + // get this or error out... + let epentrynum = _get_epoll_entrynum_or_error(cageid, epfd)?; + + // Okay, I know which table entry, now verify the virtfd... + + + let virtfdkind:u32; + + // check if the virtfd is real and error... + // I don't care about its contents except to ensure it isn't real... + if let Some(tableentry) = FDTABLE.get(&cageid).unwrap()[virtfd as usize] { + // Right now, I don't support this, so error... + if tableentry.fdkind == FDT_KINDEPOLL { + // TODO: support EPOLLFDs... + return Err(threei::Errno::ENOSYS as u64); + } + virtfdkind = tableentry.fdkind; + } + else { + // The virtual Fd doesn't exist -- error... + return Err(threei::Errno::EBADF as u64); + } + + let mut eptable = EPOLLTABLE.lock().unwrap(); + let userhm = eptable.thisepolltable.get_mut(&epentrynum).unwrap().userhandledhashmap.entry(virtfdkind).or_default(); + + match op { + EPOLL_CTL_ADD => { + if userhm.contains_key(&virtfd) { + return Err(threei::Errno::EEXIST as u64); + } + // BUG: Need to check for ELOOP here once I support EPOLLFDs + // referencing each other... + + userhm.insert(virtfd, event); + }, + EPOLL_CTL_MOD => { + if !userhm.contains_key(&virtfd) { + return Err(threei::Errno::ENOENT as u64); + } + userhm.insert(virtfd, event); + }, + EPOLL_CTL_DEL => { + if !userhm.contains_key(&virtfd) { + return Err(threei::Errno::ENOENT as u64); + } + userhm.remove(&virtfd); + }, + _ => { + return Err(threei::Errno::EINVAL as u64); + }, + }; + Ok(()) +} + + +// #[doc = include_str!("../docs/get_virtual_epoll_wait_data.md")] +pub fn get_virtual_epoll_wait_data(cageid:u64, epfd:u64) -> Result>,threei::RetVal> { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + // get this or error out... + let epentrynum = _get_epoll_entrynum_or_error(cageid, epfd)?; + + let eptable = EPOLLTABLE.lock().unwrap(); + Ok(eptable.thisepolltable.get(&epentrynum).unwrap().userhandledhashmap.clone()) +} + + + +/********************** TESTING HELPER FUNCTION **********************/ + +// #[doc(hidden)] +// Helper to initialize / empty out state so we can test with a clean system... +// This is only used in tests, thus is hidden... +pub fn refresh() { + FDTABLE.clear(); + FDTABLE.insert(threei::TESTING_CAGEID,[Option::None;FD_PER_PROCESS_MAX as usize]); + let mut closehandlers = CLOSEHANDLERTABLE.lock().unwrap_or_else(|e| { + CLOSEHANDLERTABLE.clear_poison(); + e.into_inner() + }); + closehandlers.clear(); + // Note, it doesn't seem that Dashmaps can be poisoned... +} diff --git a/src/fdtables/dashmapvecglobal.rs b/src/fdtables/dashmapvecglobal.rs new file mode 100644 index 00000000..df76abed --- /dev/null +++ b/src/fdtables/dashmapvecglobal.rs @@ -0,0 +1,999 @@ +// DashMap;FD_PER_PROCESSS_MAX]> Space is ~30KB +// per cage w/ 1024 fds?!? +// Static DashMap. Let's see if having the FDTableEntries be a Vector +// is any faster... + +use crate::fdtables::threei; +use crate::safeposix::cage; + + +use dashmap::DashMap; + +use lazy_static::lazy_static; + +use std::collections::HashMap; + +use std::sync::Mutex; + +// This uses a Dashmap (for cages) with an array of FDTableEntry items. + +// Get constants about the fd table sizes, etc. +pub use super::commonconstants::*; + +// algorithm name. Need not be listed. Used in benchmarking output +#[doc(hidden)] +pub const ALGONAME: &str = "DashMapVecGlobal"; + +// It's fairly easy to check the fd count on a per-process basis (I just check +// when I would add a new fd). +// +// TODO: I will ignore the total limit for now. I would ideally do this on +// every creation, close, fork, etc. but it's a PITA to track this. + +// We will raise a panic anywhere we receive an unknown cageid. This frankly +// should not be possible and indicates some sort of internal error in our +// code. However, other issues, such as an invalid file descriptor when a +// cage makes a call, will be handled by returning the appropriate errno. + +// In order to store this information, I'm going to use a DashMap which +// has keys of (cageid:u64) and values that are an array of FD_PER_PROCESS_MAX +// Option items. +// +// + +// This lets me initialize the code as a global. +lazy_static! { + + #[derive(Debug)] + pub static ref FDTABLE: DashMap>> = { + let m = DashMap::new(); + // Insert a cage so that I have something to fork / test later, if need + // be. Otherwise, I'm not sure how I get this started. I think this + // should be invalid from a 3i standpoint, etc. Could this mask an + // error in the future? + // m.insert(threei::TESTING_CAGEID,vec!(Option::None;FD_PER_PROCESS_MAX as usize)); + m + }; +} + +lazy_static! { + // This is needed for close and similar functionality. I need track the + // number of times a (fdkind,underfd) is open. Note that this is across + // cages in order to enable a library to have situations where two cages + // have the same fd open. The (fdkind,underfd) tuple is the key and the + // number of times it appears is the value. If it reaches 0, the entry + // is removed. + #[derive(Debug)] + static ref FDCOUNT: DashMap<(u32,u64), u64> = { + DashMap::new() + }; + +} + +// #[doc = include_str!("../docs/init_empty_cage.md")] +pub fn init_empty_cage(cageid: u64) { + + assert!(!FDTABLE.contains_key(&cageid),"Known cageid in fdtable access"); + + FDTABLE.insert(cageid,vec!(Option::None;FD_PER_PROCESS_MAX as usize)); +} + +// #[doc = include_str!("../docs/translate_virtual_fd.md")] +pub fn translate_virtual_fd(cageid: u64, virtualfd: u64) -> Result { + + // They should not be able to pass a new cage I don't know. I should + // always have a table for each cage because each new cage is added at fork + // time + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + // Below condition checks if the virtualfd is out of bounds and if yes it throws an error + // Note that this assumes that all virtualfd numbers returned < FD_PER_PROCESS_MAX + if virtualfd >= FD_PER_PROCESS_MAX { + return Err(threei::Errno::EBADFD as u64); + } + + return match FDTABLE.get(&cageid).unwrap()[virtualfd as usize] { + Some(tableentry) => Ok(tableentry), + None => Err(threei::Errno::EBADFD as u64), + }; +} + + +// This is fairly slow if I just iterate sequentially through numbers. +// However there are not that many to choose from. I could pop from a list +// or a set as well... Likely the best solution is to keep a count of the +// largest fd handed out and to just use this until you wrap. This will be +// super fast for a normal cage and will be correct in the weird case. +// Right now, I'll just implement the slow path and will speed this up +// later, if needed. +// #[doc = include_str!("../docs/get_unused_virtual_fd.md")] +pub fn get_unused_virtual_fd( + cageid: u64, + fdkind: u32, + underfd: u64, + should_cloexec: bool, + perfdinfo: u64, +) -> Result { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + // Set up the entry so it has the right info... + // Note, a HashMap stores its data on the heap! No need to box it... + // https://doc.rust-lang.org/book/ch08-03-hash-maps.html#creating-a-new-hash-map + let myentry = FDTableEntry { + fdkind, + underfd, + should_cloexec, + perfdinfo, + }; + + let mut myfdrow = FDTABLE.get_mut(&cageid).unwrap(); + + // Check the fds in order. + for fdcandidate in 0..FD_PER_PROCESS_MAX { + // FIXME: This is likely very slow. Should do something smarter... + if myfdrow[fdcandidate as usize].is_none() { + // I just checked. Should not be there... + myfdrow[fdcandidate as usize] = Some(myentry); + _increment_fdcount(myentry); + return Ok(fdcandidate); + } + } + + // I must have checked all fds and failed to find one open. Fail! + Err(threei::Errno::EMFILE as u64) +} + +// This is used for things like dup2, which need a specific fd... +// If the requested_virtualfd is used, I close it... +// #[doc = include_str!("../docs/get_specific_virtual_fd.md")] +pub fn get_specific_virtual_fd( + cageid: u64, + requested_virtualfd: u64, + fdkind: u32, + underfd: u64, + should_cloexec: bool, + perfdinfo: u64, +) -> Result<(), threei::RetVal> { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + // If you ask for a FD number that is too large, I'm going to reject it. + // Note that, I need to use the FD_PER_PROCESS_MAX setting because this + // is also how I'm tracking how many values you have open. If this + // changed, then these constants could be decoupled... + if requested_virtualfd > FD_PER_PROCESS_MAX { + return Err(threei::Errno::EBADF as u64); + } + + // Set up the entry so it has the right info... + // Note, a HashMap stores its data on the heap! No need to box it... + // https://doc.rust-lang.org/book/ch08-03-hash-maps.html#creating-a-new-hash-map + let myentry = FDTableEntry { + fdkind, + underfd, + should_cloexec, + perfdinfo, + }; + + // This is before the FDTABLE action, so if I decrement the same fd, it + // calls the intermediate handler instead of the last one. + _increment_fdcount(myentry); + let myoptionentry = FDTABLE.get(&cageid).unwrap()[requested_virtualfd as usize]; + // always add the new entry. I'm doing this first, before I close + // the old one because I need to ensure I've cleaned up state correctly + // before calling the close handlers... + FDTABLE.get_mut(&cageid).unwrap()[requested_virtualfd as usize] = Some(myentry); + + // Update the fdcount / close the old entry, if existed + if let Some(entry) = myoptionentry { + _decrement_fdcount(entry); + } + + Ok(()) +} + +// We're just setting a flag here, so this should be pretty straightforward. +// #[doc = include_str!("../docs/set_cloexec.md")] +pub fn set_cloexec(cageid: u64, virtualfd: u64, is_cloexec: bool) -> Result<(), threei::RetVal> { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + // return EBADFD, if the fd is missing... + if FDTABLE.get(&cageid).unwrap()[virtualfd as usize].is_none() { + return Err(threei::Errno::EBADFD as u64); + } + // Set the is_cloexec flag + FDTABLE.get_mut(&cageid).unwrap()[virtualfd as usize].as_mut().unwrap().should_cloexec = is_cloexec; + Ok(()) +} + +// We're setting an opaque value here. This should be pretty straightforward. +// #[doc = include_str!("../docs/set_perfdinfo.md")] +pub fn set_perfdinfo( + cageid: u64, + virtualfd: u64, + perfdinfo: u64, +) -> Result<(), threei::RetVal> { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + // return EBADFD, if the fd is missing... + if FDTABLE.get(&cageid).unwrap()[virtualfd as usize].is_none() { + return Err(threei::Errno::EBADFD as u64); + } + + // Set optionalinfo or return EBADFD, if that's missing... + FDTABLE.get_mut(&cageid).unwrap()[virtualfd as usize].as_mut().unwrap().perfdinfo = perfdinfo; + Ok(()) +} + +// Helper function used for fork... Copies an fdtable for another process +// #[doc = include_str!("../docs/copy_fdtable_for_cage.md")] +pub fn copy_fdtable_for_cage(srccageid: u64, newcageid: u64) -> Result<(), threei::Errno> { + + assert!(FDTABLE.contains_key(&srccageid),"Unknown cageid in fdtable access"); + assert!(!FDTABLE.contains_key(&newcageid),"Known cageid in fdtable access"); + + // Insert a copy and ensure it didn't exist... + let hmcopy = FDTABLE.get(&srccageid).unwrap().clone(); + + // Increment copied items + for entry in FDTABLE.get(&srccageid).unwrap().iter() { + if entry.is_some() { + _increment_fdcount(entry.unwrap()); + } + } + + assert!(FDTABLE.insert(newcageid, hmcopy).is_none()); + + // I'm not going to bother to check the number of fds used overall yet... + // Err(threei::Errno::EMFILE as u64), + Ok(()) +} + +// This is mostly used in handling exit, etc. Returns the HashMap +// for the cage. +// #[doc = include_str!("../docs/remove_cage_from_fdtable.md")] +pub fn remove_cage_from_fdtable(cageid: u64) { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + + // remove the item first and then we clean up and call their close + // handlers. + let myfdrow = FDTABLE.remove(&cageid).unwrap().1; + + // Take only the Some items in here (clippy suggested) + for entry in myfdrow.into_iter().flatten() { + _decrement_fdcount(entry); + } + +} + +// This removes all fds with the should_cloexec flag set. They are returned +// in a new hashmap... +// #[doc = include_str!("../docs/empty_fds_for_exec.md")] +pub fn empty_fds_for_exec(cageid: u64) { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + let mut myfdrow = FDTABLE.get_mut(&cageid).unwrap(); + // I need to call all the close handlers at the end. So I need to + // get vector of them to do the operation on... + let mut closevec = Vec::new(); + + for item in 0..FD_PER_PROCESS_MAX as usize { + if myfdrow[item].is_some() && myfdrow[item].unwrap().should_cloexec { + // handle this in a moment... + closevec.push(myfdrow[item].unwrap()); + + // Always zero out the row before calling their handler + myfdrow[item] = None; + } + } + + // Need to drop the lock, before calling the handlers. + drop(myfdrow); + + // Now, we can call the close handlers! + for entry in closevec { + _decrement_fdcount(entry); + } + +} + +// Returns the HashMap returns a copy of the fdtable for a cage. Useful +// helper function for a caller that needs to examine the table. Likely could +// be more efficient by letting the caller borrow this... +// #[doc = include_str!("../docs/return_fdtable_copy.md")] +#[must_use] // must use the return value if you call it. +pub fn return_fdtable_copy(cageid: u64) -> HashMap { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + let mut myhashmap = HashMap::new(); + + let myfdrow = FDTABLE.get(&cageid).unwrap(); + for item in 0..FD_PER_PROCESS_MAX as usize { + if myfdrow[item].is_some() { + myhashmap.insert(item as u64,myfdrow[item].unwrap()); + } + } + myhashmap +} + + + +/******************* CLOSE SPECIFIC FUNCTIONALITY *******************/ + +// These indicate what functions should be called upon a virtualfd closing. +// The handler which is called depends on number of (fdkind,underfd) tuples +// that are used across *all instances managed by this library including in +// other cages*. +struct CloseHandlers { + // Called when close is called, but at least one (fdkind,underfd) + // reference still remains. Called with (fdkind,underfd,count) + intermediate: fn(FDTableEntry,u64), + // Called when close is called, but at least one (fdkind,underfd) + // reference still remains. Called with (fdkind,underfd,0) + last: fn(FDTableEntry,u64), +} + + +lazy_static! { + // This holds the user registered handlers they want to have called when + // a close occurs. I did this rather than return messy data structures + // from the close, exec, and exit handlers because it seemed cleaner... + #[derive(Debug)] + static ref CLOSEHANDLERTABLE: Mutex> = { + Mutex::new(HashMap::new()) + }; +} + + +// #[doc = include_str!("../docs/close_virtualfd.md")] +pub fn close_virtualfd(cageid:u64, virtfd:u64) -> Result<(),threei::RetVal> { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + // cloning this so I don't hold a lock and deadlock close handlers + let mut myfdrow = FDTABLE.get_mut(&cageid).unwrap().clone(); + + + if myfdrow[virtfd as usize].is_some() { + let entry = myfdrow[virtfd as usize]; + + // Zero out this entry before calling the close handler... + myfdrow[virtfd as usize] = None; + + FDTABLE.insert(cageid, myfdrow.clone()); + + // always _decrement last as it may call the user handler... + _decrement_fdcount(entry.unwrap()); + return Ok(()); + } + Err(threei::Errno::EBADFD as u64) +} + + +// Register a series of helpers to be called for close. Can be called +// multiple times to override the older helpers. +// #[doc = include_str!("../docs/register_close_handlers.md")] +pub fn register_close_handlers(fdkind:u32, intermediate: fn(FDTableEntry,u64), last: fn(FDTableEntry,u64)) { + // Unlock the table and set the handlers... + let mut closehandlertable = CLOSEHANDLERTABLE.lock().unwrap(); + let closehandler = CloseHandlers { + intermediate, + last, + }; + // overwrite whatever is in there... + closehandlertable.insert(fdkind,closehandler); +} + + +// Helpers to track the count of times each (fdkind,underfd) is used +#[doc(hidden)] +fn _decrement_fdcount(entry:FDTableEntry) { + + let mytuple = (entry.fdkind, entry.underfd); + + let newcount:u64 = FDCOUNT.get(&mytuple).unwrap().value() - 1; + + let intermediatech; + let lastch; + // Doing this to release the lock so I can call it recursively... + let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + if let Some(closehandlerentry) = closehandlers.get(&entry.fdkind) { + intermediatech = closehandlerentry.intermediate; + lastch = closehandlerentry.last; + } + else { + // TODO: If at any future point, I wanted to add a "default" handler + // for all fdkind values, I would add it here... + intermediatech = NULL_FUNC; + lastch = NULL_FUNC; + } + // release the lock... + drop(closehandlers); + + if newcount > 0 { + // Update before calling their close handler in case they do operations + // inside the close handler which create / close fds... + FDCOUNT.insert(mytuple,newcount); + (intermediatech)(entry,newcount); + } + else{ + // Remove before calling their close handler in case they do operations + // inside the close handler which create / close fds... + FDCOUNT.remove(&mytuple); + (lastch)(entry,0); + } +} + +// Helpers to track the count of times each (fdkind,underfd) is used +#[doc(hidden)] +fn _increment_fdcount(entry:FDTableEntry) { + + let mytuple = (entry.fdkind, entry.underfd); + + // Get a mutable reference to the entry so we can update it. + if let Some(mut count) = FDCOUNT.get_mut(&mytuple) { + *count += 1; + } else { + FDCOUNT.insert(mytuple, 1); + } +} + + + +/*************** Code for handling select() ****************/ + +use libc::fd_set; +use std::collections::HashSet; +use std::cmp; +use std::mem; + +// Helper to get an empty fd_set. Helper function to isolate unsafe code, +// etc. +#[doc(hidden)] +#[must_use] // must use the return value if you call it. +pub fn _init_fd_set() -> fd_set { + let raw_fd_set:fd_set; + unsafe { + let mut this_fd_set = mem::MaybeUninit::::uninit(); + libc::FD_ZERO(this_fd_set.as_mut_ptr()); + raw_fd_set = this_fd_set.assume_init(); + } + raw_fd_set +} + +#[doc(hidden)] +pub fn _fd_set(fd:u64, thisfdset:&mut fd_set) { + unsafe{libc::FD_SET(fd as i32,thisfdset)} +} + +#[doc(hidden)] +#[must_use] // must use the return value if you call it. +pub fn _fd_isset(fd:u64, thisfdset:&fd_set) -> bool { + unsafe{libc::FD_ISSET(fd as i32,thisfdset)} +} + + + +// This is a helper that just does a single type (r/w/e) and returns: +// bithashmap: HashMap +// unhandledhashmap: HashMap> +// mappingtable: HashMap +// +// With this we trivially build the whole function... + +// helper to call before calling select beneath you. Translates your virtfds +// into a bitmask you may use for select. +// See: https://man7.org/linux/man-pages/man2/select.2.html for details / +// corner cases about the arguments. +// + +// I hate doing this, but don't know how to make this interface better... +#[allow(clippy::type_complexity)] +#[allow(clippy::implicit_hasher)] +// #[doc = include_str!("../docs/get_bitmask_for_select.md")] +pub fn get_bitmask_for_select(cageid:u64, nfds:u64, bits:Option, fdkinds:&HashSet) -> Result<(HashMap, HashMap>, HashMap<(u32,u64),u64>),threei::RetVal> { + + if nfds >= FD_PER_PROCESS_MAX { + return Err(threei::Errno::EINVAL as u64); + } + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + // The three things I will return... + let mut retbittable:HashMap = HashMap::new(); + let mut retunparsedtable:HashMap> = HashMap::new(); + let mut mappingtable:HashMap<(u32,u64),u64> = HashMap::new(); + + // If we were asked to do this on nothing, return empty mappings... + if bits.is_none() { + return Ok((retbittable, retunparsedtable, mappingtable)); + } + + let infdset = bits.unwrap(); + + // dashmaps are lockless, but usually I would grab a lock on the fdtable + // here... + let binding = FDTABLE.get(&cageid).unwrap(); + let myfdrow = binding.value().clone(); + + // Clippy is somehow missing how the virtualfd is being used throughout + // here. It's not just a range value + #[allow(clippy::needless_range_loop)] + // iterate through the set bits... + for bit in 0..nfds as usize { + let pos = bit as u64; + if _fd_isset(pos,&infdset) { + if let Some(entry) = myfdrow[bit] { + + // I like to do the shorter case first rather than having + // it later. + #[allow(clippy::if_not_else)] + // Which return set do I go in? + if !fdkinds.contains(&entry.fdkind) { + // Is unparsed... Clippy's suggestion to insert if missing + retunparsedtable.entry(entry.fdkind).or_default(); + retunparsedtable.get_mut(&entry.fdkind).unwrap().insert(entry); + // and update the mappingtable to have the bit from the + // original fd... + mappingtable.insert((entry.fdkind,entry.underfd),pos); + } + else { + + let startingnfds; + let mut startingfdset; + + // Either initialize it or use what exists + if retbittable.contains_key(&entry.fdkind) { + (startingnfds, startingfdset) = *retbittable.get(&entry.fdkind).unwrap(); + } + else{ + startingnfds = 1; + // I don't init this above because a fd_set is a large + // data structure and would be costly. + startingfdset = _init_fd_set(); + } + + // Update the table and the nfds + _fd_set(entry.underfd,&mut startingfdset); + let newnfds = cmp::max(startingnfds, entry.underfd+1); + + // and update the mappingtable to have the bit from the + // original fd... + mappingtable.insert((entry.fdkind,entry.underfd),pos); + + // insert the item + retbittable.insert(entry.fdkind,(newnfds,startingfdset)); + } + } + else { + return Err(threei::Errno::EBADF as u64); + } + } + } + Ok((retbittable, retunparsedtable, mappingtable)) + +} + + +#[allow(clippy::type_complexity)] +#[allow(clippy::implicit_hasher)] +// #[doc = include_str!("../docs/prepare_bitmasks_for_select.md")] +pub fn prepare_bitmasks_for_select(cageid:u64, nfds:u64, rbits:Option, wbits:Option, ebits:Option, fdkinds:&HashSet) -> Result<([HashMap;3], [HashMap>;3], HashMap<(u32,u64),u64>),threei::RetVal> { + // This is a pretty simple function. Calls get_bitmask_for_select + // repeatedly and combines the results... + // [HashSet<(u64,u64)>;3] + + // return the error, if need be + let rresult = get_bitmask_for_select(cageid, nfds, rbits, fdkinds)?; + let wresult = get_bitmask_for_select(cageid, nfds, wbits, fdkinds)?; + let eresult = get_bitmask_for_select(cageid, nfds, ebits, fdkinds)?; + + let mut mappingtable = rresult.2; + mappingtable.extend(wresult.2); + mappingtable.extend(eresult.2); + + Ok(([rresult.0,wresult.0,eresult.0],[rresult.1,wresult.1,eresult.1],mappingtable)) + +} + + +// helper to call after calling select beneath you. returns the fd_set you +// need for your return from a select call and the number of unique flags +// set... + +// I've given them the hashmap, so don't need flexibility in what they return... +#[allow(clippy::implicit_hasher)] +#[must_use] // must use the return value if you call it. +// #[doc = include_str!("../docs/get_one_virtual_bitmask_from_select_result.md")] +pub fn get_one_virtual_bitmask_from_select_result(fdkind:u32, nfds:u64, bits:Option, unprocessedset:HashSet, startingbits:Option,mappingtable:&HashMap<(u32,u64),u64>) -> (u64, Option) { + + // Note, I don't need the cage_id here because I have the mappingtable... + + assert!(nfds < FD_PER_PROCESS_MAX,"This shouldn't be possible because we shouldn't have returned this previously"); + + let mut flagsset = 0; + + if bits.is_none() && unprocessedset.is_empty() { + return (flagsset,None); + } + + // I probably should pass a reference to startingbits to avoid copying the + // bit structure... + let mut retbits = match startingbits { + Some(val) => val, + None => _init_fd_set(), + }; + + if let Some(inset) = bits { + for bit in 0..nfds as usize { + let pos = bit as u64; + if _fd_isset(pos,&inset)&& !_fd_isset(*mappingtable.get(&(fdkind,pos)).unwrap(),&retbits) { + flagsset+=1; + _fd_set(*mappingtable.get(&(fdkind,pos)).unwrap(),&mut retbits); + } + } + } + for virtfd in unprocessedset { + if !_fd_isset(virtfd,&retbits) { + flagsset+=1; + _fd_set(virtfd,&mut retbits); + } + } + + (flagsset,Some(retbits)) + +} + + + +/********************** POLL SPECIFIC FUNCTIONS **********************/ + +// helper to call before calling poll beneath you. replaces the fds in +// the poll struct with virtual versions and returns the items you need +// to check yourself... +#[allow(clippy::implicit_hasher)] +#[allow(clippy::type_complexity)] +// #[doc = include_str!("../docs/convert_virtualfds_for_poll.md")] +#[must_use] // must use the return value if you call it. +pub fn convert_virtualfds_for_poll(cageid:u64, virtualfds:HashSet) -> (HashMap>, HashMap<(u32,u64),u64>) { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + let thefdrow = FDTABLE.get(&cageid).unwrap().clone(); + let mut mappingtable:HashMap<(u32,u64),u64> = HashMap::new(); + let mut rethashmap:HashMap> = HashMap::new(); + + + // BUG?: I'm ignoring the fact that virtualfds can show up multiple times. + // I'm not sure this actually matters, but I didn't think hard about it. + for virtfd in virtualfds { + if let Some(entry) = thefdrow[virtfd as usize] { + // Insert an empty HashSet, if needed + rethashmap.entry(entry.fdkind).or_default(); + mappingtable.entry((entry.fdkind,entry.underfd)).or_default(); + + rethashmap.get_mut(&entry.fdkind).unwrap().insert((virtfd,entry)); + mappingtable.insert((entry.fdkind,entry.underfd), virtfd); + } + else { + let myentry = FDTableEntry { + fdkind:FDT_INVALID_FD, + underfd:virtfd, + should_cloexec:false, + perfdinfo:u64::from(FDT_INVALID_FD), + }; + + // Insert an empty HashSet, if needed + rethashmap.entry(FDT_INVALID_FD).or_default(); + mappingtable.entry((FDT_INVALID_FD,virtfd)).or_default(); + + rethashmap.get_mut(&FDT_INVALID_FD).unwrap().insert((virtfd,myentry)); + // Add this because they need to handle it if POLLNVAL is set. + // An exception should not be raised!!! + + // I will add this to the mapping table, because I do think they + // may want to raise an exception, etc. based upon this and signal + // back. I am setting the underfd to be the virtfd, so I can + // reverse this process, if multiple entries like this occur. + mappingtable.insert((FDT_INVALID_FD,virtfd), virtfd); + } + } + + (rethashmap, mappingtable) +} + + + +// helper to call after calling poll. replaces the fds in the vector +// with virtual ones... +// #[doc = include_str!("../docs/convert_poll_result_back_to_virtual.md")] +// I give them the hashmap, so don't need flexibility in what they return... +#[allow(clippy::implicit_hasher)] +#[must_use] // must use the return value if you call it. +pub fn convert_poll_result_back_to_virtual(fdkind:u32,underfd:u64, mappingtable:&HashMap<(u32,u64),u64>) -> Option { + + // I don't care what cage was used, and don't need to lock anything... + // I have the mappingtable! + + // Should this even be a function? + mappingtable.get(&(fdkind,underfd)).copied() +} + + + +/********************** EPOLL SPECIFIC FUNCTIONS **********************/ + + +// Supporting epollfds is done by a fdkind which is not set by the user. +// There are a few complexities here: +// 1) an epollfd gets a virtual file descriptor +// 2) a epollfd can point to any number of other fds of different kinds +// 3) an epollfd can point to epollfds, which can point to other epollfds, etc. +// and possibly cause a loop to occur (which is an error) +// +// My thinking is this is handled as similarly to poll as possible. We push +// off the problem of understanding what the event types are to the implementer +// of the library. +// +// In my view, epoll_wait() is quite simple to support. One basically just +// keeps a list of virtual fds for this epollfd and their corresponding event +// types, which they may need to poll themselves. After this, they handle the +// call. +// +// epoll_ctl is complex, but really has the same fundamental problem as +// epoll_create: the epollfd. +// +// I'll create a new fdkind for epoll. When epoll_create is called, the +// caller can decide which fdkinds need to be passed down to the underlying +// epoll_create call(s). Similarly, when epoll_ctl is called, one either +// handles the call internally or uses the underfd for the fdkind... +// +// Interestingly, this actually would be just as easy to build on top of the +// fdtables library as into it. +// +// Each epollfd will have some virtual fds associated with it. Each of those +// will have an event mask. So I'll have a mutex around an EPollTable struct. +// This contains the next available entry and an epollhashmap. +// I use a hashmap here to better support removing and modifying items. + +// Note, I'm defining a bunch of symbols myself because libc doesn't import +// them on systems that don't support epoll and I want to be able to build +// the code anywhere. See commonconstants.rs for more info. + + +// Okay, so the basic structure is like this: +// 1) epoll_create_helper sets up an epollfd. You will need to have a unique +// underfd for each fdkind below you, if you want to call down. +// 2) my API should track all of the fdkinds where there isn't an underlying +// epollfd +// 3) epoll_ctl / epoll_wait will work on whichever is appropriate +// +// A hashmap of: +// HashMap for tracking how to call down. +// HashMap> seems to make the most sense for the other +// descriptors. + + +// a structure that exists for each epoll descriptor to track the underfd(s) +// and parts the user will handle +#[derive(Clone, Debug, Default)] +struct EPollDescriptorInfo { + // I didn't combine thewe two hashmaps into one because they are used + // separately and the resulting value type would be too messy... + + underfdhashmap: HashMap, // The underfd for a specific fdkind. + // Used only when an epoll call will + // call down beneath it. + userhandledhashmap: HashMap>, + // This has all of the things the user + // will virtualize and handle. The key + // is the fdkind. +} + +// TODO: I don't clean up this table yet. I probably should when the last +// reference to a fd is closed, but this bookkeeping seems excessive at this +// time... +#[derive(Clone, Debug)] +struct EPollTable { + highestneverusedentry: u64, // Never resets (even after close). Used to + // let us quickly get an unused entry + thisepolltable: HashMap, +} + +lazy_static! { + + #[derive(Debug)] + static ref EPOLLTABLE: Mutex = { + let newetable = HashMap::new(); + let m = EPollTable { + highestneverusedentry:0, + thisepolltable:newetable, + }; + Mutex::new(m) + }; +} + +fn _get_epoll_entrynum_or_error(cageid:u64, epfd:u64) -> Result { + // Is the epfd ok? + match FDTABLE.get(&cageid).unwrap()[epfd as usize] { + None => { + Err(threei::Errno::EBADF as u64) + }, + Some(tableentry) => { + // You must call this on an epoll fd + if tableentry.fdkind == FDT_KINDEPOLL { + Ok(tableentry.underfd) + } + else { + Err(threei::Errno::EINVAL as u64) + } + }, + } +} + + +// #[doc = include_str!("../docs/epoll_create_empty.md")] +pub fn epoll_create_empty(cageid:u64, should_cloexec:bool) -> Result { + + let mut ept = EPOLLTABLE.lock().unwrap(); + + // return the same errno (EMFile), if we get one + let newepollfd = get_unused_virtual_fd(cageid, FDT_KINDEPOLL, ept.highestneverusedentry, should_cloexec, 0)?; + + let newentrynum = ept.highestneverusedentry; + ept.highestneverusedentry+=1; + + // Create a new entry with empty values + ept.thisepolltable.entry(newentrynum).or_default(); + Ok(newepollfd) + +} + +// #[doc = include_str!("../docs/epoll_add_underfd.md")] +pub fn epoll_add_underfd(cageid:u64, virtepollfd:u64, fdkind:u32, underfd:u64) -> Result<(),threei::RetVal> { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + let mut ept = EPOLLTABLE.lock().unwrap(); + + // get this or error out... + let epentrynum = _get_epoll_entrynum_or_error(cageid, virtepollfd)?; + + let myhm = &mut ept.thisepolltable.get_mut(&epentrynum).unwrap().underfdhashmap; + + assert!(!myhm.contains_key(&fdkind),"Adding duplicate underfd to epollfd"); + + myhm.insert(fdkind,underfd); + + Ok(()) + +} + + +// #[doc = include_str!("../docs/epoll_get_underfd_hashmap.md")] +pub fn epoll_get_underfd_hashmap(cageid:u64, virtepollfd:u64) -> Result,threei::RetVal> { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + let ept = EPOLLTABLE.lock().unwrap(); + + // get this or error out... + let epentrynum = _get_epoll_entrynum_or_error(cageid, virtepollfd)?; + + Ok(ept.thisepolltable.get(&epentrynum).unwrap().underfdhashmap.clone()) + +} + + + +// #[doc = include_str!("../docs/virtualize_epoll_ctl.md")] +pub fn virtualize_epoll_ctl(cageid:u64, epfd:u64, op:i32, virtfd:u64, event:epoll_event) -> Result<(),threei::RetVal> { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + if epfd == virtfd { + return Err(threei::Errno::EINVAL as u64); + } + + // get this or error out... + let epentrynum = _get_epoll_entrynum_or_error(cageid, epfd)?; + + // Okay, I know which table entry, now verify the virtfd... + + + let virtfdkind:u32; + + if let Some(tableentry) = FDTABLE.get(&cageid).unwrap()[virtfd as usize] { + // Right now, I don't support this, so error... + if tableentry.fdkind == FDT_KINDEPOLL { + // TODO: support EPOLLFDs... + return Err(threei::Errno::ENOSYS as u64); + } + virtfdkind = tableentry.fdkind; + } + else { + // The virtual Fd doesn't exist -- error... + return Err(threei::Errno::EBADF as u64); + } + + let mut eptable = EPOLLTABLE.lock().unwrap(); +// let userhm = eptable.thisepolltable.get_mut(&epentrynum).unwrap().userhandledhashmap.entry(virtfdkind).or_default(); + let userhm = &mut eptable.thisepolltable.get_mut(&epentrynum).unwrap().userhandledhashmap; + + match op { + EPOLL_CTL_ADD => { + let thisuserhm = userhm.entry(virtfdkind).or_default(); + if thisuserhm.contains_key(&virtfd) { + return Err(threei::Errno::EEXIST as u64); + } + // BUG: Need to check for ELOOP here once I support EPOLLFDs + // referencing each other... + + thisuserhm.insert(virtfd, event); + }, + EPOLL_CTL_MOD => { + if !userhm.contains_key(&virtfdkind) { + return Err(threei::Errno::ENOENT as u64); + } + let thisuserhm: &mut HashMap = userhm.get_mut(&virtfdkind).unwrap(); + if !thisuserhm.contains_key(&virtfd) { + return Err(threei::Errno::ENOENT as u64); + } + thisuserhm.insert(virtfd, event); + }, + EPOLL_CTL_DEL => { + if !userhm.contains_key(&virtfdkind) { + return Err(threei::Errno::ENOENT as u64); + } + let thisuserhm: &mut HashMap = userhm.get_mut(&virtfdkind).unwrap(); + if !thisuserhm.contains_key(&virtfd) { + return Err(threei::Errno::ENOENT as u64); + } + thisuserhm.remove(&virtfd); + // If this was the last entry, delete the key altogether... + if thisuserhm.is_empty() { + userhm.remove(&virtfdkind); + } + }, + _ => { + return Err(threei::Errno::EINVAL as u64); + }, + }; + Ok(()) +} + + +// #[doc = include_str!("../docs/get_virtual_epoll_wait_data.md")] +pub fn get_virtual_epoll_wait_data(cageid:u64, epfd:u64) -> Result>,threei::RetVal> { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + // get this or error out... + let epentrynum = _get_epoll_entrynum_or_error(cageid, epfd)?; + + let eptable = EPOLLTABLE.lock().unwrap(); + Ok(eptable.thisepolltable.get(&epentrynum).unwrap().userhandledhashmap.clone()) +} + + + +/********************** TESTING HELPER FUNCTION **********************/ + +#[doc(hidden)] +// Helper to initialize / empty out state so we can test with a clean system... +// This is only used in tests, thus is hidden... +pub fn refresh() { + FDTABLE.clear(); + FDTABLE.insert(threei::TESTING_CAGEID,vec![Option::None;FD_PER_PROCESS_MAX as usize]); + let mut closehandlers = CLOSEHANDLERTABLE.lock().unwrap_or_else(|e| { + CLOSEHANDLERTABLE.clear_poison(); + e.into_inner() + }); + closehandlers.clear(); + // Note, it doesn't seem that Dashmaps can be poisoned... +} diff --git a/src/example_grates/mod.rs b/src/fdtables/mod.rs similarity index 90% rename from src/example_grates/mod.rs rename to src/fdtables/mod.rs index 0f6f3747..bdf93ae6 100644 --- a/src/example_grates/mod.rs +++ b/src/fdtables/mod.rs @@ -12,4 +12,4 @@ pub use commonconstants::*; pub use dashmapvecglobal::*; // pub use vanillaglobal::*; // pub use muthashmaxglobal::*; -// pub use dashmaparrayglobal::*; \ No newline at end of file +// pub use dashmaparrayglobal::*; diff --git a/src/example_grates/muthashmaxglobal.rs b/src/fdtables/muthashmaxglobal.rs similarity index 59% rename from src/example_grates/muthashmaxglobal.rs rename to src/fdtables/muthashmaxglobal.rs index 266916cf..a2f8e934 100644 --- a/src/example_grates/muthashmaxglobal.rs +++ b/src/fdtables/muthashmaxglobal.rs @@ -1,8 +1,4 @@ - -use crate::safeposix::cage; -use crate::safeposix::syscalls::fs_calls::*; - -use super::threei; +use crate::threei; use lazy_static::lazy_static; @@ -18,19 +14,9 @@ use std::collections::HashMap; pub use super::commonconstants::*; // algorithm name. Need not be listed in the docs. -#[doc(hidden)] +// #[doc(hidden)] pub const ALGONAME: &str = "MutHashMaxGlobal"; -// These are the values we look up with at the end... -// #[doc = include_str!("../docs/fdtableentry.md")] -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct FDTableEntry { - pub realfd: u64, // underlying fd (may be a virtual fd below us or - // a kernel fd) - pub should_cloexec: bool, // should I close this when exec is called? - pub optionalinfo: u64, // user specified / controlled data -} - #[derive(Clone, Debug)] struct FDTable { highestneverusedfd: u64, // Never resets (even after close). Used to @@ -64,33 +50,33 @@ struct FDTable { // This lets me initialize the code as a global. lazy_static! { - #[derive(Debug)] - static ref GLOBALFDTABLE: Mutex> = { - let mut m = HashMap::new(); - // Insert a cage so that I have something to fork / test later, if need - // be. Otherwise, I'm not sure how I get this started. I think this - // should be invalid from a 3i standpoint, etc. Could this mask an - // error in the future? - // - // - let newmap = HashMap::new(); - let emptytab = FDTable{ - highestneverusedfd:0, - thisfdtable:newmap, - }; + #[derive(Debug)] + static ref GLOBALFDTABLE: Mutex> = { + let mut m = HashMap::new(); + // Insert a cage so that I have something to fork / test later, if need + // be. Otherwise, I'm not sure how I get this started. I think this + // should be invalid from a 3i standpoint, etc. Could this mask an + // error in the future? + // + // + let newmap = HashMap::new(); + let emptytab = FDTable{ + highestneverusedfd:0, + thisfdtable:newmap, + }; - m.insert(threei::TESTING_CAGEID,emptytab); - Mutex::new(m) - }; + m.insert(threei::TESTING_CAGEID,emptytab); + Mutex::new(m) + }; } lazy_static! { - // This is needed for close and similar functionality. I need track the - // number of times a realfd is open - #[derive(Debug)] - static ref GLOBALREALFDCOUNT: Mutex> = { - Mutex::new(HashMap::new()) - }; + // This is needed for close and similar functionality. I need track the + // number of times a realfd is open + #[derive(Debug)] + static ref GLOBALREALFDCOUNT: Mutex> = { + Mutex::new(HashMap::new()) + }; } @@ -101,10 +87,6 @@ struct CloseHandlers { unreal_handler: fn(u64), } -// Seems sort of like a constant... I'm not sure if this is bad form or not... -#[allow(non_snake_case)] -pub fn NULL_FUNC(_:u64) { } - lazy_static! { // This holds the user registered handlers they want to have called when // a close occurs. I did this rather than return messy data structures @@ -249,19 +231,23 @@ pub fn get_specific_virtual_fd( // I moved this up so that if I decrement the same realfd, it calls // the intermediate handler instead of the final one. _increment_realfd(realfd); - if let Some(entry) = fdtable.get(&cageid).unwrap().thisfdtable.get(&requested_virtualfd) { + + // always add the new entry. insert returns the old entry. + let myoptionentry = fdtable.get_mut(&cageid).unwrap().thisfdtable.insert(requested_virtualfd,myentry); + drop(fdtable); + + // Close the old entry, if I need to... + if let Some(entry) = myoptionentry { if entry.realfd != NO_REAL_FD { - _decrement_realfd(entry.realfd); + _decrement_realfd(entry.realfd); } else { // Let their code know this has been closed... - let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); - (closehandlers.unreal_handler)(entry.optionalinfo); + let unrealclosehandler = (*CLOSEHANDLERTABLE.lock().unwrap()).unreal_handler; + (unrealclosehandler)(entry.optionalinfo); } } - // always add the new entry - fdtable.get_mut(&cageid).unwrap().thisfdtable.insert(requested_virtualfd,myentry); Ok(()) } @@ -360,20 +346,21 @@ pub fn remove_cage_from_fdtable(cageid: u64) { panic!("Unknown cageid in fdtable access"); } + let cagetable = fdtable.remove(&cageid).unwrap(); + drop(fdtable); + // decrement the reference to items in the fdtable appropriately... - for v in fdtable.get(&cageid).unwrap().thisfdtable.values() { + for v in cagetable.thisfdtable.values() { if v.realfd != NO_REAL_FD { _decrement_realfd(v.realfd); } else { // Let their code know this has been closed... - let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); - (closehandlers.unreal_handler)(v.optionalinfo); + let unrealclosehandler = CLOSEHANDLERTABLE.lock().unwrap().unreal_handler; + (unrealclosehandler)(v.optionalinfo); } } - - fdtable.remove(&cageid).unwrap(); } // This removes all fds with the should_cloexec flag set. They are returned @@ -400,17 +387,14 @@ pub fn empty_fds_for_exec(cageid: u64) { let thiscagefdtable = &mut fdtable.get_mut(&cageid).unwrap().thisfdtable; let mut without_cloexec_hm:HashMap = HashMap::new(); + // I bother to put this in a hashmap so I can call the closehandlers + // all after I have re-inserted everything. This ensures the state + // is consistent. I only need the values, not the keys... + let mut with_cloexec_vec:Vec = Vec::new(); + for (k,v) in thiscagefdtable.drain() { if v.should_cloexec { - if v.realfd == NO_REAL_FD { - // Let their code know this has been closed... - let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); - (closehandlers.unreal_handler)(v.optionalinfo); - } - else { - // Let the helper tell the user and decrement the count - _decrement_realfd(v.realfd); - } + with_cloexec_vec.push(v); } else{ without_cloexec_hm.insert(k,v); @@ -424,8 +408,26 @@ pub fn empty_fds_for_exec(cageid: u64) { thisfdtable:without_cloexec_hm, }; - // Put the ones without_cloexec back in the hashmap... + // Put the ones without_cloexec back in the hashmap since they shouldn't + // be closed... fdtable.insert(cageid,newfdtable); + // Release the lock... + drop(fdtable); + + // Now call the close handlers on the others... + for v in with_cloexec_vec { + if v.realfd == NO_REAL_FD { + // Let their code know this has been closed... I get the handler + // repeatedly in case they change it during execution of another + // handler... + let closehandlerunreal = CLOSEHANDLERTABLE.lock().unwrap().unreal_handler; + (closehandlerunreal)(v.optionalinfo); + } + else { + // Let the helper tell the user and decrement the count + _decrement_realfd(v.realfd); + } + } } @@ -456,14 +458,17 @@ pub fn close_virtualfd(cageid:u64, virtfd:u64) -> Result<(),threei::RetVal> { panic!("Unknown cageid in fdtable access"); } - let thiscagesfdtable = &mut fdtable.get_mut(&cageid).unwrap().thisfdtable; + // remove the closed item from the fdtable (and inspect it) + let thisoption = fdtable.get_mut(&cageid).unwrap().thisfdtable.remove(&virtfd); + drop(fdtable); - match thiscagesfdtable.remove(&virtfd) { + match thisoption { Some(entry) => if entry.realfd == NO_REAL_FD { // Let their code know this has been closed... - let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); - (closehandlers.unreal_handler)(entry.optionalinfo); + let closehandlerunreal = CLOSEHANDLERTABLE.lock().unwrap().unreal_handler; + let optionalinfo = entry.optionalinfo; + (closehandlerunreal)(optionalinfo); Ok(()) } else { @@ -480,6 +485,8 @@ pub fn close_virtualfd(cageid:u64, virtfd:u64) -> Result<(),threei::RetVal> { // #[doc = include_str!("../docs/register_close_handlers.md")] pub fn register_close_handlers(intermediate_handler: fn(u64), final_handler: fn(u64), unreal_handler: fn(u64)) { // Unlock the table and set the handlers... + // TODO: I made a serious attempt to try to keep closehandlers that call + // close, etc. from deadlocking the system. More testing, etc. is needed. let mut closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); closehandlers.intermediate_handler = intermediate_handler; closehandlers.final_handler = final_handler; @@ -487,30 +494,43 @@ pub fn register_close_handlers(intermediate_handler: fn(u64), final_handler: fn( } // Helpers to track the count of times each realfd is used -#[doc(hidden)] +// #[doc(hidden)] fn _decrement_realfd(realfd:u64) -> u64 { // Do nothing if it's not a realfd... - if realfd == NO_REAL_FD { - panic!("Called _decrement_realfd with NO_REAL_FD"); - } + assert!(realfd != NO_REAL_FD, "Called _decrement_realfd with NO_REAL_FD"); // Get this table's lock... let mut realfdcount = GLOBALREALFDCOUNT.lock().unwrap(); let newcount:u64 = realfdcount.get(&realfd).unwrap() - 1; let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + let intermediateclosehandler = closehandlers.intermediate_handler; + let finalclosehandler = closehandlers.final_handler; + // release the lock... + drop(closehandlers); + if newcount > 0 { - (closehandlers.intermediate_handler)(realfd); realfdcount.insert(realfd,newcount); + // Need to drop locks to call the handlers or else will deadlock... + drop(realfdcount); + + (intermediateclosehandler)(realfd); } else { - (closehandlers.final_handler)(realfd); + // Remove before calling their close handler in case they do operations + // inside the close handler which create / close fds... + realfdcount.remove(&realfd); + // Need to drop locks to call the handlers or else will deadlock... + drop(realfdcount); + + (finalclosehandler)(realfd); + } newcount } // Helpers to track the count of times each realfd is used -#[doc(hidden)] +// #[doc(hidden)] fn _increment_realfd(realfd:u64) -> u64 { if realfd == NO_REAL_FD { return 0 @@ -541,6 +561,7 @@ use std::mem; // Helper to get an empty fd_set. Helper function to isolate unsafe code, // etc. +// #[doc(hidden)] pub fn _init_fd_set() -> fd_set { let raw_fd_set:fd_set; unsafe { @@ -551,22 +572,18 @@ pub fn _init_fd_set() -> fd_set { raw_fd_set } -// Helper to get a null pointer. -pub fn _get_null_fd_set() -> fd_set { - //unsafe{ptr::null_mut()} - // BUG!!! Need to fix this later. - _init_fd_set() -} - +// #[doc(hidden)] pub fn _fd_set(fd:u64, thisfdset:&mut fd_set) { unsafe{libc::FD_SET(fd as i32,thisfdset)} } +// #[doc(hidden)] pub fn _fd_isset(fd:u64, thisfdset:&fd_set) -> bool { unsafe{libc::FD_ISSET(fd as i32,thisfdset)} } // Computes the bitmodifications and returns a (maxnfds, unrealset) tuple... +// #[doc(hidden)] fn _do_bitmods(myfdmap:HashMap, nfds:u64, infdset: fd_set, thisfdset: &mut fd_set, mappingtable: &mut HashMap) -> Result<(u64,HashSet<(u64,u64)>),threei::RetVal> { let mut unrealhashset:HashSet<(u64,u64)> = HashSet::new(); // Iterate through the infdset and set those values as is appropriate @@ -605,46 +622,45 @@ fn _do_bitmods(myfdmap:HashMap, nfds:u64, infdset: fd_set, thi #[allow(clippy::type_complexity)] #[allow(clippy::too_many_arguments)] // #[doc = include_str!("../docs/get_real_bitmasks_for_select.md")] -// pub fn get_real_bitmasks_for_select(cageid:u64, nfds:u64, readbits:Option, writebits:Option, exceptbits:Option) -> Result<(u64, fd_set, fd_set, fd_set, [HashSet<(u64,u64)>;3], HashMap),threei::RetVal> { +pub fn get_real_bitmasks_for_select(cageid:u64, nfds:u64, readbits:Option, writebits:Option, exceptbits:Option) -> Result<(u64, Option, Option, Option, [HashSet<(u64,u64)>;3], HashMap),threei::RetVal> { -// if nfds >= FD_PER_PROCESS_MAX { -// return Err(threei::Errno::EINVAL as u64); -// } - -// let globfdtable = GLOBALFDTABLE.lock().unwrap(); - -// if !globfdtable.contains_key(&cageid) { -// panic!("Unknown cageid in fdtable access"); -// } + if nfds >= FD_PER_PROCESS_MAX { + return Err(threei::Errno::EINVAL as u64); + } -// let mut unrealarray:[HashSet<(u64,u64)>;3] = [HashSet::new(),HashSet::new(),HashSet::new()]; -// let mut mappingtable:HashMap = HashMap::new(); -// let mut newnfds = 0; + let globfdtable = GLOBALFDTABLE.lock().unwrap(); -// // putting results in a vec was the cleanest way I found to do this.. -// let mut resultvec = Vec::new(); + if !globfdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } -// for (unrealoffset, inset) in [readbits,writebits, exceptbits].into_iter().enumerate() { -// match inset { -// Some(virtualbits) => { -// let mut retset = _init_fd_set(); -// let (thisnfds,myunrealhashset) = _do_bitmods(globfdtable.get(&cageid).unwrap().clone().thisfdtable,nfds,virtualbits, &mut retset,&mut mappingtable)?; -// resultvec.push(retset); -// newnfds = cmp::max(thisnfds, newnfds); -// unrealarray[unrealoffset] = myunrealhashset; -// } -// None => { -// // This item is null. No unreal items -// // BUG: Need to actually return null! -// resultvec.push(_get_null_fd_set()); -// unrealarray[unrealoffset] = HashSet::new(); -// } -// } -// } + let mut unrealarray:[HashSet<(u64,u64)>;3] = [HashSet::new(),HashSet::new(),HashSet::new()]; + let mut mappingtable:HashMap = HashMap::new(); + let mut newnfds = 0; + + // putting results in a vec was the cleanest way I found to do this.. + let mut resultvec = Vec::new(); + + for (unrealoffset, inset) in [readbits,writebits, exceptbits].into_iter().enumerate() { + match inset { + Some(virtualbits) => { + let mut retset = _init_fd_set(); + let (thisnfds,myunrealhashset) = _do_bitmods(globfdtable.get(&cageid).unwrap().clone().thisfdtable,nfds,virtualbits, &mut retset,&mut mappingtable)?; + resultvec.push(Some(retset)); + newnfds = cmp::max(thisnfds, newnfds); + unrealarray[unrealoffset] = myunrealhashset; + } + None => { + // This item is null. No unreal items + resultvec.push(None); + unrealarray[unrealoffset] = HashSet::new(); + } + } + } -// Ok((newnfds, resultvec[0], resultvec[1], resultvec[2], unrealarray, mappingtable)) + Ok((newnfds, resultvec[0], resultvec[1], resultvec[2], unrealarray, mappingtable)) -// } +} // helper to call after calling select beneath you. returns the fd_sets you @@ -655,7 +671,7 @@ fn _do_bitmods(myfdmap:HashMap, nfds:u64, infdset: fd_set, thi #[allow(clippy::type_complexity)] #[allow(clippy::too_many_arguments)] // #[doc = include_str!("../docs/get_virtual_bitmasks_from_select_result.md")] -pub fn get_virtual_bitmasks_from_select_result(nfds:u64, readbits:fd_set, writebits:fd_set, exceptbits:fd_set,unrealreadset:HashSet, unrealwriteset:HashSet, unrealexceptset:HashSet, mappingtable:HashMap) -> Result<(u64, fd_set, fd_set, fd_set),threei::RetVal> { +pub fn get_virtual_bitmasks_from_select_result(nfds:u64, readbits:Option, writebits:Option, exceptbits:Option,unrealreadset:HashSet, unrealwriteset:HashSet, unrealexceptset:HashSet, mappingtable:&HashMap) -> Result<(u64, Option, Option, Option),threei::RetVal> { // Note, I don't need the cage_id here because I have the mappingtable... @@ -666,13 +682,21 @@ pub fn get_virtual_bitmasks_from_select_result(nfds:u64, readbits:fd_set, writeb let mut flagsset = 0; let mut retvec = Vec::new(); - for (inset,unrealset) in [(readbits,unrealreadset), (writebits,unrealwriteset), (exceptbits,unrealexceptset)] { + for (insetoption,unrealset) in [(readbits,unrealreadset), (writebits,unrealwriteset), (exceptbits,unrealexceptset)] { + // If I don't have any data, just return None (NULL) and skip... + if insetoption.is_none()&&unrealset.is_empty() { + retvec.push(None); + continue; + } + let mut retbits = _init_fd_set(); - for bit in 0..nfds as usize { - let pos = bit as u64; - if _fd_isset(pos,&inset)&& !_fd_isset(*mappingtable.get(&pos).unwrap(),&retbits) { - flagsset+=1; - _fd_set(*mappingtable.get(&pos).unwrap(),&mut retbits); + if let Some(inset) = insetoption { + for bit in 0..nfds as usize { + let pos = bit as u64; + if _fd_isset(pos,&inset)&& !_fd_isset(*mappingtable.get(&pos).unwrap(),&retbits) { + flagsset+=1; + _fd_set(*mappingtable.get(&pos).unwrap(),&mut retbits); + } } } for virtfd in unrealset { @@ -681,7 +705,7 @@ pub fn get_virtual_bitmasks_from_select_result(nfds:u64, readbits:fd_set, writeb _fd_set(virtfd,&mut retbits); } } - retvec.push(retbits); + retvec.push(Some(retbits)); } Ok((flagsset,retvec[0],retvec[1],retvec[2])) @@ -744,7 +768,7 @@ pub fn convert_virtualfds_to_real(cageid:u64, virtualfds:Vec) -> (Vec, // helper to call after calling poll. replaces the realfds the vector // with virtual ones... // #[doc = include_str!("../docs/convert_realfds_back_to_virtual.md")] -pub fn convert_realfds_back_to_virtual(realfds:Vec, mappingtable:HashMap) -> Vec { +pub fn convert_realfds_back_to_virtual(realfds:Vec, mappingtable:&HashMap) -> Vec { // I don't care what cage was used, and don't need to lock anything... // I have the mappingtable! @@ -759,6 +783,220 @@ pub fn convert_realfds_back_to_virtual(realfds:Vec, mappingtable:HashMap real mappings. The caller can decide whether to +// create an underlying epollfd when this is called, when epoll_ctl is called +// to add a realfd, etc. +// +// epoll_ctl is complex, but really has the same fundamental problem as +// epoll_create: the epollfd. +// +// What if I just ignore the epollfd problem by just making another table +// for epoll information? Then what I do is set the realfd to EPOLLFD and +// have optionalinfo point into the epollfd table. If I do this, then when +// epoll_create is called, if it contains realfds, those need to be passed +// down to the underlying epoll_create. Similarly, when epoll_ctl is called, +// we either modify our data or return the realfd... +// +// Interestingly, this actually would be just as easy to build on top of the +// fdtables library as into it. +// +// Each epollfd will have some virtual fds associated with it. Each of those +// will have an event mask. So I'll have a mutex around an EPollTable struct. +// This contains the next available entry and an epollhashmap. +// I use a hashmap here to better support removing and modifying items. + +// Note, I'm defining a bunch of symbols myself because libc doesn't import +// them on systems that don't support epoll and I want to be able to build +// the code anywhere. See commonconstants.rs for more info. + +// Design notes: I'm not adding realfds. I return them when you do a epoll_ctl +// operation that tries to add them. So, I only have unrealfds in my epoll +// structures. + +// TODO: I don't clean up this table yet. I probably should when the last +// reference to a fd is closed, but this bookkeeping seems excessive at this +// time... +#[derive(Clone, Debug)] +struct EPollTable { + highestneverusedentry: u64, // Never resets (even after close). Used to + // let us quickly get an unused entry + thisepolltable: HashMap>, // the epollentry -> + // virtfd -> + // event map + realfdtable: HashMap, // the epollentry -> realfd map. I need + // this because the realfd field in the + // main data structure is EPOLLFD +} + +lazy_static! { + + #[derive(Debug)] + static ref EPOLLTABLE: Mutex = { + let newetable = HashMap::new(); + let newrealfdtable = HashMap::new(); + let m = EPollTable { + highestneverusedentry:0, + thisepolltable:newetable, + realfdtable:newrealfdtable, + }; + Mutex::new(m) + }; +} + + +// #[doc = include_str!("../docs/epoll_create_helper.md")] +pub fn epoll_create_helper(cageid:u64, realfd:u64, should_cloexec:bool) -> Result { + + let mut ept = EPOLLTABLE.lock().unwrap(); + + // I'll use my other functions to make this easier. + // return the same errno (EMFile), if we get one + let newepollfd = get_unused_virtual_fd(cageid, EPOLLFD, should_cloexec, ept.highestneverusedentry)?; + + let newentry = ept.highestneverusedentry; + // add in my realfd. + ept.realfdtable.insert(newentry,realfd); + ept.highestneverusedentry+=1; + // if it errored out above that is okay. I haven't changed any state yet. + ept.thisepolltable.insert(newentry, HashMap::new()); + Ok(newepollfd) + +} + + + +// #[doc = include_str!("../docs/try_epoll_ctl.md")] +pub fn try_epoll_ctl(cageid:u64, epfd:u64, op:i32, virtfd:u64, event:epoll_event) -> Result<(u64,u64),threei::RetVal> { + + if !GLOBALFDTABLE.lock().unwrap().contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + if epfd == virtfd { + return Err(threei::Errno::EINVAL as u64); + } + + // Is the epfd ok? + let epollentrynum:u64 = match GLOBALFDTABLE.lock().unwrap().get(&cageid).unwrap().thisfdtable.get(&epfd) { + None => { + return Err(threei::Errno::EBADF as u64); + }, + // Do I need to have EPOLLFDs here too? + Some(tableentry) => { + if tableentry.realfd != EPOLLFD { + return Err(threei::Errno::EINVAL as u64); + } + tableentry.optionalinfo + }, + }; + + // Okay, I know which table entry and verified the virtfd... + + let mut epttable = EPOLLTABLE.lock().unwrap(); + let realepollfd = epttable.realfdtable.get(&epollentrynum).unwrap().clone(); + let eptentry = epttable.thisepolltable.get_mut(&epollentrynum).unwrap(); + + // check if the virtfd is real and error... + // I don't care about its contents except to ensure it isn't real... + match GLOBALFDTABLE.lock().unwrap().get(&cageid).unwrap().thisfdtable.get(&virtfd) { + // Do I need to have EPOLLFDs here too? + Some(tableentry) => { + if tableentry.realfd != NO_REAL_FD { + // Return realfds because the caller should handle them instead + // I only track unrealfds. + if tableentry.realfd == EPOLLFD { + // BUG: How should I be doing this, really!?! + println!("epollfds acting on epollfds is not supported!"); + } + return Ok((realepollfd,tableentry.realfd)); + } + }, + None => { + return Err(threei::Errno::EBADF as u64); + }, + }; + + // okay, virtfd is real... + + match op { + EPOLL_CTL_ADD => { + if eptentry.contains_key(&virtfd) { + return Err(threei::Errno::EEXIST as u64); + } + // BUG: Need to check for ELOOP here... + + eptentry.insert(virtfd, event); + }, + EPOLL_CTL_MOD => { + if !eptentry.contains_key(&virtfd) { + return Err(threei::Errno::ENOENT as u64); + } + eptentry.insert(virtfd, event); + }, + EPOLL_CTL_DEL => { + if !eptentry.contains_key(&virtfd) { + return Err(threei::Errno::ENOENT as u64); + } + eptentry.remove(&virtfd); + }, + _ => { + return Err(threei::Errno::EINVAL as u64); + }, + }; + Ok((realepollfd,NO_REAL_FD)) +} + + +// #[doc = include_str!("../docs/get_epoll_wait_data.md")] +pub fn get_epoll_wait_data(cageid:u64, epfd:u64) -> Result<(u64,HashMap),threei::RetVal> { + + if !GLOBALFDTABLE.lock().unwrap().contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + // Note that because I don't track realfds or deal with epollfds, I just + // return the epolltable... + let epollentrynum:u64 = match GLOBALFDTABLE.lock().unwrap().get(&cageid).unwrap().thisfdtable.get(&epfd) { + None => { + return Err(threei::Errno::EBADF as u64); + }, + // Do I need to have EPOLLFDs here too? + Some(tableentry) => { + if tableentry.realfd != EPOLLFD { + return Err(threei::Errno::EINVAL as u64); + } + tableentry.optionalinfo + }, + }; + + let epttable = EPOLLTABLE.lock().unwrap(); + Ok((*epttable.realfdtable.get(&epollentrynum).unwrap(),epttable.thisepolltable[&epollentrynum].clone())) +} + + + /********************** TESTING HELPER FUNCTION **********************/ @@ -768,7 +1006,7 @@ pub fn convert_realfds_back_to_virtual(realfds:Vec, mappingtable:HashMap { $(#[$settings])* + // #[doc(hidden)] $visibility enum $enumname { $($valuename = $value,)* } impl $enumname { + // #[doc(hidden)] $visibility fn from_discriminant(v: u64) -> Result { match v { $($value => Ok($enumname::$valuename),)* @@ -56,10 +74,15 @@ macro_rules! reversible_enum { } } +/// Return value for system calls... Can be errno +pub type RetVal = u64; + // BUG: ? I don't understand this setup... reversible_enum! { #[derive(Debug, PartialEq, Eq)] #[repr(u64)] + /// Errno values for OS calls + #[non_exhaustive] // I want to be able to update this later... pub enum Errno { EPERM = 1, // Operation not permitted ENOENT = 2, // No such file or directory @@ -191,6 +214,5 @@ reversible_enum! { EKEYREJECTED = 129, // Key was rejected by service for robust mutexes EOWNERDEAD = 130, // Owner died ENOTRECOVERABLE = 131, // State not recoverable - ELIND = 224, // Lind specific error } } diff --git a/src/example_grates/vanillaglobal.rs b/src/fdtables/vanillaglobal.rs similarity index 57% rename from src/example_grates/vanillaglobal.rs rename to src/fdtables/vanillaglobal.rs index baac065e..91e37920 100644 --- a/src/example_grates/vanillaglobal.rs +++ b/src/fdtables/vanillaglobal.rs @@ -1,8 +1,4 @@ - -use crate::safeposix::cage; -use crate::safeposix::syscalls::fs_calls::*; - -use super::threei; +use crate::threei; use lazy_static::lazy_static; @@ -18,19 +14,9 @@ use std::collections::HashMap; pub use super::commonconstants::*; // algorithm name. Need not be listed in the docs. -#[doc(hidden)] +// #[doc(hidden)] pub const ALGONAME: &str = "VanillaGlobal"; -// These are the values we look up with at the end... -// #[doc = include_str!("../docs/fdtableentry.md")] -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct FDTableEntry { - pub realfd: u64, // underlying fd (may be a virtual fd below us or - // a kernel fd) - pub should_cloexec: bool, // should I close this when exec is called? - pub optionalinfo: u64, // user specified / controlled data -} - // It's fairly easy to check the fd count on a per-process basis (I just check // when I would // add a new fd). @@ -58,28 +44,27 @@ pub struct FDTableEntry { // // This lets me initialize the code as a global. -// BUG / TODO: Use a DashMap instead of a Mutex for this? lazy_static! { - #[derive(Debug)] - static ref GLOBALFDTABLE: Mutex>> = { - let mut m = HashMap::new(); - // Insert a cage so that I have something to fork / test later, if need - // be. Otherwise, I'm not sure how I get this started. I think this - // should be invalid from a 3i standpoint, etc. Could this mask an - // error in the future? - m.insert(threei::TESTING_CAGEID,HashMap::new()); - Mutex::new(m) - }; + #[derive(Debug)] + static ref GLOBALFDTABLE: Mutex>> = { + let mut m = HashMap::new(); + // Insert a cage so that I have something to fork / test later, if need + // be. Otherwise, I'm not sure how I get this started. I think this + // should be invalid from a 3i standpoint, etc. Could this mask an + // error in the future? + m.insert(threei::TESTING_CAGEID,HashMap::new()); + Mutex::new(m) + }; } lazy_static! { - // This is needed for close and similar functionality. I need track the - // number of times a realfd is open - #[derive(Debug)] - static ref GLOBALREALFDCOUNT: Mutex> = { - Mutex::new(HashMap::new()) - }; + // This is needed for close and similar functionality. I need track the + // number of times a realfd is open + #[derive(Debug)] + static ref GLOBALREALFDCOUNT: Mutex> = { + Mutex::new(HashMap::new()) + }; } @@ -90,10 +75,6 @@ struct CloseHandlers { unreal_handler: fn(u64), } -// Seems sort of like a constant... I'm not sure if this is bad form or not... -#[allow(non_snake_case)] -pub fn NULL_FUNC(_:u64) { } - lazy_static! { // This holds the user registered handlers they want to have called when // a close occurs. I did this rather than return messy data structures @@ -221,19 +202,23 @@ pub fn get_specific_virtual_fd( // I moved this up so that if I decrement the same realfd, it calls // the intermediate handler instead of the final one. _increment_realfd(realfd); - if let Some(entry) = fdtable.get(&cageid).unwrap().get(&requested_virtualfd) { + + // always add the new entry. insert returns the old entry. + let myoptionentry = fdtable.get_mut(&cageid).unwrap().insert(requested_virtualfd,myentry); + drop(fdtable); + + // Close the old entry, if I need to... + if let Some(entry) = myoptionentry { if entry.realfd != NO_REAL_FD { - _decrement_realfd(entry.realfd); + _decrement_realfd(entry.realfd); } else { // Let their code know this has been closed... - let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); - (closehandlers.unreal_handler)(entry.optionalinfo); + let unrealclosehandler = (*CLOSEHANDLERTABLE.lock().unwrap()).unreal_handler; + (unrealclosehandler)(entry.optionalinfo); } } - // always add the new entry - fdtable.get_mut(&cageid).unwrap().insert(requested_virtualfd,myentry); Ok(()) } @@ -332,20 +317,21 @@ pub fn remove_cage_from_fdtable(cageid: u64) { panic!("Unknown cageid in fdtable access"); } + let cagetable = fdtable.remove(&cageid).unwrap(); + drop(fdtable); + // decrement the reference to items in the fdtable appropriately... - for v in fdtable.get(&cageid).unwrap().values() { + for v in cagetable.values() { if v.realfd != NO_REAL_FD { _decrement_realfd(v.realfd); } else { // Let their code know this has been closed... - let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); - (closehandlers.unreal_handler)(v.optionalinfo); + let unrealclosehandler = CLOSEHANDLERTABLE.lock().unwrap().unreal_handler; + (unrealclosehandler)(v.optionalinfo); } } - - fdtable.remove(&cageid).unwrap(); } // This removes all fds with the should_cloexec flag set. They are returned @@ -372,25 +358,41 @@ pub fn empty_fds_for_exec(cageid: u64) { let thiscagefdtable = fdtable.get_mut(&cageid).unwrap(); let mut without_cloexec_hm:HashMap = HashMap::new(); + // I bother to put this in a hashmap so I can call the closehandlers + // all after I have re-inserted everything. This ensures the state + // is consistent. I only need the values, not the keys... + let mut with_cloexec_vec:Vec = Vec::new(); + for (k,v) in thiscagefdtable.drain() { if v.should_cloexec { - if v.realfd == NO_REAL_FD { - // Let their code know this has been closed... - let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); - (closehandlers.unreal_handler)(v.optionalinfo); - } - else { - // Let the helper tell the user and decrement the count - _decrement_realfd(v.realfd); - } + with_cloexec_vec.push(v); } else{ without_cloexec_hm.insert(k,v); } } - // Put the ones without_cloexec back in the hashmap... + + // Put the ones without_cloexec back in the hashmap since they shouldn't + // be closed... fdtable.insert(cageid,without_cloexec_hm); + // Release the lock... + drop(fdtable); + + // Now call the close handlers on the others... + for v in with_cloexec_vec { + if v.realfd == NO_REAL_FD { + // Let their code know this has been closed... I get the handler + // repeatedly in case they change it during execution of another + // handler... + let closehandlerunreal = CLOSEHANDLERTABLE.lock().unwrap().unreal_handler; + (closehandlerunreal)(v.optionalinfo); + } + else { + // Let the helper tell the user and decrement the count + _decrement_realfd(v.realfd); + } + } } @@ -420,14 +422,17 @@ pub fn close_virtualfd(cageid:u64, virtfd:u64) -> Result<(),threei::RetVal> { panic!("Unknown cageid in fdtable access"); } - let thiscagesfdtable = fdtable.get_mut(&cageid).unwrap(); + // Remove this item from the table (and inspect it) + let thisoption = fdtable.get_mut(&cageid).unwrap().remove(&virtfd); + drop(fdtable); - match thiscagesfdtable.remove(&virtfd) { + match thisoption { Some(entry) => if entry.realfd == NO_REAL_FD { // Let their code know this has been closed... - let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); - (closehandlers.unreal_handler)(entry.optionalinfo); + let closehandlerunreal = CLOSEHANDLERTABLE.lock().unwrap().unreal_handler; + let optionalinfo = entry.optionalinfo; + (closehandlerunreal)(optionalinfo); Ok(()) } else { @@ -444,6 +449,8 @@ pub fn close_virtualfd(cageid:u64, virtfd:u64) -> Result<(),threei::RetVal> { // #[doc = include_str!("../docs/register_close_handlers.md")] pub fn register_close_handlers(intermediate_handler: fn(u64), final_handler: fn(u64), unreal_handler: fn(u64)) { // Unlock the table and set the handlers... + // TODO: I made a serious attempt to try to keep closehandlers that call + // close, etc. from deadlocking the system. More testing, etc. is needed. let mut closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); closehandlers.intermediate_handler = intermediate_handler; closehandlers.final_handler = final_handler; @@ -451,30 +458,42 @@ pub fn register_close_handlers(intermediate_handler: fn(u64), final_handler: fn( } // Helpers to track the count of times each realfd is used -#[doc(hidden)] +// #[doc(hidden)] fn _decrement_realfd(realfd:u64) -> u64 { // Do nothing if it's not a realfd... - if realfd == NO_REAL_FD { - panic!("Called _decrement_realfd with NO_REAL_FD"); - } + assert!(realfd != NO_REAL_FD, "Called _decrement_realfd with NO_REAL_FD"); // Get this table's lock... let mut realfdcount = GLOBALREALFDCOUNT.lock().unwrap(); let newcount:u64 = realfdcount.get(&realfd).unwrap() - 1; let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + let intermediateclosehandler = closehandlers.intermediate_handler; + let finalclosehandler = closehandlers.final_handler; + // release the lock... + drop(closehandlers); + if newcount > 0 { - (closehandlers.intermediate_handler)(realfd); realfdcount.insert(realfd,newcount); + // Need to drop locks to call the handlers or else will deadlock... + drop(realfdcount); + + (intermediateclosehandler)(realfd); } else { - (closehandlers.final_handler)(realfd); + // Remove before calling their close handler in case they do operations + // inside the close handler which create / close fds... + realfdcount.remove(&realfd); + // Need to drop locks to call the handlers or else will deadlock... + drop(realfdcount); + + (finalclosehandler)(realfd); } newcount } // Helpers to track the count of times each realfd is used -#[doc(hidden)] +// #[doc(hidden)] fn _increment_realfd(realfd:u64) -> u64 { if realfd == NO_REAL_FD { return 0 @@ -505,6 +524,7 @@ use std::mem; // Helper to get an empty fd_set. Helper function to isolate unsafe code, // etc. +// #[doc(hidden)] pub fn _init_fd_set() -> fd_set { let raw_fd_set:fd_set; unsafe { @@ -515,22 +535,18 @@ pub fn _init_fd_set() -> fd_set { raw_fd_set } -// Helper to get a null pointer. -pub fn _get_null_fd_set() -> fd_set { - //unsafe{ptr::null_mut()} - // BUG!!! Need to fix this later. - _init_fd_set() -} - +// #[doc(hidden)] pub fn _fd_set(fd:u64, thisfdset:&mut fd_set) { unsafe{libc::FD_SET(fd as i32,thisfdset)} } +// #[doc(hidden)] pub fn _fd_isset(fd:u64, thisfdset:&fd_set) -> bool { unsafe{libc::FD_ISSET(fd as i32,thisfdset)} } // Computes the bitmodifications and returns a (maxnfds, unrealset) tuple... +// #[doc(hidden)] fn _do_bitmods(myfdmap:HashMap, nfds:u64, infdset: fd_set, thisfdset: &mut fd_set, mappingtable: &mut HashMap) -> Result<(u64,HashSet<(u64,u64)>),threei::RetVal> { let mut unrealhashset:HashSet<(u64,u64)> = HashSet::new(); // Iterate through the infdset and set those values as is appropriate @@ -569,46 +585,45 @@ fn _do_bitmods(myfdmap:HashMap, nfds:u64, infdset: fd_set, thi #[allow(clippy::type_complexity)] #[allow(clippy::too_many_arguments)] // #[doc = include_str!("../docs/get_real_bitmasks_for_select.md")] -// pub fn get_real_bitmasks_for_select(cageid:u64, nfds:u64, readbits:Option, writebits:Option, exceptbits:Option) -> Result<(u64, fd_set, fd_set, fd_set, [HashSet<(u64,u64)>;3], HashMap),threei::RetVal> { - -// if nfds >= FD_PER_PROCESS_MAX { -// return Err(threei::Errno::EINVAL as u64); -// } - -// let globfdtable = GLOBALFDTABLE.lock().unwrap(); +pub fn get_real_bitmasks_for_select(cageid:u64, nfds:u64, readbits:Option, writebits:Option, exceptbits:Option) -> Result<(u64, Option, Option, Option, [HashSet<(u64,u64)>;3], HashMap),threei::RetVal> { -// if !globfdtable.contains_key(&cageid) { -// panic!("Unknown cageid in fdtable access"); -// } + if nfds >= FD_PER_PROCESS_MAX { + return Err(threei::Errno::EINVAL as u64); + } -// let mut unrealarray:[HashSet<(u64,u64)>;3] = [HashSet::new(),HashSet::new(),HashSet::new()]; -// let mut mappingtable:HashMap = HashMap::new(); -// let mut newnfds = 0; + let globfdtable = GLOBALFDTABLE.lock().unwrap(); -// // putting results in a vec was the cleanest way I found to do this.. -// let mut resultvec = Vec::new(); + if !globfdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } -// for (unrealoffset, inset) in [readbits,writebits, exceptbits].into_iter().enumerate() { -// match inset { -// Some(virtualbits) => { -// let mut retset = _init_fd_set(); -// let (thisnfds,myunrealhashset) = _do_bitmods(globfdtable.get(&cageid).unwrap().clone(),nfds,virtualbits, &mut retset,&mut mappingtable)?; -// resultvec.push(retset); -// newnfds = cmp::max(thisnfds, newnfds); -// unrealarray[unrealoffset] = myunrealhashset; -// } -// None => { -// // This item is null. No unreal items -// // BUG: Need to actually return null! -// resultvec.push(_get_null_fd_set()); -// unrealarray[unrealoffset] = HashSet::new(); -// } -// } -// } + let mut unrealarray:[HashSet<(u64,u64)>;3] = [HashSet::new(),HashSet::new(),HashSet::new()]; + let mut mappingtable:HashMap = HashMap::new(); + let mut newnfds = 0; + + // putting results in a vec was the cleanest way I found to do this.. + let mut resultvec = Vec::new(); + + for (unrealoffset, inset) in [readbits,writebits, exceptbits].into_iter().enumerate() { + match inset { + Some(virtualbits) => { + let mut retset = _init_fd_set(); + let (thisnfds,myunrealhashset) = _do_bitmods(globfdtable.get(&cageid).unwrap().clone(),nfds,virtualbits, &mut retset,&mut mappingtable)?; + resultvec.push(Some(retset)); + newnfds = cmp::max(thisnfds, newnfds); + unrealarray[unrealoffset] = myunrealhashset; + } + None => { + // This item is null. No unreal items + resultvec.push(None); + unrealarray[unrealoffset] = HashSet::new(); + } + } + } -// Ok((newnfds, resultvec[0], resultvec[1], resultvec[2], unrealarray, mappingtable)) + Ok((newnfds, resultvec[0], resultvec[1], resultvec[2], unrealarray, mappingtable)) -// } +} // helper to call after calling select beneath you. returns the fd_sets you @@ -619,7 +634,7 @@ fn _do_bitmods(myfdmap:HashMap, nfds:u64, infdset: fd_set, thi #[allow(clippy::type_complexity)] #[allow(clippy::too_many_arguments)] // #[doc = include_str!("../docs/get_virtual_bitmasks_from_select_result.md")] -pub fn get_virtual_bitmasks_from_select_result(nfds:u64, readbits:fd_set, writebits:fd_set, exceptbits:fd_set,unrealreadset:HashSet, unrealwriteset:HashSet, unrealexceptset:HashSet, mappingtable:HashMap) -> Result<(u64, fd_set, fd_set, fd_set),threei::RetVal> { +pub fn get_virtual_bitmasks_from_select_result(nfds:u64, readbits:Option, writebits:Option, exceptbits:Option,unrealreadset:HashSet, unrealwriteset:HashSet, unrealexceptset:HashSet, mappingtable:&HashMap) -> Result<(u64, Option, Option, Option),threei::RetVal> { // Note, I don't need the cage_id here because I have the mappingtable... @@ -630,13 +645,21 @@ pub fn get_virtual_bitmasks_from_select_result(nfds:u64, readbits:fd_set, writeb let mut flagsset = 0; let mut retvec = Vec::new(); - for (inset,unrealset) in [(readbits,unrealreadset), (writebits,unrealwriteset), (exceptbits,unrealexceptset)] { + for (insetoption,unrealset) in [(readbits,unrealreadset), (writebits,unrealwriteset), (exceptbits,unrealexceptset)] { + // If I don't have any data, just return None (NULL) and skip... + if insetoption.is_none()&&unrealset.is_empty() { + retvec.push(None); + continue; + } + let mut retbits = _init_fd_set(); - for bit in 0..nfds as usize { - let pos = bit as u64; - if _fd_isset(pos,&inset)&& !_fd_isset(*mappingtable.get(&pos).unwrap(),&retbits) { - flagsset+=1; - _fd_set(*mappingtable.get(&pos).unwrap(),&mut retbits); + if let Some(inset) = insetoption { + for bit in 0..nfds as usize { + let pos = bit as u64; + if _fd_isset(pos,&inset)&& !_fd_isset(*mappingtable.get(&pos).unwrap(),&retbits) { + flagsset+=1; + _fd_set(*mappingtable.get(&pos).unwrap(),&mut retbits); + } } } for virtfd in unrealset { @@ -645,7 +668,7 @@ pub fn get_virtual_bitmasks_from_select_result(nfds:u64, readbits:fd_set, writeb _fd_set(virtfd,&mut retbits); } } - retvec.push(retbits); + retvec.push(Some(retbits)); } Ok((flagsset,retvec[0],retvec[1],retvec[2])) @@ -708,7 +731,7 @@ pub fn convert_virtualfds_to_real(cageid:u64, virtualfds:Vec) -> (Vec, // helper to call after calling poll. replaces the realfds the vector // with virtual ones... // #[doc = include_str!("../docs/convert_realfds_back_to_virtual.md")] -pub fn convert_realfds_back_to_virtual(realfds:Vec, mappingtable:HashMap) -> Vec { +pub fn convert_realfds_back_to_virtual(realfds:Vec, mappingtable:&HashMap) -> Vec { // I don't care what cage was used, and don't need to lock anything... // I have the mappingtable! @@ -722,6 +745,216 @@ pub fn convert_realfds_back_to_virtual(realfds:Vec, mappingtable:HashMap real mappings. The caller can decide whether to +// create an underlying epollfd when this is called, when epoll_ctl is called +// to add a realfd, etc. +// +// epoll_ctl is complex, but really has the same fundamental problem as +// epoll_create: the epollfd. +// +// What if I just ignore the epollfd problem by just making another table +// for epoll information? Then what I do is set the realfd to EPOLLFD and +// have optionalinfo point into the epollfd table. If I do this, then when +// epoll_create is called, if it contains realfds, those need to be passed +// down to the underlying epoll_create. Similarly, when epoll_ctl is called, +// we either modify our data or return the realfd... +// +// Interestingly, this actually would be just as easy to build on top of the +// fdtables library as into it. +// +// Each epollfd will have some virtual fds associated with it. Each of those +// will have an event mask. So I'll have a mutex around an EPollTable struct. +// This contains the next available entry and an epollhashmap. +// I use a hashmap here to better support removing and modifying items. + +// Note, I'm defining a bunch of symbols myself because libc doesn't import +// them on systems that don't support epoll and I want to be able to build +// the code anywhere. See commonconstants.rs for more info. + +// Design notes: I'm not adding realfds. I return them when you do a epoll_ctl +// operation that tries to add them. So, I only have unrealfds in my epoll +// structures. + +// TODO: I don't clean up this table yet. I probably should when the last +// reference to a fd is closed, but this bookkeeping seems excessive at this +// time... +#[derive(Clone, Debug)] +struct EPollTable { + highestneverusedentry: u64, // Never resets (even after close). Used to + // let us quickly get an unused entry + thisepolltable: HashMap>, // the epollentry -> + // virtfd -> + // event map + realfdtable: HashMap, // the epollentry -> realfd map. I need + // this because the realfd field in the + // main data structure is EPOLLFD +} + +lazy_static! { + + #[derive(Debug)] + static ref EPOLLTABLE: Mutex = { + let newetable = HashMap::new(); + let newrealfdtable = HashMap::new(); + let m = EPollTable { + highestneverusedentry:0, + realfdtable:newrealfdtable, + thisepolltable:newetable, + }; + Mutex::new(m) + }; +} + + +// #[doc = include_str!("../docs/epoll_create_helper.md")] +pub fn epoll_create_helper(cageid:u64, realfd:u64, should_cloexec:bool) -> Result { + + let mut ept = EPOLLTABLE.lock().unwrap(); + + // I'll use my other functions to make this easier. + // return the same errno (EMFile), if we get one + let newepollfd = get_unused_virtual_fd(cageid, EPOLLFD, should_cloexec, ept.highestneverusedentry)?; + + let newentry = ept.highestneverusedentry; + ept.highestneverusedentry+=1; + // add in my realfd. + ept.realfdtable.insert(newentry,realfd); + // if it errored out above that is okay. I haven't changed any state yet. + ept.thisepolltable.insert(newentry, HashMap::new()); + Ok(newepollfd) + +} + + + +// #[doc = include_str!("../docs/try_epoll_ctl.md")] +pub fn try_epoll_ctl(cageid:u64, epfd:u64, op:i32, virtfd:u64, event:epoll_event) -> Result<(u64,u64),threei::RetVal> { + + if !GLOBALFDTABLE.lock().unwrap().contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + if epfd == virtfd { + return Err(threei::Errno::EINVAL as u64); + } + // Is the epfd ok? + let epollentrynum:u64 = match GLOBALFDTABLE.lock().unwrap().get(&cageid).unwrap().get(&epfd) { + None => { + return Err(threei::Errno::EBADF as u64); + }, + // Do I need to have EPOLLFDs here too? + Some(tableentry) => { + if tableentry.realfd != EPOLLFD { + return Err(threei::Errno::EINVAL as u64); + } + tableentry.optionalinfo + }, + }; + + // Okay, I know which table entry and verified the virtfd... + + let mut epttable = EPOLLTABLE.lock().unwrap(); + let realepollfd = epttable.realfdtable.get(&epollentrynum).unwrap().clone(); + let eptentry = epttable.thisepolltable.get_mut(&epollentrynum).unwrap(); + + // check if the virtfd is real and error... + // I don't care about its contents except to ensure it isn't real... + match GLOBALFDTABLE.lock().unwrap().get(&cageid).unwrap().get(&virtfd) { + // Do I need to have EPOLLFDs here too? + Some(tableentry) => { + if tableentry.realfd != NO_REAL_FD { + // Return realfds because the caller should handle them instead + // I only track unrealfds. + if tableentry.realfd == EPOLLFD { + // BUG: How should I be doing this, really!?! + println!("epollfds acting on epollfds is not supported!"); + } + return Ok((realepollfd,tableentry.realfd)); + } + }, + None => { + return Err(threei::Errno::EBADF as u64); + }, + }; + + // okay, virtfd is real... + + match op { + EPOLL_CTL_ADD => { + if eptentry.contains_key(&virtfd) { + return Err(threei::Errno::EEXIST as u64); + } + // BUG: Need to check for ELOOP here... + + eptentry.insert(virtfd, event); + }, + EPOLL_CTL_MOD => { + if !eptentry.contains_key(&virtfd) { + return Err(threei::Errno::ENOENT as u64); + } + eptentry.insert(virtfd, event); + }, + EPOLL_CTL_DEL => { + if !eptentry.contains_key(&virtfd) { + return Err(threei::Errno::ENOENT as u64); + } + eptentry.remove(&virtfd); + }, + _ => { + return Err(threei::Errno::EINVAL as u64); + }, + }; + Ok((realepollfd,NO_REAL_FD)) +} + + +// #[doc = include_str!("../docs/get_epoll_wait_data.md")] +pub fn get_epoll_wait_data(cageid:u64, epfd:u64) -> Result<(u64,HashMap),threei::RetVal> { + + if !GLOBALFDTABLE.lock().unwrap().contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + // Note that because I don't track realfds or deal with epollfds, I just + // return the epolltable... + let epollentrynum:u64 = match GLOBALFDTABLE.lock().unwrap().get(&cageid).unwrap().get(&epfd) { + None => { + return Err(threei::Errno::EBADF as u64); + }, + // Do I need to have EPOLLFDs here too? + Some(tableentry) => { + if tableentry.realfd != EPOLLFD { + return Err(threei::Errno::EINVAL as u64); + } + tableentry.optionalinfo + }, + }; + + let epttable = EPOLLTABLE.lock().unwrap(); + Ok((*epttable.realfdtable.get(&epollentrynum).unwrap(),epttable.thisepolltable[&epollentrynum].clone())) +} /********************** TESTING HELPER FUNCTION **********************/ @@ -732,23 +965,22 @@ pub fn convert_realfds_back_to_virtual(realfds:Vec, mappingtable:HashMap { @@ -393,12 +387,6 @@ pub extern "C" fn dispatcher( interface::get_int(arg1), Ok::<&interface::GenSockaddr, i32>(&addr) ) - // check_and_dispatch!( - // cage.bind_syscall, - // interface::get_int(arg1), - // interface::get_constsockaddr(arg2), - // interface::get_uint(arg3) - // ) } SEND_SYSCALL => { check_and_dispatch!( @@ -420,15 +408,6 @@ pub extern "C" fn dispatcher( interface::get_int(arg4), Ok::<&interface::GenSockaddr, i32>(&addr) ) - // check_and_dispatch!( - // cage.sendto_syscall, - // interface::get_int(arg1), - // interface::get_cbuf(arg2), - // interface::get_usize(arg3), - // interface::get_int(arg4), - // interface::get_constsockaddr(arg5), - // interface::get_uint(arg6) - // ) } RECV_SYSCALL => { check_and_dispatch!( @@ -477,15 +456,6 @@ pub extern "C" fn dispatcher( "exactly one of the last two arguments was zero", ) } - // check_and_dispatch!( - // cage.recvfrom_syscall, - // interface::get_int(arg1), - // interface::get_mutcbuf(arg2), - // interface::get_usize(arg3), - // interface::get_int(arg4), - // interface::get_sockaddr(arg5), - // interface::get_uint(arg6) - // ) } CONNECT_SYSCALL => { let addrlen = get_onearg!(interface::get_uint(arg3)); @@ -495,12 +465,6 @@ pub extern "C" fn dispatcher( interface::get_int(arg1), Ok::<&interface::GenSockaddr, i32>(&addr) ) - // check_and_dispatch!( - // cage.connect_syscall, - // interface::get_int(arg1), - // interface::get_constsockaddr(arg2), - // interface::get_uint(arg3) - // ) } LISTEN_SYSCALL => { check_and_dispatch!( @@ -510,41 +474,6 @@ pub extern "C" fn dispatcher( ) } ACCEPT_SYSCALL => { - /* We don't care the type of addr - * We will directly copy all stuffs in copy_out_sockaddr - */ - // let mut addr = interface::GenSockaddr::V4(interface::SockaddrV4::default()); - - // let nullity1 = interface::arg_nullity(&arg2); - // let nullity2 = interface::arg_nullity(&arg3); - - // if nullity1 && nullity2 { - // check_and_dispatch!( - // cage.accept_syscall, - // interface::get_int(arg1), - // Ok::<&mut Option<&mut interface::GenSockaddr>, i32>(&mut Some( - // &mut addr - // )) - // ) - // } else if !(nullity1 || nullity2) { - // let rv = check_and_dispatch!( - // cage.accept_syscall, - // interface::get_int(arg1), - // Ok::<&mut Option<&mut interface::GenSockaddr>, i32>(&mut Some( - // &mut addr - // )) - // ); - // if rv >= 0 { - // interface::copy_out_sockaddr(arg2, arg3, addr); - // } - // rv - // } else { - // syscall_error( - // Errno::EINVAL, - // "accept", - // "exactly one of the last two arguments was zero", - // ) - // } let nullity1 = interface::arg_nullity(&arg2); let nullity2 = interface::arg_nullity(&arg3); @@ -577,7 +506,6 @@ pub extern "C" fn dispatcher( } } GETPEERNAME_SYSCALL => { - // let mut addr = interface::GenSockaddr::V4(interface::SockaddrV4::default()); //value doesn't matter if interface::arg_nullity(&arg2) || interface::arg_nullity(&arg3) { return syscall_error( Errno::EINVAL, @@ -589,7 +517,6 @@ pub extern "C" fn dispatcher( let rv = check_and_dispatch!( cage.getpeername_syscall, interface::get_int(arg1), - // Ok::<&mut interface::GenSockaddr, i32>(&mut addr) Ok::<&mut Option<&mut interface::GenSockaddr>, i32>(&mut Some( &mut addr )) @@ -599,19 +526,11 @@ pub extern "C" fn dispatcher( interface::copy_out_sockaddr(arg2, arg3, addr); } rv - // check_and_dispatch!( - // cage.getpeername_syscall, - // interface::get_int(arg1), - // interface::get_sockaddr(arg2) - // ) } GETSOCKNAME_SYSCALL => { - // let mut addr = interface::GenSockaddr::V4(interface::SockaddrV4::default()); //value doesn't matter let mut addr = interface::set_gensockaddr(arg2, arg3).unwrap(); let len = interface::get_socklen_t_ptr(arg3).unwrap(); - // println!("[Dispatcher getsockname] socklen: {:?}", len); - // io::stdout().flush().unwrap(); if interface::arg_nullity(&arg2) || interface::arg_nullity(&arg3) { return syscall_error( @@ -623,7 +542,6 @@ pub extern "C" fn dispatcher( let rv = check_and_dispatch!( cage.getsockname_syscall, interface::get_int(arg1), - // Ok::<&mut interface::GenSockaddr, i32>(&mut addr) Ok::<&mut Option<&mut interface::GenSockaddr>, i32>(&mut Some( &mut addr )) @@ -633,12 +551,6 @@ pub extern "C" fn dispatcher( interface::copy_out_sockaddr(arg2, arg3, addr); } rv - // check_and_dispatch!( - // cage.getsockname_syscall, - // interface::get_int(arg1), - // interface::get_sockaddr(arg2), - // interface::get_uint(arg3) - // ) } GETIFADDRS_SYSCALL => { check_and_dispatch!( @@ -673,34 +585,8 @@ pub extern "C" fn dispatcher( //we take it as a given that the length is 4 both in and out rv - // let mut optval: Vec = vec![0; 4 as usize]; - // check_and_dispatch!( - // cage.getsockopt_syscall, - // interface::get_int(arg1), - // interface::get_int(arg2), - // interface::get_int(arg3), - // // interface::get_mutcbuf(arg4) - // Ok::<&mut Vec, i32>(&mut optval) - // // interface::get_uint(arg5) - // ) } SETSOCKOPT_SYSCALL => { - // let sockval; - // if !interface::arg_nullity(&arg4) { - // if get_onearg!(interface::get_uint(arg5)) != 4 { - // return syscall_error(Errno::EINVAL, "setsockopt", "Invalid optlen passed"); - // } - // sockval = interface::get_int_from_intptr(arg4); - // } else { - // sockval = 0; - // } - // check_and_dispatch!( - // cage.setsockopt_syscall, - // interface::get_int(arg1), - // interface::get_int(arg2), - // interface::get_int(arg3), - // Ok::(sockval) - // ) check_and_dispatch!( cage.setsockopt_syscall, interface::get_int(arg1), @@ -743,20 +629,10 @@ pub extern "C" fn dispatcher( cage.poll_syscall, interface::get_pollstruct_slice(arg1, nfds), interface::get_ulong(arg2), - // interface::get_duration_from_millis(arg3) interface::get_int(arg3) ) } SOCKETPAIR_SYSCALL => { - /* [TODO] - WHY */ - // check_and_dispatch_socketpair!( - // Cage::socketpair_syscall, - // cage, - // interface::get_int(arg1), - // interface::get_int(arg2), - // interface::get_int(arg3), - // interface::get_sockpair(arg4) - // ) check_and_dispatch!( cage.socketpair_syscall, interface::get_int(arg1), @@ -858,14 +734,6 @@ pub extern "C" fn dispatcher( "The number of fds passed was invalid", ); } - - // check_and_dispatch!( - // cage.epoll_wait_syscall, - // interface::get_int(arg1), - // interface::get_epollevent_slice(arg2, nfds), - // Ok::(nfds), - // interface::get_duration_from_millis(arg4) - // ) check_and_dispatch!( cage.epoll_wait_syscall, interface::get_int(arg1), @@ -1114,27 +982,6 @@ pub extern "C" fn lindthreadremove(cageid: u64, pthreadid: u64) { cage.thread_table.remove(&pthreadid); } -// fn cleartmp(init: bool) { -// let path = "/tmp"; - -// let cage = interface::cagetable_getref(0); -// let mut statdata = StatData::default(); - -// if cage.stat_syscall(path, &mut statdata) == 0 { -// visit_children(&cage, path, None, |childcage, childpath, isdir, _| { -// if isdir { -// lind_deltree(childcage, childpath); -// } else { -// childcage.unlink_syscall(childpath); -// } -// }); -// } else { -// if init == true { -// cage.mkdir_syscall(path, S_IRWXA); -// } -// } -// } - #[no_mangle] pub extern "C" fn lindgetsighandler(cageid: u64, signo: i32) -> u32 { let cage = interface::cagetable_getref(cageid); @@ -1164,19 +1011,14 @@ pub extern "C" fn lindgetsighandler(cageid: u64, signo: i32) -> u32 { pub extern "C" fn lindrustinit(verbosity: isize) { let _ = interface::VERBOSE.set(verbosity); //assigned to suppress unused result warning interface::cagetable_init(); - // load_fs(); - // incref_root(); - // incref_root(); - // fs::chroot("/home/lind/lind_project/src/safeposix-rust/tmp/").unwrap(); - // std::env::set_current_dir("/").unwrap(); - register_close_handlers(NULL_FUNC, kernel_close, NULL_FUNC); + // TODO: needs to add close() that handling im-pipe + fdtables::register_close_handlers(FDKIND_KERNEL, fdtables::NULL_FUNC, kernel_close); let utilcage = Cage { cageid: 0, cwd: interface::RustLock::new(interface::RustRfc::new(interface::RustPathBuf::from("/"))), parent: 0, - // filedescriptortable: init_fdtable(), cancelstatus: interface::RustAtomicBool::new(false), getgid: interface::RustAtomicI32::new(-1), getuid: interface::RustAtomicI32::new(-1), @@ -1195,15 +1037,21 @@ pub extern "C" fn lindrustinit(verbosity: isize) { }; interface::cagetable_insert(0, utilcage); - // let mut fdtable = FDTABLE; - init_empty_cage(0); + fdtables::init_empty_cage(0); // Set the first 3 fd to STDIN / STDOUT / STDERR // STDIN - get_specific_virtual_fd(0, 0, 0, false, 0).unwrap(); + let dev_null = CString::new("/home/lind/lind_project/src/safeposix-rust/tmp/dev/null").unwrap(); + unsafe { + libc::open(dev_null.as_ptr(), libc::O_RDONLY); + libc::open(dev_null.as_ptr(), libc::O_WRONLY); + libc::dup(1); + } + + fdtables::get_specific_virtual_fd(0, 0, FDKIND_KERNEL, 0, false, 0).unwrap(); // STDOUT - get_specific_virtual_fd(0, 1, 1, false, 0).unwrap(); + fdtables::get_specific_virtual_fd(0, 1, FDKIND_KERNEL, 1, false, 0).unwrap(); // STDERR - get_specific_virtual_fd(0, 2, 2, false, 0).unwrap(); + fdtables::get_specific_virtual_fd(0, 2, FDKIND_KERNEL, 2, false, 0).unwrap(); //init cage is its own parent let initcage = Cage { @@ -1228,35 +1076,18 @@ pub extern "C" fn lindrustinit(verbosity: isize) { interval_timer: interface::IntervalTimer::new(1), }; interface::cagetable_insert(1, initcage); - init_empty_cage(1); + fdtables::init_empty_cage(1); // Set the first 3 fd to STDIN / STDOUT / STDERR // STDIN - get_specific_virtual_fd(1, 0, 0, false, 0).unwrap(); + fdtables::get_specific_virtual_fd(1, 0, FDKIND_KERNEL, 0, false, 0).unwrap(); // STDOUT - get_specific_virtual_fd(1, 1, 1, false, 0).unwrap(); + fdtables::get_specific_virtual_fd(1, 1, FDKIND_KERNEL, 1, false, 0).unwrap(); // STDERR - get_specific_virtual_fd(1, 2, 2, false, 0).unwrap(); - // make sure /tmp is clean - // cleartmp(true); + fdtables::get_specific_virtual_fd(1, 2, FDKIND_KERNEL, 2, false, 0).unwrap(); + } #[no_mangle] pub extern "C" fn lindrustfinalize() { - // remove any open domain socket inodes - // for truepath in NET_METADATA.get_domainsock_paths() { - // remove_domain_sock(truepath); - // } - - // clear /tmp folder - // cleartmp(false); interface::cagetable_clear(); - // if we get here, persist and delete log - // persist_metadata(&FS_METADATA); - // if interface::pathexists(LOGFILENAME.to_string()) { - // // remove file if it exists, assigning it to nothing to avoid the compiler yelling about unused result - // let mut logobj = LOGMAP.write(); - // let log = logobj.take().unwrap(); - // let _close = log.close().unwrap(); - // let _logremove = interface::removefile(LOGFILENAME.to_string()); - // } } diff --git a/src/safeposix/syscalls/fs_calls.rs b/src/safeposix/syscalls/fs_calls.rs index 7defb615..541ec76d 100644 --- a/src/safeposix/syscalls/fs_calls.rs +++ b/src/safeposix/syscalls/fs_calls.rs @@ -4,7 +4,7 @@ use std::fs; use super::fs_constants; // File system related system calls use super::fs_constants::*; -use super::sys_constants::*; +use super::sys_constants; use crate::interface; use crate::interface::get_errno; use crate::interface::handle_errno; @@ -28,22 +28,12 @@ use std::ffi::CString; use std::ptr; use std::mem; -// use crate::example_grates::vanillaglobal::*; -use crate::example_grates::dashmapvecglobal::*; -// use crate::example_grates::muthashmaxglobal::*; -// use crate::example_grates::dashmaparrayglobal::*; +use crate::fdtables; static LIND_ROOT: &str = "/home/lind/lind_project/src/safeposix-rust/tmp"; -/* -* We will receive parameters with type u64 by default, then we will do type conversion inside -* of each syscall -* -* [Concerns] -* - cloexec in get_unused_virtual_fd()..? -* - there's no getdents() API in rust libc -* -*/ +const FDKIND_KERNEL: u32 = 0; +const FDKIND_IMPIPE: u32 = 1; impl Cage { //------------------------------------OPEN SYSCALL------------------------------------ @@ -55,7 +45,6 @@ impl Cage { pub fn open_syscall(&self, path: &str, oflag: i32, mode: u32) -> i32 { // Convert data type from &str into *const i8 - // let c_path = CString::new(path).unwrap(); let relpath = normpath(convpath(path), self); let relative_path = relpath.to_str().unwrap(); let full_path = format!("{}{}", LIND_ROOT, relative_path); @@ -65,49 +54,16 @@ impl Cage { if kernel_fd < 0 { - // let err = unsafe { - // *libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[open] Error message: {:?}", err_msg); - // println!("[open] c_path: {:?}", c_path); - // println!("[open] oflag: {:?}", oflag); - // println!("[open] mode: {:?}", mode); - // println!("[open] kernel fd: {:?}", kernel_fd); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "open"); } let should_cloexec = (oflag & O_CLOEXEC) != 0; - let virtual_fd = get_unused_virtual_fd(self.cageid, kernel_fd as u64, should_cloexec, 0).unwrap(); - // let mut count = 0; - // FDTABLE.iter().for_each(|entry| { - // // println!("Cage ID: {}", entry.key()); - // for (index, fd_entry) in entry.value().iter().enumerate() { - // if let Some(entry) = fd_entry { - // // println!(" Index {}: {:?}", index, entry); - // count = count+1; - // } - // } - // }); - // println!("[OPEN] cageid: {:?}", self.cageid); - // println!("[OPEN] vfd: {:?}", virtual_fd); - // println!("[OPEN] realfd: {:?}", kernel_fd); - // if kernel_fd > 1000 { - // let entries = fs::read_dir("/proc/self/fd")?; - // let count = entries.count(); - // println!("!!!![OPEN]!!!! TOTAL FD: {:?}", count); - // } - // // println!("[OPEN] Total: {:?}", count); - // io::stdout().flush().unwrap(); - virtual_fd as i32 + match fdtables::get_unused_virtual_fd(self.cageid, FDKIND_KERNEL, kernel_fd as u64, should_cloexec, 0) { + Ok(virtual_fd) => return virtual_fd as i32, + Err(_) => return syscall_error(Errno::EMFILE, "open", "Too many files opened") + } } //------------------MKDIR SYSCALL------------------ @@ -116,7 +72,6 @@ impl Cage { */ pub fn mkdir_syscall(&self, path: &str, mode: u32) -> i32 { // Convert data type from &str into *const i8 - // let c_path = CString::new(path).expect("CString::new failed"); let relpath = normpath(convpath(path), self); let relative_path = relpath.to_str().unwrap(); let full_path = format!("{}{}", LIND_ROOT, relative_path); @@ -126,18 +81,6 @@ impl Cage { libc::mkdir(c_path.as_ptr(), mode) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[mkdir] Error message: {:?}", err_msg); - // println!("[mkdir] c_path: {:?}", c_path); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "mkdir"); } @@ -150,7 +93,6 @@ impl Cage { */ pub fn mknod_syscall(&self, path: &str, mode: u32, dev: u64) -> i32 { // Convert data type from &str into *const i8 - // let c_path = CString::new(path).expect("CString::new failed"); let relpath = normpath(convpath(path), self); let relative_path = relpath.to_str().unwrap(); let full_path = format!("{}{}", LIND_ROOT, relative_path); @@ -159,18 +101,6 @@ impl Cage { libc::mknod(c_path.as_ptr(), mode, dev) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[mknod] Error message: {:?}", err_msg); - // println!("[mknod] c_path: {:?}", c_path); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "mknod"); } @@ -197,18 +127,6 @@ impl Cage { libc::link(old_cpath.as_ptr(), new_cpath.as_ptr()) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[link] Error message: {:?}", err_msg); - // println!("[link] c_path: {:?}", old_cpath); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "link"); } @@ -231,21 +149,6 @@ impl Cage { }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // let errno = unsafe { - // *libc::__errno_location() - // } as i32; - // println!("[unlink] Error message: {:?}", err_msg); - // println!("[unlink] c_path: {:?}", c_path); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "unlink"); } @@ -267,23 +170,11 @@ impl Cage { libc::creat(c_path.as_ptr(), mode) }; if kernel_fd < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[creat] Error message: {:?}", err_msg); - // println!("[creat] c_path: {:?}", c_path); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "creat"); } - let virtual_fd = get_unused_virtual_fd(self.cageid, kernel_fd as u64, false, 0).unwrap(); + let virtual_fd = fdtables::get_unused_virtual_fd(self.cageid, FDKIND_KERNEL, kernel_fd as u64, false, 0).unwrap(); virtual_fd as i32 } @@ -291,16 +182,6 @@ impl Cage { /* * stat() will return 0 when success and -1 when fail */ - // pub fn stat_syscall(&self, path: &str, statbuf: &mut stat) -> i32 { - // // let c_path = CString::new(path).expect("CString::new failed"); - // let relpath = normpath(convpath(path), self); - // let relative_path = relpath.to_str().unwrap(); - // let full_path = format!("{}{}", LIND_ROOT, relative_path); - // let c_path = CString::new(full_path).unwrap(); - // unsafe { - // libc::stat(c_path.as_ptr(), statbuf) - // } - // } pub fn stat_syscall(&self, path: &str, rposix_statbuf: &mut StatData) -> i32 { let relpath = normpath(convpath(path), self); let relative_path = relpath.to_str().unwrap(); @@ -314,21 +195,6 @@ impl Cage { }; if libcret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // let errno = unsafe { - // *libc::__errno_location() - // } as i32; - // println!("[stat] Error message: {:?}", err_msg); - // println!("[stat] c_path: {:?}", c_path); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "stat"); } @@ -352,22 +218,17 @@ impl Cage { * Get the kernel fd with provided virtual fd first * fstat() will return 0 when success and -1 when fail */ - // pub fn fstat_syscall(&self, virtual_fd: i32, statbuf: &mut stat) -> i32 { - // let kernel_fd = translate_virtual_fd(self.cageid, virtual_fd).unwrap(); - // unsafe { - // libc::fstat(kernel_fd, statbuf) - // } - // } pub fn fstat_syscall(&self, virtual_fd: i32, rposix_statbuf: &mut StatData) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "fstat", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); + // Declare statbuf by ourselves let mut libc_statbuf: stat = unsafe { std::mem::zeroed() }; let libcret = unsafe { - libc::fstat(kernel_fd as i32, &mut libc_statbuf) + libc::fstat(vfd.underfd as i32, &mut libc_statbuf) }; if libcret < 0 { @@ -432,14 +293,14 @@ impl Cage { * fstatfs() will return 0 when success and -1 when fail */ pub fn fstatfs_syscall(&self, virtual_fd: i32, rposix_databuf: &mut FSData) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "fstatfs", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); let mut libc_databuf: statfs = unsafe { mem::zeroed() }; let libcret = unsafe { - libc::fstatfs(kernel_fd as i32, &mut libc_databuf) + libc::fstatfs(vfd.underfd as i32, &mut libc_databuf) }; if libcret < 0 { @@ -471,33 +332,21 @@ impl Cage { * - -1, fail */ pub fn read_syscall(&self, virtual_fd: i32, readbuf: *mut u8, count: usize) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "read", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + + let vfd = wrappedvfd.unwrap(); + //kernel fd let ret = unsafe { - libc::read(kernel_fd as i32, readbuf as *mut c_void, count) as i32 + libc::read(vfd.underfd as i32, readbuf as *mut c_void, count) as i32 }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[READ] Error message: {:?}", err_msg); - // println!("kernel_fd: {:?}", kernel_fd); - // println!("vfd: {:?}", virtual_fd); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "read"); } return ret; - } //------------------------------------PREAD SYSCALL------------------------------------ @@ -508,27 +357,15 @@ impl Cage { * - -1, fail */ pub fn pread_syscall(&self, virtual_fd: i32, buf: *mut u8, count: usize, offset: i64) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "pread", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); let ret = unsafe { - libc::pread(kernel_fd as i32, buf as *mut c_void, count, offset) as i32 + libc::pread(vfd.underfd as i32, buf as *mut c_void, count, offset) as i32 }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[pread] Error message: {:?}", err_msg); - // println!("[pread] virtual fd: {:?}", virtual_fd); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "pread"); } @@ -544,32 +381,20 @@ impl Cage { * - -1, fail */ pub fn write_syscall(&self, virtual_fd: i32, buf: *const u8, count: usize) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "write", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + + let vfd = wrappedvfd.unwrap(); let ret = unsafe { - libc::write(kernel_fd as i32, buf as *const c_void, count) as i32 + libc::write(vfd.underfd as i32, buf as *const c_void, count) as i32 }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[write] Error message: {:?}", err_msg); - // println!("[write] virtual fd: {:?}", virtual_fd); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "write"); } return ret; - } //------------------------------------PWRITE SYSCALL------------------------------------ @@ -580,27 +405,15 @@ impl Cage { * - -1, fail */ pub fn pwrite_syscall(&self, virtual_fd: i32, buf: *const u8, count: usize, offset: i64) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "pwrite", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); let ret = unsafe { - libc::pwrite(kernel_fd as i32, buf as *const c_void, count, offset) as i32 + libc::pwrite(vfd.underfd as i32, buf as *const c_void, count, offset) as i32 }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[pwrite] Error message: {:?}", err_msg); - // println!("[pwrite] virtual fd: {:?}", virtual_fd); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "pwrite"); } @@ -616,31 +429,20 @@ impl Cage { iovec: *const interface::IovecStruct, iovcnt: i32, ) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { - return syscall_error(Errno::EBADF, "writev", "Bad File Descriptor"); + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { + return syscall_error(Errno::EBADF, "write", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + + let vfd = wrappedvfd.unwrap(); let ret = unsafe { - libc::writev(kernel_fd as i32, iovec, iovcnt) + libc::writev(vfd.underfd as i32, iovec, iovcnt) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[writev] Error message: {:?}", err_msg); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "writev"); } return ret as i32; - } //------------------------------------LSEEK SYSCALL------------------------------------ @@ -651,27 +453,15 @@ impl Cage { * - -1, fail */ pub fn lseek_syscall(&self, virtual_fd: i32, offset: isize, whence: i32) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "lseek", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); let ret = unsafe { - libc::lseek(kernel_fd as i32, offset as i64, whence) as i32 + libc::lseek(vfd.underfd as i32, offset as i64, whence) as i32 }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[lseek] Error message: {:?}", err_msg); - // println!("[lseek] virtual fd: {:?}", virtual_fd); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "lseek"); } @@ -684,7 +474,6 @@ impl Cage { * access() will return 0 when sucess, -1 when fail */ pub fn access_syscall(&self, path: &str, amode: i32) -> i32 { - // let c_path = CString::new(path).expect("CString::new failed"); let relpath = normpath(convpath(path), self); let relative_path = relpath.to_str().unwrap(); let full_path = format!("{}{}", LIND_ROOT, relative_path); @@ -693,18 +482,6 @@ impl Cage { libc::access(c_path.as_ptr(), amode) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[access] Error message: {:?}", err_msg); - // println!("[access] c_path: {:?}", c_path); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "access"); } @@ -717,39 +494,60 @@ impl Cage { * fchdir() will return 0 when sucess, -1 when fail */ pub fn fchdir_syscall(&self, virtual_fd: i32) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "fchdir", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); let ret = unsafe { - libc::fchdir(kernel_fd as i32) + libc::fchdir(vfd.underfd as i32) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[fchdir] Error message: {:?}", err_msg); - // println!("[fchdir] vfd: {:?}", virtual_fd); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "fchdir"); } + + // Get the working directory in native + let mut buf = [0; PATH_MAX as usize]; + let cwdret = unsafe { + libc::getcwd(buf.as_mut_ptr(), buf.len()) + }; + if cwdret == ptr::null_mut() { + let errno = get_errno(); + return handle_errno(errno, "fchdir"); + } + let cwdcstr = unsafe { CStr::from_ptr(buf.as_ptr() as *const i8) }; + let cwd = cwdcstr.to_str().unwrap(); + // Update RawPOSIX working directory + let raw_path = &cwd[LIND_ROOT.len()..]; + let true_path = normpath(convpath(raw_path), self); + let mut cwd_container = self.cwd.write(); + *cwd_container = interface::RustRfc::new(true_path); + return ret; } //------------------------------------CHDIR SYSCALL------------------------------------ /* + * We first check if desired path exists in native * chdir() will return 0 when sucess, -1 when fail */ pub fn chdir_syscall(&self, path: &str) -> i32 { + let relpath = normpath(convpath(path), self); + let relative_path = relpath.to_str().unwrap(); + let full_path = format!("{}{}", LIND_ROOT, relative_path); + let c_path = CString::new(full_path).unwrap(); + + let ret = unsafe { + libc::chdir(c_path.as_ptr()) + }; + + if ret < 0 { + let errno = get_errno(); + return handle_errno(errno, "chdir"); + } + let truepath = normpath(convpath(path), self); //at this point, syscall isn't an error @@ -766,13 +564,16 @@ impl Cage { * Then return virtual fd */ pub fn dup_syscall(&self, virtual_fd: i32, _start_desc: Option) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + if virtual_fd < 0 { return syscall_error(Errno::EBADF, "dup", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); - let ret_kernelfd = unsafe{ libc::dup(kernel_fd as i32) }; - let ret_virtualfd = get_unused_virtual_fd(self.cageid, ret_kernelfd as u64, false, 0).unwrap(); + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { + return syscall_error(Errno::EBADF, "dup", "Bad File Descriptor"); + } + let vfd = wrappedvfd.unwrap(); + let ret_kernelfd = unsafe{ libc::dup(vfd.underfd as i32) }; + let ret_virtualfd = fdtables::get_unused_virtual_fd(self.cageid, vfd.fdkind, ret_kernelfd as u64, false, 0).unwrap(); return ret_virtualfd as i32; } @@ -780,15 +581,18 @@ impl Cage { /* */ pub fn dup2_syscall(&self, old_virtualfd: i32, new_virtualfd: i32) -> i32 { - match translate_virtual_fd(self.cageid, old_virtualfd as u64) { - Ok(old_kernelfd) => { + if old_virtualfd < 0 || new_virtualfd < 0 { + return syscall_error(Errno::EBADF, "dup", "Bad File Descriptor"); + } + + match fdtables::translate_virtual_fd(self.cageid, old_virtualfd as u64) { + Ok(old_vfd) => { let new_kernelfd = unsafe { - libc::dup(old_kernelfd as i32) + libc::dup(old_vfd.underfd as i32) }; // Map new kernel fd with provided kernel fd - let _ret_kernelfd = unsafe{ libc::dup2(old_kernelfd as i32, new_kernelfd) }; - let optinfo = get_optionalinfo(self.cageid, old_virtualfd as u64).unwrap(); - let _ = get_specific_virtual_fd(self.cageid, new_virtualfd as u64, new_kernelfd as u64, false, optinfo).unwrap(); + let _ret_kernelfd = unsafe{ libc::dup2(old_vfd.underfd as i32, new_kernelfd) }; + let _ = fdtables::get_specific_virtual_fd(self.cageid, new_virtualfd as u64, old_vfd.fdkind, new_kernelfd as u64, false, old_vfd.perfdinfo).unwrap(); return new_virtualfd; }, Err(_e) => { @@ -804,36 +608,14 @@ impl Cage { * close() will return 0 when sucess, -1 when fail */ pub fn close_syscall(&self, virtual_fd: i32) -> i32 { - // println!(); - // println!("[CLOSE] REALFDCOUNT:"); - // io::stdout().flush().unwrap(); - // for entry in REALFDCOUNT.iter() { - // let key = entry.key(); - // let value = entry.value(); - // println!("realfd: {:?}, Value: {:?}", key, value); - // io::stdout().flush().unwrap(); - // } - // println!("[CLOSE] FDTABLE: "); - // io::stdout().flush().unwrap(); - // for entry in FDTABLE.iter() { - // let cageid = entry.key(); - // let fds = entry.value(); - // println!("cageid: {:?}", cageid); - // for (vfd, fd_entry) in fds.iter().enumerate() { - // if let Some(fd_entry) = fd_entry { - // println!(" virtual_fd: {}, FDTableEntry: {:?}", vfd, fd_entry); - // } - // } - // } - // io::stdout().flush().unwrap(); - // println!("[CLOSE] cageid: {:?}", self.cageid); - // println!("[CLOSE] vfd: {:?}", virtual_fd); - // io::stdout().flush().unwrap(); - match close_virtualfd(self.cageid, virtual_fd as u64) { + match fdtables::close_virtualfd(self.cageid, virtual_fd as u64) { Ok(()) => { return 0; } - Err(_) => { + Err(e) => { + if e == Errno::EBADFD as u64 { + return syscall_error(Errno::EBADF, "close", "bad file descriptor"); + } return -1; } } @@ -887,31 +669,20 @@ impl Cage { 0 } _ => { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "fcntl", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); if cmd == libc::F_DUPFD { - let new_kernelfd = unsafe { libc::fcntl(kernel_fd as i32, cmd, arg) }; - // Get status - let new_virtualfd = get_unused_virtual_fd(self.cageid, new_kernelfd as u64, false, 0).unwrap(); - return new_virtualfd as i32; + match arg { + n if n < 0 => return syscall_error(Errno::EINVAL, "fcntl", "op is F_DUPFD and arg is negative or is greater than the maximum allowable value"), + 0..=1024 => return self.dup2_syscall(virtual_fd, arg), + _ => return syscall_error(Errno::EMFILE, "fcntl", "op is F_DUPFD and the per-process limit on the number of open file descriptors has been reached") + } } - let ret = unsafe { libc::fcntl(kernel_fd as i32, cmd, arg) }; + let ret = unsafe { libc::fcntl(vfd.underfd as i32, cmd, arg) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[fcntl] Error message: {:?}", err_msg); - // println!("[fcntl] vfd: {:?}", virtual_fd); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "fcntl"); } @@ -930,25 +701,13 @@ impl Cage { * a nonnegative value on success. */ pub fn ioctl_syscall(&self, virtual_fd: i32, request: u64, ptrunion: *mut u8) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "ioctl", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); - let ret = unsafe { libc::ioctl(kernel_fd as i32, request, ptrunion as *mut c_void) }; + let vfd = wrappedvfd.unwrap(); + let ret = unsafe { libc::ioctl(vfd.underfd as i32, request, ptrunion as *mut c_void) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[ioctl] Error message: {:?}", err_msg); - // println!("[ioctl] vfd: {:?}", virtual_fd); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "ioctl"); } @@ -962,7 +721,6 @@ impl Cage { * chmod() will return 0 when success and -1 when fail */ pub fn chmod_syscall(&self, path: &str, mode: u32) -> i32 { - // let c_path = CString::new(path).expect("CString::new failed"); let relpath = normpath(convpath(path), self); let relative_path = relpath.to_str().unwrap(); let full_path = format!("{}{}", LIND_ROOT, relative_path); @@ -971,18 +729,6 @@ impl Cage { libc::chmod(c_path.as_ptr(), mode) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[chmod] Error message: {:?}", err_msg); - // println!("[chmod] c_path: {:?}", c_path); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "chmod"); } @@ -995,27 +741,15 @@ impl Cage { * fchmod() will return 0 when sucess, -1 when fail */ pub fn fchmod_syscall(&self, virtual_fd: i32, mode: u32) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "fchmod", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); let ret = unsafe { - libc::fchmod(kernel_fd as i32, mode) + libc::fchmod(vfd.underfd as i32, mode) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[fchmod] Error message: {:?}", err_msg); - // println!("[fchmod] vfd: {:?}", virtual_fd); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "fchmod"); } @@ -1040,28 +774,28 @@ impl Cage { off: i64, ) -> i32 { if virtual_fd != -1 { - match translate_virtual_fd(self.cageid, virtual_fd as u64) { + match fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64) { Ok(kernel_fd) => { let ret = unsafe { - ((libc::mmap(addr as *mut c_void, len, prot, flags, kernel_fd as i32, off) as i64) + ((libc::mmap(addr as *mut c_void, len, prot, flags, kernel_fd.underfd as i32, off) as i64) & 0xffffffff) as i32 }; - // if ret < 0 { - // let errno = get_errno(); - // return handle_errno(errno, "mmap"); - // } return ret; }, Err(_e) => { return syscall_error(Errno::EBADF, "mmap", "Bad File Descriptor"); } } - } - - // Do type conversion to translate from c_void into i32 - unsafe { - ((libc::mmap(addr as *mut c_void, len, prot, flags, -1, off) as i64) - & 0xffffffff) as i32 + } else { + // Handle mmap with fd = -1 (anonymous memory mapping or special case) + let ret = unsafe { + libc::mmap(addr as *mut c_void, len, prot, flags, -1, off) as i64 + }; + // Check if mmap failed and return the appropriate error if so + if ret == -1 { + return syscall_error(Errno::EINVAL, "mmap", "mmap failed with invalid flags"); + } + return (ret & 0xffffffff) as i32; } } @@ -1088,27 +822,15 @@ impl Cage { * flock() will return 0 when sucess, -1 when fail */ pub fn flock_syscall(&self, virtual_fd: i32, operation: i32) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "flock", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); let ret = unsafe { - libc::flock(kernel_fd as i32, operation) + libc::flock(vfd.underfd as i32, operation) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[flock] Error message: {:?}", err_msg); - // println!("[flock] vfd: {:?}", virtual_fd); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "flock"); } @@ -1121,31 +843,25 @@ impl Cage { * rmdir() will return 0 when sucess, -1 when fail */ pub fn rmdir_syscall(&self, path: &str) -> i32 { - // let c_path = CString::new(path).expect("CString::new failed"); - let relpath = normpath(convpath(path), self); - let relative_path = relpath.to_str().unwrap(); - let full_path = format!("{}{}", LIND_ROOT, relative_path); - let c_path = CString::new(full_path).unwrap(); - let ret = unsafe { - libc::rmdir(c_path.as_ptr()) - }; - if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[rmdir] Error message: {:?}", err_msg); - // println!("[rmdir] c_path: {:?}", c_path); - // io::stdout().flush().unwrap(); - let errno = get_errno(); - return handle_errno(errno, "rmdir"); + match path { + "" => return syscall_error(Errno::ENOENT, "rmdir", "A directory component in pathname does not exist"), + "/" => return syscall_error(Errno::EBUSY, "rmdir", "pathname is currently in use by the system or some process that prevents its removal"), + _ => { + let relpath = normpath(convpath(path), self); + let relative_path = relpath.to_str().unwrap(); + let full_path = format!("{}{}", LIND_ROOT, relative_path); + let c_path = CString::new(full_path).unwrap(); + let ret = unsafe { + libc::rmdir(c_path.as_ptr()) + }; + if ret < 0 { + let errno = get_errno(); + return handle_errno(errno, "rmdir"); + } + return ret; + } } - ret + } //------------------RENAME SYSCALL------------------ @@ -1167,19 +883,6 @@ impl Cage { libc::rename(old_cpath.as_ptr(), new_cpath.as_ptr()) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[rename] Error message: {:?}", err_msg); - // println!("[rename] old: {:?}", old_cpath); - // println!("[rename] new: {:?}", new_cpath); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "rename"); } @@ -1192,27 +895,15 @@ impl Cage { * fsync() will return 0 when sucess, -1 when fail */ pub fn fsync_syscall(&self, virtual_fd: i32) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "fsync", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); let ret = unsafe { - libc::fsync(kernel_fd as i32) + libc::fsync(vfd.underfd as i32) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[fsync] Error message: {:?}", err_msg); - // println!("[fsync] vfd: {:?}", virtual_fd); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "fsync"); } @@ -1226,27 +917,15 @@ impl Cage { * fdatasync() will return 0 when sucess, -1 when fail */ pub fn fdatasync_syscall(&self, virtual_fd: i32) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "fdatasync", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); let ret = unsafe { - libc::fdatasync(kernel_fd as i32) + libc::fdatasync(vfd.underfd as i32) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[fdatasync] Error message: {:?}", err_msg); - // println!("[fdatasync] vfd: {:?}", virtual_fd); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "fdatasync"); } @@ -1266,27 +945,15 @@ impl Cage { nbytes: isize, flags: u32, ) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "sync", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); let ret = unsafe { - libc::sync_file_range(kernel_fd as i32, offset as i64, nbytes as i64, flags) + libc::sync_file_range(vfd.underfd as i32, offset as i64, nbytes as i64, flags) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[sync file range] Error message: {:?}", err_msg); - // println!("[sync file range] vfd: {:?}", virtual_fd); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "sync_file_range"); } @@ -1299,27 +966,15 @@ impl Cage { * ftruncate() will return 0 when sucess, -1 when fail */ pub fn ftruncate_syscall(&self, virtual_fd: i32, length: isize) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "ftruncate", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); let ret = unsafe { - libc::ftruncate(kernel_fd as i32, length as i64) + libc::ftruncate(vfd.underfd as i32, length as i64) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[ftruncate] Error message: {:?}", err_msg); - // println!("[ftruncate] vfd: {:?}", virtual_fd); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "ftruncate"); } @@ -1331,7 +986,6 @@ impl Cage { * truncate() will return 0 when sucess, -1 when fail */ pub fn truncate_syscall(&self, path: &str, length: isize) -> i32 { - // let c_path = CString::new(path).expect("CString::new failed"); let relpath = normpath(convpath(path), self); let relative_path = relpath.to_str().unwrap(); let full_path = format!("{}{}", LIND_ROOT, relative_path); @@ -1340,18 +994,6 @@ impl Cage { libc::truncate(c_path.as_ptr(), length as i64) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[truncate] Error message: {:?}", err_msg); - // println!("[truncate] c_path: {:?}", c_path); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "truncate"); } @@ -1367,36 +1009,24 @@ impl Cage { * pipe() will return 0 when sucess, -1 when fail */ pub fn pipe_syscall(&self, pipefd: &mut PipeArray) -> i32 { - let mut kernel_fds = [0; 2]; - - let ret = unsafe { libc::pipe(kernel_fds.as_mut_ptr() as *mut i32) }; - if ret < 0 { - let errno = get_errno(); - return handle_errno(errno, "pipe"); - } - - pipefd.readfd = get_unused_virtual_fd(self.cageid, kernel_fds[0], false, 0).unwrap() as i32; - pipefd.writefd = get_unused_virtual_fd(self.cageid, kernel_fds[1], false, 0).unwrap() as i32; - - return ret; + self.pipe2_syscall(pipefd, 0) } pub fn pipe2_syscall(&self, pipefd: &mut PipeArray, flags: i32) -> i32 { let mut kernel_fds:[i32; 2] = [0; 2]; - + let ret = unsafe { libc::pipe2(kernel_fds.as_mut_ptr() as *mut i32, flags as i32) }; if ret < 0 { let errno = get_errno(); return handle_errno(errno, "pipe2"); } - if flags == libc::O_CLOEXEC { - pipefd.readfd = get_unused_virtual_fd(self.cageid, kernel_fds[0] as u64, true, 0).unwrap() as i32; - pipefd.writefd = get_unused_virtual_fd(self.cageid, kernel_fds[1] as u64, true, 0).unwrap() as i32; - } else { - pipefd.readfd = get_unused_virtual_fd(self.cageid, kernel_fds[0] as u64, false, 0).unwrap() as i32; - pipefd.writefd = get_unused_virtual_fd(self.cageid, kernel_fds[1] as u64, false, 0).unwrap() as i32; - } + let should_cloexec = if flags & O_CLOEXEC != 0 { + true + } else { false }; + + pipefd.readfd = fdtables::get_unused_virtual_fd(self.cageid, FDKIND_KERNEL, kernel_fds[0] as u64, should_cloexec, 0).unwrap() as i32; + pipefd.writefd = fdtables::get_unused_virtual_fd(self.cageid, FDKIND_KERNEL, kernel_fds[1] as u64, should_cloexec, 0).unwrap() as i32; return ret; } @@ -1409,15 +1039,13 @@ impl Cage { * - 0, EOF * - -1, fail */ - pub fn getdents_syscall(&self, virtual_fd: i32, buf: *mut u8, nbytes: u32) -> i32 { - // let kernel_fd = translate_virtual_fd(self.cageid, virtual_fd as u64); - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "getdents", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); - let ret = unsafe { libc::syscall(libc::SYS_getdents as c_long, kernel_fd as i32, buf as *mut c_void, nbytes) as i32 }; + let vfd = wrappedvfd.unwrap(); + let ret = unsafe { libc::syscall(libc::SYS_getdents as c_long, vfd.underfd as i32, buf as *mut c_void, nbytes) as i32 }; if ret < 0 { let errno = get_errno(); return handle_errno(errno, "getdents"); @@ -1432,10 +1060,17 @@ impl Cage { * - null, fail */ pub fn getcwd_syscall(&self, buf: *mut u8, bufsize: u32) -> i32 { + if (!buf.is_null() && bufsize == 0) || (buf.is_null() && bufsize != 0) { + return syscall_error(Errno::EINVAL, "getcwd", "Invalid arguments"); + } + let cwd_container = self.cwd.read(); let path = cwd_container.to_str().unwrap(); - if path.len() >= bufsize as usize { - return -1; + // The required size includes the null terminator + let required_size = path.len() + 1; + if required_size > bufsize as usize { + unsafe { *libc::__errno_location() = libc::ERANGE }; + return -libc::ERANGE; } unsafe { ptr::copy(path.as_ptr(), buf, path.len()); @@ -2246,28 +1881,8 @@ impl Cage { } } -pub fn kernel_close(kernelfd: u64) { - // println!("[KERNEL CLOSE] cageid: {:?}", self.cageid); - // println!("[KERNEL CLOSE] realfd: {:?}", kernelfd); - // io::stdout().flush().unwrap(); - - let ret = unsafe { +pub fn kernel_close(_fdentry: fdtables::FDTableEntry, kernelfd: u64) { + let _ret = unsafe { libc::close(kernelfd as i32) }; - if ret != 0 { - let err = unsafe { - *libc::__errno_location() - }; - let err_str = unsafe { - libc::strerror(err) - }; - let err_msg = unsafe { - CStr::from_ptr(err_str).to_string_lossy().into_owned() - }; - println!("errno: {:?}", err); - println!("Error message: {:?}", err_msg); - println!("kernel fd: {:?}", kernelfd); - io::stdout().flush().unwrap(); - panic!("kernel close failed! "); - } -} \ No newline at end of file +} diff --git a/src/safeposix/syscalls/fs_constants.rs b/src/safeposix/syscalls/fs_constants.rs index e316539b..a5c1fb61 100644 --- a/src/safeposix/syscalls/fs_constants.rs +++ b/src/safeposix/syscalls/fs_constants.rs @@ -42,20 +42,20 @@ use crate::interface; // pub const DEFAULTTIME: u64 = 1323630836; -// //Standard flag combinations -// pub const S_IRWXA: u32 = 0o777; -// pub const S_IRWXU: u32 = 0o700; -// pub const S_IRUSR: u32 = 0o400; -// pub const S_IWUSR: u32 = 0o200; -// pub const S_IXUSR: u32 = 0o100; -// pub const S_IRWXG: u32 = 0o070; -// pub const S_IRGRP: u32 = 0o040; -// pub const S_IWGRP: u32 = 0o020; -// pub const S_IXGRP: u32 = 0o010; -// pub const S_IRWXO: u32 = 0o007; -// pub const S_IROTH: u32 = 0o004; -// pub const S_IWOTH: u32 = 0o002; -// pub const S_IXOTH: u32 = 0o001; +//Standard flag combinations +pub const S_IRWXA: u32 = 0o777; +pub const S_IRWXU: u32 = 0o700; +pub const S_IRUSR: u32 = 0o400; +pub const S_IWUSR: u32 = 0o200; +pub const S_IXUSR: u32 = 0o100; +pub const S_IRWXG: u32 = 0o070; +pub const S_IRGRP: u32 = 0o040; +pub const S_IWGRP: u32 = 0o020; +pub const S_IXGRP: u32 = 0o010; +pub const S_IRWXO: u32 = 0o007; +pub const S_IROTH: u32 = 0o004; +pub const S_IWOTH: u32 = 0o002; +pub const S_IXOTH: u32 = 0o001; // //Commands for FCNTL // pub const F_DUPFD: i32 = 0; diff --git a/src/safeposix/syscalls/net_calls.rs b/src/safeposix/syscalls/net_calls.rs index 1989130d..e2270ac4 100644 --- a/src/safeposix/syscalls/net_calls.rs +++ b/src/safeposix/syscalls/net_calls.rs @@ -2,18 +2,18 @@ // Network related system calls // outlines and implements all of the networking system calls that are being emulated/faked in Lind -use super::net_constants::*; +use super::net_constants; use crate::{interface::FdSet, safeposix::cage::*}; use crate::interface::*; +use crate::interface; +use super::sys_constants; -// use crate::example_grates::vanillaglobal::*; -use crate::example_grates::dashmapvecglobal::*; -// use crate::example_grates::muthashmaxglobal::*; -// use crate::example_grates::dashmaparrayglobal::*; +use crate::fdtables::{self, FDTableEntry}; use std::collections::HashSet; use std::collections::HashMap; use std::convert::TryInto; +use dashmap::mapref::entry; use parking_lot::Mutex; use lazy_static::lazy_static; use std::io::{Read, Write}; @@ -22,12 +22,19 @@ use std::mem::size_of; use libc::*; use std::ffi::CString; use std::ffi::CStr; +use std::sync::Arc; + +use crate::safeposix::filesystem::convpath; +use crate::safeposix::filesystem::normpath; use libc::*; use std::{os::fd::RawFd, ptr}; use bit_set::BitSet; static LIND_ROOT: &str = "/home/lind/lind_project/src/safeposix-rust/tmp/"; +const FDKIND_KERNEL: u32 = 0; +const FDKIND_IMPIPE: u32 = 1; +const FDKIND_IMSOCK: u32 = 2; lazy_static! { // A hashmap used to store epoll mapping relationships @@ -47,18 +54,10 @@ impl Cage { */ if kernel_fd < 0 { let errno = get_errno(); - let err_str = unsafe { - libc::strerror(errno) - }; - let err_msg = unsafe { - CStr::from_ptr(err_str).to_string_lossy().into_owned() - }; - println!("[socket] Error message: {:?}", err_msg); - io::stdout().flush().unwrap(); return handle_errno(errno, "socket"); } - return get_unused_virtual_fd(self.cageid, kernel_fd as u64, false, 0).unwrap() as i32; + return fdtables::get_unused_virtual_fd(self.cageid, FDKIND_KERNEL, kernel_fd as u64, false, 0).unwrap() as i32; } /* @@ -69,14 +68,12 @@ impl Cage { /* translate_virtual_fd(cageid: u64, virtualfd: u64) -> Result */ - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "bind", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); - // println!("[Bind] Before GenSockaddr: {:?}", addr); - // io::stdout().flush().unwrap(); let mut new_addr = SockaddrUnix::default(); let (finalsockaddr, addrlen) = match addr { @@ -89,52 +86,6 @@ impl Cage { size_of::(), ), GenSockaddr::Unix(addrrefu) => { - // let original_path_ptr; - // let original_path_len; - // unsafe { - // // Skip the first '/' - // original_path_ptr = addrrefu.sun_path.as_ptr().add(1); - - // original_path_len = libc::strlen(original_path_ptr as *const i8); - // } - - - // // Create new path - // let lind_root_len = LIND_ROOT.len(); - // let new_path_len = lind_root_len + original_path_len; - - // if new_path_len >= addrrefu.sun_path.len() { - // panic!("New path is too long to fit in sun_path"); - // } - - // let mut new_addr = SockaddrUnix { - // sun_family: addrrefu.sun_family, - // sun_path: [0; 108], - // }; - - // // First copy LIND_ROOT and then copy original path - // unsafe { - // std::ptr::copy_nonoverlapping( - // LIND_ROOT.as_ptr(), - // new_addr.sun_path.as_mut_ptr(), - // lind_root_len - // ); - - - // std::ptr::copy_nonoverlapping( - // original_path_ptr, - // new_addr.sun_path.as_mut_ptr().add(lind_root_len), - // original_path_len - // ); - - // // End with NULL - // // *new_addr.sun_path.get_unchecked_mut(new_path_len) = 0; - // } - - // ( - // (&new_addr as *const SockaddrUnix).cast::(), - // size_of::(), - // ) // Convert sun_path to LIND_ROOT path let original_path = unsafe { CStr::from_ptr(addrrefu.sun_path.as_ptr() as *const i8).to_str().unwrap() }; let lind_path = format!("{}{}", LIND_ROOT, &original_path[..]); // Skip the initial '/' in original path @@ -158,9 +109,6 @@ impl Cage { ); *new_addr.sun_path.get_unchecked_mut(lind_path.len()) = 0; // Null-terminate the string } - - // println!("[bind] new_addr:{:?} ", new_addr); - // io::stdout().flush().unwrap(); ( (&new_addr as *const SockaddrUnix).cast::(), @@ -171,29 +119,9 @@ impl Cage { }; - let ret = unsafe { libc::bind(kernel_fd as i32, finalsockaddr, addrlen as u32) }; + let ret = unsafe { libc::bind(vfd.underfd as i32, finalsockaddr, addrlen as u32) }; if ret < 0 { let errno = get_errno(); - let err_str = unsafe { - libc::strerror(errno) - }; - let err_msg = unsafe { - CStr::from_ptr(err_str).to_string_lossy().into_owned() - }; - unsafe { - let sockaddr_un_ptr = finalsockaddr as *const sockaddr_un; - - let sun_path_ptr = (*sockaddr_un_ptr).sun_path.as_ptr(); - - let c_str = CStr::from_ptr(sun_path_ptr); - let str_slice = c_str.to_str().expect("Failed to convert CStr to str"); - - println!("[bind] addr: {:?}", addr); - println!("[bind] sun_path: {}", str_slice); - io::stdout().flush().unwrap(); - } - println!("[Bind] Error message: {:?}", err_msg); - io::stdout().flush().unwrap(); return handle_errno(errno, "bind"); } ret @@ -204,11 +132,11 @@ impl Cage { * connect() will return 0 when success and -1 when fail */ pub fn connect_syscall(&self, virtual_fd: i32, addr: &GenSockaddr) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "connect", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); let mut new_addr = SockaddrUnix::default(); @@ -222,52 +150,6 @@ impl Cage { size_of::(), ), GenSockaddr::Unix(addrrefu) => { - // let original_path_ptr; - // let original_path_len; - // unsafe { - // // Skip the first '/' - // original_path_ptr = addrrefu.sun_path.as_ptr().add(1); - - // original_path_len = libc::strlen(original_path_ptr as *const i8); - // } - - - // // Create new path - // let lind_root_len = LIND_ROOT.len(); - // let new_path_len = lind_root_len + original_path_len; - - // if new_path_len >= addrrefu.sun_path.len() { - // panic!("New path is too long to fit in sun_path"); - // } - - // let mut new_addr = SockaddrUnix { - // sun_family: addrrefu.sun_family, - // sun_path: [0; 108], - // }; - - // // First copy LIND_ROOT and then copy original path - // unsafe { - // std::ptr::copy_nonoverlapping( - // LIND_ROOT.as_ptr(), - // new_addr.sun_path.as_mut_ptr(), - // lind_root_len - // ); - - - // std::ptr::copy_nonoverlapping( - // original_path_ptr, - // new_addr.sun_path.as_mut_ptr().add(lind_root_len), - // original_path_len - // ); - - // // End with NULL - // *new_addr.sun_path.get_unchecked_mut(new_path_len) = 0; - // } - - // ( - // (&new_addr as *const SockaddrUnix).cast::(), - // size_of::(), - // ) // Convert sun_path to LIND_ROOT path let original_path = unsafe { CStr::from_ptr(addrrefu.sun_path.as_ptr() as *const i8).to_str().unwrap() }; let lind_path = format!("{}{}", LIND_ROOT, &original_path[..]); // Skip the initial '/' in original path @@ -291,8 +173,7 @@ impl Cage { ); *new_addr.sun_path.get_unchecked_mut(lind_path.len()) = 0; // Null-terminate the string } - // println!("[connect] new_addr:{:?} ", new_addr); - // io::stdout().flush().unwrap(); + ( (&new_addr as *const SockaddrUnix).cast::(), size_of::(), @@ -301,19 +182,8 @@ impl Cage { } }; - let ret = unsafe { libc::connect(kernel_fd as i32, finalsockaddr, addrlen as u32) }; + let ret = unsafe { libc::connect(vfd.underfd as i32, finalsockaddr, addrlen as u32) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[Connect] Error message: {:?}", err_msg); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "connect"); } @@ -332,11 +202,11 @@ impl Cage { flags: i32, dest_addr: &GenSockaddr, ) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "sendto", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); let (finalsockaddr, addrlen) = match dest_addr { GenSockaddr::V6(addrref6) => ( @@ -382,7 +252,7 @@ impl Cage { let ret = unsafe { libc::sendto( - kernel_fd as i32, + vfd.underfd as i32, buf as *const c_void, buflen, flags, @@ -410,26 +280,14 @@ impl Cage { buflen: usize, flags: i32, ) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "send", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); - let ret = unsafe { libc::send(kernel_fd as i32, buf as *const c_void, buflen, flags) as i32}; + let ret = unsafe { libc::send(vfd.underfd as i32, buf as *const c_void, buflen, flags) as i32}; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[Send] Error message: {:?}", err_msg); - // println!("[Send] kernel fd: {:?}", kernel_fd); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "send"); } @@ -452,11 +310,11 @@ impl Cage { flags: i32, addr: &mut Option<&mut GenSockaddr>, ) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "recvfrom", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); let (finalsockaddr, mut addrlen) = match addr { Some(GenSockaddr::V6(ref mut addrref6)) => ( @@ -474,21 +332,9 @@ impl Cage { None => (std::ptr::null::() as *mut libc::sockaddr, 0), }; - let ret = unsafe { libc::recvfrom(kernel_fd as i32, buf as *mut c_void, buflen, flags, finalsockaddr, &mut addrlen as *mut u32) as i32 }; + let ret = unsafe { libc::recvfrom(vfd.underfd as i32, buf as *mut c_void, buflen, flags, finalsockaddr, &mut addrlen as *mut u32) as i32 }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[Recvfrom] Error message: {:?}", err_msg); - // println!("[Recvfrom] addr: {:?}", addr); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "recvfrom"); } @@ -504,26 +350,14 @@ impl Cage { * - Fail: -1 */ pub fn recv_syscall(&self, virtual_fd: i32, buf: *mut u8, len: usize, flags: i32) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "recv", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); - let ret = unsafe { libc::recv(kernel_fd as i32, buf as *mut c_void, len, flags) as i32 }; + let ret = unsafe { libc::recv(vfd.underfd as i32, buf as *mut c_void, len, flags) as i32 }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[Recv] Error message: {:?}", err_msg); - // println!("[Recv] kernel fd: {:?}", kernel_fd); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "recv"); } @@ -535,25 +369,14 @@ impl Cage { * listen() will return 0 when success and -1 when fail */ pub fn listen_syscall(&self, virtual_fd: i32, backlog: i32) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "listen", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); - let ret = unsafe { libc::listen(kernel_fd as i32, backlog) }; + let ret = unsafe { libc::listen(vfd.underfd as i32, backlog) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[Listen] Error message: {:?}", err_msg); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "listen"); } @@ -565,13 +388,13 @@ impl Cage { * shutdown() will return 0 when success and -1 when fail */ pub fn shutdown_syscall(&self, virtual_fd: i32, how: i32) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "shutdown", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); - let ret = unsafe { libc::shutdown(kernel_fd as i32, how) }; + let ret = unsafe { libc::shutdown(vfd.underfd as i32, how) }; if ret < 0 { let errno = get_errno(); @@ -596,11 +419,11 @@ impl Cage { virtual_fd: i32, addr: &mut Option<&mut GenSockaddr>, ) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "accept", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); let (finalsockaddr, mut addrlen) = match addr { Some(GenSockaddr::V6(ref mut addrref6)) => ( @@ -618,28 +441,9 @@ impl Cage { None => (std::ptr::null::() as *mut libc::sockaddr, 0), }; - // println!("[Accept] Before GenSockaddr: {:?}", addr); - // io::stdout().flush().unwrap(); - - let ret_kernelfd = unsafe { libc::accept(kernel_fd as i32, finalsockaddr, &mut addrlen as *mut u32) }; + let ret_kernelfd = unsafe { libc::accept(vfd.underfd as i32, finalsockaddr, &mut addrlen as *mut u32) }; if ret_kernelfd < 0 { - // let errno = unsafe { - // *libc::__errno_location() - // } as i32; - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[Accept] Error message: {:?}", err_msg); - // io::stdout().flush().unwrap(); - // println!("[Accept] errno: {:?}", errno); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "accept"); } @@ -647,9 +451,6 @@ impl Cage { // change the GenSockaddr type according to the sockaddr we received // GenSockAddr will be modified after libc::accept returns // So we only need to modify values in GenSockAddr, and rest of the things will be finished in dispatcher stage - // println!("[Accept] After GenSockaddr: {:?}", addr); - // println!("[Accept] finalsockaddr: {:?}", finalsockaddr); - // io::stdout().flush().unwrap(); if let Some(sockaddr) = addr { if let GenSockaddr::Unix(ref mut sockaddr_unix) = sockaddr{ @@ -675,11 +476,11 @@ impl Cage { } } - let ret_virtualfd = get_unused_virtual_fd(self.cageid, ret_kernelfd as u64, false, 0).unwrap(); + let ret_virtualfd = fdtables::get_unused_virtual_fd(self.cageid, FDKIND_KERNEL, ret_kernelfd as u64, false, 0).unwrap(); ret_virtualfd as i32 } - + /* * fd_set is used in the Linux select system call to specify the file descriptor * to be monitored. fd_set is actually a bit array, each bit of which represents @@ -706,14 +507,8 @@ impl Cage { mut readfds: Option<&mut fd_set>, mut writefds: Option<&mut fd_set>, mut errorfds: Option<&mut fd_set>, - // timeout: *mut timeval, rposix_timeout: Option, ) -> i32 { - // println!("[Select] nfds: {:?}", nfds); - // println!("[Select] readfds: {:?}", readfds); - // println!("[Select] writefds: {:?}", writefds); - // println!("[Select] errorfds: {:?}", errorfds); - // io::stdout().flush().unwrap(); let mut timeout; if rposix_timeout.is_none() { @@ -727,134 +522,260 @@ impl Cage { tv_usec: rposix_timeout.unwrap().subsec_micros() as i64, }; } - let orfds = readfds.as_mut().map(|fds| &mut **fds); let owfds = writefds.as_mut().map(|fds| &mut **fds); let oefds = errorfds.as_mut().map(|fds| &mut **fds); - // println!("[Select] orfds: {:?}", orfds); - // println!("[Select] owfds: {:?}", owfds); - // println!("[Select] oefds: {:?}", oefds); - // io::stdout().flush().unwrap(); - - let (newnfds, mut real_readfds, mut real_writefds, mut real_errorfds, _unrealset, mappingtable) - = get_real_bitmasks_for_select( - self.cageid, - nfds as u64, - orfds.copied(), - owfds.copied(), - oefds.copied(), - ).unwrap(); - - // println!("[Select] real_readfds: {:?}", real_readfds); - // println!("[Select] real_writefds: {:?}", real_writefds); - // println!("[Select] real_errorfds: {:?}", real_errorfds); - // io::stdout().flush().unwrap(); - - // println!("[Select] Before kernel select real_readfds: {:?}", real_readfds); - // println!("[Select] Before kernel select timeout: {:?}\nrposix_timeout: {:?}", timeout, rposix_timeout); - // io::stdout().flush().unwrap(); + let mut fdkindset = HashSet::new(); + // fdkindset.insert(FDKIND_IMPIPE); + fdkindset.insert(FDKIND_KERNEL); + + let (selectbittables, unparsedtables, mappingtable) = fdtables::prepare_bitmasks_for_select(self.cageid, nfds as u64, orfds.copied(), owfds.copied(), oefds.copied(), &fdkindset).unwrap(); + // libc select() + let (readnfd, mut real_readfds) = selectbittables[0].get(&FDKIND_KERNEL).unwrap(); + let (writenfd, mut real_writefds) = selectbittables[1].get(&FDKIND_KERNEL).unwrap(); + let (errornfd, mut real_errorfds) = selectbittables[2].get(&FDKIND_KERNEL).unwrap(); + + let mut realnewnfds = readnfd; + if realnewnfds < writenfd { + realnewnfds = writenfd; + } else if realnewnfds < errornfd { + realnewnfds = errornfd; + } // Ensured that null_mut is used if the Option is None for fd_set parameters. let ret = unsafe { libc::select( - newnfds as i32, - real_readfds.as_mut().map_or(std::ptr::null_mut(), |fds| fds as *mut fd_set), - real_writefds.as_mut().map_or(std::ptr::null_mut(), |fds| fds as *mut fd_set), - real_errorfds.as_mut().map_or(std::ptr::null_mut(), |fds| fds as *mut fd_set), + *realnewnfds as i32, + &mut real_readfds as *mut fd_set, + &mut real_writefds as *mut fd_set, + &mut real_errorfds as *mut fd_set, &mut timeout as *mut timeval) }; - // println!("[Select] After kernel select real_readfds: {:?}", real_readfds); - // io::stdout().flush().unwrap(); - if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[Select] Error message: {:?}", err_msg); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "select"); } - // Revert result - // let (_retnfds, Some(retreadfds), Some(retwritefds), Some(reterrorfds)) = .unwrap(); - match get_virtual_bitmasks_from_select_result( - newnfds as u64, - real_readfds, - real_writefds, - real_errorfds, - HashSet::new(), - HashSet::new(), - HashSet::new(), - &mappingtable, - // mappingtable, - ) { - Ok((_retnfds, retreadfds, retwritefds, reterrorfds)) => { - if let Some(rfds) = readfds.as_mut() { - if let Some(ret_rfds) = retreadfds { - **rfds = ret_rfds; - } else { - // Clear the fd_set if result is None - unsafe { libc::FD_ZERO(&mut **rfds); } - } - } - - if let Some(wfds) = writefds.as_mut() { - if let Some(ret_wfds) = retwritefds { - **wfds = ret_wfds; - } else { - // Clear the fd_set if result is None - unsafe { libc::FD_ZERO(&mut **wfds); } - } - } - - if let Some(efds) = errorfds.as_mut() { - if let Some(ret_efds) = reterrorfds { - **efds = ret_efds; - } else { - // Clear the fd_set if result is None - unsafe { libc::FD_ZERO(&mut **efds); } - } - } - }, - Err(e) => { - panic!(""); - } - } - // println!("[Select] retreadfds: {:?}", retreadfds); - // println!("[Select] mappingtable: {:?}", mappingtable); - // io::stdout().flush().unwrap(); + // impipe/imsock select() + let start_time = starttimer(); - - // println!("[Select] readfds: {:?}", readfds); - // io::stdout().flush().unwrap(); - // println!("[Select] ret: {:?}", ret); - // io::stdout().flush().unwrap(); - // let mut count = 0; - // FDTABLE.iter().for_each(|entry| { - // // println!("Cage ID: {}", entry.key()); - // for (index, fd_entry) in entry.value().iter().enumerate() { - // if let Some(entry) = fd_entry { - // // println!(" Index {}: {:?}", index, entry); - // count = count+1; + let end_time = match rposix_timeout { + Some(time) => time, + None => RustDuration::MAX, + }; + + let mut return_code = 0; + let mut unreal_read = HashSet::new(); + let mut unreal_write = HashSet::new(); + + /* TODO + 1. Do we need to handle errfds? + 2. Err returns? + */ + // loop { + // for (fdkind_flag, entry) in unparsedtables[0].iter() { + // if *fdkind_flag == FDKIND_IMPIPE { + // let res = self.select_impipe_read(fdkind_flag, entry, &mut unreal_read, &mut return_code, mappingtable.clone()); + // if res != 0 { + // return syscall_error(Errno::EINVAL, "select", ""); + // } + // } else if *fdkind_flag == FDKIND_IMSOCK { + // let res = self.select_imsock_read(fdkind_flag, entry, &mut unreal_read, &mut return_code, mappingtable.clone()); + // if res != 0 { + // return syscall_error(Errno::EINVAL, "select", ""); + // } // } // } - // }); - // println!("[SELECT] Total: {:?}", count); - // io::stdout().flush().unwrap(); - ret + // for (fdkind_flag, entry) in unparsedtables[1].iter() { + // if *fdkind_flag == FDKIND_IMPIPE { + // let res = self.select_impipe_write(fdkind_flag, entry, &mut unreal_write, &mut return_code, mappingtable.clone()); + // if res != 0 { + // return syscall_error(Errno::EINVAL, "select", ""); + // } + // } else if *fdkind_flag == FDKIND_IMSOCK { + // let res = self.select_imsock_write(entry); + // if res != 0 { + // return syscall_error(Errno::EINVAL, "select", ""); + // } + // } + // } + + // // We haven't handle errfds + + // // we break if there is any file descriptor ready + // // or timeout is reached + // if return_code != 0 || readtimer(start_time) > end_time { + // break; + // } else { + // // otherwise, check for signal and loop again + // if sigcheck() { + // return syscall_error(Errno::EINTR, "select", "interrupted function call"); + // } + // // We yield to let other threads continue if we've found no ready descriptors + // lind_yield(); + // } + // } + // Revert result + let (read_flags, read_result) = fdtables::get_one_virtual_bitmask_from_select_result( + FDKIND_KERNEL, + nfds as u64, + Some(real_readfds), + unreal_read, + None, + &mappingtable + ); + + if let Some(readfds) = readfds.as_mut() { + **readfds = read_result.unwrap(); + } + + let (write_flags, write_result) = fdtables::get_one_virtual_bitmask_from_select_result( + FDKIND_KERNEL, + nfds as u64, + Some(real_writefds), + unreal_write, + None, + &mappingtable + ); + + if let Some(writefds) = writefds.as_mut() { + **writefds = write_result.unwrap(); + } + + let (error_flags, error_result) = fdtables::get_one_virtual_bitmask_from_select_result( + FDKIND_KERNEL, + nfds as u64, + Some(real_errorfds), + HashSet::new(), // Assuming there are no unreal errorsets + None, + &mappingtable + ); + + if let Some(errorfds) = errorfds.as_mut() { + **errorfds = error_result.unwrap(); + } + + // The total number of descriptors ready + (read_flags + write_flags + error_flags) as i32 } + // pub fn select_impipe_read( + // &self, + // fdkind: &u32, + // entry: &HashSet, + // unreal_read: &mut HashSet, + // return_code: &mut i32, + // mappingtable: HashMap<(u32, u64), u64>, + // ) -> i32 { + // for impipe_entry in entry { + // if let IPCTableEntry::Pipe(ref pipe_entry) = *IPC_TABLE.get(&impipe_entry.underfd).unwrap() { + // if impipe_entry.perfdinfo as i32 & O_RDONLY != 0 { + // if pipe_entry.pipe.check_select_read() { + // *return_code += 1; + // match mappingtable.get(&(*fdkind, impipe_entry.underfd)) { + // Some(&virfd) => unreal_read.insert(virfd), + // None => return syscall_error(Errno::EBADFD, "select", "impipe") + // }; + // } + // } + // } + // } + // return 0; + // } + + // pub fn select_imsock_read( + // &self, + // fdkind: &u32, + // entry: &HashSet, + // unreal_read: &mut HashSet, + // return_code: &mut i32, + // mappingtable: HashMap<(u32, u64), u64>, + // ) -> i32 { + // for imsock_entry in entry { + // if let IPCTableEntry::DomainSocket(ref mut sock_entry) = *IPC_TABLE.get_mut(&imsock_entry.underfd).unwrap() { + // match sock_entry.state { + // ConnState::INPROGRESS => { + // let remotepathstring = CString::new(sock_entry.remoteaddr.unwrap().path()).unwrap(); + // let dsconnobj = DS_CONNECTION_TABLE.get(&remotepathstring); + // if dsconnobj.is_none() { + // sock_entry.state = ConnState::CONNECTED; + // } + // }, + // ConnState::LISTEN => { + // let localpathstring = CString::new(sock_entry.localaddr.unwrap().path()).unwrap(); + // let dsconnobj = DS_CONNECTION_TABLE.get(&localpathstring); + // if dsconnobj.is_some() { + // match mappingtable.get(&(*fdkind, imsock_entry.underfd)) { + // Some(&virfd) => unreal_read.insert(virfd), + // None => return syscall_error(Errno::EINVAL, "select", "invalid operation") + // }; + // *return_code += 1; + // } + // }, + // ConnState::CONNECTED | ConnState::CONNRDONLY => { + // let receivepipe = sock_entry.receivepipe.as_ref().unwrap(); + // if receivepipe.check_select_read() { + // match mappingtable.get(&(*fdkind, imsock_entry.underfd)) { + // Some(&virfd) => unreal_read.insert(virfd), + // None => return syscall_error(Errno::EINVAL, "select", "invalid operation") + // }; + // *return_code += 1; + // } + // }, + // _ => {} + // } + // } + // } + // 0 + // } + + // pub fn select_impipe_write( + // &self, + // fdkind: &u32, + // entry: &HashSet, + // unreal_write: &mut HashSet, + // return_code: &mut i32, + // mappingtable: HashMap<(u32, u64), u64>, + // ) -> i32 { + // for impipe_entry in entry { + // if let IPCTableEntry::Pipe(ref pipe_entry) = *IPC_TABLE.get(&impipe_entry.underfd).unwrap() { + // if impipe_entry.perfdinfo as i32 & O_WRONLY != 0 { + // if pipe_entry.pipe.check_select_write() { + // *return_code += 1; + // match mappingtable.get(&(*fdkind, impipe_entry.underfd)) { + // Some(&virfd) => unreal_write.insert(virfd), + // None => return syscall_error(Errno::EBADFD, "select", "impipe") + // }; + // } + // } + + // } + // } + // return 0; + // } + + // pub fn select_imsock_write( + // &self, + // entry: &HashSet, + // ) -> i32 { + // for imsock_entry in entry { + // if let IPCTableEntry::DomainSocket(ref mut sock_entry) = *IPC_TABLE.get_mut(&imsock_entry.underfd).unwrap() { + // if sock_entry.state == ConnState::INPROGRESS || sock_entry.state == ConnState::CONNWRONLY { // and writeonly + // let remotepathstring = CString::new(sock_entry.remoteaddr.unwrap().path()).unwrap(); + // let dsconnobj = DS_CONNECTION_TABLE.get(&remotepathstring); + // if dsconnobj.is_none() { + // sock_entry.state = ConnState::CONNECTED; + // } + // } + // } + // } + // return 0; + // } + /* * Get the kernel fd with provided virtual fd first * getsockopt() will return 0 when success and -1 when fail @@ -864,40 +785,22 @@ impl Cage { virtual_fd: i32, level: i32, optname: i32, - // optval: *mut u8, optval: &mut i32, - // optlen: u32, ) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "getsockopt", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); - // let mut optlen: u32 = 4; let mut optlen: socklen_t = 4; - // let ret = unsafe { libc::getsockopt(kernel_fd as i32, level, optname, optval as *mut c_void, optlen as *mut u32) }; - let ret = unsafe { libc::getsockopt(kernel_fd as i32, level, optname, optval as *mut c_int as *mut c_void, &mut optlen as *mut socklen_t) }; + let ret = unsafe { libc::getsockopt(vfd.underfd as i32, level, optname, optval as *mut c_int as *mut c_void, &mut optlen as *mut socklen_t) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[Getsockopt] Error message: {:?}", err_msg); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "getsockopt"); } - - // println!("[Getsockopt] optval: {:?}", optval); - // io::stdout().flush().unwrap(); ret } @@ -913,27 +816,16 @@ impl Cage { optval: *mut u8, optlen: u32, ) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "setsockopt", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); let ret = unsafe { - libc::setsockopt(kernel_fd as i32, level, optname, optval as *mut c_void, optlen) + libc::setsockopt(vfd.underfd as i32, level, optname, optval as *mut c_void, optlen) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[Setsockopt] Error message: {:?}", err_msg); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "setsockopt"); } @@ -949,11 +841,11 @@ impl Cage { virtual_fd: i32, address: &mut Option<&mut GenSockaddr> ) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "getpeername", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); let (finalsockaddr, mut addrlen) = match address { Some(GenSockaddr::V6(ref mut addrref6)) => ( @@ -971,31 +863,12 @@ impl Cage { None => (std::ptr::null::() as *mut libc::sockaddr, 0), }; - // println!("[getpeername] addr BEFORE: {:?}", address); - // println!("[getpeername] finalsockaddr BEFORE: {:?}", finalsockaddr); - // io::stdout().flush().unwrap(); - - let ret = unsafe { libc::getpeername(kernel_fd as i32, finalsockaddr, &mut addrlen as *mut u32) }; + let ret = unsafe { libc::getpeername(vfd.underfd as i32, finalsockaddr, &mut addrlen as *mut u32) }; if ret < 0 { - let err = unsafe { - libc::__errno_location() - }; - let err_str = unsafe { - libc::strerror(*err) - }; - let err_msg = unsafe { - CStr::from_ptr(err_str).to_string_lossy().into_owned() - }; - println!("[getpeername] Error message: {:?}", err_msg); - let errno = get_errno(); - println!("[getpeername] Errno: {:?}", errno); - io::stdout().flush().unwrap(); return handle_errno(errno, "getpeername"); } - // println!("[getpeername] finalsockaddr After: {:?}", finalsockaddr); - // io::stdout().flush().unwrap(); if let Some(sockaddr) = address { if let GenSockaddr::Unix(ref mut sockaddr_unix) = sockaddr{ @@ -1021,9 +894,6 @@ impl Cage { } } - // println!("[getpeername] addr: {:?}", address); - // io::stdout().flush().unwrap(); - ret } @@ -1036,13 +906,13 @@ impl Cage { virtual_fd: i32, address: &mut Option<&mut GenSockaddr>, ) -> i32 { - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() { + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() { return syscall_error(Errno::EBADF, "getsockname", "Bad File Descriptor"); } - let kernel_fd = kfd.unwrap(); + let vfd = wrappedvfd.unwrap(); - let (finalsockaddr, mut addrlen) = match address { + let (finalsockaddr, mut _addrlen) = match address { Some(GenSockaddr::V6(ref mut addrref6)) => ( (addrref6 as *mut SockaddrV6).cast::(), size_of::() as u32, @@ -1059,23 +929,9 @@ impl Cage { }; let mut testlen = 128 as u32; - // let ret = unsafe { libc::getsockname(kernel_fd as i32, finalsockaddr, addrlen as *mut u32) }; - let ret = unsafe { libc::getsockname(kernel_fd as i32, finalsockaddr, &mut testlen as *mut u32) }; + let ret = unsafe { libc::getsockname(vfd.underfd as i32, finalsockaddr, &mut testlen as *mut u32) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[getsockname] Error message: {:?}", err_msg); - // println!("[getsockname] address: {:?}", address); - // println!("[getsockname] finalsockaddr: {:?}", finalsockaddr); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "getsockname"); } @@ -1113,36 +969,70 @@ impl Cage { pub fn poll_syscall( &self, virtual_fds: &mut [PollStruct], // lots of fds, a ptr - nfds: u64, + _nfds: u64, timeout: i32, ) -> i32 { - let mut real_fd = virtual_to_real_poll(self.cageid, virtual_fds); - let ret = unsafe { libc::poll(real_fd.as_mut_ptr(), nfds as u64, timeout) }; - if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[POLL] Error message: {:?}", err_msg); - // println!("[POLL] kernel fd: {:?}", real_fd); - // io::stdout().flush().unwrap(); - let errno = get_errno(); - return handle_errno(errno, "poll"); + + let mut virfdvec = HashSet::new(); + + for vpoll in &mut *virtual_fds { + let vfd = vpoll.fd as u64; + virfdvec.insert(vfd); } - // Convert back to PollStruct - for (i, libcpoll) in real_fd.iter().enumerate() { - if let Some(rposix_poll) = virtual_fds.get_mut(i) { - rposix_poll.revents = libcpoll.revents; + let (allhashmap, _mappingtable) = fdtables::convert_virtualfds_for_poll(self.cageid, virfdvec); + + let mut libc_nfds = 0; + let mut libc_pollfds: Vec = Vec::new(); + for (fd_kind, fdtuple) in allhashmap { + match fd_kind { + FDKIND_KERNEL => { + for (virtfd, _entry) in fdtuple { + if let Some(vpollstruct) = virtual_fds.iter().find(|&ps| ps.fd == virtfd as i32) { + // Convert PollStruct to libc::pollfd + let libcpollstruct = self.convert_to_libc_pollfd(vpollstruct); + libc_pollfds.push(libcpollstruct); + libc_nfds = libc_nfds + 1; + } + } + if libc_nfds != 0 { + let ret = unsafe { libc::poll(libc_pollfds.as_mut_ptr(), libc_nfds as u64, timeout) }; + if ret < 0 { + let errno = get_errno(); + return handle_errno(errno, "poll"); + } + // Convert back to PollStruct + for (i, libcpoll) in libc_pollfds.iter().enumerate() { + if let Some(rposix_poll) = virtual_fds.get_mut(i) { + rposix_poll.revents = libcpoll.revents; + } + } + + return ret; + } + }, + _ => { + /*TODO + Need to confirm the error num (we could add fdkind specific error..? eg: EFDKIND) + */ + return syscall_error(Errno::EBADFD, "poll", "Invalid fdkind"); + } } } + + // TODO: Return check...? + 0 - ret + } + + /* POLL() + */ + fn convert_to_libc_pollfd(&self, poll_struct: &PollStruct) -> pollfd { + pollfd { + fd: poll_struct.fd, + events: poll_struct.events, + revents: poll_struct.revents, + } } /* EPOLL @@ -1185,27 +1075,12 @@ impl Cage { let kernel_fd = unsafe { libc::epoll_create(size) }; if kernel_fd < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("Error message: {:?}", err_msg); - // println!("[EPOLL] size: {:?}", size); - // println!("[EPOLL] kernelfd: {:?}", kernel_fd); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "epoll_create"); } // Get the virtual epfd - let virtual_epfd = get_unused_virtual_fd(self.cageid, kernel_fd as u64, false, 0).unwrap(); - // println!("[epoll_create] virtual_epfd: {:?}", virtual_epfd); - // io::stdout().flush().unwrap(); + let virtual_epfd = fdtables::get_unused_virtual_fd(self.cageid, FDKIND_KERNEL, kernel_fd as u64, false, 0).unwrap(); // We don't need to update mapping table at now // Return virtual epfd @@ -1225,34 +1100,23 @@ impl Cage { virtual_fd: i32, epollevent: &mut EpollEvent, ) -> i32 { - let k_epfd = translate_virtual_fd(self.cageid, virtual_epfd as u64); - let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); - if kfd.is_err() || k_epfd.is_err() { + let wrappedepfd = fdtables::translate_virtual_fd(self.cageid, virtual_epfd as u64); + let wrappedvfd = fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64); + if wrappedvfd.is_err() || wrappedepfd.is_err() { return syscall_error(Errno::EBADF, "epoll", "Bad File Descriptor"); } - let kernel_epfd = k_epfd.unwrap(); - let kernel_fd = kfd.unwrap(); + let vepfd = wrappedepfd.unwrap(); + let vfd = wrappedvfd.unwrap(); // EpollEvent conversion let event = epollevent.events; let mut epoll_event = epoll_event { events: event, - u64: kernel_fd as u64, + u64: vfd.underfd as u64, }; - let ret = unsafe { libc::epoll_ctl(kernel_epfd as i32, op, kernel_fd as i32, &mut epoll_event) }; + let ret = unsafe { libc::epoll_ctl(vepfd.underfd as i32, op, vfd.underfd as i32, &mut epoll_event) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[epoll_ctl] Error message: {:?}", err_msg); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "epoll_ctl"); } @@ -1265,17 +1129,17 @@ impl Cage { // Update the mapping table for epoll if op == libc::EPOLL_CTL_DEL { let mut epollmapping = REAL_EPOLL_MAP.lock(); - if let Some(fdmap) = epollmapping.get_mut(&(virtual_epfd as u64)) { - if fdmap.remove(&(kernel_fd as i32)).is_some() { + if let Some(fdmap) = epollmapping.get_mut(&(vepfd.underfd as u64)) { + if fdmap.remove(&(vfd.underfd as i32)).is_some() { if fdmap.is_empty() { - epollmapping.remove(&(virtual_epfd as u64)); + epollmapping.remove(&(vepfd.underfd as u64)); } return ret; } } } else { let mut epollmapping = REAL_EPOLL_MAP.lock(); - epollmapping.entry(virtual_epfd as u64).or_insert_with(HashMap::new).insert(kernel_fd as i32, virtual_fd as u64); + epollmapping.entry(vepfd.underfd as u64).or_insert_with(HashMap::new).insert(vfd.underfd as i32, virtual_fd as u64); return ret; } @@ -1298,11 +1162,11 @@ impl Cage { maxevents: i32, timeout: i32, ) -> i32 { - let k_epfd = translate_virtual_fd(self.cageid, virtual_epfd as u64); - if k_epfd.is_err() { + let wrappedepfd = fdtables::translate_virtual_fd(self.cageid, virtual_epfd as u64); + if wrappedepfd.is_err() { return syscall_error(Errno::EBADF, "epoll_wait", "Bad File Descriptor"); } - let kernel_epfd = k_epfd.unwrap(); + let vepfd = wrappedepfd.unwrap(); let mut kernel_events: Vec = Vec::with_capacity(maxevents as usize); @@ -1314,19 +1178,8 @@ impl Cage { } ); - let ret = unsafe { libc::epoll_wait(kernel_epfd as i32, kernel_events.as_mut_ptr(), maxevents, timeout as i32) }; + let ret = unsafe { libc::epoll_wait(vepfd.underfd as i32, kernel_events.as_mut_ptr(), maxevents, timeout as i32) }; if ret < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("[epoll_wait] Error message: {:?}", err_msg); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "epoll_wait"); } @@ -1337,7 +1190,7 @@ impl Cage { let ret_kernelfd = kernel_events[i].u64; let epollmapping = REAL_EPOLL_MAP.lock(); - let ret_virtualfd = epollmapping.get(&(virtual_epfd as u64)).and_then(|kernel_map| kernel_map.get(&(ret_kernelfd as i32)).copied()); + let ret_virtualfd = epollmapping.get(&(vepfd.underfd as u64)).and_then(|kernel_map| kernel_map.get(&(ret_kernelfd as i32)).copied()); events[i].fd = ret_virtualfd.unwrap() as i32; events[i].events = kernel_events[i].events; @@ -1367,8 +1220,8 @@ impl Cage { let ksv_1 = kernel_socket_vector[0]; let ksv_2 = kernel_socket_vector[1]; - let vsv_1 = get_unused_virtual_fd(self.cageid, ksv_1 as u64, false, 0).unwrap(); - let vsv_2 = get_unused_virtual_fd(self.cageid, ksv_2 as u64, false, 0).unwrap(); + let vsv_1 = fdtables::get_unused_virtual_fd(self.cageid, FDKIND_KERNEL, ksv_1 as u64, false, 0).unwrap(); + let vsv_2 = fdtables::get_unused_virtual_fd(self.cageid, FDKIND_KERNEL, ksv_2 as u64, false, 0).unwrap(); virtual_socket_vector.sock1 = vsv_1 as i32; virtual_socket_vector.sock2 = vsv_2 as i32; return 0; @@ -1383,11 +1236,6 @@ impl Cage { unsafe { if getifaddrs(&mut ifaddr) < 0 { - // let err = libc::__errno_location(); - // let err_str = libc::strerror(*err); - // let err_msg = CStr::from_ptr(err_str).to_string_lossy().into_owned(); - // println!("Error message: {:?}", err_msg); - // io::stdout().flush().unwrap(); let errno = get_errno(); return handle_errno(errno, "getifaddrs"); } @@ -1422,29 +1270,3 @@ impl Cage { } -/* POLL() -*/ -pub fn virtual_to_real_poll(cageid: u64, virtual_poll: &mut [PollStruct]) -> Vec { - // Change from ptr to reference - // let virtual_fds = unsafe { &mut *virtual_poll }; - - let mut real_fds = Vec::with_capacity(virtual_poll.len()); - - for vfd in &mut *virtual_poll { - - let rfd = translate_virtual_fd(cageid, vfd.fd as u64); - if rfd.is_err() { - // return syscall_error(Errno::EBADF, "poll", "Bad File Descriptor"); - panic!(); - } - let real_fd = rfd.unwrap(); - let kernel_poll = pollfd { - fd: real_fd as i32, - events: vfd.events, - revents: vfd.revents, - }; - real_fds.push(kernel_poll); - } - - real_fds -} diff --git a/src/safeposix/syscalls/sys_calls.rs b/src/safeposix/syscalls/sys_calls.rs index bea5f03c..fbe1eed7 100644 --- a/src/safeposix/syscalls/sys_calls.rs +++ b/src/safeposix/syscalls/sys_calls.rs @@ -10,10 +10,7 @@ use crate::safeposix::cage; use crate::safeposix::cage::*; use crate::safeposix::shm::*; -// use crate::example_grates::vanillaglobal::*; -use crate::example_grates::dashmapvecglobal::*; -// use crate::example_grates::muthashmaxglobal::*; -// use crate::example_grates::dashmaparrayglobal::*; +use crate::fdtables; use libc::*; @@ -50,7 +47,7 @@ impl Cage { pub fn fork_syscall(&self, child_cageid: u64) -> i32 { // Modify the fdtable manually - copy_fdtable_for_cage(self.cageid, child_cageid).unwrap(); + fdtables::copy_fdtable_for_cage(self.cageid, child_cageid).unwrap(); // println!("[FORK]"); // io::stdout().flush().unwrap(); @@ -225,11 +222,11 @@ impl Cage { */ pub fn exec_syscall(&self, child_cageid: u64) -> i32 { // Empty fd with flag should_cloexec - empty_fds_for_exec(self.cageid); + fdtables::empty_fds_for_exec(self.cageid); // Add the new one to fdtable - let _ = copy_fdtable_for_cage(self.cageid, child_cageid); + let _ = fdtables::copy_fdtable_for_cage(self.cageid, child_cageid); // Delete the original one - let _newfdtable = remove_cage_from_fdtable(self.cageid); + let _newfdtable = fdtables::remove_cage_from_fdtable(self.cageid); interface::cagetable_remove(self.cageid); @@ -289,7 +286,7 @@ impl Cage { interface::flush_stdout(); self.unmap_shm_mappings(); - let _ = remove_cage_from_fdtable(self.cageid); + let _ = fdtables::remove_cage_from_fdtable(self.cageid); //may not be removable in case of lindrustfinalize, we don't unwrap the remove result interface::cagetable_remove(self.cageid); diff --git a/src/tests/fs_tests.rs b/src/tests/fs_tests.rs index afb5ca78..f66b389e 100644 --- a/src/tests/fs_tests.rs +++ b/src/tests/fs_tests.rs @@ -1,1404 +1,4787 @@ #[allow(unused_parens)] #[cfg(test)] pub mod fs_tests { + use super::super::*; - use crate::{interface, safeposix}; + use crate::fdtables::translate_virtual_fd; + use crate::interface; use crate::safeposix::syscalls::fs_calls::*; - // use crate::safeposix::{cage::*, dispatcher::*, filesystem}; - use crate::safeposix::{cage::*, dispatcher::*, shm}; + use crate::safeposix::{cage::*, dispatcher::*, filesystem}; + use libc::{c_void, O_DIRECTORY}; use std::fs::OpenOptions; use std::os::unix::fs::PermissionsExt; + use crate::interface::{StatData, FSData}; use libc::*; + use crate::interface::{ShmidsStruct, get_errno}; + pub use std::ffi::CStr as RustCStr; use std::mem; + use crate::fdtables::FDTABLE; - use std::io::Write; - use std::io; - use std::ptr; - use libc::*; - use std::ffi::CStr; - use crate::example_grates::translate_virtual_fd; - - use crate::tests::fs_tests::fs_tests::shm::SHM_METADATA; - use crate::interface::StatData; - - static S_IRWXA: u32 = 0o777; - static S_LIND: u32 = 0o755; - - pub fn test_fs() { - // ut_lind_fs_open(); - // ut_lind_fs_fork(); - // ut_lind_fs_simple(); // has to go first, else the data files created screw with link count test - // rdwrtest(); - - // ut_lind_fs_broken_close(); - // ut_lind_fs_chmod(); - // ut_lind_fs_fchmod(); - // ut_lind_fs_dir_chdir(); - // ut_lind_fs_dir_mode(); - // ut_lind_fs_dir_multiple(); - // ut_lind_fs_dup(); - // ut_lind_fs_dup2(); - // ut_lind_fs_fcntl(); - - // ut_lind_fs_ioctl(); - - // ut_lind_fs_fdflags(); - // ut_lind_fs_file_link_unlink(); - // ut_lind_fs_file_lseek_past_end(); - // ut_lind_fs_fstat_complex(); - // ut_lind_fs_getuid(); - // ut_lind_fs_load_fs(); - // ut_lind_fs_mknod(); - // ut_lind_fs_multiple_open(); - // ut_lind_fs_rename(); - // ut_lind_fs_rmdir(); - // ut_lind_fs_stat_file_complex(); - // ut_lind_fs_stat_file_mode(); - // ut_lind_fs_statfs(); - // ut_lind_fs_fstatfs(); - // ut_lind_fs_ftruncate(); - // ut_lind_fs_truncate(); - // ut_lind_fs_getdents(); - // ut_lind_fs_dir_chdir_getcwd(); - // prdwrtest(); - // chardevtest(); - // ut_lind_fs_exec_cloexec(); - // ut_lind_fs_shm(); - // ut_lind_fs_getpid_getppid(); - ut_lind_fs_sem_fork(); - // ut_lind_fs_sem_trytimed(); - // ut_lind_fs_sem_test(); - // ut_lind_fs_tmp_file_test(); - } - - // pub fn ut_lind_fs_open() { - // lindrustinit(0); - // let cage = interface::cagetable_getref(1); - // let fd = cage.open_syscall("/fcntl_file", O_RDWR | O_CREAT | O_EXCL, S_IWUSR); - // if fd < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("errno: {:?}", err); - // println!("Error message: {:?}", err_msg); - // println!("VirtualFD: {:?}", fd); - // io::stdout().flush().unwrap(); - // // panic!(); - // } - // assert_eq!(fd, 3); - - // let kernel_fd = translate_virtual_fd(1, fd).unwrap(); - // if cage.fcntl_syscall(fd, F_GETFD, 0) < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("errno: {:?}", err); - // println!("Error message: {:?}", err_msg); - // println!("VirtualFD: {:?}", fd); - // println!("KernelFD: {:?}", kernel_fd); - // io::stdout().flush().unwrap(); - // // panic!(); - // } - // assert_eq!(kernel_fd, 3); + #[test] + pub fn ut_lind_fs_simple() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); - // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); - // lindrustfinalize(); - // } + let cage = interface::cagetable_getref(1); - // pub fn ut_lind_fs_fork() { - // lindrustinit(0); - // let cage = interface::cagetable_getref(1); + // Create a test directory + let test_root = "/test_root"; + let res = cage.mkdir_syscall(test_root, 0o755); + assert!(res == 0 || res == -libc::EEXIST); + + // Verify access + assert_eq!(cage.access_syscall(test_root, F_OK), 0); + assert_eq!(cage.access_syscall(test_root, X_OK | R_OK), 0); + + let mut statdata2 = StatData::default(); + + // Get stats for the test directory + assert_eq!(cage.stat_syscall(test_root, &mut statdata2), 0); + + // Since the directory is newly created and empty, st_nlink should be 2 + assert_eq!(statdata2.st_nlink, 2); // . and .. + + // Check that st_size is greater than or equal to 4096 + assert!(statdata2.st_size >= 4096, "Expected st_size >= 4096, got {}", statdata2.st_size); + + // Clean up + assert_eq!(cage.rmdir_syscall(test_root), 0); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + + lindrustfinalize(); + } + + #[test] + pub fn rdwrtest() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let fd = cage.open_syscall("/foobar", O_CREAT | O_TRUNC | O_RDWR, S_IRWXA); + assert!(fd >= 0); + + assert_eq!(cage.write_syscall(fd, str2cbuf("hello there!"), 12), 12); + + assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); + let mut read_buf1 = sizecbuf(5); + assert_eq!(cage.read_syscall(fd, read_buf1.as_mut_ptr(), 5), 5); + assert_eq!(cbuf2str(&read_buf1), "hello"); + + assert_eq!(cage.write_syscall(fd, str2cbuf(" world"), 6), 6); + + assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); + let mut read_buf2 = sizecbuf(12); + assert_eq!(cage.read_syscall(fd, read_buf2.as_mut_ptr(), 12), 12); + assert_eq!(cbuf2str(&read_buf2), "hello world!"); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + + lindrustfinalize(); + } + + #[test] + pub fn prdwrtest() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let fd = cage.open_syscall("/foobar2", O_CREAT | O_TRUNC | O_RDWR, S_IRWXA); + assert!(fd >= 0); + + assert_eq!(cage.pwrite_syscall(fd, str2cbuf("hello there!"), 12, 0), 12); + + let mut read_buf1 = sizecbuf(5); + assert_eq!(cage.pread_syscall(fd, read_buf1.as_mut_ptr(), 5, 0), 5); + assert_eq!(cbuf2str(&read_buf1), "hello"); + + assert_eq!(cage.pwrite_syscall(fd, str2cbuf(" world"), 6, 5), 6); + + let mut read_buf2 = sizecbuf(12); + assert_eq!(cage.pread_syscall(fd, read_buf2.as_mut_ptr(), 12, 0), 12); + assert_eq!(cbuf2str(&read_buf2), "hello world!"); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + // #[test] + pub fn chardevtest() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let fd = cage.open_syscall("/dev/zero", O_RDWR, S_IRWXA); + assert!(fd >= 0); + + assert_eq!( + cage.pwrite_syscall( + fd, + str2cbuf("Lorem ipsum dolor sit amet, consectetur adipiscing elit"), + 55, + 0 + ), + 55 + ); + + let mut read_bufzero = sizecbuf(1000); + assert_eq!( + cage.pread_syscall(fd, read_bufzero.as_mut_ptr(), 1000, 0), + 1000 + ); + assert_eq!( + cbuf2str(&read_bufzero), + std::iter::repeat("\0") + .take(1000) + .collect::() + .as_str() + ); + + assert_eq!(cage.chdir_syscall("dev"), 0); + assert_eq!(cage.close_syscall(fd), 0); + + let fd2 = cage.open_syscall("./urandom", O_RDWR, S_IRWXA); + assert!(fd2 >= 0); + let mut read_bufrand = sizecbuf(1000); + assert_eq!( + cage.read_syscall(fd2, read_bufrand.as_mut_ptr(), 1000), + 1000 + ); + assert_eq!(cage.close_syscall(fd2), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_broken_close() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + //testing a muck up with the inode table where the regular close does not work + // as intended + + let cage = interface::cagetable_getref(1); + + //write should work + let mut fd = cage.open_syscall("/broken_close_file", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); + assert_eq!(cage.write_syscall(fd, str2cbuf("Hello There!"), 12), 12); + println!("fd1: {}", fd); + for entry in FDTABLE.iter() { + let (key, fd_array) = entry.pair(); + println!("Cage ID: {}", key); + for fd_entry in fd_array.iter().flatten() { // Flatten removes None elements + println!("{}", fd_entry.underfd); // Using Display trait + } + } + println!(""); - // // Fork child process - // assert_eq!(cage.fork_syscall(2), 0); - // // Child process - // let thread_child = interface::helper_thread(move || { - // interface::sleep(interface::RustDuration::from_millis(500)); - // let cage1 = interface::cagetable_getref(2); - // let fd1 = cage1.open_syscall("/foobar", O_RDWR, S_LIND); - // assert_eq!(cage1.lseek_syscall(fd1, 5, SEEK_SET), 5); - // assert_eq!(cage1.write_syscall(fd1, str2cbuf(" world"), 6), 6); - // assert_eq!(cage1.lseek_syscall(fd1, 0, SEEK_SET), 0); - // let mut read_buf2 = sizecbuf(12); - // assert_eq!(cage1.read_syscall(fd1, read_buf2.as_mut_ptr(), 12), 12); - // assert_eq!(cbuf2str(&read_buf2), "hello world!"); - // cage1.exit_syscall(libc::EXIT_SUCCESS); - // }); - // //Parent processes - // let thread_parent = interface::helper_thread(move || { - // let fd = cage.open_syscall("/foobar", O_CREAT | O_RDWR, S_LIND); - // assert!(fd >= 0); - // assert_eq!(cage.write_syscall(fd, str2cbuf("hello there!"), 12), 12); - - // assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); - // let mut read_buf1 = sizecbuf(5); - // assert_eq!(cage.read_syscall(fd, read_buf1.as_mut_ptr(), 5), 5); - // assert_eq!(cbuf2str(&read_buf1), "hello"); - // // interface::sleep(interface::RustDuration::from_millis(1000)); - // cage.exit_syscall(libc::EXIT_SUCCESS); - // }); - // thread_child.join().unwrap(); - // thread_parent.join().unwrap(); - // lindrustfinalize(); - // } + assert_eq!(cage.close_syscall(fd), 0); - // pub fn ut_lind_fs_simple() { - // lindrustinit(0); - // let cage = interface::cagetable_getref(1); - // assert_eq!(cage.access_syscall("/foobar", F_OK), 0); - // assert_eq!(cage.access_syscall("/foobar", X_OK | R_OK), 0); + println!("fd1: {}", fd); + for entry in FDTABLE.iter() { + let (key, fd_array) = entry.pair(); + println!("Cage ID: {}", key); + for fd_entry in fd_array.iter().flatten() { // Flatten removes None elements + println!("{}", fd_entry.underfd); // Using Display trait + } + } - // let mut statdata2 = StatData::default(); + //close the file and then open it again... and then close it again + fd = cage.open_syscall("/broken_close_file", O_RDWR, S_IRWXA); - // assert_eq!(cage.stat_syscall("/foobar", &mut statdata2), 0); - // //ensure that there are two hard links + assert_eq!(cage.close_syscall(fd), 0); - // assert_eq!(statdata2.st_nlink, 1); //2 for . and .., one for dev, and one so that it can never be removed + println!("\nfd2: {}", fd); + for entry in FDTABLE.iter() { + let (key, fd_array) = entry.pair(); + println!("Cage ID: {}", key); + for fd_entry in fd_array.iter().flatten() { // Flatten removes None elements + println!("{}", fd_entry.underfd); // Using Display trait + } + } - // //ensure that there is no associated size - // assert_eq!(statdata2.st_size, 4); - // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); - // lindrustfinalize(); - // } + //let's try some things with connect + //we are going to open a socket with a UDP specification... + let sockfd = cage.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, 0); - // pub fn rdwrtest() { - // println!("RDWTEST begin"); - // io::stdout().flush().unwrap(); + //bind should not be interesting + let mut sockad = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + sockad.set_family(libc::AF_INET as u16); + assert_eq!(cage.bind_syscall(sockfd, &sockad), 0); + fd = cage.open_syscall("/broken_close_file", O_RDWR, S_IRWXA); + assert_eq!(cage.close_syscall(fd), 0); - // lindrustinit(0); - // let cage = interface::cagetable_getref(1); + fd = cage.open_syscall("/broken_close_file", O_RDWR, S_IRWXA); + assert_eq!(cage.close_syscall(fd), 0); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_chmod_valid_args() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; + //checking if `chmod_syscall()` works with a relative path that includes only + // normal components, e.g. without `.` or `..` references + let filepath = "/chmodTestFile1"; + + let mut statdata = StatData::default(); + + //checking if the file was successfully created with the specified initial + // flags set all mode bits to 0 to change them later + let fd = cage.open_syscall(filepath, flags, 0); + assert_eq!(cage.stat_syscall(filepath, &mut statdata), 0); + assert_eq!(statdata.st_mode, S_IFREG as u32); + + //checking if owner read, write, and execute or search mode bits are correctly + // set + assert_eq!(cage.chmod_syscall(filepath, 0o400 | 0o200 | 0o100), 0); + assert_eq!(cage.stat_syscall(filepath, &mut statdata), 0); + assert_eq!( + statdata.st_mode, + 0o400 | 0o200 | 0o100 | S_IFREG as u32 + ); + + //resetting access mode bits + assert_eq!(cage.chmod_syscall(filepath, 0), 0); + + //checking if group owners read, write, and execute or search mode bits are + // correctly set + assert_eq!(cage.chmod_syscall(filepath, 0o040 | 0o020 | 0o010), 0); + assert_eq!(cage.stat_syscall(filepath, &mut statdata), 0); + assert_eq!( + statdata.st_mode, + 0o040 | 0o020 | 0o010 | S_IFREG as u32 + ); + + //resetting access mode bits + assert_eq!(cage.chmod_syscall(filepath, 0), 0); + + //checking if other users read, write, and execute or search mode bits are + // correctly set + assert_eq!(cage.chmod_syscall(filepath, 0o004 | 0o002 | 0o001), 0); + assert_eq!(cage.stat_syscall(filepath, &mut statdata), 0); + assert_eq!( + statdata.st_mode, + 0o004 | 0o002 | 0o001 | S_IFREG as u32 + ); + + assert_eq!(cage.close_syscall(fd), 0); + + //checking if `chmod_syscall()` works with relative path that include parent + // directory reference + let newdir = "../testFolder"; + assert_eq!(cage.mkdir_syscall(newdir, S_IRWXA), 0); + let filepath = "../testFolder/chmodTestFile"; + + //checking if the file was successfully created with the specified initial + // flags set all mode bits to 0 to set them later + let fd = cage.open_syscall(filepath, flags, 0); + assert_eq!(cage.stat_syscall(filepath, &mut statdata), 0); + assert_eq!(statdata.st_mode, S_IFREG as u32); + + //checking if owner, group owners, and other users read, write, and execute or + // search mode bits are correctly set + assert_eq!(cage.chmod_syscall(filepath, S_IRWXA), 0); + assert_eq!(cage.stat_syscall(filepath, &mut statdata), 0); + assert_eq!(statdata.st_mode, S_IRWXA | S_IFREG as u32); + + assert_eq!(cage.close_syscall(fd), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_chmod_invalid_args() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //checking if passing a nonexistent pathname to `chmod_syscall()` + //correctly results in `A component of path does not name an existing file` + // error + let invalidpath = "/someInvalidPath/testFile"; + assert_eq!( + cage.chmod_syscall(invalidpath, 0o100 | 0o400 | 0o100), + -(Errno::ENOENT as i32) + ); + + //checking if passing an invalid set of mod bits to `chmod_syscall()` + //correctly results in `The value of the mode argument is invalid` error + let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; + let filepath = "/chmodTestFile2"; + let mut statdata = StatData::default(); + let fd = cage.open_syscall(filepath, flags, 0o755); + assert_eq!(cage.stat_syscall(filepath, &mut statdata), 0); + assert_eq!(statdata.st_mode, 0o755 | S_IFREG as u32); + //0o7777 is an arbitrary value that does not correspond to any combination of + // valid mode bits + /* The extra bits are special permission bits in linux */ + assert_eq!( + cage.chmod_syscall(filepath, 0o7777 as u32), + 0 + ); + + assert_eq!(cage.close_syscall(fd), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_fchmod() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; + //checking if `fchmod_syscall()` works with a valid file descriptor + let filepath = "/fchmodTestFile1"; + + let mut statdata = StatData::default(); + + //checking if the file was successfully created with the specified initial + // flags set all mode bits to 0 to change them later + let fd = cage.open_syscall(filepath, flags, 0); + assert_eq!(cage.fstat_syscall(fd, &mut statdata), 0); + assert_eq!(statdata.st_mode, S_IFREG as u32); + + //checking if owner, group owners, and other users read, write, and execute or + // search mode bits are correctly set + assert_eq!(cage.fchmod_syscall(fd, S_IRWXA), 0); + assert_eq!(cage.fstat_syscall(fd, &mut statdata), 0); + assert_eq!(statdata.st_mode, S_IRWXA | S_IFREG as u32); + + //checking if passing an invalid set of mod bits to `fchmod_syscall()` + //correctly results in `The value of the mode argument is invalid` error + //0o7777 is an arbitrary value that does not correspond to any combination of + // valid mode bits or supported file types + /* The extra bits are special permission bits in native linux */ + assert_eq!( + cage.fchmod_syscall(fd, 0o7777 as u32), + 0 + ); + + //checking if passing an invalid file descriptor to `fchmod_syscall` correctly + //results in `Invalid file descriptor` error. + //closing a previously opened file would make its file descriptor unused, and + //thus, invalid as `fchmod_syscall()` fd argument + assert_eq!(cage.close_syscall(fd), 0); + assert_eq!(cage.fchmod_syscall(fd, S_IRWXA), -(Errno::EBADF as i32)); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_mmap_zerolen() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //Creating a regular file with `O_RDWR` flag + //making it valid for any mapping. + let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; + let filepath = "/mmapTestFile1"; + let fd = cage.open_syscall(filepath, flags, S_IRWXA); + //Writing into that file's first 9 bytes. + assert_eq!(cage.write_syscall(fd, str2cbuf("Test text"), 9), 9); + + //Checking if passing 0 as `len` to `mmap_syscall()` + //correctly results in 'The value of len is 0` error. + assert_eq!( + cage.mmap_syscall(0 as *mut u8, 0, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0), + -(Errno::EINVAL as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_mmap_invalid_flags_none() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //Creating a regular file with `O_RDWR` flag + //making it valid for any mapping. + let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; + let filepath = "/mmapTestFile1"; + let fd = cage.open_syscall(filepath, flags, S_IRWXA); + //Writing into that file's first 9 bytes. + assert_eq!(cage.write_syscall(fd, str2cbuf("Test text"), 9), 9); + + let mmap_result = cage.mmap_syscall(0 as *mut u8, 5, PROT_READ | PROT_WRITE, 0, fd, 0); + assert_eq!(mmap_result, -1, "mmap did not fail as expected"); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + + #[test] + pub fn ut_lind_fs_mmap_invalid_flags_both() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //Creating a regular file with `O_RDWR` flag + //making it valid for any mapping. + let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; + let filepath = "/mmapTestFile1"; + let fd = cage.open_syscall(filepath, flags, S_IRWXA); + //Writing into that file's first 9 bytes. + assert_eq!(cage.write_syscall(fd, str2cbuf("Test text"), 9), 9); + + //Checking if passing both `MAP_PRIVATE` + //and `MAP_SHARED` flags correctly results in `The value + //of flags is invalid (`MAP_PRIVATE` and `MAP_SHARED` + //cannot be both set)` error. + assert!( + cage.mmap_syscall( + 0 as *mut u8, + 5, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_SHARED, + fd, + 0 + ) < 0 + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_mmap_no_read() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //Creating a regular file without a reading flag + //making it invalid for any mapping. + let flags: i32 = O_TRUNC | O_CREAT | O_WRONLY; + let filepath = "/mmapTestFile1"; + let fd = cage.open_syscall(filepath, flags, S_IRWXA); + //Writing into that file's first 9 bytes. + assert_eq!(cage.write_syscall(fd, str2cbuf("Test text"), 9), 9); + + //Checking if trying to map a file that does not + //allow reading correctly results in `File descriptor + //is not open for reading` error. + assert_eq!( + cage.mmap_syscall(0 as *mut u8, 5, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0), + -(Errno::EACCES as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_mmap_no_write() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //Creating a regular file with flags for + //reading and writing + let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; + let filepath = "/mmapTestFile1"; + let fd = cage.open_syscall(filepath, flags, S_IRWXA); + //Writing into that file's first 9 bytes. + assert_eq!(cage.write_syscall(fd, str2cbuf("Test text"), 9), 9); + + //Opening a file descriptor for the same file + //but now with a read flag and without a write + //flag making it invalid for shared mapping with + //write protection flag. + let testflags: i32 = O_RDONLY; + let testfd = cage.open_syscall(filepath, testflags, 0); + + //Checking if trying to map a file that does not + //allow writing for shared mapping with writing + //protection flag set correctly results in + //``MAP_SHARED` was requested and PROT_WRITE is + //set, but fd is not open in read/write (`O_RDWR`) + //mode` error. + assert_eq!( + cage.mmap_syscall( + 0 as *mut u8, + 5, + PROT_READ | PROT_WRITE, + MAP_SHARED, + testfd, + 0 + ), + -(Errno::EACCES as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_mmap_invalid_offset_len() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //Creating a regular file with `O_RDWR` flag + //making it valid for any mapping. + let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; + let filepath = "/mmapTestFile1"; + let fd = cage.open_syscall(filepath, flags, S_IRWXA); + //Writing into that file's first 9 bytes. + assert_eq!(cage.write_syscall(fd, str2cbuf("Test text"), 9), 9); + + //Checking if passing a negative offset correctly + //results in `Addresses in the range [off,off+len) + //are invalid for the object specified by `fildes`` error. + + /* Native linux will return EINVAL - TESTED locally */ + assert_eq!( + cage.mmap_syscall(0 as *mut u8, 5, PROT_READ | PROT_WRITE, MAP_SHARED, fd, -10), + -(Errno::EINVAL as i32) + ); + + //Checking if passing an offset that seeks beyond the end + //of the file correctly results in `Addresses in the + //range [off,off+len) are invalid for the object specified + //by `fildes`` error. + + /* Native linux will return EINVAL - TESTED locally */ + assert_eq!( + cage.mmap_syscall(0 as *mut u8, 5, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 25), + -(Errno::EINVAL as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_mmap_chardev() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //Opening a character device file `/dev/zero`. + let fd = cage.open_syscall("/dev/zero", O_RDWR, S_IRWXA); + //Writing into that file's first 9 bytes. + assert_eq!(cage.write_syscall(fd, str2cbuf("Test text"), 9), 9); + + //Checking if calling `mmap_syscall()` on the character device + //file correctly results in `Lind currently does not support + //mapping character files` error. + + /* will succeed in native linux - TESTED locally */ + assert!( + cage.mmap_syscall(0 as *mut u8, 5, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0) < 0 + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_mmap_unsupported_file() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //Creating a directory. + assert_eq!(cage.mkdir_syscall("/testdir", S_IRWXA), 0); + + /* Native linux require specific flags to open a dir */ + let fd = cage.open_syscall("/testdir", O_RDONLY | O_DIRECTORY, S_IRWXA); - // let fd = cage.open_syscall( - // "/foobar", - // O_CREAT | O_TRUNC | O_RDWR, - // (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH) as u32); - // if fd < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("errno: {:?}", err); - // println!("Error message: {:?}", err_msg); - // println!("VirtualFD: {:?}", fd); - // io::stdout().flush().unwrap(); - // // panic!(); - // } - // println!("open finished"); - // io::stdout().flush().unwrap(); - // assert_eq!(cage.write_syscall(fd, str2cbuf("hello there!"), 12), 12); + //Checking if passing the created directory to + //`mmap_syscall()` correctly results in `The `fildes` + //argument refers to a file whose type is not + //supported by mmap` error. - // println!("write finished"); - // io::stdout().flush().unwrap(); + /* Native linux will return ENODEV */ + assert_eq!( + cage.mmap_syscall(0 as *mut u8, 5, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0), + -(Errno::ENODEV as i32) + ); - // assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); - // let mut read_buf1 = sizecbuf(5); - // assert_eq!(cage.read_syscall(fd, read_buf1.as_mut_ptr(), 5), 5); - // assert_eq!(cbuf2str(&read_buf1), "hello"); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // assert_eq!(cage.write_syscall(fd, str2cbuf(" world"), 6), 6); + #[test] + pub fn ut_lind_fs_mmap_invalid_fildes() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //Creating a regular file with `O_RDWR` flag + //making it valid for any mapping and then + //closing it, thereby making the obtained + //filede scriptor invalid because no other + //file is opened after it. + let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; + let filepath = "/mmapTestFile1"; + let fd = cage.open_syscall(filepath, flags, S_IRWXA); + assert_eq!(cage.close_syscall(fd), 0); + + //Checking if passing the invalid file descriptor + //correctly results in `Invalid file descriptor` error. + assert_eq!( + cage.mmap_syscall(0 as *mut u8, 5, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0), + -(Errno::EBADF as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); - // let mut read_buf2 = sizecbuf(12); - // assert_eq!(cage.read_syscall(fd, read_buf2.as_mut_ptr(), 12), 12); - // assert_eq!(cbuf2str(&read_buf2), "hello world!"); + #[test] + pub fn ut_lind_fs_munmap_zerolen() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //Creating a regular file with `O_RDWR` flag + //making it valid for any mapping. + let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; + let filepath = "/mmapTestFile1"; + let fd = cage.open_syscall(filepath, flags, S_IRWXA); + //Writing into that file's first 9 bytes. + assert_eq!(cage.write_syscall(fd, str2cbuf("Test text"), 9), 9); + + //Checking if passing 0 as `len` to `munmap_syscall()` + //correctly results in 'The value of len is 0` error. + assert_eq!( + cage.munmap_syscall(0 as *mut u8, 0), + -(Errno::EINVAL as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // println!("read finished"); - // io::stdout().flush().unwrap(); + #[test] + pub fn ut_lind_fs_chdir_valid_args() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //Testing the ability to make and change to directories + //using absolute and relative and `..` reference + + assert_eq!(cage.mkdir_syscall("/subdir1", S_IRWXA), 0); + assert_eq!(cage.mkdir_syscall("/subdir1/subdir2", S_IRWXA), 0); + + //Changing to a new current working directory, and then obtaining + //the current working directory using `getcwd_syscall()` to see + //if it was correctly changed + assert_eq!(cage.chdir_syscall("subdir1"), 0); + let mut buf1 = vec![0u8; 9]; + let bufptr1: *mut u8 = &mut buf1[0]; + assert_eq!(cage.getcwd_syscall(bufptr1, 9), 0); + assert_eq!(std::str::from_utf8(&buf1).unwrap(), "/subdir1\0"); + + assert_eq!(cage.chdir_syscall("/subdir1/subdir2"), 0); + assert_eq!(cage.chdir_syscall(".."), 0); + let mut buf1 = vec![0u8; 9]; + let bufptr1: *mut u8 = &mut buf1[0]; + assert_eq!(cage.getcwd_syscall(bufptr1, 9), 0); + assert_eq!(std::str::from_utf8(&buf1).unwrap(), "/subdir1\0"); + + // Cleanup: Remove the directories + assert_eq!(cage.rmdir_syscall("/subdir1/subdir2"), 0); + assert_eq!(cage.rmdir_syscall("/subdir1"), 0); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + #[test] + pub fn ut_lind_fs_chdir_removeddir() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //Checking if removing the current working directory + //works correctly + assert_eq!(cage.mkdir_syscall("/subdir1", S_IRWXA), 0); + assert_eq!(cage.mkdir_syscall("/subdir2", S_IRWXA), 0); + assert_eq!(cage.chdir_syscall("subdir1"), 0); + assert_eq!(cage.rmdir_syscall("/subdir1"), 0); + assert_eq!(cage.chdir_syscall("/subdir2"), 0); + assert_eq!(cage.chdir_syscall("subdir1"), -(Errno::ENOENT as i32)); + + // Cleanup: Remove /subdir2 + assert_eq!(cage.rmdir_syscall("/subdir2"), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // println!("exit finished"); - // io::stdout().flush().unwrap(); + #[test] + pub fn ut_lind_fs_chdir_invalid_args() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + //and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; + let filepath = "/TestFile1"; + let _fd1 = cage.open_syscall(filepath, flags, 0); + + //Checking if passing a regular file pathname correctly + //returns `The last component in path is not a directory` error + assert_eq!(cage.chdir_syscall("/TestFile1"), -(Errno::ENOTDIR as i32)); + + //Checking if a nonexistent pathname correctly + //returns `The directory referred to in path does not exist` error. + //`/arbitrarypath` is a pathname that does not correspond to any existing + //directory pathname. + assert_eq!( + cage.chdir_syscall("/arbitrarypath"), + -(Errno::ENOENT as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // lindrustfinalize(); - // } + #[test] + pub fn ut_lind_fs_fchdir_valid_args() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //Testing the ability to make and change to directories + //using file descriptors + + assert_eq!(cage.mkdir_syscall("/subdir1", S_IRWXA), 0); + assert_eq!(cage.mkdir_syscall("/subdir1/subdir2", S_IRWXA), 0); + + //Retrieving a valid directory file descriptor + + /* Native linux require specific flags to open a dir */ + let fd1 = cage.open_syscall("/subdir1", O_RDONLY | O_DIRECTORY, S_IRWXA); + let fd2 = cage.open_syscall("/subdir1/subdir2", O_RDONLY | O_DIRECTORY, S_IRWXA); + + //Changing to a new current working directory, and then obtaining + //the current working directory using `getcwd_syscall()` to see + //if it was correctly changed + assert_eq!(cage.access_syscall("subdir1", F_OK), 0); + assert_eq!(cage.fchdir_syscall(fd1), 0); + let mut buf1 = vec![0u8; 9]; + let bufptr1: *mut u8 = &mut buf1[0]; + assert_eq!(cage.getcwd_syscall(bufptr1, 9), 0); + assert_eq!(std::str::from_utf8(&buf1).unwrap(), "/subdir1\0"); + + assert_eq!(cage.access_syscall("subdir2", F_OK), 0); + assert_eq!(cage.fchdir_syscall(fd2), 0); + let mut buf2 = vec![0u8; 17]; + let bufptr2: *mut u8 = &mut buf2[0]; + assert_eq!(cage.getcwd_syscall(bufptr2, 17), 0); + assert_eq!(std::str::from_utf8(&buf2).unwrap(), "/subdir1/subdir2\0"); + + // Cleanup: Remove /subdir1/subdir2 and /subdir1 after the test + assert_eq!(cage.rmdir_syscall("/subdir1/subdir2"), 0); + assert_eq!(cage.rmdir_syscall("/subdir1"), 0); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // pub fn prdwrtest() { - // lindrustinit(0); - // let cage = interface::cagetable_getref(1); + #[test] + pub fn ut_lind_fs_fchdir_invalid_args() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + //and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; + let filepath = "/TestFile1"; + let _ = cage.unlink_syscall(filepath); + let fd1 = cage.open_syscall(filepath, flags, 0); + + //Checking if passing a regular file descriptor correctly + //returns `The last component in path is not a directory` error + assert_eq!(cage.fchdir_syscall(fd1), -(Errno::ENOTDIR as i32)); + + //Checking if passing an invalid file descriptor correctly + //results in `Invalid file descriptor` error + //Since the file corresponding to file descriptor `fd1` is closed, + //and no other file is opened after that, `fd1` file descriptor + //should be invalid + assert_eq!(cage.close_syscall(fd1), 0); + assert_eq!(cage.fchdir_syscall(fd1), -(Errno::EBADF as i32)); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // let fd = cage.open_syscall("/foobar2", O_CREAT | O_TRUNC | O_RDWR, (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH) as u32); - // assert!(fd >= 0); + #[test] + pub fn ut_lind_fs_dir_mode() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let filepath1 = "/subdirDirMode1"; + let filepath2 = "/subdirDirMode2"; + + let mut statdata = StatData::default(); + assert_eq!(cage.mkdir_syscall(filepath1, S_IRWXA), 0); + assert_eq!(cage.stat_syscall(filepath1, &mut statdata), 0); + assert_eq!(statdata.st_mode, 0o755 | S_IFDIR as u32); + + assert_eq!(cage.mkdir_syscall(filepath2, 0), 0); + assert_eq!(cage.stat_syscall(filepath2, &mut statdata), 0); + assert_eq!(statdata.st_mode, S_IFDIR as u32); + // Cleanup: Remove the directories + assert_eq!(cage.rmdir_syscall(filepath1), 0); + assert_eq!(cage.rmdir_syscall(filepath2), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // assert_eq!(cage.pwrite_syscall(fd, str2cbuf("hello there!"), 12, 0), 12); + #[test] + pub fn ut_lind_fs_dir_multiple() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + assert_eq!(cage.mkdir_syscall("/subdirMultiple1", S_IRWXA), 0); + assert_eq!( + cage.mkdir_syscall("/subdirMultiple1/subdirMultiple2", S_IRWXA), + 0 + ); + assert_eq!( + cage.mkdir_syscall("/subdirMultiple1/subdirMultiple2/subdirMultiple3", 0), + 0 + ); + + let mut statdata = StatData::default(); + + //ensure that the file is a dir with all of the correct bits on for nodes + assert_eq!( + cage.stat_syscall("/subdirMultiple1/subdirMultiple2", &mut statdata), + 0 + ); + assert_eq!(statdata.st_mode, 0o755 | S_IFDIR as u32); + + assert_eq!( + cage.stat_syscall( + "/subdirMultiple1/subdirMultiple2/subdirMultiple3", + &mut statdata + ), + 0 + ); + assert_eq!(statdata.st_mode, S_IFDIR as u32); + // Cleanup: Remove the directories + assert_eq!(cage.rmdir_syscall("/subdirMultiple1/subdirMultiple2/subdirMultiple3"), 0); + assert_eq!(cage.rmdir_syscall("/subdirMultiple1/subdirMultiple2"), 0); + assert_eq!(cage.rmdir_syscall("/subdirMultiple1"), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // let mut read_buf1 = sizecbuf(5); - // assert_eq!(cage.pread_syscall(fd, read_buf1.as_mut_ptr(), 5, 0), 5); - // assert_eq!(cbuf2str(&read_buf1), "hello"); + #[test] + pub fn ut_lind_fs_dup() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; + let filepath = "/dupfile"; + + let fd = cage.open_syscall(filepath, flags, S_IRWXA); + let mut temp_buffer = sizecbuf(2); + assert!(fd >= 0); + assert_eq!(cage.write_syscall(fd, str2cbuf("12"), 2), 2); + assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); + assert_eq!(cage.read_syscall(fd, temp_buffer.as_mut_ptr(), 2), 2); + assert_eq!(cbuf2str(&temp_buffer), "12"); + + //duplicate the file descriptor + let fd2 = cage.dup_syscall(fd, None); + assert!(fd != fd2); + + //essentially a no-op, but duplicate again -- they should be diff &fd's + let fd3 = cage.dup_syscall(fd, None); + assert!(fd != fd2 && fd != fd3); + + //We don't need all three, though: + assert_eq!(cage.close_syscall(fd3), 0); + + assert_eq!(cage.lseek_syscall(fd, 0, SEEK_END), 2); + assert_eq!(cage.lseek_syscall(fd2, 0, SEEK_END), 2); + + // write some data to move the first position + assert_eq!(cage.write_syscall(fd, str2cbuf("34"), 2), 2); + + //Make sure that they are still in the same place: + let mut buffer = sizecbuf(4); + assert_eq!( + cage.lseek_syscall(fd, 0, SEEK_SET), + cage.lseek_syscall(fd2, 0, SEEK_SET) + ); + assert_eq!(cage.read_syscall(fd, buffer.as_mut_ptr(), 4), 4); + assert_eq!(cbuf2str(&buffer), "1234"); + + assert_eq!(cage.close_syscall(fd), 0); + + //the other &fd should still work + assert_eq!(cage.lseek_syscall(fd2, 0, SEEK_END), 4); + assert_eq!(cage.write_syscall(fd2, str2cbuf("5678"), 4), 4); + + assert_eq!(cage.lseek_syscall(fd2, 0, SEEK_SET), 0); + let mut buffer2 = sizecbuf(8); + assert_eq!(cage.read_syscall(fd2, buffer2.as_mut_ptr(), 8), 8); + assert_eq!(cage.close_syscall(fd2), 0); + assert_eq!(cbuf2str(&buffer2), "12345678"); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + #[test] + fn ut_lind_fs_dup_invalid_fd() { + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); - // assert_eq!(cage.pwrite_syscall(fd, str2cbuf(" world"), 6, 5), 6); + // Open a file and get a valid file descriptor + let fd = cage.open_syscall("/testfile", O_CREAT | O_WRONLY, S_IRWXA); + assert_ne!(fd, -(Errno::ENOENT as i32)); - // let mut read_buf2 = sizecbuf(12); - // assert_eq!(cage.pread_syscall(fd, read_buf2.as_mut_ptr(), 12, 0), 12); - // assert_eq!(cbuf2str(&read_buf2), "hello world!"); + // Close the file descriptor, making it invalid + assert_eq!(cage.close_syscall(fd), 0); - // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); - // lindrustfinalize(); - // } + // Attempt to duplicate the invalid file descriptor + let new_fd = cage.dup_syscall(fd, None); + assert_eq!(new_fd, -(Errno::EBADF as i32)); -// pub fn chardevtest() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); - -// let fd = cage.open_syscall("/dev/zero", O_RDWR, S_IRWXA); -// assert!(fd >= 0); - -// assert_eq!( -// cage.pwrite_syscall( -// fd, -// str2cbuf("Lorem ipsum dolor sit amet, consectetur adipiscing elit"), -// 55, -// 0 -// ), -// 55 -// ); - -// let mut read_bufzero = sizecbuf(1000); -// assert_eq!( -// cage.pread_syscall(fd, read_bufzero.as_mut_ptr(), 1000, 0), -// 1000 -// ); -// assert_eq!( -// cbuf2str(&read_bufzero), -// std::iter::repeat("\0") -// .take(1000) -// .collect::() -// .as_str() -// ); - -// assert_eq!(cage.chdir_syscall("dev"), 0); -// assert_eq!(cage.close_syscall(fd), 0); - -// let fd2 = cage.open_syscall("./urandom", O_RDWR, S_IRWXA); -// assert!(fd2 >= 0); -// let mut read_bufrand = sizecbuf(1000); -// assert_eq!( -// cage.read_syscall(fd2, read_bufrand.as_mut_ptr(), 1000), -// 1000 -// ); -// assert_eq!(cage.close_syscall(fd2), 0); -// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } - - // pub fn ut_lind_fs_broken_close() { - // //testing a muck up with the inode table where the regular close does not work as intended - - // lindrustinit(0); - // let cage = interface::cagetable_getref(1); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // //write should work - // let mut fd = cage.open_syscall("/broken_close_file", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); - // assert_eq!(cage.write_syscall(fd, str2cbuf("Hello There!"), 12), 12); - // assert_eq!(cage.close_syscall(fd), 0); + #[test] + fn ut_lind_fs_dup_full_table() { + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); - // //close the file and then open it again... and then close it again - // fd = cage.open_syscall("/broken_close_file", O_RDWR, S_IRWXA); - // assert_eq!(cage.close_syscall(fd), 0); + // Open a large number of files to fill the file descriptor table + for i in 0..1021 { + let fd = cage.open_syscall(&format!("/testfile{}", i), O_CREAT | O_WRONLY, S_IRWXA); + assert_ne!(fd, -(Errno::EMFILE as i32)); + } - // //let's try some things with connect - // //we are going to open a socket with a UDP specification... - // // let sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + // Attempt to duplicate a file descriptor, which should fail + let fd = cage.open_syscall("/testfile", O_CREAT | O_WRONLY, S_IRWXA); + assert_eq!(fd, -(Errno::EMFILE as i32)); + let new_fd = cage.dup_syscall(fd, None); + assert_eq!(new_fd, -(Errno::EBADF as i32)); - // // //bind should not be interesting - // // let mut sockad = interface::GenSockaddr::V4(interface::SockaddrV4::default()); - // // sockad.set_family(AF_INET as u16); - // // assert_eq!(cage.bind_syscall(sockfd, &sockad), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // fd = cage.open_syscall( - // "/broken_close_file", - // O_RDWR, - // (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH) as u32); - // assert_eq!(cage.close_syscall(fd), 0); + #[test] + pub fn ut_lind_fs_dup2() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); - // fd = cage.open_syscall("/broken_close_file", O_RDWR, S_IRWXA); - // assert_eq!(cage.close_syscall(fd), 0); + let cage = interface::cagetable_getref(1); - // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); - // lindrustfinalize(); - // } + let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; + let filepath = "/dup2file"; - // pub fn ut_lind_fs_chmod() { - // lindrustinit(0); - // let cage = interface::cagetable_getref(1); + let fd = cage.open_syscall(filepath, flags, 0o755); - // let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; - // let filepath = "/chmodTestFile"; + assert_eq!(cage.write_syscall(fd, str2cbuf("12"), 2), 2); - // let mut statdata: stat = unsafe { std::mem::zeroed() }; + //trying to dup fd into fd + 1 + let _fd2: i32 = cage.dup2_syscall(fd, fd + 1 as i32); - // let fd = cage.open_syscall(filepath, flags, S_LIND); - // assert_eq!(cage.stat_syscall(filepath, &mut statdata), 0); - // assert_eq!(statdata.st_mode, S_LIND | S_IFREG); - // // assert_eq!(statdata.st_mode & !S_IFMT, S_IRWXA); + //should be a no-op since the last line did the same thing + let fd2: i32 = cage.dup2_syscall(fd, fd + 1 as i32); - // assert_eq!(cage.chmod_syscall(filepath, S_IRUSR | S_IRGRP), 0); - // assert_eq!(cage.stat_syscall(filepath, &mut statdata), 0); - // assert_eq!(statdata.st_mode, S_IRUSR | S_IRGRP | S_IFREG); + //read/write tests for the files + assert_eq!( + cage.lseek_syscall(fd, 0, SEEK_END), + cage.lseek_syscall(fd2, 0, SEEK_END) + ); + assert_eq!(cage.write_syscall(fd, str2cbuf("34"), 2), 2); + assert_eq!( + cage.lseek_syscall(fd, 0, SEEK_SET), + cage.lseek_syscall(fd2, 0, SEEK_SET) + ); - // assert_eq!(cage.chmod_syscall(filepath, S_LIND), 0); - // assert_eq!(cage.stat_syscall(filepath, &mut statdata), 0); - // assert_eq!(statdata.st_mode, S_LIND | S_IFREG as u32); + let mut buffer = sizecbuf(4); + assert_eq!(cage.lseek_syscall(fd2, 0, SEEK_SET), 0); + assert_eq!(cage.read_syscall(fd, buffer.as_mut_ptr(), 4), 4); + assert_eq!(cbuf2str(&buffer), "1234"); - // assert_eq!(cage.close_syscall(fd), 0); - // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); - // lindrustfinalize(); - // } + assert_eq!(cage.close_syscall(fd), 0); - // pub fn ut_lind_fs_fchmod() { - // lindrustinit(0); - // let cage = interface::cagetable_getref(1); + let mut buffer2 = sizecbuf(8); + assert_eq!(cage.lseek_syscall(fd2, 0, SEEK_END), 4); + assert_eq!(cage.write_syscall(fd2, str2cbuf("5678"), 4), 4); - // let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; - // let filepath = "/fchmodTestFile"; + assert_eq!(cage.lseek_syscall(fd2, 0, SEEK_SET), 0); + assert_eq!(cage.read_syscall(fd2, buffer2.as_mut_ptr(), 8), 8); + assert_eq!(cbuf2str(&buffer2), "12345678"); - // let mut statdata: stat = unsafe { std::mem::zeroed() }; + assert_eq!(cage.close_syscall(fd2), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // let fd = cage.open_syscall(filepath, flags, S_LIND); - // assert_eq!(cage.fstat_syscall(fd, &mut statdata), 0); - // assert_eq!(statdata.st_mode, S_LIND | S_IFREG as u32); + #[test] + fn ut_lind_fs_dup2_invalid_fd() { + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); - // assert_eq!(cage.fchmod_syscall(fd, S_IRUSR | S_IRGRP), 0); - // assert_eq!(cage.fstat_syscall(fd, &mut statdata), 0); - // assert_eq!(statdata.st_mode, S_IRUSR | S_IRGRP | S_IFREG as u32); + // Open a file + let fd = cage.open_syscall("/testfile", O_CREAT | O_WRONLY, S_IRWXA); + assert_ne!(fd, -(Errno::ENOENT as i32)); - // assert_eq!(cage.fchmod_syscall(fd, S_LIND), 0); - // assert_eq!(cage.fstat_syscall(fd, &mut statdata), 0); - // assert_eq!(statdata.st_mode, S_LIND | S_IFREG as u32); + // Close the file descriptor, making it invalid + assert_eq!(cage.close_syscall(fd), 0); - // assert_eq!(cage.close_syscall(fd), 0); - // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); - // lindrustfinalize(); - // } + // Attempt to duplicate the invalid file descriptor + let new_fd = cage.dup2_syscall(fd, 5); + assert_eq!(new_fd, -(Errno::EBADF as i32)); - // pub fn ut_lind_fs_dir_chdir() { - // lindrustinit(0); - // let cage = interface::cagetable_getref(1); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // //testing the ability to make and change to directories + #[test] + fn ut_lind_fs_dup2_full_table() { + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); - // assert_eq!(cage.mkdir_syscall("/subdir1", S_LIND), 0); - // assert_eq!(cage.mkdir_syscall("/subdir1/subdir2", S_LIND), 0); - // assert_eq!(cage.mkdir_syscall("/subdir1/subdir2/subdir3", S_LIND), 0); + // Open a large number of files to fill the file descriptor table + for i in 0..1024 { + let fd = cage.open_syscall(&format!("/testfile{}", i), O_CREAT | O_WRONLY, S_IRWXA); + assert_ne!(fd, -(Errno::ENOENT as i32)); + } - // assert_eq!(cage.access_syscall("/subdir1", F_OK), 0); - // assert_eq!(cage.chdir_syscall("/subdir1"), 0); + // Attempt to duplicate a file descriptor, which should fail + let fd = cage.open_syscall("/testfile", O_CREAT | O_WRONLY, S_IRWXA); + assert_ne!(fd, -(Errno::ENOENT as i32)); + let new_fd = cage.dup2_syscall(fd, 5); // Try to duplicate to an existing fd + assert_eq!(new_fd, -(Errno::EBADF as i32)); - // assert_eq!(cage.access_syscall("/subdir1/subdir2", F_OK), 0); - // assert_eq!(cage.chdir_syscall(".."), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // assert_eq!(cage.access_syscall("/subdir1", F_OK), 0); - // assert_eq!(cage.chdir_syscall("/subdir1/subdir2/subdir3"), 0); - // assert_eq!(cage.access_syscall("../../../subdir1", F_OK), 0); + #[test] + fn ut_lind_fs_dup2_with_fork() { + // Acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup. + let _thelock = setup::lock_and_init(); - // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); - // lindrustfinalize(); - // } + let cage = interface::cagetable_getref(1); - // pub fn ut_lind_fs_dir_mode() { - // lindrustinit(0); - // let cage = interface::cagetable_getref(1); + let flags: i32 = O_CREAT | O_RDWR; + let filepath1 = "/dup2file_with_fork1"; + let filepath2 = "/dup2file_with_fork2"; - // let filepath1 = "/subdirDirMode1"; - // let filepath2 = "/subdirDirMode2"; + // Open file descriptors + let fd1 = cage.open_syscall(filepath1, flags, S_IRWXA); + let fd2 = cage.open_syscall(filepath2, flags, S_IRWXA); + assert!(fd1 >= 0); + assert!(fd2 >= 0); - // let mut statdata: stat = unsafe { std::mem::zeroed() }; + // Write data to the first file + assert_eq!(cage.write_syscall(fd1, str2cbuf("parent data"), 11), 11); - // assert_eq!(cage.mkdir_syscall(filepath1, S_LIND), 0); - // assert_eq!(cage.stat_syscall(filepath1, &mut statdata), 0); - // assert_eq!(statdata.st_mode, S_LIND | S_IFDIR as u32); + // Fork the process + assert_eq!(cage.fork_syscall(2), 0); - // assert_eq!(cage.mkdir_syscall(filepath2, 0), 0); - // assert_eq!(cage.stat_syscall(filepath2, &mut statdata), 0); - // assert_eq!(statdata.st_mode, S_IFDIR as u32); + let child = std::thread::spawn(move || { + let cage2 = interface::cagetable_getref(2); - // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); - // lindrustfinalize(); - // } + // In the child process, duplicate fd1 to fd2 + assert!(cage2.dup2_syscall(fd1, fd2) >= 0); - // pub fn ut_lind_fs_dir_multiple() { - // lindrustinit(0); - // let cage = interface::cagetable_getref(1); + // Write new data to the duplicated file descriptor + assert_eq!(cage2.write_syscall(fd2, str2cbuf(" child data"), 11), 11); - // assert_eq!(cage.mkdir_syscall("/subdirMultiple1", S_LIND), 0); - // assert_eq!( - // cage.mkdir_syscall("/subdirMultiple1/subdirMultiple2", S_LIND), - // 0 - // ); - // assert_eq!( - // cage.mkdir_syscall("/subdirMultiple1/subdirMultiple2/subdirMultiple3", 0), - // 0 - // ); + assert_eq!(cage2.close_syscall(fd2), 0); - // let mut statdata: stat = unsafe { std::mem::zeroed() }; + assert_eq!(cage2.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + }); - // //ensure that the file is a dir with all of the correct bits on for nodes - // assert_eq!( - // cage.stat_syscall("/subdirMultiple1/subdirMultiple2", &mut statdata), - // 0 - // ); - // assert_eq!(statdata.st_mode, S_LIND | S_IFDIR as u32); + child.join().unwrap(); - // assert_eq!( - // cage.stat_syscall( - // "/subdirMultiple1/subdirMultiple2/subdirMultiple3", - // &mut statdata - // ), - // 0 - // ); - // assert_eq!(statdata.st_mode, S_IFDIR as u32); + let mut buffer = sizecbuf(22); + assert_eq!(cage.lseek_syscall(fd1, 0, SEEK_SET), 0); + assert_eq!(cage.read_syscall(fd1, buffer.as_mut_ptr(), 22), 22); + assert_eq!(cbuf2str(&buffer), "parent data child data"); - // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); - // lindrustfinalize(); - // } + assert_eq!(cage.close_syscall(fd1), 0); - // pub fn ut_lind_fs_dup() { - // lindrustinit(0); - // let cage = interface::cagetable_getref(1); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; - // let filepath = "/dupfile"; + #[test] + pub fn ut_lind_fs_fcntl_valid_args() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); - // let fd = cage.open_syscall(filepath, flags, S_IRWXA); - // let mut temp_buffer = sizecbuf(2); - // assert!(fd >= 0); - // assert_eq!(cage.write_syscall(fd, str2cbuf("12"), 2), 2); - // assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); - // assert_eq!(cage.read_syscall(fd, temp_buffer.as_mut_ptr(), 2), 2); - // assert_eq!(cbuf2str(&temp_buffer), "12"); + let cage = interface::cagetable_getref(1); - // //duplicate the file descriptor - // let fd2 = cage.dup_syscall(fd, None); - // assert!(fd != fd2); + let sockfd = cage.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, 0); + let filefd = cage.open_syscall("/fcntl_file_1", O_CREAT | O_EXCL, 0o755); - // //essentially a no-op, but duplicate again -- they should be diff &fd's - // let fd3 = cage.dup_syscall(fd, None); - // assert!(fd != fd2 && fd != fd3); + //changing O_CLOEXEC file descriptor flag and checking if it was correctly set - // //We don't need all three, though: - // assert_eq!(cage.close_syscall(fd3), 0); + /* Use FD_CLOEXEC when setting and checking file descriptor flags with F_SETFD and F_GETFD. */ + assert_eq!(cage.fcntl_syscall(sockfd, F_SETFD, FD_CLOEXEC), 0); + assert_eq!(cage.fcntl_syscall(sockfd, F_GETFD, 0), FD_CLOEXEC); - // assert_eq!(cage.lseek_syscall(fd, 0, SEEK_END), 2); - // assert_eq!(cage.lseek_syscall(fd2, 0, SEEK_END), 2); + //changing the file access mode to read-only, enabling the + //O_NONBLOCK file status flag, and checking if they were correctly set - // // write some data to move the first position - // assert_eq!(cage.write_syscall(fd, str2cbuf("34"), 2), 2); + /* Usage are different - including using different parameters */ + assert_eq!( + cage.fcntl_syscall(filefd, F_SETFL, O_RDONLY | O_NONBLOCK), + 0 + ); - // //Make sure that they are still in the same place: - // let mut buffer = sizecbuf(4); - // assert_eq!( - // cage.lseek_syscall(fd, 0, SEEK_SET), - // cage.lseek_syscall(fd2, 0, SEEK_SET) - // ); - // assert_eq!(cage.read_syscall(fd, buffer.as_mut_ptr(), 4), 4); - // assert_eq!(cbuf2str(&buffer), "1234"); + let flags = cage.fcntl_syscall(filefd, F_GETFL, 0); + assert_eq!( + flags & O_ACCMODE, + O_RDONLY + ); + assert_eq!( + flags & O_NONBLOCK, + O_NONBLOCK + ); - // assert_eq!(cage.close_syscall(fd), 0); - // //the other &fd should still work - // assert_eq!(cage.lseek_syscall(fd2, 0, SEEK_END), 4); - // assert_eq!(cage.write_syscall(fd2, str2cbuf("5678"), 4), 4); + //when provided with 'F_GETFD' or 'F_GETFL' command, 'arg' should be ignored, + // thus even negative arg values should produce nomal behavior + assert_eq!(cage.fcntl_syscall(sockfd, F_GETFD, -132), FD_CLOEXEC); + assert_eq!(cage.fcntl_syscall(filefd, F_GETFL, -1998), flags); - // assert_eq!(cage.lseek_syscall(fd2, 0, SEEK_SET), 0); - // let mut buffer2 = sizecbuf(8); - // assert_eq!(cage.read_syscall(fd2, buffer2.as_mut_ptr(), 8), 8); - // assert_eq!(cage.close_syscall(fd2), 0); - // assert_eq!(cbuf2str(&buffer2), "12345678"); + assert_eq!(cage.close_syscall(filefd), 0); + assert_eq!(cage.close_syscall(sockfd), 0); - // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); - // lindrustfinalize(); - // } + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // pub fn ut_lind_fs_dup2() { - // lindrustinit(0); - // let cage = interface::cagetable_getref(1); + #[test] + pub fn ut_lind_fs_fcntl_invalid_args() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let filefd = cage.open_syscall("/fcntl_file_2", O_CREAT | O_EXCL, S_IRWXA); + //when presented with a nonexistent command, 'Invalid Argument' error should be + // thrown 29 is an arbitrary number that does not correspond to any of + // the defined 'fcntl' commands + assert_eq!(cage.fcntl_syscall(filefd, 29, 0), -(Errno::EINVAL as i32)); + //when a negative arg is provided with F_SETFD, F_SETFL, or F_DUPFD, + //Invalid Argument' error should be thrown as well + + /* F_SETFD, F_SETFL with negative value args will not cause fcntl return error */ + assert_eq!( + cage.fcntl_syscall(filefd, F_SETFD, -5), + 0 + ); + assert_eq!( + cage.fcntl_syscall(filefd, F_SETFL, -5), + 0 + ); + assert_eq!( + cage.fcntl_syscall(filefd, F_DUPFD, -5), + -(Errno::EINVAL as i32) + ); + + assert_eq!(cage.close_syscall(filefd), 0); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; - // let filepath = "/dup2file"; + #[test] + pub fn ut_lind_fs_fcntl_dup() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let filefd1 = cage.open_syscall("/fcntl_file_4", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); + //on success, returning the new file descriptor greater than or equal to 100 + //and different from the original file descriptor + let filefd2 = cage.fcntl_syscall(filefd1, F_DUPFD, 100); + assert!(filefd2 >= 100 && filefd2 != filefd1); + + //to check if both file descriptors refer to the same fie, we can write into a + // file using one file descriptor, read from the file using another file + // descriptor, and make sure that the contents are the same + let mut temp_buffer = sizecbuf(9); + assert_eq!(cage.write_syscall(filefd1, str2cbuf("Test text"), 9), 9); + assert_eq!(cage.lseek_syscall(filefd1, 0, SEEK_SET), 0); + assert_eq!(cage.read_syscall(filefd2, temp_buffer.as_mut_ptr(), 9), 9); + assert_eq!(cbuf2str(&temp_buffer), "Test text"); + + //file status flags are shared by duplicated file descriptors resulting from + //a single opening of the file + assert_eq!( + cage.fcntl_syscall(filefd1, F_GETFL, 0), + cage.fcntl_syscall(filefd2, F_GETFL, 0) + ); + + assert_eq!(cage.close_syscall(filefd1), 0); + assert_eq!(cage.close_syscall(filefd2), 0); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // let fd = cage.open_syscall(filepath, flags, S_IRWXA); + #[test] + pub fn ut_lind_fs_ioctl_valid_args() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); - // assert_eq!(cage.write_syscall(fd, str2cbuf("12"), 2), 2); + let cage = interface::cagetable_getref(1); - // //trying to dup fd into fd + 1 - // let _fd2: i32 = cage.dup2_syscall(fd, fd + 1 as i32); + let mut union0: winsize = unsafe { mem::zeroed() }; + let mut union1: winsize = unsafe { mem::zeroed() }; + union1.ws_col = 1; + union1.ws_row = 1; - // //should be a no-op since the last line did the same thing - // let fd2: i32 = cage.dup2_syscall(fd, fd + 1 as i32); + let union0_ptr: *mut winsize = &mut union0; + let union1_ptr: *mut winsize = &mut union1; - // //read/write tests for the files - // assert_eq!( - // cage.lseek_syscall(fd, 0, SEEK_END), - // cage.lseek_syscall(fd2, 0, SEEK_END) - // ); - // assert_eq!(cage.write_syscall(fd, str2cbuf("34"), 2), 2); - // assert_eq!( - // cage.lseek_syscall(fd, 0, SEEK_SET), - // cage.lseek_syscall(fd2, 0, SEEK_SET) - // ); + let sockfd = cage.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, 0); + let filefd = cage.open_syscall("/ioctl_file", O_CREAT | O_EXCL, 0o755); - // let mut buffer = sizecbuf(4); - // assert_eq!(cage.lseek_syscall(fd2, 0, SEEK_SET), 0); - // assert_eq!(cage.read_syscall(fd, buffer.as_mut_ptr(), 4), 4); - // assert_eq!(cbuf2str(&buffer), "1234"); + //try to use FIONBIO for a non-socket + assert_eq!( + cage.ioctl_syscall(filefd, FIONBIO, union0_ptr as *mut u8), + 0 + ); - // assert_eq!(cage.close_syscall(fd), 0); + //clear the O_NONBLOCK flag + assert_eq!(cage.ioctl_syscall(sockfd, FIONBIO, union0_ptr as *mut u8), 0); - // let mut buffer2 = sizecbuf(8); - // assert_eq!(cage.lseek_syscall(fd2, 0, SEEK_END), 4); - // assert_eq!(cage.write_syscall(fd2, str2cbuf("5678"), 4), 4); + //checking to see if the flag was updated + assert_eq!(cage.fcntl_syscall(sockfd, F_GETFL, 0) & O_NONBLOCK, 0); - // assert_eq!(cage.lseek_syscall(fd2, 0, SEEK_SET), 0); - // assert_eq!(cage.read_syscall(fd2, buffer2.as_mut_ptr(), 8), 8); - // assert_eq!(cbuf2str(&buffer2), "12345678"); + //set the O_NONBLOCK flag + assert_eq!(cage.ioctl_syscall(sockfd, FIONBIO, union1_ptr as *mut u8), 0); - // assert_eq!(cage.close_syscall(fd2), 0); - // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); - // lindrustfinalize(); - // } + //checking to see if the flag was updated + assert_eq!( + cage.fcntl_syscall(sockfd, F_GETFL, 0) & O_NONBLOCK, + O_NONBLOCK + ); - // pub fn ut_lind_fs_fcntl() { - // lindrustinit(0); - // let cage = interface::cagetable_getref(1); + //clear the O_NONBLOCK flag + assert_eq!(cage.ioctl_syscall(sockfd, FIONBIO, union0_ptr as *mut u8), 0); - // let sockfd = cage.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, 0); - // let filefd = cage.open_syscall("/fcntl_file", O_CREAT | O_EXCL, S_IRWXA); + //checking to see if the flag was updated + assert_eq!(cage.fcntl_syscall(sockfd, F_GETFL, 0) & O_NONBLOCK, 0); - // //set the setfd flag - // assert_eq!(cage.fcntl_syscall(sockfd, F_SETFD, libc::FD_CLOEXEC), 0); + assert_eq!(cage.close_syscall(filefd), 0); + assert_eq!(cage.close_syscall(sockfd), 0); - // //checking to see if the wrong flag was set or not - // assert_eq!(cage.fcntl_syscall(sockfd, F_GETFD, 0), libc::FD_CLOEXEC); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // //let's get some more flags on the filefd - // assert_eq!( - // cage.fcntl_syscall(filefd, F_SETFL, O_RDONLY | O_NONBLOCK), - // 0 - // ); + #[test] + pub fn ut_lind_fs_ioctl_invalid_args() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //setting up two integer values (a zero value to test clearing nonblocking I/O + // behavior on non-socket type and a non-zero value to test setting + // nonblocking I/O behavior on non-socket type) + + //ioctl requires a pointer to an integer to be passed with FIONBIO command + let mut union0: winsize = unsafe { mem::zeroed() }; + let mut union1: winsize = unsafe { mem::zeroed() }; + union1.ws_col = 1; + union1.ws_row = 1; + + let union0_ptr: *mut winsize = &mut union0; + let union1_ptr: *mut winsize = &mut union1; + + let sockfd = cage.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, 0); + let filefd = cage.open_syscall("/ioctl_file2", O_CREAT | O_EXCL, S_IRWXA); + + //trying to use FIONBIO command on a non-socket type (the file type in this + // case) for any 'ptrunion' value should throw a 'Not a typewriter' + // error + + /* Those invalid argument will success in native linux (ArchLinux) + [https://stackoverflow.com/a/1151077/22572322] + ...but these behaved inconsistently between systems, and even within the same system... + */ + assert_eq!( + cage.ioctl_syscall(filefd, FIONBIO, union0_ptr as *mut u8), + 0 + ); + assert_eq!( + cage.ioctl_syscall(filefd, FIONBIO, union1_ptr as *mut u8), + 0 + ); + assert_eq!(cage.close_syscall(filefd), 0); + + //calling 'ioctl' with a control function that is not implemented yet should + //return an 'Invalid argument' error + //21600 is an arbitrary integer that does not correspond to any implemented + //control functions for ioctl syscall + assert_eq!( + cage.ioctl_syscall(sockfd, 21600, union0_ptr as *mut u8), + -(Errno::ENOTTY as i32) + ); + + //calling ioctl with FIONBIO command and a null pointer + //should return a 'Bad address' error + let null_ptr: *mut u8 = std::ptr::null_mut(); + // let union_null: IoctlPtrUnion = IoctlPtrUnion { int_ptr: null_ptr }; + assert_eq!( + cage.ioctl_syscall(sockfd, FIONBIO, null_ptr as *mut u8), + -(Errno::EFAULT as i32) + ); + + //calling ioctl on a closed file descriptor should throw a 'Bad file number' + // error + assert_eq!(cage.close_syscall(sockfd), 0); + assert_eq!( + cage.fcntl_syscall(sockfd, F_GETFL, 0), + -(Errno::EBADF as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // //checking if the flags are updated... - // // assert_eq!(cage.fcntl_syscall(filefd, F_GETFL, 0), 2048); - // let flags = cage.fcntl_syscall(filefd, F_GETFL, 0); - - // assert_ne!(flags & libc::O_NONBLOCK, 0); + #[test] + pub fn ut_lind_fs_fdflags() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let path = "/fdFlagsFile"; + + let fd = cage.creat_syscall(path, S_IRWXA); + assert_eq!(cage.close_syscall(fd), 0); + + let read_fd = cage.open_syscall(path, O_RDONLY, S_IRWXA); + assert_eq!(cage.lseek_syscall(read_fd, 0, SEEK_SET), 0); + assert_eq!( + cage.write_syscall(read_fd, str2cbuf("Hello! This should not write."), 28), + -(Errno::EBADF as i32) + ); + + let mut buf = sizecbuf(100); + assert_eq!(cage.lseek_syscall(read_fd, 0, SEEK_SET), 0); + + //this fails because nothing is written to the readfd (the previous write was + // unwritable) + assert_eq!(cage.read_syscall(read_fd, buf.as_mut_ptr(), 100), 0); + assert_eq!(cage.close_syscall(read_fd), 0); + + let write_fd = cage.open_syscall(path, O_WRONLY, S_IRWXA); + let mut buf2 = sizecbuf(100); + assert_eq!(cage.lseek_syscall(write_fd, 0, SEEK_SET), 0); + assert_eq!( + cage.read_syscall(write_fd, buf2.as_mut_ptr(), 100), + -(Errno::EBADF as i32) + ); + + assert_eq!(cage.lseek_syscall(write_fd, 0, SEEK_SET), 0); + assert_eq!( + cage.write_syscall(write_fd, str2cbuf("Hello! This should write."), 24), + 24 + ); + assert_eq!(cage.close_syscall(write_fd), 0); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // assert_eq!(cage.close_syscall(filefd), 0); - // assert_eq!(cage.close_syscall(sockfd), 0); + #[test] + pub fn ut_lind_fs_link_empty_path() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); - // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); - // lindrustfinalize(); - // } + let cage = interface::cagetable_getref(1); - // pub fn ut_lind_fs_ioctl() { - // lindrustinit(0); - // let cage = interface::cagetable_getref(1); + // Case: When only oldpath is empty, expect an error ENOENT + let oldpath = ""; + let newpath = "/newpath"; + assert_eq!(cage.link_syscall(oldpath, newpath), -(Errno::EPERM as i32)); - // let mut union0: winsize = unsafe { mem::zeroed() }; - // let mut union1: winsize = unsafe { mem::zeroed() }; - // union1.ws_col = 1; - // union1.ws_row = 1; - - // let union0_ptr: *mut winsize = &mut union0; - // let union1_ptr: *mut winsize = &mut union1; - - // let sockfd = cage.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, 0); - // let filefd = cage.open_syscall("/ioctl_file", O_CREAT | O_EXCL, S_LIND); - - // //try to use FIONBIO for a non-socket - // // assert_eq!( - // // cage.ioctl_syscall(filefd, FIONBIO, union0_ptr as *mut u8), - // // 0 - // // ); - // if cage.ioctl_syscall(filefd, FIONBIO, union0_ptr as *mut u8) < 0 { - // let err = unsafe { - // libc::__errno_location() - // }; - // let err_str = unsafe { - // libc::strerror(*err) - // }; - // let err_msg = unsafe { - // CStr::from_ptr(err_str).to_string_lossy().into_owned() - // }; - // println!("errno: {:?}", err); - // println!("Error message: {:?}", err_msg); - // io::stdout().flush().unwrap(); - // panic!(); - // } - - // //clear the O_NONBLOCK flag - // assert_eq!(cage.ioctl_syscall(sockfd, FIONBIO, union0_ptr as *mut u8), 0); - - // //checking to see if the flag was updated - // assert_eq!(cage.fcntl_syscall(sockfd, F_GETFL, 0) & O_NONBLOCK, 0); - - // //set the O_NONBLOCK flag - // assert_eq!(cage.ioctl_syscall(sockfd, FIONBIO, union1_ptr as *mut u8), 0); - - // //checking to see if the flag was updated - // assert_eq!( - // cage.fcntl_syscall(sockfd, F_GETFL, 0) & O_NONBLOCK, - // O_NONBLOCK - // ); + // Case: When only newpath is empty, expect an error ENOENT + let oldpath = "/oldpath"; + let newpath = ""; + assert_eq!(cage.link_syscall(oldpath, newpath), -(Errno::ENOENT as i32)); - // //clear the O_NONBLOCK flag - // assert_eq!(cage.ioctl_syscall(sockfd, FIONBIO, union0_ptr as *mut u8), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } - // //checking to see if the flag was updated - // assert_eq!(cage.fcntl_syscall(sockfd, F_GETFL, 0) & O_NONBLOCK, 0); + #[test] + pub fn ut_lind_fs_link_nonexistent_oldpath() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); - // assert_eq!(cage.close_syscall(filefd), 0); - // assert_eq!(cage.close_syscall(sockfd), 0); + let cage = interface::cagetable_getref(1); - // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); - // lindrustfinalize(); - // } + let oldpath = "/nonexistent"; + let newpath = "/newpath"; -// pub fn ut_lind_fs_fdflags() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); - -// let path = "/fdFlagsFile"; + // Expect an error for non-existent oldpath + assert_eq!(cage.link_syscall(oldpath, newpath), -(Errno::ENOENT as i32)); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } -// let fd = cage.creat_syscall(path, S_IRWXA); -// assert_eq!(cage.close_syscall(fd), 0); + #[test] + pub fn ut_lind_fs_link_existing_newpath() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); -// let read_fd = cage.open_syscall(path, O_RDONLY, S_IRWXA); -// assert_eq!(cage.lseek_syscall(read_fd, 0, SEEK_SET), 0); -// assert_eq!( -// cage.write_syscall(read_fd, str2cbuf("Hello! This should not write."), 28), -// -(Errno::EBADF as i32) -// ); + let cage = interface::cagetable_getref(1); -// let mut buf = sizecbuf(100); -// assert_eq!(cage.lseek_syscall(read_fd, 0, SEEK_SET), 0); + let oldpath = "/oldfile"; + let newpath = "/newfile"; -// //this fails because nothing is written to the readfd (the previous write was unwritable) -// assert_eq!(cage.read_syscall(read_fd, buf.as_mut_ptr(), 100), 0); -// assert_eq!(cage.close_syscall(read_fd), 0); + // Create the oldfile + let _fd1 = cage.open_syscall(oldpath, O_CREAT | O_EXCL | O_WRONLY, S_IRWXA); -// let write_fd = cage.open_syscall(path, O_WRONLY, S_IRWXA); -// let mut buf2 = sizecbuf(100); -// assert_eq!(cage.lseek_syscall(write_fd, 0, SEEK_SET), 0); -// assert_eq!( -// cage.read_syscall(write_fd, buf2.as_mut_ptr(), 100), -// -(Errno::EBADF as i32) -// ); + // Create the newfile + let _fd2 = cage.open_syscall(newpath, O_CREAT | O_EXCL | O_WRONLY, S_IRWXA); -// assert_eq!(cage.lseek_syscall(write_fd, 0, SEEK_SET), 0); -// assert_eq!( -// cage.write_syscall(write_fd, str2cbuf("Hello! This should write."), 24), -// 24 -// ); -// assert_eq!(cage.close_syscall(write_fd), 0); + // Expect an error since newpath already exists + assert_eq!(cage.link_syscall(oldpath, newpath), -(Errno::EEXIST as i32)); -// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } -// pub fn ut_lind_fs_file_link_unlink() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); + #[test] + pub fn ut_lind_fs_link_directory() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); -// let path = "/fileLink"; -// let path2 = "/fileLink2"; + let cage = interface::cagetable_getref(1); -// let fd = cage.open_syscall(path, O_CREAT | O_EXCL | O_WRONLY, S_IRWXA); -// assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); -// assert_eq!(cage.write_syscall(fd, str2cbuf("hi"), 2), 2); + let oldpath = "/olddir"; + let newpath = "/newpath"; -// let mut statdata = StatData::default(); + // Create the directory for the oldpath + assert_eq!(cage.mkdir_syscall(oldpath, S_IRWXA), 0); -// assert_eq!(cage.stat_syscall(path, &mut statdata), 0); -// assert_eq!(statdata.st_size, 2); -// assert_eq!(statdata.st_nlink, 1); + // Expect an error since linking directories is not allowed + assert_eq!(cage.link_syscall(oldpath, newpath), -(Errno::EPERM as i32)); + // Cleanup remove the directory for a clean environment + let _ = cage.rmdir_syscall(oldpath); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } -// let mut statdata2 = StatData::default(); + #[test] + pub fn ut_lind_fs_unlink_empty_path() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); -// //make sure that this has the same traits as the other file that we linked -// // and make sure that the link count on the orig file has increased -// assert_eq!(cage.link_syscall(path, path2), 0); -// assert_eq!(cage.stat_syscall(path, &mut statdata), 0); -// assert_eq!(cage.stat_syscall(path2, &mut statdata2), 0); -// assert!(statdata == statdata2); -// assert_eq!(statdata.st_nlink, 2); + let cage = interface::cagetable_getref(1); -// //now we unlink -// assert_eq!(cage.unlink_syscall(path), 0); -// assert_eq!(cage.stat_syscall(path2, &mut statdata2), 0); -// assert_eq!(statdata2.st_nlink, 1); + let path = ""; + // Expect an error for empty path + assert_eq!(cage.unlink_syscall(path), -(Errno::EISDIR as i32)); -// //it shouldn't work to stat the orig since it is gone -// assert_ne!(cage.stat_syscall(path, &mut statdata), 0); -// assert_eq!(cage.unlink_syscall(path2), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } -// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } + #[test] + pub fn ut_lind_fs_unlink_nonexistent_file() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); -// pub fn ut_lind_fs_file_lseek_past_end() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); + let cage = interface::cagetable_getref(1); -// let path = "/lseekPastEnd"; - -// let fd = cage.open_syscall(path, O_CREAT | O_EXCL | O_RDWR, S_IRWXA); -// assert_eq!(cage.write_syscall(fd, str2cbuf("hello"), 5), 5); - -// //seek past the end and then write -// assert_eq!(cage.lseek_syscall(fd, 10, SEEK_SET), 10); -// assert_eq!(cage.write_syscall(fd, str2cbuf("123456"), 6), 6); - -// let mut buf = sizecbuf(16); -// assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); -// assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 20), 16); -// assert_eq!(cbuf2str(&buf), "hello\0\0\0\0\0123456"); - -// assert_eq!(cage.close_syscall(fd), 0); -// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } - -// pub fn ut_lind_fs_fstat_complex() { -// lindrustinit(0); - -// let cage = interface::cagetable_getref(1); -// let path = "/complexFile"; - -// let fd = cage.open_syscall(path, O_CREAT | O_WRONLY, S_IRWXA); -// assert_eq!(cage.write_syscall(fd, str2cbuf("testing"), 4), 4); - -// let mut statdata = StatData::default(); - -// assert_eq!(cage.fstat_syscall(fd, &mut statdata), 0); -// assert_eq!(statdata.st_size, 4); -// assert_eq!(statdata.st_nlink, 1); - -// assert_eq!(cage.close_syscall(fd), 0); -// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } - -// pub fn ut_lind_fs_getuid() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); - -// //let's get the initial -1s out of the way -// cage.getgid_syscall(); -// cage.getegid_syscall(); -// cage.getuid_syscall(); -// cage.geteuid_syscall(); - -// //testing to make sure that all of the gid and uid values are good to go when system is initialized -// assert_eq!(cage.getgid_syscall() as u32, DEFAULT_GID); -// assert_eq!(cage.getegid_syscall() as u32, DEFAULT_GID); -// assert_eq!(cage.getuid_syscall() as u32, DEFAULT_UID); -// assert_eq!(cage.geteuid_syscall() as u32, DEFAULT_UID); - -// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } - -// pub fn ut_lind_fs_load_fs() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); - -// let mut statdata = StatData::default(); - -// //testing that all of the dev files made it out safe and sound -// cage.stat_syscall("/dev", &mut statdata); - -// assert_eq!(cage.stat_syscall("/dev/null", &mut statdata), 0); -// assert_eq!(statdata.st_rdev, makedev(&DevNo { major: 1, minor: 3 })); - -// assert_eq!(cage.stat_syscall("/dev/random", &mut statdata), 0); -// assert_eq!(statdata.st_rdev, makedev(&DevNo { major: 1, minor: 8 })); - -// assert_eq!(cage.stat_syscall("/dev/urandom", &mut statdata), 0); -// assert_eq!(statdata.st_rdev, makedev(&DevNo { major: 1, minor: 9 })); - -// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } - -// pub fn ut_lind_fs_mknod() { -// // let's create /dev/null -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); -// let dev = makedev(&DevNo { major: 1, minor: 3 }); -// let path = "/null"; - -// //now we are going to mknod /dev/null with create, read and write flags and permissions -// //and then makr sure that it exists -// assert_eq!(cage.mknod_syscall(path, S_IFCHR as u32, dev), 0); -// let fd = cage.open_syscall(path, O_RDWR, S_IRWXA); - -// //checking the metadata of the file: -// let mut statdata = StatData::default(); - -// //should be a chr file, so let's check this -// let mut buf = sizecbuf(4); -// assert_eq!(cage.fstat_syscall(fd, &mut statdata), 0); -// assert_eq!(statdata.st_mode & S_FILETYPEFLAGS as u32, S_IFCHR as u32); -// assert_eq!(statdata.st_rdev, dev); -// assert_eq!(cage.write_syscall(fd, str2cbuf("test"), 4), 4); -// assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 4), 0); -// assert_eq!(cbuf2str(&buf), "\0\0\0\0"); -// assert_eq!(cage.close_syscall(fd), 0); - -// let mut statdata2 = StatData::default(); - -// //try it again with /dev/random -// let dev2 = makedev(&DevNo { major: 1, minor: 8 }); -// let path2 = "/random"; - -// //making the node and then making sure that it exists -// assert_eq!(cage.mknod_syscall(path2, S_IFCHR as u32, dev2), 0); -// let fd2 = cage.open_syscall(path2, O_RDWR, S_IRWXA); - -// let mut buf2 = sizecbuf(4); -// assert_eq!(cage.fstat_syscall(fd2, &mut statdata2), 0); -// assert_eq!(statdata2.st_mode & S_FILETYPEFLAGS as u32, S_IFCHR as u32); -// assert_eq!(statdata2.st_rdev, dev2); -// assert_eq!(cage.write_syscall(fd2, str2cbuf("testing"), 7), 7); -// assert_ne!(cage.read_syscall(fd2, buf2.as_mut_ptr(), 7), 0); -// assert_eq!(cage.close_syscall(fd2), 0); - -// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } - -// pub fn ut_lind_fs_multiple_open() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); - -// //try to open several files at once -- the fd's should not be overwritten -// let fd1 = cage.open_syscall("/foo", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); -// let fd2 = cage.open_syscall("/foo", O_RDWR, S_IRWXA); -// assert_ne!(fd1, fd2); - -// let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; -// let mode: u32 = 0o666; // 0666 -// let name = "double_open_file"; - -// let mut read_buf = sizecbuf(2); -// let fd3 = cage.open_syscall(name, flags, mode); -// assert_eq!(cage.write_syscall(fd3, str2cbuf("hi"), 2), 2); -// assert_eq!(cage.lseek_syscall(fd3, 0, SEEK_SET), 0); -// assert_eq!(cage.read_syscall(fd3, read_buf.as_mut_ptr(), 2), 2); -// assert_eq!(cbuf2str(&read_buf), "hi"); - -// let _fd4 = cage.open_syscall(name, flags, mode); -// let mut buf = sizecbuf(5); -// assert_eq!(cage.lseek_syscall(fd3, 2, SEEK_SET), 2); -// assert_eq!(cage.write_syscall(fd3, str2cbuf("boo"), 3), 3); -// assert_eq!(cage.lseek_syscall(fd3, 0, SEEK_SET), 0); -// assert_eq!(cage.read_syscall(fd3, buf.as_mut_ptr(), 5), 5); -// assert_eq!(cbuf2str(&buf), "\0\0boo"); - -// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } - -// pub fn ut_lind_fs_rmdir() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); - -// let path = "/parent_dir/dir"; -// assert_eq!(cage.mkdir_syscall("/parent_dir", S_IRWXA), 0); -// assert_eq!(cage.mkdir_syscall(path, S_IRWXA), 0); -// assert_eq!(cage.rmdir_syscall(path), 0); - -// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } - -// pub fn ut_lind_fs_stat_file_complex() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); -// let fd = cage.open_syscall("/fooComplex", O_CREAT | O_EXCL | O_WRONLY, S_IRWXA); - -// assert_eq!(cage.write_syscall(fd, str2cbuf("hi"), 2), 2); - -// let mut statdata = StatData::default(); -// let mut statdata2 = StatData::default(); - -// assert_eq!(cage.fstat_syscall(fd, &mut statdata), 0); -// assert_eq!(statdata.st_size, 2); -// assert_eq!(statdata.st_nlink, 1); - -// assert_eq!(cage.link_syscall("/fooComplex", "/barComplex"), 0); -// assert_eq!(cage.stat_syscall("/fooComplex", &mut statdata), 0); -// assert_eq!(cage.stat_syscall("/barComplex", &mut statdata2), 0); - -// //check that they are the same and that the link count is 0 -// assert!(statdata == statdata2); -// assert_eq!(statdata.st_nlink, 2); - -// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } - -// pub fn ut_lind_fs_stat_file_mode() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); -// let path = "/fooFileMode"; -// let _fd = cage.open_syscall(path, O_CREAT | O_EXCL | O_WRONLY, S_IRWXA); - -// let mut statdata = StatData::default(); -// assert_eq!(cage.stat_syscall(path, &mut statdata), 0); -// assert_eq!(statdata.st_mode, S_IRWXA | S_IFREG as u32); - -// //make a file without permissions and check that it is a reg file without permissions -// let path2 = "/fooFileMode2"; -// let _fd2 = cage.open_syscall(path2, O_CREAT | O_EXCL | O_WRONLY, 0); -// assert_eq!(cage.stat_syscall(path2, &mut statdata), 0); -// assert_eq!(statdata.st_mode, S_IFREG as u32); - -// //check that stat can be done on the current (root) dir -// assert_eq!(cage.stat_syscall(".", &mut statdata), 0); - -// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } - -// pub fn ut_lind_fs_statfs() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); -// let mut fsdata = FSData::default(); - -// assert_eq!(cage.statfs_syscall("/", &mut fsdata), 0); -// assert_eq!(fsdata.f_type, 0xBEEFC0DE); -// assert_eq!(fsdata.f_bsize, 4096); - -// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } - -// pub fn ut_lind_fs_fstatfs() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); -// let mut fsdata = FSData::default(); - -// // Get fd -// let fd = cage.open_syscall("/", O_RDONLY, 0); -// assert!(fd >= 0); -// // fstatfs -// assert_eq!(cage.fstatfs_syscall(fd, &mut fsdata), 0); -// // Check the output -// assert_eq!(fsdata.f_type, 0xBEEFC0DE); -// assert_eq!(fsdata.f_bsize, 4096); -// // Close the file -// assert_eq!(cage.close_syscall(fd), 0); - -// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } - -// pub fn ut_lind_fs_rename() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); - -// let old_path = "/test_dir"; -// assert_eq!(cage.mkdir_syscall(old_path, S_IRWXA), 0); -// assert_eq!(cage.rename_syscall(old_path, "/test_dir_renamed"), 0); - -// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } - -// pub fn ut_lind_fs_ftruncate() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); - -// let fd = cage.open_syscall("/ftruncate", O_CREAT | O_TRUNC | O_RDWR, S_IRWXA); -// assert!(fd >= 0); - -// // check if ftruncate() works for extending file with null bytes -// assert_eq!(cage.write_syscall(fd, str2cbuf("Hello there!"), 12), 12); -// assert_eq!(cage.ftruncate_syscall(fd, 15), 0); -// assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); -// let mut buf = sizecbuf(15); -// assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 15), 15); -// assert_eq!(cbuf2str(&buf), "Hello there!\0\0\0"); - -// // check if ftruncate() works for cutting off extra bytes -// assert_eq!(cage.ftruncate_syscall(fd, 5), 0); -// assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); -// let mut buf1 = sizecbuf(7); -// assert_eq!(cage.read_syscall(fd, buf1.as_mut_ptr(), 7), 5); -// assert_eq!(cbuf2str(&buf1), "Hello\0\0"); - -// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } - -// pub fn ut_lind_fs_truncate() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); - -// let path = String::from("/truncate"); -// let fd = cage.open_syscall(&path, O_CREAT | O_TRUNC | O_RDWR, S_IRWXA); -// assert!(fd >= 0); - -// // check if truncate() works for extending file with null bytes -// assert_eq!(cage.write_syscall(fd, str2cbuf("Hello there!"), 12), 12); -// assert_eq!(cage.truncate_syscall(&path, 15), 0); -// assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); -// let mut buf = sizecbuf(15); -// assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 15), 15); -// assert_eq!(cbuf2str(&buf), "Hello there!\0\0\0"); - -// // check if truncate() works for cutting off extra bytes -// assert_eq!(cage.truncate_syscall(&path, 5), 0); -// assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); -// let mut buf1 = sizecbuf(7); -// assert_eq!(cage.read_syscall(fd, buf1.as_mut_ptr(), 7), 5); -// assert_eq!(cbuf2str(&buf1), "Hello\0\0"); - -// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } - -// #[cfg(target_os = "macos")] -// type CharPtr = *const u8; - -// #[cfg(not(target_os = "macos"))] -// type CharPtr = *const i8; - -// pub fn ut_lind_fs_getdents() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); - -// let bufsize = 50; -// let mut vec = vec![0u8; bufsize as usize]; -// let baseptr: *mut u8 = &mut vec[0]; - -// assert_eq!(cage.mkdir_syscall("/getdents", S_IRWXA), 0); -// let fd = cage.open_syscall("/getdents", O_RDWR, S_IRWXA); -// assert_eq!(cage.getdents_syscall(fd, baseptr, bufsize as u32), 48); - -// unsafe { -// let first_dirent = baseptr as *mut interface::ClippedDirent; -// assert!((*first_dirent).d_off == 24); -// let reclen_matched: bool = ((*first_dirent).d_reclen == 24); -// assert_eq!(reclen_matched, true); - -// let nameoffset = baseptr.wrapping_offset(interface::CLIPPED_DIRENT_SIZE as isize); -// let returnedname = interface::RustCStr::from_ptr(nameoffset as *const _); -// let name_matched: bool = (returnedname -// == interface::RustCStr::from_bytes_with_nul(b".\0").unwrap()) -// | (returnedname == interface::RustCStr::from_bytes_with_nul(b"..\0").unwrap()); -// assert_eq!(name_matched, true); - -// let second_dirent = baseptr.wrapping_offset(24) as *mut interface::ClippedDirent; -// assert!((*second_dirent).d_off >= 48); -// } - -// assert_eq!(cage.close_syscall(fd), 0); -// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } - -// pub fn ut_lind_fs_dir_chdir_getcwd() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); -// let needed = "/subdir1\0".as_bytes().to_vec().len(); - -// let needed_u32: u32 = needed as u32; - -// let mut buf = vec![0u8; needed]; -// let bufptr: *mut u8 = &mut buf[0]; - -// assert_eq!(cage.chdir_syscall("/"), 0); -// assert_eq!(cage.getcwd_syscall(bufptr, 0), -(Errno::ERANGE as i32)); -// assert_eq!(cage.getcwd_syscall(bufptr, 1), -(Errno::ERANGE as i32)); -// assert_eq!(cage.getcwd_syscall(bufptr, 2), 0); -// assert_eq!(std::str::from_utf8(&buf).unwrap(), "/\0\0\0\0\0\0\0\0"); - -// cage.mkdir_syscall("/subdir1", S_IRWXA); -// assert_eq!(cage.access_syscall("subdir1", F_OK), 0); -// assert_eq!(cage.chdir_syscall("subdir1"), 0); - -// assert_eq!(cage.getcwd_syscall(bufptr, 0), -(Errno::ERANGE as i32)); -// assert_eq!( -// cage.getcwd_syscall(bufptr, needed_u32 - 1), -// -(Errno::ERANGE as i32) -// ); -// assert_eq!(cage.getcwd_syscall(bufptr, needed_u32), 0); -// assert_eq!(std::str::from_utf8(&buf).unwrap(), "/subdir1\0"); - -// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } - -// pub fn ut_lind_fs_exec_cloexec() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); -// let mut uselessstatdata = StatData::default(); - -// let fd1 = cage.open_syscall( -// "/cloexecuted", -// O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, -// S_IRWXA, -// ); -// let fd2 = cage.open_syscall("/cloexekept", O_CREAT | O_TRUNC | O_RDWR, S_IRWXA); -// assert!(fd1 > 0); -// assert!(fd2 > 0); -// assert_eq!(cage.fstat_syscall(fd1, &mut uselessstatdata), 0); -// assert_eq!(cage.fstat_syscall(fd2, &mut uselessstatdata), 0); - -// assert_eq!(cage.exec_syscall(2), 0); - -// let execcage = interface::cagetable_getref(2); -// assert_eq!( -// execcage.fstat_syscall(fd1, &mut uselessstatdata), -// -(Errno::EBADF as i32) -// ); -// assert_eq!(execcage.fstat_syscall(fd2, &mut uselessstatdata), 0); - -// assert_eq!(execcage.close_syscall(fd2), 0); -// assert_eq!(cage.unlink_syscall("/cloexecuted"), 0); -// assert_eq!(cage.unlink_syscall("/cloexekept"), 0); - -// assert_eq!(execcage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } - - // use libc::c_void; - // pub fn ut_lind_fs_shm() { - // lindrustinit(0); - // let cage = interface::cagetable_getref(1); - // let key = 31337; - // let mut shmidstruct = interface::ShmidsStruct::default(); + let path = "/nonexistent"; + // Expect an error for non-existent path + assert_eq!(cage.unlink_syscall(path), -(Errno::ENOENT as i32)); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_unlink_root_directory() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let path = "/"; + // Expect an error for unlinking root directory + assert_eq!(cage.unlink_syscall(path), -(Errno::EISDIR as i32)); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_unlink_directory() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let path = "/testdirunlink"; + // Create the directory + assert_eq!(cage.mkdir_syscall(path, S_IRWXA), 0); + + // Expect an error for unlinking a directory + assert_eq!(cage.unlink_syscall(path), -(Errno::EISDIR as i32)); + // Cleanup the directory to ensure clean environment + let _ = cage.rmdir_syscall(path); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_unlink_and_close_file() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let path = "/testfile"; + // Create a file + let fd = cage.open_syscall(path, O_CREAT | O_EXCL | O_WRONLY, S_IRWXA); + let mut statdata = StatData::default(); + assert_eq!(cage.stat_syscall(path, &mut statdata), 0); + // Linkcount for the file should be 1 originally + assert_eq!(statdata.st_nlink, 1); + // Perform the unlinking of the file + assert_eq!(cage.unlink_syscall(path), 0); + // Once we close the file, all the existing references for it + // will get closed, so the file descriptor will get deleted + // from the system. + assert_eq!(cage.close_syscall(fd), 0); + // Inorder to verify if the file has been deleted, + // we will try to fetch its data but we must expect an error: + // (ENOENT) "Invalid File". + assert_eq!( + cage.stat_syscall(path, &mut statdata), + -(Errno::ENOENT as i32) + ); + // Verify if the file descriptor has been deleted + // (EBADF) "Invalid File Descriptor". + assert_eq!( + cage.fstat_syscall(fd, &mut statdata), + -(Errno::EBADF as i32) + ); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_unlink_file() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let path = "/testfile"; + // Create a file + let fd = cage.open_syscall(path, O_CREAT | O_EXCL | O_WRONLY, S_IRWXA); + let mut statdata = StatData::default(); + assert_eq!(cage.stat_syscall(path, &mut statdata), 0); + // Linkcount for the file should be 1 originally + assert_eq!(statdata.st_nlink, 1); + // Perform the unlinking of the file + assert_eq!(cage.unlink_syscall(path), 0); + // Since we are not closing the file, the reference + // count should be > 0, and the fd should be valid. + // Verify if the file descriptor is still present + assert_eq!(cage.fstat_syscall(fd, &mut statdata), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_link_unlink_success() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let oldpath = "/fileLink"; + let newpath = "/fileLink2"; + + // Create the oldpath file + let fd = cage.open_syscall(oldpath, O_CREAT | O_EXCL | O_WRONLY, S_IRWXA); + assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); + assert_eq!(cage.write_syscall(fd, str2cbuf("hi"), 2), 2); + + let mut statdata = StatData::default(); + assert_eq!(cage.stat_syscall(oldpath, &mut statdata), 0); + assert_eq!(statdata.st_size, 2); + + // Linkcount for the original file (oldpath) before linking should be 1 + assert_eq!(statdata.st_nlink, 1); + + let mut statdata2 = StatData::default(); + + // Link the two files + assert_eq!(cage.link_syscall(oldpath, newpath), 0); + assert_eq!(cage.stat_syscall(oldpath, &mut statdata), 0); + assert_eq!(cage.stat_syscall(newpath, &mut statdata2), 0); + // make sure that this has the same traits as the other file that we linked + assert!(statdata == statdata2); + // and make sure that the link count on the orig file has increased by 1 + assert_eq!(statdata.st_nlink, 2); - // // shmget returns an identifier in shmid - // let shmid = cage.shmget_syscall(key, 1024, 0666 | IPC_CREAT); + // Perform unlinking of the original file (oldpath) + assert_eq!(cage.unlink_syscall(oldpath), 0); + assert_eq!(cage.stat_syscall(newpath, &mut statdata2), 0); + // Since the file is unlinked, it's link count should be decreased by 1 + assert_eq!(statdata2.st_nlink, 1); - // // shmat to attach to shared memory - // let shmatret = cage.shmat_syscall(shmid, 0xfffff000 as *mut u8, 0); + //it shouldn't work to stat the original since it is gone + assert_ne!(cage.stat_syscall(oldpath, &mut statdata), 0); + assert_eq!(cage.unlink_syscall(newpath), 0); - // // assert_ne!(shmatret, -1); - // assert_ne!(shmatret, 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_link_invalid_path_permissions() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let oldpath = "/invalidtestdir/olddir"; + let newpath = "/newpath"; + + // Create the directory for the oldpath with the parent not having read + // permission. Currently assigning "Write only" permissions + assert_eq!(cage.mkdir_syscall("/invalidtestdir", 0o200), 0); + assert_eq!( + cage.open_syscall(oldpath, O_CREAT | O_EXCL | O_WRONLY, 0o200), + -(Errno::EACCES as i32) + ); + + // Expect the linking to be successful, but this is a bug which must be fixed + // as the parent directory doesn't have read permissions due to which it should + // not be able to link the files. + assert_eq!( + cage.link_syscall(oldpath, newpath), + -(Errno::EACCES as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_file_lseek_past_end() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let path = "/lseekPastEnd"; + + let fd = cage.open_syscall(path, O_CREAT | O_EXCL | O_RDWR, S_IRWXA); + assert_eq!(cage.write_syscall(fd, str2cbuf("hello"), 5), 5); + + //seek past the end and then write + assert_eq!(cage.lseek_syscall(fd, 10, SEEK_SET), 10); + assert_eq!(cage.write_syscall(fd, str2cbuf("123456"), 6), 6); + + let mut buf = sizecbuf(16); + assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); + assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 20), 16); + assert_eq!(cbuf2str(&buf), "hello\0\0\0\0\0123456"); + + assert_eq!(cage.close_syscall(fd), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_fstat_complex() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let path = "/complexFile"; + + let fd = cage.open_syscall(path, O_CREAT | O_WRONLY, S_IRWXA); + assert_eq!(cage.write_syscall(fd, str2cbuf("testing"), 4), 4); + + let mut statdata = StatData::default(); + + assert_eq!(cage.fstat_syscall(fd, &mut statdata), 0); + assert_eq!(statdata.st_size, 4); + assert_eq!(statdata.st_nlink, 1); - // // get struct info - // let shmctlret1 = cage.shmctl_syscall(shmid, IPC_STAT, Some(&mut shmidstruct)); + assert_eq!(cage.close_syscall(fd), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_getuid() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //let's get the initial -1s out of the way + cage.getgid_syscall(); + cage.getegid_syscall(); + cage.getuid_syscall(); + cage.geteuid_syscall(); + + //testing to make sure that all of the gid and uid values are good to go when + // system is initialized + assert_eq!(cage.getgid_syscall() as u32, DEFAULT_GID); + assert_eq!(cage.getegid_syscall() as u32, DEFAULT_GID); + assert_eq!(cage.getuid_syscall() as u32, DEFAULT_UID); + assert_eq!(cage.geteuid_syscall() as u32, DEFAULT_UID); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + // #[test] + // pub fn ut_lind_fs_load_fs() { + // //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // // and also performs clean env setup + // let _thelock = setup::lock_and_init(); - // assert_eq!(shmctlret1, 0); + // let cage = interface::cagetable_getref(1); - // assert_eq!(shmidstruct.shm_nattch, 1); + // let mut statdata = StatData::default(); - // // mark the shared memory to be rmoved - // let shmctlret2 = cage.shmctl_syscall(shmid, IPC_RMID, None); + // //testing that all of the dev files made it out safe and sound + // cage.stat_syscall("/dev", &mut statdata); - // assert_eq!(shmctlret2, 0); + // assert_eq!(cage.stat_syscall("/dev/null", &mut statdata), 0); + // assert_eq!(statdata.st_rdev, makedev(&DevNo { major: 1, minor: 3 })); - // //detach from shared memory - // let shmdtret = cage.shmdt_syscall(0xfffff000 as *mut u8); + // assert_eq!(cage.stat_syscall("/dev/random", &mut statdata), 0); + // assert_eq!(statdata.st_rdev, makedev(&DevNo { major: 1, minor: 8 })); - // assert_eq!(shmdtret, shmid); //NaCl requires shmdt to return the shmid, so this is non-posixy + // assert_eq!(cage.stat_syscall("/dev/urandom", &mut statdata), 0); + // assert_eq!(statdata.st_rdev, makedev(&DevNo { major: 1, minor: 9 })); + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); // lindrustfinalize(); // } -// pub fn ut_lind_fs_getpid_getppid() { -// lindrustinit(0); - -// let cage1 = interface::cagetable_getref(1); -// let pid1 = cage1.getpid_syscall(); - -// assert_eq!(cage1.fork_syscall(2), 0); - -// let child = std::thread::spawn(move || { -// let cage2 = interface::cagetable_getref(2); -// let pid2 = cage2.getpid_syscall(); -// let ppid2 = cage2.getppid_syscall(); - -// assert_ne!(pid2, pid1); // make sure the child and the parent have different pids -// assert_eq!(ppid2, pid1); // make sure the child's getppid is correct - -// assert_eq!(cage2.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// }); - -// child.join().unwrap(); -// assert_eq!(cage1.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); -// lindrustfinalize(); -// } - - pub fn ut_lind_fs_sem_fork() { - lindrustinit(0); - let cage = interface::cagetable_getref(1); - let key = 31337; - // Create a shared memory region - let shmid = cage.shmget_syscall(key, 1024, 0666 | IPC_CREAT); - // Attach the shared memory region - let shmatret = cage.shmat_syscall(shmid, 0xfffff000 as *mut u8, 0); - assert_ne!(shmatret, -1); - // Initialize the semaphore with shared between process - let ret_init = cage.sem_init_syscall(shmatret as u32, 1, 1); - assert_eq!(ret_init, 0); - assert_eq!(cage.sem_getvalue_syscall(shmatret as u32), 1); - // Fork child process - assert_eq!(cage.fork_syscall(2), 0); - // Child process - let thread_child = interface::helper_thread(move || { - let cage1 = interface::cagetable_getref(2); - // Child waits for the semaphore - assert_eq!(cage1.sem_wait_syscall(shmatret as u32), 0); - interface::sleep(interface::RustDuration::from_millis(40)); - // Release the semaphore - assert_eq!(cage1.sem_post_syscall(shmatret as u32), 0); - cage1.exit_syscall(libc::EXIT_SUCCESS); - }); - //Parent processes - // Parents waits for the semaphore - assert_eq!(cage.sem_wait_syscall(shmatret as u32), 0); - assert_eq!(cage.sem_getvalue_syscall(shmatret as u32), 0); - interface::sleep(interface::RustDuration::from_millis(100)); - // Parents release the semaphore - assert_eq!(cage.sem_post_syscall(shmatret as u32), 0); - interface::sleep(interface::RustDuration::from_millis(100)); - assert_eq!(cage.sem_getvalue_syscall(shmatret as u32), 1); - // Destroy the semaphore - assert_eq!(cage.sem_destroy_syscall(shmatret as u32), 0); - // mark the shared memory to be rmoved - let shmctlret2 = cage.shmctl_syscall(shmid, IPC_RMID, None); - assert_eq!(shmctlret2, 0); - //detach from shared memory - // panic!(&SHM_METADATA); - let shmdtret = cage.shmdt_syscall(0xfffff000 as *mut u8); - assert_eq!(shmdtret, shmid); - - thread_child.join().unwrap(); - cage.exit_syscall(libc::EXIT_SUCCESS); - lindrustfinalize(); - } + // #[test] + // pub fn ut_lind_fs_mknod_empty_path() { + // //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // // and also performs clean env setup + // let _thelock = setup::lock_and_init(); + + // let cage = interface::cagetable_getref(1); + // let dev = makedev(&DevNo { major: 1, minor: 3 }); + // let path = ""; + // // Check for error when directory is empty + // assert_eq!( + // cage.mknod_syscall(path, S_IRWXA | S_IFCHR as u32, dev), + // -(Errno::ENOENT as i32) + // ); + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } -// pub fn ut_lind_fs_sem_trytimed() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); -// let key = 31337; -// // Create a shared memory region -// let shmid = cage.shmget_syscall(key, 1024, 0666 | IPC_CREAT); -// // Attach the shared memory region -// let shmatret = cage.shmat_syscall(shmid, 0xfffff000 as *mut u8, 0); -// assert_ne!(shmatret, -1); -// // Initialize the semaphore with shared between process -// let ret_init = cage.sem_init_syscall(shmatret as u32, 1, 1); -// // assert_eq!(shmatret as u32, 0); -// assert_eq!(ret_init, 0); -// assert_eq!(cage.sem_getvalue_syscall(shmatret as u32), 1); -// // Fork child process -// assert_eq!(cage.fork_syscall(2), 0); -// // Child process -// let thread_child = interface::helper_thread(move || { -// let cage1 = interface::cagetable_getref(2); -// // Child waits for the semaphore -// assert_eq!(cage1.sem_trywait_syscall(shmatret as u32), 0); -// // Wait -// interface::sleep(interface::RustDuration::from_millis(20)); -// // Release the semaphore -// assert_eq!(cage1.sem_post_syscall(shmatret as u32), 0); -// cage1.exit_syscall(libc::EXIT_SUCCESS); -// }); -// //Parent processes -// let thread_parent = interface::helper_thread(move || { -// // Parents waits for the semaphore -// assert_eq!( -// cage.sem_timedwait_syscall( -// shmatret as u32, -// interface::RustDuration::from_millis(100) -// ), -// 0 -// ); -// assert_eq!(cage.sem_getvalue_syscall(shmatret as u32), 0); -// interface::sleep(interface::RustDuration::from_millis(10)); -// // Parents release the semaphore -// assert_eq!(cage.sem_post_syscall(shmatret as u32), 0); -// assert_eq!(cage.sem_getvalue_syscall(shmatret as u32), 1); -// // Destroy the semaphore -// assert_eq!(cage.sem_destroy_syscall(shmatret as u32), 0); -// // mark the shared memory to be rmoved -// let shmctlret2 = cage.shmctl_syscall(shmid, IPC_RMID, None); -// assert_eq!(shmctlret2, 0); -// //detach from shared memory -// let shmdtret = cage.shmdt_syscall(0xfffff000 as *mut u8); -// assert_eq!(shmdtret, shmid); -// cage.exit_syscall(libc::EXIT_SUCCESS); -// }); -// thread_child.join().unwrap(); -// thread_parent.join().unwrap(); -// lindrustfinalize(); -// } - -// pub fn ut_lind_fs_sem_test() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); -// let key = 31337; -// // Create a shared memory region -// let shmid = cage.shmget_syscall(key, 1024, 0666 | IPC_CREAT); -// // Attach the shared memory region -// let shmatret = cage.shmat_syscall(shmid, 0xfffff000 as *mut u8, 0); -// assert_ne!(shmatret, -1); -// assert_eq!(cage.sem_destroy_syscall(shmatret as u32), -22); -// assert_eq!(cage.sem_getvalue_syscall(shmatret as u32), -22); -// assert_eq!(cage.sem_post_syscall(shmatret as u32), -22); -// // Initialize the semaphore with shared between process -// let ret_init = cage.sem_init_syscall(shmatret as u32, 1, 0); -// assert_eq!(ret_init, 0); -// // Should return errno -// assert_eq!( -// cage.sem_timedwait_syscall(shmatret as u32, interface::RustDuration::from_millis(100)), -// -110 -// ); -// assert_eq!(cage.sem_trywait_syscall(shmatret as u32), -11); -// lindrustfinalize(); -// } - -// pub fn ut_lind_fs_tmp_file_test() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); - -// // Check if /tmp is there -// assert_eq!(cage.access_syscall("/tmp", F_OK), 0); - -// // Open file in /tmp -// let file_path = "/tmp/testfile"; -// let fd = cage.open_syscall(file_path, O_CREAT | O_TRUNC | O_RDWR, S_IRWXA); - -// assert_eq!(cage.write_syscall(fd, str2cbuf("Hello world"), 6), 6); -// assert_eq!(cage.close_syscall(fd), 0); - -// lindrustfinalize(); - -// // Init again -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); - -// // Check if /tmp is there -// assert_eq!(cage.access_syscall("/tmp", F_OK), 0); -// // Check if file is still there (it shouldn't be, assert no) -// assert_eq!(cage.access_syscall(file_path, F_OK), -2); - -// lindrustfinalize(); -// } + // #[test] + // pub fn ut_lind_fs_mknod_nonexisting_parent_directory() { + // //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // // and also performs clean env setup + // let _thelock = setup::lock_and_init(); + + // let cage = interface::cagetable_getref(1); + // let dev = makedev(&DevNo { major: 1, minor: 3 }); + // let path = "/parentdir/file"; + // // Check for error when both parent and file don't exist + // assert_eq!( + // cage.mknod_syscall(path, S_IRWXA | S_IFCHR as u32, dev), + // -(Errno::ENOENT as i32) + // ); + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } + + // #[test] + // pub fn ut_lind_fs_mknod_existing_file() { + // //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // // and also performs clean env setup + // let _thelock = setup::lock_and_init(); + + // let cage = interface::cagetable_getref(1); + // let dev = makedev(&DevNo { major: 1, minor: 3 }); + // let path = "/charfile"; + // // Create a special character file for the first time + // assert_eq!(cage.mknod_syscall(path, S_IRWXA | S_IFCHR as u32, dev), 0); + + // // Check for error when the same file is created again + // assert_eq!( + // cage.mknod_syscall(path, S_IRWXA | S_IFCHR as u32, dev), + // -(Errno::EEXIST as i32) + // ); + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } + + // #[test] + // pub fn ut_lind_fs_mknod_invalid_modebits() { + // //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // // and also performs clean env setup + // let _thelock = setup::lock_and_init(); + + // let cage = interface::cagetable_getref(1); + // let dev = makedev(&DevNo { major: 1, minor: 3 }); + // let path = "/testfile"; + // let invalid_mode = 0o77777; // Invalid mode bits for testing + // // Check for error when the file is being created with invalid mode + // assert_eq!( + // cage.mknod_syscall(path, invalid_mode, dev), + // -(Errno::EPERM as i32) + // ); + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } + + // #[test] + // pub fn ut_lind_fs_mknod_invalid_filetypes() { + // //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // // and also performs clean env setup + // let _thelock = setup::lock_and_init(); + + // // Check for error when file types other than S_IFCHR are passed in the input + // let cage = interface::cagetable_getref(1); + // let dev = makedev(&DevNo { major: 1, minor: 3 }); + // let path = "/invalidfile"; + + // // When file type is S_IFDIR (Directory), error is expected + // assert_eq!( + // cage.mknod_syscall(path, S_IRWXA | S_IFDIR as u32, dev), + // -(Errno::EINVAL as i32) + // ); + + // // When file type is S_IFIFO (FIFO), error is expected + // assert_eq!( + // cage.mknod_syscall(path, S_IRWXA | S_IFIFO as u32, dev), + // -(Errno::EINVAL as i32) + // ); + + // // When file type is S_IFREG (Regular File), error is expected + // assert_eq!( + // cage.mknod_syscall(path, S_IRWXA | S_IFREG as u32, dev), + // -(Errno::EINVAL as i32) + // ); + + // // When file type is S_IFSOCK (Socket), error is expected + // assert_eq!( + // cage.mknod_syscall(path, S_IRWXA | S_IFSOCK as u32, dev), + // -(Errno::EINVAL as i32) + // ); + + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } + + // #[test] + // pub fn ut_lind_fs_mknod_success() { + // //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // // and also performs clean env setup + // let _thelock = setup::lock_and_init(); + + // // let's create /dev/null + // let cage = interface::cagetable_getref(1); + // let dev = makedev(&DevNo { major: 1, minor: 3 }); + + // //making the node with read only permission (S_IRUSR) and check if it gets + // // created successfully + // assert_eq!( + // cage.mknod_syscall("/readOnlyFile", S_IRUSR | S_IFCHR as u32, dev), + // 0 + // ); + + // //making the node with write only permission (S_IWUSR) and check if it gets + // // created successfully + // assert_eq!( + // cage.mknod_syscall("/writeOnlyFile", S_IWUSR | S_IFCHR as u32, dev), + // 0 + // ); + + // //making the node with execute only permission (S_IXUSR) and check if it gets + // // created successfully + // assert_eq!( + // cage.mknod_syscall("/executeOnlyFile", S_IXUSR | S_IFCHR as u32, dev), + // 0 + // ); + + // //now we are going to mknod /dev/null with read, write, and execute flags and + // // permissions and then make sure that it exists + // let path = "/null"; + // assert_eq!(cage.mknod_syscall(path, S_IRWXA | S_IFCHR as u32, dev), 0); + // let fd = cage.open_syscall(path, O_RDWR, S_IRWXA); + + // //checking the metadata of the file: + // let mut statdata = StatData::default(); + + // //should be a chr file, so let's check this + // let mut buf = sizecbuf(4); + // assert_eq!(cage.fstat_syscall(fd, &mut statdata), 0); + // assert_eq!(statdata.st_mode & S_FILETYPEFLAGS as u32, S_IFCHR as u32); + // assert_eq!(statdata.st_rdev, dev); + // assert_eq!(cage.write_syscall(fd, str2cbuf("test"), 4), 4); + // assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 4), 0); + // assert_eq!(cbuf2str(&buf), "\0\0\0\0"); + // assert_eq!(cage.close_syscall(fd), 0); + + // let mut statdata2 = StatData::default(); + + // //try it again with /dev/random + // let dev2 = makedev(&DevNo { major: 1, minor: 8 }); + // let path2 = "/random"; + + // //making the node and then making sure that it exists + // assert_eq!(cage.mknod_syscall(path2, S_IRWXA | S_IFCHR as u32, dev2), 0); + // let fd2 = cage.open_syscall(path2, O_RDWR, S_IRWXA); + + // let mut buf2 = sizecbuf(4); + // assert_eq!(cage.fstat_syscall(fd2, &mut statdata2), 0); + // assert_eq!(statdata2.st_mode & S_FILETYPEFLAGS as u32, S_IFCHR as u32); + // assert_eq!(statdata2.st_rdev, dev2); + // assert_eq!(cage.write_syscall(fd2, str2cbuf("testing"), 7), 7); + // assert_ne!(cage.read_syscall(fd2, buf2.as_mut_ptr(), 7), 0); + // assert_eq!(cage.close_syscall(fd2), 0); + + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } + + #[test] + pub fn ut_lind_fs_multiple_open() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //try to open several files at once -- the fd's should not be overwritten + let fd1 = cage.open_syscall("/foo", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); + let fd2 = cage.open_syscall("/foo", O_RDWR, S_IRWXA); + assert_ne!(fd1, fd2); + + let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; + let mode: u32 = 0o666; // 0666 + let name = "double_open_file"; + + let mut read_buf = sizecbuf(2); + let fd3 = cage.open_syscall(name, flags, mode); + assert_eq!(cage.write_syscall(fd3, str2cbuf("hi"), 2), 2); + assert_eq!(cage.lseek_syscall(fd3, 0, SEEK_SET), 0); + assert_eq!(cage.read_syscall(fd3, read_buf.as_mut_ptr(), 2), 2); + assert_eq!(cbuf2str(&read_buf), "hi"); + + let _fd4 = cage.open_syscall(name, flags, mode); + let mut buf = sizecbuf(5); + assert_eq!(cage.lseek_syscall(fd3, 2, SEEK_SET), 2); + assert_eq!(cage.write_syscall(fd3, str2cbuf("boo"), 3), 3); + assert_eq!(cage.lseek_syscall(fd3, 0, SEEK_SET), 0); + assert_eq!(cage.read_syscall(fd3, buf.as_mut_ptr(), 5), 5); + assert_eq!(cbuf2str(&buf), "\0\0boo"); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_rmdir_normal() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + //and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //We create a new parent directory `/parent_dir` + //and its child directory '/parent_dir/dir` both + //with the required write permission flags, thus + //calling `rmdir_syscall()`on the child directory + //should result in a normal behavior + let path = "/parent_dir/dir"; + assert_eq!(cage.mkdir_syscall("/parent_dir", S_IRWXA), 0); + assert_eq!(cage.mkdir_syscall(path, S_IRWXA), 0); + assert_eq!(cage.rmdir_syscall(path), 0); + //To check if the child directory was successfully + //removed, we call `open_syscall()` on it, and see + //if it correctly returns `Path does not exist` error + assert_eq!( + cage.open_syscall(path, O_TRUNC, S_IRWXA), + -(Errno::ENOENT as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_rmdir_empty_path() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //Trying to remove a directory by providing an empty string + //should return `Given path is an empty string` error + assert_eq!(cage.rmdir_syscall(""), -(Errno::ENOENT as i32)); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_rmdir_nonexist_dir() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //We create a new parent directory `/parent_dir` + //However, we never create its child directory + //'/parent_dir/dir`, thus calling `rmdir_syscall()` + //on this child directory should return + //`Path does not exist` error + let path = "/parent_dir_nonexist/dir"; + assert_eq!(cage.mkdir_syscall("/parent_dir_nonexist", S_IRWXA), 0); + assert_eq!(cage.rmdir_syscall(path), -(Errno::ENOENT as i32)); + // Clean up if the parent directory + let _ = cage.rmdir_syscall("/parent_dir_nonexist"); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_rmdir_root() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //Trying to remove the root directory should return + //`Cannot remove root directory` error + assert_eq!(cage.rmdir_syscall("/"), -(Errno::EBUSY as i32)); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_rmdir_nonempty_dir() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + //and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //We create a new parent directory `/parent_dir` and + //its child directory '/parent_dir/dir`, thus calling `rmdir_syscall()` + //on the parent directory should return `Directory is not empty` error + let path = "/parent_dir_nonempty/dir"; + assert_eq!(cage.mkdir_syscall("/parent_dir_nonempty", S_IRWXA), 0); + assert_eq!(cage.mkdir_syscall(path, S_IRWXA), 0); + assert_eq!( + cage.rmdir_syscall("/parent_dir_nonempty"), + -(Errno::ENOTEMPTY as i32) + ); + // Clean up the directories for clean environment + let _ = cage.rmdir_syscall(path); + let _ = cage.rmdir_syscall("/parent_dir_nonempty"); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_rmdir_nowriteperm_child_dir() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //We create a new parent directory `/parent_dir` with all write permission + //flags and its child directory '/parent_dir/dir` without any write + //permision flags, thus calling `rmdir_syscall()`on the child directory + //should return `Directory does not allow write permission` error + //because the directory cannot be removed if it does not allow + //write permission + let path = "/parent_dir_nwchild/dir"; + assert_eq!(cage.mkdir_syscall("/parent_dir_nwchild", S_IRWXA), 0); + assert_eq!(cage.mkdir_syscall(path, S_IRWXA), 0); + assert_eq!( + cage.chmod_syscall(path, 0o400 | 0o040 | 0o004), + 0 + ); + assert_eq!(cage.rmdir_syscall(path), 0); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_rmdir_nowriteperm_parent_dir() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //We create a new parent directory `/parent_dir` with all write permission + //flags (to be able to create its child directory) and its child directory + //'/parent_dir/dir` with all write permision flags. + let parent_dir = "/parent_dir_nowriteperm"; + let path = "/parent_dir_nowriteperm/dir"; + assert_eq!(cage.mkdir_syscall(parent_dir, S_IRWXA), 0); + assert_eq!(cage.mkdir_syscall(path, S_IRWXA), 0); + //Now, we change the parent directories write permission flags to 0, + //thus calling `rmdir_syscall()`on the child directory + //should return `Directory does not allow write permission` error + //because the directory cannot be removed if its parent directory + //does not allow write permission + assert_eq!(cage.chmod_syscall(parent_dir, 0o500), 0); // Set parent to read + execute only + assert_eq!(cage.rmdir_syscall(path), -(Errno::EACCES as i32)); + + // Restore write permissions to the parent to clean up + assert_eq!(cage.chmod_syscall(parent_dir, S_IRWXA), 0); + // Clean up + assert_eq!(cage.rmdir_syscall(path), 0); + assert_eq!(cage.rmdir_syscall(parent_dir), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + //BUG: + //The correct behavior of the `rmdir_syscall()` when called on a directory + //whose path includes a component that does not allow search permission + //(the read flag) is to return with `EACCES` error. + //However, the `metawalkandparent())` helper function used + //to retrieve the inodes of the directory to be removed and its parent + //directory does not check for search permission. Thus, the following test + //will not return any errors and run normally even though the parent + //directory does not grand search permission. + pub fn ut_lind_fs_search_permission_bug_with_rmdir() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + //and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //Creating the parent directory that does not allow search permission + //by excluding any read flags and specifying only write flags + //to be able to delete the child directory. + let path = "/parent_dir_permissionbug/dir"; + assert_eq!( + cage.mkdir_syscall("/parent_dir_permissionbug", 0o200 | 0o020 | 0o002), + 0 + ); + //Creating the child directory with all the required flags + //and then deleting it. Because of the bug described above, + //removing the directory will not return any errors. + assert_eq!(cage.mkdir_syscall(path, S_IRWXA), -(Errno::EACCES as i32)); + assert_eq!(cage.rmdir_syscall("/parent_dir_permissionbug"), 0); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_stat_file_complex() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let fd = cage.open_syscall("/fooComplex", O_CREAT | O_EXCL | O_WRONLY, S_IRWXA); + + assert_eq!(cage.write_syscall(fd, str2cbuf("hi"), 2), 2); + + let mut statdata = StatData::default(); + let mut statdata2 = StatData::default(); + + assert_eq!(cage.fstat_syscall(fd, &mut statdata), 0); + assert_eq!(statdata.st_size, 2); + assert_eq!(statdata.st_nlink, 1); + + assert_eq!(cage.link_syscall("/fooComplex", "/barComplex"), 0); + assert_eq!(cage.stat_syscall("/fooComplex", &mut statdata), 0); + assert_eq!(cage.stat_syscall("/barComplex", &mut statdata2), 0); + + //check that they are the same and that the link count is 0 + assert!(statdata == statdata2); + assert_eq!(statdata.st_nlink, 2); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_stat_file_mode() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let path = "/fooFileMode"; + let _fd = cage.open_syscall(path, O_CREAT | O_EXCL | O_WRONLY, S_IRWXA); + + let mut statdata = StatData::default(); + assert_eq!(cage.stat_syscall(path, &mut statdata), 0); + assert_eq!(statdata.st_mode, S_IRWXA | S_IFREG as u32); + + //make a file without permissions and check that it is a reg file without + // permissions + let path2 = "/fooFileMode2"; + let _fd2 = cage.open_syscall(path2, O_CREAT | O_EXCL | O_WRONLY, 0); + assert_eq!(cage.stat_syscall(path2, &mut statdata), 0); + assert_eq!(statdata.st_mode, S_IFREG as u32); + + //check that stat can be done on the current (root) dir + assert_eq!(cage.stat_syscall(".", &mut statdata), 0); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + // #[test] + // pub fn ut_lind_fs_statfs() { + // //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // // and also performs clean env setup + // let _thelock = setup::lock_and_init(); + + // let cage = interface::cagetable_getref(1); + // let mut fsdata = FSData::default(); + + // assert_eq!(cage.statfs_syscall("/", &mut fsdata), 0); + // assert_eq!(fsdata.f_type, 0xBEEFC0DE); + // assert_eq!(fsdata.f_bsize, 4096); + + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } + + + // pub fn ut_lind_fs_fstatfs() { + // //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // // and also performs clean env setup + // let _thelock = setup::lock_and_init(); + + // let cage = interface::cagetable_getref(1); + // let mut fsdata = FSData::default(); + + // // Get fd + // let fd = cage.open_syscall("/", O_RDONLY, 0); + // assert!(fd >= 0); + // // fstatfs + // assert_eq!(cage.fstatfs_syscall(fd, &mut fsdata), 0); + // // Check the output + // assert_eq!(fsdata.f_type, 0xBEEFC0DE); + // assert_eq!(fsdata.f_bsize, 4096); + // // Close the file + // assert_eq!(cage.close_syscall(fd), 0); + + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } + + #[test] + pub fn ut_lind_fs_rename() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let old_path = "/test_dir"; + assert_eq!(cage.mkdir_syscall(old_path, S_IRWXA), 0); + assert_eq!(cage.rename_syscall(old_path, "/test_dir_renamed"), 0); + // Remove the directory for a clean environment + assert_eq!(cage.rmdir_syscall("/test_dir_renamed"), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_ftruncate() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let fd = cage.open_syscall("/ftruncate", O_CREAT | O_TRUNC | O_RDWR, S_IRWXA); + assert!(fd >= 0); + + // check if ftruncate() works for extending file with null bytes + assert_eq!(cage.write_syscall(fd, str2cbuf("Hello there!"), 12), 12); + assert_eq!(cage.ftruncate_syscall(fd, 15), 0); + assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); + let mut buf = sizecbuf(15); + assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 15), 15); + assert_eq!(cbuf2str(&buf), "Hello there!\0\0\0"); + + // check if ftruncate() works for cutting off extra bytes + assert_eq!(cage.ftruncate_syscall(fd, 5), 0); + assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); + let mut buf1 = sizecbuf(7); + assert_eq!(cage.read_syscall(fd, buf1.as_mut_ptr(), 7), 5); + assert_eq!(cbuf2str(&buf1), "Hello\0\0"); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_truncate() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let path = String::from("/truncate"); + let fd = cage.open_syscall(&path, O_CREAT | O_TRUNC | O_RDWR, S_IRWXA); + assert!(fd >= 0); + + // check if truncate() works for extending file with null bytes + assert_eq!(cage.write_syscall(fd, str2cbuf("Hello there!"), 12), 12); + assert_eq!(cage.truncate_syscall(&path, 15), 0); + assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); + let mut buf = sizecbuf(15); + assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 15), 15); + assert_eq!(cbuf2str(&buf), "Hello there!\0\0\0"); + + // check if truncate() works for cutting off extra bytes + assert_eq!(cage.truncate_syscall(&path, 5), 0); + assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); + let mut buf1 = sizecbuf(7); + assert_eq!(cage.read_syscall(fd, buf1.as_mut_ptr(), 7), 5); + assert_eq!(cbuf2str(&buf1), "Hello\0\0"); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[cfg(target_os = "macos")] + type CharPtr = *const u8; + + #[cfg(not(target_os = "macos"))] + type CharPtr = *const i8; + + #[test] + pub fn ut_lind_fs_getdents() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let bufsize = 50; + let mut vec = vec![0u8; bufsize as usize]; + let baseptr: *mut u8 = &mut vec[0]; + + assert_eq!(cage.mkdir_syscall("/getdents", S_IRWXA), 0); + let fd = cage.open_syscall("/getdents", O_RDWR, S_IRWXA); + assert_eq!(cage.getdents_syscall(fd, baseptr, bufsize as u32), 48); + + unsafe { + let first_dirent = baseptr as *mut interface::ClippedDirent; + assert!((*first_dirent).d_off == 24); + let reclen_matched: bool = ((*first_dirent).d_reclen == 24); + assert_eq!(reclen_matched, true); + + let nameoffset = baseptr.wrapping_offset(interface::CLIPPED_DIRENT_SIZE as isize); + let returnedname = RustCStr::from_ptr(nameoffset as *const _); + let name_matched: bool = (returnedname + == RustCStr::from_bytes_with_nul(b".\0").unwrap()) + | (returnedname == RustCStr::from_bytes_with_nul(b"..\0").unwrap()); + assert_eq!(name_matched, true); + + let second_dirent = baseptr.wrapping_offset(24) as *mut interface::ClippedDirent; + assert!((*second_dirent).d_off >= 48); + } + + assert_eq!(cage.close_syscall(fd), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + #[test] + fn ut_lind_fs_getdents_invalid_fd() { + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + + let bufsize = 50; + let mut vec = vec![0u8; bufsize as usize]; + let baseptr: *mut u8 = &mut vec[0]; + + // Create a directory + assert_eq!(cage.mkdir_syscall("/getdents", S_IRWXA), 0); + + // Open the directory + let fd = cage.open_syscall("/getdents", O_RDWR, S_IRWXA); + + // Attempt to call `getdents_syscall` with an invalid file descriptor + let result = cage.getdents_syscall(-1, baseptr, bufsize as u32); + + // Assert that the return value is EBADF (errno for "Bad file descriptor") + assert_eq!(result, -(Errno::EBADF as i32)); + + // Close the directory + assert_eq!(cage.close_syscall(fd), 0); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + fn ut_lind_fs_getdents_out_of_range_fd() { + // Acquire a lock on TESTMUTEX to prevent other tests from running concurrently, + // and also perform clean environment setup. + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Allocate a buffer to store directory entries + let bufsize = 1024; + let mut vec = vec![0u8; bufsize as usize]; + let baseptr: *mut u8 = &mut vec[0]; + + // Attempt to call getdents_syscall with a file descriptor out of range + let result = cage.getdents_syscall(1024 + 1, baseptr, bufsize as u32); + + // Verify that it returns EBADF (errno for "Bad file descriptor") + assert_eq!(result, -(Errno::EBADF as i32)); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + fn ut_lind_fs_getdents_non_existing_fd() { + // Acquire a lock on TESTMUTEX to prevent other tests from running concurrently, + // and also perform clean environment setup. + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Allocate a buffer to store directory entries + let bufsize = 1024; + let mut vec = vec![0u8; bufsize as usize]; + let baseptr: *mut u8 = &mut vec[0]; + + // Attempt to call getdents_syscall with a non-existing file descriptor + let result = cage.getdents_syscall(100, baseptr, bufsize as u32); + + // Verify that it returns EBADF (errno for "Bad file descriptor") + assert_eq!(result, -(Errno::EBADF as i32)); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + fn ut_lind_fs_getdents_bufsize_too_small() { + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + let bufsize = interface::CLIPPED_DIRENT_SIZE - 1; // Buffer size smaller than CLIPPED_DIRENT_SIZE + let mut vec = vec![0u8; bufsize as usize]; + let baseptr: *mut u8 = &mut vec[0]; + + // Create a directory + assert_eq!(cage.mkdir_syscall("/getdents", S_IRWXA), 0); + + // Open the directory with O_RDONLY (read-only) + let fd = cage.open_syscall("/getdents", O_RDONLY, S_IRWXA); + + // Attempt to call `getdents_syscall` with a buffer size smaller than + // CLIPPED_DIRENT_SIZE + let result = cage.getdents_syscall(fd, baseptr, bufsize as u32); + + // Assert that the return value is EINVAL (errno for "Invalid argument") + assert_eq!(result, -(Errno::EINVAL as i32)); + + // Close the directory + assert_eq!(cage.close_syscall(fd), 0); + // Clean up: Remove the directory and finalize the test environment + assert_eq!(cage.rmdir_syscall("/getdents"), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + fn ut_lind_fs_getdents_non_directory_fd() { + // Acquire a lock on TESTMUTEX to prevent other tests from running concurrently, + // and also perform clean environment setup. + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Create a regular file + let filepath = "/regularfile"; + let fd = cage.open_syscall(filepath, O_CREAT | O_WRONLY, S_IRWXA); + assert!(fd >= 0); + // Allocate a buffer to store directory entries + let bufsize = 1024; + let mut vec = vec![0u8; bufsize as usize]; + let baseptr: *mut u8 = &mut vec[0]; + + // Attempt to call getdents_syscall on the regular file descriptor + let result = cage.getdents_syscall(fd, baseptr, bufsize as u32); + // Verify that it returns ENOTDIR + assert_eq!(result, -(Errno::ENOTDIR as i32)); + + // Clean up: Close the file descriptor and finalize the test environment + assert_eq!(cage.close_syscall(fd), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_dir_chdir_getcwd() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + //and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + + //Checking if retrieving the current working directory + //`/newcwd1` by passing a string long enough to store + //the directory succeeds + //`newcwdsize` is the size of the string needed to store the new + //current woring directory `/newcwd1` + let newcwdsize = "/newcwd1\0".as_bytes().to_vec().len(); + let newcwdsize_u32: u32 = newcwdsize as u32; + //Creating the new current working directory and checking if it exists + cage.mkdir_syscall("/newcwd1", S_IRWXA); + assert_eq!(cage.access_syscall("newcwd1", F_OK), 0); + //Creating a string large enough to store the new current + //working directory and filling it with 0's + let mut buf = vec![0u8; newcwdsize]; + let bufptr: *mut u8 = &mut buf[0]; + + //First, we change to the root directory and call `getcwd_syscall()` + //to check if the root current working directory is successfully + //retrieved + assert_eq!(cage.chdir_syscall("/"), 0); + assert_eq!(cage.getcwd_syscall(bufptr, 2), 0); + assert_eq!(std::str::from_utf8(&buf).unwrap(), "/\0\0\0\0\0\0\0\0"); + + //Now, we change to the newly created directory and check if it is + //successfully retrieved as a new current working directory + assert_eq!(cage.chdir_syscall("/newcwd1"), 0); + assert_eq!(cage.getcwd_syscall(bufptr, newcwdsize_u32), 0); + assert_eq!(std::str::from_utf8(&buf).unwrap(), "/newcwd1\0"); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_getcwd_invalid_args() { + //Acquiring a lock on TESTMUTEX prevents other tests from running + //concurrently, and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + + //`/newcwd2` is a valid directory that we will create to check + //if calling `getcwd_syscall()` with invalid arguments + //correctly returns the required errors. + //`newcwdsize` is the size of the string needed to store the new + //current working directory `/newcwd1` + let newcwdsize = "/newcwd2\0".as_bytes().to_vec().len(); + let newcwdsize_u32: u32 = newcwdsize as u32; + //Creating the new current working directory, checking if it exists + //and changing to it + cage.mkdir_syscall("/newcwd2", S_IRWXA); + assert_eq!(cage.access_syscall("newcwd2", F_OK), 0); + assert_eq!(cage.chdir_syscall("newcwd2"), 0); + + //Checking if passing a valid string pointer and a size of 0 to + //`getcwd_syscall()` correctly results in `The size argument is zero and + //buf is not a null pointer` error + //Creating a string large enough to store the new current + //working directory and filling it with 0's + let mut buf = vec![0u8; newcwdsize]; + let bufptr: *mut u8 = &mut buf[0]; + assert_eq!(cage.getcwd_syscall(bufptr, 0), -(Errno::EINVAL as i32)); + + //Checking if passing a valid string pointer and a non-zero size smaller + //than the size of the current working directory plus the terminating null + //character to `getcwd_syscall()` correctly results in `The bufsize argument + //is less than the length of the absolute pathname of the working directory, + //including the terminating null byte` error + assert_eq!( + cage.getcwd_syscall(bufptr, newcwdsize_u32 - 1), + -(Errno::ERANGE as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_exec_cloexec() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let mut uselessstatdata = StatData::default(); + + let fd1 = cage.open_syscall( + "/cloexecuted", + O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, + S_IRWXA, + ); + let fd2 = cage.open_syscall("/cloexekept", O_CREAT | O_TRUNC | O_RDWR, S_IRWXA); + assert!(fd1 > 0); + assert!(fd2 > 0); + assert_eq!(cage.fstat_syscall(fd1, &mut uselessstatdata), 0); + assert_eq!(cage.fstat_syscall(fd2, &mut uselessstatdata), 0); + + assert_eq!(cage.exec_syscall(2), 0); + + let execcage = interface::cagetable_getref(2); + assert_eq!( + execcage.fstat_syscall(fd1, &mut uselessstatdata), + -(Errno::EBADF as i32) + ); + assert_eq!(execcage.fstat_syscall(fd2, &mut uselessstatdata), 0); + + assert_eq!(execcage.close_syscall(fd2), 0); + assert_eq!(cage.unlink_syscall("/cloexecuted"), 0); + assert_eq!(cage.unlink_syscall("/cloexekept"), 0); + + assert_eq!(execcage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_shm() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let key = 31337; + let mut shmidstruct = ShmidsStruct::default(); + + // shmget returns an identifier in shmid + let shmid = cage.shmget_syscall(key, 1024, 0666 | IPC_CREAT); + + // shmat to attach to shared memory + let shmatret = cage.shmat_syscall(shmid, 0xfffff000 as *mut u8, 0); + + assert_ne!(shmatret, -1); + + // get struct info + let shmctlret1 = cage.shmctl_syscall(shmid, IPC_STAT, Some(&mut shmidstruct)); + + assert_eq!(shmctlret1, 0); + + assert_eq!(shmidstruct.shm_nattch, 1); + + // mark the shared memory to be rmoved + let shmctlret2 = cage.shmctl_syscall(shmid, IPC_RMID, None); + + assert_eq!(shmctlret2, 0); + + //detach from shared memory + let shmdtret = cage.shmdt_syscall(0xfffff000 as *mut u8); + + assert_eq!(shmdtret, shmid); //NaCl requires shmdt to return the shmid, so this is non-posixy + + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_getpid_getppid() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage1 = interface::cagetable_getref(1); + let pid1 = cage1.getpid_syscall(); + + assert_eq!(cage1.fork_syscall(2), 0); + + let child = std::thread::spawn(move || { + let cage2 = interface::cagetable_getref(2); + let pid2 = cage2.getpid_syscall(); + let ppid2 = cage2.getppid_syscall(); + + assert_ne!(pid2, pid1); // make sure the child and the parent have different pids + assert_eq!(ppid2, pid1); // make sure the child's getppid is correct + + assert_eq!(cage2.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + }); + + child.join().unwrap(); + assert_eq!(cage1.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + // This test verifies the functionality of semaphores in a fork scenario. + // The test involves a parent process and a child process that synchronize + //their execution using a shared semaphore. The test aims to ensure: + // 1. The semaphore is initialized correctly. + // 2. The child process can acquire and release the semaphore. + // 3. The parent process can acquire and release the semaphore after the child + // process exits. + // 4. The semaphore can be destroyed safely. + #[test] + pub fn ut_lind_fs_sem_fork() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let key = 31337; + + // Create a shared memory region of 1024 bytes. This region will be + // shared between the parent and child process. + // IPC_CREAT tells the system to create a new memory segment for the shared + // memory and 0666 sets the access permissions of the memory segment. + let shmid = cage.shmget_syscall(key, 1024, 0666 | IPC_CREAT); + + // Attach shared memory for semaphore access. + let shmatret = cage.shmat_syscall(shmid, 0xfffff000 as *mut u8, 0); + assert_ne!(shmatret, -1); + // Initialize semaphore in shared memory (initial value: 1, available). + let ret_init = cage.sem_init_syscall(shmatret as u32, 1, 1); + assert_eq!(ret_init, 0); + assert_eq!(cage.sem_getvalue_syscall(shmatret as u32), 1); + // Fork process to create child (new cagetable ID 2) for semaphore testing. + assert_eq!(cage.fork_syscall(2), 0); + // Create thread to simulate child process behavior after forking. + let thread_child = interface::helper_thread(move || { + // Set reference to child process's cagetable (ID 2) for independent operation. + let cage1 = interface::cagetable_getref(2); + // Child process blocks on semaphore wait (decrementing it from 1 to 0). + assert_eq!(cage1.sem_wait_syscall(shmatret as u32), 0); + // Simulate processing time with 40ms delay. + interface::sleep(interface::RustDuration::from_millis(40)); + // Child process releases semaphore, signaling its availability to parent + //(value increases from 0 to 1). + assert_eq!(cage1.sem_post_syscall(shmatret as u32), 0); + cage1.exit_syscall(libc::EXIT_SUCCESS); + }); + + // Parent waits on semaphore (blocks until released by child, decrementing to + // 0). + assert_eq!(cage.sem_wait_syscall(shmatret as u32), 0); + assert_eq!(cage.sem_getvalue_syscall(shmatret as u32), 0); + // Simulate parent process processing time with 100ms delay to ensure + // synchronization. + interface::sleep(interface::RustDuration::from_millis(100)); + // Wait for child process to finish to prevent race conditions before destroying + // semaphore. Release semaphore, making it available again (value + // increases to 1). + assert_eq!(cage.sem_post_syscall(shmatret as u32), 0); + thread_child.join().unwrap(); + + // Destroy the semaphore + assert_eq!(cage.sem_destroy_syscall(shmatret as u32), 0); + // Mark the shared memory segment to be removed. + let shmctlret2 = cage.shmctl_syscall(shmid, IPC_RMID, None); + assert_eq!(shmctlret2, 0); + //detach from shared memory + let shmdtret = cage.shmdt_syscall(0xfffff000 as *mut u8); + assert_eq!(shmdtret, shmid); + cage.exit_syscall(libc::EXIT_SUCCESS); + + lindrustfinalize(); + } + + // This test verifies the functionality of timed semaphores in a fork scenario. + // It involves a parent process and a child process that synchronize their + // execution using a shared semaphore with a timeout. The test aims to + // ensure: + // 1. The semaphore is initialized correctly. + // 2. The child process can acquire and release the semaphore. + // 3. The parent process can acquire the semaphore using a timed wait operation + // with a + // timeout, and the semaphore is acquired successfully. + // 4. The parent process can release the semaphore. + // 5. The semaphore can be destroyed safely. + #[test] + pub fn ut_lind_fs_sem_trytimed() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let key = 31337; + // Create a shared memory region of 1024 bytes. + //This region will be shared between the parent and child process. + // IPC_CREAT tells the system to create a new memory segment for the shared + // memory and 0666 sets the access permissions of the memory segment. + let shmid = cage.shmget_syscall(key, 1024, 0666 | IPC_CREAT); + // Attach the shared memory region to the address space of the process + // to make sure for both processes to access the shared semaphore. + let shmatret = cage.shmat_syscall(shmid, 0xfffff000 as *mut u8, 0); + assert_ne!(shmatret, -1); + // Initialize semaphore in shared memory (initial value: 1, available). + let ret_init = cage.sem_init_syscall(shmatret as u32, 1, 1); + assert_eq!(ret_init, 0); + assert_eq!(cage.sem_getvalue_syscall(shmatret as u32), 1); + // Fork process, creating a child process with its own independent cagetable (ID + // 2). + assert_eq!(cage.fork_syscall(2), 0); + // Define the child process behavior in a separate thread + let thread_child = interface::helper_thread(move || { + // Get reference to child's cagetable (ID 2) for independent operations. + let cage1 = interface::cagetable_getref(2); + // Child process blocks on semaphore, waiting until it becomes available + //(semaphore decremented to 0). + assert_eq!(cage1.sem_wait_syscall(shmatret as u32), 0); + // Simulate some work by sleeping for 20 milliseconds. + interface::sleep(interface::RustDuration::from_millis(20)); + // Child process releases semaphore, signaling its availability to the parent + // process + //(value increases from 0 to 1). + assert_eq!(cage1.sem_post_syscall(shmatret as u32), 0); + cage1.exit_syscall(libc::EXIT_SUCCESS); + }); + // Parent process waits (with 100ms timeout) for semaphore release by child + //returns 0 if acquired successfully before timeout. + assert_eq!( + cage.sem_timedwait_syscall(shmatret as u32, interface::RustDuration::from_millis(100)), + 0 + ); + assert_eq!(cage.sem_getvalue_syscall(shmatret as u32), 0); + // Simulate some work by sleeping for 10 milliseconds. + interface::sleep(interface::RustDuration::from_millis(10)); + // Release semaphore, signaling its availability for parent + //(value increases from 0 to 1). + assert_eq!(cage.sem_post_syscall(shmatret as u32), 0); + + // wait for the child process to exit before destroying the semaphore. + thread_child.join().unwrap(); + + // Destroy the semaphore + assert_eq!(cage.sem_destroy_syscall(shmatret as u32), 0); + // Mark the shared memory segment to be removed. + let shmctlret2 = cage.shmctl_syscall(shmid, IPC_RMID, None); + assert_eq!(shmctlret2, 0); + // Detach from the shared memory region. + let shmdtret = cage.shmdt_syscall(0xfffff000 as *mut u8); + assert_eq!(shmdtret, shmid); + + cage.exit_syscall(libc::EXIT_SUCCESS); + + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_sem_test() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let key = 31337; + // Create a shared memory region + let shmid = cage.shmget_syscall(key, 1024, 0666 | IPC_CREAT); + // Attach the shared memory region + let shmatret = cage.shmat_syscall(shmid, 0xfffff000 as *mut u8, 0); + assert_ne!(shmatret, -1); + assert_eq!(cage.sem_destroy_syscall(shmatret as u32), -22); + assert_eq!(cage.sem_getvalue_syscall(shmatret as u32), -22); + assert_eq!(cage.sem_post_syscall(shmatret as u32), -22); + // Initialize the semaphore with shared between process + let ret_init = cage.sem_init_syscall(shmatret as u32, 1, 0); + assert_eq!(ret_init, 0); + // Should return errno + assert_eq!( + cage.sem_timedwait_syscall(shmatret as u32, interface::RustDuration::from_millis(100)), + -110 + ); + assert_eq!(cage.sem_trywait_syscall(shmatret as u32), -11); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_tmp_file_test() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Check if /tmp is there + if cage.access_syscall("/tmp", F_OK) != 0 { + assert_eq!(cage.mkdir_syscall("/tmp", S_IRWXA), 0, "Failed to create /tmp directory"); + } + assert_eq!(cage.access_syscall("/tmp", F_OK), 0); + // Open file in /tmp + let file_path = "/tmp/testfile"; + let fd = cage.open_syscall(file_path, O_CREAT | O_TRUNC | O_RDWR, S_IRWXA); + + assert_eq!(cage.write_syscall(fd, str2cbuf("Hello world"), 6), 6); + assert_eq!(cage.close_syscall(fd), 0); + // Explicitly delete the file to clean up + assert_eq!(cage.unlink_syscall(file_path), 0, "Failed to delete /tmp/testfile"); + + lindrustfinalize(); + + // Init again + lindrustinit(0); + let cage = interface::cagetable_getref(1); + // Ensure /tmp is created again after reinitialization + if cage.access_syscall("/tmp", F_OK) != 0 { + assert_eq!(cage.mkdir_syscall("/tmp", S_IRWXA), 0, "Failed to recreate /tmp directory"); + } + + // Check if /tmp is there + assert_eq!(cage.access_syscall("/tmp", F_OK), 0); + // Check if file is still there (it shouldn't be, assert no) + assert_eq!(cage.access_syscall(file_path, F_OK), -2); + + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_mkdir_empty_directory() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let path = ""; + // Check for error when directory is empty + assert_eq!(cage.mkdir_syscall(path, S_IRWXA), -(Errno::ENOENT as i32)); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_mkdir_nonexisting_directory() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let path = "/parentdir/dir"; + // Ensure the directories do not exist for clean environment setup + // BUG: this rmdir needs to be recursive, we'll change this after we PR a new version of the lindfs tool + // Clear the directory if it exists use _ to ignore the return value + let _ = cage.rmdir_syscall("/parentdir/dir"); + let _ = cage.rmdir_syscall("/parentdir"); + // Check for error when both parent and child directories don't exist + assert_eq!(cage.mkdir_syscall(path, S_IRWXA), -(Errno::ENOENT as i32)); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_mkdir_existing_directory() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let path = "/parentdir"; + // Create a parent directory + cage.mkdir_syscall(path, S_IRWXA); + // Check for error when the same directory is created again + assert_eq!(cage.mkdir_syscall(path, S_IRWXA), -(Errno::EEXIST as i32)); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_mkdir_invalid_modebits() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let path = "/parentdir"; + let invalid_mode = 0o77777; // Invalid mode bits + // Create a parent directory + cage.mkdir_syscall(path, S_IRWXA); + // Check for error when a directory is being created with invalid mode + assert_eq!( + cage.mkdir_syscall("/parentdir/dir", invalid_mode), + -(Errno::EPERM as i32) + ); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_mkdir_success() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let path = "/parentdir"; + // Create a parent directory + cage.mkdir_syscall(path, S_IRWXA); + + // Get the stat data for the parent directory and check for inode link count to + // be 3 initially + let mut statdata = StatData::default(); + assert_eq!(cage.stat_syscall(path, &mut statdata), 0); + assert_eq!(statdata.st_nlink, 3); + + // Create a child directory inside parent directory with valid mode bits + assert_eq!(cage.mkdir_syscall("/parentdir/dir", S_IRWXA), 0); + + // Get the stat data for the child directory and check for inode link count to + // be 3 initially + let mut statdata2 = StatData::default(); + assert_eq!(cage.stat_syscall("/parentdir/dir", &mut statdata2), 0); + assert_eq!(statdata2.st_nlink, 3); + + // Get the stat data for the parent directory and check for inode link count to + // be 4 now as a new child directory has been created. + let mut statdata3 = StatData::default(); + assert_eq!(cage.stat_syscall(path, &mut statdata3), 0); + assert_eq!(statdata3.st_nlink, 4); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_mkdir_using_symlink() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Create a file which will be referred to as originalFile + let fd = cage.open_syscall("/originalFile", O_CREAT | O_EXCL | O_WRONLY, S_IRWXA); + assert_eq!(cage.write_syscall(fd, str2cbuf("hi"), 2), 2); + + // Create a link between two files where the symlinkFile is originally not + // present But while linking, symlinkFile will get created + assert_eq!(cage.link_syscall("/originalFile", "/symlinkFile"), 0); + + // Check for error while creating the symlinkFile again as it would already be + // created while linking the two files above. + assert_eq!( + cage.mkdir_syscall("/symlinkFile", S_IRWXA), + -(Errno::EEXIST as i32) + ); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_open_empty_directory() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let path = ""; + // Check for error when directory is empty + assert_eq!( + cage.open_syscall(path, O_CREAT | O_TRUNC | O_RDWR, S_IRWXA), + -(Errno::ENOENT as i32) + ); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_open_nonexisting_parentdirectory_and_file() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let path = "/dir/file"; + // Check for error when neither file nor parent exists and O_CREAT flag is not + // present + assert_eq!( + cage.open_syscall(path, F_GETFD, S_IRWXA), + -(Errno::ENOENT as i32) + ); + + // Check for error when neither file nor parent exists and O_CREAT flag is + // present + assert_eq!( + cage.open_syscall(path, O_CREAT, S_IRWXA), + -(Errno::ENOENT as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_open_existing_parentdirectory_and_nonexisting_file() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + // Create a parent directory + assert_eq!(cage.mkdir_syscall("/dir", S_IRWXA), 0); + let path = "/dir/file"; + + // Check for error when parent directory exists but file doesn't exist and + // O_CREAT is not present + assert_eq!( + cage.open_syscall(path, O_TRUNC, S_IRWXA), + -(Errno::ENOENT as i32) + ); + + // Check for error when parent directory exists but file doesn't exist and + // Filetype Flags contain S_IFCHR flag + assert_eq!( + cage.open_syscall(path, 0o20000 | O_CREAT, S_IRWXA), + -(Errno::EINVAL as i32) + ); + + // Check for error when parent directory exists but file doesn't exist and mode + // bits are invalid + let invalid_mode = 0o77777; + assert_eq!( + cage.open_syscall(path, O_CREAT, invalid_mode), + -(Errno::EPERM as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_open_existing_file_without_flags() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + // This test is used for validating two scenarios: + // 1. When the non-existing file is opened using O_CREAT flag, it should open + // successfully. + // 2. When the same existing file is being opened without O_CREAT flag, it + // should open successfully. + let cage = interface::cagetable_getref(1); + + // Open a non-existing file with O_CREAT flag + // This should create a new file with a valid file descriptor + let path = "/test"; + let fd = cage.open_syscall(path, O_CREAT | O_RDWR, S_IRWXA); + assert!(fd > 0); + + // Open the existing file without O_CREAT and O_EXCL + // The file should open successfully as the two flags are not set while + // re-opening the file + let fd2 = cage.open_syscall(path, O_RDONLY, 0); + assert!(fd2 > 0); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_open_existing_file_with_flags() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + // This test is used for validating two scenarios: + // 1. When the non-existing file is opened using O_CREAT flag, it should open + // successfully. + // 2. When the same existing file is opened using O_CREAT and O_EXCL flags, it + // should return an error for file already existing. + let cage = interface::cagetable_getref(1); + + // Open a non-existing file with O_CREAT flag + // This should create a new file with a valid file descriptor + let path = "/test"; + let fd = cage.open_syscall(path, O_CREAT | O_RDWR, S_IRWXA); + assert!(fd > 0); + + // Open the existing file with O_CREAT and O_EXCL flags + // The file should not open successfully as the two flags are set while + // re-opening the file It should return an error for "File already + // exists" + assert_eq!( + cage.open_syscall(path, O_CREAT | O_EXCL | O_RDONLY, S_IRWXA), + -(Errno::EEXIST as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_open_create_new_file_and_check_link_count() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Create a new file + let path = "/newfile.txt"; + let fd = cage.open_syscall(path, O_CREAT | O_RDWR, S_IRWXA); + assert!(fd > 0); + + // Write a string to the newly opened file of size 12 + assert_eq!(cage.write_syscall(fd, str2cbuf("hello there!"), 12), 12); + + // Get the stat data for the file and check for file attributes + let mut statdata = StatData::default(); + assert_eq!(cage.stat_syscall(path, &mut statdata), 0); + + // Validate the link count for the new file to be 1 + assert_eq!(statdata.st_nlink, 1); + + // Validate the size of the file to be 12 + assert_eq!(statdata.st_size, 12); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_open_existing_file_with_o_trunc_flag() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Create a new file + let path = "/file.txt"; + let fd = cage.open_syscall(path, O_CREAT | O_WRONLY, S_IRWXA); + assert!(fd > 0); + // Write a string to the newly opened file of size 12 + assert_eq!(cage.write_syscall(fd, str2cbuf("hello there!"), 12), 12); + // Get the stat data for the file and check for file attributes + let mut statdata = StatData::default(); + assert_eq!(cage.stat_syscall(path, &mut statdata), 0); + // Validate the size of the file to be 12 + assert_eq!(statdata.st_size, 12); + + // Open the same file with O_TRUNC flag + // Since the file is truncated, the size of the file should be truncated to 0. + let fd2 = cage.open_syscall(path, O_WRONLY | O_TRUNC, S_IRWXA); + assert!(fd2 > 0); + // Get the stat data for the same file and check for file attributes + assert_eq!(cage.stat_syscall(path, &mut statdata), 0); + // Validate the size of the file to be 0 as the file is truncated now + assert_eq!(statdata.st_size, 0); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_open_new_file_with_s_ifchar_flag() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Create a parent directory + assert_eq!(cage.mkdir_syscall("/testdir", S_IRWXA), 0); + let path = "/testdir/file"; + + // Attempt to open a file with S_IFCHR flag, which should be invalid for regular + // files + assert_eq!( + cage.open_syscall(path, O_CREAT | 0o20000, S_IRWXA), + -(Errno::EINVAL as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_creat_new_file() { + // Since this call is almost similar to open_syscall, and we have + // covered all the possible test scenarios for open_syscall above. So, + // just testing the basic working flow for the creat_sycall. + + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Create a file and validate the size of it. + let path = "/creatFile"; + let fd = cage.creat_syscall(path, S_IRWXA); + assert!(fd > 0); + + let mut statdata = StatData::default(); + + // The size of the file should be 0 + assert_eq!(cage.stat_syscall(path, &mut statdata), 0); + assert_eq!(statdata.st_size, 0); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_creat_truncate_existing_file() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let path = "/creatFile"; + // Create a new file + let fd = cage.creat_syscall(path, S_IRWXA); + + // Write a string to the newly opened file of size 12 + assert_eq!(cage.write_syscall(fd, str2cbuf("hello there!"), 12), 12); + + // Get the stat data for the file and check for file attributes + let mut statdata = StatData::default(); + assert_eq!(cage.stat_syscall(path, &mut statdata), 0); + + // Validate the size of the file to be 12 + assert_eq!(statdata.st_size, 12); + + // Call the function on the existing file, which should truncate + // the file size to 0. + let _fd2 = cage.creat_syscall(path, S_IRWXA); + assert_eq!(cage.stat_syscall(path, &mut statdata), 0); + + // Validate the size of the file to be 0 now as should be truncated + assert_eq!(statdata.st_size, 0); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_read_write_only_fd() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Test to create a file with write only permissions, and check if + // a valid error is returned when the file is used for reading. + let fd = cage.open_syscall("/test_file", O_CREAT | O_WRONLY, S_IRWXA); + let mut read_buf = sizecbuf(5); + assert_eq!( + cage.read_syscall(fd, read_buf.as_mut_ptr(), 5), + -(Errno::EBADF as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_read_from_directory() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Create a directory and try to read from it. + // We should expect an error (EISDIR) as reading from a directory is not + // supported. + let path = "/test_dir"; + assert_eq!(cage.mkdir_syscall(path, S_IRWXA), 0); + let fd = cage.open_syscall(path, O_RDONLY, S_IRWXA); + + let mut read_buf = sizecbuf(5); + assert_eq!( + cage.read_syscall(fd, read_buf.as_mut_ptr(), 5), + -(Errno::EISDIR as i32) + ); + let _ = cage.rmdir_syscall(path); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_read_from_epoll() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Create an Epoll and try to read from it. + // We should expect an error (EINVAL) as reading from an Epoll is not supported. + let epfd = cage.epoll_create_syscall(1); + assert!(epfd > 0); + let mut read_buf = sizecbuf(5); + assert_eq!( + cage.read_syscall(epfd, read_buf.as_mut_ptr(), 5), + -(Errno::EINVAL as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_read_from_regular_file() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // This test mainly tests two scenarios for reading from a regular file: + // * Reading from a file should initially start from 0 position. + // * Once read, the position of the seek pointer in the file descriptor should + // increment by the count of bytes read. If the read is performed again, then + // the position should continue from the point it was previously left. + let fd = cage.open_syscall("/test_file", O_CREAT | O_TRUNC | O_RDWR, S_IRWXA); + assert!(fd >= 0); + + // Write sample data to the file. + assert_eq!(cage.write_syscall(fd, str2cbuf("hello there!"), 12), 12); + + // Set the initial position to 0 in the file descriptor. + assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); + + // Read first 5 bytes from the file, and assert the result. + let mut read_buf1 = sizecbuf(5); + assert_eq!(cage.read_syscall(fd, read_buf1.as_mut_ptr(), 5), 5); + assert_eq!(cbuf2str(&read_buf1), "hello"); + + // Read next 7 bytes which should start from the previous position. + let mut read_buf2 = sizecbuf(7); + assert_eq!(cage.read_syscall(fd, read_buf2.as_mut_ptr(), 7), 7); + assert_eq!(cbuf2str(&read_buf2), " there!"); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_read_from_chardev_file() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // This test mainly tests the case for reading from a character device type + // file. In this case, we are trying to read 100 bytes from the + // "/dev/zero" file, which should return 100 bytes of "0" filled + // characters. + let path = "/dev/zero"; + if cage.access_syscall(path, F_OK) != 0 { + let fd = cage.open_syscall(path, O_CREAT | O_TRUNC | O_RDWR, S_IRWXA); + // Write 100 bytes of 0 to mimic /dev/zero behavior + let write_data = vec![0u8; 100]; + assert_eq!(cage.write_syscall(fd, write_data.as_ptr(), 100), 100, "Failed to write zeros to /dev/zero"); + assert_eq!(cage.close_syscall(fd), 0); + } + // Open the test file again for reading + let fd = cage.open_syscall(path, O_RDWR, S_IRWXA); + + // Verify if the returned count of bytes is 100. + // Seek to the beginning of the file + assert_eq!(cage.lseek_syscall(fd, 0, libc::SEEK_SET), 0, "Failed to seek to the beginning of /dev/zero"); + // Read 100 bytes from the file + let mut read_bufzero = sizecbuf(100); + assert_eq!(cage.read_syscall(fd, read_bufzero.as_mut_ptr(), 100), 100); + // Verify if the characters present in the buffer are all "0". + assert_eq!( + cbuf2str(&read_bufzero), + std::iter::repeat("\0") + .take(100) + .collect::() + .as_str() + ); + assert_eq!(cage.close_syscall(fd), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_read_from_sockets() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // This test mainly tests the case for reading data from a pair of Sockets. + // In this case, we create a socket pair of two sockets, and send data through + // one socket, and try to read it from the other one using `read_syscall()`. + let mut socketpair = interface::SockPair::default(); + + // Verify if the socketpair is formed successfully. + assert_eq!( + Cage::socketpair_syscall(&cage.clone(), libc::AF_UNIX, libc::SOCK_STREAM, 0, &mut socketpair), + 0 + ); + // Verify if the number of bytes sent to socket1 is correct. + assert_eq!( + cage.send_syscall(socketpair.sock1, str2cbuf("test"), 4, 0), + 4 + ); + // Verify if the number of bytes received by socket2 is correct. + let mut buf2 = sizecbuf(4); + assert_eq!(cage.read_syscall(socketpair.sock2, buf2.as_mut_ptr(), 4), 4); + // Verify if the data received inside the buffer is correct. + assert_eq!(cbuf2str(&buf2), "test"); + // Close the sockets + assert_eq!(cage.close_syscall(socketpair.sock1), 0); + assert_eq!(cage.close_syscall(socketpair.sock2), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_read_from_pipe_blocking_mode() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // This test mainly tests the case of reading data from the pipe. + // We create two pipes, i.e., Read and Write and validate if the data + // received is correct or not. + + // Create a pipe of read and write file descriptors. + let mut pipe_fds = PipeArray::default(); + assert_eq!(cage.pipe_syscall(&mut pipe_fds), 0); + let read_fd = pipe_fds.readfd; + let write_fd = pipe_fds.writefd; + + let write_data = "Testing"; + let mut buf = sizecbuf(7); + + // Write data to the pipe + assert_eq!( + cage.write_syscall(write_fd, write_data.as_ptr(), write_data.len()), + write_data.len() as i32 + ); + + // Read the data from the pipe and verify its count. + assert_eq!( + cage.read_syscall(read_fd, buf.as_mut_ptr(), buf.len()), + write_data.len() as i32 + ); + // Verify if the data returned in the pipe buffer is correct. + assert_eq!(cbuf2str(&buf), write_data); + + // Close the file descriptors + assert_eq!(cage.close_syscall(read_fd), 0); + assert_eq!(cage.close_syscall(write_fd), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_read_from_pipe_nonblocking_mode() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // This test mainly tests the case of reading data from the pipe, but in + // non-blocking mode. We create two pipes, i.e., Read and Write and + // validate if the data received is correct or not. + + // Create a pipe of read and write file descriptors. + let mut pipe_fds = PipeArray::default(); + assert_eq!(cage.pipe_syscall(&mut pipe_fds), 0); + let read_fd = pipe_fds.readfd; + let write_fd = pipe_fds.writefd; + + let write_data = "Testing"; + let mut buf = sizecbuf(7); + + // Set pipe to non-blocking mode + assert_eq!(cage.fcntl_syscall(read_fd, F_SETFL, O_NONBLOCK), 0); + + // Read from the pipe (should return EAGAIN as there's no data yet) + assert_eq!( + cage.read_syscall(read_fd, buf.as_mut_ptr(), buf.len()), + -(Errno::EAGAIN as i32) + ); + + // Write data to the pipe + assert_eq!( + cage.write_syscall(write_fd, write_data.as_ptr(), write_data.len()), + write_data.len() as i32 + ); + + // Read the data from the pipe and verify its count. + assert_eq!( + cage.read_syscall(read_fd, buf.as_mut_ptr(), buf.len()), + write_data.len() as i32 + ); + // Verify if the data returned in the pipe buffer is correct. + assert_eq!(cbuf2str(&buf), write_data); + + // Close the file descriptors + assert_eq!(cage.close_syscall(read_fd), 0); + assert_eq!(cage.close_syscall(write_fd), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_pread_write_only_fd() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Test to create a file with write only permissions, and check if + // a valid error is returned when the file is used for reading. + let fd = cage.open_syscall("/test_file", O_CREAT | O_WRONLY, S_IRWXA); + let mut read_buf = sizecbuf(5); + assert_eq!( + cage.pread_syscall(fd, read_buf.as_mut_ptr(), 5, 0), + -(Errno::EBADF as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_pread_from_file() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // This test mainly tests two scenarios for reading from a file using + // `pread_syscall()`. + // * Reading from a file from the starting position offset(0). + // * Reading from a file from a random position offset. + let fd = cage.open_syscall("/test_file", O_CREAT | O_TRUNC | O_RDWR, S_IRWXA); + assert!(fd >= 0); + + // Write sample data to the file. + assert_eq!(cage.write_syscall(fd, str2cbuf("hello there!"), 12), 12); + + // Set the initial position to 0 in the file descriptor. + assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); + + // Read first 5 bytes from the file, and assert the result. + let mut read_buf1 = sizecbuf(5); + assert_eq!(cage.pread_syscall(fd, read_buf1.as_mut_ptr(), 5, 0), 5); + assert_eq!(cbuf2str(&read_buf1), "hello"); + + // Read 5 bytes, but from the 6th position offset of the file. + let mut read_buf2 = sizecbuf(5); + assert_eq!(cage.pread_syscall(fd, read_buf2.as_mut_ptr(), 5, 6), 5); + assert_eq!(cbuf2str(&read_buf2), "there"); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_pread_from_directory() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let mut buf = sizecbuf(5); + + // Test for invalid directory should fail + let path = "/test_dir"; + assert_eq!(cage.mkdir_syscall(path, S_IRWXA), 0); + // Open the directory with O_RDONLY (appropriate for directories) + let fd = cage.open_syscall(path, O_RDONLY, S_IRWXA); + assert!(fd >= 0); + assert_eq!( + cage.pread_syscall(fd, buf.as_mut_ptr(), buf.len(), 0), + -(Errno::EISDIR as i32) + ); + // Clean up the directory for clean environment + assert_eq!(cage.rmdir_syscall(path), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_pread_invalid_types() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let mut buf = sizecbuf(5); + + // Test for invalid pipe + // Try reading the data from the pipe and check for error. + let mut pipe_fds = PipeArray::default(); + assert_eq!(cage.pipe_syscall(&mut pipe_fds), 0); + let read_fd = pipe_fds.readfd; + assert_eq!( + cage.pread_syscall(read_fd, buf.as_mut_ptr(), buf.len(), 0), + -(Errno::ESPIPE as i32) + ); + + // Test for invalid sockets + // Try reading the data from the socket and check for error. + let mut socketpair = interface::SockPair::default(); + assert_eq!( + Cage::socketpair_syscall(&cage.clone(), libc::AF_UNIX,libc::SOCK_STREAM, 0, &mut socketpair), + 0 + ); + assert_eq!( + cage.pread_syscall(socketpair.sock2, buf.as_mut_ptr(), 4, 0), + -(Errno::ESPIPE as i32) + ); + + // Test for invalid epoll + // Try reading the data from the epoll and check for error. + let epfd = cage.epoll_create_syscall(1); + assert_eq!( + cage.pread_syscall(epfd, buf.as_mut_ptr(), 5, 0), + -(Errno::ESPIPE as i32) + ); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_write_read_only_fd() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Test to write to a file with read only permissions, and check if + // a valid error is returned when the file is used for writing. + let fd = cage.open_syscall("/test_file", O_CREAT | O_RDONLY, S_IRWXA); + assert!(fd >= 0); + + let write_data = "hello"; + assert_eq!( + cage.write_syscall(fd, write_data.as_ptr(), write_data.len()), + -(Errno::EBADF as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_write_to_directory() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Create a directory and try to write to it. + // We should expect an error (EISDIR) as writing to a directory is not + // supported. + let path = "/test_dir"; + // Remove the directory if it exists to ensure a clean test environment + let _ = cage.rmdir_syscall(path); + // Create the directory + assert_eq!(cage.mkdir_syscall(path, S_IRWXA), 0); + // Attempt to open the directory with O_WRONLY, expecting EISDIR + let fd_wr = cage.open_syscall(path, O_WRONLY, S_IRWXA); + assert_eq!( + fd_wr, + -(Errno::EISDIR as i32) + ); + + // Open the directory with O_RDONLY to get a valid file descriptor + let fd_rd = cage.open_syscall(path, O_RDONLY, S_IRWXA); + assert!( + fd_rd >= 0, + "Failed to open directory with O_RDONLY, got error code: {}", + fd_rd + ); + let write_data = "hello"; + let write_result = cage.write_syscall(fd_rd, write_data.as_ptr(), write_data.len()); + assert_eq!( + write_result, + -(Errno::EBADF as i32) + ); + + // Clean up + assert_eq!(cage.close_syscall(fd_rd), 0); + assert_eq!(cage.rmdir_syscall(path), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_write_to_epoll() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Create an Epoll and try to write to it. + // We should expect an error (EINVAL) as writing to an Epoll is not supported. + let epfd = cage.epoll_create_syscall(1); + assert!(epfd > 0); + let write_data = "hello"; + assert_eq!( + cage.write_syscall(epfd, write_data.as_ptr(), write_data.len()), + -(Errno::EINVAL as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_write_to_regular_file() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // This test mainly tests writing to a regular file. + // * Writing data to a file should start from position 0. + // * Once written, the position of the seek pointer in the file descriptor + // should increment by the count of bytes written. If write is performed again, + // then the position should continue from the point it was previously left. + let fd = cage.open_syscall("/test_file", O_CREAT | O_TRUNC | O_RDWR, S_IRWXA); + assert!(fd >= 0); + + let mut statdata = StatData::default(); + + // Write sample data to the file, and verify the number of bytes returned + let write_data1 = "hello"; + assert_eq!( + cage.write_syscall(fd, write_data1.as_ptr(), write_data1.len()), + 5 + ); + + // Verify the size of the file + assert_eq!(cage.fstat_syscall(fd, &mut statdata), 0); + assert_eq!(statdata.st_size, 5); + + // Write additional data to the file. + let write_data2 = " there!"; + assert_eq!( + cage.write_syscall(fd, write_data2.as_ptr(), write_data2.len()), + 7 + ); + + // Verify the updated size of the file + assert_eq!(cage.fstat_syscall(fd, &mut statdata), 0); + assert_eq!(statdata.st_size, 12); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_write_to_chardev_file() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // This test mainly tests the case for writing to a character device type + // file. In this case, we are trying to write 100 bytes to the + // "/dev/null" file, which should succeed without doing anything. + let path = "/dev/null"; + let fd = cage.open_syscall(path, O_RDWR, S_IRWXA); + + // Verify if the returned count of bytes is 100. + let write_data = "0".repeat(100); + assert_eq!(cage.write_syscall(fd, write_data.as_ptr(), 100), 100); + + assert_eq!(cage.close_syscall(fd), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_write_to_sockets() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // This test mainly tests the case for writing data to a pair of Sockets. + // In this case, we create a socket pair of two sockets, and send data through + // one socket, and try to read it from the other one. + let mut socketpair = interface::SockPair::default(); + + // Verify if the socketpair is formed successfully. + assert_eq!( + Cage::socketpair_syscall(&cage.clone(), libc::AF_UNIX, libc::SOCK_STREAM, 0, &mut socketpair), + 0 + ); + // Verify if the number of bytes sent to socket1 is correct. + let write_data = "test"; + assert_eq!( + cage.write_syscall(socketpair.sock1, write_data.as_ptr(), 4), + 4 + ); + + // Verify if the number of bytes received by socket2 is correct. + let mut buf2 = sizecbuf(4); + assert_eq!(cage.read_syscall(socketpair.sock2, buf2.as_mut_ptr(), 4), 4); + // Verify if the data received inside the buffer is correct. + assert_eq!(cbuf2str(&buf2), "test"); + + // Close the sockets + assert_eq!(cage.close_syscall(socketpair.sock1), 0); + assert_eq!(cage.close_syscall(socketpair.sock2), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_pwrite_read_only_fd() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Test to write to a file with read only permissions, and check if + // a valid error is returned when the file is used for writing. + let fd = cage.open_syscall("/test_file", O_CREAT | O_RDONLY, S_IRWXA); + assert!(fd >= 0); + + let write_data = "hello"; + assert_eq!( + cage.pwrite_syscall(fd, write_data.as_ptr(), write_data.len(), 0), + -(Errno::EBADF as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_pwrite_to_file() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // This test mainly tests two scenarios for writing to a file using + // `pwrite_syscall()`. + // * Writing to a file from the starting position offset(0). + // * Writing to a file from a random position offset, which should + // pad the file with additional "\0" bytes. + let fd = cage.open_syscall("/test_file", O_CREAT | O_TRUNC | O_RDWR, S_IRWXA); + assert!(fd >= 0); + + // Write sample data to the file and verify the number of bytes returned. + let write_data1 = "hello"; + assert_eq!(cage.pwrite_syscall(fd, write_data1.as_ptr(), 5, 0), 5); + + // Write additional data to the file starting from the 6th position offset. + let write_data2 = "there!"; + assert_eq!(cage.pwrite_syscall(fd, write_data2.as_ptr(), 6, 6), 6); + + // Read back the data to verify, but since we are changing the offset to + // a larger number than the file size, it should pad the file with "\0" values. + // Verify if the file contains the paded bytes as well. + let mut read_buf = sizecbuf(12); + assert_eq!(cage.pread_syscall(fd, read_buf.as_mut_ptr(), 12, 0), 12); + assert_eq!(cbuf2str(&read_buf), "hello\0there!"); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_pwrite_to_directory() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Create a directory and try to write to it. + // We should expect an error (EISDIR) as writing to a directory is not + // supported. + let path = "/test_dir"; + assert_eq!(cage.mkdir_syscall(path, S_IRWXA), 0); + let fd = cage.open_syscall(path, O_WRONLY, S_IRWXA); + + let write_data = "hello"; + assert_eq!( + cage.pwrite_syscall(fd, write_data.as_ptr(), write_data.len(), 0), + -(Errno::EISDIR as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_pwrite_invalid_types() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Test for invalid pipe + // Try writing the data to the pipe and check for error. + let mut pipe_fds = PipeArray::default(); + assert_eq!(cage.pipe_syscall(&mut pipe_fds), 0); + let write_fd = pipe_fds.writefd; + let write_data = "hello"; + assert_eq!( + cage.pwrite_syscall(write_fd, write_data.as_ptr(), write_data.len(), 0), + -(Errno::ESPIPE as i32) + ); + + // Test for invalid sockets + // Try writing the data to the socket and check for error. + let mut socketpair = interface::SockPair::default(); + assert_eq!( + Cage::socketpair_syscall(&cage.clone(), libc::AF_UNIX, libc::SOCK_STREAM, 0, &mut socketpair), + 0 + ); + assert_eq!( + cage.pwrite_syscall(socketpair.sock2, write_data.as_ptr(), 4, 0), + -(Errno::ESPIPE as i32) + ); + + // Test for invalid epoll + // Try writing the data to the epoll and check for error. + let epfd = cage.epoll_create_syscall(1); + assert_eq!( + cage.pwrite_syscall(epfd, write_data.as_ptr(), 5, 0), + -(Errno::ESPIPE as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_pwrite_to_chardev_file() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // This test mainly tests the case for writing to a character device type + // file. In this case, we are trying to write 100 bytes to the + // "/dev/null" file, which should succeed without doing anything. + let path = "/dev/null"; + let fd = cage.open_syscall(path, O_RDWR, S_IRWXA); + + // Verify if the returned count of bytes is 100. + let write_data = "0".repeat(100); + assert_eq!(cage.pwrite_syscall(fd, write_data.as_ptr(), 100, 0), 100); + + assert_eq!(cage.close_syscall(fd), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + pub fn ut_lind_fs_shmget_syscall() { + // acquire locks and start env cleanup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + + let key = 33123; + // Get shmid of a memory segment / create a new one if it doesn't exist + let shmid = cage.shmget_syscall(33123, 1024, IPC_CREAT); + assert_eq!(shmid, 4); + + // Check error upon asking for a valid key and passing the IPC_CREAT and + // IPC_EXCL flag + assert_eq!( + cage.shmget_syscall(key, 1024, IPC_CREAT | IPC_EXCL), + -(Errno::EEXIST as i32) + ); + + // Check error when passing IPC_CREAT flag as the key + assert_eq!( + cage.shmget_syscall(IPC_PRIVATE, 1024, IPC_PRIVATE), + -(Errno::ENOENT as i32) + ); + + // Check if the function returns a correct shmid upon asking with a key that we + // know exists + assert_eq!(cage.shmget_syscall(key, 1024, 0666), shmid); + + // Check if the function returns the correct error when we don't pass IPC_CREAT + // for a key that doesn't exist + assert_eq!( + cage.shmget_syscall(123456, 1024, 0), + -(Errno::ENOENT as i32) + ); + + // Check if the size error is returned correctly + assert_eq!( + cage.shmget_syscall(123456, (SHMMAX + 10) as usize, IPC_CREAT), + -(Errno::EINVAL as i32) + ); + assert_eq!( + cage.shmget_syscall(123456, 0 as usize, IPC_CREAT), + -(Errno::EINVAL as i32) + ); + + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_lseek_on_file() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Test to create a file and check if seeking to a new location is possible. + let fd = cage.open_syscall("/test_file", O_CREAT | O_WRONLY, S_IRWXA); + assert!(fd >= 0); + + // Attempt to seek within the file and check if it succeeds + assert_eq!(cage.lseek_syscall(fd, 10, SEEK_SET), 10); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_lseek_on_directory() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Create a directory and try to seek within it. + let path = "/test_dir"; + assert_eq!(cage.mkdir_syscall(path, S_IRWXA), 0); + let fd = cage.open_syscall(path, O_RDONLY, S_IRWXA); + assert!(fd >= 0); + + // Attempt to seek within the directory and check if it succeeds + assert_eq!(cage.lseek_syscall(fd, 1, SEEK_SET), 1); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_lseek_invalid_whence() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Test to create a file and check for invalid `whence` value + let fd = cage.open_syscall("/test_file", O_CREAT | O_RDWR, S_IRWXA); + assert!(fd >= 0); + + // Attempt to seek with an invalid `whence` value and check if it returns an + // error + assert_eq!( + cage.lseek_syscall(fd, 10, 999), // Invalid whence value + -(Errno::EINVAL as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_lseek_beyond_file_size() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Test to create a file and seek beyond its size + let fd = cage.open_syscall("/test_file", O_CREAT | O_RDWR, S_IRWXA); + assert!(fd >= 0); + + // Write sample data to the file. + assert_eq!(cage.write_syscall(fd, str2cbuf("hello"), 5), 5); + + // Seek beyond the end of the file and verify if it succeeds + assert_eq!( + cage.lseek_syscall(fd, 10, SEEK_END), + 15 // 5 (file size) + 10 (offset) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_lseek_before_start_of_file() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Test to create a file and attempt to seek before the start of the file + let fd = cage.open_syscall("/test_file", O_CREAT | O_RDWR, S_IRWXA); + assert!(fd >= 0); + + // Attempt to seek to a negative offset and check if it returns an error + // using "SEEK_SET" whence, where we are explicitly setting the file + // offset to -10 value. + assert_eq!( + cage.lseek_syscall(fd, -10, SEEK_SET), + -(Errno::EINVAL as i32) + ); + + // Attempt to seek to a negative offset and check if it returns an error + // using "SEEK_CUR" whence, where current position of the file is 0, + // as it's empty initially, and we are adding -10 to the offset. + assert_eq!( + cage.lseek_syscall(fd, -10, SEEK_CUR), + -(Errno::EINVAL as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_lseek_on_pipe() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Create a pipe and attempt to seek within it + let mut pipe_fds = PipeArray::default(); + assert_eq!(cage.pipe_syscall(&mut pipe_fds), 0); + let read_fd = pipe_fds.readfd; + + // Attempt to seek within the pipe and check if it returns an error + assert_eq!( + cage.lseek_syscall(read_fd, 10, SEEK_SET), + -(Errno::ESPIPE as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_lseek_on_chardev() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Attempt to seek within a character device file + let path = "/dev/null"; + let fd = cage.open_syscall(path, O_RDWR, S_IRWXA); + + // Seek within the character device and check if it returns 0 (no operation) + assert_eq!(cage.lseek_syscall(fd, 10, SEEK_SET), 0); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_lseek_on_epoll() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Create an Epoll and try to seek from it. + let epfd = cage.epoll_create_syscall(1); + assert!(epfd > 0); + + // Attempt to seek from the epoll and check if it returns an error + assert_eq!( + cage.lseek_syscall(epfd, 10, SEEK_SET), + -(Errno::ESPIPE as i32) + ); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_close_regular_file() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Create and open a regular file, then close it. + let fd = cage.open_syscall("/test_file", O_CREAT | O_RDWR, S_IRWXA); + assert!(fd >= 0); + + // Write sample data to the file. + assert_eq!(cage.write_syscall(fd, str2cbuf("hello"), 5), 5); + + // Close the file descriptor, which should succeed. + assert_eq!(cage.close_syscall(fd), 0); + + // Attempt to close the file descriptor again to ensure it's already closed. + // Expect an error for "Invalid File Descriptor". + assert_eq!(cage.close_syscall(fd), -(Errno::EBADF as i32)); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_close_directory() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Create a directory and open it. + let path = "/test_dir"; + assert_eq!(cage.mkdir_syscall(path, S_IRWXA), 0); + let fd = cage.open_syscall(path, O_RDONLY, S_IRWXA); + assert!(fd >= 0); + + // Close the directory file descriptor, which should succeed. + assert_eq!(cage.close_syscall(fd), 0); + + // Attempt to close the file descriptor again to ensure it's already closed. + // Expect an error for "Invalid File Descriptor". + assert_eq!(cage.close_syscall(fd), -(Errno::EBADF as i32)); + // Remove the directory to clean up the environment + assert_eq!(cage.rmdir_syscall(path), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_close_socket() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Create a socket pair. + let mut socketpair = interface::SockPair::default(); + assert_eq!( + Cage::socketpair_syscall(&cage.clone(),libc::AF_UNIX, libc::SOCK_STREAM, 0, &mut socketpair), + 0 + ); + + // Close both the socket file descriptors, which should succeed. + assert_eq!(cage.close_syscall(socketpair.sock1), 0); + assert_eq!(cage.close_syscall(socketpair.sock2), 0); + + // Attempt to close the file descriptors again to ensure they are already + // closed. Expect an error for "Invalid File Descriptor". + assert_eq!(cage.close_syscall(socketpair.sock1), -(Errno::EBADF as i32)); + assert_eq!(cage.close_syscall(socketpair.sock2), -(Errno::EBADF as i32)); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_close_pipe() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Create a pipe. + let mut pipe_fds = PipeArray::default(); + assert_eq!(cage.pipe_syscall(&mut pipe_fds), 0); + let read_fd = pipe_fds.readfd; + let write_fd = pipe_fds.writefd; + + // Write data to the pipe + let write_data = "Testing"; + assert_eq!( + cage.write_syscall(write_fd, write_data.as_ptr(), write_data.len()), + write_data.len() as i32 + ); + + // Read the data from the pipe. + let mut buf = sizecbuf(7); + assert_eq!( + cage.read_syscall(read_fd, buf.as_mut_ptr(), buf.len()), + write_data.len() as i32 + ); + assert_eq!(cbuf2str(&buf), write_data); + + // Close the pipe file descriptors, which should succeed. + assert_eq!(cage.close_syscall(read_fd), 0); + assert_eq!(cage.close_syscall(write_fd), 0); + + // Attempt to close the file descriptor again to ensure they are already closed. + // Expect an error for "Invalid File Descriptor". + assert_eq!(cage.close_syscall(read_fd), -(Errno::EBADF as i32)); + assert_eq!(cage.close_syscall(write_fd), -(Errno::EBADF as i32)); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_fs_close_chardev() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Open a character device file. + let fd = cage.open_syscall("/dev/zero", O_RDWR, S_IRWXA); + assert!(fd >= 0); + + // Close the character device file descriptor, which should succeed. + assert_eq!(cage.close_syscall(fd), 0); + + // Attempt to close the file descriptor again to ensure it's already closed. + // Expect an error for "Invalid File Descriptor". + assert_eq!(cage.close_syscall(fd), -(Errno::EBADF as i32)); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + // #[test] + // pub fn ut_lind_fs_stat_syscall_tests() { + // // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // // and also performs clean env setup + // let _thelock = setup::lock_and_init(); + + // let cage = interface::cagetable_getref(1); + // let mut statdata = StatData::default(); + + // // test out whether an error is output for a non existent file path + // // (ENOENT[-2]) + // assert_eq!( + // cage.stat_syscall("non_existent_file_path", &mut statdata), + // syscall_error(Errno::ENOENT, "stat", "test_failure") + // ); + + // // setting up directory inode object '/tmp' for testing stat_syscall with a + // // directory + // let dir_path = "/tmp"; // since setup already initializes tmp, assuming it is there + // assert_eq!(cage.stat_syscall(dir_path, &mut statdata), 0); + + // // setting up generic inode object "/tmp/generic" for testing stat_syscall with + // // a generic file + // let generic_path = "/tmp/generic"; + // let creat_fd = cage.creat_syscall(generic_path, S_IRWXA); + // assert!(creat_fd > 0); + // assert_eq!(cage.stat_syscall(generic_path, &mut statdata), 0); + + // // setting up character device inode object "/chardev" for testing stat_syscall + // // with a character device + // let dev = makedev(&DevNo { major: 1, minor: 3 }); + // let chardev_path = "/chardev"; + // assert_eq!( + // cage.mknod_syscall(chardev_path, S_IRWXA | S_IFCHR as u32, dev), + // 0 + // ); + // assert_eq!(cage.stat_syscall(chardev_path, &mut statdata), 0); + + // // setting up socket inode object with path "/socket.sock" for testing + // // stat_syscall with a socket + // let socketfile_path = "/socket.sock"; + // let socketfd = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + // assert!(socketfd > 0); + // let sockaddr = interface::new_sockaddr_unix(AF_UNIX as u16, socketfile_path.as_bytes()); + // let socket = interface::GenSockaddr::Unix(sockaddr); + // assert_eq!(cage.bind_syscall(socketfd, &socket), 0); + + // // stat_syscall test here + // assert_eq!(cage.stat_syscall(socketfile_path, &mut statdata), 0); + + // // socket teardown + // assert_eq!(cage.close_syscall(socketfd), 0); + // cage.unlink_syscall(socketfile_path); + + // lindrustfinalize(); + // return; + // } + + // #[test] + // pub fn ut_lind_fs_fstat_syscall_tests() { + // //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // // and also performs clean env setup + // let _thelock = setup::lock_and_init(); + + // let cage = interface::cagetable_getref(1); + + // let mut statdata = StatData::default(); + + // // test out whether an error is output for a non existent fd (1000) + // // (ENOENT[-2]) + // let non_existent_fd = 1000; + // assert_eq!(cage.fstat_syscall(non_existent_fd, &mut statdata), -9); + + // // setting up directory inode object '/tmp' for testing fstat_syscall with a + // // directory + // let dir_path = "/tmp"; // since setup already initializes tmp, assuming it is there + // let dir_fd = cage.open_syscall(dir_path, O_RDONLY | O_DIRECTORY, S_IRWXA); + // assert!(dir_fd > 0); + // assert_eq!(cage.fstat_syscall(dir_fd, &mut statdata), 0); + // assert_eq!(cage.close_syscall(dir_fd), 0); + + // // setting up generic inode object "/tmp/generic" for testing fstat_syscall with + // // a generic file + // let generic_path = "/tmp/generic"; + // let creat_fd = cage.creat_syscall(generic_path, S_IRWXA); + // assert!(creat_fd > 0); + // assert_eq!(cage.fstat_syscall(creat_fd, &mut statdata), 0); + + // // setting up character device inode object "/chardev" for testing fstat_syscall + // // with a character device + // let dev = makedev(&DevNo { major: 1, minor: 3 }); + // let chardev_path = "/chardev"; + // assert_eq!( + // cage.mknod_syscall(chardev_path, S_IRWXA | S_IFCHR as u32, dev), + // 0 + // ); + // let chardev_fd = cage.open_syscall(chardev_path, O_RDONLY, S_IRWXA); + // assert!(chardev_fd > 0); + // assert_eq!(cage.fstat_syscall(chardev_fd, &mut statdata), 0); + // assert_eq!(cage.close_syscall(chardev_fd), 0); + + // // setting up socket inode object with path "/socket.sock" for testing + // // fstat_syscall with a socket + // let socketfile_path = "/socket.sock"; + + // let socketfd = cage.socket_syscall(libc::AF_UNIX, libc::SOCK_STREAM, 0); + // assert!(socketfd > 0); + + // let sockaddr = interface::new_sockaddr_unix(libc::AF_UNIX as u16, socketfile_path.as_bytes()); + // let socket = interface::GenSockaddr::Unix(sockaddr); + // assert_eq!(cage.bind_syscall(socketfd, &socket), 0); + + // // Errno::EOPNOTSUPP : -95 + // assert_eq!(cage.fstat_syscall(socketfd, &mut statdata), -95); + + // // Clean up + // assert_eq!(cage.close_syscall(socketfd), 0); + + // cage.unlink_syscall(socketfile_path); + + // lindrustfinalize(); + // return; + // } + + // #[test] + // pub fn ut_lind_fs_statfs_syscall_tests() { + // // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // // and also performs clean env setup + // let _thelock = setup::lock_and_init(); + + // let cage = interface::cagetable_getref(1); + // let mut fsdata = FSData::default(); + + // // test out whether an error is output for a non existent file path + // // (ENOENT[-2]) + // assert_eq!( + // cage.statfs_syscall("non_existent_file_path", &mut fsdata), + // syscall_error(Errno::ENOENT, "stat", "test_failure") + // ); + + // // setting up inode object "/tmp/generic" for testing statfs_syscall + // let generic_path = "/tmp/generic"; + // let creat_fd = cage.creat_syscall(generic_path, S_IRWXA); + // assert!(creat_fd > 0); + // assert_eq!(cage.statfs_syscall(generic_path, &mut fsdata), 0); + + // lindrustfinalize(); + // return; + // } + + // #[test] + // pub fn ut_lind_fs_fstatfs_syscall_tests() { + // //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // // and also performs clean env setup + // let _thelock = setup::lock_and_init(); + + // let cage = interface::cagetable_getref(1); + + // let mut fsdata = FSData::default(); + + // // test out whether an error is output for a non existent fd (1000) + // // (ENOENT[-2]) + // let non_existent_fd = 1000; + // assert_eq!( + // cage.fstatfs_syscall(non_existent_fd, &mut fsdata), + // syscall_error(Errno::EBADF, "stat", "test_failure") + // ); + + // // setting up generic inode object "/tmp/generic" for testing fstat_syscall with + // // a generic file + // let generic_path = "/tmp/generic"; + // let creat_fd = cage.creat_syscall(generic_path, S_IRWXA); + // assert!(creat_fd > 0); + // assert_eq!(cage.fstatfs_syscall(creat_fd, &mut fsdata), 0); + + // // setting up socket inode object with path "/socket.sock" for testing + // // fstat_syscall with a socket + // let socketfile_path = "/socket.sock"; + + // let socketfd = cage.socket_syscall(libc::AF_UNIX, libc::SOCK_STREAM, 0); + // assert!(socketfd > 0); + + // let sockaddr = interface::new_sockaddr_unix(libc::AF_UNIX as u16, socketfile_path.as_bytes()); + // let socket = interface::GenSockaddr::Unix(sockaddr); + // assert_eq!(cage.bind_syscall(socketfd, &socket), 0); + + // // Errno::EBADF : -9 + // assert_eq!( + // cage.fstatfs_syscall(socketfd, &mut fsdata), + // syscall_error(Errno::EBADF, "stat", "test_failure") + // ); + + // // Clean up + // assert_eq!(cage.close_syscall(socketfd), 0); + + // cage.unlink_syscall(socketfile_path); + + // lindrustfinalize(); + // return; + // } } diff --git a/src/tests/ipc_tests.rs b/src/tests/ipc_tests.rs index 31918279..d533d227 100644 --- a/src/tests/ipc_tests.rs +++ b/src/tests/ipc_tests.rs @@ -1,342 +1,633 @@ -// #[cfg(test)] -// pub mod ipc_tests { -// use super::super::*; -// use crate::interface; -// use crate::safeposix::{cage::*, dispatcher::*, filesystem}; -// use std::fs::OpenOptions; -// use std::os::unix::fs::PermissionsExt; -// use std::time::Instant; - -// //#[test] -// pub fn test_ipc() { -// ut_lind_ipc_pipe(); -// ut_lind_ipc_domain_socket(); -// ut_lind_ipc_socketpair(); -// } - -// pub fn ut_lind_ipc_pipe() { -// let byte_chunk: usize = 131072; // 128 KB -// let num_writes: usize = 8192; // 8 KB - -// lindrustinit(0); - -// let cage1 = interface::cagetable_getref(1); - -// let mut pipefds = PipeArray { -// readfd: -1, -// writefd: -1, -// }; -// assert_eq!(cage1.pipe_syscall(&mut pipefds), 0); -// assert_eq!(cage1.fork_syscall(2), 0); - -// let sender = std::thread::spawn(move || { -// let cage2 = interface::cagetable_getref(2); - -// assert_eq!(cage2.close_syscall(pipefds.writefd), 0); -// assert_eq!(cage2.dup2_syscall(pipefds.readfd, 0), 0); -// assert_eq!(cage2.close_syscall(pipefds.readfd), 0); - -// let mut bytes_read: usize = 1; - -// let mut buf: Vec = Vec::with_capacity(byte_chunk * num_writes); -// let mut bufptr = buf.as_mut_ptr(); -// let mut buflen: usize = 0; - -// while bytes_read != 0 { -// bytes_read = cage2.read_syscall(0, bufptr, byte_chunk) as usize; -// unsafe { -// bufptr = bufptr.add(bytes_read); -// } -// buf.resize(buflen + bytes_read, 0); -// buflen += bytes_read; -// } -// assert_eq!(cage2.close_syscall(0), 0); - -// assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); -// }); - -// assert_eq!(cage1.close_syscall(pipefds.readfd), 0); -// assert_eq!(cage1.dup2_syscall(pipefds.writefd, 1), 1); -// assert_eq!(cage1.close_syscall(pipefds.writefd), 0); - -// for _i in 0..num_writes { -// let mut buf: Vec = vec!['A' as u8; byte_chunk]; -// let bufptr = buf.as_mut_ptr(); -// buf.resize(byte_chunk, 0); -// cage1.write_syscall(1, bufptr, byte_chunk); -// } - -// assert_eq!(cage1.close_syscall(1), 0); - -// sender.join().unwrap(); - -// assert_eq!(cage1.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); - -// lindrustfinalize(); -// } - -// pub fn ut_lind_ipc_domain_socket() { -// //bind net zero test reformatted for domain sockets - -// let clientsockfilename = "/client.sock"; -// let serversockfilename = "/server.sock"; - -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); - -// //both the server and the socket are run from this file -// let serversockfd = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); -// let clientsockfd = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); - -// //making sure that the assigned fd's are valid -// assert!(serversockfd > 0); -// assert!(clientsockfd > 0); - -// //binding to a socket -// let serversockaddr = -// interface::new_sockaddr_unix(AF_UNIX as u16, serversockfilename.as_bytes()); -// let serversocket = interface::GenSockaddr::Unix(serversockaddr); -// let clientsockaddr = -// interface::new_sockaddr_unix(AF_UNIX as u16, clientsockfilename.as_bytes()); -// let clientsocket = interface::GenSockaddr::Unix(clientsockaddr); - -// assert_eq!(cage.bind_syscall(serversockfd, &serversocket), 0); -// assert_eq!(cage.bind_syscall(clientsockfd, &clientsocket), 0); -// assert_eq!(cage.listen_syscall(serversockfd, 1), 0); //we are only allowing for one client at a time - -// //forking the cage to get another cage with the same information -// assert_eq!(cage.fork_syscall(2), 0); - -// //creating a thread for the server so that the information can be sent between the two threads -// let thread = interface::helper_thread(move || { -// let cage2 = interface::cagetable_getref(2); -// let mut socket2 = interface::GenSockaddr::Unix(interface::new_sockaddr_unix( -// AF_UNIX as u16, -// "".as_bytes(), -// )); // blank unix sockaddr - -// let sockfd = cage2.accept_syscall(serversockfd, &mut socket2); //really can only make sure that the fd is valid -// assert!(sockfd > 0); - -// interface::sleep(interface::RustDuration::from_millis(100)); - -// //process the first test... -// //Writing 100, then peek 100, then read 100 -// let mut buf = sizecbuf(100); -// assert_eq!( -// cage2.recvfrom_syscall( -// sockfd, -// buf.as_mut_ptr(), -// 100, -// MSG_PEEK, -// &mut Some(&mut socket2) -// ), -// 100 -// ); //peeking at the input message -// assert_eq!(cbuf2str(&buf), &"A".repeat(100)); -// buf = sizecbuf(100); -// assert_eq!( -// cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 100, 0, &mut Some(&mut socket2)), -// 100 -// ); //reading the input message -// assert_eq!(cbuf2str(&buf), &"A".repeat(100)); -// buf = sizecbuf(100); - -// interface::sleep(interface::RustDuration::from_millis(200)); - -// //process the second test... -// //Writing 100, read 20, peek 20, read 80 -// assert_eq!( -// cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 20, 0, &mut Some(&mut socket2)), -// 20 -// ); -// assert_eq!(cbuf2str(&buf), "A".repeat(20) + &"\0".repeat(80)); -// buf = sizecbuf(100); -// assert_eq!( -// cage2.recvfrom_syscall( -// sockfd, -// buf.as_mut_ptr(), -// 20, -// MSG_PEEK, -// &mut Some(&mut socket2) -// ), -// 20 -// ); -// assert_eq!(cbuf2str(&buf), "A".repeat(20) + &"\0".repeat(80)); -// buf = sizecbuf(100); -// assert_eq!( -// cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 80, 0, &mut Some(&mut socket2)), -// 80 -// ); -// assert_eq!(cbuf2str(&buf), "A".repeat(80) + &"\0".repeat(20)); -// buf = sizecbuf(100); - -// interface::sleep(interface::RustDuration::from_millis(200)); - -// //process the third test... -// //Writing 100, peek several times, read 100 -// for _ in 0..4 { -// assert_eq!( -// cage2.recvfrom_syscall( -// sockfd, -// buf.as_mut_ptr(), -// 10, -// MSG_PEEK, -// &mut Some(&mut socket2) -// ), -// 10 -// ); -// assert_eq!(cbuf2str(&buf), "A".repeat(10) + &"\0".repeat(90)); -// buf = sizecbuf(100); -// } -// for _ in 0..4 { -// assert_eq!( -// cage2.recvfrom_syscall( -// sockfd, -// buf.as_mut_ptr(), -// 20, -// MSG_PEEK, -// &mut Some(&mut socket2) -// ), -// 20 -// ); -// assert_eq!(cbuf2str(&buf), "A".repeat(20) + &"\0".repeat(80)); -// buf = sizecbuf(100); -// } -// for _ in 0..4 { -// assert_eq!( -// cage2.recvfrom_syscall( -// sockfd, -// buf.as_mut_ptr(), -// 30, -// MSG_PEEK, -// &mut Some(&mut socket2) -// ), -// 30 -// ); -// assert_eq!(cbuf2str(&buf), "A".repeat(30) + &"\0".repeat(70)); -// buf = sizecbuf(100); -// } -// for _ in 0..4 { -// assert_eq!( -// cage2.recvfrom_syscall( -// sockfd, -// buf.as_mut_ptr(), -// 40, -// MSG_PEEK, -// &mut Some(&mut socket2) -// ), -// 40 -// ); -// assert_eq!(cbuf2str(&buf), "A".repeat(40) + &"\0".repeat(60)); -// buf = sizecbuf(100); -// } -// assert_eq!( -// cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 100, 0, &mut Some(&mut socket2)), -// 100 -// ); -// assert_eq!(cbuf2str(&buf), &"A".repeat(100)); -// buf = sizecbuf(100); - -// interface::sleep(interface::RustDuration::from_millis(200)); - -// //process the fourth test... -// //Writing 50, peek 50 -// assert_eq!( -// cage2.recvfrom_syscall( -// sockfd, -// buf.as_mut_ptr(), -// 50, -// MSG_PEEK, -// &mut Some(&mut socket2) -// ), -// 50 -// ); -// assert_eq!(cbuf2str(&buf), "A".repeat(50) + &"\0".repeat(50)); -// assert_eq!(cage2.close_syscall(sockfd), 0); -// assert_eq!(cage2.close_syscall(serversockfd), 0); - -// assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); -// }); - -// //connect to the server -// interface::sleep(interface::RustDuration::from_millis(20)); - -// assert_eq!(cage.connect_syscall(clientsockfd, &serversocket), 0); - -// //send the data with delays so that the server can process the information cleanly -// assert_eq!( -// cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(100)), 100, 0), -// 100 -// ); -// interface::sleep(interface::RustDuration::from_millis(100)); - -// assert_eq!( -// cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(100)), 100, 0), -// 100 -// ); -// interface::sleep(interface::RustDuration::from_millis(100)); - -// assert_eq!( -// cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(100)), 100, 0), -// 100 -// ); -// interface::sleep(interface::RustDuration::from_millis(100)); - -// assert_eq!( -// cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(50)), 50, 0), -// 50 -// ); -// interface::sleep(interface::RustDuration::from_millis(100)); - -// assert_eq!(cage.close_syscall(clientsockfd), 0); - -// thread.join().unwrap(); - -// cage.unlink_syscall(serversockfilename); -// cage.unlink_syscall(clientsockfilename); - -// assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); -// lindrustfinalize(); -// } - -// pub fn ut_lind_ipc_socketpair() { -// lindrustinit(0); -// let cage = interface::cagetable_getref(1); -// let mut socketpair = interface::SockPair::default(); -// assert_eq!( -// Cage::socketpair_syscall(cage.clone(), AF_UNIX, SOCK_STREAM, 0, &mut socketpair), -// 0 -// ); -// let cage2 = cage.clone(); - -// let thread = interface::helper_thread(move || { -// let mut buf = sizecbuf(10); -// cage2.recv_syscall(socketpair.sock2, buf.as_mut_ptr(), 10, 0); -// assert_eq!(cbuf2str(&buf), "test\0\0\0\0\0\0"); - -// interface::sleep(interface::RustDuration::from_millis(30)); -// assert_eq!( -// cage2.send_syscall(socketpair.sock2, str2cbuf("Socketpair Test"), 15, 0), -// 15 -// ); -// }); - -// assert_eq!( -// cage.send_syscall(socketpair.sock1, str2cbuf("test"), 4, 0), -// 4 -// ); - -// let mut buf2 = sizecbuf(15); -// cage.recv_syscall(socketpair.sock1, buf2.as_mut_ptr(), 15, 0); -// assert_eq!(cbuf2str(&buf2), "Socketpair Test"); - -// thread.join().unwrap(); - -// assert_eq!(cage.close_syscall(socketpair.sock1), 0); -// assert_eq!(cage.close_syscall(socketpair.sock2), 0); - -// assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); -// lindrustfinalize(); -// } -// } +#[cfg(test)] +pub mod ipc_tests { + use super::super::*; + use crate::interface; + use crate::safeposix::{cage::*, dispatcher::*, filesystem}; + use libc::c_void; + use std::fs::OpenOptions; + use std::os::unix::fs::PermissionsExt; + use std::time::Instant; + + #[test] + pub fn ut_lind_ipc_pipe_simple() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + // lets test transferring 1GB of data through the pipe in 128KB chunks + let byte_chunk: usize = 131072; // 128 KB + let num_writes: usize = 8192; // iterations 1GB/128KB + + let cage1 = interface::cagetable_getref(1); + + // lets create a blank pipefd array, setting fds to -1 here before they can be + // populated by the pipe call + let mut pipefds = PipeArray { + readfd: -1, + writefd: -1, + }; + assert_eq!(cage1.pipe_syscall(&mut pipefds), 0); + assert_eq!(cage1.fork_syscall(3), 0); + + let sender = std::thread::spawn(move || { + let cage2 = interface::cagetable_getref(3); + + // dup our pipe write end to stdout and close unused pipe ends + assert_eq!(cage2.close_syscall(pipefds.writefd), 0); + assert_eq!(cage2.dup2_syscall(pipefds.readfd, 0), 0); + assert_eq!(cage2.close_syscall(pipefds.readfd), 0); + + let mut bytes_read: usize = 1; + + let mut buf: Vec = Vec::with_capacity(byte_chunk * num_writes); + let mut bufptr = buf.as_mut_ptr(); + let mut buflen: usize = 0; + + // lets read in 128KB chunks until bytes read becomes 0 signaling EOF + while bytes_read != 0 { + bytes_read = cage2.read_syscall(0, bufptr, byte_chunk) as usize; + unsafe { + bufptr = bufptr.add(bytes_read); + } + buf.resize(buflen + bytes_read, 0); + buflen += bytes_read; + } + assert_eq!(cage2.close_syscall(0), 0); + + assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); + + // dup our pipe read end to stdout and close unused pipe ends + assert_eq!(cage1.close_syscall(pipefds.readfd), 0); + assert_eq!(cage1.dup2_syscall(pipefds.writefd, 1), 1); + assert_eq!(cage1.close_syscall(pipefds.writefd), 0); + + // lets now write those chunks to the pipe + for _i in 0..num_writes { + let mut buf: Vec = vec!['A' as u8; byte_chunk]; + cage1.write_syscall(1, buf.as_mut_ptr(), byte_chunk); + } + + assert_eq!(cage1.close_syscall(1), 0); + + sender.join().unwrap(); + + assert_eq!(cage1.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_ipc_domain_socket() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + //bind net zero test reformatted for domain sockets + + let clientsockfilename = "/client.sock"; + let serversockfilename = "/server.sock"; + + let cage = interface::cagetable_getref(1); + + //both the server and the socket are run from this file + let serversockfd = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + let clientsockfd = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + + //making sure that the assigned fd's are valid + assert!(serversockfd > 0); + assert!(clientsockfd > 0); + + //binding to a socket + let serversockaddr = + interface::new_sockaddr_unix(AF_UNIX as u16, serversockfilename.as_bytes()); + let serversocket = interface::GenSockaddr::Unix(serversockaddr); + let clientsockaddr = + interface::new_sockaddr_unix(AF_UNIX as u16, clientsockfilename.as_bytes()); + let clientsocket = interface::GenSockaddr::Unix(clientsockaddr); + + assert_eq!(cage.bind_syscall(serversockfd, &serversocket), 0); + assert_eq!(cage.bind_syscall(clientsockfd, &clientsocket), 0); + assert_eq!(cage.listen_syscall(serversockfd, 1), 0); //we are only allowing for one client at a time + + //forking the cage to get another cage with the same information + assert_eq!(cage.fork_syscall(3), 0); + + //creating a thread for the server so that the information can be sent between + // the two threads + let thread = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(3); + let mut socket2 = interface::GenSockaddr::Unix(interface::new_sockaddr_unix( + AF_UNIX as u16, + "".as_bytes(), + )); // blank unix sockaddr + + let sockfd = cage2.accept_syscall(serversockfd, &mut socket2); //really can only make sure that the fd is valid + assert!(sockfd > 0); + + interface::sleep(interface::RustDuration::from_millis(100)); + + //process the first test... + //Writing 100, then peek 100, then read 100 + let mut buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 100, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 100 + ); //peeking at the input message + assert_eq!(cbuf2str(&buf), &"A".repeat(100)); + buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 100, 0, &mut Some(&mut socket2)), + 100 + ); //reading the input message + assert_eq!(cbuf2str(&buf), &"A".repeat(100)); + buf = sizecbuf(100); + + interface::sleep(interface::RustDuration::from_millis(200)); + + //process the second test... + //Writing 100, read 20, peek 20, read 80 + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 20, 0, &mut Some(&mut socket2)), + 20 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(20) + &"\0".repeat(80)); + buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 20, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 20 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(20) + &"\0".repeat(80)); + buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 80, 0, &mut Some(&mut socket2)), + 80 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(80) + &"\0".repeat(20)); + buf = sizecbuf(100); + + interface::sleep(interface::RustDuration::from_millis(200)); + + //process the third test... + //Writing 100, peek several times, read 100 + for _ in 0..4 { + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 10, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 10 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(10) + &"\0".repeat(90)); + buf = sizecbuf(100); + } + for _ in 0..4 { + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 20, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 20 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(20) + &"\0".repeat(80)); + buf = sizecbuf(100); + } + for _ in 0..4 { + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 30, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 30 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(30) + &"\0".repeat(70)); + buf = sizecbuf(100); + } + for _ in 0..4 { + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 40, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 40 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(40) + &"\0".repeat(60)); + buf = sizecbuf(100); + } + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 100, 0, &mut Some(&mut socket2)), + 100 + ); + assert_eq!(cbuf2str(&buf), &"A".repeat(100)); + buf = sizecbuf(100); + + interface::sleep(interface::RustDuration::from_millis(200)); + + //process the fourth test... + //Writing 50, peek 50 + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 50, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 50 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(50) + &"\0".repeat(50)); + assert_eq!(cage2.close_syscall(sockfd), 0); + assert_eq!(cage2.close_syscall(serversockfd), 0); + + assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); + + //connect to the server + interface::sleep(interface::RustDuration::from_millis(20)); + + assert_eq!(cage.connect_syscall(clientsockfd, &serversocket), 0); + + //send the data with delays so that the server can process the information + // cleanly + assert_eq!( + cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(100)), 100, 0), + 100 + ); + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!( + cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(100)), 100, 0), + 100 + ); + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!( + cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(100)), 100, 0), + 100 + ); + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!( + cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(50)), 50, 0), + 50 + ); + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!(cage.close_syscall(clientsockfd), 0); + + thread.join().unwrap(); + + cage.unlink_syscall(serversockfilename); + cage.unlink_syscall(clientsockfilename); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_ipc_socketpair() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let mut socketpair = interface::SockPair::default(); + assert_eq!( + Cage::socketpair_syscall(&cage.clone(), AF_UNIX, SOCK_STREAM, 0, &mut socketpair), + 0 + ); + let cage2 = cage.clone(); + + let thread = interface::helper_thread(move || { + let mut buf = sizecbuf(10); + cage2.recv_syscall(socketpair.sock2, buf.as_mut_ptr(), 10, 0); + assert_eq!(cbuf2str(&buf), "test\0\0\0\0\0\0"); + + interface::sleep(interface::RustDuration::from_millis(30)); + assert_eq!( + cage2.send_syscall(socketpair.sock2, str2cbuf("Socketpair Test"), 15, 0), + 15 + ); + }); + + assert_eq!( + cage.send_syscall(socketpair.sock1, str2cbuf("test"), 4, 0), + 4 + ); + + let mut buf2 = sizecbuf(15); + cage.recv_syscall(socketpair.sock1, buf2.as_mut_ptr(), 15, 0); + assert_eq!(cbuf2str(&buf2), "Socketpair Test"); + + thread.join().unwrap(); + + assert_eq!(cage.close_syscall(socketpair.sock1), 0); + assert_eq!(cage.close_syscall(socketpair.sock2), 0); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_ipc_writev() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let mut socketpair = interface::SockPair::default(); + assert_eq!( + Cage::socketpair_syscall(&cage.clone(), AF_UNIX, SOCK_STREAM, 0, &mut socketpair), + 0 + ); + let cage2 = cage.clone(); + + let thread = interface::helper_thread(move || { + let mut buf = sizecbuf(10); + cage2.recv_syscall(socketpair.sock2, buf.as_mut_ptr(), 10, 0); + assert_eq!(cbuf2str(&buf), "test\0\0\0\0\0\0"); + + interface::sleep(interface::RustDuration::from_millis(30)); + + let iovec: [interface::IovecStruct; 3] = [ + interface::IovecStruct { + iov_base: str2cbuf(&"A".repeat(100)) as *mut c_void, + iov_len: 100, + }, + interface::IovecStruct { + iov_base: str2cbuf(&"B".repeat(100)) as *mut c_void, + iov_len: 100, + }, + interface::IovecStruct { + iov_base: str2cbuf(&"C".repeat(100)) as *mut c_void, + iov_len: 100, + }, + ]; + + assert_eq!( + cage2.writev_syscall(socketpair.sock2, iovec.as_ptr(), 3), + 300 + ); + }); + + let iovec2: [interface::IovecStruct; 1] = [interface::IovecStruct { + iov_base: str2cbuf("test") as *mut c_void, + iov_len: 4, + }]; + assert_eq!(cage.writev_syscall(socketpair.sock1, iovec2.as_ptr(), 1), 4); + + let mut buf2 = sizecbuf(300); + assert_eq!( + cage.recv_syscall(socketpair.sock1, buf2.as_mut_ptr(), 300, 0), + 300 + ); + thread.join().unwrap(); + + assert_eq!(cage.close_syscall(socketpair.sock1), 0); + assert_eq!(cage.close_syscall(socketpair.sock2), 0); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_ipc_pipe2_nonblock() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage1 = interface::cagetable_getref(1); + + // lets create a blank pipefd array, setting fds to -1 here before they can be + // populated by the pipe call + let mut pipefds = PipeArray { + readfd: -1, + writefd: -1, + }; + assert_eq!(cage1.pipe2_syscall(&mut pipefds, libc::O_NONBLOCK), 0); + assert_eq!(cage1.fork_syscall(3), 0); + + // dup our pipe read end to stdout and close unused pipe ends + assert_eq!(cage1.close_syscall(pipefds.readfd), 0); + assert_eq!(cage1.dup2_syscall(pipefds.writefd, 1), 1); + assert_eq!(cage1.close_syscall(pipefds.writefd), 0); + + // lets write to half the pipe capacity + let writesize1: usize = 32768; // 32KB + let mut buf1: Vec = vec!['A' as u8; writesize1]; + assert_eq!( + cage1.write_syscall(1, buf1.as_mut_ptr(), writesize1), + writesize1 as i32 + ); + + // now lets try to write to the whole pipe capacity before we've read anything + let room = PIPE_CAPACITY - writesize1; + + let writesize2: usize = 65536; // 64KB + let mut buf2: Vec = vec!['B' as u8; writesize2]; + // we expect to only write half - 32KB + let expected_partial_write = writesize2 - room; + + assert_eq!( + cage1.write_syscall(1, buf2.as_mut_ptr(), writesize2), + expected_partial_write as i32 + ); + + // now if we try to write anything the pipe is full so we expect EAGAIN + let writesize3: usize = 4096; // 4KB KB + let mut buf3: Vec = vec!['B' as u8; writesize3]; + assert_eq!( + cage1.write_syscall(1, buf3.as_mut_ptr(), writesize3), + -(Errno::EAGAIN as i32) + ); + + assert_eq!(cage1.close_syscall(1), 0); + + let sender = std::thread::spawn(move || { + let cage2 = interface::cagetable_getref(3); + + // dup our pipe read end to stdout and close unused pipe ends + assert_eq!(cage2.close_syscall(pipefds.writefd), 0); + assert_eq!(cage2.dup2_syscall(pipefds.readfd, 0), 0); + assert_eq!(cage2.close_syscall(pipefds.readfd), 0); + + let bytes_to_read = 65536; + let mut buf: Vec = Vec::with_capacity(bytes_to_read); + + // ok now lets read everything + assert_eq!( + cage2.read_syscall(0, buf.as_mut_ptr(), bytes_to_read), + bytes_to_read as i32 + ); + // we've close the write ends so now we expect 0, signifying EOF + assert_eq!( + cage2.read_syscall(0, buf.as_mut_ptr(), bytes_to_read), + 0 as i32 + ); + + assert_eq!(cage2.close_syscall(0), 0); + + assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); + + sender.join().unwrap(); + + assert_eq!(cage1.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_ipc_pipe2_wouldblock() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage1 = interface::cagetable_getref(1); + + // lets create a blank pipefd array, setting fds to -1 here before they can be + // populated by the pipe call + let mut pipefds = PipeArray { + readfd: -1, + writefd: -1, + }; + assert_eq!(cage1.pipe2_syscall(&mut pipefds, libc::O_NONBLOCK), 0); + assert_eq!(cage1.fork_syscall(3), 0); + + // dup our pipe read end to stdout and close unused pipe ends + assert_eq!(cage1.close_syscall(pipefds.readfd), 0); + assert_eq!(cage1.dup2_syscall(pipefds.writefd, 1), 1); + assert_eq!(cage1.close_syscall(pipefds.writefd), 0); + + // lets write one byte more than the last page boundary of the pipe + let writesize1: usize = 61441; + let mut buf1: Vec = vec!['A' as u8; writesize1]; + assert_eq!( + cage1.write_syscall(1, buf1.as_mut_ptr(), writesize1), + writesize1 as i32 + ); + + // now if we try to write anything we expect EAGAIN because there isnt a page of + // room + let writesize2: usize = 4; // 4 bytes + let mut buf2: Vec = vec!['B' as u8; writesize2]; + assert_eq!( + cage1.write_syscall(1, buf2.as_mut_ptr(), writesize2), + -(Errno::EAGAIN as i32) + ); + + assert_eq!(cage1.close_syscall(1), 0); + + let sender = std::thread::spawn(move || { + let cage2 = interface::cagetable_getref(3); + + // dup our pipe read end to stdout and close unused pipe ends + assert_eq!(cage2.close_syscall(pipefds.writefd), 0); + assert_eq!(cage2.dup2_syscall(pipefds.readfd, 0), 0); + assert_eq!(cage2.close_syscall(pipefds.readfd), 0); + + let bytes_to_read = 65536; + let mut buf: Vec = Vec::with_capacity(bytes_to_read); + + // ok now lets read everything + assert_eq!( + cage2.read_syscall(0, buf.as_mut_ptr(), bytes_to_read), + writesize1 as i32 + ); + // we've close the write ends so now we expect 0, signifying EOF + assert_eq!( + cage2.read_syscall(0, buf.as_mut_ptr(), bytes_to_read), + 0 as i32 + ); + + assert_eq!(cage2.close_syscall(0), 0); + + assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); + + sender.join().unwrap(); + + assert_eq!(cage1.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_ipc_pipe_rw_zero() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage1 = interface::cagetable_getref(1); + + // lets create a blank pipefd array, setting fds to -1 here before they can be + // populated by the pipe call + let mut pipefds = PipeArray { + readfd: -1, + writefd: -1, + }; + + // now setup the pipe and fork + + assert_eq!(cage1.pipe_syscall(&mut pipefds), 0); + assert_eq!(cage1.fork_syscall(3), 0); + + let sender = std::thread::spawn(move || { + let cage2 = interface::cagetable_getref(3); + + // dup our pipe write end to stdout and close unused pipe ends + assert_eq!(cage2.close_syscall(pipefds.writefd), 0); + assert_eq!(cage2.dup2_syscall(pipefds.readfd, 0), 0); + assert_eq!(cage2.close_syscall(pipefds.readfd), 0); + + // now lets check reading a length of 0, should return 0 + let mut buf: Vec = Vec::new(); + assert_eq!(cage2.read_syscall(0, buf.as_mut_ptr(), 0), 0 as i32); + + assert_eq!(cage2.close_syscall(0), 0); + + assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); + + // dup our pipe read end to stdout and close unused pipe ends + assert_eq!(cage1.close_syscall(pipefds.readfd), 0); + assert_eq!(cage1.dup2_syscall(pipefds.writefd, 1), 1); + assert_eq!(cage1.close_syscall(pipefds.writefd), 0); + + // now lets check writing a length of 0, should return 0 + let mut buf: Vec = Vec::new(); + assert_eq!(cage1.write_syscall(1, buf.as_mut_ptr(), 0), 0 as i32); + + assert_eq!(cage1.close_syscall(1), 0); + + sender.join().unwrap(); + + assert_eq!(cage1.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + + lindrustfinalize(); + } +} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 534d6f45..38beed6f 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,35 +1,58 @@ -#![allow(dead_code)] //suppress warning for these functions not being used in targets other than the tests +#![allow(dead_code)] //suppress warning for these functions not being used in targets other than the + // tests mod fs_tests; // mod ipc_tests; // mod networking_tests; +mod sys_tests; +// use rand::Rng; +use std::net::{TcpListener, UdpSocket}; use crate::interface; -// use crate::safeposix::{cage::*, filesystem::*}; -use crate::safeposix::cage::*; +use crate::safeposix::{cage::*, filesystem::*}; +#[allow(unused_parens)] #[cfg(test)] -mod main_tests { - use crate::tests::fs_tests::fs_tests::test_fs; - // use crate::tests::ipc_tests::ipc_tests::test_ipc; - // use crate::tests::networking_tests::net_tests::net_tests; +mod setup { use crate::interface; - // use crate::safeposix::{cage::*, dispatcher::*, filesystem::*}; - use crate::safeposix::{cage::*, dispatcher::*}; + use crate::safeposix::{cage::*, dispatcher::*, filesystem::*}; + + use lazy_static::lazy_static; use std::process::Command; - use std::io::Write; - use std::io; + use std::sync::Mutex; + + // Tests in rust as parallel by default and to make them share resources we are + // using a global static lock. + lazy_static! { + // This has a junk value (a bool). Could be anything... + #[derive(Debug)] + pub static ref TESTMUTEX: Mutex = { + Mutex::new(true) + }; + } - #[test] - pub fn tests() { - println!("TEST begin"); - io::stdout().flush().unwrap(); + // Using explicit lifetime to have a safe reference to the lock in the tests. + pub fn lock_and_init<'a>() -> std::sync::MutexGuard<'a, bool> { + set_panic_hook(); + + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently + let thelock = TESTMUTEX.lock().unwrap_or_else(|e| { + //if the lock is poisoned, we need to clear the poison and clean up references + // to the cage. + lindrustfinalize(); + //clear the mutex poisoning. + TESTMUTEX.clear_poison(); + //return the underlying guard. + e.into_inner() + }); interface::RUSTPOSIX_TESTSUITE.store(true, interface::RustAtomicOrdering::Relaxed); + //setup the lind filesystem, creates a clean filesystem for each test // lindrustinit(0); // { + // println!("test_setup()"); // let cage = interface::cagetable_getref(1); // crate::lib_fs_utils::lind_deltree(&cage, "/"); // assert_eq!(cage.mkdir_syscall("/dev", S_IRWXA), 0); @@ -69,14 +92,23 @@ mod main_tests { // } // lindrustfinalize(); - println!("FS TESTS"); - test_fs(); + //initialize the cage for the test. + lindrustinit(0); - // println!("NET TESTS"); - // net_tests(); + //return the lock to the caller which holds it till the end of the test. + thelock + } - // println!("IPC TESTS"); - // test_ipc(); + fn set_panic_hook() { + let orig_hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |panic_info| { + // this hook would be triggered whenever a panic occurs + // good for test cases that panicked inside the non-main thread + // so the trace information could be printed immediately + // instead of raising the error when the thread is joined, which might + // never happen and left the test blocking forever in some test cases + orig_hook(panic_info); + })); } } @@ -96,3 +128,21 @@ pub fn sizecbuf<'a>(size: usize) -> Box<[u8]> { pub fn cbuf2str(buf: &[u8]) -> &str { std::str::from_utf8(buf).unwrap() } + +// The RustPOSIX test suite avoids conflicts caused by repeatedly binding to the +// same ports by generating a random port number within the valid range +// (49152-65535) for each test run. This eliminates the need for waiting between +// tests. + +fn is_port_available(port: u16) -> bool { + TcpListener::bind(("127.0.0.1", port)).is_ok() && UdpSocket::bind(("127.0.0.1", port)).is_ok() +} + +pub fn generate_random_port() -> u16 { + for port in 49152..65535 { + if is_port_available(port) { + return port; + } + } + panic!("No available ports found"); +} diff --git a/src/tests/networking_tests.rs b/src/tests/networking_tests.rs index c8e64a60..cdcb9d5d 100644 --- a/src/tests/networking_tests.rs +++ b/src/tests/networking_tests.rs @@ -3,353 +3,5693 @@ pub mod net_tests { use super::super::*; use crate::interface; use crate::safeposix::{cage::*, dispatcher::*, filesystem}; + use libc::c_void; use std::mem::size_of; use std::sync::{Arc, Barrier}; - use std::io::Write; - use std::io; - use std::ptr; - use libc::*; - use std::ffi::CString; - use std::ffi::CStr; + #[test] + pub fn ut_lind_net_bind() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + let sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let port: u16 = generate_random_port(); + let socket = interface::GenSockaddr::V4(interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }); //127.0.0.1 + + //first bind should work... but second bind should not + assert_eq!(cage.bind_syscall(sockfd, &socket), 0); + assert_eq!(cage.bind_syscall(sockfd, &socket), -(Errno::EINVAL as i32)); //already bound so should fail + + //trying to bind another to the same IP/PORT + let sockfd2 = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + assert_eq!( + cage.bind_syscall(sockfd2, &socket), + -(Errno::EADDRINUSE as i32) + ); //already bound so should fail + + //UDP should still work... + let sockfd3 = cage.socket_syscall(AF_INET, SOCK_DGRAM, 0); + assert_eq!(cage.bind_syscall(sockfd3, &socket), 0); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_bind_on_zero() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //both the server and the socket are run from this file + let serversockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd2 = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + //making sure that the assigned fd's are valid + assert!(serversockfd > 0); + assert!(clientsockfd > 0); + assert!(clientsockfd2 > 0); + let port: u16 = generate_random_port(); + //binding to a socket + let sockaddr = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 + assert_eq!(cage.bind_syscall(serversockfd, &socket), 0); + assert_eq!(cage.listen_syscall(serversockfd, 1), 0); //we are only allowing for one client at a time + + //forking the cage to get another cage with the same information + assert_eq!(cage.fork_syscall(2), 0); + + //creating a thread for the server so that the information can be sent between + // the two threads + let thread = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + let port: u16 = generate_random_port(); + let mut socket2 = interface::GenSockaddr::V4(interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { s_addr: 0 }, + padding: 0, + }); //0.0.0.0 + + let mut sockfd = cage2.accept_syscall(serversockfd, &mut socket2); //really can only make sure that the fd is valid + assert!(sockfd > 0); + + interface::sleep(interface::RustDuration::from_millis(100)); + + //process the first test... + //Writing 100, then peek 100, then read 100 + let mut buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 100, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 100 + ); //peeking at the input message + assert_eq!(cbuf2str(&buf), &"A".repeat(100)); + buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 100, 0, &mut Some(&mut socket2)), + 100 + ); //reading the input message + assert_eq!(cbuf2str(&buf), &"A".repeat(100)); + buf = sizecbuf(100); + + interface::sleep(interface::RustDuration::from_millis(200)); + + //process the second test... + //Writing 100, read 20, peek 20, read 80 + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 20, 0, &mut Some(&mut socket2)), + 20 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(20) + &"\0".repeat(80)); + buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 20, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 20 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(20) + &"\0".repeat(80)); + buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 80, 0, &mut Some(&mut socket2)), + 80 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(80) + &"\0".repeat(20)); + buf = sizecbuf(100); + + interface::sleep(interface::RustDuration::from_millis(200)); + + //process the third test... + //Writing 100, peek several times, read 100 + for _ in 0..4 { + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 10, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 10 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(10) + &"\0".repeat(90)); + buf = sizecbuf(100); + } + for _ in 0..4 { + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 20, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 20 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(20) + &"\0".repeat(80)); + buf = sizecbuf(100); + } + for _ in 0..4 { + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 30, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 30 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(30) + &"\0".repeat(70)); + buf = sizecbuf(100); + } + for _ in 0..4 { + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 40, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 40 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(40) + &"\0".repeat(60)); + buf = sizecbuf(100); + } + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 100, 0, &mut Some(&mut socket2)), + 100 + ); + assert_eq!(cbuf2str(&buf), &"A".repeat(100)); + buf = sizecbuf(100); + + interface::sleep(interface::RustDuration::from_millis(200)); + + //process the fourth test... + //Writing 50, peek 50 + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 50, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 50 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(50) + &"\0".repeat(50)); + assert_eq!(cage2.close_syscall(sockfd), 0); + let port: u16 = generate_random_port(); + socket2 = interface::GenSockaddr::V4(interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { s_addr: 0 }, + padding: 0, + }); //0.0.0.0 + interface::sleep(interface::RustDuration::from_millis(200)); + sockfd = cage2.accept_syscall(serversockfd, &mut socket2); //really can only make sure that the fd is valid + assert!(sockfd > 0); + + //process the first test... + //Writing 100, then peek 100, then read 100 + let mut buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 100, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 100 + ); //peeking at the input message + assert_eq!(cbuf2str(&buf), &"A".repeat(100)); + buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 100, 0, &mut Some(&mut socket2)), + 100 + ); //reading the input message + assert_eq!(cbuf2str(&buf), &"A".repeat(100)); + buf = sizecbuf(100); + + interface::sleep(interface::RustDuration::from_millis(200)); + + //process the second test... + //Writing 100, read 20, peek 20, read 80 + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 20, 0, &mut Some(&mut socket2)), + 20 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(20) + &"\0".repeat(80)); + buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 20, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 20 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(20) + &"\0".repeat(80)); + buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 80, 0, &mut Some(&mut socket2)), + 80 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(80) + &"\0".repeat(20)); + + interface::sleep(interface::RustDuration::from_millis(200)); + + //process the third test... + //Writing 100, peek several times, read 100 + for _ in 0..4 { + buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 10, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 10 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(10) + &"\0".repeat(90)); + } + for _ in 0..4 { + buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 20, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 20 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(20) + &"\0".repeat(80)); + } + for _ in 0..4 { + buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 30, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 30 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(30) + &"\0".repeat(70)); + } + for _ in 0..4 { + buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 40, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 40 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(40) + &"\0".repeat(60)); + } + buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 100, 0, &mut Some(&mut socket2)), + 100 + ); + assert_eq!(cbuf2str(&buf), &"A".repeat(100)); + buf = sizecbuf(100); + + interface::sleep(interface::RustDuration::from_millis(200)); + + //process the fourth test... + //Writing 50, peek 50 + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 50, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 50 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(50) + &"\0".repeat(50)); + + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!(cage2.close_syscall(sockfd), 0); + assert_eq!(cage2.close_syscall(serversockfd), 0); + + assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); + + //connect to the server + interface::sleep(interface::RustDuration::from_millis(20)); + + assert_eq!(cage.connect_syscall(clientsockfd, &socket), 0); + + //send the data with delays so that the server can process the information + // cleanly + assert_eq!( + cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(100)), 100, 0), + 100 + ); + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!( + cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(100)), 100, 0), + 100 + ); + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!( + cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(100)), 100, 0), + 100 + ); + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!( + cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(50)), 50, 0), + 50 + ); + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!(cage.close_syscall(clientsockfd), 0); + + //connect to the server with the other sockfd + assert_eq!(cage.connect_syscall(clientsockfd2, &socket), 0); + + //send the data with delays so that the server can process the information + // cleanly + assert_eq!( + cage.send_syscall(clientsockfd2, str2cbuf(&"A".repeat(100)), 100, 0), + 100 + ); + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!( + cage.send_syscall(clientsockfd2, str2cbuf(&"A".repeat(100)), 100, 0), + 100 + ); + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!( + cage.send_syscall(clientsockfd2, str2cbuf(&"A".repeat(100)), 100, 0), + 100 + ); + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!( + cage.send_syscall(clientsockfd2, str2cbuf(&"A".repeat(50)), 50, 0), + 50 + ); + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!(cage.close_syscall(clientsockfd2), 0); + + thread.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_bind_multiple() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let mut sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let port: u16 = generate_random_port(); + let socket = interface::GenSockaddr::V4(interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }); //127.0.0.1 + assert_eq!( + cage.setsockopt_syscall(sockfd, SOL_SOCKET, SO_REUSEPORT, 1), + 0 + ); + assert_eq!(cage.bind_syscall(sockfd, &socket), 0); + + let sockfd2 = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + + //allowing port reuse + assert_eq!( + cage.setsockopt_syscall(sockfd2, SOL_SOCKET, SO_REUSEPORT, 1), + 0 + ); + + assert_eq!(cage.bind_syscall(sockfd2, &socket), 0); + + //double listen should be allowed + assert_eq!(cage.listen_syscall(sockfd, 1), 0); + assert_eq!(cage.listen_syscall(sockfd2, 1), 0); + + //UDP bind should be allowed + sockfd = cage.socket_syscall(AF_INET, SOCK_DGRAM, 0); + assert_eq!(cage.bind_syscall(sockfd, &socket), 0); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_connect_basic_udp() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //should be okay... + let sockfd = cage.socket_syscall(AF_INET, SOCK_DGRAM, 0); + let port: u16 = generate_random_port(); + let mut socket = interface::GenSockaddr::V4(interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }); //127.0.0.1 + assert_eq!(cage.connect_syscall(sockfd, &socket), 0); + let port: u16 = generate_random_port(); + //should be able to retarget the socket + socket = interface::GenSockaddr::V4(interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }); //127.0.0.1 + assert_eq!(cage.connect_syscall(sockfd, &socket), 0); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + #[ignore] + //Test connect sys call using AF_INET6/IPv6 address family and UDP socket type + //Currently failing as IPv6 is not implemented via gen_netdevs + pub fn ut_lind_net_connect_basic_udp_ipv6() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //Initialize initial socket fd and remote socket to connect to + let sockfd = cage.socket_syscall(AF_INET6, SOCK_DGRAM, 0); + let port: u16 = generate_random_port(); + let mut socket = interface::GenSockaddr::V6(interface::SockaddrV6 { + sin6_family: AF_INET6 as u16, + sin6_port: port.to_be(), + sin6_addr: interface::V6Addr { + s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + }, + sin6_flowinfo: 0, + sin6_scope_id: 0, + }); //::1 LOCALHOST + assert_eq!(cage.connect_syscall(sockfd, &socket), 0); + + //Change the port and retarget the socket + let port: u16 = generate_random_port(); + socket = interface::GenSockaddr::V6(interface::SockaddrV6 { + sin6_family: AF_INET6 as u16, + sin6_port: port.to_be(), + sin6_addr: interface::V6Addr { + s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + }, + sin6_flowinfo: 0, + sin6_scope_id: 0, + }); //::1 LOCALHOST + assert_eq!(cage.connect_syscall(sockfd, &socket), 0); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_getpeername_bad_input() { + // this test is used for testing getpeername with invalid input + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let filefd = cage.open_syscall("/getpeernametest.txt", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); + assert!(filefd > 0); + + // test for invalid file descriptor + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); //127.0.0.1 + // fd that is out of range + assert_eq!( + cage.getpeername_syscall(-1, &mut retsocket), + -(Errno::EBADF as i32) + ); + // fd that does not exist + assert_eq!( + cage.getpeername_syscall(10, &mut retsocket), + -(Errno::EBADF as i32) + ); + // fd that is not socket + assert_eq!( + cage.getpeername_syscall(filefd, &mut retsocket), + -(Errno::ENOTSOCK as i32) + ); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_getpeername_inet() { + // this test is used for testing getpeername on AF_INET sockets + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd1 = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + + let port: u16 = generate_random_port(); + let socket = interface::GenSockaddr::V4(interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }); // 127.0.0.1 + + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!(cage.bind_syscall(sockfd, &socket), 0); + // we havn't connected yet, so it should return ENOTCONN error + assert_eq!( + cage.getpeername_syscall(sockfd, &mut retsocket), + -(Errno::ENOTCONN as i32) + ); + + assert_eq!(cage.fork_syscall(2), 0); // used for AF_INET thread client + + let barrier = Arc::new(Barrier::new(2)); + let barrier_clone = barrier.clone(); + + let threadclient1 = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + assert_eq!(cage2.close_syscall(sockfd), 0); + + barrier_clone.wait(); + // connect to server + assert_eq!(cage2.connect_syscall(clientsockfd1, &socket), 0); + + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!(cage2.getpeername_syscall(clientsockfd1, &mut retsocket), 0); + // the retsocket should be exactly the same as the address of the server + assert_eq!(retsocket, socket); + + cage2.exit_syscall(EXIT_SUCCESS); + }); + + assert_eq!(cage.listen_syscall(sockfd, 4), 0); + let mut sockgarbage = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + barrier.wait(); + let fd = cage.accept_syscall(sockfd as i32, &mut sockgarbage); + assert!(fd > 0); + + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!(cage.getpeername_syscall(fd, &mut retsocket), 0); + // peer's port should not be zero + assert_ne!(retsocket.port(), 0); + // peer's address should be 127.0.0.1 + assert_eq!( + retsocket.addr(), + interface::GenIpaddr::V4(interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }) + ); + + threadclient1.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_getpeername_unix() { + // this test is used for testing getpeername on AF_UNIX sockets + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let sockfd = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + + let serversockaddr_unix = + interface::new_sockaddr_unix(AF_UNIX as u16, "server_getpeername".as_bytes()); + let serversocket_unix = interface::GenSockaddr::Unix(serversockaddr_unix); + assert_eq!(cage.bind_syscall(sockfd, &serversocket_unix), 0); + + assert_eq!(cage.fork_syscall(2), 0); + + let barrier = Arc::new(Barrier::new(2)); + let barrier_clone = barrier.clone(); + + let threadclient1 = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + assert_eq!(cage2.close_syscall(sockfd), 0); + + let clientsockfd1 = cage2.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + + barrier_clone.wait(); + // connect to server + assert_eq!(cage2.connect_syscall(clientsockfd1, &serversocket_unix), 0); + + let mut retsocket = interface::GenSockaddr::Unix(interface::new_sockaddr_unix(0, &[])); + assert_eq!(cage2.getpeername_syscall(clientsockfd1, &mut retsocket), 0); + let unixinfo = match retsocket { + interface::GenSockaddr::Unix(value) => value, + _ => unreachable!(), + }; + // client's peer address should be exactly same as the server address + assert_eq!(unixinfo, serversockaddr_unix); + + cage2.exit_syscall(EXIT_SUCCESS); + }); + + assert_eq!(cage.listen_syscall(sockfd, 1), 0); + let mut sockgarbage = interface::GenSockaddr::Unix(interface::new_sockaddr_unix( + AF_UNIX as u16, + "".as_bytes(), + )); + barrier.wait(); + let fd = cage.accept_syscall(sockfd as i32, &mut sockgarbage); + assert!(fd > 0); + + let mut retsocket = interface::GenSockaddr::Unix(interface::new_sockaddr_unix(0, &[])); + assert_eq!(cage.getpeername_syscall(fd, &mut retsocket), 0); + let unixinfo = match retsocket { + interface::GenSockaddr::Unix(value) => value, + _ => unreachable!(), + }; + // server's peer address should have family of AF_UNIX + assert_eq!(unixinfo.sun_family, AF_UNIX as u16); + // and a path that is not empty + assert_ne!(unixinfo.sun_path[0], 0); + + threadclient1.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_getpeername_udp() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //doing a few things with connect -- only UDP right now + let sockfd = cage.socket_syscall(AF_INET, SOCK_DGRAM, 0); + let port: u16 = generate_random_port(); + let mut socket = interface::GenSockaddr::V4(interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }); //127.0.0.1 + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); //127.0.0.1 + + assert_eq!(cage.connect_syscall(sockfd, &socket), 0); + assert_eq!(cage.getpeername_syscall(sockfd, &mut retsocket), 0); + assert_eq!(retsocket, socket); + let port: u16 = generate_random_port(); + //should be able to retarget + socket = interface::GenSockaddr::V4(interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }); //127.0.0.1 + assert_eq!(cage.connect_syscall(sockfd, &socket), 0); + assert_eq!(cage.getpeername_syscall(sockfd, &mut retsocket), 0); + assert_eq!(retsocket, socket); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_getsockname_bad_input() { + // this test is used for testing getsockname with invalid input + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // test for passing invalid fd + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!( + cage.getsockname_syscall(10, &mut retsocket), + -(Errno::EBADF as i32) + ); + + // test for passing fd that is not socket + let filefd = cage.open_syscall("/getsocknametest.txt", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); + assert!(filefd > 0); + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!( + cage.getsockname_syscall(filefd, &mut retsocket), + -(Errno::ENOTSOCK as i32) + ); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_getsockname_empty() { + // this test is used for testing getsockname with socket that hasn't bound to + // any address + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // test for ipv4 + let emptysocket_ipv4 = interface::GenSockaddr::V4(interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: 0, + sin_addr: interface::V4Addr::default(), + padding: 0, + }); + + let sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + assert!(sockfd >= 0); + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!(cage.getsockname_syscall(sockfd, &mut retsocket), 0); + assert_eq!(retsocket, emptysocket_ipv4); + + // test for unix socket + let sockfd = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + assert!(sockfd >= 0); + let mut retsocket = + interface::GenSockaddr::Unix(interface::new_sockaddr_unix(SOCK_STREAM as u16, &[])); + let emptysocket_unix = + interface::GenSockaddr::Unix(interface::new_sockaddr_unix(SOCK_STREAM as u16, &[])); + assert_eq!(cage.getsockname_syscall(sockfd, &mut retsocket), 0); + assert_eq!(retsocket, emptysocket_unix); + + // test for ipv6 + let sockfd = cage.socket_syscall(AF_INET6, SOCK_STREAM, 0); + assert!(sockfd >= 0); + let mut retsocket = interface::GenSockaddr::V6(interface::SockaddrV6::default()); + assert_eq!(cage.getsockname_syscall(sockfd, &mut retsocket), 0); + // port should be 0 + assert_eq!(retsocket.port(), 0); + // address should be :: + assert_eq!( + retsocket.addr(), + interface::GenIpaddr::V6(interface::V6Addr::default()) + ); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + #[ignore] + pub fn ut_lind_net_getsockname_inet_connect() { + // temporary test derived from ut_lind_net_getsockname_inet due to a bug + // once the bug is fixed, ideally should merge back into + // ut_lind_net_getsockname_inet + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd1 = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + + let port: u16 = generate_random_port(); + let socket = interface::GenSockaddr::V4(interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }); // 127.0.0.1 + + assert_eq!(cage.bind_syscall(sockfd, &socket), 0); + + assert_eq!(cage.fork_syscall(2), 0); // used for AF_INET thread client + + let barrier = Arc::new(Barrier::new(2)); + let barrier_clone = barrier.clone(); + + let threadclient1 = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + assert_eq!(cage2.close_syscall(sockfd), 0); + + barrier_clone.wait(); + // connect to server + assert_eq!(cage2.connect_syscall(clientsockfd1, &socket), 0); + + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!(cage2.getsockname_syscall(clientsockfd1, &mut retsocket), 0); + + // BUG: when calling connect without binding to any address, an new address will + // be automatically assigned to the socket, and its ip address is supposed to be + // 127.0.0.1, and port is not supposed to be 0. But we failed this test + assert_ne!(retsocket.port(), 0); + assert_eq!( + retsocket.addr(), + interface::GenIpaddr::V4(interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }) + ); + + cage2.exit_syscall(EXIT_SUCCESS); + }); + + assert_eq!(cage.listen_syscall(sockfd, 4), 0); + let mut sockgarbage = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + barrier.wait(); + let fd = cage.accept_syscall(sockfd as i32, &mut sockgarbage); + assert!(fd > 0); + + threadclient1.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_getsockname_inet() { + // this test is used for testing getsockname on AF_INET sockets + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd1 = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + + let port: u16 = generate_random_port(); + let socket = interface::GenSockaddr::V4(interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }); // 127.0.0.1 + + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!(cage.bind_syscall(sockfd, &socket), 0); + assert_eq!(cage.getsockname_syscall(sockfd, &mut retsocket), 0); + assert_eq!(retsocket, socket); + + // checking that we cannot rebind the socket + assert_eq!(cage.bind_syscall(sockfd, &socket), -(Errno::EINVAL as i32)); //already bound so should fail + assert_eq!(cage.getsockname_syscall(sockfd, &mut retsocket), 0); + assert_eq!(retsocket, socket); + + assert_eq!(cage.fork_syscall(2), 0); // used for AF_INET thread client + + let barrier = Arc::new(Barrier::new(2)); + let barrier_clone = barrier.clone(); + + let threadclient1 = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + assert_eq!(cage2.close_syscall(sockfd), 0); + + barrier_clone.wait(); + // connect to server + assert_eq!(cage2.connect_syscall(clientsockfd1, &socket), 0); + + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!(cage2.getsockname_syscall(clientsockfd1, &mut retsocket), 0); + + // assert_ne!(retsocket.port(), 0); + // assert_eq!(retsocket.addr(), interface::GenIpaddr::V4(interface::V4Addr { + // s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + // })); + + cage2.exit_syscall(EXIT_SUCCESS); + }); + + assert_eq!(cage.listen_syscall(sockfd, 4), 0); + let mut sockgarbage = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + barrier.wait(); + let fd = cage.accept_syscall(sockfd as i32, &mut sockgarbage); + + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!(cage.getsockname_syscall(fd, &mut retsocket), 0); + assert_ne!(retsocket.port(), 0); + assert_eq!( + retsocket.addr(), + interface::GenIpaddr::V4(interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }) + ); + + threadclient1.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_getsockname_unix() { + // this test is used for testing getsockname on AF_UNIX sockets + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // create a AF_UNIX socket + let sockfd = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + + let serversockaddr_unix = + interface::new_sockaddr_unix(AF_UNIX as u16, "server_getsockname".as_bytes()); + let serversocket_unix = interface::GenSockaddr::Unix(serversockaddr_unix); + assert_eq!(cage.bind_syscall(sockfd, &serversocket_unix), 0); + + // now the socket is bound to an address, check if the address returned from + // getsockname_syscall is correct + let mut retsocket = interface::GenSockaddr::Unix(interface::new_sockaddr_unix(0, &[])); + assert_eq!(cage.getsockname_syscall(sockfd, &mut retsocket), 0); + // retrieve the unix info + let unixinfo = match retsocket { + interface::GenSockaddr::Unix(value) => value, + _ => unreachable!(), + }; + assert_eq!(serversockaddr_unix, unixinfo); + + assert_eq!(cage.fork_syscall(2), 0); + + // this barrier is to coordinate server and client + let barrier = Arc::new(Barrier::new(2)); + let barrier_clone = barrier.clone(); + + let threadclient1 = interface::helper_thread(move || { + // client thread + let cage2 = interface::cagetable_getref(2); + assert_eq!(cage2.close_syscall(sockfd), 0); + + let clientsockfd1 = cage2.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + + barrier_clone.wait(); + // connect to server + assert_eq!(cage2.connect_syscall(clientsockfd1, &serversocket_unix), 0); + + // the client address hasn't bound to an address before connect + // so a new address must be automatically assigned to it + let mut retsocket = interface::GenSockaddr::Unix(interface::new_sockaddr_unix(0, &[])); + assert_eq!(cage2.getsockname_syscall(clientsockfd1, &mut retsocket), 0); + // retrieve the unix info + let unixinfo = match retsocket { + interface::GenSockaddr::Unix(value) => value, + _ => unreachable!(), + }; + // check the family + assert_eq!(unixinfo.sun_family, AF_UNIX as u16); + // its path should not be empty + // since we do not know the assigned address exactly + // so we just check if the first character of the address it not null + // to verify if the address is not empty + assert_ne!(unixinfo.sun_path[0], 0); + + cage2.exit_syscall(EXIT_SUCCESS); + }); + + // server listen + assert_eq!(cage.listen_syscall(sockfd, 1), 0); + let mut sockgarbage = interface::GenSockaddr::Unix(interface::new_sockaddr_unix( + AF_UNIX as u16, + "".as_bytes(), + )); + barrier.wait(); + let fd = cage.accept_syscall(sockfd as i32, &mut sockgarbage); + + // the socket created from accept should be automatically assigned an address as + // well + let mut retsocket = interface::GenSockaddr::Unix(interface::new_sockaddr_unix(0, &[])); + assert_eq!(cage.getsockname_syscall(fd, &mut retsocket), 0); + let unixinfo = match retsocket { + interface::GenSockaddr::Unix(value) => value, + _ => unreachable!(), + }; + // same check as client + assert_eq!(unixinfo.sun_family, AF_UNIX as u16); + assert_ne!(unixinfo.sun_path[0], 0); + + threadclient1.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_listen() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let serversockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + + assert!(serversockfd > 0); + assert!(clientsockfd > 0); + let port: u16 = generate_random_port(); + //binding to a socket + let sockaddr = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 + assert_eq!(cage.bind_syscall(serversockfd, &socket), 0); + assert_eq!(cage.listen_syscall(serversockfd, 10), 0); + + //forking the cage to get another cage with the same information + assert_eq!(cage.fork_syscall(2), 0); + + let thread = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + let mut socket2 = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert!(cage2.accept_syscall(serversockfd, &mut socket2) > 0); //really can only make sure that the fd is valid + + assert_eq!(cage2.close_syscall(serversockfd), 0); + assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); + + interface::sleep(interface::RustDuration::from_millis(100)); + assert_eq!(cage.connect_syscall(clientsockfd, &socket), 0); + + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!(cage.getsockname_syscall(clientsockfd, &mut retsocket), 0); + assert_ne!(retsocket, socket); + + assert_eq!(cage.close_syscall(serversockfd), 0); + + thread.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + //Attempt to call accept on a socket that is not listening for a connection + //Attempt to call accept on a socket that is closed + //Both of these should return their respective errors, being EINVAL and EBADF + pub fn ut_lind_net_accept_errs() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let serversockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + + assert!(serversockfd > 0); + assert!(clientsockfd > 0); + let port: u16 = generate_random_port(); + //binding to a socket + let sockaddr = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 + assert_eq!(cage.bind_syscall(serversockfd, &socket), 0); + + //forking the cage to get another cage with the same information + assert_eq!(cage.fork_syscall(2), 0); + + let thread = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + let mut socket2 = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + + //Accept before listening + assert_eq!( + cage2.accept_syscall(serversockfd, &mut socket2), + -(Errno::EINVAL as i32) + ); + //Now listen + assert_eq!(cage2.listen_syscall(serversockfd, 10), 0); + + //Check that fd > 0, indicating a valid connection + assert!(cage2.accept_syscall(serversockfd, &mut socket2) > 0); + //Close serverfd and check that it doesn't accept + assert_eq!(cage2.close_syscall(serversockfd), 0); + assert_eq!( + cage2.accept_syscall(serversockfd, &mut socket2), + -(Errno::EBADF as i32) + ); + + assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); + + interface::sleep(interface::RustDuration::from_millis(1000)); + assert_eq!(cage.connect_syscall(clientsockfd, &socket), 0); + + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!(cage.getsockname_syscall(clientsockfd, &mut retsocket), 0); + assert_ne!(retsocket, socket); + + assert_eq!(cage.close_syscall(serversockfd), 0); + + thread.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + //We try to queue more than 1 connection with the + //backlog is set to 1. We expect both connections to return with + //errno set to EINPROGRESS as the sockets are non-blocking + pub fn ut_lind_net_listen_more_than_backlog() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let serversockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd1 = cage.socket_syscall(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + let clientsockfd2 = cage.socket_syscall(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + + assert!(serversockfd > 0); + assert!(clientsockfd1 > 0); + assert!(clientsockfd2 > 0); + let port: u16 = generate_random_port(); + //binding to a socket + let sockaddr = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 + assert_eq!(cage.bind_syscall(serversockfd, &socket), 0); + assert_eq!(cage.listen_syscall(serversockfd, 1), 0); + + //forking the cage to get another cage with the same information + assert_eq!(cage.fork_syscall(2), 0); + + let thread = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + + assert_eq!( + cage2.connect_syscall(clientsockfd1, &socket), + -(Errno::EINPROGRESS as i32) + ); + + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!( + cage2.connect_syscall(clientsockfd2, &socket), + -(Errno::EINPROGRESS as i32) + ); + + assert_eq!(cage2.close_syscall(serversockfd), 0); + assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); + + assert_eq!(cage.close_syscall(serversockfd), 0); + + thread.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + //UDP Socket should not be able to listen + //Fail with errno EOPNOTSUPP + #[test] + pub fn ut_lind_net_listen_udp_socket() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let serversockfd = cage.socket_syscall(AF_INET, SOCK_DGRAM, 0); + assert!(serversockfd > 0); + let port: u16 = generate_random_port(); + //binding to a socket + let sockaddr = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 + assert_eq!(cage.bind_syscall(serversockfd, &socket), 0); + assert_eq!(cage.listen_syscall(serversockfd, 10), -95); //why can't i use EOPNOTSUPP here?? + + assert_eq!(cage.close_syscall(serversockfd), 0); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_poll_bad_input() { + // this test is used for testing poll with some error/edge cases + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // error case 1: invalid file descriptor + // contruct a PollStruct with invalid fd (10) + let mut polled = vec![interface::PollStruct { + fd: 10, + events: POLLIN, + revents: 0, + }]; + + // exactly one fd should have non-zero revents field + assert_eq!(cage.poll_syscall(&mut polled.as_mut_slice(), None), 1); + // and its revents should be set to POLLNVAL + assert_eq!(polled[0].revents, POLLNVAL); + + // error case 2: negative file descriptor should be ignored + // contruct a PollStruct with negative fd + let mut polled = vec![interface::PollStruct { + fd: -1, + events: POLLIN, + revents: 0, + }]; + + // the fd should be ignored, so no error is expected + assert_eq!( + cage.poll_syscall( + &mut polled.as_mut_slice(), + Some(interface::RustDuration::ZERO) + ), + 0 + ); + // revents should be 0 + assert_eq!(polled[0].revents, 0); + + // edge case: revents should always be cleared + // create a file + let filefd = cage.open_syscall("/netpolltest.txt", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); + assert!(filefd > 0); + // create a pipe + let mut pipefds = PipeArray { + readfd: -1, + writefd: -1, + }; + assert_eq!(cage.pipe2_syscall(&mut pipefds, O_NONBLOCK), 0); + // contruct a PollStruct with three PollStruct: + // 1. normal file with non-zero revents, test for revents when the fd is ready + // 2. negative fd with non-zero revents, even this fd should be ignored, its + // revents should still be cleared + // 3. pipe readfd, test for revents when the fd is not ready + let mut polled = vec![ + interface::PollStruct { + fd: filefd, + events: POLLIN, + revents: 123, + }, + interface::PollStruct { + fd: -1, + events: POLLIN, + revents: 123, + }, + interface::PollStruct { + fd: pipefds.readfd, + events: POLLIN, + revents: 123, + }, + ]; + // should have exactly one fd ready (file fd) + assert_eq!(cage.poll_syscall(&mut polled.as_mut_slice(), None), 1); + assert_eq!(polled[0].revents, POLLIN); // file fd + assert_eq!(polled[1].revents, 0); // negative fd + assert_eq!(polled[2].revents, 0); // unready fd + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_poll_timeout() { + // this test is used for testing poll with timeout behaviors specifically + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // subtest 1: poll when timeout could expire + // create a TCP AF_INET socket + let serversockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + assert!(serversockfd > 0); + assert!(clientsockfd > 0); + + let port: u16 = generate_random_port(); + let sockaddr = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 from bytes above + + // server bind and listen + assert_eq!(cage.bind_syscall(serversockfd, &socket), 0); + assert_eq!(cage.listen_syscall(serversockfd, 4), 0); + + assert_eq!(cage.fork_syscall(2), 0); + assert_eq!(cage.close_syscall(clientsockfd), 0); + + // this barrier is used for preventing + // an unfixed bug (`close` could block when other thread/cage is `accept`) from + // deadlocking the test + let barrier = Arc::new(Barrier::new(2)); + let barrier_2 = barrier.clone(); + + //client connects to the server to send and recv data... + let threadclient = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + assert_eq!(cage2.close_syscall(serversockfd), 0); + + barrier_2.wait(); + + // connect to the server + assert_eq!(cage2.connect_syscall(clientsockfd, &socket), 0); + + // wait for 100ms + interface::sleep(interface::RustDuration::from_millis(100)); + + // send some message to client + assert_eq!(cage2.send_syscall(clientsockfd, str2cbuf("test"), 4, 0), 4); + + assert_eq!(cage2.close_syscall(clientsockfd), 0); + cage2.exit_syscall(EXIT_SUCCESS); + }); + + // make sure client thread closed the duplicated socket before server start to + // accept + barrier.wait(); + + // wait for client to connect + let mut sockgarbage = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + let sockfd = cage.accept_syscall(serversockfd as i32, &mut sockgarbage); + + // create PollStruct + let mut polled = vec![interface::PollStruct { + fd: sockfd, + events: POLLIN, + revents: 0, + }]; + + // this counter is used for recording how many times do poll returns due to + // timeout + let mut counter = 0; + + loop { + let poll_result = cage.poll_syscall( + &mut polled.as_mut_slice(), + Some(interface::RustDuration::new(0, 10000000)), // 10ms + ); + assert!(poll_result >= 0); + // poll timeout after 10ms, but client will send messages after 100ms + // so there should be some timeout return + if poll_result == 0 { + counter += 1; + } else if polled[0].revents & POLLIN != 0 { + // just received the message, check the message and break + let mut buf = sizecbuf(4); + assert_eq!(cage.recv_syscall(sockfd, buf.as_mut_ptr(), 4, 0), 4); + assert_eq!(cbuf2str(&buf), "test"); + break; + } else { + unreachable!(); + } + } + // check if poll timeout correctly + assert!(counter > 0); + + threadclient.join().unwrap(); + + // subtest 2: poll when all arguments were None except for timeout + // since no set is passed into `poll`, `poll` here should behave like + // `sleep` + let start_time = interface::starttimer(); + let timeout = interface::RustDuration::new(0, 10000000); // 10ms + let poll_result = cage.poll_syscall(&mut vec![].as_mut_slice(), Some(timeout)); + assert!(poll_result == 0); + // should wait for at least 10ms + assert!(interface::readtimer(start_time) >= timeout); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + #[ignore] + pub fn ut_lind_net_poll() { + // test for poll monitoring on multiple different file descriptors: + // 1. regular file + // 2. AF_INET server socket waiting for two clients + // 3. AF_INET server socket's connection file descriptor with clients + // 4. AF_UNIX server socket's connection file descriptor with a client + // 5. pipe + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + + // creating regular file's file descriptor + let filefd = cage.open_syscall("/netpolltest.txt", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); + assert!(filefd > 0); + + // creating socket file descriptors + let serversockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd1 = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd2 = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let serversockfd_unix = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + let clientsockfd_unix = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + + assert!(serversockfd > 0); + assert!(clientsockfd1 > 0); + assert!(clientsockfd2 > 0); + assert!(serversockfd_unix > 0); + assert!(clientsockfd_unix > 0); + + // creating a pipe + let mut pipefds = PipeArray { + readfd: -1, + writefd: -1, + }; + assert_eq!(cage.pipe_syscall(&mut pipefds), 0); + + // create a INET address + let port: u16 = generate_random_port(); + + let sockaddr = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 from bytes above + + //binding to a socket + let serversockaddr_unix = + interface::new_sockaddr_unix(AF_UNIX as u16, "server_poll".as_bytes()); + let serversocket_unix = interface::GenSockaddr::Unix(serversockaddr_unix); + + let clientsockaddr_unix = + interface::new_sockaddr_unix(AF_UNIX as u16, "client_poll".as_bytes()); + let clientsocket_unix = interface::GenSockaddr::Unix(clientsockaddr_unix); + + assert_eq!(cage.bind_syscall(serversockfd_unix, &serversocket_unix), 0); + assert_eq!(cage.bind_syscall(clientsockfd_unix, &clientsocket_unix), 0); + assert_eq!(cage.listen_syscall(serversockfd_unix, 1), 0); + + assert_eq!(cage.bind_syscall(serversockfd, &socket), 0); + assert_eq!(cage.listen_syscall(serversockfd, 4), 0); + + // create a PollStruct for each fd + let serverpoll = interface::PollStruct { + fd: serversockfd, + events: POLLIN, + revents: 0, + }; + + let serverunixpoll = interface::PollStruct { + fd: serversockfd_unix, + events: POLLIN, + revents: 0, + }; + + let filepoll = interface::PollStruct { + fd: filefd, + events: POLLIN | POLLOUT, + revents: 0, + }; + + let pipepoll = interface::PollStruct { + fd: pipefds.readfd, + events: POLLIN, + revents: 0, + }; + + let mut polled = vec![filepoll, serverpoll, serverunixpoll, pipepoll]; + + assert_eq!(cage.fork_syscall(2), 0); // used for AF_INET thread client 1 + assert_eq!(cage.fork_syscall(3), 0); // used for AF_INET thread client 2 + assert_eq!(cage.fork_syscall(4), 0); // used for AF_UNIX thread client + + assert_eq!(cage.fork_syscall(5), 0); // used for pipe thread + + assert_eq!(cage.close_syscall(clientsockfd1), 0); + assert_eq!(cage.close_syscall(clientsockfd2), 0); + assert_eq!(cage.close_syscall(clientsockfd_unix), 0); + + // this barrier have to ensure that the clients finish the connect before we do + // the poll due to an unfixed bug (`close` could block when other + // thread/cage is `accept`) + let barrier = Arc::new(Barrier::new(3)); + let barrier_clone1 = barrier.clone(); + let barrier_clone2 = barrier.clone(); + + // this barrier is used for control the flow the pipe + let barrier_pipe = Arc::new(Barrier::new(2)); + let barrier_pipe_clone = barrier_pipe.clone(); + + // due to an unfixed bug in ref counter of AF_UNIX socket pipe + // have to make sure all the threads exits only after the AF_UNIX test finished + let barrier_exit = Arc::new(Barrier::new(4)); + let barrier_exit_clone1 = barrier_exit.clone(); + let barrier_exit_clone2 = barrier_exit.clone(); + let barrier_exit_clone3 = barrier_exit.clone(); + + // client 1 connects to the server to send and recv data + let threadclient1 = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + assert_eq!(cage2.close_syscall(serversockfd), 0); + assert_eq!(cage2.close_syscall(clientsockfd2), 0); + + // connect to server + assert_eq!(cage2.connect_syscall(clientsockfd1, &socket), 0); + barrier_clone1.wait(); + + // send message to server + assert_eq!(cage2.send_syscall(clientsockfd1, str2cbuf("test"), 4, 0), 4); + + interface::sleep(interface::RustDuration::from_millis(1)); + + // receive message from server + let mut buf = sizecbuf(4); + assert_eq!(cage2.recv_syscall(clientsockfd1, buf.as_mut_ptr(), 4, 0), 4); + assert_eq!(cbuf2str(&buf), "test"); + + assert_eq!(cage2.close_syscall(clientsockfd1), 0); + barrier_exit_clone1.wait(); + cage2.exit_syscall(EXIT_SUCCESS); + }); + + // client 2 connects to the server to send and recv data + let threadclient2 = interface::helper_thread(move || { + let cage3 = interface::cagetable_getref(3); + assert_eq!(cage3.close_syscall(serversockfd), 0); + assert_eq!(cage3.close_syscall(clientsockfd1), 0); + + // connect to server + assert_eq!(cage3.connect_syscall(clientsockfd2, &socket), 0); + barrier_clone2.wait(); + + // send message to server + assert_eq!(cage3.send_syscall(clientsockfd2, str2cbuf("test"), 4, 0), 4); + + interface::sleep(interface::RustDuration::from_millis(1)); + + // receive message from server + let mut buf = sizecbuf(4); + let mut result: i32; + loop { + result = cage3.recv_syscall(clientsockfd2, buf.as_mut_ptr(), 4, 0); + if result != -libc::EINTR { + break; // if the error was EINTR, retry the syscall + } + } + assert_eq!(result, 4); + assert_eq!(cbuf2str(&buf), "test"); + + assert_eq!(cage3.close_syscall(clientsockfd2), 0); + barrier_exit_clone2.wait(); + cage3.exit_syscall(EXIT_SUCCESS); + }); + + let threadclient_unix = interface::helper_thread(move || { + let cage4 = interface::cagetable_getref(4); + assert_eq!(cage4.close_syscall(serversockfd_unix), 0); + assert_eq!(cage4.close_syscall(serversockfd), 0); + + // connect to server + assert_eq!( + cage4.connect_syscall(clientsockfd_unix, &serversocket_unix), + 0 + ); + + // send message to server + assert_eq!( + cage4.send_syscall(clientsockfd_unix, str2cbuf("test"), 4, 0), + 4 + ); + + interface::sleep(interface::RustDuration::from_millis(1)); + + // recieve message from server + let mut buf = sizecbuf(4); + let mut result: i32; + loop { + result = cage4.recv_syscall(clientsockfd_unix, buf.as_mut_ptr(), 4, 0); + if result != -libc::EINTR { + break; // if the error was EINTR, retry the syscall + } + } + assert_eq!(result, 4); + assert_eq!(cbuf2str(&buf), "test"); + + assert_eq!(cage4.close_syscall(clientsockfd_unix), 0); + cage4.exit_syscall(EXIT_SUCCESS); + }); + + let thread_pipe = interface::helper_thread(move || { + let cage5 = interface::cagetable_getref(5); + + interface::sleep(interface::RustDuration::from_millis(1)); + // send message to pipe + assert_eq!(cage5.write_syscall(pipefds.writefd, str2cbuf("test"), 4), 4); + + let mut buf = sizecbuf(5); + // wait until peer read the message + barrier_pipe_clone.wait(); + + // read the message sent by peer + assert_eq!(cage5.read_syscall(pipefds.readfd, buf.as_mut_ptr(), 5), 5); + assert_eq!(cbuf2str(&buf), "test2"); + + barrier_exit_clone3.wait(); + cage5.exit_syscall(EXIT_SUCCESS); + }); + + barrier.wait(); + // acting as the server and processing the request + // Server loop to handle connections and I/O + // Check for any activity in any of the Input sockets + for counter in 0..600 { + let poll_result = cage.poll_syscall(&mut polled.as_mut_slice(), None); + assert!(poll_result >= 0); // check for error + + // clearfds stores the fds that should be removed from polled at the end of the + // iteration + let mut clearfds = vec![]; + // addfds stores the fds that should be added to polled at the end of the + // iteration + let mut addfds = vec![]; + + // check for readfds + for poll in &mut polled { + // If the socket returned was listerner socket, then there's a new conn., so we + // accept it, and put the client socket in the list of Inputs. + if poll.fd == serversockfd { + if poll.revents & POLLIN != 0 { + let mut sockgarbage = + interface::GenSockaddr::V4(interface::SockaddrV4::default()); + let sockfd = cage.accept_syscall(poll.fd as i32, &mut sockgarbage); + assert!(sockfd > 0); + // new connection is estalished, add it to readfds and writefds + + addfds.push(interface::PollStruct { + fd: sockfd, + events: POLLIN | POLLOUT, + revents: 0, + }); + } + } else if poll.fd == filefd { + // poll on regular file should always success + // therefore revents should be set for filefd at the first iteration + assert_eq!(counter, 0); + assert_eq!(poll.revents, POLLIN | POLLOUT); + // remove file fd from poll + clearfds.push(filefd); + } else if poll.fd == serversockfd_unix { + if poll.revents & POLLIN != 0 { + // unix socket + let mut sockgarbage = interface::GenSockaddr::Unix( + interface::new_sockaddr_unix(AF_UNIX as u16, "".as_bytes()), + ); + let sockfd = cage.accept_syscall(poll.fd as i32, &mut sockgarbage); + assert!(sockfd > 0); + // new connection is estalished, add it to poll + addfds.push(interface::PollStruct { + fd: sockfd, + events: POLLIN | POLLOUT, + revents: 0, + }); + } + } else if poll.fd == pipefds.readfd { + if poll.revents & POLLIN != 0 { + // pipe + let mut buf = sizecbuf(4); + // read the message from peer + assert_eq!(cage.read_syscall(pipefds.readfd, buf.as_mut_ptr(), 4), 4); + assert_eq!(cbuf2str(&buf), "test"); + + // write the message from peer + assert_eq!( + cage.write_syscall(pipefds.writefd, str2cbuf("test2"), 5) as usize, + 5 + ); + barrier_pipe.wait(); + + // pipe poll test done + clearfds.push(pipefds.readfd); + } + } else { + if poll.revents & POLLIN != 0 { + //If the socket is in established conn., then we recv the data. If there's + // no data, then close the client socket. + let mut buf = sizecbuf(4); + let mut recvresult: i32; + loop { + // receive message from peer + recvresult = cage.recv_syscall(poll.fd as i32, buf.as_mut_ptr(), 4, 0); + if recvresult != -libc::EINTR { + break; // if the error was EINTR, retry the + // syscall + } + } + if recvresult == 4 { + if cbuf2str(&buf) == "test" { + continue; + } + } else if recvresult == -libc::ECONNRESET { + // peer closed the connection + println!("Connection reset by peer on socket {}", poll.fd); + assert_eq!(cage.close_syscall(poll.fd as i32), 0); + clearfds.push(poll.fd); + } + } + if poll.revents & POLLOUT != 0 { + // Data is sent out this socket, it's no longer ready for writing + // clear the POLLOUT from events + assert_eq!(cage.send_syscall(poll.fd as i32, str2cbuf("test"), 4, 0), 4); + poll.events &= !POLLOUT; + } + } + } + // clear fds + polled.retain(|x| { + for fd in &clearfds { + if *fd == x.fd { + return false; + } + } + return true; + }); + // add new fds + polled.extend(addfds); + } + assert_eq!(cage.close_syscall(serversockfd), 0); + assert_eq!(cage.close_syscall(serversockfd_unix), 0); + + // let threads exit + barrier_exit.wait(); + + threadclient1.join().unwrap(); + threadclient2.join().unwrap(); + threadclient_unix.join().unwrap(); + thread_pipe.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_recvfrom() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let serversockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + + //making sure that the assigned fd's are valid + assert!(serversockfd > 0); + assert!(clientsockfd > 0); + let port: u16 = generate_random_port(); + //binding to a socket + let sockaddr = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 + assert_eq!(cage.bind_syscall(serversockfd, &socket), 0); + assert_eq!(cage.listen_syscall(serversockfd, 1), 0); //we are only allowing for one client at a time + + //forking the cage to get another cage with the same information + assert_eq!(cage.fork_syscall(2), 0); + + //creating a thread for the server so that the information can be sent between + // the two threads + let thread = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + interface::sleep(interface::RustDuration::from_millis(100)); + let port: u16 = generate_random_port(); + + let mut socket2 = interface::GenSockaddr::V4(interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }); //127.0.0.1 + let sockfd = cage2.accept_syscall(serversockfd, &mut socket2); //really can only make sure that the fd is valid + assert!(sockfd > 0); + + //process the first test... + //Writing 100, then peek 100, then read 100 + let mut buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 100, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 100 + ); //peeking at the input message + buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 100, 0, &mut Some(&mut socket2)), + 100 + ); //reading the input message + buf = sizecbuf(100); + + interface::sleep(interface::RustDuration::from_millis(200)); + + //process the second test... + //Writing 100, read 20, peek 20, read 80 + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 20, 0, &mut Some(&mut socket2)), + 20 + ); + buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 20, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 20 + ); + buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 80, 0, &mut Some(&mut socket2)), + 80 + ); + buf = sizecbuf(100); + + interface::sleep(interface::RustDuration::from_millis(200)); + + //process the third test... + //Writing 100, peek several times, read 100 + for _ in 0..4 { + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 10, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 10 + ); + buf = sizecbuf(100); + } + for _ in 0..4 { + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 20, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 20 + ); + buf = sizecbuf(100); + } + for _ in 0..4 { + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 30, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 30 + ); + buf = sizecbuf(100); + } + for _ in 0..4 { + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 40, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 40 + ); + buf = sizecbuf(100); + } + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 100, 0, &mut Some(&mut socket2)), + 100 + ); + buf = sizecbuf(100); + + interface::sleep(interface::RustDuration::from_millis(200)); + + //process the fourth test... + //Writing 50, peek 50 + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 50, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 50 + ); + + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!(cage2.close_syscall(sockfd), 0); + assert_eq!(cage2.close_syscall(serversockfd), 0); + assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); + + //connect to the server + assert_eq!(cage.connect_syscall(clientsockfd, &socket), 0); + + //send the data with delays so that the server can process the information + // cleanly + assert_eq!( + cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(100)), 100, 0), + 100 + ); + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!( + cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(100)), 100, 0), + 100 + ); + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!( + cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(100)), 100, 0), + 100 + ); + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!( + cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(50)), 50, 0), + 50 + ); + interface::sleep(interface::RustDuration::from_millis(100)); + + thread.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_select_badinput() { + // this test is used for testing select with error cases + + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + + let sets = &mut interface::FdSet::new(); + + // test for invalid file descriptor error + // 5 is an invalid file descriptor + sets.set(5 as i32); + assert_eq!( + cage.select_syscall(6, Some(sets), None, None, None,), + -(Errno::EBADF as i32) + ); + + // test for invalid file descriptor range + // negative number + assert_eq!( + cage.select_syscall(-1, None, None, None, None,), + -(Errno::EINVAL as i32) + ); + + // + assert_eq!( + cage.select_syscall(FD_SET_MAX_FD + 1, None, None, None, None,), + -(Errno::EINVAL as i32) + ); + + // test for signal while in select + // TO-DO: sending signals using kill_syscall is + // currently not supported in raw safeposix environment + + // let thread = interface::helper_thread(move || { + // let cage = interface::cagetable_getref(1); + // cage.kill_syscall(1, SIGUSR1); + // }); + + // assert_eq!(cage.select_syscall( + // 0, + // None, + // None, + // None, + // None, + // ), -(Errno::EINTR as i32)); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_select_timeout() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + + // subtest 1: select when timeout could expire + // create a TCP AF_UNIX socket + let serversockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + assert!(serversockfd > 0); + assert!(clientsockfd > 0); + + let port: u16 = generate_random_port(); + let sockaddr = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 from bytes above + + // server bind and listen + assert_eq!(cage.bind_syscall(serversockfd, &socket), 0); + assert_eq!(cage.listen_syscall(serversockfd, 4), 0); + + assert_eq!(cage.fork_syscall(2), 0); + assert_eq!(cage.close_syscall(clientsockfd), 0); + + // this barrier is used for preventing + // an unfixed bug (`close` could block when other thread/cage is `accept`) from + // deadlocking the test + let barrier = Arc::new(Barrier::new(2)); + let barrier_2 = barrier.clone(); + + //client connects to the server to send and recv data... + let threadclient = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + assert_eq!(cage2.close_syscall(serversockfd), 0); + + barrier_2.wait(); + + // connect to the server + assert_eq!(cage2.connect_syscall(clientsockfd, &socket), 0); + + // wait for 100ms + interface::sleep(interface::RustDuration::from_millis(100)); + + // send some message to client + assert_eq!(cage2.send_syscall(clientsockfd, str2cbuf("test"), 4, 0), 4); + + assert_eq!(cage2.close_syscall(clientsockfd), 0); + cage2.exit_syscall(EXIT_SUCCESS); + }); + + // make sure client thread closed the duplicated socket before server start to + // accept + barrier.wait(); + + // wait for client to connect + let mut sockgarbage = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + let sockfd = cage.accept_syscall(serversockfd as i32, &mut sockgarbage); + + // add to client socket to fdset + let master_sets = &mut interface::FdSet::new(); + master_sets.set(sockfd); + + // this counter is used for recording how many times do select returns due to + // timeout + let mut counter = 0; + + loop { + let sets = &mut interface::FdSet::new(); + sets.copy_from(master_sets); + let select_result = cage.select_syscall( + sockfd + 1, + Some(sets), + None, + None, + Some(interface::RustDuration::new(0, 10000000)), // 10ms + ); + assert!(select_result >= 0); + // select timeout after 10ms, but client will send messages after 100ms + // so there should be some timeout return + if select_result == 0 { + counter += 1; + } else if sets.is_set(sockfd) { + // just received the message, check the message and break + let mut buf = sizecbuf(4); + assert_eq!(cage.recv_syscall(sockfd, buf.as_mut_ptr(), 4, 0), 4); + assert_eq!(cbuf2str(&buf), "test"); + break; + } else { + unreachable!(); + } + } + // check if select timeout correctly + assert!(counter > 0); + + threadclient.join().unwrap(); + + // subtest 2: select when all arguments were None except for timeout + // since no set is passed into `select`, `select` here should behave like + // `sleep` + let start_time = interface::starttimer(); + let timeout = interface::RustDuration::new(0, 10000000); // 10ms + let select_result = cage.select_syscall(sockfd + 1, None, None, None, Some(timeout)); + assert!(select_result == 0); + // should wait for at least 10ms + assert!(interface::readtimer(start_time) >= timeout); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_select_pipe_write_blocking() { + // this test is used for testing select on pipe writefds + // PIPE_CAPACITY: the maximum size of a pipe buffer + let byte_chunk: usize = PIPE_CAPACITY; + + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + + // create a pipe + let mut pipefds = PipeArray { + readfd: -1, + writefd: -1, + }; + + // we use nonblocking mode since we can check if pipe is ready to read/write + // easily by checking if the read/write is returning EAGAIN + assert_eq!(cage.pipe2_syscall(&mut pipefds, O_NONBLOCK), 0); + assert_eq!(cage.fork_syscall(2), 0); + + // this barrier is for better control about when receiver should consume the + // data + let barrier = Arc::new(Barrier::new(2)); + let barrier_clone = barrier.clone(); + + let receiver = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + + // receiver end: close writefd, dup readfd + assert_eq!(cage2.close_syscall(pipefds.writefd), 0); + assert_eq!(cage2.dup2_syscall(pipefds.readfd, 0), 0); + assert_eq!(cage2.close_syscall(pipefds.readfd), 0); + + // used for holding read_syscall return + let mut bytes_read: i32 = 1; + + // receiver buffer + let mut buf: Vec = Vec::with_capacity(byte_chunk); + let bufptr = buf.as_mut_ptr(); + + // make a barrier before receiver started to consume data + barrier_clone.wait(); + + // before actually started to consume data, sleep for 10ms + // to test if select is really going to wait + interface::sleep(interface::RustDuration::from_millis(10)); + + while bytes_read != 0 { + // consume the data until peer closed the pipe + bytes_read = cage2.read_syscall(0, bufptr, byte_chunk); + if bytes_read == -(Errno::EAGAIN as i32) { + continue; + } + assert!(bytes_read <= byte_chunk as i32); + } + + assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); + + // sender end: close readfd, dup writefd + assert_eq!(cage.close_syscall(pipefds.readfd), 0); + assert_eq!(cage.dup2_syscall(pipefds.writefd, 1), 1); + assert_eq!(cage.close_syscall(pipefds.writefd), 0); + + // first write will fill up the entire pipe + let mut buf: Vec = vec!['A' as u8; byte_chunk]; + let bufptr = buf.as_mut_ptr(); + assert_eq!( + cage.write_syscall(1, bufptr, byte_chunk) as usize, + byte_chunk + ); + + // since pipe is already filled up, following write should fail + assert_eq!( + cage.write_syscall(1, bufptr, byte_chunk), + -(Errno::EAGAIN as i32) + ); + + let outputs = &mut interface::FdSet::new(); + outputs.set(1); + + // release the barrier and let receiver consume the data + barrier.wait(); + + let select_result = cage.select_syscall(2, None, Some(outputs), None, None); + assert!(select_result == 1); // should have exactly one file descriptor ready + + // all the data are just consumed by the receiver, now the write should be + // successful + assert_ne!( + cage.write_syscall(1, bufptr, byte_chunk), + -(Errno::EAGAIN as i32) + ); + + // close the pipe + assert_eq!(cage.close_syscall(1), 0); + + receiver.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + #[ignore] + pub fn ut_lind_net_select_socket_write_blocking() { + // this test is used for testing select on AF_UNIX socket pipe writefds + // currently would fail since select_syscall does not handle socket pipe + // writefds correctly + let byte_chunk: usize = UDSOCK_CAPACITY; + + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + + // create a AF_UNIX socket + // we use nonblocking mode since we can check if pipe is ready to read/write + // easily by checking if the read/write is returning EAGAIN + let serversockfd_unix = cage.socket_syscall(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); + let clientsockfd_unix = cage.socket_syscall(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); + + //binding to a socket + let serversockaddr_unix = + interface::new_sockaddr_unix(AF_UNIX as u16, "server_select".as_bytes()); + let serversocket_unix = interface::GenSockaddr::Unix(serversockaddr_unix); + + let clientsockaddr_unix = + interface::new_sockaddr_unix(AF_UNIX as u16, "client_select".as_bytes()); + let clientsocket_unix = interface::GenSockaddr::Unix(clientsockaddr_unix); + + assert_eq!(cage.bind_syscall(serversockfd_unix, &serversocket_unix), 0); + assert_eq!(cage.bind_syscall(clientsockfd_unix, &clientsocket_unix), 0); + assert_eq!(cage.listen_syscall(serversockfd_unix, 1), 0); + + assert_eq!(cage.fork_syscall(2), 0); + + // this barrier is for better control about when receiver should consume the + // data + let barrier = Arc::new(Barrier::new(2)); + let barrier_clone = barrier.clone(); + + let threadclient_unix = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + assert_eq!(cage2.close_syscall(serversockfd_unix), 0); + + // connect to the server + assert_eq!( + cage2.connect_syscall(clientsockfd_unix, &serversocket_unix), + 0 + ); + + // receiver buffer + let mut buf: Vec = Vec::with_capacity(byte_chunk); + let bufptr = buf.as_mut_ptr(); + let mut result: i32 = 1; + + // make a barrier before receiver started to consume data + barrier_clone.wait(); + + // before actually started to consume data, sleep for 10ms + // to test if select is really going to wait + interface::sleep(interface::RustDuration::from_millis(10)); + + while result != 0 { + // consume the data until peer closed the socket + result = cage2.recv_syscall(clientsockfd_unix, bufptr, byte_chunk, 0); + if result == -(Errno::EAGAIN as i32) { + continue; + } + assert!(result <= byte_chunk as i32); + } + + assert_eq!(cage2.close_syscall(clientsockfd_unix), 0); + cage2.exit_syscall(EXIT_SUCCESS); + }); + + // this sleep is to prevent an unfixed bug (`close` would block when other + // thread/cage is `accept`) from deadlocking the test + interface::sleep(interface::RustDuration::from_millis(10)); + + let mut sockgarbage = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + let sockfd = cage.accept_syscall(serversockfd_unix as i32, &mut sockgarbage); + + // sender buffer + let mut buf: Vec = vec!['A' as u8; byte_chunk]; + let bufptr = buf.as_mut_ptr(); + + // first send will fill up the entire socket pipe + assert_eq!( + cage.send_syscall(sockfd, bufptr, byte_chunk, 0) as usize, + byte_chunk + ); + // since socket pipe is already filled up, following send should fail + assert_eq!( + cage.send_syscall(sockfd, bufptr, byte_chunk, 0), + -(Errno::EAGAIN as i32) + ); + + let outputs = &mut interface::FdSet::new(); + outputs.set(sockfd); + + // release the barrier and let receiver consume the data + barrier.wait(); + + let select_result = cage.select_syscall(sockfd + 1, None, Some(outputs), None, None); + assert!(select_result == 1); // should have exactly one file descriptor ready + + // all the data are just consumed by the receiver, now the write should be + // successful + assert_ne!( + cage.send_syscall(sockfd, bufptr, byte_chunk, 0), + -(Errno::EAGAIN as i32) + ); + + assert_eq!(cage.close_syscall(sockfd), 0); + + threadclient_unix.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + #[ignore] + pub fn ut_lind_net_select() { + // test for select monitoring on multiple different file descriptors: + // 1. regular file + // 2. AF_INET server socket waiting for two clients + // 3. AF_INET server socket's connection file descriptor with clients + // 4. AF_UNIX server socket's connection file descriptor with a client + // 5. pipe + + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + + // creating regular file's file descriptor + let filefd = cage.open_syscall("/netselecttest.txt", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); + assert!(filefd > 0); + + // creating socket file descriptors + let serversockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd1 = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd2 = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let serversockfd_unix = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + let clientsockfd_unix = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + + // record the maximum fd number to pass as nfds of select + let mut max_fds = 2; + + assert!(serversockfd > 0); + assert!(clientsockfd1 > 0); + assert!(clientsockfd2 > 0); + assert!(serversockfd_unix > 0); + assert!(clientsockfd_unix > 0); + + // creating a pipe + let mut pipefds = PipeArray { + readfd: -1, + writefd: -1, + }; + assert_eq!(cage.pipe_syscall(&mut pipefds), 0); + + // create a INET address + let port: u16 = generate_random_port(); + + let sockaddr = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 from bytes above + + //binding to a socket + let serversockaddr_unix = + interface::new_sockaddr_unix(AF_UNIX as u16, "server_select".as_bytes()); + let serversocket_unix = interface::GenSockaddr::Unix(serversockaddr_unix); + + let clientsockaddr_unix = + interface::new_sockaddr_unix(AF_UNIX as u16, "client_select".as_bytes()); + let clientsocket_unix = interface::GenSockaddr::Unix(clientsockaddr_unix); + + assert_eq!(cage.bind_syscall(serversockfd_unix, &serversocket_unix), 0); + assert_eq!(cage.bind_syscall(clientsockfd_unix, &clientsocket_unix), 0); + assert_eq!(cage.listen_syscall(serversockfd_unix, 1), 0); + + assert_eq!(cage.bind_syscall(serversockfd, &socket), 0); + assert_eq!(cage.listen_syscall(serversockfd, 4), 0); + + // allocate spaces for fd_set bitmaps + // `master_set`: Consits of all read file descriptors. + // `working_set`: Consits of a copy of `master_set`. Modified by `select()` to + // contain only ready descriptors. `master_outputs_set`: Consits of all + // write file descriptors. `outputs`: Consits of a copy of + // `master_outputs_set`. Modified by `select()` to contain only ready + // descriptors. + let master_set = &mut interface::FdSet::new(); + let working_set = &mut interface::FdSet::new(); + let master_outputs_set = &mut interface::FdSet::new(); + let outputs = &mut interface::FdSet::new(); + + // readfds + master_set.set(serversockfd); // AF_INET socket server fd + master_set.set(serversockfd_unix); // AF_UNIX socket server fd + master_set.set(filefd); // regular file fd + master_set.set(pipefds.readfd); // pipe fd + + // writefds + master_outputs_set.set(filefd); // regular file fd + + // update max_fds + max_fds = std::cmp::max(max_fds, serversockfd); + max_fds = std::cmp::max(max_fds, serversockfd_unix); + max_fds = std::cmp::max(max_fds, filefd); + max_fds = std::cmp::max(max_fds, pipefds.readfd); + + // check if FdSet works correctly + assert_eq!(master_set.is_set(serversockfd), true); + assert_eq!(master_set.is_set(serversockfd_unix), true); + assert_eq!(master_set.is_set(filefd), true); + assert_eq!(master_set.is_set(pipefds.readfd), true); + assert_eq!(master_outputs_set.is_set(filefd), true); + + assert_eq!(cage.fork_syscall(2), 0); // used for AF_INET thread client 1 + assert_eq!(cage.fork_syscall(3), 0); // used for AF_INET thread client 2 + assert_eq!(cage.fork_syscall(4), 0); // used for AF_UNIX thread client + + assert_eq!(cage.fork_syscall(5), 0); // used for pipe thread + + assert_eq!(cage.close_syscall(clientsockfd1), 0); + assert_eq!(cage.close_syscall(clientsockfd2), 0); + assert_eq!(cage.close_syscall(clientsockfd_unix), 0); + + // this barrier have to ensure that the clients finish the connect before we do + // the select due to an unfixed bug (`close` could block when other + // thread/cage is `accept`) + let barrier = Arc::new(Barrier::new(3)); + let barrier_clone1 = barrier.clone(); + let barrier_clone2 = barrier.clone(); + + // this barrier is used for control the flow the pipe + let barrier_pipe = Arc::new(Barrier::new(2)); + let barrier_pipe_clone = barrier_pipe.clone(); + + // due to an unfixed bug in ref counter of AF_UNIX socket pipe + // have to make sure all the threads exits only after the AF_UNIX test finished + let barrier_exit = Arc::new(Barrier::new(4)); + let barrier_exit_clone1 = barrier_exit.clone(); + let barrier_exit_clone2 = barrier_exit.clone(); + let barrier_exit_clone3 = barrier_exit.clone(); + + //client 1 connects to the server to send and recv data... + let threadclient1 = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + assert_eq!(cage2.close_syscall(serversockfd), 0); + assert_eq!(cage2.close_syscall(clientsockfd2), 0); + + // connect to server + assert_eq!(cage2.connect_syscall(clientsockfd1, &socket), 0); + barrier_clone1.wait(); + + // send message to server + assert_eq!(cage2.send_syscall(clientsockfd1, str2cbuf("test"), 4, 0), 4); + + interface::sleep(interface::RustDuration::from_millis(1)); + + // receive message from server + let mut buf = sizecbuf(4); + assert_eq!(cage2.recv_syscall(clientsockfd1, buf.as_mut_ptr(), 4, 0), 4); + assert_eq!(cbuf2str(&buf), "test"); + + assert_eq!(cage2.close_syscall(clientsockfd1), 0); + barrier_exit_clone1.wait(); + cage2.exit_syscall(EXIT_SUCCESS); + }); + + //client 2 connects to the server to send and recv data... + let threadclient2 = interface::helper_thread(move || { + let cage3 = interface::cagetable_getref(3); + assert_eq!(cage3.close_syscall(serversockfd), 0); + assert_eq!(cage3.close_syscall(clientsockfd1), 0); + + // connect to server + assert_eq!(cage3.connect_syscall(clientsockfd2, &socket), 0); + barrier_clone2.wait(); + + // send message to server + assert_eq!(cage3.send_syscall(clientsockfd2, str2cbuf("test"), 4, 0), 4); + + interface::sleep(interface::RustDuration::from_millis(1)); + + // receive message from server + let mut buf = sizecbuf(4); + let mut result: i32; + loop { + result = cage3.recv_syscall(clientsockfd2, buf.as_mut_ptr(), 4, 0); + if result != -libc::EINTR { + break; // if the error was EINTR, retry the syscall + } + } + assert_eq!(result, 4); + assert_eq!(cbuf2str(&buf), "test"); + + assert_eq!(cage3.close_syscall(clientsockfd2), 0); + barrier_exit_clone2.wait(); + cage3.exit_syscall(EXIT_SUCCESS); + }); + + let threadclient_unix = interface::helper_thread(move || { + let cage4 = interface::cagetable_getref(4); + assert_eq!(cage4.close_syscall(serversockfd_unix), 0); + assert_eq!(cage4.close_syscall(serversockfd), 0); + + // connect to server + assert_eq!( + cage4.connect_syscall(clientsockfd_unix, &serversocket_unix), + 0 + ); + + // send message to server + assert_eq!( + cage4.send_syscall(clientsockfd_unix, str2cbuf("test"), 4, 0), + 4 + ); + + interface::sleep(interface::RustDuration::from_millis(1)); + + // recieve message from server + let mut buf = sizecbuf(4); + let mut result: i32; + loop { + result = cage4.recv_syscall(clientsockfd_unix, buf.as_mut_ptr(), 4, 0); + if result != -libc::EINTR { + break; // if the error was EINTR, retry the syscall + } + } + assert_eq!(result, 4); + assert_eq!(cbuf2str(&buf), "test"); + + assert_eq!(cage4.close_syscall(clientsockfd_unix), 0); + cage4.exit_syscall(EXIT_SUCCESS); + }); + + let thread_pipe = interface::helper_thread(move || { + let cage5 = interface::cagetable_getref(5); + + interface::sleep(interface::RustDuration::from_millis(1)); + // send message to pipe + assert_eq!(cage5.write_syscall(pipefds.writefd, str2cbuf("test"), 4), 4); + + let mut buf = sizecbuf(5); + // wait until peer read the message + barrier_pipe_clone.wait(); + + // read the message sent by peer + assert_eq!(cage5.read_syscall(pipefds.readfd, buf.as_mut_ptr(), 5), 5); + assert_eq!(cbuf2str(&buf), "test2"); + + barrier_exit_clone3.wait(); + cage5.exit_syscall(EXIT_SUCCESS); + }); + + barrier.wait(); + // acting as the server and processing the request + // Server loop to handle connections and I/O + // Check for any activity in any of the Input sockets... + for _counter in 0..600 { + working_set.copy_from(master_set); + outputs.copy_from(master_outputs_set); + let select_result = + cage.select_syscall(max_fds + 1, Some(working_set), Some(outputs), None, None); + assert!(select_result >= 0); // check for error + + // check for readfds + for sock in 0..=max_fds { + if !working_set.is_set(sock) { + continue; + } + //If the socket returned was listerner socket, then there's a new conn., so we + // accept it, and put the client socket in the list of Inputs. + if sock == serversockfd { + let mut sockgarbage = + interface::GenSockaddr::V4(interface::SockaddrV4::default()); + let sockfd = cage.accept_syscall(sock as i32, &mut sockgarbage); + assert!(sockfd > 0); + // new connection is estalished, add it to readfds and writefds + master_set.set(sockfd); + master_outputs_set.set(sockfd); + // update max_fds + max_fds = std::cmp::max(max_fds, sockfd); + } else if sock == filefd { + //Write to a file... + assert_eq!(cage.write_syscall(sock as i32, str2cbuf("test"), 4), 4); + assert_eq!(cage.lseek_syscall(sock as i32, 0, SEEK_SET), 0); + master_set.clear(sock); + // regular file select test done + } else if sock == serversockfd_unix { + // unix socket + let mut sockgarbage = interface::GenSockaddr::Unix( + interface::new_sockaddr_unix(AF_UNIX as u16, "".as_bytes()), + ); + let sockfd = cage.accept_syscall(sock as i32, &mut sockgarbage); + assert!(sockfd > 0); + // new connection is estalished, add it to readfds and writefds + master_set.set(sockfd); + master_outputs_set.set(sockfd); + // update max_fds + max_fds = std::cmp::max(max_fds, sockfd); + } else if sock == pipefds.readfd { + // pipe + let mut buf = sizecbuf(4); + // read the message from peer + assert_eq!(cage.read_syscall(sock, buf.as_mut_ptr(), 4), 4); + assert_eq!(cbuf2str(&buf), "test"); + + // write the message from peer + assert_eq!( + cage.write_syscall(pipefds.writefd, str2cbuf("test2"), 5) as usize, + 5 + ); + barrier_pipe.wait(); + + // pipe select test done + master_set.clear(sock); + } else { + //If the socket is in established conn., then we recv the data. If there's no + // data, then close the client socket. + let mut buf = sizecbuf(4); + let mut recvresult: i32; + loop { + // receive message from peer + recvresult = cage.recv_syscall(sock as i32, buf.as_mut_ptr(), 4, 0); + if recvresult != -libc::EINTR { + break; // if the error was EINTR, retry the syscall + } + } + if recvresult == 4 { + if cbuf2str(&buf) == "test" { + continue; + } + } else if recvresult == -libc::ECONNRESET { + // peer closed the connection + println!("Connection reset by peer on socket {}", sock); + assert_eq!(cage.close_syscall(sock as i32), 0); + master_set.clear(sock); + master_outputs_set.clear(sock); + } + } + } + + // check for writefds + for sock in 0..FD_SET_MAX_FD { + if !outputs.is_set(sock) { + continue; + } + if sock == filefd { + // regular file + let mut buf = sizecbuf(4); + assert_eq!(cage.read_syscall(sock as i32, buf.as_mut_ptr(), 4), 4); + assert_eq!(cbuf2str(&buf), "test"); + master_outputs_set.clear(sock); + } else { + //Data is sent out this socket, it's no longer ready for writing remove this + // socket from writefd's. + assert_eq!(cage.send_syscall(sock as i32, str2cbuf("test"), 4, 0), 4); + master_outputs_set.clear(sock); + } + } + } + assert_eq!(cage.close_syscall(serversockfd), 0); + assert_eq!(cage.close_syscall(serversockfd_unix), 0); + + // let threads exit + barrier_exit.wait(); + + threadclient1.join().unwrap(); + threadclient2.join().unwrap(); + threadclient_unix.join().unwrap(); + thread_pipe.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_shutdown_bad_input() { + // this test is used for testing shutdown with error input + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // unexist file descriptor error + assert_eq!( + cage.netshutdown_syscall(10, SHUT_RD), + -(Errno::EBADF as i32) + ); + + // out of range file descriptor error + assert_eq!( + cage.netshutdown_syscall(-1, SHUT_RD), + -(Errno::EBADF as i32) + ); + + let filefd = cage.open_syscall("/netshutdowntest.txt", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); + assert!(filefd > 0); + // wrong fd type error + assert_eq!( + cage.netshutdown_syscall(filefd, SHUT_RD), + -(Errno::ENOTSOCK as i32) + ); + + let sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let port: u16 = generate_random_port(); + //binding to a socket + let sockaddr = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 + + // socket not connect error + // BUG: failed the test + // assert_eq!( + // cage.netshutdown_syscall(sockfd, SHUT_RD), + // -(Errno::ENOTCONN as i32) + // ); + + assert_eq!(cage.bind_syscall(sockfd, &socket), 0); + assert_eq!(cage.listen_syscall(sockfd, 10), 0); + + // wrong how argument error + assert_eq!( + cage.netshutdown_syscall(sockfd, 10), + -(Errno::EINVAL as i32) + ); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_shutdown_unix() { + // this test is used for testing shutdown with UNIX socket + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let serverfd = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + + let serveraddr = interface::new_sockaddr_unix(AF_UNIX as u16, "server_shutdown".as_bytes()); + let serversocket = interface::GenSockaddr::Unix(serveraddr); + + assert_eq!(cage.bind_syscall(serverfd, &serversocket), 0); + assert_eq!(cage.listen_syscall(serverfd, 10), 0); + + assert_eq!(cage.fork_syscall(2), 0); + assert_eq!(cage.fork_syscall(3), 0); + + let barrier = Arc::new(Barrier::new(2)); + let barrier_clone = barrier.clone(); + + let barrier_shut = Arc::new(Barrier::new(2)); + let barrier_shut_clone = barrier_shut.clone(); + + let thread = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + + let fd = cage2.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + assert_eq!(cage2.connect_syscall(fd, &serversocket), 0); + + // first make sure send and recv are working before shutdown + assert_eq!(cage2.send_syscall(fd, str2cbuf("client send"), 11, 0), 11); + let mut buf = sizecbuf(11); + assert_eq!(cage2.read_syscall(fd, buf.as_mut_ptr(), 11), 11); + assert_eq!(cbuf2str(&buf), "server send"); + + barrier_clone.wait(); + + // now let's shutdown RD + assert_eq!(cage2.netshutdown_syscall(fd, SHUT_RD), 0); + + // BUG: read after SHUT_RD should not raise ENOTCONN error + // the desired behavior is to return end-of-file (0) + // assert_eq!(cage2.read_syscall(fd, buf.as_mut_ptr(), 8), 0); + + barrier_shut_clone.wait(); + + // write should succeed + assert_eq!( + cage2.send_syscall(fd, str2cbuf("before SHUT_WR"), 14, 0), + 14 + ); + assert_eq!(cage2.netshutdown_syscall(fd, SHUT_WR), 0); + + assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); + + let mut sockgarbage = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + let fd = cage.accept_syscall(serverfd, &mut sockgarbage); + assert!(fd > 0); + + // first make sure send and recv are working + let mut buf = sizecbuf(11); + assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 11), 11); + assert_eq!(cbuf2str(&buf), "client send"); + assert_eq!(cage.send_syscall(fd, str2cbuf("server send"), 11, 0), 11); + + assert_eq!(cage.send_syscall(fd, str2cbuf("shutdown"), 8, 0), 8); + barrier.wait(); + barrier_shut.wait(); + // BUG: peer already closed RD, now subsequent write should fail with EPIPE + // assert_ne!(cage.send_syscall(fd, str2cbuf("shutdown"), 8, 0), 8); + + // after SHUT_WR, once the peer application has read all outstanding data, it + // will see end-of-file. + let mut buf = sizecbuf(14); + assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 14), 14); + assert_eq!(cbuf2str(&buf), "before SHUT_WR"); + assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 14), 0); + + assert_eq!(cage.close_syscall(fd), 0); + + let barrier2 = Arc::new(Barrier::new(2)); + let barrier2_clone = barrier2.clone(); + + let barrier2_shut = Arc::new(Barrier::new(2)); + let barrier2_shut_clone = barrier2_shut.clone(); + + let thread2 = interface::helper_thread(move || { + let cage3 = interface::cagetable_getref(3); + + let fd = cage3.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + assert_eq!(cage3.connect_syscall(fd, &serversocket), 0); + + // first make sure send and recv are working before shutdown + assert_eq!(cage3.send_syscall(fd, str2cbuf("client send"), 11, 0), 11); + let mut buf = sizecbuf(11); + assert_eq!(cage3.read_syscall(fd, buf.as_mut_ptr(), 11), 11); + assert_eq!(cbuf2str(&buf), "server send"); + + barrier2_clone.wait(); + + // now let's shutdown RD and WR + assert_eq!(cage3.netshutdown_syscall(fd, SHUT_RDWR), 0); + + barrier2_shut_clone.wait(); + + // now neither send nor recv should succeed + assert_ne!(cage3.send_syscall(fd, str2cbuf("client send"), 11, 0), 11); + // BUG: should not raise ENOTCONN error + // the desired behavior is to return end-of-file (0) + // let mut buf = sizecbuf(11); + // assert_eq!(cage3.read_syscall(fd, buf.as_mut_ptr(), 11), 0); + + assert_eq!(cage3.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); + + let fd = cage.accept_syscall(serverfd, &mut sockgarbage); + assert!(fd > 0); + + // first make sure send and recv are working + let mut buf = sizecbuf(11); + assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 11), 11); + assert_eq!(cbuf2str(&buf), "client send"); + assert_eq!(cage.send_syscall(fd, str2cbuf("server send"), 11, 0), 11); + + barrier2.wait(); + barrier2_shut.wait(); + + // peer just shutdown RD and WR + // now neither send nor recv should succeed + // BUG: failed the test below + // assert_ne!(cage.send_syscall(fd, str2cbuf("server send"), 11, 0), 11); + + // BUG: peer already shutdown, read should not block + // let mut buf = sizecbuf(11); + // assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 11), 0); + + thread.join().unwrap(); + thread2.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_shutdown_inet() { + // this test is used for testing shutdown with INET socket + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let serverfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + + let port: u16 = generate_random_port(); + //binding to a socket + let sockaddr = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 + + assert_eq!(cage.bind_syscall(serverfd, &socket), 0); + assert_eq!(cage.listen_syscall(serverfd, 10), 0); + + assert_eq!(cage.fork_syscall(2), 0); + assert_eq!(cage.fork_syscall(3), 0); + + let barrier = Arc::new(Barrier::new(2)); + let barrier_clone = barrier.clone(); + + let thread = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + + let fd = cage2.socket_syscall(AF_INET, SOCK_STREAM, 0); + assert_eq!(cage2.connect_syscall(fd, &socket), 0); + + // first make sure send and recv are working before shutdown + assert_eq!(cage2.send_syscall(fd, str2cbuf("client send"), 11, 0), 11); + let mut buf = sizecbuf(11); + assert_eq!(cage2.read_syscall(fd, buf.as_mut_ptr(), 11), 11); + assert_eq!(cbuf2str(&buf), "server send"); + + assert_eq!( + cage2.send_syscall(fd, str2cbuf("before SHUT_WR"), 14, 0), + 14 + ); + assert_eq!(cage2.netshutdown_syscall(fd, SHUT_WR), 0); + + barrier_clone.wait(); + + assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); + + let mut sockgarbage = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + let fd = cage.accept_syscall(serverfd, &mut sockgarbage); + assert!(fd > 0); + + // first make sure send and recv are working + let mut buf = sizecbuf(11); + assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 11), 11); + assert_eq!(cbuf2str(&buf), "client send"); + assert_eq!(cage.send_syscall(fd, str2cbuf("server send"), 11, 0), 11); + + // peer SHUT_WR + barrier.wait(); + let mut buf = sizecbuf(14); + // data already sent should still be readable + assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 14), 14); + assert_eq!(cbuf2str(&buf), "before SHUT_WR"); + // subsequent read should return end-of-file + assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 14), 0); + + assert_eq!(cage.close_syscall(fd), 0); + + let barrier2 = Arc::new(Barrier::new(2)); + let barrier2_clone = barrier2.clone(); + + let thread2 = interface::helper_thread(move || { + let cage3 = interface::cagetable_getref(3); + + let fd = cage3.socket_syscall(AF_INET, SOCK_STREAM, 0); + assert_eq!(cage3.connect_syscall(fd, &socket), 0); + + // first make sure send and recv are working before shutdown + assert_eq!(cage3.send_syscall(fd, str2cbuf("client send"), 11, 0), 11); + let mut buf = sizecbuf(11); + assert_eq!(cage3.read_syscall(fd, buf.as_mut_ptr(), 11), 11); + assert_eq!(cbuf2str(&buf), "server send"); + + assert_eq!( + cage3.send_syscall(fd, str2cbuf("before SHUT_RDWR"), 16, 0), + 16 + ); + assert_eq!(cage3.netshutdown_syscall(fd, SHUT_RDWR), 0); + + barrier2_clone.wait(); + + assert_eq!(cage3.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); + + let fd = cage.accept_syscall(serverfd, &mut sockgarbage); + assert!(fd > 0); + + // first make sure send and recv are working + let mut buf = sizecbuf(11); + assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 11), 11); + assert_eq!(cbuf2str(&buf), "client send"); + assert_eq!(cage.send_syscall(fd, str2cbuf("server send"), 11, 0), 11); + + // peer SHUT_RDWR + barrier2.wait(); + let mut buf = sizecbuf(16); + // data already sent should still be readable + assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 16), 16); + assert_eq!(cbuf2str(&buf), "before SHUT_RDWR"); + // subsequent read should return end-of-file + assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 16), 0); + + thread.join().unwrap(); + thread2.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_shutdown() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let serversockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + + assert!(serversockfd > 0); + assert!(clientsockfd > 0); + let port: u16 = generate_random_port(); + + //binding to a socket + let sockaddr = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 + assert_eq!(cage.bind_syscall(serversockfd, &socket), 0); + assert_eq!(cage.listen_syscall(serversockfd, 10), 0); + + //forking the cage to get another cage with the same information + assert_eq!(cage.fork_syscall(2), 0); + + let thread = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + + interface::sleep(interface::RustDuration::from_millis(100)); + let port: u16 = generate_random_port(); + + let mut socket2 = interface::GenSockaddr::V4(interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }); //127.0.0.1 + let fd = cage2.accept_syscall(serversockfd, &mut socket2); + assert!(fd > 0); + + assert_eq!(cage2.netshutdown_syscall(fd, SHUT_RD), 0); + assert_eq!(cage2.send_syscall(fd, str2cbuf("random string"), 13, 0), 13); + assert_eq!(cage2.netshutdown_syscall(fd, SHUT_RDWR), 0); + + assert_eq!(cage2.close_syscall(serversockfd), 0); + assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); + + assert_eq!(cage.connect_syscall(clientsockfd, &socket), 0); + + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!(cage.getsockname_syscall(clientsockfd, &mut retsocket), 0); + assert_ne!(retsocket, socket); + + thread.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_socket() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // Following checks are inplace to ensure that the socket types are correctly + // defined and that the assumptions about the values of the socket types + // are correct across platforms. Check that SOCK_STREAM only uses the + // lowest 3 bits + assert_eq!(SOCK_STREAM & !0x7, 0); + + // Check that SOCK_DGRAM only uses the lowest 3 bits + assert_eq!(SOCK_DGRAM & !0x7, 0); + + // Check that SOCK_NONBLOCK does not use the lowest 3 bits + assert_eq!(SOCK_NONBLOCK & 0x7, 0); + + // Check that SOCK_CLOEXEC does not use the lowest 3 bits + assert_eq!(SOCK_CLOEXEC & 0x7, 0); + + //let's check an illegal operation... + // RDM is not a valid socket type for SOCK_DGRAM (UDP) as its not implemented + // yet. + let sockfd5 = cage.socket_syscall(AF_INET, SOCK_RDM, 0); + assert!( + sockfd5 < 0, + "Expected an error, got a valid file descriptor" + ); + + //let's check an illegal operation... + //invalid protocol for SOCK_STREAM Type. + let sockfd6 = cage.socket_syscall(AF_INET, SOCK_STREAM, 999); + assert!( + sockfd6 < 0, + "Expected an error, got a valid file descriptor" + ); + + //let's check an illegal operation... + //invalid domain for socket + let sockfd7 = cage.socket_syscall(999, SOCK_STREAM, 0); + assert!( + sockfd7 < 0, + "Expected an error, got a valid file descriptor" + ); + + //let's check an illegal operation... + //invalid socket type flags combination + let sockfd8 = cage.socket_syscall(AF_INET, SOCK_STREAM | 0x100000, 0); + assert!( + sockfd8 < 0, + "Expected an error, got a valid file descriptor" + ); + + //let's check an illegal operation... + //invalid socket type protocol combination + let sockfd9 = cage.socket_syscall(AF_INET, SOCK_STREAM, IPPROTO_UDP); + assert!( + sockfd9 < 0, + "Expected an error, got a valid file descriptor" + ); + + //let's check an illegal operation... + //invalid socket type protocol combination + let sockfd10 = cage.socket_syscall(AF_INET, SOCK_DGRAM, IPPROTO_TCP); + assert!( + sockfd10 < 0, + "Expected an error, got a valid file descriptor" + ); + + let mut sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + assert!(sockfd > 0, "Expected a valid file descriptor, got error"); + + let sockfd2 = cage.socket_syscall(AF_INET, SOCK_STREAM, IPPROTO_TCP); + assert!(sockfd2 > 0, "Expected a valid file descriptor, got error"); + + let sockfd3 = cage.socket_syscall(AF_INET, SOCK_DGRAM, 0); + assert!(sockfd3 > 0, "Expected a valid file descriptor, got error"); + + let sockfd4 = cage.socket_syscall(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + assert!(sockfd4 > 0, "Expected a valid file descriptor, got error"); + + //let's check an illegal operation... + // let sockfd7 = cage.socket_syscall(AF_INET, !0b111 | 0b001, 0); + // assert!(sockfd7 < 0, "Expected an error, got a valid file descriptor"); + + let sockfddomain = cage.socket_syscall(AF_UNIX, SOCK_DGRAM, 0); + assert!( + sockfddomain > 0, + "Expected a valid file descriptor, got error" + ); + + sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + assert!(sockfd > 0, "Expected a valid file descriptor, got error"); + + assert_eq!( + cage.close_syscall(sockfd), + 0, + "Expected successful close, got error" + ); + assert_eq!( + cage.exit_syscall(EXIT_SUCCESS), + EXIT_SUCCESS, + "Expected successful exit, got error" + ); + lindrustfinalize(); + } + + #[test] + #[ignore] + pub fn ut_lind_net_sockopt_bad_input_optname() { + // this test is used for testing invalid optname that is + // large enough to overflow to bitwise shift + // would fail currently + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let mut optstore = 0; + + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_TCP, 100, &mut optstore), + -(Errno::ENOPROTOOPT as i32) + ); + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, 100, &mut optstore), + -(Errno::ENOPROTOOPT as i32) + ); + + assert_eq!( + cage.setsockopt_syscall(sockfd, SOL_TCP, 100, 0), + -(Errno::EOPNOTSUPP as i32) + ); + assert_eq!( + cage.setsockopt_syscall(sockfd, SOL_SOCKET, 100, 0), + -(Errno::EOPNOTSUPP as i32) + ); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_getsockopt_bad_input() { + // this test is used for testing getsockopt_syscall with error input + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let filefd = + cage.open_syscall("/netgetsockopttest.txt", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); + let sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + + let mut optstore = -12; + + // unexist file descriptor + assert_eq!( + cage.getsockopt_syscall(10, SOL_SOCKET, SO_REUSEPORT, &mut optstore), + -(Errno::EBADF as i32) + ); + + // out of range file descriptor + assert_eq!( + cage.getsockopt_syscall(-1, SOL_SOCKET, SO_REUSEPORT, &mut optstore), + -(Errno::EBADF as i32) + ); + + // fd that is not socket + assert_eq!( + cage.getsockopt_syscall(filefd, SOL_SOCKET, SO_REUSEPORT, &mut optstore), + -(Errno::ENOTSOCK as i32) + ); + + // invalid level argument + assert_eq!( + cage.getsockopt_syscall(sockfd, 30, SO_REUSEPORT, &mut optstore), + -(Errno::EOPNOTSUPP as i32) + ); + + // unsupported level argument + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_UDP, SO_REUSEPORT, &mut optstore), + -(Errno::ENOPROTOOPT as i32) + ); + + // invalid optname argument + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, 20, &mut optstore), + -(Errno::ENOPROTOOPT as i32) + ); + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_TCP, 20, &mut optstore), + -(Errno::ENOPROTOOPT as i32) + ); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_setsockopt_bad_input() { + // this test is used for testing setsockopt_syscall with error input + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let filefd = + cage.open_syscall("/netsetsockopttest.txt", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); + let sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + + // unexist file descriptor + assert_eq!( + cage.setsockopt_syscall(10, SOL_SOCKET, SO_REUSEPORT, 0), + -(Errno::EBADF as i32) + ); + + // out of range file descriptor + assert_eq!( + cage.setsockopt_syscall(-1, SOL_SOCKET, SO_REUSEPORT, 0), + -(Errno::EBADF as i32) + ); + + // fd that is not socket + assert_eq!( + cage.setsockopt_syscall(filefd, SOL_SOCKET, SO_REUSEPORT, 0), + -(Errno::ENOTSOCK as i32) + ); + + // invalid level argument + assert_eq!( + cage.setsockopt_syscall(sockfd, 30, SO_REUSEPORT, 0), + -(Errno::EOPNOTSUPP as i32) + ); + + // unsupported level argument + assert_eq!( + cage.setsockopt_syscall(sockfd, SOL_UDP, SO_REUSEPORT, 0), + -(Errno::EOPNOTSUPP as i32) + ); + + // invalid optname argument + assert_eq!( + cage.setsockopt_syscall(sockfd, SOL_SOCKET, 20, 0), + -(Errno::EOPNOTSUPP as i32) + ); + assert_eq!( + cage.setsockopt_syscall(sockfd, SOL_TCP, 20, 0), + -(Errno::EOPNOTSUPP as i32) + ); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_socketoptions() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + assert!(sockfd > 0); + let port: u16 = generate_random_port(); + + let sockaddr = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 + assert_eq!(cage.bind_syscall(sockfd, &socket), 0); + assert_eq!(cage.listen_syscall(sockfd, 4), 0); + + // set and get some options: + let mut optstore = -12; + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_REUSEPORT, &mut optstore), + 0 + ); + assert_eq!(optstore, 0); + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_LINGER, &mut optstore), + 0 + ); + assert_eq!(optstore, 0); + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_KEEPALIVE, &mut optstore), + 0 + ); + assert_eq!(optstore, 0); + + // linger... + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_LINGER, &mut optstore), + 0 + ); + assert_eq!(optstore, 0); + assert_eq!(cage.setsockopt_syscall(sockfd, SOL_SOCKET, SO_LINGER, 1), 0); + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_LINGER, &mut optstore), + 0 + ); + assert_eq!(optstore, 1); + + // check the options + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_REUSEPORT, &mut optstore), + 0 + ); + assert_eq!(optstore, 0); + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_LINGER, &mut optstore), + 0 + ); + assert_eq!(optstore, 1); + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_KEEPALIVE, &mut optstore), + 0 + ); + assert_eq!(optstore, 0); + + // reuseport... + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_REUSEPORT, &mut optstore), + 0 + ); + assert_eq!(optstore, 0); + assert_eq!( + cage.setsockopt_syscall(sockfd, SOL_SOCKET, SO_REUSEPORT, 1), + 0 + ); + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_REUSEPORT, &mut optstore), + 0 + ); + assert_eq!(optstore, 1); + + // check the options + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_REUSEPORT, &mut optstore), + 0 + ); + assert_eq!(optstore, 1); + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_LINGER, &mut optstore), + 0 + ); + assert_eq!(optstore, 1); + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_KEEPALIVE, &mut optstore), + 0 + ); + assert_eq!(optstore, 0); + + // keep alive... + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_KEEPALIVE, &mut optstore), + 0 + ); + assert_eq!(optstore, 0); + assert_eq!( + cage.setsockopt_syscall(sockfd, SOL_SOCKET, SO_KEEPALIVE, 1), + 0 + ); + + // check the options + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_REUSEPORT, &mut optstore), + 0 + ); + assert_eq!(optstore, 1); + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_LINGER, &mut optstore), + 0 + ); + assert_eq!(optstore, 1); + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_KEEPALIVE, &mut optstore), + 0 + ); + assert_eq!(optstore, 1); + + assert_eq!( + cage.setsockopt_syscall(sockfd, SOL_SOCKET, SO_SNDBUF, 1000), + 0 + ); + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_SNDBUF, &mut optstore), + 0 + ); + assert_eq!(optstore, 1000); + + assert_eq!( + cage.setsockopt_syscall(sockfd, SOL_SOCKET, SO_RCVBUF, 2000), + 0 + ); + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_RCVBUF, &mut optstore), + 0 + ); + assert_eq!(optstore, 2000); + + // check the options + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_REUSEPORT, &mut optstore), + 0 + ); + assert_eq!(optstore, 1); + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_LINGER, &mut optstore), + 0 + ); + assert_eq!(optstore, 1); + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_SOCKET, SO_KEEPALIVE, &mut optstore), + 0 + ); + assert_eq!(optstore, 1); + + // TCP_NODELAY for SOL_TCP + assert_eq!(cage.setsockopt_syscall(sockfd, SOL_TCP, TCP_NODELAY, 1), 0); + assert_eq!( + cage.getsockopt_syscall(sockfd, SOL_TCP, TCP_NODELAY, &mut optstore), + 0 + ); + assert_eq!(optstore, 1); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_socketpair() { + // this test is used for testing a generic use case of socketpair + // test involves creating a TCP socketpair let two threads communicate + // with the socketpair + + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + let mut socketpair = interface::SockPair::default(); + assert_eq!( + Cage::socketpair_syscall(cage.clone(), AF_UNIX, SOCK_STREAM, 0, &mut socketpair), + 0 + ); + let cage2 = cage.clone(); + + let thread = interface::helper_thread(move || { + // this thread first receives the message, then send the message + let mut buf = sizecbuf(10); + loop { + let result = cage2.recv_syscall(socketpair.sock2, buf.as_mut_ptr(), 10, 0); + if result != -libc::EINTR { + break; // if the error was EINTR, retry the syscall + } + } + // check if received message is correct + assert_eq!(cbuf2str(&buf), "test\0\0\0\0\0\0"); + + // send message to peer + assert_eq!( + cage2.send_syscall(socketpair.sock2, str2cbuf("Socketpair Test"), 15, 0), + 15 + ); + }); + + let cage3 = cage.clone(); + let thread_2 = interface::helper_thread(move || { + // this thread first send the message, then receive the message + assert_eq!( + cage3.send_syscall(socketpair.sock1, str2cbuf("test"), 4, 0), + 4 + ); + + let mut buf2 = sizecbuf(15); + loop { + let result = cage3.recv_syscall(socketpair.sock1, buf2.as_mut_ptr(), 15, 0); + if result != -libc::EINTR { + break; // if the error was EINTR, retry the syscall + } + } + let str2 = cbuf2str(&buf2); + // check if received message is correct + assert_eq!(str2, "Socketpair Test"); + }); + + thread.join().unwrap(); + thread_2.join().unwrap(); + + assert_eq!(cage.close_syscall(socketpair.sock1), 0); + assert_eq!(cage.close_syscall(socketpair.sock2), 0); + + // end of the socket pair test (note we are only supporting AF_UNIX and TCP) + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_socketpair_bad_input() { + // test for error cases of socketpair + + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + let mut socketpair = interface::SockPair::default(); + + // test for unsupported domain + // socketpair only works with AF_UNIX + assert_eq!( + Cage::socketpair_syscall(cage.clone(), AF_INET, SOCK_STREAM, 0, &mut socketpair), + -(Errno::EOPNOTSUPP as i32) + ); + assert_eq!( + Cage::socketpair_syscall(cage.clone(), AF_INET6, SOCK_STREAM, 0, &mut socketpair), + -(Errno::EOPNOTSUPP as i32) + ); + + // test for unsupported socktype + // socketpair only works with SOCK_STREAM + assert_eq!( + Cage::socketpair_syscall(cage.clone(), AF_UNIX, SOCK_DGRAM, 0, &mut socketpair), + -(Errno::EOPNOTSUPP as i32) + ); + + // test for unsupported protocol + // we only support for protocol of 0 + assert_eq!( + Cage::socketpair_syscall(cage.clone(), AF_UNIX, SOCK_STREAM, 1, &mut socketpair), + -(Errno::EOPNOTSUPP as i32) + ); + + // test for bad structured input + assert_eq!( + Cage::socketpair_syscall(cage.clone(), AF_UNIX, 472810394, 0, &mut socketpair), + -(Errno::EOPNOTSUPP as i32) + ); + + // test for invalid flags + assert_eq!( + Cage::socketpair_syscall( + cage.clone(), + AF_UNIX, + SOCK_STREAM | 1024, + 0, + &mut socketpair + ), + -(Errno::EINVAL as i32) + ); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_socketpair_cloexec() { + // this test is used for testing socketpair when cloexec flag is set + // when cloexec flag is set, the file descriptor of the socket should + // be automatically closed when exec_syscall is called + + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + let mut socketpair = interface::SockPair::default(); + // try with cloexec flag + assert_eq!( + Cage::socketpair_syscall( + cage.clone(), + AF_UNIX, + SOCK_STREAM | SOCK_CLOEXEC, + 0, + &mut socketpair + ), + 0 + ); + + // we use fstat_syscall to inspect if the file descriptor is valid + let mut uselessstatdata = StatData::default(); + + // check if the file descriptor exists + // if the file descriptor does not exist, it should return another error + assert_eq!( + cage.fstat_syscall(socketpair.sock1, &mut uselessstatdata), + -(Errno::EOPNOTSUPP as i32) + ); + assert_eq!( + cage.fstat_syscall(socketpair.sock2, &mut uselessstatdata), + -(Errno::EOPNOTSUPP as i32) + ); + + // now exec the cage + assert_eq!(cage.exec_syscall(2), 0); + + // check if the file descriptor is closed in new cage + // EBADF is the error that is supposed to be returned when file descriptor does + // not exist + let newcage = interface::cagetable_getref(2); + assert_eq!( + newcage.fstat_syscall(socketpair.sock1, &mut uselessstatdata), + -(Errno::EBADF as i32) + ); + assert_eq!( + newcage.fstat_syscall(socketpair.sock2, &mut uselessstatdata), + -(Errno::EBADF as i32) + ); + + assert_eq!(newcage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_socketpair_nonblocking() { + // this test is used for testing socketpair when nonblocking flag is set + // when nonblocking flag is set, the socket should not block on syscalls like + // recv_syscall, instead, EAGAIN error should be returned when there is no + // data to receive + + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + let mut socketpair = interface::SockPair::default(); + + // try with nonblocking flag + assert_eq!( + Cage::socketpair_syscall( + cage.clone(), + AF_UNIX, + SOCK_STREAM | SOCK_NONBLOCK, + 0, + &mut socketpair + ), + 0 + ); + + let cage2 = cage.clone(); + + let thread = interface::helper_thread(move || { + let mut buf = sizecbuf(10); + // counter is used for recording how many times do recv_syscall returned + // with EAGAIN + let mut counter = 0; + + // receive the message + // since peer will sleep for 30ms before send the message + // some nonblocking returns are expected + loop { + let result = cage2.recv_syscall(socketpair.sock2, buf.as_mut_ptr(), 10, 0); + if result == -(Errno::EAGAIN as i32) { + // return due to nonblocking flag + counter += 1; + continue; + } + if result != -libc::EINTR { + break; // if the error was EINTR, retry the syscall + } + } + // check if the received message is correct + assert_eq!(cbuf2str(&buf), "test\0\0\0\0\0\0"); + // check if there is any nonblocking return + assert_ne!(counter, 0); + + // sleep for 30ms so receiver could have some nonblocking return + interface::sleep(interface::RustDuration::from_millis(30)); + // send the message + assert_eq!( + cage2.send_syscall(socketpair.sock2, str2cbuf("Socketpair Test"), 15, 0), + 15 + ); + }); + + let cage3 = cage.clone(); + + let thread_2 = interface::helper_thread(move || { + // sleep for 30ms so receiver could have some nonblocking return + interface::sleep(interface::RustDuration::from_millis(30)); + + // send message + assert_eq!( + cage3.send_syscall(socketpair.sock1, str2cbuf("test"), 4, 0), + 4 + ); + + // receive the message + // since peer will sleep for 30ms before send the message + // some nonblocking returns are expected + let mut buf2 = sizecbuf(15); + let mut counter = 0; + loop { + let result = cage3.recv_syscall(socketpair.sock1, buf2.as_mut_ptr(), 15, 0); + if result == -(Errno::EAGAIN as i32) { + // return due to nonblocking flag + counter += 1; + continue; + } + if result != -libc::EINTR { + break; // if the error was EINTR, retry the syscall + } + } + // check if the received message is correct + let str2 = cbuf2str(&buf2); + assert_eq!(str2, "Socketpair Test"); + // check if there is any nonblocking return + assert_ne!(counter, 0); + }); + + thread.join().unwrap(); + thread_2.join().unwrap(); + + assert_eq!(cage.close_syscall(socketpair.sock1), 0); + assert_eq!(cage.close_syscall(socketpair.sock2), 0); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_udp_bad_bind() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let sockfd = cage.socket_syscall(AF_INET, SOCK_DGRAM, 0); + assert!(sockfd > 0); //checking that the sockfd is valid + let port: u16 = generate_random_port(); + + let sockaddr = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 + let port: u16 = generate_random_port(); + + let _sockaddr2 = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket2 = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 + + assert_eq!(cage.bind_syscall(sockfd, &socket), 0); + assert_eq!(cage.connect_syscall(sockfd, &socket2), 0); + + //now the bind should fail... + assert_ne!(cage.bind_syscall(sockfd, &socket), 0); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_udp_simple() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //just going to test the basic connect with UDP now... + let serverfd = cage.socket_syscall(AF_INET, SOCK_DGRAM, 0); + let clientfd = cage.socket_syscall(AF_INET, SOCK_DGRAM, 0); + let port: u16 = generate_random_port(); + + let socket = interface::GenSockaddr::V4(interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }); + + assert!(serverfd > 0); + assert!(clientfd > 0); + + //forking the cage to get another cage with the same information + assert_eq!(cage.fork_syscall(2), 0); + let thread = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + assert_eq!(cage2.bind_syscall(serverfd, &socket), 0); + + interface::sleep(interface::RustDuration::from_millis(30)); + + let mut buf = sizecbuf(10); + loop { + let result = cage2.recv_syscall(serverfd, buf.as_mut_ptr(), 10, 0); + if result != -libc::EINTR { + break; // if the error was EINTR, retry the syscall + } + } + assert_eq!(cbuf2str(&buf), "test\0\0\0\0\0\0"); + + interface::sleep(interface::RustDuration::from_millis(30)); + loop { + let result = cage2.recv_syscall(serverfd, buf.as_mut_ptr(), 10, 0); + if result != -libc::EINTR { + assert_eq!(result, 5); + break; // if the error was EINTR, retry the syscall + } + } + assert_eq!(cbuf2str(&buf), "test2\0\0\0\0\0"); + + assert_eq!(cage2.close_syscall(serverfd), 0); + assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); + + interface::sleep(interface::RustDuration::from_millis(50)); + let mut buf2 = str2cbuf("test"); + assert_eq!(cage.sendto_syscall(clientfd, buf2, 4, 0, &socket), 4); + let sendsockfd2 = cage.socket_syscall(AF_INET, SOCK_DGRAM, 0); + assert!(sendsockfd2 > 0); + let port: u16 = generate_random_port(); + + let sockaddr2 = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket2 = interface::GenSockaddr::V4(sockaddr2); //127.0.0.1 + + interface::sleep(interface::RustDuration::from_millis(50)); + + buf2 = str2cbuf("test2"); + assert_eq!(cage.bind_syscall(sendsockfd2, &socket2), 0); + assert_eq!(cage.sendto_syscall(sendsockfd2, buf2, 5, 0, &socket), 5); + + thread.join().unwrap(); + + assert_eq!(cage.close_syscall(sendsockfd2), 0); + assert_eq!(cage.close_syscall(clientfd), 0); + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_udp_connect() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + //getting the sockets set up... + let listenfd = cage.socket_syscall(AF_INET, SOCK_DGRAM, 0); + let sendfd = cage.socket_syscall(AF_INET, SOCK_DGRAM, 0); + let port: u16 = generate_random_port(); + let sockaddr = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 + + assert!(listenfd > 0); + assert!(sendfd > 0); + + assert_eq!(cage.bind_syscall(listenfd, &socket), 0); + + //forking the cage to get another cage with the same information + assert_eq!(cage.fork_syscall(2), 0); + + let thread = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + + interface::sleep(interface::RustDuration::from_millis(20)); + let mut buf = sizecbuf(16); + loop { + let result = cage2.recv_syscall(listenfd, buf.as_mut_ptr(), 16, 0); + if result != -libc::EINTR { + assert_eq!(result, 16); + break; // if the error was EINTR, retry the syscall + } + } + assert_ne!(buf, sizecbuf(16)); + assert_eq!(cbuf2str(&buf), "UDP Connect Test"); + + assert_eq!(cage2.close_syscall(listenfd), 0); + assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); + + assert_eq!(cage.connect_syscall(sendfd, &socket), 0); + interface::sleep(interface::RustDuration::from_millis(50)); + assert_eq!( + cage.send_syscall(sendfd, str2cbuf("UDP Connect Test"), 16, 0), + 16 + ); + thread.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_gethostname() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + // Assuming DEFAULT_HOSTNAME == "Lind" and change of hostname is not allowed + + let cage = interface::cagetable_getref(1); + + let mut buf = vec![0u8; 5]; + let bufptr: *mut u8 = &mut buf[0]; + assert_eq!( + cage.gethostname_syscall(bufptr, -1), + -(Errno::EINVAL as i32) + ); + assert_eq!(cage.gethostname_syscall(bufptr, 5), 0); + assert_eq!(std::str::from_utf8(&buf).unwrap(), "Lind\0"); + + let mut buf = vec![0u8; 5]; + let bufptr: *mut u8 = &mut buf[0]; + assert_eq!(cage.gethostname_syscall(bufptr, 4), 0); + assert_eq!(std::str::from_utf8(&buf).unwrap(), "Lind\0"); + + let mut buf = vec![0u8; 5]; + let bufptr: *mut u8 = &mut buf[0]; + assert_eq!(cage.gethostname_syscall(bufptr, 2), 0); + assert_eq!(std::str::from_utf8(&buf).unwrap(), "Li\0\0\0"); + + let mut buf = vec![0u8; 4]; + let bufptr: *mut u8 = &mut buf[0]; + assert_eq!(cage.gethostname_syscall(bufptr, 4), 0); + assert_eq!(std::str::from_utf8(&buf).unwrap(), "Lind"); + + let mut buf = vec![0u8; 2]; + let bufptr: *mut u8 = &mut buf[0]; + assert_eq!(cage.gethostname_syscall(bufptr, 2), 0); + assert_eq!(std::str::from_utf8(&buf).unwrap(), "Li"); + + let mut buf = vec![0u8; 2]; + let bufptr: *mut u8 = &mut buf[0]; + assert_eq!(cage.gethostname_syscall(bufptr, 0), 0); + assert_eq!(std::str::from_utf8(&buf).unwrap(), "\0\0"); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_getifaddrs() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // get the address + let mut buf = vec![0u8; 200]; + let bufptr: *mut u8 = &mut buf[0]; + // not enough space would cause EOPNOTSUPP error + assert_eq!( + cage.getifaddrs_syscall(bufptr, 1), + -(Errno::EOPNOTSUPP as i32) + ); + assert_eq!(cage.getifaddrs_syscall(bufptr, 200), 0); + // split the address string into vector + let result = std::str::from_utf8(&buf).unwrap().replace("\0", ""); + let addrs = result.trim().split("\n").collect::>(); + // we should have some addresses returned + assert!(addrs.len() > 0); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_dns_rootserver_ping() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + //https://w3.cs.jmu.edu/kirkpams/OpenCSF/Books/csf/html/UDPSockets.html + #[repr(C)] + struct DnsHeader { + xid: u16, + flags: u16, + qdcount: u16, + ancount: u16, + nscount: u16, + arcount: u16, + } + + /* Structure of the bytes for an IPv4 answer */ + #[repr(C, packed(1))] + struct DnsRecordAT { + compression: u16, + typ: u16, + clas: u16, + ttl: u32, + length: u16, + addr: interface::V4Addr, + } + + let cage = interface::cagetable_getref(1); + + let dnssocket = cage.socket_syscall(AF_INET, SOCK_DGRAM, 0); + assert!(dnssocket > 0); + + let dnsh = DnsHeader { + xid: 0x1234u16.to_be(), + flags: 0x0100u16.to_be(), + qdcount: 0x0001u16.to_be(), + ancount: 0, + nscount: 0, + arcount: 0, + }; + + //specify payload information for dns request + let hostname = "\x0Bengineering\x03nyu\x03edu\0".to_string().into_bytes(); //numbers signify how many characters until next dot + let dnstype = 1u16; + let dnsclass = 1u16; + + //construct packet + let packetlen = std::mem::size_of::() + + hostname.len() + + std::mem::size_of::() + + std::mem::size_of::(); + let mut packet = vec![0u8; packetlen]; + + let packslice = packet.as_mut_slice(); + let mut pslen = std::mem::size_of::(); + unsafe { + let dnss = ::std::slice::from_raw_parts( + ((&dnsh) as *const DnsHeader) as *const u8, + std::mem::size_of::(), + ); + packslice[..pslen].copy_from_slice(dnss); + } + packslice[pslen..pslen + hostname.len()].copy_from_slice(hostname.as_slice()); + pslen += hostname.len(); + packslice[pslen..pslen + 2].copy_from_slice(&dnstype.to_be_bytes()); + packslice[pslen + 2..pslen + 4].copy_from_slice(&dnsclass.to_be_bytes()); + + //send packet + let mut dnsaddr = interface::GenSockaddr::V4(interface::SockaddrV4 { + sin_family: AF_INET as u16, + // static port is used beacuse this test doesn't bind. + sin_port: 53u16.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([208, 67, 222, 222]), + }, + padding: 0, + }); //opendns ip addr + assert_eq!( + cage.sendto_syscall(dnssocket, packslice.as_ptr(), packslice.len(), 0, &dnsaddr), + packslice.len() as i32 + ); + + let mut dnsresp = [0u8; 512]; + + //recieve DNS response + loop { + let result = cage.recvfrom_syscall( + dnssocket, + dnsresp.as_mut_ptr(), + 512, + 0, + &mut Some(&mut dnsaddr), + ); + + if result != -libc::EINTR { + assert!(result >= 0); + break; + } + // if the error was EINTR, retry the syscall + } + + //extract packet header + let response_header = unsafe { &*(dnsresp.as_ptr() as *const DnsHeader) }; + assert_eq!(u16::from_be(response_header.flags) & 0xf, 0); + + //skip over the name + let mut nameptr = std::mem::size_of::(); + while dnsresp[nameptr] != 0 { + nameptr += dnsresp[nameptr] as usize + 1; + } + + //next we need to skip the null byte, qtype, and qclass to extract the main + // response payload + let recordptr = + dnsresp.as_ptr().wrapping_offset(nameptr as isize + 5) as *const DnsRecordAT; + let record = unsafe { &*recordptr }; + let addr = u32::from_be(record.addr.s_addr); + assert_eq!(addr, 0x23ac5973); //check that what is returned is the actual ip, 35.172.89.115 + //assert_eq!(record.addr.s_addr, 0x7359ac23); //check that what is returned is + // the actual ip, 35.172.89.115 + + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_domain_socket() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + //bind net zero test reformatted for domain sockets + + let clientsockfilename = "/client.sock"; + let serversockfilename = "/server.sock"; + + let cage = interface::cagetable_getref(1); + + //both the server and the socket are run from this file + let serversockfd = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + let clientsockfd = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + + //making sure that the assigned fd's are valid + assert!(serversockfd > 0); + assert!(clientsockfd > 0); + + //binding to a socket + let serversockaddr = + interface::new_sockaddr_unix(AF_UNIX as u16, serversockfilename.as_bytes()); + let serversocket = interface::GenSockaddr::Unix(serversockaddr); + let clientsockaddr = + interface::new_sockaddr_unix(AF_UNIX as u16, clientsockfilename.as_bytes()); + let clientsocket = interface::GenSockaddr::Unix(clientsockaddr); + + assert_eq!(cage.bind_syscall(serversockfd, &serversocket), 0); + assert_eq!(cage.bind_syscall(clientsockfd, &clientsocket), 0); + assert_eq!(cage.listen_syscall(serversockfd, 1), 0); //we are only allowing for one client at a time + + //forking the cage to get another cage with the same information + assert_eq!(cage.fork_syscall(2), 0); + + //creating a thread for the server so that the information can be sent between + // the two threads + let thread = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + let mut socket2 = interface::GenSockaddr::Unix(interface::new_sockaddr_unix( + AF_UNIX as u16, + "".as_bytes(), + )); // blank unix sockaddr + + let sockfd = cage2.accept_syscall(serversockfd, &mut socket2); //really can only make sure that the fd is valid + assert!(sockfd > 0); + + interface::sleep(interface::RustDuration::from_millis(100)); + + //process the first test... + //Writing 100, then peek 100, then read 100 + let mut buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 100, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 100 + ); //peeking at the input message + assert_eq!(cbuf2str(&buf), &"A".repeat(100)); + buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 100, 0, &mut Some(&mut socket2)), + 100 + ); //reading the input message + assert_eq!(cbuf2str(&buf), &"A".repeat(100)); + buf = sizecbuf(100); + + interface::sleep(interface::RustDuration::from_millis(200)); + + //process the second test... + //Writing 100, read 20, peek 20, read 80 + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 20, 0, &mut Some(&mut socket2)), + 20 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(20) + &"\0".repeat(80)); + buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 20, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 20 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(20) + &"\0".repeat(80)); + buf = sizecbuf(100); + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 80, 0, &mut Some(&mut socket2)), + 80 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(80) + &"\0".repeat(20)); + buf = sizecbuf(100); + + interface::sleep(interface::RustDuration::from_millis(200)); + + //process the third test... + //Writing 100, peek several times, read 100 + for _ in 0..4 { + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 10, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 10 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(10) + &"\0".repeat(90)); + buf = sizecbuf(100); + } + for _ in 0..4 { + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 20, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 20 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(20) + &"\0".repeat(80)); + buf = sizecbuf(100); + } + for _ in 0..4 { + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 30, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 30 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(30) + &"\0".repeat(70)); + buf = sizecbuf(100); + } + for _ in 0..4 { + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 40, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 40 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(40) + &"\0".repeat(60)); + buf = sizecbuf(100); + } + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 100, 0, &mut Some(&mut socket2)), + 100 + ); + assert_eq!(cbuf2str(&buf), &"A".repeat(100)); + buf = sizecbuf(100); + + interface::sleep(interface::RustDuration::from_millis(200)); + + //process the fourth test... + //Writing 50, peek 50 + assert_eq!( + cage2.recvfrom_syscall( + sockfd, + buf.as_mut_ptr(), + 50, + MSG_PEEK, + &mut Some(&mut socket2) + ), + 50 + ); + assert_eq!(cbuf2str(&buf), "A".repeat(50) + &"\0".repeat(50)); + assert_eq!(cage2.close_syscall(sockfd), 0); + + assert_eq!(cage2.close_syscall(serversockfd), 0); + + assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); + + //connect to the server + interface::sleep(interface::RustDuration::from_millis(20)); + + assert_eq!(cage.connect_syscall(clientsockfd, &serversocket), 0); + + //send the data with delays so that the server can process the information + // cleanly + assert_eq!( + cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(100)), 100, 0), + 100 + ); + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!( + cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(100)), 100, 0), + 100 + ); + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!( + cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(100)), 100, 0), + 100 + ); + interface::sleep(interface::RustDuration::from_millis(100)); + + assert_eq!( + cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(50)), 50, 0), + 50 + ); + interface::sleep(interface::RustDuration::from_millis(100)); - use std::net::SocketAddrV4; - use std::net::Ipv4Addr; + assert_eq!(cage.close_syscall(clientsockfd), 0); - use crate::example_grates::fdtable::*; + thread.join().unwrap(); - use libc::*; + cage.unlink_syscall(serversockfilename); + cage.unlink_syscall(clientsockfilename); - pub fn net_tests() { - ut_lind_net_bind(); - ut_lind_net_socketpair(); - ut_lind_net_connect(); - ut_lind_net_socket(); - ut_lind_net_epoll(); + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); } - pub fn ut_lind_net_bind() { - lindrustinit(0); - let cage = interface::cagetable_getref(1); - let sockfd = cage.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, 0); + #[test] + pub fn ut_lind_net_epoll_create_bad_input() { + // this test is used for testing epoll_create_syscall with error/edge cases + // specifically + // following tests are performed: + // 1. test for errno with invalid size argument - let mut addr: sockaddr_in = unsafe { std::mem::zeroed() }; - addr.sin_family = libc::AF_INET as u16; - addr.sin_addr.s_addr = INADDR_ANY; - addr.sin_port = 8080_u16.to_be();//8080 - - //first bind should work... but second bind should not - assert_eq!(cage.bind_syscall(sockfd, &addr as *const _ as *const _, std::mem::size_of::() as u32), 0); + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); - //trying to bind another to the same IP/PORT - let sockfd2 = cage.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, 0); - assert_eq!( - cage.bind_syscall(sockfd2, &addr as *const _ as *const _, std::mem::size_of::() as u32), - -1 - ); //already bound so should fail + let cage = interface::cagetable_getref(1); - //UDP should still work... - let sockfd3 = cage.socket_syscall(libc::AF_INET, libc::SOCK_DGRAM, 0); - assert_eq!(cage.bind_syscall(sockfd3, &addr as *const _ as *const _, std::mem::size_of::() as u32), 0); + // test for invalid size argument + assert_eq!(cage.epoll_create_syscall(0), -(Errno::EINVAL as i32)); - assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); lindrustfinalize(); } - - pub fn ut_lind_net_socket() { - lindrustinit(0); + #[test] + pub fn ut_lind_net_epoll_ctl_bad_input() { + // this test is used for testing epoll_ctl_syscall with error/edge cases + // specifically + // following tests are performed: + // 1. test for errno with invalid fd number + // 2. test for errno with invalid epfd number + // 3. test for errno with out of range fd number + // 4. test for errno with out of range epfd number + // 5. test for errno when epfd is not epoll instance + // 6. test for errno when epfd and fd are the same + // 7. test for errno when fd is a file fd + // 8. test for errno when trying to modify a fd that does not added to set + // 9. test for errno when trying to delete a fd that does not added to set + // 10. test for errno when trying to add a fd that already added to the set + // 11. test for errno when passing invalid flag + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); - let mut sockfd = cage.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, 0); - let sockfd2 = cage.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, libc::IPPROTO_TCP); + // create an epoll instance + let epfd = cage.epoll_create_syscall(1); - let sockfd3 = cage.socket_syscall(libc::AF_INET, libc::SOCK_DGRAM, 0); - let sockfd4 = cage.socket_syscall(libc::AF_INET, libc::SOCK_DGRAM, libc::IPPROTO_UDP); + // create a pipe fd + let mut pipefds = PipeArray { + readfd: -1, + writefd: -1, + }; + assert_eq!(cage.pipe2_syscall(&mut pipefds, O_NONBLOCK), 0); - //checking that the fd's are correct - assert!(sockfd >= 0); - assert!(sockfd2 >= 0); - assert!(sockfd3 >= 0); - assert!(sockfd4 >= 0); + // create a file fd + let filefd = cage.open_syscall("/netepolltest.txt", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); + assert!(filefd > 0); - //let's check an illegal operation... - let sockfddomain = cage.socket_syscall(libc::AF_UNIX, libc::SOCK_DGRAM, 0); - assert!(sockfddomain > 0); + // test for unexist fd number + assert_eq!( + cage.epoll_ctl_syscall( + epfd, + EPOLL_CTL_MOD, + 10, + &mut EpollEvent { + events: EPOLLIN as u32, + fd: 10, + } + ), + -(Errno::EBADF as i32) + ); - sockfd = cage.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, 0); - assert!(sockfd > 0); + assert_eq!( + cage.epoll_ctl_syscall( + 10, + EPOLL_CTL_MOD, + pipefds.readfd, + &mut EpollEvent { + events: EPOLLIN as u32, + fd: pipefds.readfd, + } + ), + -(Errno::EBADF as i32) + ); - assert_eq!(cage.close_syscall(sockfd), 0); - assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // test for out of range fd number + assert_eq!( + cage.epoll_ctl_syscall( + epfd, + EPOLL_CTL_MOD, + -1, + &mut EpollEvent { + events: EPOLLIN as u32, + fd: -1, + } + ), + -(Errno::EBADF as i32) + ); + assert_eq!( + cage.epoll_ctl_syscall( + -1, + EPOLL_CTL_MOD, + pipefds.readfd, + &mut EpollEvent { + events: EPOLLIN as u32, + fd: pipefds.readfd, + } + ), + -(Errno::EBADF as i32) + ); + + // test for fd that is not epoll fd + assert_eq!( + cage.epoll_ctl_syscall( + filefd, + EPOLL_CTL_ADD, + pipefds.readfd, + &mut EpollEvent { + events: EPOLLIN as u32, + fd: pipefds.readfd, + } + ), + -(Errno::EINVAL as i32) + ); + + // test when fd and epfd are the same + assert_eq!( + cage.epoll_ctl_syscall( + epfd, + EPOLL_CTL_ADD, + epfd, + &mut EpollEvent { + events: EPOLLIN as u32, + fd: epfd, + } + ), + -(Errno::EINVAL as i32) + ); + + // test when fd is a file fd + assert_eq!( + cage.epoll_ctl_syscall( + epfd, + EPOLL_CTL_ADD, + filefd, + &mut EpollEvent { + events: EPOLLIN as u32, + fd: filefd, + } + ), + -(Errno::EPERM as i32) + ); + + // test for modifying fd that does not exists + assert_eq!( + cage.epoll_ctl_syscall( + epfd, + EPOLL_CTL_MOD, + pipefds.readfd, + &mut EpollEvent { + events: EPOLLIN as u32, + fd: pipefds.readfd, + } + ), + -(Errno::ENOENT as i32) + ); + + // test for deleting fd that does not exists + assert_eq!( + cage.epoll_ctl_syscall( + epfd, + EPOLL_CTL_DEL, + pipefds.readfd, + &mut EpollEvent { + events: EPOLLIN as u32, + fd: pipefds.readfd, + } + ), + -(Errno::ENOENT as i32) + ); + + // now add a fd + assert_eq!( + cage.epoll_ctl_syscall( + epfd, + EPOLL_CTL_ADD, + pipefds.readfd, + &mut EpollEvent { + events: EPOLLIN as u32, + fd: pipefds.readfd, + } + ), + 0 + ); + + // test for adding fd that already exists + assert_eq!( + cage.epoll_ctl_syscall( + epfd, + EPOLL_CTL_ADD, + pipefds.readfd, + &mut EpollEvent { + events: EPOLLIN as u32, + fd: pipefds.readfd, + } + ), + -(Errno::EEXIST as i32) + ); + + // test for passing invalid flag + assert_eq!( + cage.epoll_ctl_syscall( + epfd, + 123, + pipefds.readfd, + &mut EpollEvent { + events: EPOLLIN as u32, + fd: pipefds.readfd, + } + ), + -(Errno::EINVAL as i32) + ); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); lindrustfinalize(); } - pub fn ut_lind_net_connect() { - lindrustinit(0); + #[test] + pub fn ut_lind_net_epoll_wait_bad_input() { + // this test is used for testing epoll_wait_syscall with error/edge cases + // specifically + // following tests are performed: + // 1. test for errno with out of range fd number + // 2. test for errno with invalid fd number + // 3. test for errno when fd is not an epoll instance + // 4. test for errno with invalid maxevents argument + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); - let server_fd = cage.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, 0); - assert!(server_fd >= 0); - let mut server_addr: libc::sockaddr_in = unsafe { - std::mem::zeroed() + // create an epoll instance + let epfd = cage.epoll_create_syscall(1); + + // create a pipe fd + let mut pipefds = PipeArray { + readfd: -1, + writefd: -1, }; - server_addr.sin_family = libc::AF_INET as u16; - server_addr.sin_addr.s_addr = libc::INADDR_ANY; - server_addr.sin_port = 7878_u16.to_be(); + assert_eq!(cage.pipe2_syscall(&mut pipefds, O_NONBLOCK), 0); - let bind_result = cage.bind_syscall( - server_fd, - &server_addr as *const _ as *const _, - std::mem::size_of::() as u32, - ); - - if bind_result < 0 { - let err = unsafe { - libc::__errno_location() - }; - let err_str = unsafe { - libc::strerror(*err) + // create a file fd + let filefd = cage.open_syscall("/netepolltest.txt", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); + assert!(filefd > 0); + + let mut event_list: Vec = vec![ + EpollEvent { + events: EPOLLIN as u32, + fd: 0, }; - let err_msg = unsafe { - CStr::from_ptr(err_str).to_string_lossy().into_owned() + 2 + ]; + + // test for out of range fd range + assert_eq!( + cage.epoll_wait_syscall(-1, &mut event_list, 1, None), + -(Errno::EBADF as i32) + ); + + // test for invalid fd range + assert_eq!( + cage.epoll_wait_syscall(10, &mut event_list, 1, None), + -(Errno::EBADF as i32) + ); + + // test for fd that is not epoll + assert_eq!( + cage.epoll_wait_syscall(filefd, &mut event_list, 1, None), + -(Errno::EINVAL as i32) + ); + + // test for invalid maxevents argument + assert_eq!( + cage.epoll_wait_syscall(epfd, &mut event_list, 0, None), + -(Errno::EINVAL as i32) + ); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_epoll_maxevents_arg() { + // this test is used for testing maxevents argument of epoll_wait_syscall + // specifically + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let pipe_num = 10; + // create some pipes + let mut pipefds = vec![ + PipeArray { + readfd: -1, + writefd: -1, }; - println!("errno: {:?}", err); - println!("Error message: {:?}", err_msg); - io::stdout().flush().unwrap(); + pipe_num + ]; + for pipefd in pipefds.iter_mut() { + assert_eq!(cage.pipe2_syscall(pipefd, O_NONBLOCK), 0); + } + + // create an epoll instance + let epfd = cage.epoll_create_syscall(1); + // add all pipes to epoll + for pipefd in pipefds.iter_mut() { + assert_eq!( + cage.epoll_ctl_syscall( + epfd, + EPOLL_CTL_ADD, + pipefd.readfd, + &mut EpollEvent { + events: EPOLLIN as u32, + fd: pipefd.readfd, + } + ), + 0 + ); + + // write something to the pipe at the same time + assert_eq!(cage.write_syscall(pipefd.writefd, str2cbuf("test"), 4), 4); } - let listen_result = cage.listen_syscall(server_fd, 128); - if listen_result < 0 { - panic!("listen_result"); + // at this point, all pipes are added to epoll, and they should all be readable + + // prepare the event_list to store the return value + let mut event_list: Vec = vec![EpollEvent { events: 0, fd: 0 }; pipe_num]; + + // test #1: all the fds should be ready + assert_eq!( + cage.epoll_wait_syscall(epfd, &mut event_list, pipe_num as i32, None), + pipe_num as i32 + ); + for event in event_list.iter() { + // check if all fd are marked as readable + assert_ne!(event.events & (EPOLLIN as u32), 0); } - cage.fork_syscall(2); + // test #2: maxevents set to be smaller than pipe_num - let thread = interface::helper_thread(move || { - // Client - let cage2 = interface::cagetable_getref(2); - let clientfd = cage2.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, 0); - if clientfd < 0 { - panic!("Failed to create socket"); - } + // clear event_list + let mut event_list: Vec = vec![ + EpollEvent { + events: 0xdeadbeef, + fd: 0, + }; + pipe_num + ]; - let connect_result = cage2.connect_syscall( - clientfd, - &server_addr as *const libc::sockaddr_in as *const libc::sockaddr, - std::mem::size_of::() as u32, - ); - if connect_result < 0 { - panic!("Failed to connect to server"); + assert_eq!(cage.epoll_wait_syscall(epfd, &mut event_list, 5, None), 5); + + for i in 0..pipe_num { + // even though all fds are ready, only first 5 should be marked with EPOLLIN + if i < 5 { + assert_ne!(event_list[i].events & (EPOLLIN as u32), 0); + } else { + assert_eq!(event_list[i].events, 0xdeadbeef); } - let message = CString::new("Hello from client").unwrap(); - let sendret = cage2.send_syscall(clientfd, message.as_ptr() as *const u8, message.to_bytes().len(), 0); - assert_ne!(sendret, 0); - cage2.close_syscall(clientfd); - assert_eq!(cage2.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); - }); + } - let mut client_addr: libc::sockaddr_in = unsafe { - std::mem::zeroed() - }; - let addr_len = std::mem::size_of::() as u32; - let client_fd = cage.accept_syscall( - server_fd, - &mut client_addr as *mut libc::sockaddr_in as *mut libc::sockaddr, - addr_len, + // test #3: maxevents set to be larger than actual ready fds (case 1) + + // clear event_list + let mut event_list: Vec = vec![ + EpollEvent { + events: 0xdeadbeef, + fd: 0, + }; + pipe_num + ]; + // first let's consume some pipes + let mut buf = sizecbuf(4); + assert_eq!(cage.read_syscall(pipefds[0].readfd, buf.as_mut_ptr(), 4), 4); + assert_eq!(cbuf2str(&buf), "test"); + assert_eq!(cage.read_syscall(pipefds[1].readfd, buf.as_mut_ptr(), 4), 4); + assert_eq!(cbuf2str(&buf), "test"); + // now pipe with index 0 and 1 are consumed and they are no longer readable + assert_eq!( + cage.epoll_wait_syscall(epfd, &mut event_list, pipe_num as i32, None), + (pipe_num - 2) as i32 ); - if client_fd < 0 { - panic!("client_fd"); + + for i in 0..pipe_num { + // even though all fds are ready, only first 5 should be marked with EPOLLIN + if i < 8 { + assert_ne!(event_list[i].events & (EPOLLIN as u32), 0); + } else { + assert_eq!(event_list[i].events, 0xdeadbeef); + } } - let mut buffer = [0u8; 1024]; - let len = cage.recv_syscall(client_fd, buffer.as_mut_ptr() as *mut u8, buffer.len(), 0); - if len == 0 { - panic!("Fail on child recv"); + // test #4: maxevents set to be larger than actual ready fds (case 2) + // clear event_list + let mut event_list: Vec = vec![ + EpollEvent { + events: 0xdeadbeef, + fd: 0, + }; + pipe_num + ]; + // we try to only read 5 this time + // since the number of avaliable fds is still 8 + // so it is supposed to return 5 + assert_eq!(cage.epoll_wait_syscall(epfd, &mut event_list, 5, None), 5); + for i in 0..pipe_num { + // only first 5 should be marked with EPOLLIN and others remain untouched + if i < 5 { + assert_ne!(event_list[i].events & (EPOLLIN as u32), 0); + } else { + assert_eq!(event_list[i].events, 0xdeadbeef); + } } - cage.close_syscall(client_fd); - cage.close_syscall(server_fd); - thread.join().unwrap(); - assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); lindrustfinalize(); } - pub fn ut_lind_net_socketpair() { - lindrustinit(0); + #[test] + pub fn ut_lind_net_epoll_timeout() { + // this test is used for testing timeout argument of epoll_wait_syscall + // specifically + // following tests are performed: + // 1. test for epoll_wait when timeout could expire + // 2. test for epoll_wait when not fd is monitored but timeout is set + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); let cage = interface::cagetable_getref(1); - let mut socketpair = interface::SockPair::default(); - cage.socketpair_syscall(libc::AF_UNIX, libc::SOCK_STREAM, 0, &mut socketpair); - // assert_eq!( - // Cage::socketpair_syscall(&cage.clone(), libc::AF_UNIX, libc::SOCK_STREAM, 0, &mut socketpair), - // 0 - // ); - let cage2 = cage.clone(); - let thread = interface::helper_thread(move || { - let mut buf = sizecbuf(10); - loop { - let result = cage2.recv_syscall(socketpair.sock2, buf.as_mut_ptr(), 10, 0); - if result != -libc::EINTR { - break; // if the error was EINTR, retry the syscall - } - } - assert_eq!(cbuf2str(&buf), "test\0\0\0\0\0\0"); + // subtest 1: epoll when timeout could expire + // create a TCP AF_UNIX socket + let serversockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + assert!(serversockfd > 0); + assert!(clientsockfd > 0); - interface::sleep(interface::RustDuration::from_millis(30)); - assert_eq!( - cage2.send_syscall(socketpair.sock2, str2cbuf("Socketpair Test"), 15, 0), - 15 - ); + let port: u16 = generate_random_port(); + let sockaddr = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 from bytes above + + // server bind and listen + assert_eq!(cage.bind_syscall(serversockfd, &socket), 0); + assert_eq!(cage.listen_syscall(serversockfd, 4), 0); + + assert_eq!(cage.fork_syscall(2), 0); + assert_eq!(cage.close_syscall(clientsockfd), 0); + + // this barrier is used for preventing + // an unfixed bug (`close` could block when other thread/cage is `accept`) from + // deadlocking the test + let barrier = Arc::new(Barrier::new(2)); + let barrier_2 = barrier.clone(); + + // client connects to the server to send and recv data... + let threadclient = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + assert_eq!(cage2.close_syscall(serversockfd), 0); + + barrier_2.wait(); + + // connect to the server + assert_eq!(cage2.connect_syscall(clientsockfd, &socket), 0); + + // wait for 100ms + interface::sleep(interface::RustDuration::from_millis(100)); + + // send some message to client + assert_eq!(cage2.send_syscall(clientsockfd, str2cbuf("test"), 4, 0), 4); + + assert_eq!(cage2.close_syscall(clientsockfd), 0); + cage2.exit_syscall(EXIT_SUCCESS); }); + // make sure client thread closed the duplicated socket before server start to + // accept + barrier.wait(); + + // wait for client to connect + let mut sockgarbage = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + let sockfd = cage.accept_syscall(serversockfd as i32, &mut sockgarbage); + + // add to client socket to epoll + // perform another test by the way: the fd field of EpollEvent is user data by + // standard which means we could put anything we want here, and kernel + // is not supposed to touch it + let epfd = cage.epoll_create_syscall(1); assert_eq!( - cage.send_syscall(socketpair.sock1, str2cbuf("test"), 4, 0), - 4 + cage.epoll_ctl_syscall( + epfd, + EPOLL_CTL_ADD, + sockfd, + &mut EpollEvent { + events: EPOLLIN as u32, + fd: 123, + } + ), + 0 ); + // event_list used for holding return value + let mut event_list: Vec = vec![EpollEvent { events: 0, fd: 0 }]; + + // this counter is used for recording how many times do select returns due to + // timeout + let mut counter = 0; - let mut buf2 = sizecbuf(15); loop { - let result = cage.recv_syscall(socketpair.sock1, buf2.as_mut_ptr(), 15, 0); - if result != -libc::EINTR { - break; // if the error was EINTR, retry the syscall + let epoll_result = cage.epoll_wait_syscall( + epfd, + &mut event_list, + 1, + Some(interface::RustDuration::new(0, 10000000)), // 10ms + ); + assert!(epoll_result >= 0); + // epoll timeout after 10ms, but client will send messages after 100ms + // so there should be some timeout return + if epoll_result == 0 { + counter += 1; + } else if event_list[0].events & (EPOLLIN as u32) != 0 { + assert_eq!(event_list[0].fd, 123); // fd field should remain touched + // just received the message, check the message and break + let mut buf = sizecbuf(4); + assert_eq!(cage.recv_syscall(sockfd, buf.as_mut_ptr(), 4, 0), 4); + assert_eq!(cbuf2str(&buf), "test"); + break; + } else { + unreachable!(); } } - let str2 = cbuf2str(&buf2); - assert_eq!(str2, "Socketpair Test"); + // check if epoll timeout correctly + assert!(counter > 0); - thread.join().unwrap(); + threadclient.join().unwrap(); - assert_eq!(cage.close_syscall(socketpair.sock1), 0); - assert_eq!(cage.close_syscall(socketpair.sock2), 0); + // subtest 2: epoll when nothing is monitored, `epoll` here should behave like + // `sleep` + let epfd2 = cage.epoll_create_syscall(1); - // end of the socket pair test (note we are only supporting AF_UNIX and TCP) + let start_time = interface::starttimer(); + let timeout = interface::RustDuration::new(0, 10000000); // 10ms + let epoll_result = cage.epoll_wait_syscall(epfd2, &mut event_list, 1, Some(timeout)); + assert!(epoll_result == 0); + // should wait for at least 10ms + assert!(interface::readtimer(start_time) >= timeout); - assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); lindrustfinalize(); } - + #[test] + #[ignore] pub fn ut_lind_net_epoll() { - lindrustinit(0); + // test for epoll monitoring on multiple different file descriptors: + // 1. AF_INET server socket waiting for two clients + // 2. AF_INET server socket's connection file descriptor with clients + // 3. AF_UNIX server socket's connection file descriptor with a client + // 4. pipe + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); let cage = interface::cagetable_getref(1); - let epoll_fd = cage.epoll_create_syscall(1); - assert_ne!(epoll_fd, -1); + // creating socket file descriptors + let serversockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd1 = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd2 = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let serversockfd_unix = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + let clientsockfd_unix = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); - println!("epoll_fd: {:?}", epoll_fd); - io::stdout().flush().unwrap(); + assert!(serversockfd > 0); + assert!(clientsockfd1 > 0); + assert!(clientsockfd2 > 0); + assert!(serversockfd_unix > 0); + assert!(clientsockfd_unix > 0); + // creating a pipe let mut pipefds = PipeArray { - readfd: 0, - writefd: 0, + readfd: -1, + writefd: -1, }; assert_eq!(cage.pipe_syscall(&mut pipefds), 0); - println!("pipe finished"); - io::stdout().flush().unwrap(); + // create a INET address + let port: u16 = generate_random_port(); - assert_eq!(cage.fork_syscall(2), 0); - let sender = std::thread::spawn(move || { + let sockaddr = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 from bytes above + + //binding to a socket + let serversockaddr_unix = + interface::new_sockaddr_unix(AF_UNIX as u16, "server_poll".as_bytes()); + let serversocket_unix = interface::GenSockaddr::Unix(serversockaddr_unix); + + let clientsockaddr_unix = + interface::new_sockaddr_unix(AF_UNIX as u16, "client_poll".as_bytes()); + let clientsocket_unix = interface::GenSockaddr::Unix(clientsockaddr_unix); + + assert_eq!(cage.bind_syscall(serversockfd_unix, &serversocket_unix), 0); + assert_eq!(cage.bind_syscall(clientsockfd_unix, &clientsocket_unix), 0); + assert_eq!(cage.listen_syscall(serversockfd_unix, 1), 0); + + assert_eq!(cage.bind_syscall(serversockfd, &socket), 0); + assert_eq!(cage.listen_syscall(serversockfd, 4), 0); + + // create epoll file descriptor + let epfd = cage.epoll_create_syscall(1); + assert!(epfd > 0); + + // add file descriptors to epoll + cage.epoll_ctl_syscall( + epfd, + EPOLL_CTL_ADD, + serversockfd, + &mut EpollEvent { + events: EPOLLIN as u32, + fd: serversockfd, + }, + ); + + cage.epoll_ctl_syscall( + epfd, + EPOLL_CTL_ADD, + serversockfd_unix, + &mut EpollEvent { + events: EPOLLIN as u32, + fd: serversockfd_unix, + }, + ); + + cage.epoll_ctl_syscall( + epfd, + EPOLL_CTL_ADD, + pipefds.readfd, + &mut EpollEvent { + events: EPOLLIN as u32, + fd: pipefds.readfd, + }, + ); + + assert_eq!(cage.fork_syscall(2), 0); // used for AF_INET thread client 1 + assert_eq!(cage.fork_syscall(3), 0); // used for AF_INET thread client 2 + assert_eq!(cage.fork_syscall(4), 0); // used for AF_UNIX thread client + + assert_eq!(cage.fork_syscall(5), 0); // used for pipe thread + + assert_eq!(cage.close_syscall(clientsockfd1), 0); + assert_eq!(cage.close_syscall(clientsockfd2), 0); + assert_eq!(cage.close_syscall(clientsockfd_unix), 0); + + // this barrier have to ensure that the clients finish the connect before we do + // the epoll due to an unfixed bug (`close` could block when other + // thread/cage is `accept`) + let barrier = Arc::new(Barrier::new(3)); + let barrier_clone1 = barrier.clone(); + let barrier_clone2 = barrier.clone(); + + // this barrier is used for control the flow the pipe + let barrier_pipe = Arc::new(Barrier::new(2)); + let barrier_pipe_clone = barrier_pipe.clone(); + + // due to an unfixed bug in ref counter of AF_UNIX socket pipe + // have to make sure all the threads exits only after the AF_UNIX test finished + let barrier_exit = Arc::new(Barrier::new(4)); + let barrier_exit_clone1 = barrier_exit.clone(); + let barrier_exit_clone2 = barrier_exit.clone(); + let barrier_exit_clone3 = barrier_exit.clone(); + + // client 1 connects to the server to send and recv data + let threadclient1 = interface::helper_thread(move || { let cage2 = interface::cagetable_getref(2); + assert_eq!(cage2.close_syscall(serversockfd), 0); + assert_eq!(cage2.close_syscall(clientsockfd2), 0); - println!("child start"); - io::stdout().flush().unwrap(); - - if cage2.close_syscall(pipefds.readfd) < 0 { - let err = unsafe { - libc::__errno_location() - }; - let err_str = unsafe { - libc::strerror(*err) - }; - let err_msg = unsafe { - CStr::from_ptr(err_str).to_string_lossy().into_owned() - }; - println!("errno: {:?}", err); - println!("Error message: {:?}", err_msg); - println!("pipefds.readfd: {:?}", pipefds.readfd); - io::stdout().flush().unwrap(); - panic!(); - } - - let message = b"Hello from child"; - - println!("child - before write"); - io::stdout().flush().unwrap(); - - if cage2.write_syscall(pipefds.writefd, message.as_ptr(), message.len()) < 0 { - let err = unsafe { - libc::__errno_location() - }; - let err_str = unsafe { - libc::strerror(*err) - }; - let err_msg = unsafe { - CStr::from_ptr(err_str).to_string_lossy().into_owned() - }; - println!("errno: {:?}", err); - println!("Error message: {:?}", err_msg); - println!("pipefds.writefd: {:?}", pipefds.writefd); - io::stdout().flush().unwrap(); - panic!(); - } - println!("child - after write"); - io::stdout().flush().unwrap(); + // connect to server + assert_eq!(cage2.connect_syscall(clientsockfd1, &socket), 0); + barrier_clone1.wait(); - assert_eq!(cage2.close_syscall(pipefds.writefd), 0); + // send message to server + assert_eq!(cage2.send_syscall(clientsockfd1, str2cbuf("test"), 4, 0), 4); - println!("child - after close"); - io::stdout().flush().unwrap(); + interface::sleep(interface::RustDuration::from_millis(1)); - assert_eq!(cage2.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // receive message from server + let mut buf = sizecbuf(4); + assert_eq!(cage2.recv_syscall(clientsockfd1, buf.as_mut_ptr(), 4, 0), 4); + assert_eq!(cbuf2str(&buf), "test"); + + assert_eq!(cage2.close_syscall(clientsockfd1), 0); + barrier_exit_clone1.wait(); + cage2.exit_syscall(EXIT_SUCCESS); }); - println!("parent - before close"); - io::stdout().flush().unwrap(); + // client 2 connects to the server to send and recv data + let threadclient2 = interface::helper_thread(move || { + let cage3 = interface::cagetable_getref(3); + assert_eq!(cage3.close_syscall(serversockfd), 0); + assert_eq!(cage3.close_syscall(clientsockfd1), 0); - assert_eq!(cage.close_syscall(pipefds.writefd), 0); - let mut epollevent = EpollEvent { - events: libc::EPOLLIN as u32, - fd: pipefds.readfd, - }; + // connect to server + assert_eq!(cage3.connect_syscall(clientsockfd2, &socket), 0); + barrier_clone2.wait(); - if cage.epoll_ctl_syscall(epoll_fd, libc::EPOLL_CTL_ADD, pipefds.readfd, &mut epollevent) < 0 { - let err = unsafe { - libc::__errno_location() - }; - let err_str = unsafe { - libc::strerror(*err) - }; - let err_msg = unsafe { - CStr::from_ptr(err_str).to_string_lossy().into_owned() - }; - let kernelfd = translate_virtual_fd(1, pipefds.readfd).unwrap(); - println!("kernel fd: {:?}", kernelfd); - println!("Error message: {:?}", err_msg); - println!("pipefds.readfd: {:?}", pipefds.readfd); - io::stdout().flush().unwrap(); - panic!(); + // send message to server + assert_eq!(cage3.send_syscall(clientsockfd2, str2cbuf("test"), 4, 0), 4); + + interface::sleep(interface::RustDuration::from_millis(1)); + + // receive message from server + let mut buf = sizecbuf(4); + let mut result: i32; + loop { + result = cage3.recv_syscall(clientsockfd2, buf.as_mut_ptr(), 4, 0); + if result != -libc::EINTR { + break; // if the error was EINTR, retry the syscall + } + } + assert_eq!(result, 4); + assert_eq!(cbuf2str(&buf), "test"); + + assert_eq!(cage3.close_syscall(clientsockfd2), 0); + barrier_exit_clone2.wait(); + cage3.exit_syscall(EXIT_SUCCESS); + }); + + let threadclient_unix = interface::helper_thread(move || { + let cage4 = interface::cagetable_getref(4); + assert_eq!(cage4.close_syscall(serversockfd_unix), 0); + assert_eq!(cage4.close_syscall(serversockfd), 0); + + // connect to server + assert_eq!( + cage4.connect_syscall(clientsockfd_unix, &serversocket_unix), + 0 + ); + + // send message to server + assert_eq!( + cage4.send_syscall(clientsockfd_unix, str2cbuf("test"), 4, 0), + 4 + ); + + interface::sleep(interface::RustDuration::from_millis(1)); + + // recieve message from server + let mut buf = sizecbuf(4); + let mut result: i32; + loop { + result = cage4.recv_syscall(clientsockfd_unix, buf.as_mut_ptr(), 4, 0); + if result != -libc::EINTR { + break; // if the error was EINTR, retry the syscall + } + } + assert_eq!(result, 4); + assert_eq!(cbuf2str(&buf), "test"); + + assert_eq!(cage4.close_syscall(clientsockfd_unix), 0); + cage4.exit_syscall(EXIT_SUCCESS); + }); + + let thread_pipe = interface::helper_thread(move || { + let cage5 = interface::cagetable_getref(5); + + interface::sleep(interface::RustDuration::from_millis(1)); + // send message to pipe + assert_eq!(cage5.write_syscall(pipefds.writefd, str2cbuf("test"), 4), 4); + + let mut buf = sizecbuf(5); + // wait until peer read the message + barrier_pipe_clone.wait(); + + // read the message sent by peer + assert_eq!(cage5.read_syscall(pipefds.readfd, buf.as_mut_ptr(), 5), 5); + assert_eq!(cbuf2str(&buf), "test2"); + + barrier_exit_clone3.wait(); + cage5.exit_syscall(EXIT_SUCCESS); + }); + + barrier.wait(); + + let event_list_size = 5; + let mut event_list: Vec = + vec![EpollEvent { events: 0, fd: 0 }; event_list_size]; + // acting as the server and processing the request + // Server loop to handle connections and I/O + // Check for any activity in any of the Input sockets + for _counter in 0..600 { + // epoll call + let num_events = cage.epoll_wait_syscall( + epfd, + &mut event_list, + event_list_size as i32, + Some(interface::RustDuration::ZERO), + ); + assert!(num_events >= 0); // check for error + + for event in &mut event_list[..num_events as usize] { + // Check for any activity in the input socket and if there are events ready for + // reading + if event.events & (EPOLLIN as u32) != 0 { + // If the socket returned was listener socket, then there's a new connection + if event.fd == serversockfd { + let mut sockgarbage = + interface::GenSockaddr::V4(interface::SockaddrV4::default()); + let sockfd = cage.accept_syscall(event.fd as i32, &mut sockgarbage); + assert!(sockfd > 0); + let event = interface::EpollEvent { + events: EPOLLIN as u32 | EPOLLOUT as u32, + fd: sockfd, + }; + // Error raised to indicate that the socket file descriptor couldn't be + // added to the epoll instance + assert_eq!( + cage.epoll_ctl_syscall(epfd, EPOLL_CTL_ADD, sockfd, &event), + 0 + ); + } else if event.fd == serversockfd_unix { + // unix socket + let mut sockgarbage = interface::GenSockaddr::Unix( + interface::new_sockaddr_unix(AF_UNIX as u16, "".as_bytes()), + ); + let sockfd = cage.accept_syscall(event.fd as i32, &mut sockgarbage); + assert!(sockfd > 0); + let event = interface::EpollEvent { + events: EPOLLIN as u32 | EPOLLOUT as u32, + fd: sockfd, + }; + // Error raised to indicate that the socket file descriptor couldn't be + // added to the epoll instance + assert_eq!( + cage.epoll_ctl_syscall(epfd, EPOLL_CTL_ADD, sockfd, &event), + 0 + ); + } else if event.fd == pipefds.readfd { + // pipe + let mut buf = sizecbuf(4); + // read the message from peer + assert_eq!(cage.read_syscall(pipefds.readfd, buf.as_mut_ptr(), 4), 4); + assert_eq!(cbuf2str(&buf), "test"); + + // write the message from peer + assert_eq!( + cage.write_syscall(pipefds.writefd, str2cbuf("test2"), 5) as usize, + 5 + ); + barrier_pipe.wait(); + + // pipe epoll test done + assert_eq!( + cage.epoll_ctl_syscall( + epfd, + EPOLL_CTL_DEL, + event.fd, + &EpollEvent { events: 0, fd: 0 } + ), + 0 + ); + } else { + //If the socket is in established conn., then we recv the data. If there's + // no data, then close the client socket. + let mut buf = sizecbuf(4); + let mut recvresult: i32; + loop { + // receive message from peer + recvresult = cage.recv_syscall(event.fd as i32, buf.as_mut_ptr(), 4, 0); + if recvresult != -libc::EINTR { + break; // if the error was EINTR, retry the + // syscall + } + } + if recvresult == 4 { + if cbuf2str(&buf) == "test" { + continue; + } + } else if recvresult == -libc::ECONNRESET { + // peer closed the connection + assert_eq!(cage.close_syscall(event.fd as i32), 0); + assert_eq!( + cage.epoll_ctl_syscall( + epfd, + EPOLL_CTL_DEL, + event.fd, + &EpollEvent { events: 0, fd: 0 } + ), + 0 + ); + } + } + } + if event.events & (EPOLLOUT as u32) != 0 { + // Data is sent out this socket, it's no longer ready for writing + assert_eq!( + cage.send_syscall(event.fd as i32, str2cbuf("test"), 4, 0), + 4 + ); + // remove the fd + assert_eq!( + cage.epoll_ctl_syscall( + epfd, + EPOLL_CTL_DEL, + event.fd, + &EpollEvent { events: 0, fd: 0 } + ), + 0 + ); + } + } } + assert_eq!(cage.close_syscall(serversockfd), 0); + assert_eq!(cage.close_syscall(serversockfd_unix), 0); + + // let threads exit + barrier_exit.wait(); + + threadclient1.join().unwrap(); + threadclient2.join().unwrap(); + threadclient_unix.join().unwrap(); + thread_pipe.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_writev() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let serversockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + + //making sure that the assigned fd's are valid + assert!(serversockfd > 0); + assert!(clientsockfd > 0); + let port: u16 = generate_random_port(); + + //binding to a socket + let sockaddr = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 + assert_eq!(cage.bind_syscall(serversockfd, &socket), 0); + assert_eq!(cage.listen_syscall(serversockfd, 1), 0); //we are only allowing for one client at a time + + //forking the cage to get another cage with the same information + assert_eq!(cage.fork_syscall(2), 0); + + //creating a thread for the server so that the information can be sent between + // the two threads + let thread = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + interface::sleep(interface::RustDuration::from_millis(100)); + let port: u16 = generate_random_port(); + + let mut socket2 = interface::GenSockaddr::V4(interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }); //127.0.0.1 + let sockfd = cage2.accept_syscall(serversockfd, &mut socket2); //really can only make sure that the fd is valid + assert!(sockfd > 0); + + //process the first test... + //Writing 100, then peek 100, then read 100 + let mut buf = sizecbuf(300); + assert_eq!( + cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 300, 0, &mut Some(&mut socket2)), + 300 + ); //reading the input message + + assert_eq!(cage2.close_syscall(sockfd), 0); + assert_eq!(cage2.close_syscall(serversockfd), 0); + assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); + //connect to the server + assert_eq!(cage.connect_syscall(clientsockfd, &socket), 0); - let epoll_events = [EpollEvent{ - events: 0, - fd: 0, - }; 10]; - assert_ne!(cage.epoll_wait_syscall(epoll_fd, &epoll_events, epoll_events.len() as i32, -1), -1); - let mut buffer = [0; 128]; - assert_ne!(cage.read_syscall(pipefds.readfd, buffer.as_mut_ptr(), buffer.len()), -1); + let iovec: [interface::IovecStruct; 3] = [ + interface::IovecStruct { + iov_base: str2cbuf(&"A".repeat(100)) as *mut c_void, + iov_len: 100, + }, + interface::IovecStruct { + iov_base: str2cbuf(&"B".repeat(100)) as *mut c_void, + iov_len: 100, + }, + interface::IovecStruct { + iov_base: str2cbuf(&"C".repeat(100)) as *mut c_void, + iov_len: 100, + }, + ]; - println!("parent - after read"); - io::stdout().flush().unwrap(); + assert_eq!(cage.writev_syscall(clientsockfd, iovec.as_ptr(), 3), 300); - assert_ne!(cage.close_syscall(pipefds.readfd), -1); - assert_ne!(cage.close_syscall(epoll_fd), -1); - println!("parent - after close"); - io::stdout().flush().unwrap(); + thread.join().unwrap(); - sender.join().unwrap(); - assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); lindrustfinalize(); } } diff --git a/src/tests/sys_tests.rs b/src/tests/sys_tests.rs new file mode 100644 index 00000000..f72b4f1c --- /dev/null +++ b/src/tests/sys_tests.rs @@ -0,0 +1,138 @@ +#![allow(dead_code)] //suppress warning for these functions not being used in targets other than the + // tests + +#[allow(unused_parens)] +#[cfg(test)] +pub mod sys_tests { + use super::super::*; + use crate::interface; + // use crate::safeposix::cage::{FileDescriptor::*, *}; + use crate::safeposix::{cage::*, dispatcher::*, filesystem}; + + #[test] + pub fn ut_lind_getpid() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + assert_eq!(cage.getpid_syscall(), 1); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_getppid() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + cage.fork_syscall(2); + let cage2 = interface::cagetable_getref(2); + assert_eq!(cage2.getppid_syscall(), 1); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_getuid() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + // The first call to geteuid always returns -1 + assert_eq!(cage.getuid_syscall(), -1); + // Subsequent calls return the default value + assert_eq!(cage.getuid_syscall(), DEFAULT_UID as i32); + lindrustfinalize() + } + + #[test] + pub fn ut_lind_geteuid() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + // The first call to geteuid always returns -1 + assert_eq!(cage.geteuid_syscall(), -1); + // Subsequent calls return the default value + assert_eq!(cage.geteuid_syscall(), DEFAULT_UID as i32); + lindrustfinalize() + } + + #[test] + pub fn ut_lind_getgid() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + // The first call to geteuid always returns -1 + assert_eq!(cage.getgid_syscall(), -1); + // Subsequent calls return the default value + assert_eq!(cage.getgid_syscall(), DEFAULT_GID as i32); + lindrustfinalize() + } + + #[test] + pub fn ut_lind_getegid() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + // The first call to geteuid always returns -1 + assert_eq!(cage.getegid_syscall(), -1); + // Subsequent calls return the default value + assert_eq!(cage.getegid_syscall(), DEFAULT_GID as i32); + lindrustfinalize() + } + + #[test] + pub fn ut_lind_fork() { + // Since the fork syscall is heavily tested in relation to other syscalls + // we only perform simple checks for testing the sanity of the fork syscall + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + // Spawn a new child object using the fork syscall + cage.fork_syscall(2); + // Search for the new cage object with cage_id = 2 + let child_cage = interface::cagetable_getref(2); + // Assert the parent value is the the id of the first cage object + assert_eq!(child_cage.getppid_syscall(), 1); + // Assert that the cage id of the child is the value passed in the original fork + // syscall + assert_eq!(child_cage.getuid_syscall(), -1); + assert_eq!(child_cage.getuid_syscall(), DEFAULT_UID as i32); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_exit() { + // Since exit function is heavily used and tested in other syscalls and their + // tests We only perform preliminary checks for checking the sanity of + // this syscall We don't check for cases such as exiting a cage twice - + // since the exiting process is handled by the NaCl runtime - and it + // ensures that a cage does not exit twice acquiring a lock on TESTMUTEX + // prevents other tests from running concurrently, and also performs + // clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + // Call the exit call + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_exec() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage1 = interface::cagetable_getref(1); + // Exec a new child + assert_eq!(cage1.exec_syscall(2), 0); + // Assert that the fork was correct + let child_cage = interface::cagetable_getref(2); + assert_eq!(child_cage.getuid_syscall(), -1); + assert_eq!(child_cage.getuid_syscall(), DEFAULT_UID as i32); + + lindrustfinalize(); + } +} From e244b914b91ce267dc06fc134ef5f2eeb13952a3 Mon Sep 17 00:00:00 2001 From: Chinmay Shringi <31031919+ChinmayShringi@users.noreply.github.com> Date: Tue, 8 Oct 2024 14:55:53 -0400 Subject: [PATCH 2/3] Feat update readme (#49) * feat: update readme * feat: added images --- README.md | 197 ++++++++++++++++++++++++++-- assets/comments.readme.png | Bin 0 -> 240620 bytes assets/docker.readme.png | Bin 0 -> 344406 bytes assets/network.readme.png | Bin 0 -> 77370 bytes assets/permission-denied.readme.png | Bin 0 -> 131412 bytes assets/timed-out.readme.png | Bin 0 -> 138560 bytes 6 files changed, 188 insertions(+), 9 deletions(-) create mode 100644 assets/comments.readme.png create mode 100644 assets/docker.readme.png create mode 100644 assets/network.readme.png create mode 100644 assets/permission-denied.readme.png create mode 100644 assets/timed-out.readme.png diff --git a/README.md b/README.md index 8855c085..21b86a7d 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,194 @@ More implementation details could be found at [wiki](https://github.com/Lind-Pro * [Style Guide](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Style-Guide.md) * [Testing and Debugging](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Testing-and-Debugging.md) +## Overview +This document provides a step-by-step guide to access the NYU SSH server and run Docker commands to set up and test the Lind project. If you face issues while accessing the server, troubleshooting steps are included to help you resolve them efficiently. + +## Accessing the SSH Server +### SSH Command Format +To gain access, use the following SSH command format: + +```bash +[username]@lind-server.engineering.nyu.edu +``` + +**Description**: Replace `[username]` with your NYU username to connect to the Lind server. This command will initiate a secure shell connection to the server, allowing you to work on the remote system. + +### Troubleshooting Access Issues +#### Permission Denied +- **Description**: This usually means that the password is incorrect. Please try to recall the correct password or contact seniors for assistance, or ask for help in the Slack channel. + + ![Permission Denied](assets/permission-denied.readme.png) + +#### Operation Timed Out / Unable to Resolve Host +- **Description**: This error generally means that your network is incorrect or unavailable. + + ![Operation Timed Out](assets/timed-out.readme.png) + +### Network Verification +To verify network connectivity, follow these steps: + +1. **Are you on an on-campus network?** + + ![On-campus Network](assets/network.readme.png) + +2. If connected but still unable to access, contact seniors or use the Slack channel for support. + +3. If not connected to the on-campus network, connect to VPN via the [NYU VPN Guide](https://www.nyu.edu/life/information-technology/infrastructure/network-services/vpn.html). + +## Running Docker +### Running the Docker Container +![Running Docker](assets/docker.readme.png) + +Once you have SSH access, run the Docker container with the following command: + +```bash +docker run --privileged --ipc=host --cap-add=SYS_PTRACE -it securesystemslab/lind /bin/bash +``` + +**Description**: This command starts a Docker container using the image `securesystemslab/lind`. The options used are: + +- `--privileged`: Grants extended privileges to the container. +- `--ipc=host`: Allows the container to share the host’s IPC namespace, enabling shared memory. +- `--cap-add=SYS_PTRACE`: Adds the capability to use `ptrace`, which is helpful for debugging. +- `-it`: Opens an interactive terminal session. +- `/bin/bash`: Launches a Bash shell inside the container. + +**Note**: This command will give you an interactive shell inside the Docker container where you can run other commands. + +## Next Steps After Running Docker +### Checking Git Branch and Updating +Once inside the container: + +1. **Ensure you are on the `develop` branch**. Run the following commands to check and update: + + ```bash + git branch + ``` + + **Description**: Displays the current branch. Ensure that you are on the `develop` branch. + + ```bash + git pull + ``` + + **Description**: Fetches the latest updates from the remote repository and merges them into your current branch. + +### Building Lind +1. **Update Contents**: + - Run the following command to update contents to the newest version: + + ```bash + make -2 + ``` + + **Description**: This command will ensure that all the components are updated to the latest version. The `make` command runs the instructions defined in the Makefile, and the `-2` argument here specifies a particular target or set of actions. + +2. **Build the Regular Lind Version**: + - Run the following command to build the standard version of Lind: + + ```bash + make -1 + ``` + + **Description**: This command builds the standard version of Lind, preparing it for use. + +## Running RawPOSIX +### Environment Setup for RawPOSIX +To run RawPOSIX, follow these steps: + +1. **Navigate to the project directory and set up the environment**: + + ```bash + cd src + sudo rm -rf safeposix-rust + git clone https://github.com/Lind-Project/RawPOSIX.git + mv RawPOSIX/ safeposix-rust + cd /home/lind/lind_project + make -1 + ``` + + **Description**: + - `cd src`: Change to the source directory. + - `sudo rm -rf safeposix-rust`: Remove the existing `safeposix-rust` directory (requires admin privileges). + - `git clone ...`: Clone the RawPOSIX repository from GitHub. + - `mv RawPOSIX/ safeposix-rust`: Rename the cloned directory to `safeposix-rust`. + - `cd /home/lind/lind_project`: Change to the Lind project directory. + - `make -1`: Build the project. + +### Generating Network Devices for RawPOSIX +2. **Generate network devices** required for RawPOSIX: + + ```bash + cd src/safeposix-rust + ./gen_netdev.sh + ``` + + **Description**: + - `cd src/safeposix-rust`: Change to the `safeposix-rust` directory. + - `./gen_netdev.sh`: Run the script to generate network devices. + +## Testing Suites +### Running Lind Test Suites +Navigate to the project root and run the following command: + +```bash +cd /home/lind/lind_project +make test +``` + +**Description**: This command runs the full test suite for Lind, verifying that all components are functioning as expected. + +### Running RawPOSIX Test Suites +To build and run the tests for RawPOSIX: + +```bash +cd src/safeposix-rust +cargo build +cargo test +``` + +**Description**: +- `cargo build`: Compiles the Rust code for the RawPOSIX project. +- `cargo test`: Runs the test suite for RawPOSIX to verify functionality. + +#### Running Specific Test Cases +To run a specific test case: + +```bash +cargo test +``` + +**Example**: + +```bash +cargo test ut_lind_fs_mkdir_invalid_modebits +``` + +**Description**: This command runs a specific test case, allowing you to focus on one feature or functionality at a time. + +## FAQ +### Handling Errors +1. **New error that requires a big fix**: + - Contact the team and inform the seniors. + - Open a GitHub issue to track the problem. + +2. **Encountering a smaller issue**: + - Check if an existing issue is logged. If not, create one at: [https://github.com/Lind-Project/RawPOSIX/issues/15](https://github.com/Lind-Project/RawPOSIX/issues/15). + +### Tagging for Review +- Tag two reviewers: either Alice, Nick, or Yuchen Zhang. + +### Pull Request (PR) Description +- Write a clear and concise description for each PR. +- Add comments for easier understanding. + +### Commenting on Code +- **Requirement**: Comments are required for new code to ensure others can understand it. +- **Future Improvements**: Reference the relevant GitHub issue for any future improvements. + + ![Comment Example](assets/comments.readme.png) + ## Run RustPOSIX-Rust Quick start @@ -28,15 +216,6 @@ helpful for exploration and easy testing. See reference at [Run RustPOSIX Independently](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Run-Independently.md) -## Test RustPOSIX-Rust - -Use main branch for the most stable behaviour. - -```bash -cargo build -cargo test --lib -``` - See reference at [Testing and Debugging](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Testing-and-Debugging.md) ## Development Guideline diff --git a/assets/comments.readme.png b/assets/comments.readme.png new file mode 100644 index 0000000000000000000000000000000000000000..9356eeed73e883da649902d3853486ba2c54ab97 GIT binary patch literal 240620 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

nG=jx({eSD4Zzs7U7JDgug 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 z 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 u{^uAu$hrLNA^nFz8NkpWl0hYHHs!~;c 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 z�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 literal 0 HcmV?d00001 From e44f2c03523be0257cdd8c60a226a8f903d2713f Mon Sep 17 00:00:00 2001 From: Alice Wen <40227173+Yaxuan-w@users.noreply.github.com> Date: Tue, 8 Oct 2024 14:58:46 -0400 Subject: [PATCH 3/3] Update README.md --- README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 21b86a7d..163fafac 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,4 @@ -# RustPOSIX [![Build Status](https://github.com/Lind-Project/safeposix-rust/actions/workflows/lind-selfhost.yml/badge.svg?branch=develop)](https://github.com/Lind-Project/safeposix-rust/actions/workflows/lind-selfhost.yml) - -More implementation details could be found at [wiki](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Home.md). +# RawPOSIX [![Build Status](https://github.com/Lind-Project/safeposix-rust/actions/workflows/lind-selfhost.yml/badge.svg?branch=develop)](https://github.com/Lind-Project/safeposix-rust/actions/workflows/lind-selfhost.yml) ## Contents @@ -200,7 +198,7 @@ cargo test ut_lind_fs_mkdir_invalid_modebits ![Comment Example](assets/comments.readme.png) -## Run RustPOSIX-Rust +## Run RawPOSIX-Rust Quick start Use Develop branch for the most stable behaviour. @@ -223,5 +221,3 @@ See reference at [Testing and Debugging](https://github.com/Lind-Project/lind-do * All PRs should be merged to the Develop branch * Any imports from the standard library or any crates should be done in an interface file - -More detailed guideline will be in [RustPOSIX's wiki](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Style-Guide.md) \ No newline at end of file