Skip to content

Commit c830d62

Browse files
ve1nardlind
andauthored
chdir_syscall() and fchdir_syscall() updates (#294)
* updated syscall description * removed refcount and finished the description * testing chdir * new tests for chdir and fchdir * formatted * removed decrementing refcount for cwd * fixed according to the review * merged develop and fixed typos * fixed according to the review --------- Co-authored-by: lind <lind@nyu.edu>
1 parent 01105d9 commit c830d62

File tree

4 files changed

+235
-52
lines changed

4 files changed

+235
-52
lines changed

src/safeposix/filesystem.rs

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -636,22 +636,3 @@ pub fn incref_root() {
636636
panic!("Root directory inode was not a directory");
637637
}
638638
}
639-
640-
pub fn decref_dir(cwd_container: &interface::RustPathBuf) {
641-
if let Some(cwdinodenum) = metawalk(&cwd_container) {
642-
if let Inode::Dir(ref mut cwddir) = *(FS_METADATA.inodetable.get_mut(&cwdinodenum).unwrap())
643-
{
644-
cwddir.refcount -= 1;
645-
646-
//if the directory has been removed but this cwd was the last open handle to it
647-
if cwddir.refcount == 0 && cwddir.linkcount == 0 {
648-
FS_METADATA.inodetable.remove(&cwdinodenum);
649-
}
650-
} else {
651-
panic!("Cage had a cwd that was not a directory!");
652-
}
653-
} else {
654-
panic!("Cage had a cwd which did not exist!");
655-
} //we probably want to handle this case, maybe cwd should be an inode
656-
// number?? Not urgent
657-
}

src/safeposix/syscalls/fs_calls.rs

Lines changed: 104 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2381,15 +2381,59 @@ impl Cage {
23812381
}
23822382
}
23832383

