Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
50 changes: 44 additions & 6 deletions src/safeposix/syscalls/fs_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2042,38 +2042,76 @@ impl Cage {
}

//------------------------------------IOCTL SYSCALL------------------------------------
//Description
Comment thread
ve1nard marked this conversation as resolved.
Outdated
//ioctl manipulates the underlying device parameters of special files. In particular, it is used as a way
//for user-space applications to interface with device drivers.
Comment thread
ve1nard marked this conversation as resolved.
Outdated

//Function arguments
//The function accepts three arguments:
//1. fd - an open file descriptor that refers to a device.
//2. request - the control function to be performed. The set of valid request values depends entirely on the device
// being addressed. MEDIA_IOC_DEVICE_INFO is an example of an ioctl control function to query device
// information that all media devices must support.
//3. ptrunion - additional information needed by the addressed device to perform the selected control function.
// In the example of MEDIA_IOC_DEVICE_INFO request, a valid ptrunion value is a pointer to a struct
// media_device_info, from which the device information is obtained.

//Return values
//Upon successful completion, a value other than -1 that depends on the selected control function is returned.
//In case of a failure, -1 is returned with errno set to a particular value, like EBADF, EINVAL, etc.
Comment thread
ve1nard marked this conversation as resolved.
Outdated

//To learn more about the syscall, control functions applicable to all the devices, and possible error values,
//see https://man.openbsd.org/ioctl

