From nobody Sun Dec 14 05:53:24 2025 Received: from flow-b4-smtp.messagingengine.com (flow-b4-smtp.messagingengine.com [202.12.124.139]) (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 4A22331B822; Wed, 29 Oct 2025 23:44:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=202.12.124.139 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781489; cv=none; b=P8K2IZXCeXoY37AWPxIl75VNtK5CMADtOHJJ8yTwYz9Mx3o46UcRpyHyOGiANwEdF7q9kdVYe81UIY4iwywVDY5wB8KABG+dpS4pFkII6P/ScG3RnPt0h/rR6fjFrVUe05T8PKz87Qrd/qXXEKhD8ARLT+2OyRba9PhzK/n6jyI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781489; c=relaxed/simple; bh=jP0VItjW9TmC0N0ZbtcfmmKXjB/xRix3f/Yq+pI0+9s=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Xg2r4CLRlE9Jfp9eflC3MTcBCXyMbQvI9cxP6OM0EuS9zk8QPcFbVIatwOC7aWNjOZ+LEaXHfKhaXhjNP/sNeHNZ3o1uV/MTGlMNTsHJSHOTNQaUM4fjF99wgwT/Mxtf1dSjePKIbLEcgx6j5D5APFWQSfEUdkVGGf51unL4y5g= 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=hEAA6cs7; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=CbTzKvO5; arc=none smtp.client-ip=202.12.124.139 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="hEAA6cs7"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="CbTzKvO5" Received: from phl-compute-04.internal (phl-compute-04.internal [10.202.2.44]) by mailflow.stl.internal (Postfix) with ESMTP id B7A4A1300096; Wed, 29 Oct 2025 19:44:45 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-04.internal (MEProxy); Wed, 29 Oct 2025 19:44:46 -0400 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=1761781485; x=1761788685; bh=OmYIKVAm3NJgSpHW8ca60oUoTW8yQaaXXVdC2pwpq+I=; b= hEAA6cs7qW2muMjMG37k2/umqmSxI7++86pL2JX6BJRHb3gdpP4elzeZ0hXSTCxl V2kEft8sEEcyWNHJWqf+pd8EtCCwDtBcAXMyOJ8BQIIFHrJ7kV3WR8voRB0u7kN4 ur32noHUsNP+o6LGVpBYkTD/mCGwGp9YeQrE02wBXbzLr+08cckdVrqFZbqe0BAG ZsOnnyMEGglmW7zj/qYaG9902h2ozOpgHH10V37d7C6D3BEGM2ZaKuifmIe3l1BI rtGc+w43as67Fp4bFpWIIJ+X4sinfABSqwwqz8EbZfa5IawXCD2ujLdfH28zVo3f aZwVwu2Ot7rjwcgeID5zGg== 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=1761781485; x=1761788685; bh=O mYIKVAm3NJgSpHW8ca60oUoTW8yQaaXXVdC2pwpq+I=; b=CbTzKvO52Uy+Dm6pb dFPPjjgbQnm5hcS324Ws5xyoFXVI5pn7/lyfegToo87YBJHGEX0KigmxMOkn68eW 8jh1Oi/s//R/QQeAxs7pjfB+LFut34Sr4Y5frRWPF/dDICfzysJHCrgC1En3iGns 9aAExpKtUv4KaPTDi8BzK6swqI/OVHF03tn0+lVbSB/XrX2vv8RmyiewQmeYDorR xrRRLrFg1Qcuv+tdw7sac2e0+9oUZ2BTwk/4ZNExMe3fDZiJZc+f6K/a+vgAVcpI 232QchhAAIfNyOK7nQEj4LrT65FQ5EwrXIhqjRAj91/3t2mWx8lrWkZoEqbBoa/0 j6Ykw== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggdduieehtdelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedupdhmohguvgepshhmthhp 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, 29 Oct 2025 19:44:34 -0400 (EDT) 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, apparmor@lists.ubuntu.com, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v4 01/14] debugfs: rename end_creating() to debugfs_end_creating() Date: Thu, 30 Oct 2025 10:31:01 +1100 Message-ID: <20251029234353.1321957-2-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251029234353.1321957-1-neilb@ownmail.net> References: <20251029234353.1321957-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 By not using the generic end_creating() name here we are free to use it more globally for a more generic function. This should have been done when start_creating() was renamed. For consistency, also rename failed_creating(). Reviewed-by: Amir Goldstein Reviewed-by: Jeff Layton Signed-off-by: NeilBrown Acked-by: Greg Kroah-Hartman --- fs/debugfs/inode.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 661a99a7dfbe..f241b9df642a 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -403,7 +403,7 @@ static struct dentry *debugfs_start_creating(const char= *name, return dentry; } =20 -static struct dentry *failed_creating(struct dentry *dentry) +static struct dentry *debugfs_failed_creating(struct dentry *dentry) { inode_unlock(d_inode(dentry->d_parent)); dput(dentry); @@ -411,7 +411,7 @@ static struct dentry *failed_creating(struct dentry *de= ntry) return ERR_PTR(-ENOMEM); } =20 -static struct dentry *end_creating(struct dentry *dentry) +static struct dentry *debugfs_end_creating(struct dentry *dentry) { inode_unlock(d_inode(dentry->d_parent)); return dentry; @@ -435,7 +435,7 @@ static struct dentry *__debugfs_create_file(const char = *name, umode_t mode, return dentry; =20 if (!(debugfs_allow & DEBUGFS_ALLOW_API)) { - failed_creating(dentry); + debugfs_failed_creating(dentry); return ERR_PTR(-EPERM); } =20 @@ -443,7 +443,7 @@ static struct dentry *__debugfs_create_file(const char = *name, umode_t mode, if (unlikely(!inode)) { pr_err("out of free dentries, can not create file '%s'\n", name); - return failed_creating(dentry); + return debugfs_failed_creating(dentry); } =20 inode->i_mode =3D mode; @@ -458,7 +458,7 @@ static struct dentry *__debugfs_create_file(const char = *name, umode_t mode, =20 d_instantiate(dentry, inode); fsnotify_create(d_inode(dentry->d_parent), dentry); - return end_creating(dentry); + return debugfs_end_creating(dentry); } =20 struct dentry *debugfs_create_file_full(const char *name, umode_t mode, @@ -585,7 +585,7 @@ struct dentry *debugfs_create_dir(const char *name, str= uct dentry *parent) return dentry; =20 if (!(debugfs_allow & DEBUGFS_ALLOW_API)) { - failed_creating(dentry); + debugfs_failed_creating(dentry); return ERR_PTR(-EPERM); } =20 @@ -593,7 +593,7 @@ struct dentry *debugfs_create_dir(const char *name, str= uct dentry *parent) if (unlikely(!inode)) { pr_err("out of free dentries, can not create directory '%s'\n", name); - return failed_creating(dentry); + return debugfs_failed_creating(dentry); } =20 inode->i_mode =3D S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; @@ -605,7 +605,7 @@ struct dentry *debugfs_create_dir(const char *name, str= uct dentry *parent) d_instantiate(dentry, inode); inc_nlink(d_inode(dentry->d_parent)); fsnotify_mkdir(d_inode(dentry->d_parent), dentry); - return end_creating(dentry); + return debugfs_end_creating(dentry); } EXPORT_SYMBOL_GPL(debugfs_create_dir); =20 @@ -632,7 +632,7 @@ struct dentry *debugfs_create_automount(const char *nam= e, return dentry; =20 if (!(debugfs_allow & DEBUGFS_ALLOW_API)) { - failed_creating(dentry); + debugfs_failed_creating(dentry); return ERR_PTR(-EPERM); } =20 @@ -640,7 +640,7 @@ struct dentry *debugfs_create_automount(const char *nam= e, if (unlikely(!inode)) { pr_err("out of free dentries, can not create automount '%s'\n", name); - return failed_creating(dentry); + return debugfs_failed_creating(dentry); } =20 make_empty_dir_inode(inode); @@ -652,7 +652,7 @@ struct dentry *debugfs_create_automount(const char *nam= e, d_instantiate(dentry, inode); inc_nlink(d_inode(dentry->d_parent)); fsnotify_mkdir(d_inode(dentry->d_parent), dentry); - return end_creating(dentry); + return debugfs_end_creating(dentry); } EXPORT_SYMBOL(debugfs_create_automount); =20 @@ -699,13 +699,13 @@ struct dentry *debugfs_create_symlink(const char *nam= e, struct dentry *parent, pr_err("out of free dentries, can not create symlink '%s'\n", name); kfree(link); - return failed_creating(dentry); + return debugfs_failed_creating(dentry); } inode->i_mode =3D S_IFLNK | S_IRWXUGO; inode->i_op =3D &debugfs_symlink_inode_operations; inode->i_link =3D link; d_instantiate(dentry, inode); - return end_creating(dentry); + return debugfs_end_creating(dentry); } EXPORT_SYMBOL_GPL(debugfs_create_symlink); =20 --=20 2.50.0.107.gf914562f5916.dirty From nobody Sun Dec 14 05:53:24 2025 Received: from flow-b4-smtp.messagingengine.com (flow-b4-smtp.messagingengine.com [202.12.124.139]) (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 8B9B237A3D2; Wed, 29 Oct 2025 23:45:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=202.12.124.139 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781503; cv=none; b=Rwn6crW90NHD0T/dtY+l0gRYeWJy2hny/w2Ha2ngi0QCl+4ym9xR1+zHvbR692PVzL1dEg1hBK1H7pL4Tw999CgtcM4pkOPNOpG22vrm3QQiNF98OSWzxp4FU2L6XfN/riM7A0RQFh8vA0sk5SHKq4jSV7VqJ7Ta0Ba1bJhk+rU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781503; c=relaxed/simple; bh=7z1puoGc0Maw2mYrBj5q5gzBpwaxvsXaP30tLYcOr9E=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Uv8fXIafL32lNRHZi/IoXh/pkNbZGZIEdmjOHMg4bMVj7KqPfMsYjEcADIhlJcWvyEH+fCz/0wSsVeiJqDZ3LBXa5BaEw1RJJJJe2eXnJfrUaDjNsLjwmZPiuPMunHULULHnorazythTLSJsX5bAwn84igt0khr33gXNO3krsqw= 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=asb4BGtb; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=WsKJfaAW; arc=none smtp.client-ip=202.12.124.139 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="asb4BGtb"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="WsKJfaAW" Received: from phl-compute-01.internal (phl-compute-01.internal [10.202.2.41]) by mailflow.stl.internal (Postfix) with ESMTP id ECC6313001A1; Wed, 29 Oct 2025 19:44:59 -0400 (EDT) Received: from phl-mailfrontend-01 ([10.202.2.162]) by phl-compute-01.internal (MEProxy); Wed, 29 Oct 2025 19:45:00 -0400 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=1761781499; x=1761788699; bh=XmECYJsbB/jZ2LHmTB/scwrXUR8F+2VoGp+BtgaR5S4=; b= asb4BGtbwh9wJXIiH4YOnEc1vqJzfKCJ1pF5tjVkjAoqlhNAdns7au+bOm/OSV+b 3MAVjXQz3aUHdSOyC9a6EncbqQijTGrM0dtYITJ3Lg7T+vamS3idF/xjVN/cljz6 qsTv82RqsSLbYCoWedCaYIKBiKXAXHIdURluz5oCAiJaldgyE4LL4qpDz/DrM+yD rboZYLoPUHVpWDdaL77ldIJQv4ZM0dyhIGICKKXAd9n1oLtPw0K4pwG0Fu1kq0CG l7hLorGpsLnoFmVb/r904woHGydLA4yDxqH9z7h9CwbtYAic6uZ88navVXT0+FQm 850lXP2a7hOFi8n0dX0Gww== 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=1761781499; x=1761788699; bh=X mECYJsbB/jZ2LHmTB/scwrXUR8F+2VoGp+BtgaR5S4=; b=WsKJfaAWyjGr/eolt u98gY3aY1buBgg6EINOgIAvYa7WFSKpIdlERn6v0ZxJHoNYA4nVoK6a+s1a3ZWH5 231vH/qVp5K9WMHKxtGc1ETEnQD58LTT9yW7ND6JydI3OQV1SXOM9z6x3AXWmIGD 261P6jX9PSiWcgbT+ZLalxhazH0ZmS2HNHJujH4F2HvZ5ZWMo1DDcoLTWtLMVqg5 JsEkMx/6rZzzmBBOUtb/7fHKCxaikrVucof8ybDiq7dMqdemQYk5eloqc0+QCkty 7PYvhYMT/i2X7O5cvEutJ0XOHsFWf15Vp3AATiMa2BELzKnjU6T4eAzMCB5tMGRr Xd0LQ== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggdduieehtdekucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedupdhmohguvgepshhmthhp 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, 29 Oct 2025 19:44:48 -0400 (EDT) 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, apparmor@lists.ubuntu.com, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v4 02/14] VFS: introduce start_dirop() and end_dirop() Date: Thu, 30 Oct 2025 10:31:02 +1100 Message-ID: <20251029234353.1321957-3-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251029234353.1321957-1-neilb@ownmail.net> References: <20251029234353.1321957-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 The fact that directory operations (create,remove,rename) are protected by a lock on the parent is known widely throughout the kernel. In order to change this - to instead lock the target dentry - it is best to centralise this knowledge so it can be changed in one place. This patch introduces start_dirop() which is local to VFS code. It performs the required locking for create and remove. Rename will be handled separately. Various functions with names like start_creating() or start_removing_path(), some of which already exist, will export this functionality beyond the VFS. end_dirop() is the partner of start_dirop(). It drops the lock and releases the reference on the dentry. It *is* exported so that various end_creating etc functions can be inline. As vfs_mkdir() drops the dentry on error we cannot use end_dirop() as that won't unlock when the dentry IS_ERR(). For now we need an explicit unlock when dentry IS_ERR(). I hope to change vfs_mkdir() to unlock when it drops a dentry so that explicit unlock can go away. end_dirop() can always be called on the result of start_dirop(), but not after vfs_mkdir(). After a vfs_mkdir() we still may need the explicit unlock as seen in end_creating_path(). As well as adding start_dirop() and end_dirop() this patch uses them in: - simple_start_creating (which requires sharing lookup_noperm_common() with libfs.c) - start_removing_path / start_removing_user_path_at - filename_create / end_creating_path() - do_rmdir(), do_unlinkat() Reviewed-by: Amir Goldstein Signed-off-by: NeilBrown --- fs/internal.h | 3 ++ fs/libfs.c | 36 ++++++++--------- fs/namei.c | 98 ++++++++++++++++++++++++++++++++++------------ include/linux/fs.h | 2 + 4 files changed, 95 insertions(+), 44 deletions(-) diff --git a/fs/internal.h b/fs/internal.h index 9b2b4d116880..d08d5e2235e9 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -67,6 +67,9 @@ int vfs_tmpfile(struct mnt_idmap *idmap, const struct path *parentpath, struct file *file, umode_t mode); struct dentry *d_hash_and_lookup(struct dentry *, struct qstr *); +struct dentry *start_dirop(struct dentry *parent, struct qstr *name, + unsigned int lookup_flags); +int lookup_noperm_common(struct qstr *qname, struct dentry *base); =20 /* * namespace.c diff --git a/fs/libfs.c b/fs/libfs.c index ce8c496a6940..02371f45ef7d 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -2289,27 +2289,25 @@ void stashed_dentry_prune(struct dentry *dentry) cmpxchg(stashed, dentry, NULL); } =20 -/* parent must be held exclusive */ +/** + * simple_start_creating - prepare to create a given name + * @parent: directory in which to prepare to create the name + * @name: the name to be created + * + * Required lock is taken and a lookup in performed prior to creating an + * object in a directory. No permission checking is performed. + * + * Returns: a negative dentry on which vfs_create() or similar may + * be attempted, or an error. + */ struct dentry *simple_start_creating(struct dentry *parent, const char *na= me) { - struct dentry *dentry; - struct inode *dir =3D d_inode(parent); + struct qstr qname =3D QSTR(name); + int err; =20 - inode_lock(dir); - if (unlikely(IS_DEADDIR(dir))) { - inode_unlock(dir); - return ERR_PTR(-ENOENT); - } - dentry =3D lookup_noperm(&QSTR(name), parent); - if (IS_ERR(dentry)) { - inode_unlock(dir); - return dentry; - } - if (dentry->d_inode) { - dput(dentry); - inode_unlock(dir); - return ERR_PTR(-EEXIST); - } - return dentry; + err =3D lookup_noperm_common(&qname, parent); + if (err) + return ERR_PTR(err); + return start_dirop(parent, &qname, LOOKUP_CREATE | LOOKUP_EXCL); } EXPORT_SYMBOL(simple_start_creating); diff --git a/fs/namei.c b/fs/namei.c index 7377020a2cba..3618efd4bcaa 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2765,6 +2765,48 @@ static int filename_parentat(int dfd, struct filenam= e *name, return __filename_parentat(dfd, name, flags, parent, last, type, NULL); } =20 +/** + * start_dirop - begin a create or remove dirop, performing locking and lo= okup + * @parent: the dentry of the parent in which the operation will occ= ur + * @name: a qstr holding the name within that parent + * @lookup_flags: intent and other lookup flags. + * + * The lookup is performed and necessary locks are taken so that, on succe= ss, + * the returned dentry can be operated on safely. + * The qstr must already have the hash value calculated. + * + * Returns: a locked dentry, or an error. + * + */ +struct dentry *start_dirop(struct dentry *parent, struct qstr *name, + unsigned int lookup_flags) +{ + struct dentry *dentry; + struct inode *dir =3D d_inode(parent); + + inode_lock_nested(dir, I_MUTEX_PARENT); + dentry =3D lookup_one_qstr_excl(name, parent, lookup_flags); + if (IS_ERR(dentry)) + inode_unlock(dir); + return dentry; +} + +/** + * end_dirop - signal completion of a dirop + * @de: the dentry which was returned by start_dirop or similar. + * + * If the de is an error, nothing happens. Otherwise any lock taken to + * protect the dentry is dropped and the dentry itself is release (dput()). + */ +void end_dirop(struct dentry *de) +{ + if (!IS_ERR(de)) { + inode_unlock(de->d_parent->d_inode); + dput(de); + } +} +EXPORT_SYMBOL(end_dirop); + /* does lookup, returns the object with parent locked */ static struct dentry *__start_removing_path(int dfd, struct filename *name, struct path *path) @@ -2781,10 +2823,9 @@ static struct dentry *__start_removing_path(int dfd,= struct filename *name, return ERR_PTR(-EINVAL); /* don't fail immediately if it's r/o, at least try to report other error= s */ error =3D mnt_want_write(parent_path.mnt); - inode_lock_nested(parent_path.dentry->d_inode, I_MUTEX_PARENT); - d =3D lookup_one_qstr_excl(&last, parent_path.dentry, 0); + d =3D start_dirop(parent_path.dentry, &last, 0); if (IS_ERR(d)) - goto unlock; + goto drop; if (error) goto fail; path->dentry =3D no_free_ptr(parent_path.dentry); @@ -2792,10 +2833,9 @@ static struct dentry *__start_removing_path(int dfd,= struct filename *name, return d; =20 fail: - dput(d); + end_dirop(d); d =3D ERR_PTR(error); -unlock: - inode_unlock(parent_path.dentry->d_inode); +drop: if (!error) mnt_drop_write(parent_path.mnt); return d; @@ -2910,7 +2950,7 @@ int vfs_path_lookup(struct dentry *dentry, struct vfs= mount *mnt, } EXPORT_SYMBOL(vfs_path_lookup); =20 -static int lookup_noperm_common(struct qstr *qname, struct dentry *base) +int lookup_noperm_common(struct qstr *qname, struct dentry *base) { const char *name =3D qname->name; u32 len =3D qname->len; @@ -4223,21 +4263,18 @@ static struct dentry *filename_create(int dfd, stru= ct filename *name, */ if (last.name[last.len] && !want_dir) create_flags &=3D ~LOOKUP_CREATE; - inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT); - dentry =3D lookup_one_qstr_excl(&last, path->dentry, - reval_flag | create_flags); + dentry =3D start_dirop(path->dentry, &last, reval_flag | create_flags); if (IS_ERR(dentry)) - goto unlock; + goto out_drop_write; =20 if (unlikely(error)) goto fail; =20 return dentry; fail: - dput(dentry); + end_dirop(dentry); dentry =3D ERR_PTR(error); -unlock: - inode_unlock(path->dentry->d_inode); +out_drop_write: if (!error) mnt_drop_write(path->mnt); out: @@ -4256,11 +4293,26 @@ struct dentry *start_creating_path(int dfd, const c= har *pathname, } EXPORT_SYMBOL(start_creating_path); =20 +/** + * end_creating_path - finish a code section started by start_creating_pat= h() + * @path: the path instantiated by start_creating_path() + * @dentry: the dentry returned by start_creating_path() + * + * end_creating_path() will unlock and locks taken by start_creating_path() + * and drop an references that were taken. It should only be called + * if start_creating_path() returned a non-error. + * If vfs_mkdir() was called and it returned an error, that error *should* + * be passed to end_creating_path() together with the path. + */ void end_creating_path(const struct path *path, struct dentry *dentry) { - if (!IS_ERR(dentry)) - dput(dentry); - inode_unlock(path->dentry->d_inode); + if (IS_ERR(dentry)) + /* The parent is still locked despite the error from + * vfs_mkdir() - must unlock it. + */ + inode_unlock(path->dentry->d_inode); + else + end_dirop(dentry); mnt_drop_write(path->mnt); path_put(path); } @@ -4592,8 +4644,7 @@ int do_rmdir(int dfd, struct filename *name) if (error) goto exit2; =20 - inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT); - dentry =3D lookup_one_qstr_excl(&last, path.dentry, lookup_flags); + dentry =3D start_dirop(path.dentry, &last, lookup_flags); error =3D PTR_ERR(dentry); if (IS_ERR(dentry)) goto exit3; @@ -4602,9 +4653,8 @@ int do_rmdir(int dfd, struct filename *name) goto exit4; error =3D vfs_rmdir(mnt_idmap(path.mnt), path.dentry->d_inode, dentry); exit4: - dput(dentry); + end_dirop(dentry); exit3: - inode_unlock(path.dentry->d_inode); mnt_drop_write(path.mnt); exit2: path_put(&path); @@ -4721,8 +4771,7 @@ int do_unlinkat(int dfd, struct filename *name) if (error) goto exit2; retry_deleg: - inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT); - dentry =3D lookup_one_qstr_excl(&last, path.dentry, lookup_flags); + dentry =3D start_dirop(path.dentry, &last, lookup_flags); error =3D PTR_ERR(dentry); if (!IS_ERR(dentry)) { =20 @@ -4737,9 +4786,8 @@ int do_unlinkat(int dfd, struct filename *name) error =3D vfs_unlink(mnt_idmap(path.mnt), path.dentry->d_inode, dentry, &delegated_inode); exit3: - dput(dentry); + end_dirop(dentry); } - inode_unlock(path.dentry->d_inode); if (inode) iput(inode); /* truncate the inode here */ inode =3D NULL; diff --git a/include/linux/fs.h b/include/linux/fs.h index c895146c1444..f4543612ef1e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3609,6 +3609,8 @@ extern void iterate_supers_type(struct file_system_ty= pe *, void filesystems_freeze(void); void filesystems_thaw(void); =20 +void end_dirop(struct dentry *de); + extern int dcache_dir_open(struct inode *, struct file *); extern int dcache_dir_close(struct inode *, struct file *); extern loff_t dcache_dir_lseek(struct file *, loff_t, int); --=20 2.50.0.107.gf914562f5916.dirty From nobody Sun Dec 14 05:53:24 2025 Received: from flow-b4-smtp.messagingengine.com (flow-b4-smtp.messagingengine.com [202.12.124.139]) (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 BEB7C320CBB; Wed, 29 Oct 2025 23:45:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=202.12.124.139 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781517; cv=none; b=TuWumy6tORXb7818foSkkls3Rs0CZwm8tdDr1zDn2G/gozq4O25vZjg0tc/RnaipRTIq3jD7Df7SO1Icd1oCysbXURI/qLq+sNLUWsHhvI+PAjWz1q9laFkqRTLFCCJU+YU4C7v45+zeCRaG2c44mUK3qwcTD5BUUOWK34g+Cgk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781517; c=relaxed/simple; bh=oVS+h5r0DK/yL/nJuSoEVwkYmXX5izACS08ItM8n8P4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Ubck5AuogjROC2hnbXRE0eEN+aa6JqIai0OZ5l3DW5SqpWyY/IIRuKWYeJvfRklmCM80equHR/JqgzFh5wowNsLbA3oJbTXCjqVAdET6rUT+rLvhGfkJU8t3hEE5C1AsegORmDdAAetbVkSU+FZskkegYEhBjmYn8pk9J0txkNE= 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=YUeSnern; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=ffyA31nR; arc=none smtp.client-ip=202.12.124.139 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="YUeSnern"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="ffyA31nR" Received: from phl-compute-06.internal (phl-compute-06.internal [10.202.2.46]) by mailflow.stl.internal (Postfix) with ESMTP id 0C71B1300228; Wed, 29 Oct 2025 19:45:14 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-06.internal (MEProxy); Wed, 29 Oct 2025 19:45:15 -0400 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=1761781513; x=1761788713; bh=bd+J28EpkqsL+J3nsCE3KaLQTbVtAeBoRgW0o7OZd6Q=; b= YUeSnernCnh+2nQr8CxjtEzQZY0jFVgVVRGble88W+44mPbvXoihllhMI4v2ZhrO qWIvo68KBBp7gXV/XJ3FMwLmf2gSawuYU2HCgKzLX59vbxkyJ5wLTDnYe5pE/U8S acHMznQs4pMz31cXOemU1qEh+d+KV1xN1qDW3pfb6nfaSAQy2hsZKJvpymyaobVB G3yFNZcRR7mN40Ihb2v/2V+thBZ+EpfoF0MwYfcI8hlndKtaKtuV8O878QLi2i0X KzcZ2NCXWazgVUuCbthENAD/+lJxGVT9lDMFCkaVYGT/1wDgk9+/iWqpRkpOYbfY JIJEGP5KHvCBIQjT1Lkstg== 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=1761781513; x=1761788713; bh=b d+J28EpkqsL+J3nsCE3KaLQTbVtAeBoRgW0o7OZd6Q=; b=ffyA31nRBEIf8hr/d VTpNvZr1DY/UoCijvjV+GM9VeBh3RGMwjqzIlOEFj9/Mor9hNUmemf3z2V4dSViO 9glt7i0ZDNoVjfSuDrhH0P8qjfHwqg4nv9GTxGPaySGSsybNrEP7e4MlVEFasNfy VI6OZnJypxCAJ/TaWuvf32NX3PIXM1hzmOHDbFd38k2/ePpSLkizb5LQRFW7ey1k 4G6Mk7LA6ryoVMpDyZRed7xdO1qbP2ZQhOKkmpMzSWeqoncqxLp+mWjuYzIdTjf8 lGR8/3yyXKUCwTnV/qPd+tUlUAYU+wBWobyNoax5mLfvT9pzAv/w8VzRenNwBCVx Bapnw== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggdduieehtdekucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedupdhmohguvgepshhmthhp 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, 29 Oct 2025 19:45:03 -0400 (EDT) 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, apparmor@lists.ubuntu.com, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v4 03/14] VFS: tidy up do_unlinkat() Date: Thu, 30 Oct 2025 10:31:03 +1100 Message-ID: <20251029234353.1321957-4-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251029234353.1321957-1-neilb@ownmail.net> References: <20251029234353.1321957-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 The simplification of locking in the previous patch opens up some room for tidying up do_unlinkat() - change all "exit" labels to describe what will happen at the label. - always goto an exit label on an error - unwrap the "if (!IS_ERR())" branc= h. - Move the "slashes" handing inline, but mark it as unlikely() - simplify use of the "inode" variable - we no longer need to test for NULL. Reviewed-by: Amir Goldstein Signed-off-by: NeilBrown --- fs/namei.c | 55 ++++++++++++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 3618efd4bcaa..9effaad115d9 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4755,65 +4755,62 @@ int do_unlinkat(int dfd, struct filename *name) struct path path; struct qstr last; int type; - struct inode *inode =3D NULL; + struct inode *inode; struct inode *delegated_inode =3D NULL; unsigned int lookup_flags =3D 0; retry: error =3D filename_parentat(dfd, name, lookup_flags, &path, &last, &type); if (error) - goto exit1; + goto exit_putname; =20 error =3D -EISDIR; if (type !=3D LAST_NORM) - goto exit2; + goto exit_path_put; =20 error =3D mnt_want_write(path.mnt); if (error) - goto exit2; + goto exit_path_put; retry_deleg: dentry =3D start_dirop(path.dentry, &last, lookup_flags); error =3D PTR_ERR(dentry); - if (!IS_ERR(dentry)) { + if (IS_ERR(dentry)) + goto exit_drop_write; =20 - /* Why not before? Because we want correct error value */ - if (last.name[last.len]) - goto slashes; - inode =3D dentry->d_inode; - ihold(inode); - error =3D security_path_unlink(&path, dentry); - if (error) - goto exit3; - error =3D vfs_unlink(mnt_idmap(path.mnt), path.dentry->d_inode, - dentry, &delegated_inode); -exit3: + /* Why not before? Because we want correct error value */ + if (unlikely(last.name[last.len])) { + if (d_is_dir(dentry)) + error =3D -EISDIR; + else + error =3D -ENOTDIR; end_dirop(dentry); + goto exit_drop_write; } - if (inode) - iput(inode); /* truncate the inode here */ - inode =3D NULL; + inode =3D dentry->d_inode; + ihold(inode); + error =3D security_path_unlink(&path, dentry); + if (error) + goto exit_end_dirop; + error =3D vfs_unlink(mnt_idmap(path.mnt), path.dentry->d_inode, + dentry, &delegated_inode); +exit_end_dirop: + end_dirop(dentry); + iput(inode); /* truncate the inode here */ if (delegated_inode) { error =3D break_deleg_wait(&delegated_inode); if (!error) goto retry_deleg; } +exit_drop_write: mnt_drop_write(path.mnt); -exit2: +exit_path_put: path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |=3D LOOKUP_REVAL; - inode =3D NULL; goto retry; } -exit1: +exit_putname: putname(name); return error; - -slashes: - if (d_is_dir(dentry)) - error =3D -EISDIR; - else - error =3D -ENOTDIR; - goto exit3; } =20 SYSCALL_DEFINE3(unlinkat, int, dfd, const char __user *, pathname, int, fl= ag) --=20 2.50.0.107.gf914562f5916.dirty From nobody Sun Dec 14 05:53:24 2025 Received: from flow-b4-smtp.messagingengine.com (flow-b4-smtp.messagingengine.com [202.12.124.139]) (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 077F4285042; Wed, 29 Oct 2025 23:45:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=202.12.124.139 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781532; cv=none; b=Mt8tEiPa9kJiS+uwfn+lEsHnMVMkYevUGTtzakpRajTh/R+Pb1nrapuTvkSJKdW1verFqbX14s6MRmM6zr/GTS5o/lVIa1k/bOVjqkrQyTiBkuWUGxu6GciSXnxA4QAkEaYAh+bIAV/lEgubPjadXnQ35eBQHjJDLEn4MKWKsB4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781532; c=relaxed/simple; bh=3Qbvsp+6W/rS1Dinq+RweiATbygu4bE2NiWmSazGi/Q=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QepkeDf5UmpdJw7B18yKFG3g6BkUXeLJfLmSThEFjM0eYwqLbehd2HdCT8xBs3m1J3eVACT9xzhupH0qO979TkWiwcJsp7qk4XK6z/D+8Y3lowgdnb0/VWsYO7Zi2/9pLmghfxqh9k3QQjIvTxADISQVA/PaC9Hl4dAtEGss4s4= 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=HFFTBdY4; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=SqubnBEO; arc=none smtp.client-ip=202.12.124.139 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="HFFTBdY4"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="SqubnBEO" Received: from phl-compute-07.internal (phl-compute-07.internal [10.202.2.47]) by mailflow.stl.internal (Postfix) with ESMTP id 81DD2130028C; Wed, 29 Oct 2025 19:45:28 -0400 (EDT) Received: from phl-mailfrontend-01 ([10.202.2.162]) by phl-compute-07.internal (MEProxy); Wed, 29 Oct 2025 19:45:29 -0400 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=1761781528; x=1761788728; bh=hExboH2St0Q6U1dVQXYfA6SlavJu+iiu5icpodZgYBE=; b= HFFTBdY4QKNGEGqy6SB3ohGHg5UocpMtXGN2zzd5M70Tf4Z8MCLDeHLqYZT85lNp hi8Jp6KSiWaXmnzu/oMSRUMK9+4pM2xD8y/7MSHuj89mx2mmvBkeqpzvPXDlRURG ethmy3gJBvoJUa3S5U0htZx4OBY8QmRojb2dDV4oB99Z5JBSbaULmAYqwp2Uw42c cfBHvaYlJXt3EE+yQ4zYHOWRSdeKqMKddL9o08H728J8rmLW2s4v7WpMXuSMaURv JGMcTDC/w4zBcxoGwlaGy852Kx9d81mNGTFx5q7mRdeCa7db5WAC/0yAxuBJwpUf yzpRisgVrukqrIyGFdP2Og== 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=1761781528; x=1761788728; bh=h ExboH2St0Q6U1dVQXYfA6SlavJu+iiu5icpodZgYBE=; b=SqubnBEO8+gNa/Q5x YOa+gUDbfXHgoLSUXlwFzhL4IXQ5yqH3aVsbQziBbOhqxR+lM8rvD+6hfoOmikPH Q0BGDGzRdfwVGcCkFUj89KFheR8cI1Ixj1KiYLt/d4SfW5C19UOTZvjtoFFbSi7m pDZtYgITTq9TkWibwrjfohls0PY4km9zXF/7d7bPS0CbXtuhkg1xYxMatjHBWkz4 h5i7nkl0yZZ1benX2z7BMWJgBoH8a/BjMkQ/gRwCWLbiMuAAzHVB3QUL13XMXmef aeQaNL/zu+VSgw85Ht3krks8oBX1pg8kEVatBv4nkzABDLmi2N25JO6F5gZ50xw+ nX8YA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggdduieehtdekucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedupdhmohguvgepshhmthhp 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, 29 Oct 2025 19:45:17 -0400 (EDT) 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, apparmor@lists.ubuntu.com, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v4 04/14] VFS/nfsd/cachefiles/ovl: add start_creating() and end_creating() Date: Thu, 30 Oct 2025 10:31:04 +1100 Message-ID: <20251029234353.1321957-5-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251029234353.1321957-1-neilb@ownmail.net> References: <20251029234353.1321957-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_creating() is similar to simple_start_creating() but is not so simple. It takes a qstr for the name, includes permission checking, and does NOT report an error if the name already exists, returning a positive dentry instead. This is currently used by nfsd, cachefiles, and overlayfs. end_creating() is called after the dentry has been used. end_creating() drops the reference to the dentry as it is generally no longer needed. This is exactly the first section of end_creating_path() so that function is changed to call the new end_creating() These calls help encapsulate locking rules so that directory locking can be changed. Occasionally this change means that the parent lock is held for a shorter period of time, for example in cachefiles_commit_tmpfile(). As this function now unlocks after an unlink and before the following lookup, it is possible that the lookup could again find a positive dentry, so a while loop is introduced there. In overlayfs the ovl_lookup_temp() function has ovl_tempname() split out to be used in ovl_start_creating_temp(). The other use of ovl_lookup_temp() is preparing for a rename. When rename handling is updated, ovl_lookup_temp() will be removed. Reviewed-by: Jeff Layton Reviewed-by: Amir Goldstein Signed-off-by: NeilBrown --- fs/cachefiles/namei.c | 41 ++++++++--------- fs/namei.c | 35 ++++++++++++--- fs/nfsd/nfs3proc.c | 14 +++--- fs/nfsd/nfs4proc.c | 14 +++--- fs/nfsd/nfs4recover.c | 16 +++---- fs/nfsd/nfsproc.c | 11 +++-- fs/nfsd/vfs.c | 52 +++++++++------------- fs/overlayfs/copy_up.c | 19 ++++---- fs/overlayfs/dir.c | 96 +++++++++++++++++++++++----------------- fs/overlayfs/overlayfs.h | 8 ++++ fs/overlayfs/super.c | 32 +++++++------- include/linux/namei.h | 33 ++++++++++++++ 12 files changed, 213 insertions(+), 158 deletions(-) diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index d1edb2ac3837..0a136eb434da 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -93,12 +93,11 @@ struct dentry *cachefiles_get_directory(struct cachefil= es_cache *cache, _enter(",,%s", dirname); =20 /* search the current directory for the element name */ - inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); =20 retry: ret =3D cachefiles_inject_read_error(); if (ret =3D=3D 0) - subdir =3D lookup_one(&nop_mnt_idmap, &QSTR(dirname), dir); + subdir =3D start_creating(&nop_mnt_idmap, dir, &QSTR(dirname)); else subdir =3D ERR_PTR(ret); trace_cachefiles_lookup(NULL, dir, subdir); @@ -141,7 +140,7 @@ struct dentry *cachefiles_get_directory(struct cachefil= es_cache *cache, trace_cachefiles_mkdir(dir, subdir); =20 if (unlikely(d_unhashed(subdir) || d_is_negative(subdir))) { - dput(subdir); + end_creating(subdir, dir); goto retry; } ASSERT(d_backing_inode(subdir)); @@ -154,7 +153,8 @@ struct dentry *cachefiles_get_directory(struct cachefil= es_cache *cache, =20 /* Tell rmdir() it's not allowed to delete the subdir */ inode_lock(d_inode(subdir)); - inode_unlock(d_inode(dir)); + dget(subdir); + end_creating(subdir, dir); =20 if (!__cachefiles_mark_inode_in_use(NULL, d_inode(subdir))) { pr_notice("cachefiles: Inode already in use: %pd (B=3D%lx)\n", @@ -196,14 +196,11 @@ struct dentry *cachefiles_get_directory(struct cachef= iles_cache *cache, return ERR_PTR(-EBUSY); =20 mkdir_error: - inode_unlock(d_inode(dir)); - if (!IS_ERR(subdir)) - dput(subdir); + end_creating(subdir, dir); pr_err("mkdir %s failed with error %d\n", dirname, ret); return ERR_PTR(ret); =20 lookup_error: - inode_unlock(d_inode(dir)); ret =3D PTR_ERR(subdir); pr_err("Lookup %s failed with error %d\n", dirname, ret); return ERR_PTR(ret); @@ -679,36 +676,41 @@ bool cachefiles_commit_tmpfile(struct cachefiles_cach= e *cache, =20 _enter(",%pD", object->file); =20 - inode_lock_nested(d_inode(fan), I_MUTEX_PARENT); ret =3D cachefiles_inject_read_error(); if (ret =3D=3D 0) - dentry =3D lookup_one(&nop_mnt_idmap, &QSTR(object->d_name), fan); + dentry =3D start_creating(&nop_mnt_idmap, fan, &QSTR(object->d_name)); else dentry =3D ERR_PTR(ret); if (IS_ERR(dentry)) { trace_cachefiles_vfs_error(object, d_inode(fan), PTR_ERR(dentry), cachefiles_trace_lookup_error); _debug("lookup fail %ld", PTR_ERR(dentry)); - goto out_unlock; + goto out; } =20 - if (!d_is_negative(dentry)) { + /* + * This loop will only execute more than once if some other thread + * races to create the object we are trying to create. + */ + while (!d_is_negative(dentry)) { ret =3D cachefiles_unlink(volume->cache, object, fan, dentry, FSCACHE_OBJECT_IS_STALE); if (ret < 0) - goto out_dput; + goto out_end; + + end_creating(dentry, fan); =20 - dput(dentry); ret =3D cachefiles_inject_read_error(); if (ret =3D=3D 0) - dentry =3D lookup_one(&nop_mnt_idmap, &QSTR(object->d_name), fan); + dentry =3D start_creating(&nop_mnt_idmap, fan, + &QSTR(object->d_name)); else dentry =3D ERR_PTR(ret); if (IS_ERR(dentry)) { trace_cachefiles_vfs_error(object, d_inode(fan), PTR_ERR(dentry), cachefiles_trace_lookup_error); _debug("lookup fail %ld", PTR_ERR(dentry)); - goto out_unlock; + goto out; } } =20 @@ -729,10 +731,9 @@ bool cachefiles_commit_tmpfile(struct cachefiles_cache= *cache, success =3D true; } =20 -out_dput: - dput(dentry); -out_unlock: - inode_unlock(d_inode(fan)); +out_end: + end_creating(dentry, fan); +out: _leave(" =3D %u", success); return success; } diff --git a/fs/namei.c b/fs/namei.c index 9effaad115d9..9972b0257a4c 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3221,6 +3221,33 @@ struct dentry *lookup_noperm_positive_unlocked(struc= t qstr *name, } EXPORT_SYMBOL(lookup_noperm_positive_unlocked); =20 +/** + * start_creating - prepare to create a given name with permission checking + * @idmap: idmap of the mount + * @parent: directory in which to prepare to create the name + * @name: the name to be created + * + * Locks are taken and a lookup is performed prior to creating + * an object in a directory. Permission checking (MAY_EXEC) is performed + * against @idmap. + * + * If the name already exists, a positive dentry is returned, so + * behaviour is similar to O_CREAT without O_EXCL, which doesn't fail + * with -EEXIST. + * + * Returns: a negative or positive dentry, or an error. + */ +struct dentry *start_creating(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, LOOKUP_CREATE); +} +EXPORT_SYMBOL(start_creating); + #ifdef CONFIG_UNIX98_PTYS int path_pts(struct path *path) { @@ -4306,13 +4333,7 @@ EXPORT_SYMBOL(start_creating_path); */ void end_creating_path(const struct path *path, struct dentry *dentry) { - if (IS_ERR(dentry)) - /* The parent is still locked despite the error from - * vfs_mkdir() - must unlock it. - */ - inode_unlock(path->dentry->d_inode); - else - end_dirop(dentry); + end_creating(dentry, path->dentry); mnt_drop_write(path->mnt); path_put(path); } diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index b6d03e1ef5f7..e2aac0def2cb 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c @@ -281,14 +281,11 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_= fh *fhp, if (host_err) return nfserrno(host_err); =20 - inode_lock_nested(inode, I_MUTEX_PARENT); - - child =3D lookup_one(&nop_mnt_idmap, - &QSTR_LEN(argp->name, argp->len), - parent); + child =3D start_creating(&nop_mnt_idmap, parent, + &QSTR_LEN(argp->name, argp->len)); if (IS_ERR(child)) { status =3D nfserrno(PTR_ERR(child)); - goto out; + goto out_write; } =20 if (d_really_is_negative(child)) { @@ -367,9 +364,8 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh= *fhp, status =3D nfsd_create_setattr(rqstp, fhp, resfhp, &attrs); =20 out: - inode_unlock(inode); - if (child && !IS_ERR(child)) - dput(child); + end_creating(child, parent); +out_write: fh_drop_write(fhp); return status; } diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index e466cf52d7d7..b2c95e8e7c68 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -264,14 +264,11 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_= fh *fhp, if (is_create_with_attrs(open)) nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs); =20 - inode_lock_nested(inode, I_MUTEX_PARENT); - - child =3D lookup_one(&nop_mnt_idmap, - &QSTR_LEN(open->op_fname, open->op_fnamelen), - parent); + child =3D start_creating(&nop_mnt_idmap, parent, + &QSTR_LEN(open->op_fname, open->op_fnamelen)); if (IS_ERR(child)) { status =3D nfserrno(PTR_ERR(child)); - goto out; + goto out_write; } =20 if (d_really_is_negative(child)) { @@ -379,10 +376,9 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_f= h *fhp, if (attrs.na_aclerr) open->op_bmval[0] &=3D ~FATTR4_WORD0_ACL; out: - inode_unlock(inode); + end_creating(child, parent); nfsd_attrs_free(&attrs); - if (child && !IS_ERR(child)) - dput(child); +out_write: fh_drop_write(fhp); return status; } diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index e2b9472e5c78..c247a7c3291c 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -195,13 +195,11 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) goto out_creds; =20 dir =3D nn->rec_file->f_path.dentry; - /* lock the parent */ - inode_lock(d_inode(dir)); =20 - dentry =3D lookup_one(&nop_mnt_idmap, &QSTR(dname), dir); + dentry =3D start_creating(&nop_mnt_idmap, dir, &QSTR(dname)); if (IS_ERR(dentry)) { status =3D PTR_ERR(dentry); - goto out_unlock; + goto out; } if (d_really_is_positive(dentry)) /* @@ -212,15 +210,13 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) * In the 4.0 case, we should never get here; but we may * as well be forgiving and just succeed silently. */ - goto out_put; + goto out_end; dentry =3D vfs_mkdir(&nop_mnt_idmap, d_inode(dir), dentry, S_IRWXU); if (IS_ERR(dentry)) status =3D PTR_ERR(dentry); -out_put: - if (!status) - dput(dentry); -out_unlock: - inode_unlock(d_inode(dir)); +out_end: + end_creating(dentry, dir); +out: if (status =3D=3D 0) { if (nn->in_grace) __nfsd4_create_reclaim_record_grace(clp, dname, diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index 8f71f5748c75..ee1b16e921fd 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -306,18 +306,16 @@ nfsd_proc_create(struct svc_rqst *rqstp) goto done; } =20 - inode_lock_nested(dirfhp->fh_dentry->d_inode, I_MUTEX_PARENT); - dchild =3D lookup_one(&nop_mnt_idmap, &QSTR_LEN(argp->name, argp->len), - dirfhp->fh_dentry); + dchild =3D start_creating(&nop_mnt_idmap, dirfhp->fh_dentry, + &QSTR_LEN(argp->name, argp->len)); if (IS_ERR(dchild)) { resp->status =3D nfserrno(PTR_ERR(dchild)); - goto out_unlock; + goto out_write; } fh_init(newfhp, NFS_FHSIZE); resp->status =3D fh_compose(newfhp, dirfhp->fh_export, dchild, dirfhp); if (!resp->status && d_really_is_negative(dchild)) resp->status =3D nfserr_noent; - dput(dchild); if (resp->status) { if (resp->status !=3D nfserr_noent) goto out_unlock; @@ -423,7 +421,8 @@ nfsd_proc_create(struct svc_rqst *rqstp) } =20 out_unlock: - inode_unlock(dirfhp->fh_dentry->d_inode); + end_creating(dchild, dirfhp->fh_dentry); +out_write: fh_drop_write(dirfhp); done: fh_put(dirfhp); diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 9cb20d4aeab1..4efd3688e081 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1521,7 +1521,7 @@ nfsd_check_ignore_resizing(struct iattr *iap) iap->ia_valid &=3D ~ATTR_SIZE; } =20 -/* The parent directory should already be locked: */ +/* The parent directory should already be locked - we will unlock */ __be32 nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_attrs *attrs, @@ -1587,8 +1587,9 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc= _fh *fhp, err =3D nfsd_create_setattr(rqstp, fhp, resfhp, attrs); =20 out: - if (!IS_ERR(dchild)) - dput(dchild); + if (!err) + fh_fill_post_attrs(fhp); + end_creating(dchild, dentry); return err; =20 out_nfserr: @@ -1626,28 +1627,26 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *= fhp, if (host_err) return nfserrno(host_err); =20 - inode_lock_nested(dentry->d_inode, I_MUTEX_PARENT); - dchild =3D lookup_one(&nop_mnt_idmap, &QSTR_LEN(fname, flen), dentry); + dchild =3D start_creating(&nop_mnt_idmap, dentry, &QSTR_LEN(fname, flen)); host_err =3D PTR_ERR(dchild); - if (IS_ERR(dchild)) { - err =3D nfserrno(host_err); - goto out_unlock; - } + if (IS_ERR(dchild)) + return nfserrno(host_err); + err =3D fh_compose(resfhp, fhp->fh_export, dchild, fhp); /* * We unconditionally drop our ref to dchild as fh_compose will have * already grabbed its own ref for it. */ - dput(dchild); if (err) goto out_unlock; err =3D fh_fill_pre_attrs(fhp); if (err !=3D nfs_ok) goto out_unlock; err =3D nfsd_create_locked(rqstp, fhp, attrs, type, rdev, resfhp); - fh_fill_post_attrs(fhp); + return err; + out_unlock: - inode_unlock(dentry->d_inode); + end_creating(dchild, dentry); return err; } =20 @@ -1733,11 +1732,9 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *= fhp, } =20 dentry =3D fhp->fh_dentry; - inode_lock_nested(dentry->d_inode, I_MUTEX_PARENT); - dnew =3D lookup_one(&nop_mnt_idmap, &QSTR_LEN(fname, flen), dentry); + dnew =3D start_creating(&nop_mnt_idmap, dentry, &QSTR_LEN(fname, flen)); if (IS_ERR(dnew)) { err =3D nfserrno(PTR_ERR(dnew)); - inode_unlock(dentry->d_inode); goto out_drop_write; } err =3D fh_fill_pre_attrs(fhp); @@ -1750,11 +1747,11 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh = *fhp, nfsd_create_setattr(rqstp, fhp, resfhp, attrs); fh_fill_post_attrs(fhp); out_unlock: - inode_unlock(dentry->d_inode); + end_creating(dnew, dentry); if (!err) err =3D nfserrno(commit_metadata(fhp)); - dput(dnew); - if (err=3D=3D0) err =3D cerr; + if (!err) + err =3D cerr; out_drop_write: fh_drop_write(fhp); out: @@ -1809,32 +1806,31 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ff= hp, =20 ddir =3D ffhp->fh_dentry; dirp =3D d_inode(ddir); - inode_lock_nested(dirp, I_MUTEX_PARENT); + dnew =3D start_creating(&nop_mnt_idmap, ddir, &QSTR_LEN(name, len)); =20 - dnew =3D lookup_one(&nop_mnt_idmap, &QSTR_LEN(name, len), ddir); if (IS_ERR(dnew)) { host_err =3D PTR_ERR(dnew); - goto out_unlock; + goto out_drop_write; } =20 dold =3D tfhp->fh_dentry; =20 err =3D nfserr_noent; if (d_really_is_negative(dold)) - goto out_dput; + goto out_unlock; err =3D fh_fill_pre_attrs(ffhp); if (err !=3D nfs_ok) - goto out_dput; + goto out_unlock; host_err =3D vfs_link(dold, &nop_mnt_idmap, dirp, dnew, NULL); fh_fill_post_attrs(ffhp); - inode_unlock(dirp); +out_unlock: + end_creating(dnew, ddir); if (!host_err) { host_err =3D commit_metadata(ffhp); if (!host_err) host_err =3D commit_metadata(tfhp); } =20 - dput(dnew); out_drop_write: fh_drop_write(tfhp); if (host_err =3D=3D -EBUSY) { @@ -1849,12 +1845,6 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffh= p, } out: return err !=3D nfs_ok ? err : nfserrno(host_err); - -out_dput: - dput(dnew); -out_unlock: - inode_unlock(dirp); - goto out_drop_write; } =20 static void diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index aac7e34f56c1..7a31ca9bdea2 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -613,9 +613,9 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c) if (err) goto out; =20 - inode_lock_nested(udir, I_MUTEX_PARENT); - upper =3D ovl_lookup_upper(ofs, c->dentry->d_name.name, upperdir, - c->dentry->d_name.len); + upper =3D ovl_start_creating_upper(ofs, upperdir, + &QSTR_LEN(c->dentry->d_name.name, + c->dentry->d_name.len)); err =3D PTR_ERR(upper); if (!IS_ERR(upper)) { err =3D ovl_do_link(ofs, ovl_dentry_upper(c->dentry), udir, upper); @@ -626,9 +626,8 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c) ovl_dentry_set_upper_alias(c->dentry); ovl_dentry_update_reval(c->dentry, upper); } - dput(upper); + end_creating(upper, upperdir); } - inode_unlock(udir); if (err) goto out; =20 @@ -894,16 +893,14 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx= *c) if (err) goto out; =20 - inode_lock_nested(udir, I_MUTEX_PARENT); - - upper =3D ovl_lookup_upper(ofs, c->destname.name, c->destdir, - c->destname.len); + upper =3D ovl_start_creating_upper(ofs, c->destdir, + &QSTR_LEN(c->destname.name, + c->destname.len)); err =3D PTR_ERR(upper); if (!IS_ERR(upper)) { err =3D ovl_do_link(ofs, temp, udir, upper); - dput(upper); + end_creating(upper, c->destdir); } - inode_unlock(udir); =20 if (err) goto out; diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index a5e9ddf3023b..a8a24abee6b3 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -59,15 +59,21 @@ int ovl_cleanup(struct ovl_fs *ofs, struct dentry *work= dir, return 0; } =20 -struct dentry *ovl_lookup_temp(struct ovl_fs *ofs, struct dentry *workdir) +#define OVL_TEMPNAME_SIZE 20 +static void ovl_tempname(char name[OVL_TEMPNAME_SIZE]) { - struct dentry *temp; - char name[20]; static atomic_t temp_id =3D ATOMIC_INIT(0); =20 /* counter is allowed to wrap, since temp dentries are ephemeral */ - snprintf(name, sizeof(name), "#%x", atomic_inc_return(&temp_id)); + snprintf(name, OVL_TEMPNAME_SIZE, "#%x", atomic_inc_return(&temp_id)); +} + +struct dentry *ovl_lookup_temp(struct ovl_fs *ofs, struct dentry *workdir) +{ + struct dentry *temp; + char name[OVL_TEMPNAME_SIZE]; =20 + ovl_tempname(name); temp =3D ovl_lookup_upper(ofs, name, workdir, strlen(name)); if (!IS_ERR(temp) && temp->d_inode) { pr_err("workdir/%s already exists\n", name); @@ -78,45 +84,49 @@ struct dentry *ovl_lookup_temp(struct ovl_fs *ofs, stru= ct dentry *workdir) return temp; } =20 +static struct dentry *ovl_start_creating_temp(struct ovl_fs *ofs, + struct dentry *workdir) +{ + char name[OVL_TEMPNAME_SIZE]; + + ovl_tempname(name); + return start_creating(ovl_upper_mnt_idmap(ofs), workdir, + &QSTR(name)); +} + static struct dentry *ovl_whiteout(struct ovl_fs *ofs) { int err; - struct dentry *whiteout; + struct dentry *whiteout, *link; struct dentry *workdir =3D ofs->workdir; struct inode *wdir =3D workdir->d_inode; =20 guard(mutex)(&ofs->whiteout_lock); =20 if (!ofs->whiteout) { - inode_lock_nested(wdir, I_MUTEX_PARENT); - whiteout =3D ovl_lookup_temp(ofs, workdir); - if (!IS_ERR(whiteout)) { - err =3D ovl_do_whiteout(ofs, wdir, whiteout); - if (err) { - dput(whiteout); - whiteout =3D ERR_PTR(err); - } - } - inode_unlock(wdir); + whiteout =3D ovl_start_creating_temp(ofs, workdir); if (IS_ERR(whiteout)) return whiteout; - ofs->whiteout =3D whiteout; + err =3D ovl_do_whiteout(ofs, wdir, whiteout); + if (!err) + ofs->whiteout =3D dget(whiteout); + end_creating(whiteout, workdir); + if (err) + return ERR_PTR(err); } =20 if (!ofs->no_shared_whiteout) { - inode_lock_nested(wdir, I_MUTEX_PARENT); - whiteout =3D ovl_lookup_temp(ofs, workdir); - if (!IS_ERR(whiteout)) { - err =3D ovl_do_link(ofs, ofs->whiteout, wdir, whiteout); - if (err) { - dput(whiteout); - whiteout =3D ERR_PTR(err); - } - } - inode_unlock(wdir); - if (!IS_ERR(whiteout)) - return whiteout; - if (PTR_ERR(whiteout) !=3D -EMLINK) { + link =3D ovl_start_creating_temp(ofs, workdir); + if (IS_ERR(link)) + return link; + err =3D ovl_do_link(ofs, ofs->whiteout, wdir, link); + if (!err) + whiteout =3D dget(link); + end_creating(link, workdir); + if (!err) + return whiteout;; + + if (err !=3D -EMLINK) { pr_warn("Failed to link whiteout - disabling whiteout inode sharing(nli= nk=3D%u, err=3D%lu)\n", ofs->whiteout->d_inode->i_nlink, PTR_ERR(whiteout)); @@ -252,10 +262,13 @@ struct dentry *ovl_create_temp(struct ovl_fs *ofs, st= ruct dentry *workdir, struct ovl_cattr *attr) { struct dentry *ret; - inode_lock_nested(workdir->d_inode, I_MUTEX_PARENT); - ret =3D ovl_create_real(ofs, workdir, - ovl_lookup_temp(ofs, workdir), attr); - inode_unlock(workdir->d_inode); + ret =3D ovl_start_creating_temp(ofs, workdir); + if (IS_ERR(ret)) + return ret; + ret =3D ovl_create_real(ofs, workdir, ret, attr); + if (!IS_ERR(ret)) + dget(ret); + end_creating(ret, workdir); return ret; } =20 @@ -354,18 +367,21 @@ static int ovl_create_upper(struct dentry *dentry, st= ruct inode *inode, { struct ovl_fs *ofs =3D OVL_FS(dentry->d_sb); struct dentry *upperdir =3D ovl_dentry_upper(dentry->d_parent); - struct inode *udir =3D upperdir->d_inode; struct dentry *newdentry; int err; =20 - inode_lock_nested(udir, I_MUTEX_PARENT); - newdentry =3D ovl_create_real(ofs, upperdir, - ovl_lookup_upper(ofs, dentry->d_name.name, - upperdir, dentry->d_name.len), - attr); - inode_unlock(udir); + newdentry =3D ovl_start_creating_upper(ofs, upperdir, + &QSTR_LEN(dentry->d_name.name, + dentry->d_name.len)); if (IS_ERR(newdentry)) return PTR_ERR(newdentry); + newdentry =3D ovl_create_real(ofs, upperdir, newdentry, attr); + if (IS_ERR(newdentry)) { + end_creating(newdentry, upperdir); + return PTR_ERR(newdentry); + } + dget(newdentry); + end_creating(newdentry, upperdir); =20 if (ovl_type_merge(dentry->d_parent) && d_is_dir(newdentry) && !ovl_allow_offline_changes(ofs)) { diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index c8fd5951fc5e..beeba96cfcb2 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -415,6 +415,14 @@ static inline struct dentry *ovl_lookup_upper_unlocked= (struct ovl_fs *ofs, &QSTR_LEN(name, len), base); } =20 +static inline struct dentry *ovl_start_creating_upper(struct ovl_fs *ofs, + struct dentry *parent, + struct qstr *name) +{ + return start_creating(ovl_upper_mnt_idmap(ofs), + parent, name); +} + static inline bool ovl_open_flags_need_copy_up(int flags) { if (!flags) diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 43ee4c7296a7..6e0816c1147a 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -310,8 +310,7 @@ static struct dentry *ovl_workdir_create(struct ovl_fs = *ofs, bool retried =3D false; =20 retry: - inode_lock_nested(dir, I_MUTEX_PARENT); - work =3D ovl_lookup_upper(ofs, name, ofs->workbasedir, strlen(name)); + work =3D ovl_start_creating_upper(ofs, ofs->workbasedir, &QSTR(name)); =20 if (!IS_ERR(work)) { struct iattr attr =3D { @@ -320,14 +319,13 @@ static struct dentry *ovl_workdir_create(struct ovl_f= s *ofs, }; =20 if (work->d_inode) { + dget(work); + end_creating(work, ofs->workbasedir); + if (persist) + return work; err =3D -EEXIST; - inode_unlock(dir); if (retried) goto out_dput; - - if (persist) - return work; - retried =3D true; err =3D ovl_workdir_cleanup(ofs, ofs->workbasedir, mnt, work, 0); dput(work); @@ -338,7 +336,9 @@ static struct dentry *ovl_workdir_create(struct ovl_fs = *ofs, } =20 work =3D ovl_do_mkdir(ofs, dir, work, attr.ia_mode); - inode_unlock(dir); + if (!IS_ERR(work)) + dget(work); + end_creating(work, ofs->workbasedir); err =3D PTR_ERR(work); if (IS_ERR(work)) goto out_err; @@ -376,7 +376,6 @@ static struct dentry *ovl_workdir_create(struct ovl_fs = *ofs, if (err) goto out_dput; } else { - inode_unlock(dir); err =3D PTR_ERR(work); goto out_err; } @@ -626,14 +625,17 @@ static struct dentry *ovl_lookup_or_create(struct ovl= _fs *ofs, struct dentry *parent, const char *name, umode_t mode) { - size_t len =3D strlen(name); struct dentry *child; =20 - inode_lock_nested(parent->d_inode, I_MUTEX_PARENT); - child =3D ovl_lookup_upper(ofs, name, parent, len); - if (!IS_ERR(child) && !child->d_inode) - child =3D ovl_create_real(ofs, parent, child, OVL_CATTR(mode)); - inode_unlock(parent->d_inode); + child =3D ovl_start_creating_upper(ofs, parent, &QSTR(name)); + if (!IS_ERR(child)) { + if (!child->d_inode) + child =3D ovl_create_real(ofs, parent, child, + OVL_CATTR(mode)); + if (!IS_ERR(child)) + dget(child); + end_creating(child, parent); + } dput(parent); =20 return child; diff --git a/include/linux/namei.h b/include/linux/namei.h index fed86221c69c..3f92c1a16878 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -88,6 +88,39 @@ struct dentry *lookup_one_positive_killable(struct mnt_i= dmap *idmap, struct qstr *name, struct dentry *base); =20 +struct dentry *start_creating(struct mnt_idmap *idmap, struct dentry *pare= nt, + struct qstr *name); + +/** + * end_creating - finish action started with start_creating + * @child: dentry returned by start_creating() or vfs_mkdir() + * @parent: dentry given to start_creating(), + * + * Unlock and release the child. + * + * Unlike end_dirop() this can only be called if start_creating() succeede= d. + * It handles @child being and error as vfs_mkdir() might have converted t= he + * dentry to an error - in that case the parent still needs to be unlocked. + * + * If vfs_mkdir() was called then the value returned from that function + * should be given for @child rather than the original dentry, as vfs_mkdi= r() + * may have provided a new dentry. Even if vfs_mkdir() returns an error + * it must be given to end_creating(). + * + * If vfs_mkdir() was not called, then @child will be a valid dentry and + * @parent will be ignored. + */ +static inline void end_creating(struct dentry *child, struct dentry *paren= t) +{ + if (IS_ERR(child)) + /* The parent is still locked despite the error from + * vfs_mkdir() - must unlock it. + */ + inode_unlock(parent->d_inode); + else + 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 From nobody Sun Dec 14 05:53:24 2025 Received: from flow-b4-smtp.messagingengine.com (flow-b4-smtp.messagingengine.com [202.12.124.139]) (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 48C8D21D59C; Wed, 29 Oct 2025 23:45:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=202.12.124.139 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781546; cv=none; b=gQiGx2baZqRokX8bERw+TuHFgEHJRCx79bjl157H7k4/GEVkYCmkcCsxN2zdK4Eyd9Ilh1wtT1Ty8RQKCE50QADrcbuE2wMt7BV33G21BKh+ybyj9VbqAfgHRPsTACtCUoJB2wGXIPfx5pSbAcnt5a7p5MBbx6b8ciq6x0YLBjY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781546; c=relaxed/simple; bh=5ccGHlErJEiL1U/aKcZAMbBsFACeVcSyvza8OzAgHSI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=svJte0+xnvxQFjvc7W65fvpmyGnmPOLO2R5p+JN4kOWCOk/FXLdJAxvFk9oqprdCx3N1kmq/x6Sf51JHCuIXqtOGs/0KEWAjE6pWcJEejWpC9MQP1Jv/YHOGCS+diztWNUpioH4c6j0QLQcXANVPbmeVnzzLclp5fYcpEpMg8vw= 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=EzUM1mZH; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=WeTcrWvz; arc=none smtp.client-ip=202.12.124.139 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="EzUM1mZH"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="WeTcrWvz" Received: from phl-compute-04.internal (phl-compute-04.internal [10.202.2.44]) by mailflow.stl.internal (Postfix) with ESMTP id 8736B130035C; Wed, 29 Oct 2025 19:45:42 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-04.internal (MEProxy); Wed, 29 Oct 2025 19:45:43 -0400 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=1761781542; x=1761788742; bh=ZAgrbaq/ub6v+rWXKj59uZmAaxqQeW6+Mxd+Zd+T3Ak=; b= EzUM1mZH1gLkN8TTgLcpsi5ExYcVRdlK01N/JLNPkcqU0P2M4MCeaxCKzfzhe5rM FRNip0m4yrjMcMv3WepzOR9bgB1nDkuaFAiy4a0UcKws9dROz+PnK7bagJq05E9l 7+KIIRbsyGTQWAjPjv5v/1N2HPBkjDSvYtxj+xpC7LVBxpcmysnZXCq3TnXieRhF MCf4QN01jWWcR2GBEuCrrZHvwWnhdESvFNKb/uMMn0uSGZ1JZPEaSVJYTo4fgA6t rdggP+rILS47t7Sc0syx3kAH7eS4uGhcG96pqyi7cJknl4u4s/NK2ZJ38EKb5xer +24Ynei54q/xkFzANCuD9A== 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=1761781542; x=1761788742; bh=Z Agrbaq/ub6v+rWXKj59uZmAaxqQeW6+Mxd+Zd+T3Ak=; b=WeTcrWvzUMlTT5vfo 25cBt8s1Mo8oq2HkBoXiQ87vNMocWlZPHS1mk0gV7X/OjT5Udokxv6shoof4WnVQ v/VuSudl30NLG3FL7do+6sVVLTwQLgKW6c5P741v84WogEePgBoSnb3ohvjHaAHp WV7zbvTZSwzAO5W1NMl2aYCLL826ECAuQTDF+WNPRgM/fAv5Q55ccntl6X9qZsQx L1gO1DvuY6Qr4f6tlankKCIUZUjBONKKkCRDOZKxZcSvuV58QsVkPbbT+Pg23lMN Mm85qB4czK18d+F/gI6oo3nBSr1F6lO6XQfQJRV1MK980eI2t4B8Pb82sbN1YeDH 1NL5g== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggdduieehtdelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgepudenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedupdhmohguvgepshhmthhp 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, 29 Oct 2025 19:45:31 -0400 (EDT) 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, apparmor@lists.ubuntu.com, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v4 05/14] VFS/nfsd/cachefiles/ovl: introduce start_removing() and end_removing() Date: Thu, 30 Oct 2025 10:31:05 +1100 Message-ID: <20251029234353.1321957-6-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251029234353.1321957-1-neilb@ownmail.net> References: <20251029234353.1321957-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 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 9972b0257a4c..ae833dfa277c 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 4efd3688e081..cd64ffe12e0b 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 a8a24abee6b3..b5247c9e1903 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 3f92c1a16878..9ee76e88f3dd 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -90,6 +90,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 @@ -121,6 +123,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 From nobody Sun Dec 14 05:53:24 2025 Received: from flow-b4-smtp.messagingengine.com (flow-b4-smtp.messagingengine.com [202.12.124.139]) (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 6EF4221D59C; Wed, 29 Oct 2025 23:45:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=202.12.124.139 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781560; cv=none; b=uhSzwQZql6FxI4xkRrvp2kKhuwTXVS5gv0ZClvWB8QNkzM8sFkBxXaouewqQqiDUb85T7/xzkOq6N4TftT90q8/ibZNpAu+fr+CJAJrO6GvdxRfOUoAV4P0SVtluYNPwiYzXrGjqxbiNpq+xaSNJwC43xKT5CT7hIiPm2DHcfHs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781560; c=relaxed/simple; bh=CcSxkv5Wabb/t/SJb9cSzphZ+/8fyxP3PEyZA8ZNGi0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Lk7BDHCEcCyOChBU7LJSWW344NOGHi0XPdnU59nIBDEG8PhsabSF2BQ1/KyQQdRAtvWCLGMjDnKVowFEN+vLS6sLA319TFtwTYwwhEVsOh9U/yQHT7PZux31LTbqyDDdWOEDKdg/kyVyzymGrnf0qVejiUcjywB2RMlwIYMKqN0= 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=FaWoikhU; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=YpZofkcA; arc=none smtp.client-ip=202.12.124.139 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="FaWoikhU"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="YpZofkcA" Received: from phl-compute-03.internal (phl-compute-03.internal [10.202.2.43]) by mailflow.stl.internal (Postfix) with ESMTP id BC387130034B; Wed, 29 Oct 2025 19:45:56 -0400 (EDT) Received: from phl-mailfrontend-01 ([10.202.2.162]) by phl-compute-03.internal (MEProxy); Wed, 29 Oct 2025 19:45:57 -0400 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=1761781556; x=1761788756; bh=mS5MDFtnP9koFyLzAwT9DUAKQGwdQj9NRes8BP2Tz7o=; b= FaWoikhUlLRM2OGVNvEMgtrAvMspXk1z/6hx+98y+ZJzlMMamPF5b9SehU7eOGxy NA6crjghzhhJU6al1b6nB08OZyuenflfSmpBzsCM7LIWAv1wBHFxw3ofbutfxNCV IlKnF8exn5ZzBXQ6z/wZAjPQ/XGN5+hv4phTjxV2YKrHRUXOsJkNugqv16NUuvUR yV83dC9o9buabNFhOQEG/ybgaY17CylxChWS4lW7fcwXqHHZhJhpOjDmokMrQxhn N3H8lgWJ8RdxmAxvh+2T8JZZRaVJD1vN40r/Gxewy/0nbfsKQTg3D6Huff0l+zeb Nrnmkz4ua5XSLJmoViIGpw== 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=1761781556; x=1761788756; bh=m S5MDFtnP9koFyLzAwT9DUAKQGwdQj9NRes8BP2Tz7o=; b=YpZofkcAsdaif2bfE StEAz5pxUap41BvN4As4/DZTEQgJfWG6pNPgthtb9R7VrJ97hRg23rMjfVEhYLfn Ae8XlLNztQ4G7iPQDzvMiweGHh5bSeL1Lr3BOekDmF7+ueTNbK9mW5f1AL7o6a4d P3iTQBe8Q0tormTTDwfyOHSMXTUvEsFPwbtK2U/zWuRH8Pg1m/NjjdJce/9zm6hI Gg9hkN49S2RXvGhjP3yRr8rF6TB0HXx3zwkX5aJy+zsZzLXBpG9tXZ43EEaEC+IF FwpIqa3J+5eZGzZW8G8C79SWQcLMhFKxO9Yi1CTpoeJhkIyzjsRjRH09nKBEOJgZ Jz6yg== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggdduieehtdelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedupdhmohguvgepshhmthhp 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, 29 Oct 2025 19:45:45 -0400 (EDT) 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, apparmor@lists.ubuntu.com, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v4 06/14] VFS: introduce start_creating_noperm() and start_removing_noperm() Date: Thu, 30 Oct 2025 10:31:06 +1100 Message-ID: <20251029234353.1321957-7-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251029234353.1321957-1-neilb@ownmail.net> References: <20251029234353.1321957-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 ecaec0fea3a1..40ca94922349 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 ae833dfa277c..696e4b794416 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 9ee76e88f3dd..688e157d6afc 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -92,6 +92,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 From nobody Sun Dec 14 05:53:24 2025 Received: from flow-b4-smtp.messagingengine.com (flow-b4-smtp.messagingengine.com [202.12.124.139]) (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 0C5FD258CFF; Wed, 29 Oct 2025 23:46:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=202.12.124.139 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781574; cv=none; b=e6eU9WjNAtOtjfN0/MvOrWzFJnJxDc1sdDFEYwZ+egrat+GDWBdw4n2oOu3wOSq//MWDjr14tMShxVIzxMchZWvgVIpu6qtya6fuTGZ/7TFU5EkQG72F+ttMIDuzy1z7rpPZln1NN5vpgHkRNUED8+kUEUN66/mKVJXYaQjg5nU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781574; c=relaxed/simple; bh=M3Aq+T2ThOF8yjWasTSo5EjmvdI40fBDnsjv8qUqkj0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LkvKMnq0Ol+yDpdhCSNM+fGa9q3AFjOACyFGheokO6y1bbYMJhZkApTiHrehgePwEswcy1iJ8Q4tgu9Gp/vxAYCGjM7kEUil74V65GPcjJhRcej6q9fIa6H7wX/cbZq4fCzIlKdLsFoc3usZ5qOhbv82whZ2DrUKRapT7ej9zcA= 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=L1O1Ij7Y; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=TZqQGLed; arc=none smtp.client-ip=202.12.124.139 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="L1O1Ij7Y"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="TZqQGLed" Received: from phl-compute-03.internal (phl-compute-03.internal [10.202.2.43]) by mailflow.stl.internal (Postfix) with ESMTP id 5F34E130007D; Wed, 29 Oct 2025 19:46:10 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-03.internal (MEProxy); Wed, 29 Oct 2025 19:46:11 -0400 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=1761781570; x=1761788770; bh=n0bmhfTrPmHWo1XFASS1JrpC/bkaIxmw/2lGv/3M3xY=; b= L1O1Ij7Y7dmX2VvPi9AiKWJVLxb4L7SuY5AW2JsF+yvJKnweth2eAzIwgwRod/fN IsyMBrhBOe/hMS8mAH0t6i07Zeky30pCLL/GcOivzRmObME+3Kk+wS/tMyLd1eGB es1E7uzcYBu8aqILKFm2Mq7SFxmV4h2CaJ+d+C/2niS2/9ZA5jqGXuHFsxQhNfkD 4nLrcnIl2TLp7ErF3fdAWs3BNyJW09Bg/c8JAselyo5wavodeQwLpc3XD0f/yDIm U9J9/RaraXwoAY1DXw1h+efJX++WQI7WoFfeCDpJzTyHkBltL9MQomJrG8WiUuOT QRglUPOcGExRcLgmUO5EVg== 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=1761781570; x=1761788770; bh=n 0bmhfTrPmHWo1XFASS1JrpC/bkaIxmw/2lGv/3M3xY=; b=TZqQGLedAq17aUnWI vpqBgQLvP8y11Dp3Ea6Xdes70s31Wzld0p3dGuCZtujDvlYv4B5EaeKOWfLi3tTq 3JcrI194iPTHJDbTsg3/qLJJMQSX3KmPqlnvWbCI+O2ZBfjOoEtYyaXv9q51thhy 8o4KXLkr5KY3RPnN6vjatkgS50CCpMsFHcYK/JLeuXyhgnFxwyGb2Q1SGNFkR5a6 lpNgq95HXx/CRopYwQlw3lAV/2i00eSNo+thTlPbm+G4P7iBfggH3U90COz6W9u9 5drQNkdx/mWd4eWcDqtyx6c0ymNZifPrYRcSYwcOQlQZH4Dym+gjZRqXr/kJhCfZ P0EZQ== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggdduieehtdelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgepudenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedupdhmohguvgepshhmthhp 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, 29 Oct 2025 19:45:59 -0400 (EDT) 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, apparmor@lists.ubuntu.com, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v4 07/14] VFS: introduce start_removing_dentry() Date: Thu, 30 Oct 2025 10:31:07 +1100 Message-ID: <20251029234353.1321957-8-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251029234353.1321957-1-neilb@ownmail.net> References: <20251029234353.1321957-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_dentry() is similar to start_removing() but instead of providing a name for lookup, the target dentry is given. start_removing_dentry() checks that the dentry is still hashed and in the parent, and if so it locks and increases the refcount so that end_removing() can be used to finish the operation. This is used in cachefiles, overlayfs, smb/server, and apparmor. There will be other users including ecryptfs. As start_removing_dentry() takes an extra reference to the dentry (to be put by end_removing()), there is no need to explicitly take an extra reference to stop d_delete() from using dentry_unlink_inode() to negate the dentry - as in cachefiles_delete_object(), and ksmbd_vfs_unlink(). Reviewed-by: Amir Goldstein Signed-off-by: NeilBrown --- fs/cachefiles/interface.c | 14 +++++++++----- fs/cachefiles/namei.c | 24 ++++++++++++++---------- fs/cachefiles/volume.c | 10 +++++++--- fs/namei.c | 33 +++++++++++++++++++++++++++++++++ fs/overlayfs/dir.c | 10 ++++------ fs/overlayfs/readdir.c | 8 ++++---- fs/smb/server/vfs.c | 27 ++++----------------------- include/linux/namei.h | 2 ++ security/apparmor/apparmorfs.c | 8 ++++---- 9 files changed, 81 insertions(+), 55 deletions(-) diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c index 3e63cfe15874..3f8a6f1a8fc3 100644 --- a/fs/cachefiles/interface.c +++ b/fs/cachefiles/interface.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include "internal.h" @@ -428,11 +429,14 @@ static bool cachefiles_invalidate_cookie(struct fscac= he_cookie *cookie) if (!old_tmpfile) { struct cachefiles_volume *volume =3D object->volume; struct dentry *fan =3D volume->fanout[(u8)cookie->key_hash]; - - inode_lock_nested(d_inode(fan), I_MUTEX_PARENT); - cachefiles_bury_object(volume->cache, object, fan, - old_file->f_path.dentry, - FSCACHE_OBJECT_INVALIDATED); + struct dentry *obj; + + obj =3D start_removing_dentry(fan, old_file->f_path.dentry); + if (!IS_ERR(obj)) + cachefiles_bury_object(volume->cache, object, + fan, obj, + FSCACHE_OBJECT_INVALIDATED); + end_removing(obj); } fput(old_file); } diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index c7f0c6ab9b88..b97a40917a32 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -425,13 +425,12 @@ int cachefiles_delete_object(struct cachefiles_object= *object, =20 _enter(",OBJ%x{%pD}", object->debug_id, object->file); =20 - /* Stop the dentry being negated if it's only pinned by a file struct. */ - dget(dentry); - - inode_lock_nested(d_backing_inode(fan), I_MUTEX_PARENT); - ret =3D cachefiles_unlink(volume->cache, object, fan, dentry, why); - inode_unlock(d_backing_inode(fan)); - dput(dentry); + dentry =3D start_removing_dentry(fan, dentry); + if (IS_ERR(dentry)) + ret =3D PTR_ERR(dentry); + else + ret =3D cachefiles_unlink(volume->cache, object, fan, dentry, why); + end_removing(dentry); return ret; } =20 @@ -644,9 +643,14 @@ bool cachefiles_look_up_object(struct cachefiles_objec= t *object) =20 if (!d_is_reg(dentry)) { pr_err("%pd is not a file\n", dentry); - inode_lock_nested(d_inode(fan), I_MUTEX_PARENT); - ret =3D cachefiles_bury_object(volume->cache, object, fan, dentry, - FSCACHE_OBJECT_IS_WEIRD); + struct dentry *de =3D start_removing_dentry(fan, dentry); + if (IS_ERR(de)) + ret =3D PTR_ERR(de); + else + ret =3D cachefiles_bury_object(volume->cache, object, + fan, de, + FSCACHE_OBJECT_IS_WEIRD); + end_removing(de); dput(dentry); if (ret < 0) return false; diff --git a/fs/cachefiles/volume.c b/fs/cachefiles/volume.c index 781aac4ef274..ddf95ff5daf0 100644 --- a/fs/cachefiles/volume.c +++ b/fs/cachefiles/volume.c @@ -7,6 +7,7 @@ =20 #include #include +#include #include "internal.h" #include =20 @@ -58,9 +59,12 @@ void cachefiles_acquire_volume(struct fscache_volume *vc= ookie) if (ret < 0) { if (ret !=3D -ESTALE) goto error_dir; - inode_lock_nested(d_inode(cache->store), I_MUTEX_PARENT); - cachefiles_bury_object(cache, NULL, cache->store, vdentry, - FSCACHE_VOLUME_IS_WEIRD); + vdentry =3D start_removing_dentry(cache->store, vdentry); + if (!IS_ERR(vdentry)) + cachefiles_bury_object(cache, NULL, cache->store, + vdentry, + FSCACHE_VOLUME_IS_WEIRD); + end_removing(vdentry); cachefiles_put_directory(volume->dentry); cond_resched(); goto retry; diff --git a/fs/namei.c b/fs/namei.c index 696e4b794416..bfc443bec8a9 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3323,6 +3323,39 @@ struct dentry *start_removing_noperm(struct dentry *= parent, } EXPORT_SYMBOL(start_removing_noperm); =20 +/** + * start_removing_dentry - prepare to remove a given dentry + * @parent: directory from which dentry should be removed + * @child: the dentry to be removed + * + * A lock is taken to protect the dentry again other dirops and + * the validity of the dentry is checked: correct parent and still hashed. + * + * If the dentry is valid and positive, a reference is taken and + * returned. If not an error is returned. + * + * end_removing() should be called when removal is complete, or aborted. + * + * Returns: the valid dentry, or an error. + */ +struct dentry *start_removing_dentry(struct dentry *parent, + struct dentry *child) +{ + inode_lock_nested(parent->d_inode, I_MUTEX_PARENT); + if (unlikely(IS_DEADDIR(parent->d_inode) || + child->d_parent !=3D parent || + d_unhashed(child))) { + inode_unlock(parent->d_inode); + return ERR_PTR(-EINVAL); + } + if (d_is_negative(child)) { + inode_unlock(parent->d_inode); + return ERR_PTR(-ENOENT); + } + return dget(child); +} +EXPORT_SYMBOL(start_removing_dentry); + #ifdef CONFIG_UNIX98_PTYS int path_pts(struct path *path) { diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index b5247c9e1903..c8d0885ee5e0 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -47,14 +47,12 @@ static int ovl_cleanup_locked(struct ovl_fs *ofs, struc= t inode *wdir, int ovl_cleanup(struct ovl_fs *ofs, struct dentry *workdir, struct dentry *wdentry) { - int err; - - err =3D ovl_parent_lock(workdir, wdentry); - if (err) - return err; + wdentry =3D start_removing_dentry(workdir, wdentry); + if (IS_ERR(wdentry)) + return PTR_ERR(wdentry); =20 ovl_cleanup_locked(ofs, workdir->d_inode, wdentry); - ovl_parent_unlock(workdir); + end_removing(wdentry); =20 return 0; } diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 1e9792cc557b..77ecc39fc33a 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -1242,11 +1242,11 @@ int ovl_workdir_cleanup(struct ovl_fs *ofs, struct = dentry *parent, if (!d_is_dir(dentry) || level > 1) return ovl_cleanup(ofs, parent, dentry); =20 - err =3D ovl_parent_lock(parent, dentry); - if (err) - return err; + dentry =3D start_removing_dentry(parent, dentry); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); err =3D ovl_do_rmdir(ofs, parent->d_inode, dentry); - ovl_parent_unlock(parent); + end_removing(dentry); if (err) { struct path path =3D { .mnt =3D mnt, .dentry =3D dentry }; =20 diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index 891ed2dc2b73..7c4ddc43ab39 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -49,24 +49,6 @@ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *w= ork, i_uid_write(inode, i_uid_read(parent_inode)); } =20 -/** - * ksmbd_vfs_lock_parent() - lock parent dentry if it is stable - * @parent: parent dentry - * @child: child dentry - * - * Returns: %0 on success, %-ENOENT if the parent dentry is not stable - */ -int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child) -{ - inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); - if (child->d_parent !=3D parent) { - inode_unlock(d_inode(parent)); - return -ENOENT; - } - - return 0; -} - static int ksmbd_vfs_path_lookup(struct ksmbd_share_config *share_conf, char *pathname, unsigned int flags, struct path *path, bool do_lock) @@ -1084,18 +1066,17 @@ int ksmbd_vfs_unlink(struct file *filp) return err; =20 dir =3D dget_parent(dentry); - err =3D ksmbd_vfs_lock_parent(dir, dentry); - if (err) + dentry =3D start_removing_dentry(dir, dentry); + err =3D PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; - dget(dentry); =20 if (S_ISDIR(d_inode(dentry)->i_mode)) err =3D vfs_rmdir(idmap, d_inode(dir), dentry); else err =3D vfs_unlink(idmap, d_inode(dir), dentry, NULL); =20 - dput(dentry); - inode_unlock(d_inode(dir)); + end_removing(dentry); if (err) ksmbd_debug(VFS, "failed to delete, err %d\n", err); out: diff --git a/include/linux/namei.h b/include/linux/namei.h index 688e157d6afc..7e916e9d7726 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -94,6 +94,8 @@ struct dentry *start_removing(struct mnt_idmap *idmap, st= ruct dentry *parent, 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); +struct dentry *start_removing_dentry(struct dentry *parent, + struct dentry *child); =20 /** * end_creating - finish action started with start_creating diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 391a586d0557..9d08d103f142 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -355,17 +355,17 @@ static void aafs_remove(struct dentry *dentry) if (!dentry || IS_ERR(dentry)) return; =20 + /* ->d_parent is stable as rename is not supported */ dir =3D d_inode(dentry->d_parent); - inode_lock(dir); - if (simple_positive(dentry)) { + dentry =3D start_removing_dentry(dentry->d_parent, dentry); + if (!IS_ERR(dentry) && simple_positive(dentry)) { if (d_is_dir(dentry)) simple_rmdir(dir, dentry); else simple_unlink(dir, dentry); d_delete(dentry); - dput(dentry); } - inode_unlock(dir); + end_removing(dentry); simple_release_fs(&aafs_mnt, &aafs_count); } =20 --=20 2.50.0.107.gf914562f5916.dirty From nobody Sun Dec 14 05:53:24 2025 Received: from flow-b4-smtp.messagingengine.com (flow-b4-smtp.messagingengine.com [202.12.124.139]) (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 BB33F258CFF; Wed, 29 Oct 2025 23:46:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=202.12.124.139 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781587; cv=none; b=QUUHXclLrnW30hujHRmlRQCjRZVZkI6SbDWjzG4YwoKfFEqEYei3yevO3dBTMPN3X4CS4+8fv0mPGqeHItkWqB5+owLVkYS/cB9fi2jHPu137qTDean5MAdXj9p6KcMzv0SfH589dLMSHSvgcJ9zoYfUUnWY7mIBp/xBtAISw40= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781587; c=relaxed/simple; bh=lvLaEpNbIdKSPD/pOI5N5yLYW/6x33Xdth/HeRzcyH4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PLDWU/t5A8S6Ry00sZ1emHGlKtFHXeYlkJA6meKsyaHik+rZMsqiyJDQF1vTflPqcgqU0atF/WO8xP3dhFF0Fp3/+OmmxmO/kk2JL+ay82HljHnLYfVFsL22hm3VOnNH8Yd1OBn4UHw6ezZAc/+cTVHFe0q9aYF3vAo1cqzR4P4= 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=XxOTU1us; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=Xcu3qQP4; arc=none smtp.client-ip=202.12.124.139 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="XxOTU1us"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="Xcu3qQP4" Received: from phl-compute-06.internal (phl-compute-06.internal [10.202.2.46]) by mailflow.stl.internal (Postfix) with ESMTP id 41B5F130007D; Wed, 29 Oct 2025 19:46:24 -0400 (EDT) Received: from phl-mailfrontend-01 ([10.202.2.162]) by phl-compute-06.internal (MEProxy); Wed, 29 Oct 2025 19:46:25 -0400 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=1761781584; x=1761788784; bh=I6StfOjqE3oh2lquyYNPVVvsVAS6+Gog4sTTx+bEtbc=; b= XxOTU1usXG8hD+3G2n8UCBk47hINJNnHsTZwiirgSwwNX2RBmu58wAwOo7afk5DX DWyqHdkGk9n6xBRPKmwaDSCKqJ1OJZF8pMip6eROIafDoyNIHlaN15Vlm+iU4LM/ iBkKrhywBd5fp3esFRyqOxFtRfUVeIGCvYxSYdrf2f9lX+D+kwI6YD2UgjLnID1p zGb6D8c08jMfrczXoCQSWS/zJgwECLsTylk3p8Fw+ZyzcuMp6Chq6fvMWc/+ZKGi 9JbhVSwkdyvWBHlUTeWqu2B/vgOH+dDIHxtLlRbc60cRtA2KUJDRJHvXvJcFQC1U Pf1UFaHRYUdxAs+8FEJTVA== 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=1761781584; x=1761788784; bh=I 6StfOjqE3oh2lquyYNPVVvsVAS6+Gog4sTTx+bEtbc=; b=Xcu3qQP4jbnSKAOYE gljwLYbTBSl5vsAmEcNK07nyyMdLL8t/p07MlozHHgwhEll/WCimy3pP8QnroiI/ +qNmjStgozAgJvcrab55cgo4dB+Qhm+WlUt1D0srOXEzEN1dT6vd4yw/VngPpkGc BNdflJH+3Gb7FgIhWcEjboeAKPdyzRln+St8Q7eWXsQfpcO7C5AuYrha26gO6y9L ciTgbYDeZHhkMHVvQKxP6yX2V0w+KC7/i8xCrHo2i5LVijv5hyjpfUFys6XxhLSN JbqzMeFiyROwMIVi2qN2qTVMdiiCD46kXuIgn0+AHcXQsGgT9NF4/JHqqKaHeOm/ ocsFQ== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggdduieehtdekucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgepudenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedupdhmohguvgepshhmthhp 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, 29 Oct 2025 19:46:13 -0400 (EDT) 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, apparmor@lists.ubuntu.com, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v4 08/14] VFS: add start_creating_killable() and start_removing_killable() Date: Thu, 30 Oct 2025 10:31:08 +1100 Message-ID: <20251029234353.1321957-9-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251029234353.1321957-1-neilb@ownmail.net> References: <20251029234353.1321957-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 These are similar to start_creating() and start_removing(), but allow a fatal signal to abort waiting for the lock. They are used in btrfs for subvol creation and removal. btrfs_may_create() no longer needs IS_DEADDIR() and start_creating_killable() includes that check. Reviewed-by: Amir Goldstein Signed-off-by: NeilBrown --- fs/btrfs/ioctl.c | 41 +++++++--------------- fs/namei.c | 80 +++++++++++++++++++++++++++++++++++++++++-- include/linux/namei.h | 6 ++++ 3 files changed, 95 insertions(+), 32 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 185bef0df1c2..4fbfdd8faf6a 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -904,14 +904,9 @@ static noinline int btrfs_mksubvol(struct dentry *pare= nt, struct fscrypt_str name_str =3D FSTR_INIT((char *)qname->name, qname->len= ); int ret; =20 - ret =3D down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT); - if (ret =3D=3D -EINTR) - return ret; - - dentry =3D lookup_one(idmap, qname, parent); - ret =3D PTR_ERR(dentry); + dentry =3D start_creating_killable(idmap, parent, qname); if (IS_ERR(dentry)) - goto out_unlock; + return PTR_ERR(dentry); =20 ret =3D btrfs_may_create(idmap, dir, dentry); if (ret) @@ -940,9 +935,7 @@ static noinline int btrfs_mksubvol(struct dentry *paren= t, out_up_read: up_read(&fs_info->subvol_sem); out_dput: - dput(dentry); -out_unlock: - btrfs_inode_unlock(BTRFS_I(dir), 0); + end_creating(dentry, parent); return ret; } =20 @@ -2417,18 +2410,10 @@ static noinline int btrfs_ioctl_snap_destroy(struct= file *file, goto free_subvol_name; } =20 - ret =3D down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT); - if (ret =3D=3D -EINTR) - goto free_subvol_name; - dentry =3D lookup_one(idmap, &QSTR(subvol_name), parent); + dentry =3D start_removing_killable(idmap, parent, &QSTR(subvol_name)); if (IS_ERR(dentry)) { ret =3D PTR_ERR(dentry); - goto out_unlock_dir; - } - - if (d_really_is_negative(dentry)) { - ret =3D -ENOENT; - goto out_dput; + goto out_end_removing; } =20 inode =3D d_inode(dentry); @@ -2449,7 +2434,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct f= ile *file, */ ret =3D -EPERM; if (!btrfs_test_opt(fs_info, USER_SUBVOL_RM_ALLOWED)) - goto out_dput; + goto out_end_removing; =20 /* * Do not allow deletion if the parent dir is the same @@ -2460,21 +2445,21 @@ static noinline int btrfs_ioctl_snap_destroy(struct= file *file, */ ret =3D -EINVAL; if (root =3D=3D dest) - goto out_dput; + goto out_end_removing; =20 ret =3D inode_permission(idmap, inode, MAY_WRITE | MAY_EXEC); if (ret) - goto out_dput; + goto out_end_removing; } =20 /* check if subvolume may be deleted by a user */ ret =3D btrfs_may_delete(idmap, dir, dentry, 1); if (ret) - goto out_dput; + goto out_end_removing; =20 if (btrfs_ino(BTRFS_I(inode)) !=3D BTRFS_FIRST_FREE_OBJECTID) { ret =3D -EINVAL; - goto out_dput; + goto out_end_removing; } =20 btrfs_inode_lock(BTRFS_I(inode), 0); @@ -2483,10 +2468,8 @@ static noinline int btrfs_ioctl_snap_destroy(struct = file *file, if (!ret) d_delete_notify(dir, dentry); =20 -out_dput: - dput(dentry); -out_unlock_dir: - btrfs_inode_unlock(BTRFS_I(dir), 0); +out_end_removing: + end_removing(dentry); free_subvol_name: kfree(subvol_name_ptr); free_parent: diff --git a/fs/namei.c b/fs/namei.c index bfc443bec8a9..04d2819bd351 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2778,19 +2778,33 @@ static int filename_parentat(int dfd, struct filena= me *name, * Returns: a locked dentry, or an error. * */ -struct dentry *start_dirop(struct dentry *parent, struct qstr *name, - unsigned int lookup_flags) +static struct dentry *__start_dirop(struct dentry *parent, struct qstr *na= me, + unsigned int lookup_flags, + unsigned int state) { struct dentry *dentry; struct inode *dir =3D d_inode(parent); =20 - inode_lock_nested(dir, I_MUTEX_PARENT); + if (state =3D=3D TASK_KILLABLE) { + int ret =3D down_write_killable_nested(&dir->i_rwsem, + I_MUTEX_PARENT); + if (ret) + return ERR_PTR(ret); + } else { + inode_lock_nested(dir, I_MUTEX_PARENT); + } dentry =3D lookup_one_qstr_excl(name, parent, lookup_flags); if (IS_ERR(dentry)) inode_unlock(dir); return dentry; } =20 +struct dentry *start_dirop(struct dentry *parent, struct qstr *name, + unsigned int lookup_flags) +{ + return __start_dirop(parent, name, lookup_flags, TASK_NORMAL); +} + /** * end_dirop - signal completion of a dirop * @de: the dentry which was returned by start_dirop or similar. @@ -3275,6 +3289,66 @@ struct dentry *start_removing(struct mnt_idmap *idma= p, struct dentry *parent, } EXPORT_SYMBOL(start_removing); =20 +/** + * start_creating_killable - prepare to create a given name with permissio= n checking + * @idmap: idmap of the mount + * @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. Permission checking (MAY_EXEC) is performed + * against @idmap. + * + * If the name already exists, a positive dentry is returned. + * + * If a signal is received or was already pending, the function aborts + * with -EINTR; + * + * Returns: a negative or positive dentry, or an error. + */ +struct dentry *start_creating_killable(struct mnt_idmap *idmap, + struct dentry *parent, + struct qstr *name) +{ + int err =3D lookup_one_common(idmap, name, parent); + + if (err) + return ERR_PTR(err); + return __start_dirop(parent, name, LOOKUP_CREATE, TASK_KILLABLE); +} +EXPORT_SYMBOL(start_creating_killable); + +/** + * start_removing_killable - prepare to remove a given name with permissio= n 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. + * + * If a signal is received or was already pending, the function aborts + * with -EINTR; + * + * Returns: a positive dentry, or an error. + */ +struct dentry *start_removing_killable(struct mnt_idmap *idmap, + struct dentry *parent, + struct qstr *name) +{ + int err =3D lookup_one_common(idmap, name, parent); + + if (err) + return ERR_PTR(err); + return __start_dirop(parent, name, 0, TASK_KILLABLE); +} +EXPORT_SYMBOL(start_removing_killable); + /** * start_creating_noperm - prepare to create a given name without permissi= on checking * @parent: directory in which to prepare to create the name diff --git a/include/linux/namei.h b/include/linux/namei.h index 7e916e9d7726..e5cff89679df 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -92,6 +92,12 @@ struct dentry *start_creating(struct mnt_idmap *idmap, s= truct dentry *parent, struct qstr *name); struct dentry *start_removing(struct mnt_idmap *idmap, struct dentry *pare= nt, struct qstr *name); +struct dentry *start_creating_killable(struct mnt_idmap *idmap, + struct dentry *parent, + struct qstr *name); +struct dentry *start_removing_killable(struct mnt_idmap *idmap, + struct dentry *parent, + 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); struct dentry *start_removing_dentry(struct dentry *parent, --=20 2.50.0.107.gf914562f5916.dirty From nobody Sun Dec 14 05:53:24 2025 Received: from flow-b4-smtp.messagingengine.com (flow-b4-smtp.messagingengine.com [202.12.124.139]) (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 14A54318125; Wed, 29 Oct 2025 23:46:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=202.12.124.139 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781602; cv=none; b=d+s4CH2gMHkzRzV8qvxL/Tg6lMPQjR5oY/nE8fXTzr5D02gtWmnclQq0P4L1/DoJokxIcLS/AaGo0krbyLefJ6MWhyYSuL0xvKZw+16TuXOiIQaMFSMfnl9I8Cc4HLVh+w7B/I03w5YDWCjxPKYvR57h97v/IUhOlUKqKR0sHeM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781602; c=relaxed/simple; bh=MHANjk+S27GXyAd8GOPyCeCD/Koadj6CGEdwx9p6L34=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=m6uB/Fgf5j/V5w4crpjR9UoGUSAsFQrIveOMlqq0haNPO9qnG44ZfbDpyWzQf1WHHqlzbRmZkp+KGz8ycHhRTsJdsNjNtNsM+1OoNiuqPZLXdCffUebWRIaZt9TjiK8rFGOg8rArb4ww4lG2EyfVd8pO66A2mjHiSS/2rEiPI+4= 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=dU69g85o; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=h9I96DmG; arc=none smtp.client-ip=202.12.124.139 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="dU69g85o"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="h9I96DmG" Received: from phl-compute-04.internal (phl-compute-04.internal [10.202.2.44]) by mailflow.stl.internal (Postfix) with ESMTP id 58EFA130007D; Wed, 29 Oct 2025 19:46:38 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-04.internal (MEProxy); Wed, 29 Oct 2025 19:46:39 -0400 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=1761781598; x=1761788798; bh=2ENYxGoVqtymMRsoofMjwWHzP2Ew+y4LFb4XVsM0atw=; b= dU69g85o+VMX1lE7bGMb8HoDyATawISglvCn4dlnMNilP8MYKXX+tqSpOcYieH5O 7rolmIN3uegMcj50KLvjzdvGtgYsFxWdRn2qNQNWLSHUhYqFFvwy4jnbR8y3ezS9 9ln6uw4L7H30rLy+HMX+o3zqhLDJxkU7ffvXUvGLdxjhd6HgiBDWdWbA9Tfrpv+c 9O7x8UAKAjwka44/w95CXYSNpL3dz1IyOcyryVxtopRw74rsZrx0UvbBctjooYU4 c5YTC/vBbK4inznWnJM8ziiDqoPIHe+fZfT3g53C2vTr0wo3lX/bHyJpXrGjLfDf tdYbH+KFmgr5hAK8c3iIpw== 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=1761781598; x=1761788798; bh=2 ENYxGoVqtymMRsoofMjwWHzP2Ew+y4LFb4XVsM0atw=; b=h9I96DmGcOWzJq1uU L9Wm8aawxgWLujDJLJWxJE63AnKpeN4EEABhBrT+VPxz2opclJajUCwQ5ShRFC0K 6k6JDMfnN7eKbvq65PARRk0Msdwsn0w7DPHSxY8W6akk9Xk6s+t9TEsuL1OWSSYR LuzB+d3LitRGoQK19zOZB/ENmqK6Fl1NDKfBrlo3ZdN34QhSU+yqzLV+NBfEE0ug /npkbKC5cRiFqxWQbUZBZH0PWBWoCorIgRHLpXB9eSWklEiqHRGMqGXoa65K1TdG B6ENO4O7sqd+gJ24yTVqKAHemUlmQ1Qpl/D/YD2Hgofd8g2RYdhuzjjPQGsWhf8N 5jyIA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggdduieehtdelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgepvdenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedupdhmohguvgepshhmthhp 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, 29 Oct 2025 19:46:27 -0400 (EDT) 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, apparmor@lists.ubuntu.com, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v4 09/14] VFS/nfsd/ovl: introduce start_renaming() and end_renaming() Date: Thu, 30 Oct 2025 10:31:09 +1100 Message-ID: <20251029234353.1321957-10-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251029234353.1321957-1-neilb@ownmail.net> References: <20251029234353.1321957-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_renaming() combines name lookup and locking to prepare for rename. It is used when two names need to be looked up as in nfsd and overlayfs - cases where one or both dentries are already available will be handled separately. __start_renaming() avoids the inode_permission check and hash calculation and is suitable after filename_parentat() in do_renameat2(). It subsumes quite a bit of code from that function. start_renaming() does calculate the hash and check X permission and is suitable elsewhere: - nfsd_rename() - ovl_rename() In ovl, ovl_do_rename_rd() is factored out of ovl_do_rename(), which itself will be gone by the end of the series. Reviewed-by: Amir Goldstein Signed-off-by: NeilBrown -- Changes since v3: - added missig dput() in ovl_rename when "whiteout" is not-NULL. Changes since v2: - in __start_renaming() some label have been renamed, and err is always set before a "goto out_foo" rather than passing the error in a dentry*. - ovl_do_rename() changed to call the new ovl_do_rename_rd() rather than keeping duplicate code - code around ovl_cleanup() call in ovl_rename() restructured. Acked-by: Chuck Lever --- fs/namei.c | 197 ++++++++++++++++++++++++++++----------- fs/nfsd/vfs.c | 73 +++++---------- fs/overlayfs/dir.c | 74 +++++++-------- fs/overlayfs/overlayfs.h | 23 +++-- include/linux/namei.h | 3 + 5 files changed, 218 insertions(+), 152 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 04d2819bd351..0ee0a110b088 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3667,6 +3667,129 @@ void unlock_rename(struct dentry *p1, struct dentry= *p2) } EXPORT_SYMBOL(unlock_rename); =20 +/** + * __start_renaming - lookup and lock names for rename + * @rd: rename data containing parent and flags, and + * for receiving found dentries + * @lookup_flags: extra flags to pass to ->lookup (e.g. LOOKUP_REVAL, + * LOOKUP_NO_SYMLINKS etc). + * @old_last: name of object in @rd.old_parent + * @new_last: name of object in @rd.new_parent + * + * Look up two names and ensure locks are in place for + * rename. + * + * On success the found dentries are stored in @rd.old_dentry, + * @rd.new_dentry. These references and the lock are dropped by + * end_renaming(). + * + * The passed in qstrs must have the hash calculated, and no permission + * checking is performed. + * + * Returns: zero or an error. + */ +static int +__start_renaming(struct renamedata *rd, int lookup_flags, + struct qstr *old_last, struct qstr *new_last) +{ + struct dentry *trap; + struct dentry *d1, *d2; + int target_flags =3D LOOKUP_RENAME_TARGET | LOOKUP_CREATE; + int err; + + if (rd->flags & RENAME_EXCHANGE) + target_flags =3D 0; + if (rd->flags & RENAME_NOREPLACE) + target_flags |=3D LOOKUP_EXCL; + + trap =3D lock_rename(rd->old_parent, rd->new_parent); + if (IS_ERR(trap)) + return PTR_ERR(trap); + + d1 =3D lookup_one_qstr_excl(old_last, rd->old_parent, + lookup_flags); + err =3D PTR_ERR(d1); + if (IS_ERR(d1)) + goto out_unlock; + + d2 =3D lookup_one_qstr_excl(new_last, rd->new_parent, + lookup_flags | target_flags); + err =3D PTR_ERR(d2); + if (IS_ERR(d2)) + goto out_dput_d1; + + if (d1 =3D=3D trap) { + /* source is an ancestor of target */ + err =3D -EINVAL; + goto out_dput_d2; + } + + if (d2 =3D=3D trap) { + /* target is an ancestor of source */ + if (rd->flags & RENAME_EXCHANGE) + err =3D -EINVAL; + else + err =3D -ENOTEMPTY; + goto out_dput_d2; + } + + rd->old_dentry =3D d1; + rd->new_dentry =3D d2; + return 0; + +out_dput_d2: + dput(d2); +out_dput_d1: + dput(d1); +out_unlock: + unlock_rename(rd->old_parent, rd->new_parent); + return err; +} + +/** + * start_renaming - lookup and lock names for rename with permission check= ing + * @rd: rename data containing parent and flags, and + * for receiving found dentries + * @lookup_flags: extra flags to pass to ->lookup (e.g. LOOKUP_REVAL, + * LOOKUP_NO_SYMLINKS etc). + * @old_last: name of object in @rd.old_parent + * @new_last: name of object in @rd.new_parent + * + * Look up two names and ensure locks are in place for + * rename. + * + * On success the found dentries are stored in @rd.old_dentry, + * @rd.new_dentry. These references and the lock are dropped by + * end_renaming(). + * + * The passed in qstrs need not have the hash calculated, and basic + * eXecute permission checking is performed against @rd.mnt_idmap. + * + * Returns: zero or an error. + */ +int start_renaming(struct renamedata *rd, int lookup_flags, + struct qstr *old_last, struct qstr *new_last) +{ + int err; + + err =3D lookup_one_common(rd->mnt_idmap, old_last, rd->old_parent); + if (err) + return err; + err =3D lookup_one_common(rd->mnt_idmap, new_last, rd->new_parent); + if (err) + return err; + return __start_renaming(rd, lookup_flags, old_last, new_last); +} +EXPORT_SYMBOL(start_renaming); + +void end_renaming(struct renamedata *rd) +{ + unlock_rename(rd->old_parent, rd->new_parent); + dput(rd->old_dentry); + dput(rd->new_dentry); +} +EXPORT_SYMBOL(end_renaming); + /** * vfs_prepare_mode - prepare the mode to be used for a new inode * @idmap: idmap of the mount the inode was found from @@ -5504,14 +5627,11 @@ int do_renameat2(int olddfd, struct filename *from,= int newdfd, struct filename *to, unsigned int flags) { struct renamedata rd; - struct dentry *old_dentry, *new_dentry; - struct dentry *trap; struct path old_path, new_path; struct qstr old_last, new_last; int old_type, new_type; struct inode *delegated_inode =3D NULL; - unsigned int lookup_flags =3D 0, target_flags =3D - LOOKUP_RENAME_TARGET | LOOKUP_CREATE; + unsigned int lookup_flags =3D 0; bool should_retry =3D false; int error =3D -EINVAL; =20 @@ -5522,11 +5642,6 @@ int do_renameat2(int olddfd, struct filename *from, = int newdfd, (flags & RENAME_EXCHANGE)) goto put_names; =20 - if (flags & RENAME_EXCHANGE) - target_flags =3D 0; - if (flags & RENAME_NOREPLACE) - target_flags |=3D LOOKUP_EXCL; - retry: error =3D filename_parentat(olddfd, from, lookup_flags, &old_path, &old_last, &old_type); @@ -5556,66 +5671,40 @@ int do_renameat2(int olddfd, struct filename *from,= int newdfd, goto exit2; =20 retry_deleg: - trap =3D lock_rename(new_path.dentry, old_path.dentry); - if (IS_ERR(trap)) { - error =3D PTR_ERR(trap); + rd.old_parent =3D old_path.dentry; + rd.mnt_idmap =3D mnt_idmap(old_path.mnt); + rd.new_parent =3D new_path.dentry; + rd.delegated_inode =3D &delegated_inode; + rd.flags =3D flags; + + error =3D __start_renaming(&rd, lookup_flags, &old_last, &new_last); + if (error) goto exit_lock_rename; - } =20 - old_dentry =3D lookup_one_qstr_excl(&old_last, old_path.dentry, - lookup_flags); - error =3D PTR_ERR(old_dentry); - if (IS_ERR(old_dentry)) - goto exit3; - new_dentry =3D lookup_one_qstr_excl(&new_last, new_path.dentry, - lookup_flags | target_flags); - error =3D PTR_ERR(new_dentry); - if (IS_ERR(new_dentry)) - goto exit4; if (flags & RENAME_EXCHANGE) { - if (!d_is_dir(new_dentry)) { + if (!d_is_dir(rd.new_dentry)) { error =3D -ENOTDIR; if (new_last.name[new_last.len]) - goto exit5; + goto exit_unlock; } } /* unless the source is a directory trailing slashes give -ENOTDIR */ - if (!d_is_dir(old_dentry)) { + if (!d_is_dir(rd.old_dentry)) { error =3D -ENOTDIR; if (old_last.name[old_last.len]) - goto exit5; + goto exit_unlock; if (!(flags & RENAME_EXCHANGE) && new_last.name[new_last.len]) - goto exit5; - } - /* source should not be ancestor of target */ - error =3D -EINVAL; - if (old_dentry =3D=3D trap) - goto exit5; - /* target should not be an ancestor of source */ - if (!(flags & RENAME_EXCHANGE)) - error =3D -ENOTEMPTY; - if (new_dentry =3D=3D trap) - goto exit5; + goto exit_unlock; + } =20 - error =3D security_path_rename(&old_path, old_dentry, - &new_path, new_dentry, flags); + error =3D security_path_rename(&old_path, rd.old_dentry, + &new_path, rd.new_dentry, flags); if (error) - goto exit5; + goto exit_unlock; =20 - rd.old_parent =3D old_path.dentry; - rd.old_dentry =3D old_dentry; - rd.mnt_idmap =3D mnt_idmap(old_path.mnt); - rd.new_parent =3D new_path.dentry; - rd.new_dentry =3D new_dentry; - rd.delegated_inode =3D &delegated_inode; - rd.flags =3D flags; error =3D vfs_rename(&rd); -exit5: - dput(new_dentry); -exit4: - dput(old_dentry); -exit3: - unlock_rename(new_path.dentry, old_path.dentry); +exit_unlock: + end_renaming(&rd); exit_lock_rename: if (delegated_inode) { error =3D break_deleg_wait(&delegated_inode); diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index cd64ffe12e0b..62109885d4db 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1885,11 +1885,12 @@ __be32 nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int = flen, struct svc_fh *tfhp, char *tname, int tlen) { - struct dentry *fdentry, *tdentry, *odentry, *ndentry, *trap; + struct dentry *fdentry, *tdentry; int type =3D S_IFDIR; + struct renamedata rd =3D {}; __be32 err; int host_err; - bool close_cached =3D false; + struct dentry *close_cached; =20 trace_nfsd_vfs_rename(rqstp, ffhp, tfhp, fname, flen, tname, tlen); =20 @@ -1915,15 +1916,22 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *= ffhp, char *fname, int flen, goto out; =20 retry: + close_cached =3D NULL; host_err =3D fh_want_write(ffhp); if (host_err) { err =3D nfserrno(host_err); goto out; } =20 - trap =3D lock_rename(tdentry, fdentry); - if (IS_ERR(trap)) { - err =3D nfserr_xdev; + rd.mnt_idmap =3D &nop_mnt_idmap; + rd.old_parent =3D fdentry; + rd.new_parent =3D tdentry; + + host_err =3D start_renaming(&rd, 0, &QSTR_LEN(fname, flen), + &QSTR_LEN(tname, tlen)); + + if (host_err) { + err =3D nfserrno(host_err); goto out_want_write; } err =3D fh_fill_pre_attrs(ffhp); @@ -1933,48 +1941,23 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *= ffhp, char *fname, int flen, if (err !=3D nfs_ok) goto out_unlock; =20 - odentry =3D lookup_one(&nop_mnt_idmap, &QSTR_LEN(fname, flen), fdentry); - host_err =3D PTR_ERR(odentry); - if (IS_ERR(odentry)) - goto out_nfserr; + type =3D d_inode(rd.old_dentry)->i_mode & S_IFMT; + + if (d_inode(rd.new_dentry)) + type =3D d_inode(rd.new_dentry)->i_mode & S_IFMT; =20 - host_err =3D -ENOENT; - if (d_really_is_negative(odentry)) - goto out_dput_old; - host_err =3D -EINVAL; - if (odentry =3D=3D trap) - goto out_dput_old; - type =3D d_inode(odentry)->i_mode & S_IFMT; - - ndentry =3D lookup_one(&nop_mnt_idmap, &QSTR_LEN(tname, tlen), tdentry); - host_err =3D PTR_ERR(ndentry); - if (IS_ERR(ndentry)) - goto out_dput_old; - if (d_inode(ndentry)) - type =3D d_inode(ndentry)->i_mode & S_IFMT; - host_err =3D -ENOTEMPTY; - if (ndentry =3D=3D trap) - goto out_dput_new; - - if ((ndentry->d_sb->s_export_op->flags & EXPORT_OP_CLOSE_BEFORE_UNLINK) && - nfsd_has_cached_files(ndentry)) { - close_cached =3D true; - goto out_dput_old; + if ((rd.new_dentry->d_sb->s_export_op->flags & EXPORT_OP_CLOSE_BEFORE_UNL= INK) && + nfsd_has_cached_files(rd.new_dentry)) { + close_cached =3D dget(rd.new_dentry); + goto out_unlock; } else { - struct renamedata rd =3D { - .mnt_idmap =3D &nop_mnt_idmap, - .old_parent =3D fdentry, - .old_dentry =3D odentry, - .new_parent =3D tdentry, - .new_dentry =3D ndentry, - }; int retries; =20 for (retries =3D 1;;) { host_err =3D vfs_rename(&rd); if (host_err !=3D -EAGAIN || !retries--) break; - if (!nfsd_wait_for_delegreturn(rqstp, d_inode(odentry))) + if (!nfsd_wait_for_delegreturn(rqstp, d_inode(rd.old_dentry))) break; } if (!host_err) { @@ -1983,11 +1966,6 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *f= fhp, char *fname, int flen, host_err =3D commit_metadata(ffhp); } } - out_dput_new: - dput(ndentry); - out_dput_old: - dput(odentry); - out_nfserr: if (host_err =3D=3D -EBUSY) { /* * See RFC 8881 Section 18.26.4 para 1-3: NFSv4 RENAME @@ -2006,7 +1984,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ff= hp, char *fname, int flen, fh_fill_post_attrs(tfhp); } out_unlock: - unlock_rename(tdentry, fdentry); + end_renaming(&rd); out_want_write: fh_drop_write(ffhp); =20 @@ -2017,9 +1995,8 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ff= hp, char *fname, int flen, * until this point and then reattempt the whole shebang. */ if (close_cached) { - close_cached =3D false; - nfsd_close_cached_files(ndentry); - dput(ndentry); + nfsd_close_cached_files(close_cached); + dput(close_cached); goto retry; } out: diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index c8d0885ee5e0..0f2c2da68433 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -1124,9 +1124,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct= inode *olddir, int err; struct dentry *old_upperdir; struct dentry *new_upperdir; - struct dentry *olddentry =3D NULL; - struct dentry *newdentry =3D NULL; - struct dentry *trap, *de; + struct renamedata rd =3D {}; bool old_opaque; bool new_opaque; bool cleanup_whiteout =3D false; @@ -1136,6 +1134,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct= inode *olddir, bool new_is_dir =3D d_is_dir(new); bool samedir =3D olddir =3D=3D newdir; struct dentry *opaquedir =3D NULL; + struct dentry *whiteout =3D NULL; const struct cred *old_cred =3D NULL; struct ovl_fs *ofs =3D OVL_FS(old->d_sb); LIST_HEAD(list); @@ -1233,29 +1232,21 @@ static int ovl_rename(struct mnt_idmap *idmap, stru= ct inode *olddir, } } =20 - trap =3D lock_rename(new_upperdir, old_upperdir); - if (IS_ERR(trap)) { - err =3D PTR_ERR(trap); - goto out_revert_creds; - } + rd.mnt_idmap =3D ovl_upper_mnt_idmap(ofs); + rd.old_parent =3D old_upperdir; + rd.new_parent =3D new_upperdir; + rd.flags =3D flags; =20 - de =3D ovl_lookup_upper(ofs, old->d_name.name, old_upperdir, - old->d_name.len); - err =3D PTR_ERR(de); - if (IS_ERR(de)) - goto out_unlock; - olddentry =3D de; + err =3D start_renaming(&rd, 0, + &QSTR_LEN(old->d_name.name, old->d_name.len), + &QSTR_LEN(new->d_name.name, new->d_name.len)); =20 - err =3D -ESTALE; - if (!ovl_matches_upper(old, olddentry)) - goto out_unlock; + if (err) + goto out_revert_creds; =20 - de =3D ovl_lookup_upper(ofs, new->d_name.name, new_upperdir, - new->d_name.len); - err =3D PTR_ERR(de); - if (IS_ERR(de)) + err =3D -ESTALE; + if (!ovl_matches_upper(old, rd.old_dentry)) goto out_unlock; - newdentry =3D de; =20 old_opaque =3D ovl_dentry_is_opaque(old); new_opaque =3D ovl_dentry_is_opaque(new); @@ -1263,15 +1254,15 @@ static int ovl_rename(struct mnt_idmap *idmap, stru= ct inode *olddir, err =3D -ESTALE; if (d_inode(new) && ovl_dentry_upper(new)) { if (opaquedir) { - if (newdentry !=3D opaquedir) + if (rd.new_dentry !=3D opaquedir) goto out_unlock; } else { - if (!ovl_matches_upper(new, newdentry)) + if (!ovl_matches_upper(new, rd.new_dentry)) goto out_unlock; } } else { - if (!d_is_negative(newdentry)) { - if (!new_opaque || !ovl_upper_is_whiteout(ofs, newdentry)) + if (!d_is_negative(rd.new_dentry)) { + if (!new_opaque || !ovl_upper_is_whiteout(ofs, rd.new_dentry)) goto out_unlock; } else { if (flags & RENAME_EXCHANGE) @@ -1279,19 +1270,14 @@ static int ovl_rename(struct mnt_idmap *idmap, stru= ct inode *olddir, } } =20 - if (olddentry =3D=3D trap) - goto out_unlock; - if (newdentry =3D=3D trap) - goto out_unlock; - - if (olddentry->d_inode =3D=3D newdentry->d_inode) + if (rd.old_dentry->d_inode =3D=3D rd.new_dentry->d_inode) goto out_unlock; =20 err =3D 0; if (ovl_type_merge_or_lower(old)) err =3D ovl_set_redirect(old, samedir); else if (is_dir && !old_opaque && ovl_type_merge(new->d_parent)) - err =3D ovl_set_opaque_xerr(old, olddentry, -EXDEV); + err =3D ovl_set_opaque_xerr(old, rd.old_dentry, -EXDEV); if (err) goto out_unlock; =20 @@ -1299,18 +1285,24 @@ static int ovl_rename(struct mnt_idmap *idmap, stru= ct inode *olddir, err =3D ovl_set_redirect(new, samedir); else if (!overwrite && new_is_dir && !new_opaque && ovl_type_merge(old->d_parent)) - err =3D ovl_set_opaque_xerr(new, newdentry, -EXDEV); + err =3D ovl_set_opaque_xerr(new, rd.new_dentry, -EXDEV); if (err) goto out_unlock; =20 - err =3D ovl_do_rename(ofs, old_upperdir, olddentry, - new_upperdir, newdentry, flags); - unlock_rename(new_upperdir, old_upperdir); + err =3D ovl_do_rename_rd(&rd); + + if (!err && cleanup_whiteout) + whiteout =3D dget(rd.new_dentry); + + end_renaming(&rd); + if (err) goto out_revert_creds; =20 - if (cleanup_whiteout) - ovl_cleanup(ofs, old_upperdir, newdentry); + if (whiteout) { + ovl_cleanup(ofs, old_upperdir, whiteout); + dput(whiteout); + } =20 if (overwrite && d_inode(new)) { if (new_is_dir) @@ -1336,14 +1328,12 @@ static int ovl_rename(struct mnt_idmap *idmap, stru= ct inode *olddir, else ovl_drop_write(old); out: - dput(newdentry); - dput(olddentry); dput(opaquedir); ovl_cache_free(&list); return err; =20 out_unlock: - unlock_rename(new_upperdir, old_upperdir); + end_renaming(&rd); goto out_revert_creds; } =20 diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 49ad65f829dc..3cc85a893b5c 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -355,11 +355,24 @@ static inline int ovl_do_remove_acl(struct ovl_fs *of= s, struct dentry *dentry, return vfs_remove_acl(ovl_upper_mnt_idmap(ofs), dentry, acl_name); } =20 +static inline int ovl_do_rename_rd(struct renamedata *rd) +{ + int err; + + pr_debug("rename(%pd2, %pd2, 0x%x)\n", rd->old_dentry, rd->new_dentry, + rd->flags); + err =3D vfs_rename(rd); + if (err) { + pr_debug("...rename(%pd2, %pd2, ...) =3D %i\n", + rd->old_dentry, rd->new_dentry, err); + } + return err; +} + static inline int ovl_do_rename(struct ovl_fs *ofs, struct dentry *olddir, struct dentry *olddentry, struct dentry *newdir, struct dentry *newdentry, unsigned int flags) { - int err; struct renamedata rd =3D { .mnt_idmap =3D ovl_upper_mnt_idmap(ofs), .old_parent =3D olddir, @@ -369,13 +382,7 @@ static inline int ovl_do_rename(struct ovl_fs *ofs, st= ruct dentry *olddir, .flags =3D flags, }; =20 - pr_debug("rename(%pd2, %pd2, 0x%x)\n", olddentry, newdentry, flags); - err =3D vfs_rename(&rd); - if (err) { - pr_debug("...rename(%pd2, %pd2, ...) =3D %i\n", - olddentry, newdentry, err); - } - return err; + return ovl_do_rename_rd(&rd); } =20 static inline int ovl_do_whiteout(struct ovl_fs *ofs, diff --git a/include/linux/namei.h b/include/linux/namei.h index e5cff89679df..19c3d8e336d5 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -156,6 +156,9 @@ extern int follow_up(struct path *); extern struct dentry *lock_rename(struct dentry *, struct dentry *); extern struct dentry *lock_rename_child(struct dentry *, struct dentry *); extern void unlock_rename(struct dentry *, struct dentry *); +int start_renaming(struct renamedata *rd, int lookup_flags, + struct qstr *old_last, struct qstr *new_last); +void end_renaming(struct renamedata *rd); =20 /** * mode_strip_umask - handle vfs umask stripping --=20 2.50.0.107.gf914562f5916.dirty From nobody Sun Dec 14 05:53:24 2025 Received: from flow-b4-smtp.messagingengine.com (flow-b4-smtp.messagingengine.com [202.12.124.139]) (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 53A1132BF3D; Wed, 29 Oct 2025 23:46:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=202.12.124.139 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781616; cv=none; b=bR2zPjMC7V8JAcGti7yjhn2uVj59D2LIPS3wG2l1dE8v60Qw79XoQwjSlInItrnb8v6IjIC3MSm9kMExyOXMO70q497RH/DRJOXtrUgS9OW8thEnJigBKMQHeUU63JAgLZCXbzfer30woy+hjOtkPcYwq7utGBbyP38xo9IvxEU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781616; c=relaxed/simple; bh=GrWrrLtSHjdzhh+W7LDg0wRWtUM1d37s9wITVHkhKaI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RSfQjhJLTaqk3y9yNRa6L340K+OEpouxMK9cINt52IfBjuFeIIt7IpyW4ixEMZbr95Fx+2WF1yDx5aFnqSyr2UCE3UOM1K0wYBusabySmXRVOL2Tw3RN+sMOU8IhpEubB8OJQySjuRS4dRi3zUytMDyGZN9wzabQih3onXdxtNk= 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=GmHJclZA; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=Z84vu4tZ; arc=none smtp.client-ip=202.12.124.139 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="GmHJclZA"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="Z84vu4tZ" Received: from phl-compute-01.internal (phl-compute-01.internal [10.202.2.41]) by mailflow.stl.internal (Postfix) with ESMTP id 9A894130007D; Wed, 29 Oct 2025 19:46:52 -0400 (EDT) Received: from phl-mailfrontend-01 ([10.202.2.162]) by phl-compute-01.internal (MEProxy); Wed, 29 Oct 2025 19:46:53 -0400 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=1761781612; x=1761788812; bh=I3P06nc1qk5mWCjVqPmobZQ5mBOMMFA2/DiQeMin+P4=; b= GmHJclZAxYDUWRHlLZVZiZMiDOwcFUYOk2xNR5BCveB1IOgLVggzA81s0Z7XjbL0 UxCXYYeBe/BNbJPJrctorYclNq8uOt6fa1OBm8fLq79JeHXMP2FefyHmE8RdQwxG 5rCEik7BQ/KEjN9xRgf13+VQwVG2kJzRKje3Q8vXUBW25tz6Iiiu8r85dmJmiS50 1rcoXhuM85nfzN6u/ojhEHxgnI3jjZ5BNVDcGRs2KweTl62WauS0TbmGrCQwgOas 822+IN/EfJjezOdsWlzTWANuS9WXBgwANQzFr9iCL/AcVVRDH8jysa3P+BcNAdV2 hWzSBOKA9uze5cCnVzYG3g== 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=1761781612; x=1761788812; bh=I 3P06nc1qk5mWCjVqPmobZQ5mBOMMFA2/DiQeMin+P4=; b=Z84vu4tZME4hMwHHm squSoQoIIWEnwI90a8WagcOLyxKflDZYq0MSUGL3Y5cqLTA2G7uWK3QZY1rNeEXn ivoP/dTHZPvdxdAllQoCvW/fISRJD/GHn56X3nKy8Zh4kEyo7/uHn1lToDXdtlrv h6a6bsi90+ft8LC3j4quu/XjrgkPEVgnmwfqcsVw8e5ibWosn5J8yYfGVwO2vfb5 Z0Rc5TkB95uFWGWpDsa1aakUf8fwq2K/BsPySrklXPj/6jop0TVOh3mP00Y9g5jx JjLc6dWG0Xud+Nscek8tew10y1bfh1NjOUgdC3gkEZXfP0tq6z0fyG2lQVI3I9FR 7A2jg== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggdduieehtdelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedupdhmohguvgepshhmthhp 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, 29 Oct 2025 19:46:41 -0400 (EDT) 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, apparmor@lists.ubuntu.com, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v4 10/14] VFS/ovl/smb: introduce start_renaming_dentry() Date: Thu, 30 Oct 2025 10:31:10 +1100 Message-ID: <20251029234353.1321957-11-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251029234353.1321957-1-neilb@ownmail.net> References: <20251029234353.1321957-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 Several callers perform a rename on a dentry they already have, and only require lookup for the target name. This includes smb/server and a few different places in overlayfs. start_renaming_dentry() performs the required lookup and takes the required lock using lock_rename_child() It is used in three places in overlayfs and in ksmbd_vfs_rename(). In the ksmbd case, the parent of the source is not important - the source must be renamed from wherever it is. So start_renaming_dentry() allows rd->old_parent to be NULL and only checks it if it is non-NULL. On success rd->old_parent will be the parent of old_dentry with an extra reference taken. Other start_renaming function also now take the extra reference and end_renaming() now drops this reference as well. ovl_lookup_temp(), ovl_parent_lock(), and ovl_parent_unlock() are all removed as they are no longer needed. OVL_TEMPNAME_SIZE and ovl_tempname() are now declared in overlayfs.h so that ovl_check_rename_whiteout() can access them. ovl_copy_up_workdir() now always cleans up on error. Reviewed-by: Amir Goldstein Signed-off-by: NeilBrown Reviewed-by: Namjae Jeon --- fs/namei.c | 108 ++++++++++++++++++++++++++++++++++++--- fs/overlayfs/copy_up.c | 54 +++++++++----------- fs/overlayfs/dir.c | 19 +------ fs/overlayfs/overlayfs.h | 8 +-- fs/overlayfs/super.c | 22 ++++---- fs/overlayfs/util.c | 11 ---- fs/smb/server/vfs.c | 60 ++++------------------ include/linux/namei.h | 2 + 8 files changed, 150 insertions(+), 134 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 0ee0a110b088..5153ceddd37a 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3669,7 +3669,7 @@ EXPORT_SYMBOL(unlock_rename); =20 /** * __start_renaming - lookup and lock names for rename - * @rd: rename data containing parent and flags, and + * @rd: rename data containing parents and flags, and * for receiving found dentries * @lookup_flags: extra flags to pass to ->lookup (e.g. LOOKUP_REVAL, * LOOKUP_NO_SYMLINKS etc). @@ -3680,8 +3680,8 @@ EXPORT_SYMBOL(unlock_rename); * rename. * * On success the found dentries are stored in @rd.old_dentry, - * @rd.new_dentry. These references and the lock are dropped by - * end_renaming(). + * @rd.new_dentry and an extra ref is taken on @rd.old_parent. + * These references and the lock are dropped by end_renaming(). * * The passed in qstrs must have the hash calculated, and no permission * checking is performed. @@ -3735,6 +3735,7 @@ __start_renaming(struct renamedata *rd, int lookup_fl= ags, =20 rd->old_dentry =3D d1; rd->new_dentry =3D d2; + dget(rd->old_parent); return 0; =20 out_dput_d2: @@ -3748,7 +3749,7 @@ __start_renaming(struct renamedata *rd, int lookup_fl= ags, =20 /** * start_renaming - lookup and lock names for rename with permission check= ing - * @rd: rename data containing parent and flags, and + * @rd: rename data containing parents and flags, and * for receiving found dentries * @lookup_flags: extra flags to pass to ->lookup (e.g. LOOKUP_REVAL, * LOOKUP_NO_SYMLINKS etc). @@ -3759,8 +3760,8 @@ __start_renaming(struct renamedata *rd, int lookup_fl= ags, * rename. * * On success the found dentries are stored in @rd.old_dentry, - * @rd.new_dentry. These references and the lock are dropped by - * end_renaming(). + * @rd.new_dentry. Also the refcount on @rd->old_parent is increased. + * These references and the lock are dropped by end_renaming(). * * The passed in qstrs need not have the hash calculated, and basic * eXecute permission checking is performed against @rd.mnt_idmap. @@ -3782,11 +3783,106 @@ int start_renaming(struct renamedata *rd, int look= up_flags, } EXPORT_SYMBOL(start_renaming); =20 +static int +__start_renaming_dentry(struct renamedata *rd, int lookup_flags, + struct dentry *old_dentry, struct qstr *new_last) +{ + struct dentry *trap; + struct dentry *d2; + int target_flags =3D LOOKUP_RENAME_TARGET | LOOKUP_CREATE; + int err; + + if (rd->flags & RENAME_EXCHANGE) + target_flags =3D 0; + if (rd->flags & RENAME_NOREPLACE) + target_flags |=3D LOOKUP_EXCL; + + /* Already have the dentry - need to be sure to lock the correct parent */ + trap =3D lock_rename_child(old_dentry, rd->new_parent); + if (IS_ERR(trap)) + return PTR_ERR(trap); + if (d_unhashed(old_dentry) || + (rd->old_parent && rd->old_parent !=3D old_dentry->d_parent)) { + /* dentry was removed, or moved and explicit parent requested */ + err =3D -EINVAL; + goto out_unlock; + } + + d2 =3D lookup_one_qstr_excl(new_last, rd->new_parent, + lookup_flags | target_flags); + err =3D PTR_ERR(d2); + if (IS_ERR(d2)) + goto out_unlock; + + if (old_dentry =3D=3D trap) { + /* source is an ancestor of target */ + err =3D -EINVAL; + goto out_dput_d2; + } + + if (d2 =3D=3D trap) { + /* target is an ancestor of source */ + if (rd->flags & RENAME_EXCHANGE) + err =3D -EINVAL; + else + err =3D -ENOTEMPTY; + goto out_dput_d2; + } + + rd->old_dentry =3D dget(old_dentry); + rd->new_dentry =3D d2; + rd->old_parent =3D dget(old_dentry->d_parent); + return 0; + +out_dput_d2: + dput(d2); +out_unlock: + unlock_rename(old_dentry->d_parent, rd->new_parent); + return err; +} + +/** + * start_renaming_dentry - lookup and lock name for rename with permission= checking + * @rd: rename data containing parents and flags, and + * for receiving found dentries + * @lookup_flags: extra flags to pass to ->lookup (e.g. LOOKUP_REVAL, + * LOOKUP_NO_SYMLINKS etc). + * @old_dentry: dentry of name to move + * @new_last: name of target in @rd.new_parent + * + * Look up target name and ensure locks are in place for + * rename. + * + * On success the found dentry is stored in @rd.new_dentry and + * @rd.old_parent is confirmed to be the parent of @old_dentry. If it + * was originally %NULL, it is set. In either case a reference is taken + * so that end_renaming() can have a stable reference to unlock. + * + * References and the lock can be dropped with end_renaming() + * + * The passed in qstr need not have the hash calculated, and basic + * eXecute permission checking is performed against @rd.mnt_idmap. + * + * Returns: zero or an error. + */ +int start_renaming_dentry(struct renamedata *rd, int lookup_flags, + struct dentry *old_dentry, struct qstr *new_last) +{ + int err; + + err =3D lookup_one_common(rd->mnt_idmap, new_last, rd->new_parent); + if (err) + return err; + return __start_renaming_dentry(rd, lookup_flags, old_dentry, new_last); +} +EXPORT_SYMBOL(start_renaming_dentry); + void end_renaming(struct renamedata *rd) { unlock_rename(rd->old_parent, rd->new_parent); dput(rd->old_dentry); dput(rd->new_dentry); + dput(rd->old_parent); } EXPORT_SYMBOL(end_renaming); =20 diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 7a31ca9bdea2..27014ada11c7 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -523,8 +523,8 @@ static int ovl_create_index(struct dentry *dentry, cons= t struct ovl_fh *fh, { struct ovl_fs *ofs =3D OVL_FS(dentry->d_sb); struct dentry *indexdir =3D ovl_indexdir(dentry->d_sb); - struct dentry *index =3D NULL; struct dentry *temp =3D NULL; + struct renamedata rd =3D {}; struct qstr name =3D { }; int err; =20 @@ -556,17 +556,15 @@ static int ovl_create_index(struct dentry *dentry, co= nst struct ovl_fh *fh, if (err) goto out; =20 - err =3D ovl_parent_lock(indexdir, temp); + rd.mnt_idmap =3D ovl_upper_mnt_idmap(ofs); + rd.old_parent =3D indexdir; + rd.new_parent =3D indexdir; + err =3D start_renaming_dentry(&rd, 0, temp, &name); if (err) goto out; - index =3D ovl_lookup_upper(ofs, name.name, indexdir, name.len); - if (IS_ERR(index)) { - err =3D PTR_ERR(index); - } else { - err =3D ovl_do_rename(ofs, indexdir, temp, indexdir, index, 0); - dput(index); - } - ovl_parent_unlock(indexdir); + + err =3D ovl_do_rename_rd(&rd); + end_renaming(&rd); out: if (err) ovl_cleanup(ofs, indexdir, temp); @@ -763,7 +761,8 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *= c) struct ovl_fs *ofs =3D OVL_FS(c->dentry->d_sb); struct inode *inode; struct path path =3D { .mnt =3D ovl_upper_mnt(ofs) }; - struct dentry *temp, *upper, *trap; + struct renamedata rd =3D {}; + struct dentry *temp; struct ovl_cu_creds cc; int err; struct ovl_cattr cattr =3D { @@ -807,29 +806,24 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx= *c) * ovl_copy_up_data(), so lock workdir and destdir and make sure that * temp wasn't moved before copy up completion or cleanup. */ - trap =3D lock_rename(c->workdir, c->destdir); - if (trap || temp->d_parent !=3D c->workdir) { - /* temp or workdir moved underneath us? abort without cleanup */ - dput(temp); + rd.mnt_idmap =3D ovl_upper_mnt_idmap(ofs); + rd.old_parent =3D c->workdir; + rd.new_parent =3D c->destdir; + rd.flags =3D 0; + err =3D start_renaming_dentry(&rd, 0, temp, + &QSTR_LEN(c->destname.name, c->destname.len)); + if (err) { + /* temp or workdir moved underneath us? map to -EIO */ err =3D -EIO; - if (!IS_ERR(trap)) - unlock_rename(c->workdir, c->destdir); - goto out; } - - err =3D ovl_copy_up_metadata(c, temp); if (err) - goto cleanup; + goto cleanup_unlocked; =20 - upper =3D ovl_lookup_upper(ofs, c->destname.name, c->destdir, - c->destname.len); - err =3D PTR_ERR(upper); - if (IS_ERR(upper)) - goto cleanup; + err =3D ovl_copy_up_metadata(c, temp); + if (!err) + err =3D ovl_do_rename_rd(&rd); + end_renaming(&rd); =20 - err =3D ovl_do_rename(ofs, c->workdir, temp, c->destdir, upper, 0); - unlock_rename(c->workdir, c->destdir); - dput(upper); if (err) goto cleanup_unlocked; =20 @@ -850,8 +844,6 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *= c) =20 return err; =20 -cleanup: - unlock_rename(c->workdir, c->destdir); cleanup_unlocked: ovl_cleanup(ofs, c->workdir, temp); dput(temp); diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 0f2c2da68433..2b423ebc85c4 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -57,8 +57,7 @@ int ovl_cleanup(struct ovl_fs *ofs, struct dentry *workdi= r, return 0; } =20 -#define OVL_TEMPNAME_SIZE 20 -static void ovl_tempname(char name[OVL_TEMPNAME_SIZE]) +void ovl_tempname(char name[OVL_TEMPNAME_SIZE]) { static atomic_t temp_id =3D ATOMIC_INIT(0); =20 @@ -66,22 +65,6 @@ static void ovl_tempname(char name[OVL_TEMPNAME_SIZE]) snprintf(name, OVL_TEMPNAME_SIZE, "#%x", atomic_inc_return(&temp_id)); } =20 -struct dentry *ovl_lookup_temp(struct ovl_fs *ofs, struct dentry *workdir) -{ - struct dentry *temp; - char name[OVL_TEMPNAME_SIZE]; - - ovl_tempname(name); - temp =3D ovl_lookup_upper(ofs, name, workdir, strlen(name)); - if (!IS_ERR(temp) && temp->d_inode) { - pr_err("workdir/%s already exists\n", name); - dput(temp); - temp =3D ERR_PTR(-EIO); - } - - return temp; -} - static struct dentry *ovl_start_creating_temp(struct ovl_fs *ofs, struct dentry *workdir) { diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 3cc85a893b5c..746bc4ad7b37 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -447,11 +447,6 @@ static inline bool ovl_open_flags_need_copy_up(int fla= gs) } =20 /* util.c */ -int ovl_parent_lock(struct dentry *parent, struct dentry *child); -static inline void ovl_parent_unlock(struct dentry *parent) -{ - inode_unlock(parent->d_inode); -} int ovl_get_write_access(struct dentry *dentry); void ovl_put_write_access(struct dentry *dentry); void ovl_start_write(struct dentry *dentry); @@ -888,7 +883,8 @@ struct dentry *ovl_create_real(struct ovl_fs *ofs, struct dentry *parent, struct dentry *newdentry, struct ovl_cattr *attr); int ovl_cleanup(struct ovl_fs *ofs, struct dentry *workdir, struct dentry = *dentry); -struct dentry *ovl_lookup_temp(struct ovl_fs *ofs, struct dentry *workdir); +#define OVL_TEMPNAME_SIZE 20 +void ovl_tempname(char name[OVL_TEMPNAME_SIZE]); struct dentry *ovl_create_temp(struct ovl_fs *ofs, struct dentry *workdir, struct ovl_cattr *attr); =20 diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 6e0816c1147a..a721ef2b90e8 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -566,9 +566,10 @@ static int ovl_check_rename_whiteout(struct ovl_fs *of= s) { struct dentry *workdir =3D ofs->workdir; struct dentry *temp; - struct dentry *dest; struct dentry *whiteout; struct name_snapshot name; + struct renamedata rd =3D {}; + char name2[OVL_TEMPNAME_SIZE]; int err; =20 temp =3D ovl_create_temp(ofs, workdir, OVL_CATTR(S_IFREG | 0)); @@ -576,23 +577,21 @@ static int ovl_check_rename_whiteout(struct ovl_fs *o= fs) if (IS_ERR(temp)) return err; =20 - err =3D ovl_parent_lock(workdir, temp); + rd.mnt_idmap =3D ovl_upper_mnt_idmap(ofs); + rd.old_parent =3D workdir; + rd.new_parent =3D workdir; + rd.flags =3D RENAME_WHITEOUT; + ovl_tempname(name2); + err =3D start_renaming_dentry(&rd, 0, temp, &QSTR(name2)); if (err) { dput(temp); return err; } - dest =3D ovl_lookup_temp(ofs, workdir); - err =3D PTR_ERR(dest); - if (IS_ERR(dest)) { - dput(temp); - ovl_parent_unlock(workdir); - return err; - } =20 /* Name is inline and stable - using snapshot as a copy helper */ take_dentry_name_snapshot(&name, temp); - err =3D ovl_do_rename(ofs, workdir, temp, workdir, dest, RENAME_WHITEOUT); - ovl_parent_unlock(workdir); + err =3D ovl_do_rename_rd(&rd); + end_renaming(&rd); if (err) { if (err =3D=3D -EINVAL) err =3D 0; @@ -616,7 +615,6 @@ static int ovl_check_rename_whiteout(struct ovl_fs *ofs) ovl_cleanup(ofs, workdir, temp); release_dentry_name_snapshot(&name); dput(temp); - dput(dest); =20 return err; } diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index f76672f2e686..46387aeb6be6 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -1548,14 +1548,3 @@ void ovl_copyattr(struct inode *inode) i_size_write(inode, i_size_read(realinode)); spin_unlock(&inode->i_lock); } - -int ovl_parent_lock(struct dentry *parent, struct dentry *child) -{ - inode_lock_nested(parent->d_inode, I_MUTEX_PARENT); - if (!child || - (!d_unhashed(child) && child->d_parent =3D=3D parent)) - return 0; - - inode_unlock(parent->d_inode); - return -EINVAL; -} diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index 7c4ddc43ab39..f54b5b0aaba2 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -663,7 +663,6 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char = *oldname, int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, char *newname, int flags) { - struct dentry *old_parent, *new_dentry, *trap; struct dentry *old_child =3D old_path->dentry; struct path new_path; struct qstr new_last; @@ -673,7 +672,6 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const str= uct path *old_path, struct ksmbd_file *parent_fp; int new_type; int err, lookup_flags =3D LOOKUP_NO_SYMLINKS; - int target_lookup_flags =3D LOOKUP_RENAME_TARGET | LOOKUP_CREATE; =20 if (ksmbd_override_fsids(work)) return -ENOMEM; @@ -684,14 +682,6 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const st= ruct path *old_path, goto revert_fsids; } =20 - /* - * explicitly handle file overwrite case, for compatibility with - * filesystems that may not support rename flags (e.g: fuse) - */ - if (flags & RENAME_NOREPLACE) - target_lookup_flags |=3D LOOKUP_EXCL; - flags &=3D ~(RENAME_NOREPLACE); - retry: err =3D vfs_path_parent_lookup(to, lookup_flags | LOOKUP_BENEATH, &new_path, &new_last, &new_type, @@ -708,17 +698,14 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const s= truct path *old_path, if (err) goto out2; =20 - trap =3D lock_rename_child(old_child, new_path.dentry); - if (IS_ERR(trap)) { - err =3D PTR_ERR(trap); + rd.mnt_idmap =3D mnt_idmap(old_path->mnt); + rd.old_parent =3D NULL; + rd.new_parent =3D new_path.dentry; + rd.flags =3D flags; + rd.delegated_inode =3D NULL, + err =3D start_renaming_dentry(&rd, lookup_flags, old_child, &new_last); + if (err) goto out_drop_write; - } - - old_parent =3D dget(old_child->d_parent); - if (d_unhashed(old_child)) { - err =3D -EINVAL; - goto out3; - } =20 parent_fp =3D ksmbd_lookup_fd_inode(old_child->d_parent); if (parent_fp) { @@ -731,44 +718,17 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const s= truct path *old_path, ksmbd_fd_put(work, parent_fp); } =20 - new_dentry =3D lookup_one_qstr_excl(&new_last, new_path.dentry, - lookup_flags | target_lookup_flags); - if (IS_ERR(new_dentry)) { - err =3D PTR_ERR(new_dentry); - goto out3; - } - - if (d_is_symlink(new_dentry)) { + if (d_is_symlink(rd.new_dentry)) { err =3D -EACCES; - goto out4; - } - - if (old_child =3D=3D trap) { - err =3D -EINVAL; - goto out4; - } - - if (new_dentry =3D=3D trap) { - err =3D -ENOTEMPTY; - goto out4; + goto out3; } =20 - rd.mnt_idmap =3D mnt_idmap(old_path->mnt), - rd.old_parent =3D old_parent, - rd.old_dentry =3D old_child, - rd.new_parent =3D new_path.dentry, - rd.new_dentry =3D new_dentry, - rd.flags =3D flags, - rd.delegated_inode =3D NULL, err =3D vfs_rename(&rd); if (err) ksmbd_debug(VFS, "vfs_rename failed err %d\n", err); =20 -out4: - dput(new_dentry); out3: - dput(old_parent); - unlock_rename(old_parent, new_path.dentry); + end_renaming(&rd); out_drop_write: mnt_drop_write(old_path->mnt); out2: diff --git a/include/linux/namei.h b/include/linux/namei.h index 19c3d8e336d5..f73001e3719a 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -158,6 +158,8 @@ extern struct dentry *lock_rename_child(struct dentry *= , struct dentry *); extern void unlock_rename(struct dentry *, struct dentry *); int start_renaming(struct renamedata *rd, int lookup_flags, struct qstr *old_last, struct qstr *new_last); +int start_renaming_dentry(struct renamedata *rd, int lookup_flags, + struct dentry *old_dentry, struct qstr *new_last); void end_renaming(struct renamedata *rd); =20 /** --=20 2.50.0.107.gf914562f5916.dirty From nobody Sun Dec 14 05:53:24 2025 Received: from flow-b4-smtp.messagingengine.com (flow-b4-smtp.messagingengine.com [202.12.124.139]) (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 19D7432C93B; Wed, 29 Oct 2025 23:47:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=202.12.124.139 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781630; cv=none; b=CsZlkcB4J4yPbnkU98CCMiSzsn20gaVzKfnMgWog4pxS+gDQRxXbR6Z7GT7ji+8U+9ycz4Y40EfHwVO+4uuvsz9/Lpp37I32ygwuNfMakwO3Emb4c2lwQ9rQIYA0gWWSyeUNa2D6Y13jaJfuL3OzFnhoiz0YMumx+qAIbDAOWRU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781630; c=relaxed/simple; bh=BPLlYPaaY7tGkmL5RNJ+K70Y8B+lB2jTpag0k7rzYPc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=C+m1jCSuLWWyQ3Ixa4TIf7rUWEnptOl3rne+oIhoSQlVLuOtm3ybmYi2lfEZkwT8EL8t6IS3dg491+uTjDs651t5ye5dlJwGowqVZpRByXNNHjoGCyv+ongC9UhrtCoBNo1WdaFuweTNXOW8JwuyjMecnGiwOvsbfcFjXO4UrzU= 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=E2tdbEx3; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=uQldl1dg; arc=none smtp.client-ip=202.12.124.139 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="E2tdbEx3"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="uQldl1dg" Received: from phl-compute-06.internal (phl-compute-06.internal [10.202.2.46]) by mailflow.stl.internal (Postfix) with ESMTP id 95E23130007D; Wed, 29 Oct 2025 19:47:06 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-06.internal (MEProxy); Wed, 29 Oct 2025 19:47:07 -0400 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=1761781626; x=1761788826; bh=2SBWOMlFbj/nAtdU7UJzUsxaWMXi+3bGb2vgE9LAg5A=; b= E2tdbEx3TE9TmXHqc5IaoYzrFR783JBPcXLG79oNhSfnE1zbmBTMrDWDkT5p3MMc hnjvLv/u1tl0QOP7wsBx+DtKbtMzrOEukhOpAZ87mbMQc7Cbx8XvSA5wju2Z5/Ws k6O1uejPNBcOtiIBighw4zDW2CMSiTQP20BjB9T7GQrgL7em/cQgmfiIMWvEJ5BG Flhz+bYKZh4Iijviy8hjMNzMnGkPdqDxeZr8klTq+/QyTLGPThwJO0tQ1r3QEXIt 68sweF4DUM66zH5O/xPY+tWVaTG50+fD/IKvT7DkbhFcILa6LgcguPs2QBJmy7XH 8BDMk/2mzsIOgMo2lqR69w== 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=1761781626; x=1761788826; bh=2 SBWOMlFbj/nAtdU7UJzUsxaWMXi+3bGb2vgE9LAg5A=; b=uQldl1dgWDSfyjJjl Aj+p8C80WfAa/Lt3xWVCaHM+D4RCXOgINfz1V8YKck7zYqcKdHy82qMM4ZgTvSwl 2qFJE9e80eoga8OEYy9gQRI3IaWzqApq5ez37C0/hSSfMdVr4xr590VW8hiR5og1 4XTQV6N/IhRrH7Ys/1vtVIoxDkixTV/Vr7KLGbDKsudQ/RIPoQU/E+rVUfOVRWOR 2944FewOBs2hDozkhp4qMF0y/jsuLFPIkyZiXoVnQVw7pvUZRBQOSiwWE8BvOKO5 08z3bkbLDnTb4QnmfsMFwUSvsF7zUXo0S9j0MyT1f7P0lTFlCgYdYaYsDXASTjX+ dA+sA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggdduieehtdelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedupdhmohguvgepshhmthhp 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, 29 Oct 2025 19:46:55 -0400 (EDT) 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, apparmor@lists.ubuntu.com, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v4 11/14] Add start_renaming_two_dentries() Date: Thu, 30 Oct 2025 10:31:11 +1100 Message-ID: <20251029234353.1321957-12-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251029234353.1321957-1-neilb@ownmail.net> References: <20251029234353.1321957-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 A few callers want to lock for a rename and already have both dentries. Also debugfs does want to perform a lookup but doesn't want permission checking, so start_renaming_dentry() cannot be used. This patch introduces start_renaming_two_dentries() which is given both dentries. debugfs performs one lookup itself. As it will only continue with a negative dentry and as those cannot be renamed or unlinked, it is safe to do the lookup before getting the rename locks. overlayfs uses start_renaming_two_dentries() in three places and selinux uses it twice in sel_make_policy_nodes(). Reviewed-by: Amir Goldstein Signed-off-by: NeilBrown --- changes since v3: added missing assignment to rd.mnt_idmap in ovl_cleanup_and_whiteout --- fs/debugfs/inode.c | 48 ++++++++++++-------------- fs/namei.c | 65 ++++++++++++++++++++++++++++++++++++ fs/overlayfs/dir.c | 43 ++++++++++++++++-------- include/linux/namei.h | 2 ++ security/selinux/selinuxfs.c | 27 ++++++++++----- 5 files changed, 136 insertions(+), 49 deletions(-) diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index f241b9df642a..532bd7c46baf 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -842,7 +842,8 @@ int __printf(2, 3) debugfs_change_name(struct dentry *d= entry, const char *fmt, . int error =3D 0; const char *new_name; struct name_snapshot old_name; - struct dentry *parent, *target; + struct dentry *target; + struct renamedata rd =3D {}; struct inode *dir; va_list ap; =20 @@ -855,36 +856,31 @@ int __printf(2, 3) debugfs_change_name(struct dentry = *dentry, const char *fmt, . if (!new_name) return -ENOMEM; =20 - parent =3D dget_parent(dentry); - dir =3D d_inode(parent); - inode_lock(dir); + rd.old_parent =3D dget_parent(dentry); + rd.new_parent =3D rd.old_parent; + rd.flags =3D RENAME_NOREPLACE; + target =3D lookup_noperm_unlocked(&QSTR(new_name), rd.new_parent); + if (IS_ERR(target)) + return PTR_ERR(target); =20 - take_dentry_name_snapshot(&old_name, dentry); - - if (WARN_ON_ONCE(dentry->d_parent !=3D parent)) { - error =3D -EINVAL; - goto out; - } - if (strcmp(old_name.name.name, new_name) =3D=3D 0) - goto out; - target =3D lookup_noperm(&QSTR(new_name), parent); - if (IS_ERR(target)) { - error =3D PTR_ERR(target); - goto out; - } - if (d_really_is_positive(target)) { - dput(target); - error =3D -EINVAL; + error =3D start_renaming_two_dentries(&rd, dentry, target); + if (error) { + if (error =3D=3D -EEXIST && target =3D=3D dentry) + /* it isn't an error to rename a thing to itself */ + error =3D 0; goto out; } - simple_rename_timestamp(dir, dentry, dir, target); - d_move(dentry, target); - dput(target); + + dir =3D d_inode(rd.old_parent); + take_dentry_name_snapshot(&old_name, dentry); + simple_rename_timestamp(dir, dentry, dir, rd.new_dentry); + d_move(dentry, rd.new_dentry); fsnotify_move(dir, dir, &old_name.name, d_is_dir(dentry), NULL, dentry); -out: release_dentry_name_snapshot(&old_name); - inode_unlock(dir); - dput(parent); + end_renaming(&rd); +out: + dput(rd.old_parent); + dput(target); kfree_const(new_name); return error; } diff --git a/fs/namei.c b/fs/namei.c index 5153ceddd37a..4a4b8b96c192 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3877,6 +3877,71 @@ int start_renaming_dentry(struct renamedata *rd, int= lookup_flags, } EXPORT_SYMBOL(start_renaming_dentry); =20 +/** + * start_renaming_two_dentries - Lock to dentries in given parents for ren= ame + * @rd: rename data containing parent + * @old_dentry: dentry of name to move + * @new_dentry: dentry to move to + * + * Ensure locks are in place for rename and check parentage is still corre= ct. + * + * On success the two dentries are stored in @rd.old_dentry and + * @rd.new_dentry and @rd.old_parent and @rd.new_parent are confirmed to + * be the parents of the dentries. + * + * References and the lock can be dropped with end_renaming() + * + * Returns: zero or an error. + */ +int +start_renaming_two_dentries(struct renamedata *rd, + struct dentry *old_dentry, struct dentry *new_dentry) +{ + struct dentry *trap; + int err; + + /* Already have the dentry - need to be sure to lock the correct parent */ + trap =3D lock_rename_child(old_dentry, rd->new_parent); + if (IS_ERR(trap)) + return PTR_ERR(trap); + err =3D -EINVAL; + if (d_unhashed(old_dentry) || + (rd->old_parent && rd->old_parent !=3D old_dentry->d_parent)) + /* old_dentry was removed, or moved and explicit parent requested */ + goto out_unlock; + if (d_unhashed(new_dentry) || + rd->new_parent !=3D new_dentry->d_parent) + /* new_dentry was removed or moved */ + goto out_unlock; + + if (old_dentry =3D=3D trap) + /* source is an ancestor of target */ + goto out_unlock; + + if (new_dentry =3D=3D trap) { + /* target is an ancestor of source */ + if (rd->flags & RENAME_EXCHANGE) + err =3D -EINVAL; + else + err =3D -ENOTEMPTY; + goto out_unlock; + } + + err =3D -EEXIST; + if (d_is_positive(new_dentry) && (rd->flags & RENAME_NOREPLACE)) + goto out_unlock; + + rd->old_dentry =3D dget(old_dentry); + rd->new_dentry =3D dget(new_dentry); + rd->old_parent =3D dget(old_dentry->d_parent); + return 0; + +out_unlock: + unlock_rename(old_dentry->d_parent, rd->new_parent); + return err; +} +EXPORT_SYMBOL(start_renaming_two_dentries); + void end_renaming(struct renamedata *rd) { unlock_rename(rd->old_parent, rd->new_parent); diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 2b423ebc85c4..c297648c7487 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -123,6 +123,7 @@ int ovl_cleanup_and_whiteout(struct ovl_fs *ofs, struct= dentry *dir, struct dentry *dentry) { struct dentry *whiteout; + struct renamedata rd =3D {}; int err; int flags =3D 0; =20 @@ -134,10 +135,14 @@ int ovl_cleanup_and_whiteout(struct ovl_fs *ofs, stru= ct dentry *dir, if (d_is_dir(dentry)) flags =3D RENAME_EXCHANGE; =20 - err =3D ovl_lock_rename_workdir(ofs->workdir, whiteout, dir, dentry); + rd.mnt_idmap =3D ovl_upper_mnt_idmap(ofs); + rd.old_parent =3D ofs->workdir; + rd.new_parent =3D dir; + rd.flags =3D flags; + err =3D start_renaming_two_dentries(&rd, whiteout, dentry); if (!err) { - err =3D ovl_do_rename(ofs, ofs->workdir, whiteout, dir, dentry, flags); - unlock_rename(ofs->workdir, dir); + err =3D ovl_do_rename_rd(&rd); + end_renaming(&rd); } if (err) goto kill_whiteout; @@ -388,6 +393,7 @@ static struct dentry *ovl_clear_empty(struct dentry *de= ntry, struct ovl_fs *ofs =3D OVL_FS(dentry->d_sb); struct dentry *workdir =3D ovl_workdir(dentry); struct dentry *upperdir =3D ovl_dentry_upper(dentry->d_parent); + struct renamedata rd =3D {}; struct path upperpath; struct dentry *upper; struct dentry *opaquedir; @@ -413,7 +419,11 @@ static struct dentry *ovl_clear_empty(struct dentry *d= entry, if (IS_ERR(opaquedir)) goto out; =20 - err =3D ovl_lock_rename_workdir(workdir, opaquedir, upperdir, upper); + rd.mnt_idmap =3D ovl_upper_mnt_idmap(ofs); + rd.old_parent =3D workdir; + rd.new_parent =3D upperdir; + rd.flags =3D RENAME_EXCHANGE; + err =3D start_renaming_two_dentries(&rd, opaquedir, upper); if (err) goto out_cleanup_unlocked; =20 @@ -431,8 +441,8 @@ static struct dentry *ovl_clear_empty(struct dentry *de= ntry, if (err) goto out_cleanup; =20 - err =3D ovl_do_rename(ofs, workdir, opaquedir, upperdir, upper, RENAME_EX= CHANGE); - unlock_rename(workdir, upperdir); + err =3D ovl_do_rename_rd(&rd); + end_renaming(&rd); if (err) goto out_cleanup_unlocked; =20 @@ -445,7 +455,7 @@ static struct dentry *ovl_clear_empty(struct dentry *de= ntry, return opaquedir; =20 out_cleanup: - unlock_rename(workdir, upperdir); + end_renaming(&rd); out_cleanup_unlocked: ovl_cleanup(ofs, workdir, opaquedir); dput(opaquedir); @@ -468,6 +478,7 @@ static int ovl_create_over_whiteout(struct dentry *dent= ry, struct inode *inode, struct ovl_fs *ofs =3D OVL_FS(dentry->d_sb); struct dentry *workdir =3D ovl_workdir(dentry); struct dentry *upperdir =3D ovl_dentry_upper(dentry->d_parent); + struct renamedata rd =3D {}; struct dentry *upper; struct dentry *newdentry; int err; @@ -499,7 +510,11 @@ static int ovl_create_over_whiteout(struct dentry *den= try, struct inode *inode, if (IS_ERR(newdentry)) goto out_dput; =20 - err =3D ovl_lock_rename_workdir(workdir, newdentry, upperdir, upper); + rd.mnt_idmap =3D ovl_upper_mnt_idmap(ofs); + rd.old_parent =3D workdir; + rd.new_parent =3D upperdir; + rd.flags =3D 0; + err =3D start_renaming_two_dentries(&rd, newdentry, upper); if (err) goto out_cleanup_unlocked; =20 @@ -536,16 +551,16 @@ static int ovl_create_over_whiteout(struct dentry *de= ntry, struct inode *inode, if (err) goto out_cleanup; =20 - err =3D ovl_do_rename(ofs, workdir, newdentry, upperdir, upper, - RENAME_EXCHANGE); - unlock_rename(workdir, upperdir); + rd.flags =3D RENAME_EXCHANGE; + err =3D ovl_do_rename_rd(&rd); + end_renaming(&rd); if (err) goto out_cleanup_unlocked; =20 ovl_cleanup(ofs, workdir, upper); } else { - err =3D ovl_do_rename(ofs, workdir, newdentry, upperdir, upper, 0); - unlock_rename(workdir, upperdir); + err =3D ovl_do_rename_rd(&rd); + end_renaming(&rd); if (err) goto out_cleanup_unlocked; } @@ -565,7 +580,7 @@ static int ovl_create_over_whiteout(struct dentry *dent= ry, struct inode *inode, return err; =20 out_cleanup: - unlock_rename(workdir, upperdir); + end_renaming(&rd); out_cleanup_unlocked: ovl_cleanup(ofs, workdir, newdentry); dput(newdentry); diff --git a/include/linux/namei.h b/include/linux/namei.h index f73001e3719a..a99ac8b7e24a 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -160,6 +160,8 @@ int start_renaming(struct renamedata *rd, int lookup_fl= ags, struct qstr *old_last, struct qstr *new_last); int start_renaming_dentry(struct renamedata *rd, int lookup_flags, struct dentry *old_dentry, struct qstr *new_last); +int start_renaming_two_dentries(struct renamedata *rd, + struct dentry *old_dentry, struct dentry *new_dentry); void end_renaming(struct renamedata *rd); =20 /** diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 232e087bce3e..a224ef9bb831 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -506,6 +506,7 @@ static int sel_make_policy_nodes(struct selinux_fs_info= *fsi, { int ret =3D 0; struct dentry *tmp_parent, *tmp_bool_dir, *tmp_class_dir; + struct renamedata rd =3D {}; unsigned int bool_num =3D 0; char **bool_names =3D NULL; int *bool_values =3D NULL; @@ -539,22 +540,30 @@ static int sel_make_policy_nodes(struct selinux_fs_in= fo *fsi, if (ret) goto out; =20 - lock_rename(tmp_parent, fsi->sb->s_root); + rd.old_parent =3D tmp_parent; + rd.new_parent =3D fsi->sb->s_root; =20 /* booleans */ - d_exchange(tmp_bool_dir, fsi->bool_dir); + ret =3D start_renaming_two_dentries(&rd, tmp_bool_dir, fsi->bool_dir); + if (!ret) { + d_exchange(tmp_bool_dir, fsi->bool_dir); =20 - swap(fsi->bool_num, bool_num); - swap(fsi->bool_pending_names, bool_names); - swap(fsi->bool_pending_values, bool_values); + swap(fsi->bool_num, bool_num); + swap(fsi->bool_pending_names, bool_names); + swap(fsi->bool_pending_values, bool_values); =20 - fsi->bool_dir =3D tmp_bool_dir; + fsi->bool_dir =3D tmp_bool_dir; + end_renaming(&rd); + } =20 /* classes */ - d_exchange(tmp_class_dir, fsi->class_dir); - fsi->class_dir =3D tmp_class_dir; + ret =3D start_renaming_two_dentries(&rd, tmp_class_dir, fsi->class_dir); + if (ret =3D=3D 0) { + d_exchange(tmp_class_dir, fsi->class_dir); + fsi->class_dir =3D tmp_class_dir; =20 - unlock_rename(tmp_parent, fsi->sb->s_root); + end_renaming(&rd); + } =20 out: sel_remove_old_bool_data(bool_num, bool_names, bool_values); --=20 2.50.0.107.gf914562f5916.dirty From nobody Sun Dec 14 05:53:24 2025 Received: from flow-b4-smtp.messagingengine.com (flow-b4-smtp.messagingengine.com [202.12.124.139]) (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 D5CE6320CCA; Wed, 29 Oct 2025 23:47:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=202.12.124.139 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781643; cv=none; b=SbQpRyXdNE9LahxmDxJ6n0ie0lLaLusHvJTPgdcZO+QXFqR3Cz9QLExOfXD9m5b4fw8WTtpK/N7Fp2VxRvrqoRBOJznXCkPqKfuNDG2rr/Me5f7jn7UnHSJK0HTHy7h9ou1K3oZZyIavhHNDIsqxa3PlrD0Sc0FpSprIh7grT1Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781643; c=relaxed/simple; bh=HdPv8mDw8o2H979T1xmhL3gmAMBLZERXGevY6LlAFbw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=d8aGJSiFKsi5xYjttAX0IdBqyo5f94DoDj2GZnTcV6quWUa859e8gqSfnocR615UBNUiRVVEiWDawp7QeNOQwkKDvN7Utbu+WKrkd14557Y+1npWJyXjRjJ8/W3GrxaxMVowLZCXVSKoXK2VNKsJBavpo2pn4gL4Yi+pOeooSzc= 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=Fd6qsdGa; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=lTaPxMzw; arc=none smtp.client-ip=202.12.124.139 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="Fd6qsdGa"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="lTaPxMzw" Received: from phl-compute-06.internal (phl-compute-06.internal [10.202.2.46]) by mailflow.stl.internal (Postfix) with ESMTP id 502CE130007D; Wed, 29 Oct 2025 19:47:20 -0400 (EDT) Received: from phl-mailfrontend-01 ([10.202.2.162]) by phl-compute-06.internal (MEProxy); Wed, 29 Oct 2025 19:47:21 -0400 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=1761781640; x=1761788840; bh=74qOPrcozMZPRqC6V9uAJY4k8e/gVMYB1Snqabb+puY=; b= Fd6qsdGaGT3Fchz3CaSzbNlgEPQ8rh/JzSCvdKnMb7qJFM98Ds3ut2gC1S/MKL6E YsstwUkktCgnl70ZtzhwV/EY3jZ8wLYwb3Z8Hqaud3D1aaqJITXByMStrXDhwPeh 6uv9RAaQ5npUHv2UrvrhDJomvZjyWj1nzt/QZv+FkhaufGZe4OCOEBzhaFczR9ET 3pARbp0JZuCFQ0fmKKJDD1+sZAxlgTDkBbHsmOvan7V1ZGttZEgHvWrAwbxdzQOx h/MJ0ifJBCN5y+ODKPIkL9xeUQ9DYKSwm8y8cu4/ilHutcJ0Mam5/FJur7hoXWRS SUd4qz7+Ta4k4IWbfyyJeg== 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=1761781640; x=1761788840; bh=7 4qOPrcozMZPRqC6V9uAJY4k8e/gVMYB1Snqabb+puY=; b=lTaPxMzwb50WPJh+I PgOC9CK5BtKtKX58pkPhRtSqUXIcjw+VT3l0nf7bLSw+idByVhBWMAlkphlLPcCG dWJJjxAYgns70uvOwHLTHMAeoTyiLSFBQ4LlFx2UP04MgB5GLCREF4akutDFrbrW plrn8vJHjyEMZh8OCGKjdzo7titOAWFDWGKk7+tJs2kKpcQ6yUPru6S/Papxqq5S krpYmCxEARt5kEUrnz+UpH2H4WCJpSaycG02nvvN0bNLKooCXslj/zJFRuyBf7Kz sMTR5d2SNCTKS9a7Lujh+khvpYDNqjG2+SUtwloQpH0+S34RkvTIGTdyWBrkQ+FV mHZcA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggdduieehtdelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgepudenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedupdhmohguvgepshhmthhp 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, 29 Oct 2025 19:47:09 -0400 (EDT) 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, apparmor@lists.ubuntu.com, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v4 12/14] ecryptfs: use new start_creating/start_removing APIs Date: Thu, 30 Oct 2025 10:31:12 +1100 Message-ID: <20251029234353.1321957-13-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251029234353.1321957-1-neilb@ownmail.net> References: <20251029234353.1321957-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 This requires the addition of start_creating_dentry() which is given the dentry which has already been found, and asks for it to be locked and its parent validated. Reviewed-by: Amir Goldstein Signed-off-by: NeilBrown --- fs/ecryptfs/inode.c | 153 ++++++++++++++++++++---------------------- fs/namei.c | 33 +++++++++ include/linux/namei.h | 2 + 3 files changed, 107 insertions(+), 81 deletions(-) diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index ed1394da8d6b..b3702105d236 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -24,18 +24,26 @@ #include #include "ecryptfs_kernel.h" =20 -static int lock_parent(struct dentry *dentry, - struct dentry **lower_dentry, - struct inode **lower_dir) +static struct dentry *ecryptfs_start_creating_dentry(struct dentry *dentry) { - struct dentry *lower_dir_dentry; + struct dentry *parent =3D dget_parent(dentry->d_parent); + struct dentry *ret; =20 - lower_dir_dentry =3D ecryptfs_dentry_to_lower(dentry->d_parent); - *lower_dir =3D d_inode(lower_dir_dentry); - *lower_dentry =3D ecryptfs_dentry_to_lower(dentry); + ret =3D start_creating_dentry(ecryptfs_dentry_to_lower(parent), + ecryptfs_dentry_to_lower(dentry)); + dput(parent); + return ret; +} =20 - inode_lock_nested(*lower_dir, I_MUTEX_PARENT); - return (*lower_dentry)->d_parent =3D=3D lower_dir_dentry ? 0 : -EINVAL; +static struct dentry *ecryptfs_start_removing_dentry(struct dentry *dentry) +{ + struct dentry *parent =3D dget_parent(dentry->d_parent); + struct dentry *ret; + + ret =3D start_removing_dentry(ecryptfs_dentry_to_lower(parent), + ecryptfs_dentry_to_lower(dentry)); + dput(parent); + return ret; } =20 static int ecryptfs_inode_test(struct inode *inode, void *lower_inode) @@ -141,15 +149,12 @@ static int ecryptfs_do_unlink(struct inode *dir, stru= ct dentry *dentry, struct inode *lower_dir; int rc; =20 - rc =3D lock_parent(dentry, &lower_dentry, &lower_dir); - dget(lower_dentry); // don't even try to make the lower negative - if (!rc) { - if (d_unhashed(lower_dentry)) - rc =3D -EINVAL; - else - rc =3D vfs_unlink(&nop_mnt_idmap, lower_dir, lower_dentry, - NULL); - } + lower_dentry =3D ecryptfs_start_removing_dentry(dentry); + if (IS_ERR(lower_dentry)) + return PTR_ERR(lower_dentry); + + lower_dir =3D lower_dentry->d_parent->d_inode; + rc =3D vfs_unlink(&nop_mnt_idmap, lower_dir, lower_dentry, NULL); if (rc) { printk(KERN_ERR "Error in vfs_unlink; rc =3D [%d]\n", rc); goto out_unlock; @@ -158,8 +163,7 @@ static int ecryptfs_do_unlink(struct inode *dir, struct= dentry *dentry, set_nlink(inode, ecryptfs_inode_to_lower(inode)->i_nlink); inode_set_ctime_to_ts(inode, inode_get_ctime(dir)); out_unlock: - dput(lower_dentry); - inode_unlock(lower_dir); + end_removing(lower_dentry); if (!rc) d_drop(dentry); return rc; @@ -186,10 +190,12 @@ ecryptfs_do_create(struct inode *directory_inode, struct inode *lower_dir; struct inode *inode; =20 - rc =3D lock_parent(ecryptfs_dentry, &lower_dentry, &lower_dir); - if (!rc) - rc =3D vfs_create(&nop_mnt_idmap, lower_dir, - lower_dentry, mode, true); + lower_dentry =3D ecryptfs_start_creating_dentry(ecryptfs_dentry); + if (IS_ERR(lower_dentry)) + return ERR_CAST(lower_dentry); + lower_dir =3D lower_dentry->d_parent->d_inode; + rc =3D vfs_create(&nop_mnt_idmap, lower_dir, + lower_dentry, mode, true); if (rc) { printk(KERN_ERR "%s: Failure to create dentry in lower fs; " "rc =3D [%d]\n", __func__, rc); @@ -205,7 +211,7 @@ ecryptfs_do_create(struct inode *directory_inode, fsstack_copy_attr_times(directory_inode, lower_dir); fsstack_copy_inode_size(directory_inode, lower_dir); out_lock: - inode_unlock(lower_dir); + end_creating(lower_dentry, NULL); return inode; } =20 @@ -433,10 +439,12 @@ static int ecryptfs_link(struct dentry *old_dentry, s= truct inode *dir, =20 file_size_save =3D i_size_read(d_inode(old_dentry)); lower_old_dentry =3D ecryptfs_dentry_to_lower(old_dentry); - rc =3D lock_parent(new_dentry, &lower_new_dentry, &lower_dir); - if (!rc) - rc =3D vfs_link(lower_old_dentry, &nop_mnt_idmap, lower_dir, - lower_new_dentry, NULL); + lower_new_dentry =3D ecryptfs_start_creating_dentry(new_dentry); + if (IS_ERR(lower_new_dentry)) + return PTR_ERR(lower_new_dentry); + lower_dir =3D lower_new_dentry->d_parent->d_inode; + rc =3D vfs_link(lower_old_dentry, &nop_mnt_idmap, lower_dir, + lower_new_dentry, NULL); if (rc || d_really_is_negative(lower_new_dentry)) goto out_lock; rc =3D ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb); @@ -448,7 +456,7 @@ static int ecryptfs_link(struct dentry *old_dentry, str= uct inode *dir, ecryptfs_inode_to_lower(d_inode(old_dentry))->i_nlink); i_size_write(d_inode(new_dentry), file_size_save); out_lock: - inode_unlock(lower_dir); + end_creating(lower_new_dentry, NULL); return rc; } =20 @@ -468,9 +476,11 @@ static int ecryptfs_symlink(struct mnt_idmap *idmap, size_t encoded_symlen; struct ecryptfs_mount_crypt_stat *mount_crypt_stat =3D NULL; =20 - rc =3D lock_parent(dentry, &lower_dentry, &lower_dir); - if (rc) - goto out_lock; + lower_dentry =3D ecryptfs_start_creating_dentry(dentry); + if (IS_ERR(lower_dentry)) + return PTR_ERR(lower_dentry); + lower_dir =3D lower_dentry->d_parent->d_inode; + mount_crypt_stat =3D &ecryptfs_superblock_to_private( dir->i_sb)->mount_crypt_stat; rc =3D ecryptfs_encrypt_and_encode_filename(&encoded_symname, @@ -490,7 +500,7 @@ static int ecryptfs_symlink(struct mnt_idmap *idmap, fsstack_copy_attr_times(dir, lower_dir); fsstack_copy_inode_size(dir, lower_dir); out_lock: - inode_unlock(lower_dir); + end_creating(lower_dentry, NULL); if (d_really_is_negative(dentry)) d_drop(dentry); return rc; @@ -501,12 +511,14 @@ static struct dentry *ecryptfs_mkdir(struct mnt_idmap= *idmap, struct inode *dir, { int rc; struct dentry *lower_dentry; + struct dentry *lower_dir_dentry; struct inode *lower_dir; =20 - rc =3D lock_parent(dentry, &lower_dentry, &lower_dir); - if (rc) - goto out; - + lower_dentry =3D ecryptfs_start_creating_dentry(dentry); + if (IS_ERR(lower_dentry)) + return lower_dentry; + lower_dir_dentry =3D dget(lower_dentry->d_parent); + lower_dir =3D lower_dir_dentry->d_inode; lower_dentry =3D vfs_mkdir(&nop_mnt_idmap, lower_dir, lower_dentry, mode); rc =3D PTR_ERR(lower_dentry); @@ -522,7 +534,7 @@ static struct dentry *ecryptfs_mkdir(struct mnt_idmap *= idmap, struct inode *dir, fsstack_copy_inode_size(dir, lower_dir); set_nlink(dir, lower_dir->i_nlink); out: - inode_unlock(lower_dir); + end_creating(lower_dentry, lower_dir_dentry); if (d_really_is_negative(dentry)) d_drop(dentry); return ERR_PTR(rc); @@ -534,21 +546,18 @@ static int ecryptfs_rmdir(struct inode *dir, struct d= entry *dentry) struct inode *lower_dir; int rc; =20 - rc =3D lock_parent(dentry, &lower_dentry, &lower_dir); - dget(lower_dentry); // don't even try to make the lower negative - if (!rc) { - if (d_unhashed(lower_dentry)) - rc =3D -EINVAL; - else - rc =3D vfs_rmdir(&nop_mnt_idmap, lower_dir, lower_dentry); - } + lower_dentry =3D ecryptfs_start_removing_dentry(dentry); + if (IS_ERR(lower_dentry)) + return PTR_ERR(lower_dentry); + lower_dir =3D lower_dentry->d_parent->d_inode; + + rc =3D vfs_rmdir(&nop_mnt_idmap, lower_dir, lower_dentry); if (!rc) { clear_nlink(d_inode(dentry)); fsstack_copy_attr_times(dir, lower_dir); set_nlink(dir, lower_dir->i_nlink); } - dput(lower_dentry); - inode_unlock(lower_dir); + end_removing(lower_dentry); if (!rc) d_drop(dentry); return rc; @@ -562,10 +571,12 @@ ecryptfs_mknod(struct mnt_idmap *idmap, struct inode = *dir, struct dentry *lower_dentry; struct inode *lower_dir; =20 - rc =3D lock_parent(dentry, &lower_dentry, &lower_dir); - if (!rc) - rc =3D vfs_mknod(&nop_mnt_idmap, lower_dir, - lower_dentry, mode, dev); + lower_dentry =3D ecryptfs_start_creating_dentry(dentry); + if (IS_ERR(lower_dentry)) + return PTR_ERR(lower_dentry); + lower_dir =3D lower_dentry->d_parent->d_inode; + + rc =3D vfs_mknod(&nop_mnt_idmap, lower_dir, lower_dentry, mode, dev); if (rc || d_really_is_negative(lower_dentry)) goto out; rc =3D ecryptfs_interpose(lower_dentry, dentry, dir->i_sb); @@ -574,7 +585,7 @@ ecryptfs_mknod(struct mnt_idmap *idmap, struct inode *d= ir, fsstack_copy_attr_times(dir, lower_dir); fsstack_copy_inode_size(dir, lower_dir); out: - inode_unlock(lower_dir); + end_removing(lower_dentry); if (d_really_is_negative(dentry)) d_drop(dentry); return rc; @@ -590,7 +601,6 @@ ecryptfs_rename(struct mnt_idmap *idmap, struct inode *= old_dir, struct dentry *lower_new_dentry; struct dentry *lower_old_dir_dentry; struct dentry *lower_new_dir_dentry; - struct dentry *trap; struct inode *target_inode; struct renamedata rd =3D {}; =20 @@ -605,31 +615,13 @@ ecryptfs_rename(struct mnt_idmap *idmap, struct inode= *old_dir, =20 target_inode =3D d_inode(new_dentry); =20 - trap =3D lock_rename(lower_old_dir_dentry, lower_new_dir_dentry); - if (IS_ERR(trap)) - return PTR_ERR(trap); - dget(lower_new_dentry); - rc =3D -EINVAL; - if (lower_old_dentry->d_parent !=3D lower_old_dir_dentry) - goto out_lock; - if (lower_new_dentry->d_parent !=3D lower_new_dir_dentry) - goto out_lock; - if (d_unhashed(lower_old_dentry) || d_unhashed(lower_new_dentry)) - goto out_lock; - /* source should not be ancestor of target */ - if (trap =3D=3D lower_old_dentry) - goto out_lock; - /* target should not be ancestor of source */ - if (trap =3D=3D lower_new_dentry) { - rc =3D -ENOTEMPTY; - goto out_lock; - } + rd.mnt_idmap =3D &nop_mnt_idmap; + rd.old_parent =3D lower_old_dir_dentry; + rd.new_parent =3D lower_new_dir_dentry; + rc =3D start_renaming_two_dentries(&rd, lower_old_dentry, lower_new_dentr= y); + if (rc) + return rc; =20 - rd.mnt_idmap =3D &nop_mnt_idmap; - rd.old_parent =3D lower_old_dir_dentry; - rd.old_dentry =3D lower_old_dentry; - rd.new_parent =3D lower_new_dir_dentry; - rd.new_dentry =3D lower_new_dentry; rc =3D vfs_rename(&rd); if (rc) goto out_lock; @@ -640,8 +632,7 @@ ecryptfs_rename(struct mnt_idmap *idmap, struct inode *= old_dir, if (new_dir !=3D old_dir) fsstack_copy_attr_all(old_dir, d_inode(lower_old_dir_dentry)); out_lock: - dput(lower_new_dentry); - unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); + end_renaming(&rd); return rc; } =20 diff --git a/fs/namei.c b/fs/namei.c index 4a4b8b96c192..8b7807cd1343 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3397,6 +3397,39 @@ struct dentry *start_removing_noperm(struct dentry *= parent, } EXPORT_SYMBOL(start_removing_noperm); =20 +/** + * start_creating_dentry - prepare to create a given dentry + * @parent: directory from which dentry should be removed + * @child: the dentry to be removed + * + * A lock is taken to protect the dentry again other dirops and + * the validity of the dentry is checked: correct parent and still hashed. + * + * If the dentry is valid and negative a reference is taken and + * returned. If not an error is returned. + * + * end_creating() should be called when creation is complete, or aborted. + * + * Returns: the valid dentry, or an error. + */ +struct dentry *start_creating_dentry(struct dentry *parent, + struct dentry *child) +{ + inode_lock_nested(parent->d_inode, I_MUTEX_PARENT); + if (unlikely(IS_DEADDIR(parent->d_inode) || + child->d_parent !=3D parent || + d_unhashed(child))) { + inode_unlock(parent->d_inode); + return ERR_PTR(-EINVAL); + } + if (d_is_positive(child)) { + inode_unlock(parent->d_inode); + return ERR_PTR(-EEXIST); + } + return dget(child); +} +EXPORT_SYMBOL(start_creating_dentry); + /** * start_removing_dentry - prepare to remove a given dentry * @parent: directory from which dentry should be removed diff --git a/include/linux/namei.h b/include/linux/namei.h index a99ac8b7e24a..208aed1d6728 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -100,6 +100,8 @@ struct dentry *start_removing_killable(struct mnt_idmap= *idmap, 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); +struct dentry *start_creating_dentry(struct dentry *parent, + struct dentry *child); struct dentry *start_removing_dentry(struct dentry *parent, struct dentry *child); =20 --=20 2.50.0.107.gf914562f5916.dirty From nobody Sun Dec 14 05:53:24 2025 Received: from flow-b4-smtp.messagingengine.com (flow-b4-smtp.messagingengine.com [202.12.124.139]) (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 3C36232779D; Wed, 29 Oct 2025 23:47:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=202.12.124.139 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781658; cv=none; b=UBosoLqXmhfV37pVhGOFHhfePVuZzE+MrkAf3LSPENUn/G6BE/VV/8epMV+fDyvoAUiIPyeK0c8nS+Q6Emvb19/ZWLht6d3QQb1YhLGFJ7tCOKEe0jCPqSK/nVgUjwrkPDFDvNU6zCkIovPMRCKo15LQsK2RIhtWSzPa0nqglnc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781658; c=relaxed/simple; bh=AS0DSiH1tBKw6icffSZI4nKW+kkeRwrSu2k/ltwx8UY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=BeaI2y8rtrRN2ikalhKPZgnnRk5Po70Yad0pFqvNiO6DGYzFJ+/mvsQNNxxfCcr6B54TxpUCu0VHvp2IA/pM7m6WqU9reI1ZB2J3KuFTXt4bdMlhzN6mx89CwgofvKv9r7TYtqao7dxRTE2P4cUjkFJbTowlYdPoXxM8WOeQYME= 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=AxoU4WF4; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=2uhXnO59; arc=none smtp.client-ip=202.12.124.139 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="AxoU4WF4"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="2uhXnO59" Received: from phl-compute-04.internal (phl-compute-04.internal [10.202.2.44]) by mailflow.stl.internal (Postfix) with ESMTP id 7E78B130007D; Wed, 29 Oct 2025 19:47:34 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-04.internal (MEProxy); Wed, 29 Oct 2025 19:47:35 -0400 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=1761781654; x=1761788854; bh=38dhyT4HLcXeBlNrI+olGkJcaROmKx3mkAiLG7YrpIU=; b= AxoU4WF4rABIl+52WRsr2kwTdSy3PafJpzxlb0wdUoxWpKjqAfV5lo4gEz/KbKw1 sn+KZodDZhtcfLZKxLdRRJ50uTiiForj/jY+1xlAS8zZxECJNlrutzi+ZzJYPJEA fbilQLUJyk0vz3czz/D/mi+uj/cMFAFRdVPWnVP1qToHxGIz6pten7xvQ0qZ5T2m oNeQyQn7u0VnKH2sKOmhBREkL3ap0ceHCXk3iELqf5jE/GWlN/pCB1kRPhYJdSkI saX/wg4oJAwgo55A0Z1QQkiEdRJ6CqtAkGSwOKFeUcd0h0k4YPA9CbzNNqMXVP4Z nD5BoEMYufyOdDAgX1x/NA== 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=1761781654; x=1761788854; bh=3 8dhyT4HLcXeBlNrI+olGkJcaROmKx3mkAiLG7YrpIU=; b=2uhXnO59PTk9fo3Ge IJ7qJ8OMCo2VaEHJb5gdq6PGCY2MkrAnHWRqlgz9HS480I7m6eEQxQHaxFgvCiBD Nk02uEFgjTUP3T23YnaHqJDOBptaVfwf5agd6LhOhLwpSB37coOuieTQ5DgSSTTE npTkQijnXbZLRAxBGOBsX3hmcPscSoJBIACcdiMdvQnTTeoD1yFTHfLmaYf5tWiF BCavNevtOFxpUWijBSZWWySE1zM2yO2V3ieDvLWqKjflbBEhy3KSikfQ7wivrvgI ASpm79PTQgoCH2U7difNgCXa/yrUSTo6v/Rp8CWzdRgz36htQk8uXqrZ/5ZBXhV2 +4PmQ== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggdduieehtdelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgepfeenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedupdhmohguvgepshhmthhp 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, 29 Oct 2025 19:47:23 -0400 (EDT) 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, apparmor@lists.ubuntu.com, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v4 13/14] VFS: change vfs_mkdir() to unlock on failure. Date: Thu, 30 Oct 2025 10:31:13 +1100 Message-ID: <20251029234353.1321957-14-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251029234353.1321957-1-neilb@ownmail.net> References: <20251029234353.1321957-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 vfs_mkdir() already drops the reference to the dentry on failure but it leaves the parent locked. This complicates end_creating() which needs to unlock the parent even though the dentry is no longer available. If we change vfs_mkdir() to unlock on failure as well as releasing the dentry, we can remove the "parent" arg from end_creating() and simplify the rules for calling it. Note that cachefiles_get_directory() can choose to substitute an error instead of actually calling vfs_mkdir(), for fault injection. In that case it needs to call end_creating(), just as vfs_mkdir() now does on error. Reviewed-by: Amir Goldstein Signed-off-by: NeilBrown -- changes since v2: - extra {} in if() branch in cachefiles_get_directory() to match the new extra {} in the else branch. - filesystems/porting.rst updated. --- Documentation/filesystems/porting.rst | 13 +++++++++++++ fs/btrfs/ioctl.c | 2 +- fs/cachefiles/namei.c | 16 ++++++++------- fs/ecryptfs/inode.c | 8 ++++---- fs/namei.c | 4 ++-- fs/nfsd/nfs3proc.c | 2 +- fs/nfsd/nfs4proc.c | 2 +- fs/nfsd/nfs4recover.c | 2 +- fs/nfsd/nfsproc.c | 2 +- fs/nfsd/vfs.c | 8 ++++---- fs/overlayfs/copy_up.c | 4 ++-- fs/overlayfs/dir.c | 13 ++++++------- fs/overlayfs/super.c | 6 +++--- fs/xfs/scrub/orphanage.c | 2 +- include/linux/namei.h | 28 +++++++++------------------ ipc/mqueue.c | 2 +- 16 files changed, 59 insertions(+), 55 deletions(-) diff --git a/Documentation/filesystems/porting.rst b/Documentation/filesyst= ems/porting.rst index 7233b04668fc..76ff738a00f3 100644 --- a/Documentation/filesystems/porting.rst +++ b/Documentation/filesystems/porting.rst @@ -1309,3 +1309,16 @@ a different length, use vfs_parse_fs_qstr(fc, key, &QSTR_LEN(value, len)) =20 instead. + +--- + +**mandatory** + +vfs_mkdir() now returns a dentry - the one returned by ->mkdir(). If +that dentry is different from the dentry passed in, including if it is +an IS_ERR() dentry pointer, the original dentry is dput(). + +When vfs_mkdir() returns an error, and so both dputs() the original +dentry and doesn't provide a replacement, it also unlocks the parent. +Consequently the return value from vfs_mkdir() can be passed to +end_creating() and the parent will be unlocked precisely when necessary. diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 4fbfdd8faf6a..90ef777eae25 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -935,7 +935,7 @@ static noinline int btrfs_mksubvol(struct dentry *paren= t, out_up_read: up_read(&fs_info->subvol_sem); out_dput: - end_creating(dentry, parent); + end_creating(dentry); return ret; } =20 diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index b97a40917a32..c417ba4bcec3 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -128,10 +128,12 @@ struct dentry *cachefiles_get_directory(struct cachef= iles_cache *cache, if (ret < 0) goto mkdir_error; ret =3D cachefiles_inject_write_error(); - if (ret =3D=3D 0) + if (ret =3D=3D 0) { subdir =3D vfs_mkdir(&nop_mnt_idmap, d_inode(dir), subdir, 0700); - else + } else { + end_creating(subdir); subdir =3D ERR_PTR(ret); + } if (IS_ERR(subdir)) { trace_cachefiles_vfs_error(NULL, d_inode(dir), ret, cachefiles_trace_mkdir_error); @@ -140,7 +142,7 @@ struct dentry *cachefiles_get_directory(struct cachefil= es_cache *cache, trace_cachefiles_mkdir(dir, subdir); =20 if (unlikely(d_unhashed(subdir) || d_is_negative(subdir))) { - end_creating(subdir, dir); + end_creating(subdir); goto retry; } ASSERT(d_backing_inode(subdir)); @@ -154,7 +156,7 @@ struct dentry *cachefiles_get_directory(struct cachefil= es_cache *cache, /* Tell rmdir() it's not allowed to delete the subdir */ inode_lock(d_inode(subdir)); dget(subdir); - end_creating(subdir, dir); + end_creating(subdir); =20 if (!__cachefiles_mark_inode_in_use(NULL, d_inode(subdir))) { pr_notice("cachefiles: Inode already in use: %pd (B=3D%lx)\n", @@ -196,7 +198,7 @@ struct dentry *cachefiles_get_directory(struct cachefil= es_cache *cache, return ERR_PTR(-EBUSY); =20 mkdir_error: - end_creating(subdir, dir); + end_creating(subdir); pr_err("mkdir %s failed with error %d\n", dirname, ret); return ERR_PTR(ret); =20 @@ -705,7 +707,7 @@ bool cachefiles_commit_tmpfile(struct cachefiles_cache = *cache, if (ret < 0) goto out_end; =20 - end_creating(dentry, fan); + end_creating(dentry); =20 ret =3D cachefiles_inject_read_error(); if (ret =3D=3D 0) @@ -739,7 +741,7 @@ bool cachefiles_commit_tmpfile(struct cachefiles_cache = *cache, } =20 out_end: - end_creating(dentry, fan); + end_creating(dentry); out: _leave(" =3D %u", success); return success; diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index b3702105d236..90d74ecc5028 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -211,7 +211,7 @@ ecryptfs_do_create(struct inode *directory_inode, fsstack_copy_attr_times(directory_inode, lower_dir); fsstack_copy_inode_size(directory_inode, lower_dir); out_lock: - end_creating(lower_dentry, NULL); + end_creating(lower_dentry); return inode; } =20 @@ -456,7 +456,7 @@ static int ecryptfs_link(struct dentry *old_dentry, str= uct inode *dir, ecryptfs_inode_to_lower(d_inode(old_dentry))->i_nlink); i_size_write(d_inode(new_dentry), file_size_save); out_lock: - end_creating(lower_new_dentry, NULL); + end_creating(lower_new_dentry); return rc; } =20 @@ -500,7 +500,7 @@ static int ecryptfs_symlink(struct mnt_idmap *idmap, fsstack_copy_attr_times(dir, lower_dir); fsstack_copy_inode_size(dir, lower_dir); out_lock: - end_creating(lower_dentry, NULL); + end_creating(lower_dentry); if (d_really_is_negative(dentry)) d_drop(dentry); return rc; @@ -534,7 +534,7 @@ static struct dentry *ecryptfs_mkdir(struct mnt_idmap *= idmap, struct inode *dir, fsstack_copy_inode_size(dir, lower_dir); set_nlink(dir, lower_dir->i_nlink); out: - end_creating(lower_dentry, lower_dir_dentry); + end_creating(lower_dentry); if (d_really_is_negative(dentry)) d_drop(dentry); return ERR_PTR(rc); diff --git a/fs/namei.c b/fs/namei.c index 8b7807cd1343..d284ebae41bf 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4832,7 +4832,7 @@ EXPORT_SYMBOL(start_creating_path); */ void end_creating_path(const struct path *path, struct dentry *dentry) { - end_creating(dentry, path->dentry); + end_creating(dentry); mnt_drop_write(path->mnt); path_put(path); } @@ -5034,7 +5034,7 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, str= uct inode *dir, return dentry; =20 err: - dput(dentry); + end_creating(dentry); return ERR_PTR(error); } EXPORT_SYMBOL(vfs_mkdir); diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index e2aac0def2cb..6b39e4aff959 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c @@ -364,7 +364,7 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh= *fhp, status =3D nfsd_create_setattr(rqstp, fhp, resfhp, &attrs); =20 out: - end_creating(child, parent); + end_creating(child); out_write: fh_drop_write(fhp); return status; diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index b2c95e8e7c68..524cb07a477c 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -376,7 +376,7 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh= *fhp, if (attrs.na_aclerr) open->op_bmval[0] &=3D ~FATTR4_WORD0_ACL; out: - end_creating(child, parent); + end_creating(child); nfsd_attrs_free(&attrs); out_write: fh_drop_write(fhp); diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 3eefaa2202e3..18c08395b273 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -215,7 +215,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) if (IS_ERR(dentry)) status =3D PTR_ERR(dentry); out_end: - end_creating(dentry, dir); + end_creating(dentry); out: if (status =3D=3D 0) { if (nn->in_grace) diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index ee1b16e921fd..28f03a6a3cc3 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -421,7 +421,7 @@ nfsd_proc_create(struct svc_rqst *rqstp) } =20 out_unlock: - end_creating(dchild, dirfhp->fh_dentry); + end_creating(dchild); out_write: fh_drop_write(dirfhp); done: diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 62109885d4db..6e9a57863904 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1589,7 +1589,7 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc= _fh *fhp, out: if (!err) fh_fill_post_attrs(fhp); - end_creating(dchild, dentry); + end_creating(dchild); return err; =20 out_nfserr: @@ -1646,7 +1646,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fh= p, return err; =20 out_unlock: - end_creating(dchild, dentry); + end_creating(dchild); return err; } =20 @@ -1747,7 +1747,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *f= hp, nfsd_create_setattr(rqstp, fhp, resfhp, attrs); fh_fill_post_attrs(fhp); out_unlock: - end_creating(dnew, dentry); + end_creating(dnew); if (!err) err =3D nfserrno(commit_metadata(fhp)); if (!err) @@ -1824,7 +1824,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, host_err =3D vfs_link(dold, &nop_mnt_idmap, dirp, dnew, NULL); fh_fill_post_attrs(ffhp); out_unlock: - end_creating(dnew, ddir); + end_creating(dnew); if (!host_err) { host_err =3D commit_metadata(ffhp); if (!host_err) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 27014ada11c7..36949856ddea 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -624,7 +624,7 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c) ovl_dentry_set_upper_alias(c->dentry); ovl_dentry_update_reval(c->dentry, upper); } - end_creating(upper, upperdir); + end_creating(upper); } if (err) goto out; @@ -891,7 +891,7 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *= c) err =3D PTR_ERR(upper); if (!IS_ERR(upper)) { err =3D ovl_do_link(ofs, temp, udir, upper); - end_creating(upper, c->destdir); + end_creating(upper); } =20 if (err) diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index c297648c7487..10e732360ace 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -91,7 +91,7 @@ static struct dentry *ovl_whiteout(struct ovl_fs *ofs) err =3D ovl_do_whiteout(ofs, wdir, whiteout); if (!err) ofs->whiteout =3D dget(whiteout); - end_creating(whiteout, workdir); + end_creating(whiteout); if (err) return ERR_PTR(err); } @@ -103,7 +103,7 @@ static struct dentry *ovl_whiteout(struct ovl_fs *ofs) err =3D ovl_do_link(ofs, ofs->whiteout, wdir, link); if (!err) whiteout =3D dget(link); - end_creating(link, workdir); + end_creating(link); if (!err) return whiteout;; =20 @@ -254,7 +254,7 @@ struct dentry *ovl_create_temp(struct ovl_fs *ofs, stru= ct dentry *workdir, ret =3D ovl_create_real(ofs, workdir, ret, attr); if (!IS_ERR(ret)) dget(ret); - end_creating(ret, workdir); + end_creating(ret); return ret; } =20 @@ -362,12 +362,11 @@ static int ovl_create_upper(struct dentry *dentry, st= ruct inode *inode, if (IS_ERR(newdentry)) return PTR_ERR(newdentry); newdentry =3D ovl_create_real(ofs, upperdir, newdentry, attr); - if (IS_ERR(newdentry)) { - end_creating(newdentry, upperdir); + if (IS_ERR(newdentry)) return PTR_ERR(newdentry); - } + dget(newdentry); - end_creating(newdentry, upperdir); + end_creating(newdentry); =20 if (ovl_type_merge(dentry->d_parent) && d_is_dir(newdentry) && !ovl_allow_offline_changes(ofs)) { diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index a721ef2b90e8..3acda985c8a3 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -320,7 +320,7 @@ static struct dentry *ovl_workdir_create(struct ovl_fs = *ofs, =20 if (work->d_inode) { dget(work); - end_creating(work, ofs->workbasedir); + end_creating(work); if (persist) return work; err =3D -EEXIST; @@ -338,7 +338,7 @@ static struct dentry *ovl_workdir_create(struct ovl_fs = *ofs, work =3D ovl_do_mkdir(ofs, dir, work, attr.ia_mode); if (!IS_ERR(work)) dget(work); - end_creating(work, ofs->workbasedir); + end_creating(work); err =3D PTR_ERR(work); if (IS_ERR(work)) goto out_err; @@ -632,7 +632,7 @@ static struct dentry *ovl_lookup_or_create(struct ovl_f= s *ofs, OVL_CATTR(mode)); if (!IS_ERR(child)) dget(child); - end_creating(child, parent); + end_creating(child); } dput(parent); =20 diff --git a/fs/xfs/scrub/orphanage.c b/fs/xfs/scrub/orphanage.c index e732605924a1..b77c2b6b6d44 100644 --- a/fs/xfs/scrub/orphanage.c +++ b/fs/xfs/scrub/orphanage.c @@ -199,7 +199,7 @@ xrep_orphanage_create( sc->orphanage_ilock_flags =3D 0; =20 out_dput_orphanage: - end_creating(orphanage_dentry, root_dentry); + end_creating(orphanage_dentry); out_dput_root: dput(root_dentry); out: diff --git a/include/linux/namei.h b/include/linux/namei.h index 208aed1d6728..0ef73d739a31 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -105,34 +105,24 @@ struct dentry *start_creating_dentry(struct dentry *p= arent, struct dentry *start_removing_dentry(struct dentry *parent, struct dentry *child); =20 -/** - * end_creating - finish action started with start_creating - * @child: dentry returned by start_creating() or vfs_mkdir() - * @parent: dentry given to start_creating(), - * - * Unlock and release the child. +/* end_creating - finish action started with start_creating + * @child: dentry returned by start_creating() or vfs_mkdir() * - * Unlike end_dirop() this can only be called if start_creating() succeede= d. - * It handles @child being and error as vfs_mkdir() might have converted t= he - * dentry to an error - in that case the parent still needs to be unlocked. + * Unlock and release the child. This can be called after + * start_creating() whether that function succeeded or not, + * but it is not needed on failure. * * If vfs_mkdir() was called then the value returned from that function * should be given for @child rather than the original dentry, as vfs_mkdi= r() - * may have provided a new dentry. Even if vfs_mkdir() returns an error - * it must be given to end_creating(). + * may have provided a new dentry. + * * * If vfs_mkdir() was not called, then @child will be a valid dentry and * @parent will be ignored. */ -static inline void end_creating(struct dentry *child, struct dentry *paren= t) +static inline void end_creating(struct dentry *child) { - if (IS_ERR(child)) - /* The parent is still locked despite the error from - * vfs_mkdir() - must unlock it. - */ - inode_unlock(parent->d_inode); - else - end_dirop(child); + end_dirop(child); } =20 /** diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 6d7610310003..83d9466710d6 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -932,7 +932,7 @@ static int do_mq_open(const char __user *u_name, int of= lag, umode_t mode, put_unused_fd(fd); fd =3D error; } - end_creating(path.dentry, root); + end_creating(path.dentry); if (!ro) mnt_drop_write(mnt); out_putname: --=20 2.50.0.107.gf914562f5916.dirty From nobody Sun Dec 14 05:53:24 2025 Received: from flow-b4-smtp.messagingengine.com (flow-b4-smtp.messagingengine.com [202.12.124.139]) (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 859BD328617; Wed, 29 Oct 2025 23:47:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=202.12.124.139 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781671; cv=none; b=iELk2M87GC4vT6B/IPfb8nCsI4CqaKhhqA8WPRZSuP6saH08APjo70RGrWgG/p2U8D6LRNXTTXS8He25+Xqxpi+Ce4jBJBB4HJq96mO+YK/SOeTHkov0sFd1KrFo7tezgqqWvh1Wjfhz3mB6SGeFUsN2TFYIVNhlKQ7kAvI2xyI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761781671; c=relaxed/simple; bh=UnJY3zziw7yUTfl0dL3hkuTETSblbZ0Q1Qk3fxnoh+w=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=UbrJ9r88ZQv0jI2NXbs5weK7SieE+jOHfTIqIW7L22wZF/ikbhbuCRzCAnE3N3SOjnkvCEvcQF+U3kGHbb54lJTGZ/XBwZqMRSnDdMcUpBX/mmIxQyFUFxVfLeZjFYnot6YxTiyPNPrg9AIHjBAX3NsCUZ2jCjxpYz11Jzb/sjw= 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=EB+UPCXm; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=Osdl4h/+; arc=none smtp.client-ip=202.12.124.139 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="EB+UPCXm"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="Osdl4h/+" Received: from phl-compute-04.internal (phl-compute-04.internal [10.202.2.44]) by mailflow.stl.internal (Postfix) with ESMTP id 0FB3B130007D; Wed, 29 Oct 2025 19:47:48 -0400 (EDT) Received: from phl-mailfrontend-01 ([10.202.2.162]) by phl-compute-04.internal (MEProxy); Wed, 29 Oct 2025 19:47:49 -0400 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=1761781667; x=1761788867; bh=fTSvEbE8/RNuX1OHXwzF1EdnZhiVUoe98OJSdEQ8jG4=; b= EB+UPCXmsM/DcyfLpMLGAMkwWvlp0N5GXB22rXFfjo4Va3mjK3Aye4ogW4hhQk8K kK2CJ0BdKCzy2k+pC3e4k9PO9RBtCSNyuygRajLpwjl3WjcmAiUgMFlBlxTy/2Vn Bvz3SRQLQ7EwzaZXSguk7luoN/N+VyLu9RehwYxA4iMYl81yp36jHTvEJhXd9Bmj weH7NJN4MM5tFKvp51ZobuRwPBc4I0YEM1MRP8+3ZyyG720xo2mucWyZpoa9B8Lj uSoI9wWex2o2hiyyNQ6lU42FV4FrV0uD8E5Wt7CT+bk+xxoeseIjQ85dI4IrD6ns fUGD87cWYPr4gTdEqSKAkQ== 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=1761781667; x=1761788867; bh=f TSvEbE8/RNuX1OHXwzF1EdnZhiVUoe98OJSdEQ8jG4=; b=Osdl4h/+0IdXDkEWV rbHAF7FaQmw7JpQKvjpt0k6y6O69RaYY2zmhYNbZyUQpIvnKguLIn2rV2IHBTRuA ofMioaUZfZzE2B/pgRnsHwAfEq6ajSDICQ7Woo+Vw2/XM9Y1GICXyoaL3XAGK8Ep yCE0aqIO49mcIP+/cwE29py1t+vx0LfCuwyWz7tq6UUfk98ke8HEDmC0ekCvLPyZ fnykZK1Q0FHZPWlhl/N3SwFehKc5mm5BXC6VXhMBSIxXhJ9PkkIgg9jZzKVACd9a OsY+MDpYp7ouC77iK54Tun31iI39uanJ7yzBy+R+c4bERqN7JKV3mlns7CjUYq48 LxuSA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggdduieehtdelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgepgeenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedupdhmohguvgepshhmthhp 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, 29 Oct 2025 19:47:37 -0400 (EDT) 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, apparmor@lists.ubuntu.com, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v4 14/14] VFS: introduce end_creating_keep() Date: Thu, 30 Oct 2025 10:31:14 +1100 Message-ID: <20251029234353.1321957-15-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251029234353.1321957-1-neilb@ownmail.net> References: <20251029234353.1321957-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 Occasionally the caller of end_creating() wants to keep using the dentry. Rather then requiring them to dget() the dentry (when not an error) before calling end_creating(), provide end_creating_keep() which does this. cachefiles and overlayfs make use of this. Reviewed-by: Amir Goldstein Signed-off-by: NeilBrown --- fs/cachefiles/namei.c | 3 +-- fs/overlayfs/dir.c | 8 ++------ fs/overlayfs/super.c | 11 +++-------- include/linux/namei.h | 22 ++++++++++++++++++++++ 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index c417ba4bcec3..4c72af174540 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -155,8 +155,7 @@ struct dentry *cachefiles_get_directory(struct cachefil= es_cache *cache, =20 /* Tell rmdir() it's not allowed to delete the subdir */ inode_lock(d_inode(subdir)); - dget(subdir); - end_creating(subdir); + end_creating_keep(subdir); =20 if (!__cachefiles_mark_inode_in_use(NULL, d_inode(subdir))) { pr_notice("cachefiles: Inode already in use: %pd (B=3D%lx)\n", diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 10e732360ace..8faab04dc79f 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -252,10 +252,7 @@ struct dentry *ovl_create_temp(struct ovl_fs *ofs, str= uct dentry *workdir, if (IS_ERR(ret)) return ret; ret =3D ovl_create_real(ofs, workdir, ret, attr); - if (!IS_ERR(ret)) - dget(ret); - end_creating(ret); - return ret; + return end_creating_keep(ret); } =20 static int ovl_set_opaque_xerr(struct dentry *dentry, struct dentry *upper, @@ -365,8 +362,7 @@ static int ovl_create_upper(struct dentry *dentry, stru= ct inode *inode, if (IS_ERR(newdentry)) return PTR_ERR(newdentry); =20 - dget(newdentry); - end_creating(newdentry); + end_creating_keep(newdentry); =20 if (ovl_type_merge(dentry->d_parent) && d_is_dir(newdentry) && !ovl_allow_offline_changes(ofs)) { diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 3acda985c8a3..7b8fc1cab6eb 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -319,8 +319,7 @@ static struct dentry *ovl_workdir_create(struct ovl_fs = *ofs, }; =20 if (work->d_inode) { - dget(work); - end_creating(work); + end_creating_keep(work); if (persist) return work; err =3D -EEXIST; @@ -336,9 +335,7 @@ static struct dentry *ovl_workdir_create(struct ovl_fs = *ofs, } =20 work =3D ovl_do_mkdir(ofs, dir, work, attr.ia_mode); - if (!IS_ERR(work)) - dget(work); - end_creating(work); + end_creating_keep(work); err =3D PTR_ERR(work); if (IS_ERR(work)) goto out_err; @@ -630,9 +627,7 @@ static struct dentry *ovl_lookup_or_create(struct ovl_f= s *ofs, if (!child->d_inode) child =3D ovl_create_real(ofs, parent, child, OVL_CATTR(mode)); - if (!IS_ERR(child)) - dget(child); - end_creating(child); + end_creating_keep(child); } dput(parent); =20 diff --git a/include/linux/namei.h b/include/linux/namei.h index 0ef73d739a31..3d82c6a19197 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -125,6 +125,28 @@ static inline void end_creating(struct dentry *child) end_dirop(child); } =20 +/* end_creating_keep - finish action started with start_creating() and ret= urn result + * @child: dentry returned by start_creating() or vfs_mkdir() + * + * Unlock and return the child. This can be called after + * start_creating() whether that function succeeded or not, + * but it is not needed on failure. + * + * If vfs_mkdir() was called then the value returned from that function + * should be given for @child rather than the original dentry, as vfs_mkdi= r() + * may have provided a new dentry. + * + * Returns: @child, which may be a dentry or an error. + * + */ +static inline struct dentry *end_creating_keep(struct dentry *child) +{ + if (!IS_ERR(child)) + dget(child); + end_dirop(child); + return child; +} + /** * end_removing - finish action started with start_removing * @child: dentry returned by start_removing() --=20 2.50.0.107.gf914562f5916.dirty