fchdir() (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 13 Aug 2014 20:26:29 +0000 (13:26 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 13 Aug 2014 20:26:29 +0000 (13:26 -0700)
Implements sys_fchdir, and touches up the raciness on pwd.  The 9ns side does a
better job, IIRC, of dealing with races on lookups (any of which can start at
pwd (DOT)).  Right now, a VFS lookup from pwd concurrent with a chdir could
fail to grab a ref.

Rebuild glibc.

kern/include/vfs.h
kern/src/syscall.c
kern/src/vfs.c
tests/file_test.c
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/fchdir.c

index 8eb9a20..26b150e 100644 (file)
@@ -483,6 +483,7 @@ int insert_file(struct files_struct *open_files, struct file *file, int low_fd);
 void close_all_files(struct files_struct *open_files, bool cloexec);
 void clone_files(struct files_struct *src, struct files_struct *dst);
 int do_chdir(struct fs_struct *fs_env, char *path);
+int do_fchdir(struct fs_struct *fs_env, struct file *file);
 char *do_getcwd(struct fs_struct *fs_env, char **kfree_this, size_t cwd_l);
 
 /* Debugging */
index 9603478..7ebeca9 100644 (file)
@@ -1507,7 +1507,17 @@ intreg_t sys_chdir(struct proc *p, const char *path, size_t path_l)
 
 intreg_t sys_fchdir(struct proc *p, int fd)
 {
-       return -1;
+       struct file *file;
+       int retval;
+       file = get_file_from_fd(&p->open_files, fd);
+       if (!file) {
+               /* TODO: 9ns */
+               set_errno(EBADF);
+               return -1;
+       }
+       retval = do_fchdir(&p->fs_env, file);
+       kref_put(&file->f_kref);
+       return retval;
 }
 
 /* Note cwd_l is not a strlen, it's an absolute size */
index 16f1d40..9a2de2a 100644 (file)
@@ -463,6 +463,8 @@ int path_lookup(char *path, int flags, struct nameidata *nd)
        int retval;
        printd("Path lookup for %s\n", path);
        /* we allow absolute lookups with no process context */
+       /* TODO: RCU read lock on pwd or kref_not_zero in a loop.  concurrent chdir
+        * could decref nd->dentry before we get to incref it below. */
        if (path[0] == '/') {                   /* absolute lookup */
                if (!current)
                        nd->dentry = default_ns.root->mnt_root;
@@ -2322,6 +2324,20 @@ void clone_files(struct files_struct *src, struct files_struct *dst)
        spin_unlock(&src->lock);
 }
 
+static void __chpwd(struct fs_struct *fs_env, struct dentry *new_pwd)
+{
+       struct dentry *old_pwd;
+       kref_get(&new_pwd->d_kref, 1);
+       /* writer lock, make sure we replace pwd with ours.  could also CAS.
+        * readers don't lock at all, so they need to either loop, or we need to
+        * delay releasing old_pwd til an RCU grace period. */
+       spin_lock(&fs_env->lock);
+       old_pwd = fs_env->pwd;
+       fs_env->pwd = new_pwd;
+       spin_unlock(&fs_env->lock);
+       kref_put(&old_pwd->d_kref);
+}
+
 /* Change the working directory of the given fs env (one per process, at this
  * point).  Returns 0 for success, -ERROR for whatever error. */
 int do_chdir(struct fs_struct *fs_env, char *path)
@@ -2331,14 +2347,22 @@ int do_chdir(struct fs_struct *fs_env, char *path)
        retval = path_lookup(path, LOOKUP_DIRECTORY, nd);
        if (!retval) {
                /* nd->dentry is the place we want our PWD to be */
-               kref_get(&nd->dentry->d_kref, 1);
-               kref_put(&fs_env->pwd->d_kref);
-               fs_env->pwd = nd->dentry;
+               __chpwd(fs_env, nd->dentry);
        }
        path_release(nd);
        return retval;
 }
 
+int do_fchdir(struct fs_struct *fs_env, struct file *file)
+{
+       if ((file->f_dentry->d_inode->i_mode & __S_IFMT) != __S_IFDIR) {
+               set_errno(ENOTDIR);
+               return -1;
+       }
+       __chpwd(fs_env, file->f_dentry);
+       return 0;
+}
+
 /* Returns a null-terminated string of up to length cwd_l containing the
  * absolute path of fs_env, (up to fs_env's root).  Be sure to kfree the char*
  * "kfree_this" when you are done with it.  We do this since it's easier to
index e6e111f..f9ab9a4 100644 (file)
@@ -113,6 +113,7 @@ int main()
                printf("End of the directory\n");
        else
                printf("Dirent name: %s\n", result->d_name);
+       closedir(dir);
        
        /* Hardlink tests */
        printf("Linking to /bin/hello at /dir1/hardhello\n");
@@ -145,9 +146,14 @@ int main()
        retval = access("dir1/f1.txt", R_OK);
        if (retval < 0)
                printf("WARNING! Access error for dir1/f1.txt!\n");
-       retval = chdir("/dir1");
+       fd = open("/dir1", O_RDONLY);
+       printf("OPENED DIR1, got fd %d\n", fd);
+       if (fd < 1)
+               printf("WARNING!, failed to open /dir1!\n");
+       retval = fchdir(fd);
        if (retval < 0)
-               printf("WARNING! Chdir failed for /dir1!\n");
+               printf("WARNING! fchdir failed for /dir1!\n");
+       close(fd);
        retval = access("f1.txt", R_OK);
        if (retval < 0)
                printf("WARNING! Access error for f1.txt!\n");
@@ -167,6 +173,14 @@ int main()
        else
                printf("Got CWD (/dir1/dir1-1/): %s\n", cwd);
        free(cwd);
+       fd = open("/hello.txt", O_RDONLY);
+       if (fd < 1)
+               printf("WARNING! failed to open hello.txt!\n");
+       retval = fchdir(fd);
+       if (!retval || errno != ENOTDIR)
+               printf("WARNING! didn't fail to fchdir to hello.txt %d %d\n", retval,
+                      errno);
+       close(fd);
 
        /* Try a chmod() */
        printf("Trying a chmod\n");
index 9716f4c..fafd860 100644 (file)
@@ -25,8 +25,6 @@ int
 fchdir (fd)
      int fd;
 {
-  __set_errno (ENOSYS);
-  return -1;
+  return ros_syscall(SYS_fchdir, fd, 0, 0, 0, 0, 0);
 }
-stub_warning (fchdir)
 strong_alias (fchdir,__fchdir)