pub fn ioctl_syscall(&self, fd: i32, request: u32, ptrunion: IoctlPtrUnion) -> 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 mut unlocked_fd = checkedfd.write();
//if a table descriptor entry is non-empty, a valid request is performed
if let Some(filedesc_enum) = &mut *unlocked_fd {
//For now, the only implemented control function is FIONBIO command used with sockets
match request {
//for FIONBIO, 'ptrunion' stores a pointer to an integer. If the integer is 0, the socket's
//nonblocking I/O is cleared. Otherwise, the socket is set for nonblocking I/O
FIONBIO => {
//if 'ptrunion' stores a Null pointer, a 'Bad address' error is returned
//otheriwse, the integer value stored in that address is returned and saved into 'arg_result'
let arg_result = interface::get_ioctl_int(ptrunion);
//matching the tuple and passing in filedesc_enum
match (arg_result, filedesc_enum) {
(Err(arg_result), ..)=> {
return arg_result; //syscall_error
return arg_result;
}
//since FIONBIO command is used with sockets, we need to make sure that the provided
//file descriptor addresses a socket
//otherwise, a 'Not a typewriter' error designating that the specified command
//is only applicable to sockets is returned
(Ok(arg_result), Socket(ref mut sockfdobj)) => {
let sock_tmp = sockfdobj.handle.clone();
let mut sockhandle = sock_tmp.write();

let flags = &mut sockfdobj.flags;
let arg: i32 = arg_result;
let mut ioctlret = 0;

if arg == 0 { //clear non-blocking I/O
//clearing nonblocking I/O on the socket if the integer is 0
if arg == 0 {
*flags &= !O_NONBLOCK;
//libc::fcntl is called under the hood with F_SETFL command and 0 as an argument
//to set blocking I/O, and the result of the call is stored in ioctlret
if let Some(ins) = &mut sockhandle.innersocket {

ioctlret = ins.set_blocking();
}
} else { //set for non-blocking I/O
} else {
*flags |= O_NONBLOCK;
//libc::fcntl is called under the hood with F_SETFL command ans O_NONBLOCK as an argument
//to set nonblocking I/O, and the result of the call is stored in ioctlret
if let Some(ins) = &mut sockhandle.innersocket {
ioctlret = ins.set_nonblocking();
}
}
//if ioctlret is negative, it means that the call to fcntl returned with an error
if ioctlret < 0 {
match Errno::from_discriminant(interface::get_errno()) {
Ok(i) => {return syscall_error(i, "ioctl", "The libc call to ioctl failed!");},
Expand Down
72 changes: 50 additions & 22 deletions src/tests/fs_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ pub mod fs_tests {
ut_lind_fs_fcntl_valid_args();
ut_lind_fs_fcntl_invalid_args();
ut_lind_fs_fcntl_dup();
ut_lind_fs_ioctl();
ut_lind_fs_ioctl_valid_args();
ut_lind_fs_ioctl_invalid_args();
ut_lind_fs_fdflags();
ut_lind_fs_file_link_unlink();
ut_lind_fs_file_lseek_past_end();
Expand Down Expand Up @@ -525,48 +526,75 @@ pub mod fs_tests {
}


pub fn ut_lind_fs_ioctl() {
pub fn ut_lind_fs_ioctl_valid_args() {
lindrustinit(0);
let cage = interface::cagetable_getref(1);

//setting up two integer values (a zero value to test clearing nonblocking I/O behavior
//and a non-zero value to test setting nonblocking I/O behavior)
let mut arg0: i32 = 0;
let mut arg1: i32 = 1;

//ioctl requires a pointer to an integer to be passed with FIONBIO command
let union0: IoctlPtrUnion = IoctlPtrUnion { int_ptr: &mut arg0 };
let union1: IoctlPtrUnion = IoctlPtrUnion { int_ptr: &mut arg1 };

let sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0);
let filefd = cage.open_syscall("/ioctl_file", O_CREAT | O_EXCL, S_IRWXA);

//try to use FIONBIO for a non-socket
assert_eq!(
cage.ioctl_syscall(filefd, FIONBIO, union0),
-(Errno::ENOTTY as i32)
);

//clear the O_NONBLOCK flag
//calling ioctl with FIONBIO command and a pointer to a zero-valued integer
//to clear the socket's nonblocking I/O, and checking if the flag was correctly set
assert_eq!(cage.ioctl_syscall(sockfd, FIONBIO, union0), 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
//calling ioctl with FIONBIO command and a pointer to a non-zero-valued integer
//to set the socket's nonblocking I/O, and checking if the flag was correctly set
assert_eq!(cage.ioctl_syscall(sockfd, FIONBIO, union1), 0);
assert_eq!(cage.fcntl_syscall(sockfd, F_GETFL, 0) & O_NONBLOCK, O_NONBLOCK);

//checking to see if the flag was updated
assert_eq!(
cage.fcntl_syscall(sockfd, F_GETFL, 0) & O_NONBLOCK,
O_NONBLOCK
);
assert_eq!(cage.close_syscall(sockfd), 0);

//clear the O_NONBLOCK flag
assert_eq!(cage.ioctl_syscall(sockfd, FIONBIO, union0), 0);
assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS);
lindrustfinalize();
}

//checking to see if the flag was updated
assert_eq!(cage.fcntl_syscall(sockfd, F_GETFL, 0) & O_NONBLOCK, 0);
pub fn ut_lind_fs_ioctl_invalid_args() {
lindrustinit(0);
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)
let mut arg0: i32 = 0;
let mut arg1: i32 = 1;

//ioctl requires a pointer to an integer to be passed with FIONBIO command
let union0: IoctlPtrUnion = IoctlPtrUnion { int_ptr: &mut arg0 };
let union1: IoctlPtrUnion = IoctlPtrUnion { int_ptr: &mut arg1 };

let sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0);
let filefd = cage.open_syscall("/ioctl_file", 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
assert_eq!(cage.ioctl_syscall(filefd, FIONBIO, union0), -(Errno::ENOTTY as i32));
assert_eq!(cage.ioctl_syscall(filefd, FIONBIO, union1), -(Errno::ENOTTY as i32));
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), -(Errno::EINVAL as i32));

//calling ioctl with FIONBIO command and a null pointer
//should return a 'Bad address' error
let null_ptr: *mut i32 = std::ptr::null_mut();
let union_null: IoctlPtrUnion = IoctlPtrUnion { int_ptr: null_ptr };
assert_eq!(cage.ioctl_syscall(sockfd, FIONBIO, union_null),-(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(EXIT_SUCCESS), EXIT_SUCCESS);
lindrustfinalize();
Expand Down