OSDN Git Service

d_path: regularize handling of root dentry in __dentry_path()
authorAl Viro <viro@zeniv.linux.org.uk>
Tue, 18 May 2021 01:19:35 +0000 (21:19 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Wed, 19 May 2021 00:06:36 +0000 (20:06 -0400)
All path-forming primitives boil down to sequence of prepend_name()
on dentries encountered along the way toward root.  Each time we prepend
/ + dentry name to the buffer.  Normally that does exactly what we want,
but there's a corner case when we don't call prepend_name() at all (in case
of __dentry_path() that happens if we are given root dentry).  We obviously
want to end up with "/", rather than "", so this corner case needs to be
handled.

__dentry_path() used to manually put '/' in the end of buffer before
doing anything else, to be overwritten by the first call of prepend_name()
if one happens and to be left in place if we don't call prepend_name() at
all.  That required manually checking that we had space in the buffer
(prepend_name() and prepend() take care of such checks themselves) and lead
to clumsy keeping track of return value.

A better approach is to check if the main loop has added anything
into the buffer and prepend "/" if it hasn't.  A side benefit of using prepend()
is that it does the right thing if we'd already run out of buffer, making
the overflow-handling logics simpler.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/d_path.c

index 1a1cf05..b3324ae 100644 (file)
@@ -329,31 +329,22 @@ char *simple_dname(struct dentry *dentry, char *buffer, int buflen)
 static char *__dentry_path(const struct dentry *d, char *p, int buflen)
 {
        const struct dentry *dentry;
-       char *end, *retval;
+       char *end;
        int len, seq = 0;
-       int error = 0;
-
-       if (buflen < 1)
-               goto Elong;
 
        rcu_read_lock();
 restart:
        dentry = d;
        end = p;
        len = buflen;
-       /* Get '/' right */
-       retval = end-1;
-       *retval = '/';
        read_seqbegin_or_lock(&rename_lock, &seq);
        while (!IS_ROOT(dentry)) {
                const struct dentry *parent = dentry->d_parent;
 
                prefetch(parent);
-               error = prepend_name(&end, &len, &dentry->d_name);
-               if (error)
+               if (unlikely(prepend_name(&end, &len, &dentry->d_name) < 0))
                        break;
 
-               retval = end;
                dentry = parent;
        }
        if (!(seq & 1))
@@ -363,11 +354,9 @@ restart:
                goto restart;
        }
        done_seqretry(&rename_lock, seq);
-       if (error)
-               goto Elong;
-       return retval;
-Elong:
-       return ERR_PTR(-ENAMETOOLONG);
+       if (len == buflen)
+               prepend(&end, &len, "/", 1);
+       return len >= 0 ? end : ERR_PTR(-ENAMETOOLONG);
 }
 
 char *dentry_path_raw(const struct dentry *dentry, char *buf, int buflen)