Received: by 2002:a05:6a10:f3d0:0:0:0:0 with SMTP id a16csp3338358pxv; Mon, 28 Jun 2021 02:18:31 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxmdlWtk9Kaj5mgqcjlq6pe1/ZtDiPymPwAjVPUGGaEXHq7e9799OrpxjZOo50GYN1DiAYy X-Received: by 2002:a05:6402:350e:: with SMTP id b14mr31154096edd.286.1624871911596; Mon, 28 Jun 2021 02:18:31 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1624871911; cv=none; d=google.com; s=arc-20160816; b=jWDHDFqvbH2PAVYtkiLLDjBmkBXob/oxOVG4nA8Dd/EeMswHctWOFinYLD32UXOVA7 4MkQSgPy/TVUtgwsey0whuFacyRIRsTIsm2hAwNfrwnVLsVwOUCkfwV4HDt5c/UMDbW2 syOmAn8b6UWGga4HOVf5coweCKYnqTnIoyp0Fw5BEeMczvahfgL5ycPcTuX8My3DzllZ 3BNOsUX04lJSwg8MX2bXrv4FVLUaZJlexEmudrBL/nDQfoUILUe0zXPWaG53FKX28rRy 4AC6DkX18XPLAxNggRymSWiLasFUHTLE6hw1EJ7Zj55R6W8oiENf+K+QJTwQsVCz+7E+ xsXQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:in-reply-to:content-disposition:mime-version :references:message-id:subject:cc:to:from:date:dkim-signature; bh=BhGvCEpkVZywJS343m5vqWq8ImMUiwm2JU9sNziZH8c=; b=Xa6jXa19guOidzYNaScn7/zATVaWztDVIGqkzvuU8RevoLHfgTN30/k5SLNR3yRwmL 0cIDhIEQjEtI3djdla6VAfS8UUPQsFhWOUBNIWjyi1UlcAtp5ikdwxTfXJYCayOaJr04 SSIYMXB+LlRNhxjGr1rwXKISikAVhh0+SQkALxGbCMxP/Q3HMnKiP3QtaV5BGs/M0hMY 5dF5KRKK5fv4W+f43gFKQU60eW34UXkwWoOOlfeZ/pzq+yjkvnekCKtCjrkU4AY7rYW6 QRuohe0nEmfWUS+3UcvT06H0Xyn/V2HZTDmnSUNKpLlq2Q90pTHMR3a+TzzgHBiV5bJ9 +4+g== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@suse.com header.s=susede1 header.b=cSEN3S4E; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=NONE dis=NONE) header.from=suse.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id g21si10375219edb.437.2021.06.28.02.18.08; Mon, 28 Jun 2021 02:18:31 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@suse.com header.s=susede1 header.b=cSEN3S4E; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=NONE dis=NONE) header.from=suse.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232348AbhF1JJH (ORCPT + 99 others); Mon, 28 Jun 2021 05:09:07 -0400 Received: from smtp-out2.suse.de ([195.135.220.29]:56948 "EHLO smtp-out2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232329AbhF1JJG (ORCPT ); Mon, 28 Jun 2021 05:09:06 -0400 Received: from relay2.suse.de (relay2.suse.de [149.44.160.134]) by smtp-out2.suse.de (Postfix) with ESMTP id 210F020237; Mon, 28 Jun 2021 09:06:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1624871200; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=BhGvCEpkVZywJS343m5vqWq8ImMUiwm2JU9sNziZH8c=; b=cSEN3S4EIkcrsvwhNFt7a4VDpxjrc8i9FT5YrYxVHEWuk9GaGNPwBD3FtmgWJzYrF6Qmgm /el2dxPFcWB/SGsQaaLbcjDoZZ7LZardysUkSyVXnlG+OUhbPG2nss3TXDEC4NvohvDxTR 4cGXoGEO1EfwRFsZFn6Yrx4/DNLko+I= Received: from suse.cz (unknown [10.100.224.162]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by relay2.suse.de (Postfix) with ESMTPS id A626DA3B8E; Mon, 28 Jun 2021 09:06:39 +0000 (UTC) Date: Mon, 28 Jun 2021 11:06:39 +0200 From: Petr Mladek To: Justin He Cc: Andy Shevchenko , "Peter Zijlstra (Intel)" , Eric Biggers , "Ahmed S. Darwish" , "linux-doc@vger.kernel.org" , "linux-kernel@vger.kernel.org" , "linux-fsdevel@vger.kernel.org" , Matthew Wilcox , Christoph Hellwig , nd , Steven Rostedt , Sergey Senozhatsky , Rasmus Villemoes , Jonathan Corbet , Alexander Viro , Linus Torvalds Subject: Re: [PATCH v2 1/4] fs: introduce helper d_path_unsafe() Message-ID: References: <20210623055011.22916-1-justin.he@arm.com> <20210623055011.22916-2-justin.he@arm.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Mon 2021-06-28 05:13:51, Justin He wrote: > Hi Andy, Petr > > > -----Original Message----- > > From: Jia He > > Sent: Wednesday, June 23, 2021 1:50 PM > > To: Petr Mladek ; Steven Rostedt ; > > Sergey Senozhatsky ; Andy Shevchenko > > ; Rasmus Villemoes > > ; Jonathan Corbet ; Alexander > > Viro ; Linus Torvalds > foundation.org> > > Cc: Peter Zijlstra (Intel) ; Eric Biggers > > ; Ahmed S. Darwish ; linux- > > doc@vger.kernel.org; linux-kernel@vger.kernel.org; linux- > > fsdevel@vger.kernel.org; Matthew Wilcox ; Christoph > > Hellwig ; nd ; Justin He > > Subject: [PATCH v2 1/4] fs: introduce helper d_path_unsafe() > > > > 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 > > Signed-off-by: Jia He > > --- > > 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); > > I have new concern here. > Previously, __prepend_path() would break the loop at once when p.len<0. > And the return value of __prepend_path() was 0. > The caller of prepend_path() would typically check as follows: > if (prepend_path(...) > 0) > do_sth(); > > After I replaced prepend_name() with prepend_name_with_len(), > the return value of prepend_path() is possibly positive > together with p.len<0. The behavior is different from previous. I do not feel qualified to make decision here.I do not have enough experience with this code. Anyway, the new behavior looks correct to me. The return values 1, 2, 3 mean that there was something wrong with the path. The new code checks the entire path which looks correct to me. We only need to make sure that all callers handle this correctly. Both __prepend_path() and prepend_path() are static so that the scope is well defined. If I did not miss something, all callers handle this correctly: + __d_patch() ignores buf when prepend_patch() > 0 + d_absolute_path() and d_path() use extract_string(). It ignores the buffer when p->len < 0 + SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size) ignores the path as well. It is less obvious because it is done this way: len = PATH_MAX - b.len; if (unlikely(len > PATH_MAX)) error = -ENAMETOOLONG; The condition (len > PATH_MAX) is true when b.len is negative. Best Regards, Petr