sys_getcwd()
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 19 Aug 2010 23:27:31 +0000 (16:27 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:35:52 +0000 (17:35 -0700)
Testers, feel free to check out the two ERANGE paths.  As a side note,
the kernel does not yet have a max on the size of the buffers it
allocates on a user's behalf...

kern/include/vfs.h
kern/src/syscall.c
kern/src/vfs.c
tests/file_test.c

index d6b4a0d..7244ea7 100644 (file)
@@ -523,6 +523,7 @@ int insert_file(struct files_struct *open_files, struct file *file);
 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);
+char *do_getcwd(struct fs_struct *fs_env, char **kfree_this, size_t cwd_l);
 
 /* Debugging */
 int ls_dash_r(char *path);
index baa8edf..b21d0e2 100644 (file)
@@ -1104,16 +1104,18 @@ intreg_t sys_chdir(struct proc *p, const char *path, size_t path_l)
        return 0;
 }
 
-intreg_t sys_getcwd(struct proc *p, char *pwd, int size)
+/* Note cwd_l is not a strlen, it's an absolute size */
+intreg_t sys_getcwd(struct proc *p, char *u_cwd, size_t cwd_l)
 {
-       void* kbuf = kmalloc_errno(size);
-       if(kbuf == NULL)
-               return -1;
-       int ret = ufe(read,PADDR(kbuf),size,0,0);
-       if(ret != -1 && memcpy_to_user_errno(p,pwd,kbuf,strnlen(kbuf,size)))
-               ret = -1;
-       user_memdup_free(p,kbuf);
-       return ret;
+       int retval = 0;
+       char *kfree_this;
+       char *k_cwd = do_getcwd(&p->fs_env, &kfree_this, cwd_l);
+       if (!k_cwd)
+               return -1;              /* errno set by do_getcwd */
+       if (memcpy_to_user_errno(p, u_cwd, k_cwd, strnlen(k_cwd, cwd_l - 1) + 1))
+               retval = -1;
+       kfree(kfree_this);
+       return retval;
 }
 
 intreg_t sys_gettimeofday(struct proc *p, int *buf)
index 18052f9..37bf96a 100644 (file)
@@ -1514,6 +1514,50 @@ int do_chdir(struct fs_struct *fs_env, char *path)
        return retval;
 }
 
+/* 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
+ * build this string going backwards.  Note cwd_l is not a strlen, it's an
+ * absolute size. */
+char *do_getcwd(struct fs_struct *fs_env, char **kfree_this, size_t cwd_l)
+{
+       struct dentry *dentry = fs_env->pwd;
+       size_t link_len;
+       char *path_start, *kbuf;
+
+       if (cwd_l < 2) {
+               set_errno(ERANGE);
+               return 0;
+       }
+       kbuf = kmalloc(cwd_l, 0);
+       if (!kbuf) {
+               set_errno(ENOMEM);
+               return 0;
+       }
+       *kfree_this = kbuf;
+       kbuf[cwd_l - 1] = '\0';
+       kbuf[cwd_l - 2] = '/';
+       /* for each dentry in the path, all the way back to the root of fs_env, we
+        * grab the dentry name, push path_start back enough, and write in the name,
+        * using /'s to terminate.  We skip the root, since we don't want it's
+        * actual name, just "/", which is set before each loop. */
+       path_start = kbuf + cwd_l - 2;  /* the last byte written */
+       while (dentry != fs_env->root) {
+               link_len = dentry->d_name.len;          /* this does not count the \0 */
+               if (path_start - (link_len + 2) < kbuf) {
+                       kfree(kbuf);
+                       set_errno(ERANGE);
+                       return 0;
+               }
+               path_start -= link_len + 1;     /* the 1 is for the \0 */
+               strncpy(path_start, dentry->d_name.name, link_len);
+               path_start--;
+               *path_start = '/';
+               dentry = dentry->d_parent;      
+       }
+       return path_start;
+}
+
 static void print_dir(struct dentry *dentry, char *buf, int depth)
 {
        struct dentry *child_d;
index a22751e..da9917f 100644 (file)
@@ -6,6 +6,7 @@
 #include <unistd.h>
 #include <errno.h>
 #include <dirent.h>
+#include <stdlib.h>
 
 int main() 
 { 
@@ -119,6 +120,13 @@ int main()
        if (retval < 0)
                printf("WARNING! Unlink failed!\n");
 
+       /* getcwd, on the root dir */
+       char *cwd = getcwd(0, 0);
+       if (!cwd)
+               printf("WARNING! Couldn't get a CWD!\n");
+       else
+               printf("Got CWD (/): %s\n", cwd);
+       free(cwd);
        /* chdir() tests */
        printf("Testing basic chdir\n");
        retval = access("dir1/f1.txt", R_OK);
@@ -130,6 +138,22 @@ int main()
        retval = access("f1.txt", R_OK);
        if (retval < 0)
                printf("WARNING! Access error for f1.txt!\n");
+       cwd = getcwd(0, 0);
+       if (!cwd)
+               printf("WARNING! Couldn't get a CWD!\n");
+       else
+               printf("Got CWD (/dir1/): %s\n", cwd);
+       free(cwd);
+       /* change to a weird directory, see if we can still getcwd() */
+       retval = chdir("../dir2/../dir1/dir1-1");
+       if (retval < 0)
+               printf("WARNING! Chdir failed for dir1-1!\n");
+       cwd = getcwd(0, 0);
+       if (!cwd)
+               printf("WARNING! Couldn't get a CWD!\n");
+       else
+               printf("Got CWD (/dir1/dir1-1/): %s\n", cwd);
+       free(cwd);
 
        breakpoint();
 }