Revert "[PATCH] Fix d_path for lazy unmounts"
authorLinus Torvalds <torvalds@woody.linux-foundation.org>
Tue, 13 Feb 2007 20:08:18 +0000 (12:08 -0800)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Tue, 13 Feb 2007 20:08:18 +0000 (12:08 -0800)
This reverts commit eb3dfb0cb1f4a44e2d0553f89514ce9f2a9fcaf1.

It causes some strange Gnome problem with dbus-daemon getting stuck, so
we'll revert it until that problem is understood.

Reported by both walt and Greg KH, who both independently git-bisected
the problem to this commit.

Andreas is looking at it.

Reported-by: walt <wa1ter@myrealbox.com>
Reported-by: Greg KH <greg@kroah.com>
Acked-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
fs/dcache.c

index b5f613932912436da57d062d6ffbe91c2c5e139a..d68631f18df122824a0d36a307c647f15931c7bd 100644 (file)
@@ -1739,41 +1739,45 @@ shouldnt_be_hashed:
  * @rootmnt: vfsmnt to which the root dentry belongs
  * @buffer: buffer to return value in
  * @buflen: buffer length
- * @fail_deleted: what to return for deleted files
  *
- * Convert a dentry into an ASCII path name. If the entry has been deleted,
- * then if @fail_deleted is true, ERR_PTR(-ENOENT) is returned. Otherwise,
- * the the string " (deleted)" is appended. Note that this is ambiguous.
+ * Convert a dentry into an ASCII path name. If the entry has been deleted
+ * the string " (deleted)" is appended. Note that this is ambiguous.
  *
- * Returns the buffer or an error code.
+ * Returns the buffer or an error code if the path was too long.
+ *
+ * "buflen" should be positive. Caller holds the dcache_lock.
  */
