diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2021-05-17 21:19:35 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2021-05-18 20:06:36 -0400 |
commit | 3acca043261fa6268961d152ea2505e217eabcba (patch) | |
tree | f8113f1648eb75b189c6cfbf8d638b39299e9ea6 | |
parent | 3a291c974cf7ffb4fad4df6911b912536e511c9d (diff) |
d_path: regularize handling of root dentry in __dentry_path()
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>
-rw-r--r-- | fs/d_path.c | 21 |
1 files changed, 5 insertions, 16 deletions
diff --git a/fs/d_path.c b/fs/d_path.c index 1a1cf05e7780..b3324ae7cfe2 100644 --- a/fs/d_path.c +++ b/fs/d_path.c @@ -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) |