2384-
//------------------------------------FCHDIR SYSCALL------------------------------------
2384+
/// ### Description
2385+
///
2386+
/// The `fchdir_syscall()` function changes the current working
2387+
/// directory of the calling process to the directory specified
2388+
/// by an open file descriptor `fd`.
2389+
///
2390+
/// ### Arguments
2391+
///
2392+
/// The `fchdir_syscall()` accepts one argument:
2393+
/// * `fd` - an open file descriptor that specifies the directory
2394+
/// to which we want to change the current working directory.
2395+
///
2396+
/// ### Returns
2397+
///
2398+
/// Upon successful completion, zero is returned.
2399+
/// In case of a failure, an error is returned, and `errno` is set depending
2400+
/// on the error, e.g. `ENOTDIR`, `EBADF`, etc.
2401+
///
2402+
/// ### Errors
2403+
///
2404+
/// * `EBADF` - fd is not a valid file descriptor.
2405+
/// * `ENOTDIR` - the open file descriptor fildes does not refer to a directory.
2406+
/// Other errors, like `EACCES`, `ENOMEM`, etc. are not supported.
2407+
///
2408+
/// ### Panics
2409+
///
2410+
/// * invalid or out-of-bounds file descriptor, calling unwrap() on which
2411+
/// causes a panic.
2412+
///
2413+
/// To learn more about the syscall and possible error values, see
2414+
/// [fchdir(2)](https://linux.die.net/man/2/fchdir)
23852415
23862416
pub fn fchdir_syscall(&self, fd: i32) -> i32 {
2417+
//BUG
2418+
//if the provided file descriptor is out of bounds, get_filedescriptor returns
2419+
//Err(), unwrapping on which produces a 'panic!'
2420+
//otherwise, file descriptor table entry is stored in 'checkedfd'
23872421
let checkedfd = self.get_filedescriptor(fd).unwrap();
23882422
let unlocked_fd = checkedfd.read();
2389-
2423+
//If a table descriptor entry corresponds to a file, we check if it is
2424+
//a directory file type. If it is not, we return `A component of path is
2425+
//not a directory` error.
2426+
//If it is one of the special file types, we return `Cannot change working
2427+
//directory on this file descriptor` error.
2428+
//Finally, if it does not correspond to any file type, we return `Invalid file
2429+
//descriptor` error.
23902430
let path_string = match &*unlocked_fd {
2431+
None => return syscall_error(Errno::EBADF, "fchdir", "invalid file descriptor"),
23912432
Some(File(normalfile_filedesc_obj)) => {
23922433
let inodenum = normalfile_filedesc_obj.inode;
2434+
//`pathnamefrominodenum` resolves the absolute path of a directory
2435+
//from its inode and returns None in case any of the path components
2436+
//is not a directory
23932437
match pathnamefrominodenum(inodenum) {
23942438
Some(name) => name,
23952439
None => {
@@ -2403,31 +2447,76 @@ impl Cage {
24032447
}
24042448
Some(_) => {
24052449
return syscall_error(
2406-
Errno::EACCES,
2450+
Errno::ENOTDIR,
24072451
"fchdir",
2408-
"cannot change working directory on this file descriptor",
2452+
"the file descriptor does not refer to a directory",
24092453
)
2410-
}
2411-
None => return syscall_error(Errno::EBADF, "fchdir", "invalid file descriptor"),
2454+
},
24122455
};
2413-
2456+
//Obtain the write lock on the current working directory of the cage
2457+
//and change it to the new directory
24142458
let mut cwd_container = self.cwd.write();
2415-
2416-
*cwd_container = interface::RustRfc::new(convpath(path_string.as_str()));
2459+
*cwd_container = interface::RustRfc::new(normpath(convpath(path_string.as_str()), self));
24172460

24182461
0 // fchdir success
24192462
}
24202463

2421-
//------------------------------------CHDIR SYSCALL------------------------------------
2464+
/// ### Description
2465+
///
2466+
/// The `chdir_syscall()` function changes the current working
2467+
/// directory of the calling process to the directory specified
2468+
/// in `path`, which can be an absolute or a relative pathname.
2469+
///
2470+
/// ### Arguments
2471+
///
2472+
/// The `chdir_syscall()` accepts one argument:
2473+
/// * `path` - the pathname, to which the current working
2474+
/// directory shall be changed.
2475+
///
2476+
/// ### Returns
2477+
///
2478+
/// Upon successful completion, zero is returned.
2479+
/// In case of a failure, an error is returned, and `errno` is set depending
2480+
/// on the error, e.g. EACCES, ENOENT, etc.
2481+
///
2482+
/// ### Errors
2483+
///
2484+
/// * `ENOTDIR` - a component of `path` is not a directory.
2485+
/// * `ENOENT` - the directory specified in path does not exist.
2486+
/// Other errors, like `EACCES`, `ENOMEM`, etc. are not supported.
2487+
///
2488+
/// ### Panics
2489+
///
2490+
/// * when the previous working directory does not exist or does not
2491+
/// have the directory file type flag, the function panics
2492+
///
2493+
/// To learn more about the syscall and possible error values, see
2494+
/// [chdir(2)](https://man7.org/linux/man-pages/man2/chdir.2.html)
24222495
24232496
pub fn chdir_syscall(&self, path: &str) -> i32 {
2497+
//Convert the provided pathname into an absolute path without `.` or `..`
2498+
//components.
24242499
let truepath = normpath(convpath(path), self);
2425-
//Walk the file tree to get inode from path
2500+
//Perfrom a walk down the file tree starting from the root directory to
2501+
//obtain an inode number of the file whose pathname was specified.
2502+
//`None` is returned if one of the following occurs while moving down
2503+
//the tree:
2504+
//1. Accessing a child of a non-directory inode
2505+
//2. Accessing a child of a nonexistent parent directory
2506+
//3. Accessing a nonexistent child
2507+
//4. Accessing an unexpected component, like `.` or `..` directory reference.
2508+
//In this case, `The file does not exist` error is returned.
2509+
//Otherwise, a `Some()` option containing the inode number is returned.
24262510
if let Some(inodenum) = metawalk(&truepath) {
2427-
if let Inode::Dir(ref mut dir) = *(FS_METADATA.inodetable.get_mut(&inodenum).unwrap()) {
2428-
//increment refcount of new cwd inode to ensure that you can't remove a
2429-
// directory while it is the cwd of a cage
2430-
dir.refcount += 1;
2511+
//A sanity check to make sure that the last component of the
2512+
//specified path is indeed a directory
2513+
if let Inode::Dir(ref mut _dir) = *(FS_METADATA.inodetable.get_mut(&inodenum).unwrap())
2514+
{
2515+
//Obtain the write lock on the current working directory of the cage
2516+
//and change it to the new directory
2517+
let mut cwd_container = self.cwd.write();
2518+
*cwd_container = interface::RustRfc::new(truepath);
2519+
0 //chdir has succeeded!;
24312520
} else {
24322521
return syscall_error(
24332522
Errno::ENOTDIR,
@@ -2442,15 +2531,6 @@ impl Cage {
24422531
"the directory referred to in path does not exist",
24432532
);
24442533
}
2445-
//at this point, syscall isn't an error
2446-
let mut cwd_container = self.cwd.write();
2447-
2448-
//decrement refcount of previous cwd's inode, to allow it to be removed if no
2449-
// cage has it as cwd
2450-
decref_dir(&*cwd_container);
2451-
2452-
*cwd_container = interface::RustRfc::new(truepath);
2453-
0 //chdir has succeeded!;
24542534
}
24552535

24562536
///##------------------------------------DUP & DUP2 SYSCALLS------------------------------------

src/safeposix/syscalls/sys_calls.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ use super::net_constants::*;
3939
use super::sys_constants::*;
4040
use crate::interface;
4141
use crate::safeposix::cage::{FileDescriptor::*, *};
42-
use crate::safeposix::filesystem::{decref_dir, metawalk, Inode, FS_METADATA};
42+
use crate::safeposix::filesystem::{metawalk, Inode, FS_METADATA};
4343
use crate::safeposix::net::NET_METADATA;
4444
use crate::safeposix::shm::SHM_METADATA;
4545

@@ -496,7 +496,6 @@ impl Cage {
496496

497497
//get file descriptor table into a vector
498498
let cwd_container = self.cwd.read();
499-
decref_dir(&*cwd_container);
500499

501500
//may not be removable in case of lindrustfinalize, we don't unwrap the remove
502501
// result

src/tests/fs_tests.rs

Lines changed: 130 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -343,28 +343,151 @@ pub mod fs_tests {
343343
}
344344

345345
#[test]
346-
pub fn ut_lind_fs_dir_chdir() {
346+
pub fn ut_lind_fs_chdir_valid_args() {
347347
//acquiring a lock on TESTMUTEX prevents other tests from running concurrently,
348348
// and also performs clean env setup
349349
let _thelock = setup::lock_and_init();
350350

351351
let cage = interface::cagetable_getref(1);
352352

353-
//testing the ability to make and change to directories
353+
//Testing the ability to make and change to directories
354+
//using absolute and relative and `..` reference
354355

355356
assert_eq!(cage.mkdir_syscall("/subdir1", S_IRWXA), 0);
356357
assert_eq!(cage.mkdir_syscall("/subdir1/subdir2", S_IRWXA), 0);
357-
assert_eq!(cage.mkdir_syscall("/subdir1/subdir2/subdir3", 0), 0);
358358

359-
assert_eq!(cage.access_syscall("subdir1", F_OK), 0);
359+
//Changing to a new current working directory, and then obtaining
360+
//the current working directory using `getcwd_syscall()` to see
361+
//if it was correctly changed
360362
assert_eq!(cage.chdir_syscall("subdir1"), 0);
363+
let mut buf1 = vec![0u8; 9];
364+
let bufptr1: *mut u8 = &mut buf1[0];
365+
assert_eq!(cage.getcwd_syscall(bufptr1, 9), 0);
366+
assert_eq!(std::str::from_utf8(&buf1).unwrap(), "/subdir1\0");
361367

362-
assert_eq!(cage.access_syscall("subdir2", F_OK), 0);
368+
assert_eq!(cage.chdir_syscall("/subdir1/subdir2"), 0);
363369
assert_eq!(cage.chdir_syscall(".."), 0);
370+
let mut buf1 = vec![0u8; 9];
371+
let bufptr1: *mut u8 = &mut buf1[0];
372+
assert_eq!(cage.getcwd_syscall(bufptr1, 9), 0);
373+
assert_eq!(std::str::from_utf8(&buf1).unwrap(), "/subdir1\0");
374+
375+
assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS);
376+
lindrustfinalize();
377+
}
378+
379+
#[test]
380+
pub fn ut_lind_fs_chdir_removeddir() {
381+
//acquiring a lock on TESTMUTEX prevents other tests from running concurrently,
382+
// and also performs clean env setup
383+
let _thelock = setup::lock_and_init();
384+
385+
let cage = interface::cagetable_getref(1);
386+
387+
//Checking if removing the current working directory
388+
//works correctly
389+
assert_eq!(cage.mkdir_syscall("/subdir1", S_IRWXA), 0);
390+
assert_eq!(cage.mkdir_syscall("/subdir2", S_IRWXA), 0);
391+
assert_eq!(cage.chdir_syscall("subdir1"), 0);
392+
assert_eq!(cage.rmdir_syscall("/subdir1"), 0);
393+
assert_eq!(cage.chdir_syscall("/subdir2"), 0);
394+
assert_eq!(cage.chdir_syscall("subdir1"), -(Errno::ENOENT as i32));
395+
396+
assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS);
397+
lindrustfinalize();
398+
}
399+
400+
#[test]
401+
pub fn ut_lind_fs_chdir_invalid_args() {
402+
//acquiring a lock on TESTMUTEX prevents other tests from running concurrently,
403+
//and also performs clean env setup
404+
let _thelock = setup::lock_and_init();
405+
406+
let cage = interface::cagetable_getref(1);
407+
408+
let flags: i32 = O_TRUNC | O_CREAT | O_RDWR;
409+
let filepath = "/TestFile1";
410+
let _fd1 = cage.open_syscall(filepath, flags, 0);
411+
412+
//Checking if passing a regular file pathname correctly
413+
//returns `The last component in path is not a directory` error
414+
assert_eq!(cage.chdir_syscall("/TestFile1"), -(Errno::ENOTDIR as i32));
415+
416+
//Checking if a nonexistent pathname correctly
417+
//returns `The directory referred to in path does not exist` error.
418+
//`/arbitrarypath` is a pathname that does not correspond to any existing
419+
//directory pathname.
420+
assert_eq!(
421+
cage.chdir_syscall("/arbitrarypath"),
422+
-(Errno::ENOENT as i32)
423+
);
424+
425+
assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS);
426+
lindrustfinalize();
427+
}
428+
429+
#[test]
430+
pub fn ut_lind_fs_fchdir_valid_args() {
431+
//acquiring a lock on TESTMUTEX prevents other tests from running concurrently,
432+
// and also performs clean env setup
433+
let _thelock = setup::lock_and_init();
434+
435+
let cage = interface::cagetable_getref(1);
436+
437+
//Testing the ability to make and change to directories
438+
//using file descriptors
439+
440+
assert_eq!(cage.mkdir_syscall("/subdir1", S_IRWXA), 0);
441+
assert_eq!(cage.mkdir_syscall("/subdir1/subdir2", S_IRWXA), 0);
442+
443+
//Retrieving a valid directory file descriptor
444+
let fd1 = cage.open_syscall("/subdir1", O_RDWR, S_IRWXA);
445+
let fd2 = cage.open_syscall("/subdir1/subdir2", O_RDWR, S_IRWXA);
364446

447+
//Changing to a new current working directory, and then obtaining
448+
//the current working directory using `getcwd_syscall()` to see
449+
//if it was correctly changed
365450
assert_eq!(cage.access_syscall("subdir1", F_OK), 0);
366-
assert_eq!(cage.chdir_syscall("/subdir1/subdir2/subdir3"), 0);
367-
assert_eq!(cage.access_syscall("../../../subdir1", F_OK), 0);
451+
assert_eq!(cage.fchdir_syscall(fd1), 0);
452+
let mut buf1 = vec![0u8; 9];
453+
let bufptr1: *mut u8 = &mut buf1[0];
454+
assert_eq!(cage.getcwd_syscall(bufptr1, 9), 0);
455+
assert_eq!(std::str::from_utf8(&buf1).unwrap(), "/subdir1\0");
456+
457+
assert_eq!(cage.access_syscall("subdir2", F_OK), 0);
458+
assert_eq!(cage.fchdir_syscall(fd2), 0);
459+
let mut buf2 = vec![0u8; 17];
460+
let bufptr2: *mut u8 = &mut buf2[0];
461+
assert_eq!(cage.getcwd_syscall(bufptr2, 17), 0);
462+
assert_eq!(std::str::from_utf8(&buf2).unwrap(), "/subdir1/subdir2\0");
463+
464+
assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS);
465+
lindrustfinalize();
466+
}
467+
468+
#[test]
469+
pub fn ut_lind_fs_fchdir_invalid_args() {
470+
//acquiring a lock on TESTMUTEX prevents other tests from running concurrently,
471+
//and also performs clean env setup
472+
let _thelock = setup::lock_and_init();
473+
474+
let cage = interface::cagetable_getref(1);
475+
476+
let flags: i32 = O_TRUNC | O_CREAT | O_RDWR;
477+
let filepath = "/TestFile1";
478+
let fd1 = cage.open_syscall(filepath, flags, 0);
479+
480+
//Checking if passing a regular file descriptor correctly
481+
//returns `The last component in path is not a directory` error
482+
assert_eq!(cage.fchdir_syscall(fd1), -(Errno::ENOTDIR as i32));
483+
484+
//Checking if passing an invalid file descriptor correctly
485+
//results in `Invalid file descriptor` error
486+
//Since the file corresponding to file descriptor `fd1` is closed,
487+
//and no other file is opened after that, `fd1` file descriptor
488+
//should be invalid
489+
assert_eq!(cage.close_syscall(fd1), 0);
490+
assert_eq!(cage.fchdir_syscall(fd1), -(Errno::EBADF as i32));
368491

369492
assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS);
370493
lindrustfinalize();

0 commit comments

Comments
 (0)