lkml.org 
[lkml]   [2021]   [Jun]   [23]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH v2 1/4] fs: introduce helper d_path_unsafe()
Date
This helper is similar to d_path() except that it doesn't take any
seqlock/spinlock. It is typical for debugging purposes. Besides,
an additional return value *prenpend_len* is used to get the full
path length of the dentry, ingoring the tail '\0'.
the full path length = end - buf - prepend_length - 1.

Previously it will skip the prepend_name() loop at once in
__prepen_path() when the buffer length is not enough or even negative.
prepend_name_with_len() will get the full length of dentry name
together with the parent recursively regardless of the buffer length.

Suggested-by: Matthew Wilcox <willy@infradead.org>
Signed-off-by: Jia He <justin.he@arm.com>
---
fs/d_path.c | 122 ++++++++++++++++++++++++++++++++++++++---
include/linux/dcache.h | 1 +
2 files changed, 116 insertions(+), 7 deletions(-)

diff --git a/fs/d_path.c b/fs/d_path.c
index 23a53f7b5c71..7a3ea88f8c5c 100644
--- a/fs/d_path.c
+++ b/fs/d_path.c
@@ -33,9 +33,8 @@ static void prepend(struct prepend_buffer *p, const char *str, int namelen)

/**
* prepend_name - prepend a pathname in front of current buffer pointer
- * @buffer: buffer pointer
- * @buflen: allocated length of the buffer
- * @name: name string and length qstr structure
+ * @p: prepend buffer which contains buffer pointer and allocated length
+ * @name: name string and length qstr structure
*
* With RCU path tracing, it may race with d_move(). Use READ_ONCE() to
* make sure that either the old or the new name pointer and length are
@@ -68,9 +67,84 @@ static bool prepend_name(struct prepend_buffer *p, const struct qstr *name)
return true;
}

+/**
+ * prepend_name_with_len - prepend a pathname in front of current buffer
+ * pointer with limited orig_buflen.
+ * @p: prepend buffer which contains buffer pointer and allocated length
+ * @name: name string and length qstr structure
+ * @orig_buflen: original length of the buffer
+ *
+ * p.ptr is updated each time when prepends dentry name and its parent.
+ * Given the orginal buffer length might be less than name string, the
+ * dentry name can be moved or truncated. Returns at once if !buf or
+ * original length is not positive to avoid memory copy.
+ *
+ * Load acquire is needed to make sure that we see that terminating NUL,
+ * which is similar to prepend_name().
+ */
+static bool prepend_name_with_len(struct prepend_buffer *p,
+ const struct qstr *name, int orig_buflen)
+{
+ const char *dname = smp_load_acquire(&name->name); /* ^^^ */
+ int dlen = READ_ONCE(name->len);
+ char *s;
+ int last_len = p->len;
+
+ p->len -= dlen + 1;
+
+ if (unlikely(!p->buf))
+ return false;
+
+ if (orig_buflen <= 0)
+ return false;
+
+ /*
+ * The first time we overflow the buffer. Then fill the string
+ * partially from the beginning
+ */
+ if (unlikely(p->len < 0)) {
+ int buflen = strlen(p->buf);
+
+ /* memcpy src */
+ s = p->buf;
+
+ /* Still have small space to fill partially */
+ if (last_len > 0) {
+ p->buf -= last_len;
+ buflen += last_len;
+ }
+
+ if (buflen > dlen + 1) {
+ /* Dentry name can be fully filled */
+ memmove(p->buf + dlen + 1, s, buflen - dlen - 1);
+ p->buf[0] = '/';
+ memcpy(p->buf + 1, dname, dlen);
+ } else if (buflen > 0) {
+ /* Can be partially filled, and drop last dentry */
+ p->buf[0] = '/';
+ memcpy(p->buf + 1, dname, buflen - 1);
+ }
+
+ return false;
+ }
+
+ s = p->buf -= dlen + 1;
+ *s++ = '/';
+ while (dlen--) {
+ char c = *dname++;
+
+ if (!c)
+ break;
+ *s++ = c;
+ }
+ return true;
+}
+
static int __prepend_path(const struct dentry *dentry, const struct mount *mnt,
const struct path *root, struct prepend_buffer *p)
{
+ int orig_buflen = p->len;
+
while (dentry != root->dentry || &mnt->mnt != root->mnt) {
const struct dentry *parent = READ_ONCE(dentry->d_parent);

@@ -97,8 +171,7 @@ static int __prepend_path(const struct dentry *dentry, const struct mount *mnt,
return 3;

prefetch(parent);
- if (!prepend_name(p, &dentry->d_name))
- break;
+ prepend_name_with_len(p, &dentry->d_name, orig_buflen);
dentry = parent;
}
return 0;
@@ -108,8 +181,7 @@ static int __prepend_path(const struct dentry *dentry, const struct mount *mnt,
* prepend_path - Prepend path string to a buffer
* @path: the dentry/vfsmount to report
* @root: root vfsmnt/dentry
- * @buffer: pointer to the end of the buffer
- * @buflen: pointer to buffer length
+ * @p: prepend buffer which contains buffer pointer and allocated length
*
* The function will first try to write out the pathname without taking any
* lock other than the RCU read lock to make sure that dentries won't go away.
@@ -263,6 +335,42 @@ char *d_path(const struct path *path, char *buf, int buflen)
}
EXPORT_SYMBOL(d_path);

+/**
+ * d_path_unsafe - return the full path of a dentry without taking
+ * any seqlock/spinlock. This helper is typical for debugging purposes.
+ * @path: path to report
+ * @buf: buffer to return value in
+ * @buflen: buffer length
+ * @prepend_len: prepended length when going through the full path
+ *
+ * Convert a dentry into an ASCII path name.
+ *
+ * Returns a pointer into the buffer or an error code if the path was
+ * errous.
+ *
+ * @buf can be NULL, and @buflen can be 0 or negative. But NULL @buf
+ * and buflen>0 is considered as an obvious caller bug.
+ *
+ */
+char *d_path_unsafe(const struct path *path, char *buf, int buflen,
+ int *prepend_len)
+{
+ struct path root;
+ DECLARE_BUFFER(b, buf, buflen);
+ struct mount *mnt = real_mount(path->mnt);
+
+ rcu_read_lock();
+ get_fs_root_rcu(current->fs, &root);
+
+ prepend(&b, "", 1);
+ __prepend_path(path->dentry, mnt, &root, &b);
+ rcu_read_unlock();
+
+ *prepend_len = b.len;
+
+ return b.buf;
+}
+
/*
* Helper function for dentry_operations.d_dname() members
*/
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 9e23d33bb6f1..ec118b684055 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -301,6 +301,7 @@ char *dynamic_dname(struct dentry *, char *, int, const char *, ...);
extern char *__d_path(const struct path *, const struct path *, char *, int);
extern char *d_absolute_path(const struct path *, char *, int);
extern char *d_path(const struct path *, char *, int);
+extern char *d_path_unsafe(const struct path *, char *, int, int*);
extern char *dentry_path_raw(const struct dentry *, char *, int);
extern char *dentry_path(const struct dentry *, char *, int);

--
2.17.1
\
 
 \ /
  Last update: 2021-06-23 07:51    [W:0.503 / U:1.104 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site