From 5c774e0c6732c198b53d7b41c2bc5a4d44bccb92 Mon Sep 17 00:00:00 2001 From: pranav Date: Tue, 30 Jul 2024 00:38:47 +0530 Subject: [PATCH] rebased with develop --- src/safeposix/syscalls/fs_calls.rs | 118 ++++++++++++++++++++++++----- src/tests/fs_tests.rs | 92 ++++++++++++++++++++-- 2 files changed, 186 insertions(+), 24 deletions(-) diff --git a/src/safeposix/syscalls/fs_calls.rs b/src/safeposix/syscalls/fs_calls.rs index a1fb951c..7745284d 100644 --- a/src/safeposix/syscalls/fs_calls.rs +++ b/src/safeposix/syscalls/fs_calls.rs @@ -1396,12 +1396,45 @@ impl Cage { } //------------------------------------STATFS SYSCALL------------------------------------ + /// ### Description + /// + /// `statfs_syscall` retrieves file system status information for the file + /// system containing the file specified by `path` and populates the + /// provided `databuf` with this information. + /// + /// ### Arguments + /// + /// It accepts two parameters: + /// * `path` - A string slice that specifies the file path for which file + /// system status information is to be retrieved. + /// * `databuf` - A mutable reference to a `FSData` struct where the file + /// system status will be stored. + /// + /// ### Returns + /// + /// For a successful call, the return value will be 0. On error, a negative + /// errno is returned to indicate the error. + /// + /// ### Errors + /// + /// * `ENOENT` - The file specified by `path` does not exist or the path is + /// invalid. + /// + /// ### Panics + /// + /// * There are no panics that can happen in this function. + /// + /// For more detailed description of all the commands and return values, + /// refer to the statfs man page [here](https://man7.org/linux/man-pages/man2/statfs.2.html). pub fn statfs_syscall(&self, path: &str, databuf: &mut FSData) -> i32 { + //convert the path to an absolute path of type `PathBuf` let truepath = normpath(convpath(path), self); //Walk the file tree to get inode from path if let Some(inodenum) = metawalk(truepath.as_path()) { + // won't panic since check for inode number in table is already happening in + // above 'metawalk' function let _inodeobj = FS_METADATA.inodetable.get(&inodenum).unwrap(); //populate the dev id field -- can be done outside of the helper @@ -1415,35 +1448,80 @@ impl Cage { } //------------------------------------FSTATFS SYSCALL------------------------------------ + /// ### Description + /// + /// `fstatfs_syscall` retrieves file system status information for the file + /// system containing the file specified by the file descriptor `fd` and + /// populates the provided `databuf` with this information. + /// + /// ### Arguments + /// + /// It accepts two parameters: + /// * `fd` - The file descriptor that refers to the open file. + /// * `databuf` - A mutable reference to a `FSData` struct where the file + /// system status will be stored. + /// + /// ### Returns + /// + /// For a successful call, the return value will be 0. On error, a negative + /// errno is returned to indicate the error. + /// + /// ### Errors + /// + /// * `EBADF` - The file descriptor `fd` is either invalid or it refers to a + /// socket, stream, pipe, or epoll file descriptor, which are not + /// supported by this function. + /// + /// ### Panics + /// + /// * If the file descriptor provided isn't in the appropriate range, this + /// function can panic. + /// + /// For more detailed description of all the commands and return values, + /// refer to the statfs man page [here](https://man7.org/linux/man-pages/man2/statfs.2.html). pub fn fstatfs_syscall(&self, fd: i32, databuf: &mut FSData) -> i32 { + // BUG: If the provided file descriptor is out of bounds, get_filedescriptor + // returns Err(), unwrapping on which produces a 'panic!' + // otherwise, file descriptor table entry is stored in 'checkedfd' let checkedfd = self.get_filedescriptor(fd).unwrap(); let unlocked_fd = checkedfd.read(); + if let Some(filedesc_enum) = &*unlocked_fd { //populate the dev id field -- can be done outside of the helper databuf.f_fsid = FS_METADATA.dev_id; match filedesc_enum { + // if the fd points to a normal file descriptor File(normalfile_filedesc_obj) => { + // won't panic since we have already checked that the entries exist + // in the FS_METADATA inode table let _inodeobj = FS_METADATA .inodetable .get(&normalfile_filedesc_obj.inode) .unwrap(); + // populate the databuf using a helper function return Self::_istatfs_helper(self, databuf); } + + // if the fd points to a socket, pipe, stream, or epoll file descriptor Socket(_) | Pipe(_) | Stream(_) | Epoll(_) => { return syscall_error( Errno::EBADF, "fstatfs", - "can't fstatfs on socket, stream, pipe, or epollfd", + "can't fstatfs on sockets, streams, pipes, or epoll fds", ); } } + } else { + return syscall_error(Errno::EBADF, "statfs", "invalid file descriptor"); } - return syscall_error(Errno::EBADF, "statfs", "invalid file descriptor"); } + // These values have (probably) been picked up from the previously used + // environment, and have been working fine till now for our purposes + // TODO: Figure out how to populate the databuf values properly pub fn _istatfs_helper(&self, databuf: &mut FSData) -> i32 { databuf.f_type = 0xBEEFC0DE; //unassigned databuf.f_bsize = 4096; @@ -5306,16 +5384,19 @@ impl Cage { /// ### Description /// - /// `shmget_syscall` returns the shared memory segment identifier associated with a particular `key` - /// If a key doesn't exist, shmget creates a new memory segment and attaches it to the key. - /// Traditionally if the value of the key equals `IPC_PRIVATE`, we also create a new memory segment which - /// is not associated with a key during this syscall, - /// but for our implementaion, we return an error and only create a new memory - /// segment when the IPC_CREAT flag is specified in the`shmflag` argument. + /// `shmget_syscall` returns the shared memory segment identifier associated + /// with a particular `key` If a key doesn't exist, shmget creates a new + /// memory segment and attaches it to the key. Traditionally if the + /// value of the key equals `IPC_PRIVATE`, we also create a new memory + /// segment which is not associated with a key during this syscall, + /// but for our implementaion, we return an error and only create a new + /// memory segment when the IPC_CREAT flag is specified in the`shmflag` + /// argument. /// /// ### Returns /// - /// An 32 bit integer which represens the identifier of the memory segment associated with the key + /// An 32 bit integer which represens the identifier of the memory segment + /// associated with the key /// /// ### Arguments /// @@ -5324,20 +5405,22 @@ impl Cage { /// `shmflag` : mode flags which indicate whether to create a new key or not /// The `shmflag` is composed of the following /// * IPC_CREAT - specify that the system call creates a new segment - /// * IPC_EXCL - this flag is used with IPC_CREAT to cause this function to fail when IPC_CREAT is also used - /// and the key passed has a memory segment associated with it. + /// * IPC_EXCL - this flag is used with IPC_CREAT to cause this function to + /// fail when IPC_CREAT is also used and the key passed has a memory + /// segment associated with it. /// /// ### Errors /// /// * ENOENT : the key equals the `IPC_PRIVATE` constant - /// * EEXIST : key exists and yet either `IPC_CREAT` or `IPC_EXCL` are passed as flags + /// * EEXIST : key exists and yet either `IPC_CREAT` or `IPC_EXCL` are + /// passed as flags /// * ENOENT : key did not exist and the `IPC_CREAT` flag was not passed - /// * EINVAL : the size passed was less than the minimum size of segment or greater than the maximum possible size + /// * EINVAL : the size passed was less than the minimum size of segment or + /// greater than the maximum possible size /// /// ### Panics /// /// There are no cases where the function directly panics - /// pub fn shmget_syscall(&self, key: i32, size: usize, shmflg: i32) -> i32 { //Check if the key passed equals the IPC_PRIVATE flag if key == IPC_PRIVATE { @@ -5349,7 +5432,8 @@ impl Cage { // data of the shm table let metadata = &SHM_METADATA; - // Check if there exists a memory segment associated with the key passed as argument + // Check if there exists a memory segment associated with the key passed as + // argument match metadata.shmkeyidtable.entry(key) { // If there exists a memory segment at that key interface::RustHashEntry::Occupied(occupied) => { @@ -5375,8 +5459,8 @@ impl Cage { ); } - // If memory segment doesn't exist and IPC_CREAT was specified - we create a new memory segment - // Check if the size passed is a valid value + // If memory segment doesn't exist and IPC_CREAT was specified - we create a new + // memory segment Check if the size passed is a valid value if (size as u32) < SHMMIN || (size as u32) > SHMMAX { return syscall_error( Errno::EINVAL, diff --git a/src/tests/fs_tests.rs b/src/tests/fs_tests.rs index 0fba74b2..7e87ec0f 100644 --- a/src/tests/fs_tests.rs +++ b/src/tests/fs_tests.rs @@ -4034,7 +4034,6 @@ pub mod fs_tests { assert_eq!(cage.close_syscall(fd), 0); assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); - lindrustfinalize(); } @@ -4048,7 +4047,8 @@ pub mod fs_tests { 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 + // 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) @@ -4060,10 +4060,12 @@ pub mod fs_tests { -(Errno::ENOENT as i32) ); - // Check if the function returns a correct shmid upon asking with a key that we know exists + // 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 + // 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) @@ -4408,7 +4410,7 @@ pub mod fs_tests { assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); lindrustfinalize(); } - + #[test] pub fn ut_lind_fs_stat_syscall_tests() { // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, @@ -4477,7 +4479,6 @@ pub mod fs_tests { 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; @@ -4522,7 +4523,7 @@ pub mod fs_tests { let socket = interface::GenSockaddr::Unix(sockaddr); assert_eq!(cage.bind_syscall(socketfd, &socket), 0); - // Errno::EOPNOTSUPP : -95 + // Errno::EOPNOTSUPP : -95 assert_eq!(cage.fstat_syscall(socketfd, &mut statdata), -95); // Clean up @@ -4533,4 +4534,81 @@ pub mod fs_tests { 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(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); + + // 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; + } }