-static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt,
-                     struct dentry *root, struct vfsmount *rootmnt,
-                     char *buffer, int buflen, int fail_deleted)
+static char * __d_path( struct dentry *dentry, struct vfsmount *vfsmnt,
+                       struct dentry *root, struct vfsmount *rootmnt,
+                       char *buffer, int buflen)
 {
-       int namelen, is_slash;
-
-       if (buflen < 2)
-               return ERR_PTR(-ENAMETOOLONG);
-       buffer += --buflen;
-       *buffer = '\0';
+       char * end = buffer+buflen;
+       char * retval;
+       int namelen;
 
-       spin_lock(&dcache_lock);
+       *--end = '\0';
+       buflen--;
        if (!IS_ROOT(dentry) && d_unhashed(dentry)) {
-               if (fail_deleted) {
-                       buffer = ERR_PTR(-ENOENT);
-                       goto out;
-               }
-               if (buflen < 10)
-                       goto Elong;
                buflen -= 10;
-               buffer -= 10;
-               memcpy(buffer, " (deleted)", 10);
+               end -= 10;
+               if (buflen < 0)
+                       goto Elong;
+               memcpy(end, " (deleted)", 10);
        }
-       while (dentry != root || vfsmnt != rootmnt) {
+
+       if (buflen < 1)
+               goto Elong;
+       /* Get '/' right */
+       retval = end-1;
+       *retval = '/';
+
+       for (;;) {
                struct dentry * parent;
 
+               if (dentry == root && vfsmnt == rootmnt)
+                       break;
                if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
+                       /* Global root? */
                        spin_lock(&vfsmount_lock);
                        if (vfsmnt->mnt_parent == vfsmnt) {
                                spin_unlock(&vfsmount_lock);
@@ -1787,60 +1791,33 @@ static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt,
                parent = dentry->d_parent;
                prefetch(parent);
                namelen = dentry->d_name.len;
-               if (buflen <= namelen)
-                       goto Elong;
                buflen -= namelen + 1;
-               buffer -= namelen;
-               memcpy(buffer, dentry->d_name.name, namelen);
-               *--buffer = '/';
+               if (buflen < 0)
+                       goto Elong;
+               end -= namelen;
+               memcpy(end, dentry->d_name.name, namelen);
+               *--end = '/';
+               retval = end;
                dentry = parent;
        }
-       /* Get '/' right */
-       if (*buffer != '/')
-               *--buffer = '/';
 
-out:
-       spin_unlock(&dcache_lock);
-       return buffer;
+       return retval;
 
 global_root:
-       /*
-        * We went past the (vfsmount, dentry) we were looking for and have
-        * either hit a root dentry, a lazily unmounted dentry, an
-        * unconnected dentry, or the file is on a pseudo filesystem.
-        */
        namelen = dentry->d_name.len;
-       is_slash = (namelen == 1 && *dentry->d_name.name == '/');
-       if (is_slash || (dentry->d_sb->s_flags & MS_NOUSER)) {
-               /*
-                * Make sure we won't return a pathname starting with '/'.
-                *
-                * Historically, we also glue together the root dentry and
-                * remaining name for pseudo filesystems like pipefs, which
-                * have the MS_NOUSER flag set. This results in pathnames
-                * like "pipe:[439336]".
-                */
-               if (*buffer == '/') {
-                       buffer++;
-                       buflen++;
-               }
-               if (is_slash)
-                       goto out;
-       }
-       if (buflen < namelen)
+       buflen -= namelen;
+       if (buflen < 0)
                goto Elong;
-       buffer -= namelen;
-       memcpy(buffer, dentry->d_name.name, namelen);
-       goto out;
-
+       retval -= namelen-1;    /* hit the slash */
+       memcpy(retval, dentry->d_name.name, namelen);
+       return retval;
 Elong:
-       buffer = ERR_PTR(-ENAMETOOLONG);
-       goto out;
+       return ERR_PTR(-ENAMETOOLONG);
 }
 
 /* write full pathname into buffer and return start of pathname */
-char *d_path(struct dentry *dentry, struct vfsmount *vfsmnt, char *buf,
-            int buflen)
+char * d_path(struct dentry *dentry, struct vfsmount *vfsmnt,
+                               char *buf, int buflen)
 {
        char *res;
        struct vfsmount *rootmnt;
@@ -1850,7 +1827,9 @@ char *d_path(struct dentry *dentry, struct vfsmount *vfsmnt, char *buf,
        rootmnt = mntget(current->fs->rootmnt);
        root = dget(current->fs->root);
        read_unlock(&current->fs->lock);
-       res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen, 0);
+       spin_lock(&dcache_lock);
+       res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen);
+       spin_unlock(&dcache_lock);
        dput(root);
        mntput(rootmnt);
        return res;
@@ -1876,10 +1855,10 @@ char *d_path(struct dentry *dentry, struct vfsmount *vfsmnt, char *buf,
  */
 asmlinkage long sys_getcwd(char __user *buf, unsigned long size)
 {
-       int error, len;
+       int error;
        struct vfsmount *pwdmnt, *rootmnt;
        struct dentry *pwd, *root;
-       char *page = (char *) __get_free_page(GFP_USER), *cwd;
+       char *page = (char *) __get_free_page(GFP_USER);
 
        if (!page)
                return -ENOMEM;
@@ -1891,18 +1870,29 @@ asmlinkage long sys_getcwd(char __user *buf, unsigned long size)
        root = dget(current->fs->root);
        read_unlock(&current->fs->lock);
 
-       cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE, 1);
-       error = PTR_ERR(cwd);
-       if (IS_ERR(cwd))
-               goto out;
+       error = -ENOENT;
+       /* Has the current directory has been unlinked? */
+       spin_lock(&dcache_lock);
+       if (pwd->d_parent == pwd || !d_unhashed(pwd)) {
+               unsigned long len;
+               char * cwd;
 
-       error = -ERANGE;
-       len = PAGE_SIZE + page - cwd;
-       if (len <= size) {
-               error = len;
-               if (copy_to_user(buf, cwd, len))
-                       error = -EFAULT;
-       }
+               cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE);
+               spin_unlock(&dcache_lock);
+
+               error = PTR_ERR(cwd);
+               if (IS_ERR(cwd))
+                       goto out;
+
+               error = -ERANGE;
+               len = PAGE_SIZE + page - cwd;
+               if (len <= size) {
+                       error = len;
+                       if (copy_to_user(buf, cwd, len))
+                               error = -EFAULT;
+               }
+       } else
+               spin_unlock(&dcache_lock);
 
 out:
        dput(pwd);