From 0b00cf70b666ee16d1d650e99367c0939bfcef23 Mon Sep 17 00:00:00 2001 From: Thomas Veerman Date: Wed, 1 Sep 2010 09:07:18 +0000 Subject: [PATCH] - Return ENOENT when trying to add files to removed (but open) directories. - Add test58 to test this behavior. --- servers/ext2/link.c | 13 ++++ servers/ext2/open.c | 5 ++ servers/mfs/link.c | 13 ++++ servers/mfs/open.c | 9 ++- test/Makefile | 3 +- test/run | 2 +- test/test58.c | 180 ++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 221 insertions(+), 4 deletions(-) create mode 100644 test/test58.c diff --git a/servers/ext2/link.c b/servers/ext2/link.c index a3549422c..c8c793340 100644 --- a/servers/ext2/link.c +++ b/servers/ext2/link.c @@ -77,6 +77,11 @@ PUBLIC int fs_link() if( (ip = get_inode(fs_dev, fs_m_in.REQ_DIR_INO)) == NULL) return(EINVAL); + if (ip->i_links_count == NO_LINK) { /* Dir does not actually exist */ + put_inode(ip); + return(ENOENT); + } + /* If 'name2' exists in full (even if no space) set 'r' to error. */ if ((new_ip = advance(ip, string, IGN_PERM)) == NULL) { r = err_code; @@ -349,6 +354,14 @@ PUBLIC int fs_rename() /* Get new dir inode */ if( (new_dirp = get_inode(fs_dev, (ino_t) fs_m_in.REQ_REN_NEW_DIR)) == NULL) r = err_code; + + if (new_dirp->i_links_count == NO_LINK) { /* Dir does not actually exist */ + put_inode(old_ip); + put_inode(old_dirp); + put_inode(new_dirp); + return(ENOENT); + } + new_ip = advance(new_dirp, new_name, IGN_PERM); /* not required to exist */ /* However, if the check failed because the file does exist, don't continue. diff --git a/servers/ext2/open.c b/servers/ext2/open.c index 0a6b2f27e..4081e7cee 100644 --- a/servers/ext2/open.c +++ b/servers/ext2/open.c @@ -286,6 +286,11 @@ PRIVATE struct inode *new_node(struct inode *ldirp, register struct inode *rip; register int r; + if (ldirp->i_links_count == NO_LINK) { /* Dir does not actually exist */ + err_code = ENOENT; + return(NULL); + } + /* Get final component of the path. */ rip = advance(ldirp, string, IGN_PERM); diff --git a/servers/mfs/link.c b/servers/mfs/link.c index 2848345fa..82147a663 100644 --- a/servers/mfs/link.c +++ b/servers/mfs/link.c @@ -71,6 +71,11 @@ PUBLIC int fs_link() if( (ip = get_inode(fs_dev, (ino_t) fs_m_in.REQ_DIR_INO)) == NULL) return(EINVAL); + if (ip->i_nlinks == NO_LINK) { /* Dir does not actually exist */ + put_inode(ip); + return(ENOENT); + } + /* If 'name2' exists in full (even if no space) set 'r' to error. */ if((new_ip = advance(ip, string, IGN_PERM)) == NULL) { r = err_code; @@ -317,6 +322,14 @@ PUBLIC int fs_rename() /* Get new dir inode */ if( (new_dirp = get_inode(fs_dev, (ino_t) fs_m_in.REQ_REN_NEW_DIR)) == NULL) r = err_code; + + if (new_dirp->i_nlinks == NO_LINK) { /* Dir does not actually exist */ + put_inode(old_ip); + put_inode(old_dirp); + put_inode(new_dirp); + return(ENOENT); + } + new_ip = advance(new_dirp, new_name, IGN_PERM); /* not required to exist */ /* However, if the check failed because the file does exist, don't continue. diff --git a/servers/mfs/open.c b/servers/mfs/open.c index 0e1d62525..982b18130 100644 --- a/servers/mfs/open.c +++ b/servers/mfs/open.c @@ -90,7 +90,7 @@ PUBLIC int fs_mknod() /* Get last directory inode */ if((ldirp = get_inode(fs_dev, (ino_t) fs_m_in.REQ_INODE_NR)) == NULL) return(ENOENT); - + /* Try to create the new node */ ip = new_node(ldirp, lastc, (mode_t) fs_m_in.REQ_MODE, (zone_t) fs_m_in.REQ_DEV); @@ -192,7 +192,7 @@ PUBLIC int fs_slink() /* Temporarily open the dir. */ if( (ldirp = get_inode(fs_dev, (ino_t) fs_m_in.REQ_INODE_NR)) == NULL) return(EINVAL); - + /* Create the inode for the symlink. */ sip = new_node(ldirp, string, (mode_t) (I_SYMBOLIC_LINK | RWX_MODES), (zone_t) 0); @@ -261,6 +261,11 @@ PRIVATE struct inode *new_node(struct inode *ldirp, register struct inode *rip; register int r; + if (ldirp->i_nlinks == NO_LINK) { /* Dir does not actually exist */ + err_code = ENOENT; + return(NULL); + } + /* Get final component of the path. */ rip = advance(ldirp, string, IGN_PERM); diff --git a/test/Makefile b/test/Makefile index a02893872..5abb5df3e 100644 --- a/test/Makefile +++ b/test/Makefile @@ -12,7 +12,7 @@ OBJ= test1 test2 test3 test4 test5 test6 test7 test8 test9 \ test30 test31 test32 test34 test35 test36 test37 test38 \ test39 t10a t11a t11b test40 t40a t40b t40c t40d t40e t40f test41 \ test42 test45 test47 test48 test49 test50 test51 test52 test53 \ - test54 test55 test56 + test54 test55 test56 test58 BIGOBJ= test20 test24 ROOTOBJ= test11 test33 test43 test44 test46 @@ -114,3 +114,4 @@ test55: test55.c test56: test56.c test57: test57.c test57loop.S if which $(GCC) >/dev/null 2>&1; then $(GCC) $(CFLAGS-GCC) -o $@ test57.c test57loop.S; fi +test58: test58.c diff --git a/test/run b/test/run index 1470c523e..48af18ae5 100755 --- a/test/run +++ b/test/run @@ -14,7 +14,7 @@ badones= # list of tests that failed tests=" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \ 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \ 41 42 43 44 45 45-gcc 46 47 48 49 49-gcc 50 \ - 51 51-gcc 52 52-gcc 53 54 55 56 57 \ + 51 51-gcc 52 52-gcc 53 54 55 56 57 58\ sh1.sh sh2.sh" tests_no=`expr 0` diff --git a/test/test58.c b/test/test58.c new file mode 100644 index 000000000..f26713eaf --- /dev/null +++ b/test/test58.c @@ -0,0 +1,180 @@ +/* This tests the behavior of Minix when the current working dir (cwd) doesn't + * actually exist and we either: + * - create a new file + * - make a new directory + * - make a special file (mknod) + * - create a hard link + * - create a symbolic link, or + * - rename a file + * In each case, `a component of the path does not name an existing file', and + * the operation should fail with ENOENT. These tests should actually be + * distributed over the other tests that actually test the specific system + * calls. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +int subtest = -1; +#define MAX_ERROR 999 /* Effectively no limit. This is necessary as this + * test tries to undo errors and should therefore not + * preemptively exit, as that would leave the FS + * in a corrupted state. */ +#include "common.c" + +#define TEST_PATH "a/b/c" +#define INTEGR_MSG "You might want to check fs integrity\n" + +_PROTOTYPE( void do_test, (void) ); + +void do_test(void) +{ + int r, fd; + int s[2]; + char buf[1], testroot[PATH_MAX+1], renamebuf[PATH_MAX+1]; + + subtest = 1; + if (socketpair(PF_UNIX, SOCK_STREAM, 0, s) == -1) e(1); + if (system("mkdir -p " TEST_PATH) == -1) e(2); + if (realpath(".", testroot) == NULL) e(3); + + r = fork(); + if (r == -1) e(4); + else if (r == 0) { /* Child */ + /* Change child's cwd to TEST_PATH */ + if (chdir(TEST_PATH) == -1) e(5); + + /* Signal parent we're ready for the test */ + buf[0] = 'a'; + if (write(s[0], buf, sizeof(buf)) != sizeof(buf)) e(6); + + /* Wait for parent to remove my cwd */ + if (read(s[0], buf, sizeof(buf)) != sizeof(buf)) e(7); + + /* Try to create a file */ + if ((fd = open("testfile", O_RDWR | O_CREAT)) != -1) { + e(8); + /* Uh oh. We created a file?! Try to remove it. */ + (void) close(fd); + if (unlink("testfile") != 0) { + /* This is not good. We created a file, but we can + * never access it; we have a spurious inode. + */ + e(9); + printf(INTEGR_MSG); + exit(errct); + } + } + if (errno != ENOENT) e(10); + + /* Try to create a dir */ + errno = 0; + if (mkdir("testdir", 0777) == 0) { + e(11); + /* Uh oh. This shouldn't have been possible. Try to undo. */ + if (rmdir("testdir") != 0) { + /* Not good. */ + e(12); + printf(INTEGR_MSG); + exit(errct); + } + } + if (errno != ENOENT) e(13); + + /* Try to create a special file */ + errno = 0; + if (mknod("testnode", 0777 | S_IFIFO, 0) == 0) { + e(14); + /* Impossible. Try to make it unhappen. */ + if (unlink("testnode") != 0) { + /* Not good. */ + e(15); + printf(INTEGR_MSG); + exit(errct); + } + } + if (errno != ENOENT) e(16); + + /* Try to rename a file */ + errno = 0; + /* First create a file in the test dir */ + snprintf(renamebuf, PATH_MAX, "%s/oldname", testroot); + if ((fd = open(renamebuf, O_RDWR | O_CREAT)) == -1) e(17); + if (close(fd) != 0) e(18); + + /* Now try to rename that file to an entry in the current, non-existing + * working directory. + */ + if (rename(renamebuf, "testrename") == 0) { + e(19); + /* This shouldn't have been possible. Revert the name change. + */ + if (rename("testrename", renamebuf) != 0) { + /* Failed */ + e(20); + printf(INTEGR_MSG); + exit(errct); + } + } + + /* Try to create a hard link to that file */ + errno = 0; + if (link(renamebuf, "testhlink") == 0) { + e(21); + /* Try to undo the hard link to prevent fs corruption. */ + if (unlink("testhlink") != 0) { + /* Failed. */ + e(22); + printf(INTEGR_MSG); + exit(errct); + } + } + if (errno != ENOENT) e(23); + + /* Try to create a symlink */ + errno = 0; + if (symlink(testroot, "testslink") == 0) { + e(24); + /* Try to remove the symlink to prevent fs corruption. */ + if (unlink("testslink") != 0) { + /* Failed. */ + e(25); + printf(INTEGR_MSG); + exit(errct); + } + } + if (errno != ENOENT) e(26); + + exit(errct); + } else { /* Parent */ + int status; + + /* Wait for the child to enter the TEST_PATH dir */ + if (read(s[1], buf, sizeof(buf)) != sizeof(buf)) e(27); + + /* Delete TEST_PATH */ + if (rmdir(TEST_PATH) != 0) e(28); + + /* Tell child we removed its cwd */ + buf[0] = 'b'; + if (write(s[1], buf, sizeof(buf)) != sizeof(buf)) e(29); + + wait(&status); + errct += WEXITSTATUS(status); /* Count errors */ + } +} + +int main(int argc, void* argv[]) +{ + start(58); + do_test(); + quit(); +} + +