From nobody Wed Dec 10 20:10:48 2025 Received: from flow-b5-smtp.messagingengine.com (flow-b5-smtp.messagingengine.com [202.12.124.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EFB511F461D; Thu, 13 Nov 2025 00:40:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=202.12.124.140 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762994452; cv=none; b=i2IlpI/f8HSwADyQf+qxm0QqV5C8USLn9xbgyXxMYxnmLZO+r6gPM+9xKOq6c/5vlyqvksQeZdcaL1jjcd2Uu/NQhvke5enTQjMfdnMP9IdipNgqzA+clXN50paDXRc7hDGsBV0vvv9gxl/JCkiPNcEbujZdH84szi0SJrfuj4k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762994452; c=relaxed/simple; bh=Q7NxZSz7lqmLC+Lox8Dt8YFo8yQQ6+yggqprqyJB6cg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=SgUCtJKmnVaxj51WkchA2MKScdrEYNiI9x4Ae1zX6zpM97jxIllwbqP5xbUNT7L7uqNX2TccaF2OA1Yb3dgTi/iO6o3/qvf3aZAAlsc39tIK6HdX/BReTOSHrghcfTVsrhzU/BqY5lNEMfmtAAHOOKT6Pxrz4EwRLrMxUhc8nEU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=ownmail.net; spf=pass smtp.mailfrom=ownmail.net; dkim=pass (2048-bit key) header.d=ownmail.net header.i=@ownmail.net header.b=ft9kwz1T; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=sPhgec/s; arc=none smtp.client-ip=202.12.124.140 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=ownmail.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ownmail.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=ownmail.net header.i=@ownmail.net header.b="ft9kwz1T"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="sPhgec/s" Received: from phl-compute-05.internal (phl-compute-05.internal [10.202.2.45]) by mailflow.stl.internal (Postfix) with ESMTP id 522BB1300C8F; Wed, 12 Nov 2025 19:40:49 -0500 (EST) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-05.internal (MEProxy); Wed, 12 Nov 2025 19:40:50 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ownmail.net; h= cc:cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:reply-to:subject:subject:to:to; s=fm3; t=1762994449; x=1763001649; bh=B4FGnNZGtpSfmqIW8dml+eg/9i+FzFa2v7tvpgM0c9Q=; b= ft9kwz1TDJzoTDzW3G9zofpJPRRMgjPsaRfLfHhanFzMuHfTgyYPjj5J2a+nEpnf I9XTAzJjdZcdMyBYoIgbm/6V7PuzCViLzitLMWl1MgPOorphy3MTfG0tGcOSyoxt Uq5/o6ERaZuzwo52TYojUiTBRrnGPpg7ioXZsfJ6C+G2AmHgsV0tsToG8VRYAlyZ qvoA0ijNTbVeFeU9L3RPoj1ZkggkR70YA0Vyuz4ydXCatajkhyZVzRph8GuU3xix IPlAsEzg9jwuBztkPZxlpmC8YLKFVhz1vZpJ6Hw2BFbbN7iIbuFbcUT1YX1/DHWd arr/N4uFyM2pg2vn5RYvbA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm3; t=1762994449; x=1763001649; bh=B 4FGnNZGtpSfmqIW8dml+eg/9i+FzFa2v7tvpgM0c9Q=; b=sPhgec/s6NeEGQ79j wE43DJsfUCMekAMg2TxAgRuycQlsYjG0be+RZbl2LhV/jQvmLSupuqqzUnV3J9o7 rW2ofm9J/68ZL19fD0sq+x2rxCWrI/hgsLYgagFnHrOPCf9MYrAQleMOGDy6Fm/k jLxu3kShoENzG+YkRzuOP4aaoPpKS+6ZWsmqs9O/e+wUMZiNteOusxRLgyPaGo0F iGRZgzKqiR0AsG3pKE2XKaQJjks/8MeB4+nlZM80ZtYZBPuIZGcYw7St9mTHSg1e rGTmSTIw7NV+TiZnc2DOdxh0RSVBdFOVZllAW6Bp/iYmSVtMLkxYqfLqughEBTD/ jsoiw== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddvtdehheefucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedtpdhmohguvgepshhmthhp ohhuthdprhgtphhtthhopehvihhrohesiigvnhhivhdrlhhinhhugidrohhrghdruhhkpd hrtghpthhtohepshgvlhhinhhugiesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdigfhhssehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoh eplhhinhhugidquhhnihhonhhfshesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdhsvggtuhhrihhthidqmhhoughulhgvsehvghgvrhdrkhgvrhhnvg hlrdhorhhgpdhrtghpthhtoheplhhinhhugidqnhhfshesvhhgvghrrdhkvghrnhgvlhdr ohhrghdprhgtphhtthhopehlihhnuhigqdhkvghrnhgvlhesvhhgvghrrdhkvghrnhgvlh drohhrghdprhgtphhtthhopehlihhnuhigqdhfshguvghvvghlsehvghgvrhdrkhgvrhhn vghlrdhorhhgpdhrtghpthhtoheplhhinhhugidqtghifhhssehvghgvrhdrkhgvrhhnvg hlrdhorhhg X-ME-Proxy: Feedback-ID: iab3e480c:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 12 Nov 2025 19:40:38 -0500 (EST) From: NeilBrown To: "Alexander Viro" , "Christian Brauner" , "Amir Goldstein" Cc: "Jan Kara" , linux-fsdevel@vger.kernel.org, Jeff Layton , Chris Mason , David Sterba , David Howells , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Tyler Hicks , Miklos Szeredi , Chuck Lever , Olga Kornievskaia , Dai Ngo , Namjae Jeon , Steve French , Sergey Senozhatsky , Carlos Maiolino , John Johansen , Paul Moore , James Morris , "Serge E. Hallyn" , Stephen Smalley , Ondrej Mosnacek , Mateusz Guzik , Lorenzo Stoakes , Stefan Berger , "Darrick J. Wong" , linux-kernel@vger.kernel.org, netfs@lists.linux.dev, ecryptfs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-unionfs@vger.kernel.org, linux-cifs@vger.kernel.org, linux-xfs@vger.kernel.org, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v6 05/15] VFS/nfsd/cachefiles/ovl: introduce start_removing() and end_removing() Date: Thu, 13 Nov 2025 11:18:28 +1100 Message-ID: <20251113002050.676694-6-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251113002050.676694-1-neilb@ownmail.net> References: <20251113002050.676694-1-neilb@ownmail.net> Reply-To: NeilBrown Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: NeilBrown start_removing() is similar to start_creating() but will only return a positive dentry with the expectation that it will be removed. This is used by nfsd, cachefiles, and overlayfs. They are changed to also use end_removing() to terminate the action begun by start_removing(). This is a simple alias for end_dirop(). Apart from changes to the error paths, as we no longer need to unlock on a lookup error, an effect on callers is that they don't need to test if the found dentry is positive or negative - they can be sure it is positive. Reviewed-by: Amir Goldstein Reviewed-by: Jeff Layton Signed-off-by: NeilBrown --- fs/cachefiles/namei.c | 32 ++++++++++++++------------------ fs/namei.c | 27 +++++++++++++++++++++++++++ fs/nfsd/nfs4recover.c | 18 +++++------------- fs/nfsd/vfs.c | 26 ++++++++++---------------- fs/overlayfs/dir.c | 15 +++++++-------- fs/overlayfs/overlayfs.h | 8 ++++++++ include/linux/namei.h | 18 ++++++++++++++++++ 7 files changed, 89 insertions(+), 55 deletions(-) diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index 0a136eb434da..c7f0c6ab9b88 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -260,6 +260,7 @@ static int cachefiles_unlink(struct cachefiles_cache *c= ache, * - File backed objects are unlinked * - Directory backed objects are stuffed into the graveyard for userspace= to * delete + * On entry dir must be locked. It will be unlocked on exit. */ int cachefiles_bury_object(struct cachefiles_cache *cache, struct cachefiles_object *object, @@ -274,28 +275,30 @@ int cachefiles_bury_object(struct cachefiles_cache *c= ache, =20 _enter(",'%pd','%pd'", dir, rep); =20 + /* end_removing() will dput() @rep but we need to keep + * a ref, so take one now. This also stops the dentry + * being negated when unlinked which we need. + */ + dget(rep); + if (rep->d_parent !=3D dir) { - inode_unlock(d_inode(dir)); + end_removing(rep); _leave(" =3D -ESTALE"); return -ESTALE; } =20 /* non-directories can just be unlinked */ if (!d_is_dir(rep)) { - dget(rep); /* Stop the dentry being negated if it's only pinned - * by a file struct. - */ ret =3D cachefiles_unlink(cache, object, dir, rep, why); - dput(rep); + end_removing(rep); =20 - inode_unlock(d_inode(dir)); _leave(" =3D %d", ret); return ret; } =20 /* directories have to be moved to the graveyard */ _debug("move stale object to graveyard"); - inode_unlock(d_inode(dir)); + end_removing(rep); =20 try_again: /* first step is to make up a grave dentry in the graveyard */ @@ -749,26 +752,20 @@ static struct dentry *cachefiles_lookup_for_cull(stru= ct cachefiles_cache *cache, struct dentry *victim; int ret =3D -ENOENT; =20 - inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); + victim =3D start_removing(&nop_mnt_idmap, dir, &QSTR(filename)); =20 - victim =3D lookup_one(&nop_mnt_idmap, &QSTR(filename), dir); if (IS_ERR(victim)) goto lookup_error; - if (d_is_negative(victim)) - goto lookup_put; if (d_inode(victim)->i_flags & S_KERNEL_FILE) goto lookup_busy; return victim; =20 lookup_busy: ret =3D -EBUSY; -lookup_put: - inode_unlock(d_inode(dir)); - dput(victim); + end_removing(victim); return ERR_PTR(ret); =20 lookup_error: - inode_unlock(d_inode(dir)); ret =3D PTR_ERR(victim); if (ret =3D=3D -ENOENT) return ERR_PTR(-ESTALE); /* Probably got retired by the netfs */ @@ -816,18 +813,17 @@ int cachefiles_cull(struct cachefiles_cache *cache, s= truct dentry *dir, =20 ret =3D cachefiles_bury_object(cache, NULL, dir, victim, FSCACHE_OBJECT_WAS_CULLED); + dput(victim); if (ret < 0) goto error; =20 fscache_count_culled(); - dput(victim); _leave(" =3D 0"); return 0; =20 error_unlock: - inode_unlock(d_inode(dir)); + end_removing(victim); error: - dput(victim); if (ret =3D=3D -ENOENT) return -ESTALE; /* Probably got retired by the netfs */ =20 diff --git a/fs/namei.c b/fs/namei.c index 8873ad0f05b0..38dda29552f6 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3248,6 +3248,33 @@ struct dentry *start_creating(struct mnt_idmap *idma= p, struct dentry *parent, } EXPORT_SYMBOL(start_creating); =20 +/** + * start_removing - prepare to remove a given name with permission checking + * @idmap: idmap of the mount + * @parent: directory in which to find the name + * @name: the name to be removed + * + * Locks are taken and a lookup in performed prior to removing + * an object from a directory. Permission checking (MAY_EXEC) is performed + * against @idmap. + * + * If the name doesn't exist, an error is returned. + * + * end_removing() should be called when removal is complete, or aborted. + * + * Returns: a positive dentry, or an error. + */ +struct dentry *start_removing(struct mnt_idmap *idmap, struct dentry *pare= nt, + struct qstr *name) +{ + int err =3D lookup_one_common(idmap, name, parent); + + if (err) + return ERR_PTR(err); + return start_dirop(parent, name, 0); +} +EXPORT_SYMBOL(start_removing); + #ifdef CONFIG_UNIX98_PTYS int path_pts(struct path *path) { diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index c247a7c3291c..3eefaa2202e3 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -324,20 +324,12 @@ nfsd4_unlink_clid_dir(char *name, struct nfsd_net *nn) dprintk("NFSD: nfsd4_unlink_clid_dir. name %s\n", name); =20 dir =3D nn->rec_file->f_path.dentry; - inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); - dentry =3D lookup_one(&nop_mnt_idmap, &QSTR(name), dir); - if (IS_ERR(dentry)) { - status =3D PTR_ERR(dentry); - goto out_unlock; - } - status =3D -ENOENT; - if (d_really_is_negative(dentry)) - goto out; + dentry =3D start_removing(&nop_mnt_idmap, dir, &QSTR(name)); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + status =3D vfs_rmdir(&nop_mnt_idmap, d_inode(dir), dentry); -out: - dput(dentry); -out_unlock: - inode_unlock(d_inode(dir)); + end_removing(dentry); return status; } =20 diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 24e501abad0e..6291c371caa7 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -2044,7 +2044,7 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fh= p, int type, { struct dentry *dentry, *rdentry; struct inode *dirp; - struct inode *rinode; + struct inode *rinode =3D NULL; __be32 err; int host_err; =20 @@ -2063,24 +2063,21 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *= fhp, int type, =20 dentry =3D fhp->fh_dentry; dirp =3D d_inode(dentry); - inode_lock_nested(dirp, I_MUTEX_PARENT); =20 - rdentry =3D lookup_one(&nop_mnt_idmap, &QSTR_LEN(fname, flen), dentry); + rdentry =3D start_removing(&nop_mnt_idmap, dentry, &QSTR_LEN(fname, flen)= ); + host_err =3D PTR_ERR(rdentry); if (IS_ERR(rdentry)) - goto out_unlock; + goto out_drop_write; =20 - if (d_really_is_negative(rdentry)) { - dput(rdentry); - host_err =3D -ENOENT; - goto out_unlock; - } - rinode =3D d_inode(rdentry); err =3D fh_fill_pre_attrs(fhp); if (err !=3D nfs_ok) goto out_unlock; =20 + rinode =3D d_inode(rdentry); + /* Prevent truncation until after locks dropped */ ihold(rinode); + if (!type) type =3D d_inode(rdentry)->i_mode & S_IFMT; =20 @@ -2102,10 +2099,10 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *= fhp, int type, } fh_fill_post_attrs(fhp); =20 - inode_unlock(dirp); - if (!host_err) +out_unlock: + end_removing(rdentry); + if (!err && !host_err) host_err =3D commit_metadata(fhp); - dput(rdentry); iput(rinode); /* truncate the inode here */ =20 out_drop_write: @@ -2123,9 +2120,6 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fh= p, int type, } out: return err !=3D nfs_ok ? err : nfserrno(host_err); -out_unlock: - inode_unlock(dirp); - goto out_drop_write; } =20 /* diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index b9160fefbd00..20682afdbd20 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -866,17 +866,17 @@ static int ovl_remove_upper(struct dentry *dentry, bo= ol is_dir, goto out; } =20 - inode_lock_nested(dir, I_MUTEX_PARENT); - upper =3D ovl_lookup_upper(ofs, dentry->d_name.name, upperdir, - dentry->d_name.len); + upper =3D ovl_start_removing_upper(ofs, upperdir, + &QSTR_LEN(dentry->d_name.name, + dentry->d_name.len)); err =3D PTR_ERR(upper); if (IS_ERR(upper)) - goto out_unlock; + goto out_dput; =20 err =3D -ESTALE; if ((opaquedir && upper !=3D opaquedir) || (!opaquedir && !ovl_matches_upper(dentry, upper))) - goto out_dput_upper; + goto out_unlock; =20 if (is_dir) err =3D ovl_do_rmdir(ofs, dir, upper); @@ -892,10 +892,9 @@ static int ovl_remove_upper(struct dentry *dentry, boo= l is_dir, */ if (!err) d_drop(dentry); -out_dput_upper: - dput(upper); out_unlock: - inode_unlock(dir); + end_removing(upper); +out_dput: dput(opaquedir); out: return err; diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index beeba96cfcb2..49ad65f829dc 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -423,6 +423,14 @@ static inline struct dentry *ovl_start_creating_upper(= struct ovl_fs *ofs, parent, name); } =20 +static inline struct dentry *ovl_start_removing_upper(struct ovl_fs *ofs, + struct dentry *parent, + struct qstr *name) +{ + return start_removing(ovl_upper_mnt_idmap(ofs), + parent, name); +} + static inline bool ovl_open_flags_need_copy_up(int flags) { if (!flags) diff --git a/include/linux/namei.h b/include/linux/namei.h index 37b72f4a64f0..6d1069f93ebf 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -91,6 +91,8 @@ struct dentry *lookup_one_positive_killable(struct mnt_id= map *idmap, =20 struct dentry *start_creating(struct mnt_idmap *idmap, struct dentry *pare= nt, struct qstr *name); +struct dentry *start_removing(struct mnt_idmap *idmap, struct dentry *pare= nt, + struct qstr *name); =20 /** * end_creating - finish action started with start_creating @@ -122,6 +124,22 @@ static inline void end_creating(struct dentry *child, = struct dentry *parent) end_dirop(child); } =20 +/** + * end_removing - finish action started with start_removing + * @child: dentry returned by start_removing() + * @parent: dentry given to start_removing() + * + * Unlock and release the child. + * + * This is identical to end_dirop(). It can be passed the result of + * start_removing() whether that was successful or not, but it not needed + * if start_removing() failed. + */ +static inline void end_removing(struct dentry *child) +{ + end_dirop(child); +} + extern int follow_down_one(struct path *); extern int follow_down(struct path *path, unsigned int flags); extern int follow_up(struct path *); --=20 2.50.0.107.gf914562f5916.dirty