Skip to content

Commit c54156f

Browse files
ve1nardlindJustinCappos
authored
Fcntl_syscall updates (#259)
* updated comments for fnctl_syscall * implemented checking for out-of-bounds file descriptor and tested it * finished the unit tests set for fcntl_syscall * Update src/safeposix/syscalls/fs_calls.rs Co-authored-by: Justin Cappos <justincappos@gmail.com> * changed the code according to the comments * added a 'TO DO' comment for F_GETOWN and F_SETOWN commands * added the description for 'fcntl' syscall * reverted the part with get_filedescriptor to the original state and removed an extra whitespace * added a 'BUG' prefix for the 'out -f -ounds ile descriptorr issue and commented out thefailing test * removed the failing test --------- Co-authored-by: lind <lind@nyu.edu> Co-authored-by: Justin Cappos <justincappos@gmail.com>
1 parent a684d46 commit c54156f

File tree

2 files changed

+109
-30
lines changed

2 files changed

+109
-30
lines changed

src/safeposix/syscalls/fs_calls.rs

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,16 +1928,32 @@ impl Cage {
19281928
}
19291929

19301930
//------------------------------------FCNTL SYSCALL------------------------------------
1931+
1932+
//fcntl performs operations, like returning or setting file status flags,
1933+
//duplicating a file descriptor, etc., on an open file descriptor
1934+
//it accepts three parameters: fd - an open file descriptor, cmd - an operation to be performed on fd,
1935+
//and arg - an optional argument (whether or not arg is required is determined by cmd)
1936+
//for a successful call, the return value depends on the operation and can be one of: zero, the new file descriptor,
1937+
//value of file descriptor flags, value of status flags, etc.
1938+
//for more detailed description of all the commands and return values, see
1939+
//https://linux.die.net/man/2/fcntl
19311940

19321941
pub fn fcntl_syscall(&self, fd: i32, cmd: i32, arg: i32) -> i32 {
1942+
//BUG
1943+
//if the provided file descriptor is out of bounds, get_filedescriptor returns Err(),
1944+
//unwrapping on which produces a 'panic!'
1945+
//otherwise, file descriptor table entry is stored in 'checkedfd'
19331946
let checkedfd = self.get_filedescriptor(fd).unwrap();
19341947
let mut unlocked_fd = checkedfd.write();
1935-
if let Some(filedesc_enum) = &mut *unlocked_fd {
1948+
if let Some(filedesc_enum) = &mut *unlocked_fd {
1949+
//'flags' consists of bitwise-or'd access mode, file creation, and file status flags
1950+
//to retrieve a particular flag, it can bitwise-and'd with 'flags'
19361951
let flags = match filedesc_enum {
19371952
Epoll(obj) => &mut obj.flags,
19381953
Pipe(obj) => &mut obj.flags,
19391954
Stream(obj) => &mut obj.flags,
19401955
File(obj) => &mut obj.flags,
1956+
//not clear why running F_SETFL on Socket type requires special treatment
19411957
Socket(ref mut sockfdobj) => {
19421958
if cmd == F_SETFL && arg >= 0 {
19431959
let sock_tmp = sockfdobj.handle.clone();
@@ -1946,10 +1962,10 @@ impl Cage {
19461962
if let Some(ins) = &mut sockhandle.innersocket {
19471963
let fcntlret;
19481964
if arg & O_NONBLOCK == O_NONBLOCK {
1949-
//set for non-blocking I/O
1965+
//set non-blocking I/O
19501966
fcntlret = ins.set_nonblocking();
19511967
} else {
1952-
//clear non-blocking I/O
1968+
//set blocking I/O
19531969
fcntlret = ins.set_blocking();
19541970
}
19551971
if fcntlret < 0 {
@@ -1974,41 +1990,54 @@ impl Cage {
19741990
//matching the tuple
19751991
match (cmd, arg) {
19761992
//because the arg parameter is not used in certain commands, it can be anything (..)
1993+
//F_GETFD returns file descriptor flags only, meaning that access mode flags
1994+
//and file status flags are excluded
1995+
//F_SETFD is used to set file descriptor flags only, meaning that any changes to access mode flags
1996+
//or file status flags should be ignored
1997+
//currently, O_CLOEXEC is the only defined file descriptor flag, thus only this flag is
1998+
//masked when using F_GETFD or F_SETFD
19771999
(F_GETFD, ..) => *flags & O_CLOEXEC,
1978-
// set the flags but make sure that the flags are valid
19792000
(F_SETFD, arg) if arg >= 0 => {
19802001
if arg & O_CLOEXEC != 0 {
2002+
//if O_CLOEXEC flag is set to 1 in 'arg', 'flags' is updated by setting its O_CLOEXEC bit to 1
19812003
*flags |= O_CLOEXEC;
19822004
} else {
2005+
//if O_CLOEXEC flag is set to 0 in 'arg', 'flags' is updated by setting its O_CLOEXEC bit to 0
19832006
*flags &= !O_CLOEXEC;
19842007
}
19852008
0
19862009
}
2010+
//F_GETFL should return file access mode and file status flags, which means that
2011+
//file creation flags should be masked out
19872012
(F_GETFL, ..) => {
1988-
//for get, we just need to return the flags
1989-
*flags & !O_CLOEXEC
2013+
*flags & !(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC)
19902014
}
2015+
//F_SETFL is used to set file status flags, thus any changes to file access mode and file
2016+
//creation flags should be ignored (see F_SETFL command in the man page for fcntl for the reference)
19912017
(F_SETFL, arg) if arg >= 0 => {
1992-
*flags |= arg;
2018+
//valid changes are extracted by ignoring changes to file access mode and file creation flags
2019+
let valid_changes = arg & !(O_RDWRFLAGS | O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
2020+
//access mode and creation flags are extracted and other flags are set to 0 to update them
2021+
let acc_and_creation_flags = *flags & (O_RDWRFLAGS | O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
2022+
//valid changes are combined with the old file access mode and file creation flags
2023+
*flags = valid_changes | acc_and_creation_flags;
19932024
0
19942025
}
19952026
(F_DUPFD, arg) if arg >= 0 => self._dup2_helper(&filedesc_enum, arg, false),
1996-
//TO DO: implement. this one is saying get the signals
2027+
//TO DO: F_GETOWN and F_SETOWN commands are not implemented yet
19972028
(F_GETOWN, ..) => {
1998-
0 //TO DO: traditional SIGIO behavior
2029+
0
19992030
}
20002031
(F_SETOWN, arg) if arg >= 0 => {
2001-
0 //this would return the PID if positive and the process group if negative,
2002-
//either way do nothing and return success
2032+
0
20032033
}
2004-
_ => syscall_error(
2005-
Errno::EINVAL,
2006-
"fcntl",
2007-
"Arguments provided do not match implemented parameters",
2008-
),
2034+
_ => {
2035+
let err_msg = format!("Arguments pair ({}, {}) does not match implemented parameters", cmd, arg);
2036+
syscall_error(Errno::EINVAL, "fcntl", &err_msg)
2037+
},
20092038
}
20102039
} else {
2011-
syscall_error(Errno::EBADF, "fcntl", "Invalid file descriptor")
2040+
syscall_error(Errno::EBADF, "fcntl", "File descriptor is out of range")
20122041
}
20132042
}
20142043

src/tests/fs_tests.rs

Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ pub mod fs_tests {
2020
ut_lind_fs_dir_multiple();
2121
ut_lind_fs_dup();
2222
ut_lind_fs_dup2();
23-
ut_lind_fs_fcntl();
23+
ut_lind_fs_fcntl_valid_args();
24+
ut_lind_fs_fcntl_invalid_args();
25+
ut_lind_fs_fcntl_dup();
2426
ut_lind_fs_ioctl();
2527
ut_lind_fs_fdflags();
2628
ut_lind_fs_file_link_unlink();
@@ -446,35 +448,83 @@ pub mod fs_tests {
446448
lindrustfinalize();
447449
}
448450

449-
pub fn ut_lind_fs_fcntl() {
451+
pub fn ut_lind_fs_fcntl_valid_args() {
450452
lindrustinit(0);
451453
let cage = interface::cagetable_getref(1);
452454

453455
let sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0);
454-
let filefd = cage.open_syscall("/fcntl_file", O_CREAT | O_EXCL, S_IRWXA);
456+
let filefd = cage.open_syscall("/fcntl_file_1", O_CREAT | O_EXCL, S_IRWXA);
455457

456-
//set the setfd flag
458+
//changing O_CLOEXEC file descriptor flag and checking if it was correctly set
457459
assert_eq!(cage.fcntl_syscall(sockfd, F_SETFD, O_CLOEXEC), 0);
458-
459-
//checking to see if the wrong flag was set or not
460460
assert_eq!(cage.fcntl_syscall(sockfd, F_GETFD, 0), O_CLOEXEC);
461461

462-
//let's get some more flags on the filefd
463-
assert_eq!(
464-
cage.fcntl_syscall(filefd, F_SETFL, O_RDONLY | O_NONBLOCK),
465-
0
466-
);
467-
468-
//checking if the flags are updated...
462+
//changing the file access mode to read-only, enabling the
463+
//O_NONBLOCK file status flag, and checking if they were correctly set
464+
assert_eq!(cage.fcntl_syscall(filefd, F_SETFL, O_RDONLY | O_NONBLOCK), 0);
469465
assert_eq!(cage.fcntl_syscall(filefd, F_GETFL, 0), 2048);
470466

467+
//when provided with 'F_GETFD' or 'F_GETFL' command, 'arg' should be ignored, thus even
468+
//negative arg values should produce nomal behavior
469+
assert_eq!(cage.fcntl_syscall(sockfd, F_GETFD, -132), O_CLOEXEC);
470+
assert_eq!(cage.fcntl_syscall(filefd, F_GETFL, -1998), 2048);
471+
471472
assert_eq!(cage.close_syscall(filefd), 0);
472473
assert_eq!(cage.close_syscall(sockfd), 0);
473474

474475
assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS);
475476
lindrustfinalize();
476477
}
477478

479+
pub fn ut_lind_fs_fcntl_invalid_args(){
480+
lindrustinit(0);
481+
let cage = interface::cagetable_getref(1);
482+
let filefd = cage.open_syscall("/fcntl_file_2", O_CREAT | O_EXCL, S_IRWXA);
483+
//when presented with a nonexistent command, 'Invalid Argument' error should be thrown
484+
//29 is an arbitrary number that does not correspond to any of the defined 'fcntl' commands
485+
assert_eq!(cage.fcntl_syscall(filefd, 29, 0), -(Errno::EINVAL as i32));
486+
//when a negative arg is provided with F_SETFD, F_SETFL, or F_DUPFD,
487+
//Invalid Argument' error should be thrown as well
488+
assert_eq!(cage.fcntl_syscall(filefd, F_SETFD, -5), -(Errno::EINVAL as i32));
489+
assert_eq!(cage.fcntl_syscall(filefd, F_SETFL, -5), -(Errno::EINVAL as i32));
490+
assert_eq!(cage.fcntl_syscall(filefd, F_DUPFD, -5), -(Errno::EINVAL as i32));
491+
492+
assert_eq!(cage.close_syscall(filefd), 0);
493+
494+
assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS);
495+
lindrustfinalize();
496+
}
497+
498+
pub fn ut_lind_fs_fcntl_dup(){
499+
lindrustinit(0);
500+
let cage = interface::cagetable_getref(1);
501+
502+
let filefd1 = cage.open_syscall("/fcntl_file_4", O_CREAT | O_EXCL | O_RDWR, S_IRWXA);
503+
//on success, returning the new file descriptor greater than or equal to 100
504+
//and different from the original file descriptor
505+
let filefd2 = cage.fcntl_syscall(filefd1, F_DUPFD, 100);
506+
assert!(filefd2 >= 100 && filefd2 != filefd1);
507+
508+
//to check if both file descriptors refer to the same fie, we can write into a file
509+
//using one file descriptor, read from the file using another file descriptor,
510+
//and make sure that the contents are the same
511+
let mut temp_buffer = sizecbuf(9);
512+
assert_eq!(cage.write_syscall(filefd1, str2cbuf("Test text"), 9), 9);
513+
assert_eq!(cage.read_syscall(filefd2, temp_buffer.as_mut_ptr(), 9), 9);
514+
assert_eq!(cbuf2str(&temp_buffer), "Test text");
515+
516+
//file status flags are shared by duplicated file descriptors resulting from
517+
//a single opening of the file
518+
assert_eq!(cage.fcntl_syscall(filefd1, F_GETFL, 0), cage.fcntl_syscall(filefd2, F_GETFL, 0));
519+
520+
assert_eq!(cage.close_syscall(filefd1), 0);
521+
assert_eq!(cage.close_syscall(filefd2), 0);
522+
523+
assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS);
524+
lindrustfinalize();
525+
}
526+
527+
478528
pub fn ut_lind_fs_ioctl() {
479529
lindrustinit(0);
480530
let cage = interface::cagetable_getref(1);

0 commit comments

Comments
 (0)