From patchwork Fri Jun 11 15:59:51 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Justin He X-Patchwork-Id: 12316061 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 390F1C48BD1 for ; Fri, 11 Jun 2021 16:00:20 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1B51C6141B for ; Fri, 11 Jun 2021 16:00:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230440AbhFKQCQ (ORCPT ); Fri, 11 Jun 2021 12:02:16 -0400 Received: from foss.arm.com ([217.140.110.172]:33744 "EHLO foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230358AbhFKQCO (ORCPT ); Fri, 11 Jun 2021 12:02:14 -0400 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 02D521396; Fri, 11 Jun 2021 09:00:16 -0700 (PDT) Received: from entos-ampere-02.shanghai.arm.com (entos-ampere-02.shanghai.arm.com [10.169.214.103]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 95B203F719; Fri, 11 Jun 2021 09:00:11 -0700 (PDT) From: Jia He To: Petr Mladek , Steven Rostedt , Sergey Senozhatsky , Andy Shevchenko , Rasmus Villemoes , Jonathan Corbet , Alexander Viro , Linus Torvalds Cc: "Peter Zijlstra (Intel)" , Eric Biggers , "Ahmed S. Darwish" , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, Jia He Subject: [PATCH RFCv3 1/3] fs: introduce helper d_path_unsafe() Date: Fri, 11 Jun 2021 23:59:51 +0800 Message-Id: <20210611155953.3010-2-justin.he@arm.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20210611155953.3010-1-justin.he@arm.com> References: <20210611155953.3010-1-justin.he@arm.com> Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This helper is similar to d_path except that it doesn't take any seqlock/spinlock. It is typical for debugging purpose. Besides, an additional return value *prenpend_len* is used to get the full path length of the dentry. prepend_name_with_len() enhances the behavior of prepend_name(). Previously it will skip the loop at once in __prepen_path() when the space is not enough. __prepend_path() gets the full length of dentry together with the parent recusively. Besides, if someone invokes snprintf with small but positive space, prepend_name_with() needs to move and copy the string partially. More than that, kasnprintf will pass NULL _buf_ and _end_, hence it returns at the very beginning with false in this case; Suggested-by: Matthew Wilcox Signed-off-by: Jia He --- fs/d_path.c | 83 +++++++++++++++++++++++++++++++++++++++++- include/linux/dcache.h | 1 + 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/fs/d_path.c b/fs/d_path.c index 23a53f7b5c71..4fc224eadf58 100644 --- a/fs/d_path.c +++ b/fs/d_path.c @@ -68,9 +68,66 @@ static bool prepend_name(struct prepend_buffer *p, const struct qstr *name) return true; } +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); + + 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) { + /* This 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) { + /* Partially filled, and drop last dentry name */ + 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 +154,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; @@ -263,6 +319,29 @@ char *d_path(const struct path *path, char *buf, int buflen) } EXPORT_SYMBOL(d_path); +/** + * d_path_unsafe - fast return the full path of a dentry without taking + * any seqlock/spinlock. This helper is typical for debugging purpose. + */ +char *d_path_unsafe(const struct path *path, char *buf, int buflen, + int *prepend_len) +{ + struct path root; + struct mount *mnt = real_mount(path->mnt); + DECLARE_BUFFER(b, buf, buflen); + + 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); From patchwork Fri Jun 11 15:59:52 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Justin He X-Patchwork-Id: 12316063 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_RED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3ADECC48BE0 for ; Fri, 11 Jun 2021 16:00:26 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 23B3261407 for ; Fri, 11 Jun 2021 16:00:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230468AbhFKQCW (ORCPT ); Fri, 11 Jun 2021 12:02:22 -0400 Received: from foss.arm.com ([217.140.110.172]:33770 "EHLO foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230387AbhFKQCT (ORCPT ); Fri, 11 Jun 2021 12:02:19 -0400 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id E54AC143D; Fri, 11 Jun 2021 09:00:20 -0700 (PDT) Received: from entos-ampere-02.shanghai.arm.com (entos-ampere-02.shanghai.arm.com [10.169.214.103]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 84F103F719; Fri, 11 Jun 2021 09:00:16 -0700 (PDT) From: Jia He To: Petr Mladek , Steven Rostedt , Sergey Senozhatsky , Andy Shevchenko , Rasmus Villemoes , Jonathan Corbet , Alexander Viro , Linus Torvalds Cc: "Peter Zijlstra (Intel)" , Eric Biggers , "Ahmed S. Darwish" , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, Jia He Subject: [PATCH RFCv3 2/3] lib/vsprintf.c: make %pD print full path for file Date: Fri, 11 Jun 2021 23:59:52 +0800 Message-Id: <20210611155953.3010-3-justin.he@arm.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20210611155953.3010-1-justin.he@arm.com> References: <20210611155953.3010-1-justin.he@arm.com> Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org We have '%pD' for printing a filename. It may not be perfect (by default it only prints one component.) As suggested by Linus at [1]: A dentry has a parent, but at the same time, a dentry really does inherently have "one name" (and given just the dentry pointers, you can't show mount-related parenthood, so in many ways the "show just one name" makes sense for "%pd" in ways it doesn't necessarily for "%pD"). But while a dentry arguably has that "one primary component", a _file_ is certainly not exclusively about that last component. Hence change the behavior of '%pD' to print full path of that file. Things become more complicated when spec.precision and spec.field_width is added in. string_truncate() is to handle the small space case for '%pD' precision and field_width. [1] https://lore.kernel.org/lkml/CAHk-=wimsMqGdzik187YWLb-ru+iktb4MYbMQG1rnZ81dXYFVg@mail.gmail.com/ Suggested-by: Linus Torvalds Signed-off-by: Jia He --- Documentation/core-api/printk-formats.rst | 5 ++- lib/vsprintf.c | 47 +++++++++++++++++++++-- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/Documentation/core-api/printk-formats.rst b/Documentation/core-api/printk-formats.rst index f063a384c7c8..95ba14dc529b 100644 --- a/Documentation/core-api/printk-formats.rst +++ b/Documentation/core-api/printk-formats.rst @@ -408,12 +408,13 @@ dentry names :: %pd{,2,3,4} - %pD{,2,3,4} + %pD For printing dentry name; if we race with :c:func:`d_move`, the name might be a mix of old and new ones, but it won't oops. %pd dentry is a safer equivalent of %s dentry->d_name.name we used to use, %pd prints ``n`` -last components. %pD does the same thing for struct file. +last components. %pD prints full file path together with mount-related +parenthood. Passed by reference. diff --git a/lib/vsprintf.c b/lib/vsprintf.c index f0c35d9b65bf..317b65280252 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -601,6 +602,20 @@ char *widen_string(char *buf, int n, char *end, struct printf_spec spec) } /* Handle string from a well known address. */ +static char *string_truncate(char *buf, char *end, const char *s, + u32 full_len, struct printf_spec spec) +{ + int lim = 0; + + if (buf < end) { + if (spec.precision >= 0) + lim = strlen(s) - min_t(int, spec.precision, strlen(s)); + + return widen_string(buf + full_len, full_len, end - lim, spec); + } + + return buf; +} static char *string_nocheck(char *buf, char *end, const char *s, struct printf_spec spec) { @@ -920,13 +935,37 @@ char *dentry_name(char *buf, char *end, const struct dentry *d, struct printf_sp } static noinline_for_stack -char *file_dentry_name(char *buf, char *end, const struct file *f, +char *file_d_path_name(char *buf, char *end, const struct file *f, struct printf_spec spec, const char *fmt) { + const struct path *path; + char *p; + int prepend_len, reserved_size, dpath_len; + if (check_pointer(&buf, end, f, spec)) return buf; - return dentry_name(buf, end, f->f_path.dentry, spec, fmt); + path = &f->f_path; + if (check_pointer(&buf, end, path, spec)) + return buf; + + p = d_path_unsafe(path, buf, end - buf, &prepend_len); + + /* Minus 1 byte for '\0' */ + dpath_len = end - buf - prepend_len - 1; + + reserved_size = max_t(int, dpath_len, spec.field_width); + + /* no filling space at all */ + if (buf >= end || !buf) + return buf + reserved_size; + + /* small space for long name */ + if (buf < end && prepend_len < 0) + return string_truncate(buf, end, p, dpath_len, spec); + + /* space is enough */ + return string_nocheck(buf, end, p, spec); } #ifdef CONFIG_BLOCK static noinline_for_stack @@ -2296,7 +2335,7 @@ early_param("no_hash_pointers", no_hash_pointers_enable); * - 'a[pd]' For address types [p] phys_addr_t, [d] dma_addr_t and derivatives * (default assumed to be phys_addr_t, passed by reference) * - 'd[234]' For a dentry name (optionally 2-4 last components) - * - 'D[234]' Same as 'd' but for a struct file + * - 'D' For full path name of a struct file * - 'g' For block_device name (gendisk + partition number) * - 't[RT][dt][r]' For time and date as represented by: * R struct rtc_time @@ -2395,7 +2434,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, case 'C': return clock(buf, end, ptr, spec, fmt); case 'D': - return file_dentry_name(buf, end, ptr, spec, fmt); + return file_d_path_name(buf, end, ptr, spec, fmt); #ifdef CONFIG_BLOCK case 'g': return bdev_name(buf, end, ptr, spec, fmt); From patchwork Fri Jun 11 15:59:53 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Justin He X-Patchwork-Id: 12316065 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id CC0F7C48BE0 for ; Fri, 11 Jun 2021 16:00:28 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id BB8E8613E3 for ; Fri, 11 Jun 2021 16:00:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230479AbhFKQCZ (ORCPT ); Fri, 11 Jun 2021 12:02:25 -0400 Received: from foss.arm.com ([217.140.110.172]:33800 "EHLO foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230492AbhFKQCY (ORCPT ); Fri, 11 Jun 2021 12:02:24 -0400 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id D28521476; Fri, 11 Jun 2021 09:00:25 -0700 (PDT) Received: from entos-ampere-02.shanghai.arm.com (entos-ampere-02.shanghai.arm.com [10.169.214.103]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 6EF023F719; Fri, 11 Jun 2021 09:00:21 -0700 (PDT) From: Jia He To: Petr Mladek , Steven Rostedt , Sergey Senozhatsky , Andy Shevchenko , Rasmus Villemoes , Jonathan Corbet , Alexander Viro , Linus Torvalds Cc: "Peter Zijlstra (Intel)" , Eric Biggers , "Ahmed S. Darwish" , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, Jia He Subject: [PATCH RFCv3 3/3] lib/test_printf: add test cases for '%pD' Date: Fri, 11 Jun 2021 23:59:53 +0800 Message-Id: <20210611155953.3010-4-justin.he@arm.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20210611155953.3010-1-justin.he@arm.com> References: <20210611155953.3010-1-justin.he@arm.com> Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org After the behaviour of specifier '%pD' is changed to print full path of struct file, the related test cases are also updated. Given the string is prepended from the end of the buffer, the check of "wrote beyond the nul-terminator" should be skipped. Signed-off-by: Jia He --- lib/test_printf.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/test_printf.c b/lib/test_printf.c index ec0d5976bb69..3632bd6cf906 100644 --- a/lib/test_printf.c +++ b/lib/test_printf.c @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -34,6 +35,7 @@ KSTM_MODULE_GLOBALS(); static char *test_buffer __initdata; static char *alloced_buffer __initdata; +static bool is_prepend_buf; extern bool no_hash_pointers; @@ -78,7 +80,7 @@ do_test(int bufsize, const char *expect, int elen, return 1; } - if (memchr_inv(test_buffer + written + 1, FILL_CHAR, BUF_SIZE + PAD_SIZE - (written + 1))) { + if (!is_prepend_buf && memchr_inv(test_buffer + written + 1, FILL_CHAR, BUF_SIZE + PAD_SIZE - (written + 1))) { pr_warn("vsnprintf(buf, %d, \"%s\", ...) wrote beyond the nul-terminator\n", bufsize, fmt); return 1; @@ -496,6 +498,27 @@ dentry(void) test(" bravo/alfa| bravo/alfa", "%12pd2|%*pd2", &test_dentry[2], 12, &test_dentry[2]); } +static struct vfsmount test_vfsmnt = {}; + +static struct file test_file __initdata = { + .f_path = { .dentry = &test_dentry[2], + .mnt = &test_vfsmnt, + }, +}; + +static void __init +f_d_path(void) +{ + test("(null)", "%pD", NULL); + test("(efault)", "%pD", PTR_INVALID); + + is_prepend_buf = true; + test("/bravo/alfa |/bravo/alfa ", "%-14pD|%*pD", &test_file, -14, &test_file); + test(" /bravo/alfa| /bravo/alfa", "%14pD|%*pD", &test_file, 14, &test_file); + test(" /bravo/alfa|/bravo/alfa ", "%14pD|%-14pD", &test_file, &test_file); + is_prepend_buf = false; +} + static void __init struct_va_format(void) { @@ -779,6 +802,7 @@ test_pointer(void) ip(); uuid(); dentry(); + f_d_path(); struct_va_format(); time_and_date(); struct_clk();