From nobody Wed Dec 10 20:10:49 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 0894D35959; Thu, 13 Nov 2025 00:41:04 +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=1762994466; cv=none; b=Mbme2RLlX7U0dFUg3pX16+Vvsz2NJKG9w9ysr7IkaPPQVkQqRPlGegIvbT47RoXxvo3Nwhln/hvaNLUMWeqJde4IKOgXwJPS1bmFYGJ+5TsYTHTwXcoMFeiNSAiOYpUQddx1eK864NK4Uo4YsbQqvjEUL8DEHs3JovESFIsAwoo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762994466; c=relaxed/simple; bh=R8bHaHyEJ594zGFKE/fad7JGvr03zpWNes7mrF7tbOk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Fx27wvKd1QA5simsKesBR7lG/m5GRs6veRqmJvhKkr07f/WWLu06MkMpm/dVxoJEjfFarJtcNvwmEP7/TUoY5TtfcBIeSY9iaDtwmrfbK0Y7ZeFmuYYW6/pqGiIkBvRjx8yvDhtA7ZNm3MsSfua+dduCQSGbf1IBGsH2m5fabM8= 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=D6QaGMQD; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=ZbyNqACh; 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="D6QaGMQD"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="ZbyNqACh" Received: from phl-compute-02.internal (phl-compute-02.internal [10.202.2.42]) by mailflow.stl.internal (Postfix) with ESMTP id 6E0BF13000C2; Wed, 12 Nov 2025 19:41:03 -0500 (EST) Received: from phl-mailfrontend-01 ([10.202.2.162]) by phl-compute-02.internal (MEProxy); Wed, 12 Nov 2025 19:41:04 -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=1762994463; x=1763001663; bh=PJzgcrxhiIBIelv8Z38ByCDof4FfjJXhCw99Sa9A6Ac=; b= D6QaGMQDZbL72LQAIZ/zdlPPIKGOKBOd9COsNW+5P/Srmn8DLQ98DQ5ExmL3eyxR /rxrZz5HaSGP/rLPsQxj+6IOc+GX9bfPKV9oYUSHsYNL1T4g2u4N8+2C88VO05Oo jl0XvjbG09n3XFhPbOVE4h6TRw0nC0E2DE34DUAB2l2x4IzAJuDyRk/HX4ciu177 dJ9Wfg6x20r+ivosdDHXwZbWqkZMnWs/Izxt1XfX9cJYr5R987PPR0TQL0JqrRJi QdVvTWBox5sG2R1B0WhMEjcUa7nVYlf1rIJlcc1lb13OODmBMLfgOpWbGz+apOMj ldAgTC0GvbIf3M67DPqmYg== 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=1762994463; x=1763001663; bh=P JzgcrxhiIBIelv8Z38ByCDof4FfjJXhCw99Sa9A6Ac=; b=ZbyNqAChKye2iat9e Okru5r8Bhy9LobkAdFjA+/oZ8DKR6dUPbVSX8HxDoyMjUpthtxyA1voxq+3kh1VB v5ixs/RyhtzXh3X5ZThn+T5+vKITOZxDpTZ0P6euLpth848mQ3OG+ElxcDMno4lJ 5PWBSUi7BfGTISAsr1xpCRZeusrTzEFeFXFp547leqC+jf2Jli7K4BZP7Toj86wa E0FJHntMXhWsfA/wifVr1/AUa4A3jyVamzf9koqfcCLDYl7DfF6jEBOvH2Rc7CkY 26ubrUt+3ctd0+u7yVUJP1f/zuSMhfu+KQoEvT64wv94xTyROkYdmbMUmw77xNou Nd4Aw== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddvtdehheegucetufdoteggodetrf 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:52 -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 06/15] VFS: introduce start_creating_noperm() and start_removing_noperm() Date: Thu, 13 Nov 2025 11:18:29 +1100 Message-ID: <20251113002050.676694-7-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 xfs, fuse, ipc/mqueue need variants of start_creating or start_removing which do not check permissions. This patch adds _noperm versions of these functions. Note that do_mq_open() was only calling mntget() so it could call path_put() - it didn't really need an extra reference on the mnt. Now it doesn't call mntget() and uses end_creating() which does the dput() half of path_put(). Also mq_unlink() previously passed d_inode(dentry->d_parent) as the dir inode to vfs_unlink(). This is after locking d_inode(mnt->mnt_root) These two inodes are the same, but normally calls use the textual parent. So I've changes the vfs_unlink() call to be given d_inode(mnt->mnt_root). Reviewed-by: Amir Goldstein Reviewed-by: Jeff Layton Signed-off-by: NeilBrown -- changes since v2: - dir arg passed to vfs_unlink() in mq_unlink() changed to match the dir passed to lookup_noperm() - restore assignment to path->mnt even though the mntget() is removed. --- fs/fuse/dir.c | 19 +++++++--------- fs/namei.c | 48 ++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/orphanage.c | 11 ++++----- include/linux/namei.h | 2 ++ ipc/mqueue.c | 32 ++++++++++----------------- 5 files changed, 74 insertions(+), 38 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 316922d5dd13..a0d5b302bcc2 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1397,27 +1397,25 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, = u64 parent_nodeid, if (!parent) return -ENOENT; =20 - inode_lock_nested(parent, I_MUTEX_PARENT); if (!S_ISDIR(parent->i_mode)) - goto unlock; + goto put_parent; =20 err =3D -ENOENT; dir =3D d_find_alias(parent); if (!dir) - goto unlock; + goto put_parent; =20 - name->hash =3D full_name_hash(dir, name->name, name->len); - entry =3D d_lookup(dir, name); + entry =3D start_removing_noperm(dir, name); dput(dir); - if (!entry) - goto unlock; + if (IS_ERR(entry)) + goto put_parent; =20 fuse_dir_changed(parent); if (!(flags & FUSE_EXPIRE_ONLY)) d_invalidate(entry); fuse_invalidate_entry_cache(entry); =20 - if (child_nodeid !=3D 0 && d_really_is_positive(entry)) { + if (child_nodeid !=3D 0) { inode_lock(d_inode(entry)); if (get_node_id(d_inode(entry)) !=3D child_nodeid) { err =3D -ENOENT; @@ -1445,10 +1443,9 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u= 64 parent_nodeid, } else { err =3D 0; } - dput(entry); =20 - unlock: - inode_unlock(parent); + end_removing(entry); + put_parent: iput(parent); return err; } diff --git a/fs/namei.c b/fs/namei.c index 38dda29552f6..da01b828ede6 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3275,6 +3275,54 @@ struct dentry *start_removing(struct mnt_idmap *idma= p, struct dentry *parent, } EXPORT_SYMBOL(start_removing); =20 +/** + * start_creating_noperm - prepare to create a given name without permissi= on checking + * @parent: directory in which to prepare to create the name + * @name: the name to be created + * + * Locks are taken and a lookup in performed prior to creating + * an object in a directory. + * + * If the name already exists, a positive dentry is returned. + * + * Returns: a negative or positive dentry, or an error. + */ +struct dentry *start_creating_noperm(struct dentry *parent, + struct qstr *name) +{ + int err =3D lookup_noperm_common(name, parent); + + if (err) + return ERR_PTR(err); + return start_dirop(parent, name, LOOKUP_CREATE); +} +EXPORT_SYMBOL(start_creating_noperm); + +/** + * start_removing_noperm - prepare to remove a given name without permissi= on checking + * @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. + * + * 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_noperm(struct dentry *parent, + struct qstr *name) +{ + int err =3D lookup_noperm_common(name, parent); + + if (err) + return ERR_PTR(err); + return start_dirop(parent, name, 0); +} +EXPORT_SYMBOL(start_removing_noperm); + #ifdef CONFIG_UNIX98_PTYS int path_pts(struct path *path) { diff --git a/fs/xfs/scrub/orphanage.c b/fs/xfs/scrub/orphanage.c index 9c12cb844231..e732605924a1 100644 --- a/fs/xfs/scrub/orphanage.c +++ b/fs/xfs/scrub/orphanage.c @@ -152,11 +152,10 @@ xrep_orphanage_create( } =20 /* Try to find the orphanage directory. */ - inode_lock_nested(root_inode, I_MUTEX_PARENT); - orphanage_dentry =3D lookup_noperm(&QSTR(ORPHANAGE), root_dentry); + orphanage_dentry =3D start_creating_noperm(root_dentry, &QSTR(ORPHANAGE)); if (IS_ERR(orphanage_dentry)) { error =3D PTR_ERR(orphanage_dentry); - goto out_unlock_root; + goto out_dput_root; } =20 /* @@ -170,7 +169,7 @@ xrep_orphanage_create( orphanage_dentry, 0750); error =3D PTR_ERR(orphanage_dentry); if (IS_ERR(orphanage_dentry)) - goto out_unlock_root; + goto out_dput_orphanage; } =20 /* Not a directory? Bail out. */ @@ -200,9 +199,7 @@ xrep_orphanage_create( sc->orphanage_ilock_flags =3D 0; =20 out_dput_orphanage: - dput(orphanage_dentry); -out_unlock_root: - inode_unlock(VFS_I(sc->mp->m_rootip)); + end_creating(orphanage_dentry, root_dentry); out_dput_root: dput(root_dentry); out: diff --git a/include/linux/namei.h b/include/linux/namei.h index 6d1069f93ebf..0441f5921f87 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -93,6 +93,8 @@ struct dentry *start_creating(struct mnt_idmap *idmap, st= ruct dentry *parent, struct qstr *name); struct dentry *start_removing(struct mnt_idmap *idmap, struct dentry *pare= nt, struct qstr *name); +struct dentry *start_creating_noperm(struct dentry *parent, struct qstr *n= ame); +struct dentry *start_removing_noperm(struct dentry *parent, struct qstr *n= ame); =20 /** * end_creating - finish action started with start_creating diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 093551fe66a7..6d7610310003 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -913,13 +913,12 @@ static int do_mq_open(const char __user *u_name, int = oflag, umode_t mode, goto out_putname; =20 ro =3D mnt_want_write(mnt); /* we'll drop it in any case */ - inode_lock(d_inode(root)); - path.dentry =3D lookup_noperm(&QSTR(name->name), root); + path.dentry =3D start_creating_noperm(root, &QSTR(name->name)); if (IS_ERR(path.dentry)) { error =3D PTR_ERR(path.dentry); goto out_putfd; } - path.mnt =3D mntget(mnt); + path.mnt =3D mnt; error =3D prepare_open(path.dentry, oflag, ro, mode, name, attr); if (!error) { struct file *file =3D dentry_open(&path, oflag, current_cred()); @@ -928,13 +927,12 @@ static int do_mq_open(const char __user *u_name, int = oflag, umode_t mode, else error =3D PTR_ERR(file); } - path_put(&path); out_putfd: if (error) { put_unused_fd(fd); fd =3D error; } - inode_unlock(d_inode(root)); + end_creating(path.dentry, root); if (!ro) mnt_drop_write(mnt); out_putname: @@ -957,7 +955,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name) int err; struct filename *name; struct dentry *dentry; - struct inode *inode =3D NULL; + struct inode *inode; struct ipc_namespace *ipc_ns =3D current->nsproxy->ipc_ns; struct vfsmount *mnt =3D ipc_ns->mq_mnt; =20 @@ -969,26 +967,20 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_nam= e) err =3D mnt_want_write(mnt); if (err) goto out_name; - inode_lock_nested(d_inode(mnt->mnt_root), I_MUTEX_PARENT); - dentry =3D lookup_noperm(&QSTR(name->name), mnt->mnt_root); + dentry =3D start_removing_noperm(mnt->mnt_root, &QSTR(name->name)); if (IS_ERR(dentry)) { err =3D PTR_ERR(dentry); - goto out_unlock; + goto out_drop_write; } =20 inode =3D d_inode(dentry); - if (!inode) { - err =3D -ENOENT; - } else { - ihold(inode); - err =3D vfs_unlink(&nop_mnt_idmap, d_inode(dentry->d_parent), - dentry, NULL); - } - dput(dentry); - -out_unlock: - inode_unlock(d_inode(mnt->mnt_root)); + ihold(inode); + err =3D vfs_unlink(&nop_mnt_idmap, d_inode(mnt->mnt_root), + dentry, NULL); + end_removing(dentry); iput(inode); + +out_drop_write: mnt_drop_write(mnt); out_name: putname(name); --=20 2.50.0.107.gf914562f5916.dirty