Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 101 additions & 17 deletions src/safeposix/syscalls/fs_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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
///
Expand All @@ -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 {
Expand All @@ -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) => {
Expand All @@ -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,
Expand Down
92 changes: 85 additions & 7 deletions src/tests/fs_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -4533,4 +4534,81 @@ pub mod fs_tests {
lindrustfinalize();
Comment thread
pranav-bhatt marked this conversation as resolved.
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;
}
}