From nobody Fri Dec 19 14:36:59 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 6001613D521; Thu, 6 Nov 2025 00:54:10 +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=1762390452; cv=none; b=I6pkTPkt4sKuiynqnuXjkXxXsi6MT4i56TioGd4ArJY8H3DX5qzN2Y0aD3sYQdlf55WZr01j00sedKIz5kfLt6/I5pL2ONXR8kzsrCZmv/z3JDLRv8MNYHX0H3FSb69hdYJ2wUSZq5f22CNrzGThD8dQ8R+GLfxxOqYB0VZPs14= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762390452; c=relaxed/simple; bh=TAN7fEBHF6l6av49IjnjkWwi18U3z35Kj1YFqr+hndU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=mFFcvLfJnkiTPAOXgww7cT0f4+E6arnDedimPUqoKF1Sz7fOdfZMG+zxJVyCft7cXmL/g7qofx7BfQBpfztz2V7bolfABfsgFPLAwVnK4uKL766P16YjivbRFlIUpsUpMoVmGz/XlakNX7jZ2hlf5xZAIxZCB5G5yQni4zTL2II= 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=Jc9akLQC; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=BJlCBmIn; 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="Jc9akLQC"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="BJlCBmIn" Received: from phl-compute-01.internal (phl-compute-01.internal [10.202.2.41]) by mailflow.stl.internal (Postfix) with ESMTP id A4EEF13005FD; Wed, 5 Nov 2025 19:54:08 -0500 (EST) Received: from phl-mailfrontend-01 ([10.202.2.162]) by phl-compute-01.internal (MEProxy); Wed, 05 Nov 2025 19:54:09 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ownmail.net; h= cc:cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:reply-to:subject:subject:to:to; s=fm3; t=1762390448; x=1762397648; bh=ymZTXBnehovWBhFY5IZfw5DvNFPVLbASCb4gqoKTrXQ=; b= Jc9akLQC0Or25ytnK4JRKP7zpntMcu6FPkUSoJgIu0ANimz4dJiSS/pqFfeoz3hv LWzRwWcwUrufaf1p3lqjvEN99ylQa/DZW6bkPu4hkMMw59Sqc25jxzLi6aTH/JLy OrqFpwrjYVYBbz0aaQ2czGANrqrF1xOV7OLMn2J9e5svURccdN1kEOJ5Pj7TcDN/ St8z9ZzAwBeAxlpPrBtXodjYCrNCEze3ldDtoXUebwNCXIiAIFcfSx+ALrpB0bc9 NS5oUpMO+2qVrMXj+s3ucax2m4hFKoqRAInUaUJRFJKU3Ip161P4+AH1nxJHegj+ qYqFKccZuMAuAZxqQzKK6Q== 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=1762390448; x=1762397648; bh=y mZTXBnehovWBhFY5IZfw5DvNFPVLbASCb4gqoKTrXQ=; b=BJlCBmInFk0SeBgfk cVSBeNN/bWjpx+3BaKOxKm7+wx2JNGW4Nkj8Bi4tXOpGz+WCExcUtVizq4vC9/uo Phet5mr6xCifpdQrK22d6LexUJqgt9QTYPmkCE7o1a2KXM29fVOJM5PU052/RZD5 /Z54iHD82JVnpoc93XRX2o4zWe7DG+oaEYJrHXMc35wXJacuU5kVdlZCEa6yiFNb SOdAfAsOe9GLNvC339d2neezSm3u4PW3y3Zk6Sk6H6TFzq0UYB70wZd/C+wu5gtW SPQMQPmPlSFzYTLqPsEkOmJ0/ElNA6iIru+Bi5ztO5ev3jTZ2Rvzgvt3lE5ZDbZ4 oB7ow== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddukeehfeelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedtpdhmohguvgepshhmthhp ohhuthdprhgtphhtthhopehvihhrohesiigvnhhivhdrlhhinhhugidrohhrghdruhhkpd hrtghpthhtohepshgvlhhinhhugiesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdigfhhssehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoh eplhhinhhugidquhhnihhonhhfshesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdhsvggtuhhrihhthidqmhhoughulhgvsehvghgvrhdrkhgvrhhnvg hlrdhorhhgpdhrtghpthhtoheplhhinhhugidqnhhfshesvhhgvghrrdhkvghrnhgvlhdr ohhrghdprhgtphhtthhopehlihhnuhigqdhkvghrnhgvlhesvhhgvghrrdhkvghrnhgvlh drohhrghdprhgtphhtthhopehlihhnuhigqdhfshguvghvvghlsehvghgvrhdrkhgvrhhn vghlrdhorhhgpdhrtghpthhtoheplhhinhhugidqtghifhhssehvghgvrhdrkhgvrhhnvg hlrdhorhhg X-ME-Proxy: Feedback-ID: iab3e480c:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 5 Nov 2025 19:53:57 -0500 (EST) From: NeilBrown To: "Alexander Viro" , "Christian Brauner" , "Amir Goldstein" Cc: "Jan Kara" , linux-fsdevel@vger.kernel.org, Jeff Layton , Chris Mason , David Sterba , David Howells , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Tyler Hicks , Miklos Szeredi , Chuck Lever , Olga Kornievskaia , Dai Ngo , Namjae Jeon , Steve French , Sergey Senozhatsky , Carlos Maiolino , John Johansen , Paul Moore , James Morris , "Serge E. Hallyn" , Stephen Smalley , Ondrej Mosnacek , Mateusz Guzik , Lorenzo Stoakes , Stefan Berger , "Darrick J. Wong" , linux-kernel@vger.kernel.org, netfs@lists.linux.dev, ecryptfs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-unionfs@vger.kernel.org, linux-cifs@vger.kernel.org, linux-xfs@vger.kernel.org, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v5 01/14] debugfs: rename end_creating() to debugfs_end_creating() Date: Thu, 6 Nov 2025 11:50:45 +1100 Message-ID: <20251106005333.956321-2-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251106005333.956321-1-neilb@ownmail.net> References: <20251106005333.956321-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 Acked-by: Greg Kroah-Hartman Signed-off-by: NeilBrown --- 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 Fri Dec 19 14:36:59 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 7D92133E7; Thu, 6 Nov 2025 00:54:24 +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=1762390466; cv=none; b=q6gwEV7lKuQdTxIpJpfB5qT7CO+lGucfpCooPnKrsqEDUxjAoW9S8vvgX1mxe0y5XnUNfFjh0K/F8dcewVaCDDNF3uXUUcYiYjQYHbjUTfPoeYc6RDIaNsa1QmejBdJN1rnzRJh3Mg30MK7B1klSpScF9SibTh7Ub7L9PvfVrxE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762390466; c=relaxed/simple; bh=RmVeYvKIABKYj54i2E6DdVi9uVuFWJDVWmWLlMKnkpg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=gAihD0ClrJ0fu5ijELIN0N1sRBKoaZ1RpaPMZpQOeV2vyTCCe02u2mVy9RdXN7FAqI0DQz5l3QERvU2ObCGYXE1oYM5kkFww1Jj7S+GbrrHURlM3y73/Fe+i0JKkBj28EUP6sEjQkp8asFmhvTYpTKLWNPscatfI156VfbHX424= 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=C0OBokuh; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=tBYGPOta; 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="C0OBokuh"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="tBYGPOta" Received: from phl-compute-04.internal (phl-compute-04.internal [10.202.2.44]) by mailflow.stl.internal (Postfix) with ESMTP id D2B5D13005D8; Wed, 5 Nov 2025 19:54:22 -0500 (EST) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-04.internal (MEProxy); Wed, 05 Nov 2025 19:54:23 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ownmail.net; h= cc:cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:reply-to:subject:subject:to:to; s=fm3; t=1762390462; x=1762397662; bh=m9Ag5U4v4i0p1qdD5EHBf7dKjG+6HaGzyjuRSYZwFvo=; b= C0OBokuhXWT2UkB3yPPJHiYYNz8NLWW6BbQvqCZhYhPktFMedI9zsznwIrUjprfl iwr5pJIM5wq0k+apvTpqbKFnzP4LSsGz+wsCTZhFzi2ngVp8fVT8ZKMfbaJqi9yS QpNURMBqBWos5hW7mixBY47mzoomv8Mm57ZquErlmvHoPJ8csH06bayWXNjdwB+w JHxlnAb9JEKt2+qmrF+ZzYDloWW/N1gjAA5S+GPTp7eli8hqvUGYYqhaGgjHZj3o JjAgIP3/4Pb7RES7rwkvek8ilZqtF2kwY4fw65UGzj3TcDl2pj08QexSJwTl10Le X0V1IoiT4/2mP3hQG2EaLw== 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=1762390462; x=1762397662; bh=m 9Ag5U4v4i0p1qdD5EHBf7dKjG+6HaGzyjuRSYZwFvo=; b=tBYGPOtaquO4GwuTe eURMTe9Mi9yerxaPXiJiwnFdW/9uWwafvWj+Jdrd0R9R/jkD71D35l1CVhEj+uVn uZ2C0q7y4ccb2I362KBoNvHnxPuZGkeXOds0HwKi2DMLkt7k3tGCd/Ud7cuKL5+O HpJbpOPZ6kQDsbnPD5EpKjFwguUYg8EhBukThRBXM+sFGqkZ6evWlUdus8GrcNUY FNPGN15lUF4xsScf1UiEdLZxnqNGuO/HaoE1XMduyuBDu30lLlvWiUyd6vm8zbIJ VkflTDIAE3nfq48IOliNOP971T6udy3tT7Xnyif4kIIlIYopHIgj9oUoOUOq9N42 BbE2A== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddukeehfeelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedtpdhmohguvgepshhmthhp ohhuthdprhgtphhtthhopehvihhrohesiigvnhhivhdrlhhinhhugidrohhrghdruhhkpd hrtghpthhtohepshgvlhhinhhugiesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdigfhhssehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoh eplhhinhhugidquhhnihhonhhfshesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdhsvggtuhhrihhthidqmhhoughulhgvsehvghgvrhdrkhgvrhhnvg hlrdhorhhgpdhrtghpthhtoheplhhinhhugidqnhhfshesvhhgvghrrdhkvghrnhgvlhdr ohhrghdprhgtphhtthhopehlihhnuhigqdhkvghrnhgvlhesvhhgvghrrdhkvghrnhgvlh drohhrghdprhgtphhtthhopehlihhnuhigqdhfshguvghvvghlsehvghgvrhdrkhgvrhhn vghlrdhorhhgpdhrtghpthhtoheplhhinhhugidqtghifhhssehvghgvrhdrkhgvrhhnvg hlrdhorhhg X-ME-Proxy: Feedback-ID: iab3e480c:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 5 Nov 2025 19:54:11 -0500 (EST) From: NeilBrown To: "Alexander Viro" , "Christian Brauner" , "Amir Goldstein" Cc: "Jan Kara" , linux-fsdevel@vger.kernel.org, Jeff Layton , Chris Mason , David Sterba , David Howells , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Tyler Hicks , Miklos Szeredi , Chuck Lever , Olga Kornievskaia , Dai Ngo , Namjae Jeon , Steve French , Sergey Senozhatsky , Carlos Maiolino , John Johansen , Paul Moore , James Morris , "Serge E. Hallyn" , Stephen Smalley , Ondrej Mosnacek , Mateusz Guzik , Lorenzo Stoakes , Stefan Berger , "Darrick J. Wong" , linux-kernel@vger.kernel.org, netfs@lists.linux.dev, ecryptfs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-unionfs@vger.kernel.org, linux-cifs@vger.kernel.org, linux-xfs@vger.kernel.org, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v5 02/14] VFS: introduce start_dirop() and end_dirop() Date: Thu, 6 Nov 2025 11:50:46 +1100 Message-ID: <20251106005333.956321-3-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251106005333.956321-1-neilb@ownmail.net> References: <20251106005333.956321-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 Reviewed-by: Jeff Layton --- 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 1661dcb7d983..2d6657947abd 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -2290,27 +2290,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 39c4d52f5b54..231e1ffd4b8d 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 03e450dd5211..9e7556e79d19 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3196,6 +3196,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 Fri Dec 19 14:36:59 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 52F5F1DBB3A; Thu, 6 Nov 2025 00:54:38 +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=1762390479; cv=none; b=eOzbJJn4OLM6+m/8o+hyI52VLN50cSD4nA5pPB6aNB64yZkSX1mHIarQyDPBUesrTBE2f5REteQXR6Tem0i2G2IlUq2yXVuZX2kuCuW5KZak7yw2vjn3pA6CQMW1xT8Y6rrrGWs9Zw41YsSFOLy+TkpirmLGiXCmh2qzT/QTUSo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762390479; c=relaxed/simple; bh=pwwf2eZdH4paX+NVy07huwl2ixfz7Y09Pw2z2VdPEEo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MMNnZ7EGktcVwJ169zCEJMT2rB/TnC9qo22VV0xoUGErvmtAutZZpSDAl9ff2EazDZrgpWaCWISdLFnLU18Y2HfiFhX//7yf9x5jv6/PGfvn75KgBlodmDBNZmECi1rGtdF9TBi3uy5gQ5eR49q2Y74g1ZDcXdJG1kxiBxtJC5o= 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=ayE02s+m; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=OydEuUfA; 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="ayE02s+m"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="OydEuUfA" Received: from phl-compute-06.internal (phl-compute-06.internal [10.202.2.46]) by mailflow.stl.internal (Postfix) with ESMTP id 8D33C13005D8; Wed, 5 Nov 2025 19:54:36 -0500 (EST) Received: from phl-mailfrontend-01 ([10.202.2.162]) by phl-compute-06.internal (MEProxy); Wed, 05 Nov 2025 19:54:37 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ownmail.net; h= cc:cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:reply-to:subject:subject:to:to; s=fm3; t=1762390476; x=1762397676; bh=eEhtPf3j1g85S+RHaSaC+a31yH7FYFSWk8gUwaN1ev4=; b= ayE02s+msFGbE6i4ybqvPgcbvFKuP5wIxzua7i3y2LKfRqSjHKCjwh+ZH9h21ek8 TGG16dsuQZaKa7bhJK09AR8/VnbQqcfPomyf1OaP1jo/CjHCexaNq1StmTk9FtIg FNJ5tTIB3d2SD4hzuS3lH4d/4KwU6xq1tLmNyx/iwW4+KL18yIru46G/lp6oTW2X gbmXw6bcjUApTnOu/KxYm7o0LnSAM50nD+a63N5koC7GeqcUDgpCD503WXbDXVSO 00qL+jEaTdlcT0cLoMv3iEOKWhO/KkRddlEUnCZqiSTr8P9ulYFzpGfcdlxrF4l/ 8BcMuIwJ1WeffIRRSwnr2Q== 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=1762390476; x=1762397676; bh=e EhtPf3j1g85S+RHaSaC+a31yH7FYFSWk8gUwaN1ev4=; b=OydEuUfA7IoXargGe Um4XRwzaRw/sm0PhnazdaXDhIKIL8ZtljGOX71d6cIR26jrOnvdn93Hq2DTBFXjt pSkRwCvV268HapLonlV6QdKEjvkAaj9/twWjiPZWBNivbGvVHkuPVnwI4ldbLjvb oUj1cMKNvG7Xeji8+kYto66WSPrr97l9JVdksdSOu6Mc6bCZvMJANO3k/qPUTsyo 8Jx+6gp9Ax60iLcM2FHtKYowSteaHjHfaSu1xfCMbGINd9LORV8+MBwMwOHd+wZk mIx7KEwS4QOd/AYji8oLPGd+eBAsu3tEm1nmD4EkD99Jh7nckam5geJjxJnTLg+V fSkTg== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddukeehfeelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedtpdhmohguvgepshhmthhp ohhuthdprhgtphhtthhopehvihhrohesiigvnhhivhdrlhhinhhugidrohhrghdruhhkpd hrtghpthhtohepshgvlhhinhhugiesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdigfhhssehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoh eplhhinhhugidquhhnihhonhhfshesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdhsvggtuhhrihhthidqmhhoughulhgvsehvghgvrhdrkhgvrhhnvg hlrdhorhhgpdhrtghpthhtoheplhhinhhugidqnhhfshesvhhgvghrrdhkvghrnhgvlhdr ohhrghdprhgtphhtthhopehlihhnuhigqdhkvghrnhgvlhesvhhgvghrrdhkvghrnhgvlh drohhrghdprhgtphhtthhopehlihhnuhigqdhfshguvghvvghlsehvghgvrhdrkhgvrhhn vghlrdhorhhgpdhrtghpthhtoheplhhinhhugidqtghifhhssehvghgvrhdrkhgvrhhnvg hlrdhorhhg X-ME-Proxy: Feedback-ID: iab3e480c:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 5 Nov 2025 19:54:25 -0500 (EST) From: NeilBrown To: "Alexander Viro" , "Christian Brauner" , "Amir Goldstein" Cc: "Jan Kara" , linux-fsdevel@vger.kernel.org, Jeff Layton , Chris Mason , David Sterba , David Howells , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Tyler Hicks , Miklos Szeredi , Chuck Lever , Olga Kornievskaia , Dai Ngo , Namjae Jeon , Steve French , Sergey Senozhatsky , Carlos Maiolino , John Johansen , Paul Moore , James Morris , "Serge E. Hallyn" , Stephen Smalley , Ondrej Mosnacek , Mateusz Guzik , Lorenzo Stoakes , Stefan Berger , "Darrick J. Wong" , linux-kernel@vger.kernel.org, netfs@lists.linux.dev, ecryptfs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-unionfs@vger.kernel.org, linux-cifs@vger.kernel.org, linux-xfs@vger.kernel.org, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v5 03/14] VFS: tidy up do_unlinkat() Date: Thu, 6 Nov 2025 11:50:47 +1100 Message-ID: <20251106005333.956321-4-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251106005333.956321-1-neilb@ownmail.net> References: <20251106005333.956321-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 Reviewed-by: Jeff Layton --- fs/namei.c | 55 ++++++++++++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 231e1ffd4b8d..93c5fce2d814 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 Fri Dec 19 14:36:59 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 C24431DF27D; Thu, 6 Nov 2025 00:54:51 +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=1762390494; cv=none; b=F2Lf/XehlWwRT9uKbm8bLj+UoT6yfQXrqfeFKfjCTzQmVTKpaEjKN6+bvVJ6G3OIhjb+8AiczG1xDqPZEuKrK5BAqgJH4V0dIdddYhPnMi68VjH5ycaaT3hhBAqs4KvQ/4buvd+1qdpjsh2WCTa6tBIqtuTI4zvc+iGFFeic50Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762390494; c=relaxed/simple; bh=KhJbkBCUFrSWlOdh98NJdHCE3KxdfdX5TiZHM9vj+4g=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Bq9ZxdxFJ63djuXzCHID2eHBhZ1cjh5dtzRbzzmSSxMACYNtW77fkuJUmn3Yx5RQYOMeNf/qH2c2qWAs+oosLP6fz7FtImJR3rowxJVkxqqiU/q/5duk8Fi9vGRw/oyRDMhENW8M5BrFqdbc7iAHprOMNb+/ZG055pr5xAB7sUw= 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=RAz0dIjP; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=DM4KXfam; 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="RAz0dIjP"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="DM4KXfam" Received: from phl-compute-06.internal (phl-compute-06.internal [10.202.2.46]) by mailflow.stl.internal (Postfix) with ESMTP id 18DCC13005FD; Wed, 5 Nov 2025 19:54:50 -0500 (EST) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-06.internal (MEProxy); Wed, 05 Nov 2025 19:54:51 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ownmail.net; h= cc:cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:reply-to:subject:subject:to:to; s=fm3; t=1762390489; x=1762397689; bh=GdImJRXn7Aw+bUPHb+tkpSoneiX//mTZIZeyEmhb7yI=; b= RAz0dIjP+pvclquubPSu4VpevmkUHQOXlJT9l8pPp0e25A7YXmtkrPBgrTvs/3A+ 0Wuwms9k45keb7bejg6ZdbpIwGJQ116sgOXz3WwUU/7qkcCnm6J/DtaVcUvJhF4K hlKfLAiVrqZd5s2iurq5ISLn110bBXDt+Q0iQlc8prGRV0nmE6vVlFQwfce81Fv8 U+PGrb9uClRjvmqBH671Nvze0RY4pZfyXQoxLNBLKCAjFBh0+Hsol4RA+CMXkhpH gIDgMHtY3cAxDbpzAgotraQGlbIX7ZpS6xiqyeN6xABEVjXELoxh+b+qUWE6JVOb BlURXPMP/O737z3VAIel4A== 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=1762390489; x=1762397689; bh=G dImJRXn7Aw+bUPHb+tkpSoneiX//mTZIZeyEmhb7yI=; b=DM4KXfamtLhmuVkL0 a8zBBUJuDySpfpioeUoF22l3WUwzWhayAQY8MIru4mgyoKUKs8/AT/5o8Zh80m41 Askf/szcObTqfup6N3xyCGgkBnkO2lsPFyLBmRoOf5f8Gme8PoDtSKGc/uP2N+N+ IX2EjfjLcuwqD4IVhDOX3+B+eOB20OnGdJhaBOx0sNKdLtmcTQU91sdjvpyouV1X MoXWR6Ahz0wqYf3sHzregsg/AMmKsHiALFvjgosjXB28YTI7fe/n6mtjeWAEEAnv kXVPkP3nKreDOV7lu8PXspgB7MZRaZ5rxaOQzMH+C/zJP69lUZG3139ojzS4c/hs kq6pQ== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddukeehfeelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgepudenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedtpdhmohguvgepshhmthhp ohhuthdprhgtphhtthhopehvihhrohesiigvnhhivhdrlhhinhhugidrohhrghdruhhkpd hrtghpthhtohepshgvlhhinhhugiesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdigfhhssehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoh eplhhinhhugidquhhnihhonhhfshesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdhsvggtuhhrihhthidqmhhoughulhgvsehvghgvrhdrkhgvrhhnvg hlrdhorhhgpdhrtghpthhtoheplhhinhhugidqnhhfshesvhhgvghrrdhkvghrnhgvlhdr ohhrghdprhgtphhtthhopehlihhnuhigqdhkvghrnhgvlhesvhhgvghrrdhkvghrnhgvlh drohhrghdprhgtphhtthhopehlihhnuhigqdhfshguvghvvghlsehvghgvrhdrkhgvrhhn vghlrdhorhhgpdhrtghpthhtoheplhhinhhugidqtghifhhssehvghgvrhdrkhgvrhhnvg hlrdhorhhg X-ME-Proxy: Feedback-ID: iab3e480c:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 5 Nov 2025 19:54:39 -0500 (EST) From: NeilBrown To: "Alexander Viro" , "Christian Brauner" , "Amir Goldstein" Cc: "Jan Kara" , linux-fsdevel@vger.kernel.org, Jeff Layton , Chris Mason , David Sterba , David Howells , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Tyler Hicks , Miklos Szeredi , Chuck Lever , Olga Kornievskaia , Dai Ngo , Namjae Jeon , Steve French , Sergey Senozhatsky , Carlos Maiolino , John Johansen , Paul Moore , James Morris , "Serge E. Hallyn" , Stephen Smalley , Ondrej Mosnacek , Mateusz Guzik , Lorenzo Stoakes , Stefan Berger , "Darrick J. Wong" , linux-kernel@vger.kernel.org, netfs@lists.linux.dev, ecryptfs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-unionfs@vger.kernel.org, linux-cifs@vger.kernel.org, linux-xfs@vger.kernel.org, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v5 04/14] VFS/nfsd/cachefiles/ovl: add start_creating() and end_creating() Date: Thu, 6 Nov 2025 11:50:48 +1100 Message-ID: <20251106005333.956321-5-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251106005333.956321-1-neilb@ownmail.net> References: <20251106005333.956321-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 --- changes since v4 Change "PTR_ERR(whiteout)" to "err" in ovl_whiteout() - thanks to Dan Carpenter. --- 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 | 100 +++++++++++++++++++++++---------------- fs/overlayfs/overlayfs.h | 8 ++++ fs/overlayfs/super.c | 32 +++++++------ include/linux/namei.h | 33 +++++++++++++ 12 files changed, 215 insertions(+), 160 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 93c5fce2d814..8873ad0f05b0 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 cf4062ac092a..24e501abad0e 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 604a82acd164..e2bdac4317e7 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 83b955a1d55c..b9160fefbd00 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,48 +84,52 @@ 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) { - pr_warn("Failed to link whiteout - disabling whiteout inode sharing(nli= nk=3D%u, err=3D%lu)\n", + 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%u)\n", ofs->whiteout->d_inode->i_nlink, - PTR_ERR(whiteout)); + err); ofs->no_shared_whiteout =3D true; } } @@ -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 b0679c7420a8..37b72f4a64f0 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -89,6 +89,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 Fri Dec 19 14:36:59 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 CB4021E1E16; Thu, 6 Nov 2025 00:55:05 +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=1762390507; cv=none; b=W8P2vOPQsHb90s6InYWyfhVsCAUsCvtAbAGMawMnkSVfX8S83LtdnAix7h0aN2rQYaYjzaOkMd7LeWgeaTjGj4gCJ+uvv1FX8k6/q6+MYJlWHrpt8gRKIGDWuO0UvmeasZYVLicaGRsN9kXkXhlUBQyew4l26yB5sFjfxzJCdHw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762390507; c=relaxed/simple; bh=a87RAZ68i34c+iR5Z4+mySR//py+4cD5CIP8dt11Wfo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cZLOiKq/3ZkOOT3rUaQ38a/IhaYMj+nRMIqFVbM1SDfXk4+EtZdHVbJzkqqV1Yn7cGgPpNl2FrhimXkGHmGbbeuUSh/UdjsxAwNfXpSZoPRLRShqCWqmXR9FGFpcha6MrxwoqZwbQFk6xSRk5NiSGYYfRF7psvZAK0vMUEHIN64= 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=RhhV2bHE; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=ueXuy7ut; 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="RhhV2bHE"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="ueXuy7ut" Received: from phl-compute-03.internal (phl-compute-03.internal [10.202.2.43]) by mailflow.stl.internal (Postfix) with ESMTP id 1969313005FD; Wed, 5 Nov 2025 19:55:04 -0500 (EST) Received: from phl-mailfrontend-01 ([10.202.2.162]) by phl-compute-03.internal (MEProxy); Wed, 05 Nov 2025 19:55:05 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ownmail.net; h= cc:cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:reply-to:subject:subject:to:to; s=fm3; t=1762390503; x=1762397703; bh=LCh70qFK+08Wk6aEucdcc9A27Smxe7IpZ2Pj0jQzYsU=; b= RhhV2bHEIjKPMgrwAT5IdS9xWOB666+MA4zzEvIM8rojz8z1dI1w/Z/8J4mhu6gU 86Vdonij9poQ6kED0wfnqiRXb0BtTta5vRQJA2lZY92M/BhuDBUC4j00x1TkaLq3 E272ChEKAcuEo6VPuo9iI8o/1qA6G2NR8pHpOixNgr6zT4FZUosDUMEyDhHIKY5w j5/q+rdw/ocmTmHl8FE4j0Y1XZGwNo/yka3GW6HHP89JiTczFbGzhCE10F+0X0Lu nQpuAyY5PozIbHRgbnPtDHf7UZ0m88tCsnNB38I1NJrH1Ycd6N5zmsy5GrP7QAKM C1nN8uOA5I/kgMI8bPwRpg== 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=1762390503; x=1762397703; bh=L Ch70qFK+08Wk6aEucdcc9A27Smxe7IpZ2Pj0jQzYsU=; b=ueXuy7ut5Fu7PTCxf k8txgTVWLj2gWlXWhbR1uNUSIcgYRlbRGKmcVUlR1zYj6kLSDlF4y9PODvknQHFR 5UYc4XIiHNiIUUZpZjiY8ZAmPkIsqfRu7iHLVsFUVExbyfPPTYGVm61HLJ6o8ZWV FfJ0kD9JhBumzx4l5mTgy9AUW7Zp8yoihx9QgewK/DHIqm54+vJ8bnQEwarkj7bY Ncw7877IIDlTh1GXXnM1Br7ERdJ/ZyP1XvtO42ZUIC0gKCjuA7ovXatP6ovGuhXx BlYB3RG8Sp7Pg7RMjUoTATMbuQI7p0rgBZpnteiJSorxEF6fBMlTa+VfHoRzncIV nzkpA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddukeehgedtucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedtpdhmohguvgepshhmthhp ohhuthdprhgtphhtthhopehvihhrohesiigvnhhivhdrlhhinhhugidrohhrghdruhhkpd hrtghpthhtohepshgvlhhinhhugiesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdigfhhssehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoh eplhhinhhugidquhhnihhonhhfshesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdhsvggtuhhrihhthidqmhhoughulhgvsehvghgvrhdrkhgvrhhnvg hlrdhorhhgpdhrtghpthhtoheplhhinhhugidqnhhfshesvhhgvghrrdhkvghrnhgvlhdr ohhrghdprhgtphhtthhopehlihhnuhigqdhkvghrnhgvlhesvhhgvghrrdhkvghrnhgvlh drohhrghdprhgtphhtthhopehlihhnuhigqdhfshguvghvvghlsehvghgvrhdrkhgvrhhn vghlrdhorhhgpdhrtghpthhtoheplhhinhhugidqtghifhhssehvghgvrhdrkhgvrhhnvg hlrdhorhhg X-ME-Proxy: Feedback-ID: iab3e480c:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 5 Nov 2025 19:54:53 -0500 (EST) From: NeilBrown To: "Alexander Viro" , "Christian Brauner" , "Amir Goldstein" Cc: "Jan Kara" , linux-fsdevel@vger.kernel.org, Jeff Layton , Chris Mason , David Sterba , David Howells , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Tyler Hicks , Miklos Szeredi , Chuck Lever , Olga Kornievskaia , Dai Ngo , Namjae Jeon , Steve French , Sergey Senozhatsky , Carlos Maiolino , John Johansen , Paul Moore , James Morris , "Serge E. Hallyn" , Stephen Smalley , Ondrej Mosnacek , Mateusz Guzik , Lorenzo Stoakes , Stefan Berger , "Darrick J. Wong" , linux-kernel@vger.kernel.org, netfs@lists.linux.dev, ecryptfs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-unionfs@vger.kernel.org, linux-cifs@vger.kernel.org, linux-xfs@vger.kernel.org, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v5 05/14] VFS/nfsd/cachefiles/ovl: introduce start_removing() and end_removing() Date: Thu, 6 Nov 2025 11:50:49 +1100 Message-ID: <20251106005333.956321-6-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251106005333.956321-1-neilb@ownmail.net> References: <20251106005333.956321-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 Reviewed-by: Jeff Layton --- fs/cachefiles/namei.c | 32 ++++++++++++++------------------ fs/namei.c | 27 +++++++++++++++++++++++++++ fs/nfsd/nfs4recover.c | 18 +++++------------- fs/nfsd/vfs.c | 26 ++++++++++---------------- fs/overlayfs/dir.c | 15 +++++++-------- fs/overlayfs/overlayfs.h | 8 ++++++++ include/linux/namei.h | 18 ++++++++++++++++++ 7 files changed, 89 insertions(+), 55 deletions(-) diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index 0a136eb434da..c7f0c6ab9b88 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -260,6 +260,7 @@ static int cachefiles_unlink(struct cachefiles_cache *c= ache, * - File backed objects are unlinked * - Directory backed objects are stuffed into the graveyard for userspace= to * delete + * On entry dir must be locked. It will be unlocked on exit. */ int cachefiles_bury_object(struct cachefiles_cache *cache, struct cachefiles_object *object, @@ -274,28 +275,30 @@ int cachefiles_bury_object(struct cachefiles_cache *c= ache, =20 _enter(",'%pd','%pd'", dir, rep); =20 + /* end_removing() will dput() @rep but we need to keep + * a ref, so take one now. This also stops the dentry + * being negated when unlinked which we need. + */ + dget(rep); + if (rep->d_parent !=3D dir) { - inode_unlock(d_inode(dir)); + end_removing(rep); _leave(" =3D -ESTALE"); return -ESTALE; } =20 /* non-directories can just be unlinked */ if (!d_is_dir(rep)) { - dget(rep); /* Stop the dentry being negated if it's only pinned - * by a file struct. - */ ret =3D cachefiles_unlink(cache, object, dir, rep, why); - dput(rep); + end_removing(rep); =20 - inode_unlock(d_inode(dir)); _leave(" =3D %d", ret); return ret; } =20 /* directories have to be moved to the graveyard */ _debug("move stale object to graveyard"); - inode_unlock(d_inode(dir)); + end_removing(rep); =20 try_again: /* first step is to make up a grave dentry in the graveyard */ @@ -749,26 +752,20 @@ static struct dentry *cachefiles_lookup_for_cull(stru= ct cachefiles_cache *cache, struct dentry *victim; int ret =3D -ENOENT; =20 - inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); + victim =3D start_removing(&nop_mnt_idmap, dir, &QSTR(filename)); =20 - victim =3D lookup_one(&nop_mnt_idmap, &QSTR(filename), dir); if (IS_ERR(victim)) goto lookup_error; - if (d_is_negative(victim)) - goto lookup_put; if (d_inode(victim)->i_flags & S_KERNEL_FILE) goto lookup_busy; return victim; =20 lookup_busy: ret =3D -EBUSY; -lookup_put: - inode_unlock(d_inode(dir)); - dput(victim); + end_removing(victim); return ERR_PTR(ret); =20 lookup_error: - inode_unlock(d_inode(dir)); ret =3D PTR_ERR(victim); if (ret =3D=3D -ENOENT) return ERR_PTR(-ESTALE); /* Probably got retired by the netfs */ @@ -816,18 +813,17 @@ int cachefiles_cull(struct cachefiles_cache *cache, s= truct dentry *dir, =20 ret =3D cachefiles_bury_object(cache, NULL, dir, victim, FSCACHE_OBJECT_WAS_CULLED); + dput(victim); if (ret < 0) goto error; =20 fscache_count_culled(); - dput(victim); _leave(" =3D 0"); return 0; =20 error_unlock: - inode_unlock(d_inode(dir)); + end_removing(victim); error: - dput(victim); if (ret =3D=3D -ENOENT) return -ESTALE; /* Probably got retired by the netfs */ =20 diff --git a/fs/namei.c b/fs/namei.c index 8873ad0f05b0..38dda29552f6 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3248,6 +3248,33 @@ struct dentry *start_creating(struct mnt_idmap *idma= p, struct dentry *parent, } EXPORT_SYMBOL(start_creating); =20 +/** + * start_removing - prepare to remove a given name with permission checking + * @idmap: idmap of the mount + * @parent: directory in which to find the name + * @name: the name to be removed + * + * Locks are taken and a lookup in performed prior to removing + * an object from a directory. Permission checking (MAY_EXEC) is performed + * against @idmap. + * + * If the name doesn't exist, an error is returned. + * + * end_removing() should be called when removal is complete, or aborted. + * + * Returns: a positive dentry, or an error. + */ +struct dentry *start_removing(struct mnt_idmap *idmap, struct dentry *pare= nt, + struct qstr *name) +{ + int err =3D lookup_one_common(idmap, name, parent); + + if (err) + return ERR_PTR(err); + return start_dirop(parent, name, 0); +} +EXPORT_SYMBOL(start_removing); + #ifdef CONFIG_UNIX98_PTYS int path_pts(struct path *path) { diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index c247a7c3291c..3eefaa2202e3 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -324,20 +324,12 @@ nfsd4_unlink_clid_dir(char *name, struct nfsd_net *nn) dprintk("NFSD: nfsd4_unlink_clid_dir. name %s\n", name); =20 dir =3D nn->rec_file->f_path.dentry; - inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); - dentry =3D lookup_one(&nop_mnt_idmap, &QSTR(name), dir); - if (IS_ERR(dentry)) { - status =3D PTR_ERR(dentry); - goto out_unlock; - } - status =3D -ENOENT; - if (d_really_is_negative(dentry)) - goto out; + dentry =3D start_removing(&nop_mnt_idmap, dir, &QSTR(name)); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + status =3D vfs_rmdir(&nop_mnt_idmap, d_inode(dir), dentry); -out: - dput(dentry); -out_unlock: - inode_unlock(d_inode(dir)); + end_removing(dentry); return status; } =20 diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 24e501abad0e..6291c371caa7 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -2044,7 +2044,7 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fh= p, int type, { struct dentry *dentry, *rdentry; struct inode *dirp; - struct inode *rinode; + struct inode *rinode =3D NULL; __be32 err; int host_err; =20 @@ -2063,24 +2063,21 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *= fhp, int type, =20 dentry =3D fhp->fh_dentry; dirp =3D d_inode(dentry); - inode_lock_nested(dirp, I_MUTEX_PARENT); =20 - rdentry =3D lookup_one(&nop_mnt_idmap, &QSTR_LEN(fname, flen), dentry); + rdentry =3D start_removing(&nop_mnt_idmap, dentry, &QSTR_LEN(fname, flen)= ); + host_err =3D PTR_ERR(rdentry); if (IS_ERR(rdentry)) - goto out_unlock; + goto out_drop_write; =20 - if (d_really_is_negative(rdentry)) { - dput(rdentry); - host_err =3D -ENOENT; - goto out_unlock; - } - rinode =3D d_inode(rdentry); err =3D fh_fill_pre_attrs(fhp); if (err !=3D nfs_ok) goto out_unlock; =20 + rinode =3D d_inode(rdentry); + /* Prevent truncation until after locks dropped */ ihold(rinode); + if (!type) type =3D d_inode(rdentry)->i_mode & S_IFMT; =20 @@ -2102,10 +2099,10 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *= fhp, int type, } fh_fill_post_attrs(fhp); =20 - inode_unlock(dirp); - if (!host_err) +out_unlock: + end_removing(rdentry); + if (!err && !host_err) host_err =3D commit_metadata(fhp); - dput(rdentry); iput(rinode); /* truncate the inode here */ =20 out_drop_write: @@ -2123,9 +2120,6 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fh= p, int type, } out: return err !=3D nfs_ok ? err : nfserrno(host_err); -out_unlock: - inode_unlock(dirp); - goto out_drop_write; } =20 /* diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index b9160fefbd00..20682afdbd20 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -866,17 +866,17 @@ static int ovl_remove_upper(struct dentry *dentry, bo= ol is_dir, goto out; } =20 - inode_lock_nested(dir, I_MUTEX_PARENT); - upper =3D ovl_lookup_upper(ofs, dentry->d_name.name, upperdir, - dentry->d_name.len); + upper =3D ovl_start_removing_upper(ofs, upperdir, + &QSTR_LEN(dentry->d_name.name, + dentry->d_name.len)); err =3D PTR_ERR(upper); if (IS_ERR(upper)) - goto out_unlock; + goto out_dput; =20 err =3D -ESTALE; if ((opaquedir && upper !=3D opaquedir) || (!opaquedir && !ovl_matches_upper(dentry, upper))) - goto out_dput_upper; + goto out_unlock; =20 if (is_dir) err =3D ovl_do_rmdir(ofs, dir, upper); @@ -892,10 +892,9 @@ static int ovl_remove_upper(struct dentry *dentry, boo= l is_dir, */ if (!err) d_drop(dentry); -out_dput_upper: - dput(upper); out_unlock: - inode_unlock(dir); + end_removing(upper); +out_dput: dput(opaquedir); out: return err; diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index beeba96cfcb2..49ad65f829dc 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -423,6 +423,14 @@ static inline struct dentry *ovl_start_creating_upper(= struct ovl_fs *ofs, parent, name); } =20 +static inline struct dentry *ovl_start_removing_upper(struct ovl_fs *ofs, + struct dentry *parent, + struct qstr *name) +{ + return start_removing(ovl_upper_mnt_idmap(ofs), + parent, name); +} + static inline bool ovl_open_flags_need_copy_up(int flags) { if (!flags) diff --git a/include/linux/namei.h b/include/linux/namei.h index 37b72f4a64f0..6d1069f93ebf 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -91,6 +91,8 @@ struct dentry *lookup_one_positive_killable(struct mnt_id= map *idmap, =20 struct dentry *start_creating(struct mnt_idmap *idmap, struct dentry *pare= nt, struct qstr *name); +struct dentry *start_removing(struct mnt_idmap *idmap, struct dentry *pare= nt, + struct qstr *name); =20 /** * end_creating - finish action started with start_creating @@ -122,6 +124,22 @@ static inline void end_creating(struct dentry *child, = struct dentry *parent) end_dirop(child); } =20 +/** + * end_removing - finish action started with start_removing + * @child: dentry returned by start_removing() + * @parent: dentry given to start_removing() + * + * Unlock and release the child. + * + * This is identical to end_dirop(). It can be passed the result of + * start_removing() whether that was successful or not, but it not needed + * if start_removing() failed. + */ +static inline void end_removing(struct dentry *child) +{ + end_dirop(child); +} + extern int follow_down_one(struct path *); extern int follow_down(struct path *path, unsigned int flags); extern int follow_up(struct path *); --=20 2.50.0.107.gf914562f5916.dirty From nobody Fri Dec 19 14:36:59 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 5647C1E1DFC; Thu, 6 Nov 2025 00:55:19 +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=1762390521; cv=none; b=jfO+yuvm8Lj9DBcRTTqlXy+mDqE4QFsKRuLloBerggpag7wzBJysU0xA+IIOU5gy811ALO0aDY2s1RLdvpNMgzr1QhlYKtHMop5pD/F0ET4rmts5bR8XKYbTL5Or7xelnHHmKcRhSqliclxYKMFubMW1Wg/IYpmxcCsR4UL3RKw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762390521; c=relaxed/simple; bh=R8bHaHyEJ594zGFKE/fad7JGvr03zpWNes7mrF7tbOk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=O71kQQ7WgACwOAWg6jMhK7QvJB3kuaiWtO6k7n4C5vmexdnAhM5DwSPvQr8o3L6kM+EkmF0QWCtv/syeUqvKZ7NhctahNWQS1R7I27xcZB9IiPqBvfyrSrDwbc2mLKgiRLhlyYZFSVczSxB0aiFBL8vGpcyTGBPxRdYlHmJQelM= 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=GU5Jw/b9; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=D/bw3jKF; 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="GU5Jw/b9"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="D/bw3jKF" Received: from phl-compute-04.internal (phl-compute-04.internal [10.202.2.44]) by mailflow.stl.internal (Postfix) with ESMTP id C987D1300618; Wed, 5 Nov 2025 19:55:17 -0500 (EST) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-04.internal (MEProxy); Wed, 05 Nov 2025 19:55:18 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ownmail.net; h= cc:cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:reply-to:subject:subject:to:to; s=fm3; t=1762390517; x=1762397717; bh=PJzgcrxhiIBIelv8Z38ByCDof4FfjJXhCw99Sa9A6Ac=; b= GU5Jw/b9rZy6URDk55s+fBj4Sz8G9mnhiFhFd4Q9WcdaqdatI/nsDgdZGc+fCIXN SfRFZNpRNbh0H4qVMjMOwEkKU7ZWwRVmCo+cj0JNxQyVFJhGyONxxcuJvRNbk6j4 X+ytsrhpsx7irNSdAPchdXPN0GKg6VqU9AuT6VJ+eyVD7IBHOmMJMouXDkrpQNR7 VDsBjYdqo7aSfkztJp5tw23EMWOP9f1PkLqTMmtyJ1WOh76aVoilfMu9udiVX0CW i+JL2xBTsCP/p5MbfZSzmGWRRly5gTVjU7okbKAqWN+GsHTYciGmYayUq/RhvqMM yr7wMJy5LufFTB6r0f1Lcw== 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=1762390517; x=1762397717; bh=P JzgcrxhiIBIelv8Z38ByCDof4FfjJXhCw99Sa9A6Ac=; b=D/bw3jKFt5ulyzu4q /oOBxDbm9n6URhx1AuR7zpzAYiY+anKKuBPTRqfc37RpiSc6uj8vWRk5jhoHoPmD J0TrYglOg/YooNVNZaeRC87CkS3BPPnhqL0ggekDiJWHlAQoqyMy9jchjyE553qq 5QDbxUwzUXchq1rh3jrjMo8w47EL/NKPyHC+IjTQnUkiKCHHDg4WCAGM28mZYHiM D2k6h5ZodmmK91d4shDq+OzZnoWok4iriGtioRvCJuwEycg18l1HWxlAXAJyCIBO qXyw1JBYJRXJXUE2BBblll0pl2PEVbtHMo+tTW2uQZrO6+0684TsagA97zutUq0D 8eE+g== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddukeehgedtucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedtpdhmohguvgepshhmthhp ohhuthdprhgtphhtthhopehvihhrohesiigvnhhivhdrlhhinhhugidrohhrghdruhhkpd hrtghpthhtohepshgvlhhinhhugiesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdigfhhssehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoh eplhhinhhugidquhhnihhonhhfshesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdhsvggtuhhrihhthidqmhhoughulhgvsehvghgvrhdrkhgvrhhnvg hlrdhorhhgpdhrtghpthhtoheplhhinhhugidqnhhfshesvhhgvghrrdhkvghrnhgvlhdr ohhrghdprhgtphhtthhopehlihhnuhigqdhkvghrnhgvlhesvhhgvghrrdhkvghrnhgvlh drohhrghdprhgtphhtthhopehlihhnuhigqdhfshguvghvvghlsehvghgvrhdrkhgvrhhn vghlrdhorhhgpdhrtghpthhtoheplhhinhhugidqtghifhhssehvghgvrhdrkhgvrhhnvg hlrdhorhhg X-ME-Proxy: Feedback-ID: iab3e480c:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 5 Nov 2025 19:55:07 -0500 (EST) From: NeilBrown To: "Alexander Viro" , "Christian Brauner" , "Amir Goldstein" Cc: "Jan Kara" , linux-fsdevel@vger.kernel.org, Jeff Layton , Chris Mason , David Sterba , David Howells , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Tyler Hicks , Miklos Szeredi , Chuck Lever , Olga Kornievskaia , Dai Ngo , Namjae Jeon , Steve French , Sergey Senozhatsky , Carlos Maiolino , John Johansen , Paul Moore , James Morris , "Serge E. Hallyn" , Stephen Smalley , Ondrej Mosnacek , Mateusz Guzik , Lorenzo Stoakes , Stefan Berger , "Darrick J. Wong" , linux-kernel@vger.kernel.org, netfs@lists.linux.dev, ecryptfs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-unionfs@vger.kernel.org, linux-cifs@vger.kernel.org, linux-xfs@vger.kernel.org, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v5 06/14] VFS: introduce start_creating_noperm() and start_removing_noperm() Date: Thu, 6 Nov 2025 11:50:50 +1100 Message-ID: <20251106005333.956321-7-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251106005333.956321-1-neilb@ownmail.net> References: <20251106005333.956321-1-neilb@ownmail.net> Reply-To: NeilBrown Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: NeilBrown xfs, fuse, ipc/mqueue need variants of start_creating or start_removing which do not check permissions. This patch adds _noperm versions of these functions. Note that do_mq_open() was only calling mntget() so it could call path_put() - it didn't really need an extra reference on the mnt. Now it doesn't call mntget() and uses end_creating() which does the dput() half of path_put(). Also mq_unlink() previously passed d_inode(dentry->d_parent) as the dir inode to vfs_unlink(). This is after locking d_inode(mnt->mnt_root) These two inodes are the same, but normally calls use the textual parent. So I've changes the vfs_unlink() call to be given d_inode(mnt->mnt_root). Reviewed-by: Amir Goldstein Reviewed-by: Jeff Layton Signed-off-by: NeilBrown -- changes since v2: - dir arg passed to vfs_unlink() in mq_unlink() changed to match the dir passed to lookup_noperm() - restore assignment to path->mnt even though the mntget() is removed. --- fs/fuse/dir.c | 19 +++++++--------- fs/namei.c | 48 ++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/orphanage.c | 11 ++++----- include/linux/namei.h | 2 ++ ipc/mqueue.c | 32 ++++++++++----------------- 5 files changed, 74 insertions(+), 38 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 316922d5dd13..a0d5b302bcc2 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1397,27 +1397,25 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, = u64 parent_nodeid, if (!parent) return -ENOENT; =20 - inode_lock_nested(parent, I_MUTEX_PARENT); if (!S_ISDIR(parent->i_mode)) - goto unlock; + goto put_parent; =20 err =3D -ENOENT; dir =3D d_find_alias(parent); if (!dir) - goto unlock; + goto put_parent; =20 - name->hash =3D full_name_hash(dir, name->name, name->len); - entry =3D d_lookup(dir, name); + entry =3D start_removing_noperm(dir, name); dput(dir); - if (!entry) - goto unlock; + if (IS_ERR(entry)) + goto put_parent; =20 fuse_dir_changed(parent); if (!(flags & FUSE_EXPIRE_ONLY)) d_invalidate(entry); fuse_invalidate_entry_cache(entry); =20 - if (child_nodeid !=3D 0 && d_really_is_positive(entry)) { + if (child_nodeid !=3D 0) { inode_lock(d_inode(entry)); if (get_node_id(d_inode(entry)) !=3D child_nodeid) { err =3D -ENOENT; @@ -1445,10 +1443,9 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u= 64 parent_nodeid, } else { err =3D 0; } - dput(entry); =20 - unlock: - inode_unlock(parent); + end_removing(entry); + put_parent: iput(parent); return err; } diff --git a/fs/namei.c b/fs/namei.c index 38dda29552f6..da01b828ede6 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3275,6 +3275,54 @@ struct dentry *start_removing(struct mnt_idmap *idma= p, struct dentry *parent, } EXPORT_SYMBOL(start_removing); =20 +/** + * start_creating_noperm - prepare to create a given name without permissi= on checking + * @parent: directory in which to prepare to create the name + * @name: the name to be created + * + * Locks are taken and a lookup in performed prior to creating + * an object in a directory. + * + * If the name already exists, a positive dentry is returned. + * + * Returns: a negative or positive dentry, or an error. + */ +struct dentry *start_creating_noperm(struct dentry *parent, + struct qstr *name) +{ + int err =3D lookup_noperm_common(name, parent); + + if (err) + return ERR_PTR(err); + return start_dirop(parent, name, LOOKUP_CREATE); +} +EXPORT_SYMBOL(start_creating_noperm); + +/** + * start_removing_noperm - prepare to remove a given name without permissi= on checking + * @parent: directory in which to find the name + * @name: the name to be removed + * + * Locks are taken and a lookup in performed prior to removing + * an object from a directory. + * + * If the name doesn't exist, an error is returned. + * + * end_removing() should be called when removal is complete, or aborted. + * + * Returns: a positive dentry, or an error. + */ +struct dentry *start_removing_noperm(struct dentry *parent, + struct qstr *name) +{ + int err =3D lookup_noperm_common(name, parent); + + if (err) + return ERR_PTR(err); + return start_dirop(parent, name, 0); +} +EXPORT_SYMBOL(start_removing_noperm); + #ifdef CONFIG_UNIX98_PTYS int path_pts(struct path *path) { diff --git a/fs/xfs/scrub/orphanage.c b/fs/xfs/scrub/orphanage.c index 9c12cb844231..e732605924a1 100644 --- a/fs/xfs/scrub/orphanage.c +++ b/fs/xfs/scrub/orphanage.c @@ -152,11 +152,10 @@ xrep_orphanage_create( } =20 /* Try to find the orphanage directory. */ - inode_lock_nested(root_inode, I_MUTEX_PARENT); - orphanage_dentry =3D lookup_noperm(&QSTR(ORPHANAGE), root_dentry); + orphanage_dentry =3D start_creating_noperm(root_dentry, &QSTR(ORPHANAGE)); if (IS_ERR(orphanage_dentry)) { error =3D PTR_ERR(orphanage_dentry); - goto out_unlock_root; + goto out_dput_root; } =20 /* @@ -170,7 +169,7 @@ xrep_orphanage_create( orphanage_dentry, 0750); error =3D PTR_ERR(orphanage_dentry); if (IS_ERR(orphanage_dentry)) - goto out_unlock_root; + goto out_dput_orphanage; } =20 /* Not a directory? Bail out. */ @@ -200,9 +199,7 @@ xrep_orphanage_create( sc->orphanage_ilock_flags =3D 0; =20 out_dput_orphanage: - dput(orphanage_dentry); -out_unlock_root: - inode_unlock(VFS_I(sc->mp->m_rootip)); + end_creating(orphanage_dentry, root_dentry); out_dput_root: dput(root_dentry); out: diff --git a/include/linux/namei.h b/include/linux/namei.h index 6d1069f93ebf..0441f5921f87 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -93,6 +93,8 @@ struct dentry *start_creating(struct mnt_idmap *idmap, st= ruct dentry *parent, struct qstr *name); struct dentry *start_removing(struct mnt_idmap *idmap, struct dentry *pare= nt, struct qstr *name); +struct dentry *start_creating_noperm(struct dentry *parent, struct qstr *n= ame); +struct dentry *start_removing_noperm(struct dentry *parent, struct qstr *n= ame); =20 /** * end_creating - finish action started with start_creating diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 093551fe66a7..6d7610310003 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -913,13 +913,12 @@ static int do_mq_open(const char __user *u_name, int = oflag, umode_t mode, goto out_putname; =20 ro =3D mnt_want_write(mnt); /* we'll drop it in any case */ - inode_lock(d_inode(root)); - path.dentry =3D lookup_noperm(&QSTR(name->name), root); + path.dentry =3D start_creating_noperm(root, &QSTR(name->name)); if (IS_ERR(path.dentry)) { error =3D PTR_ERR(path.dentry); goto out_putfd; } - path.mnt =3D mntget(mnt); + path.mnt =3D mnt; error =3D prepare_open(path.dentry, oflag, ro, mode, name, attr); if (!error) { struct file *file =3D dentry_open(&path, oflag, current_cred()); @@ -928,13 +927,12 @@ static int do_mq_open(const char __user *u_name, int = oflag, umode_t mode, else error =3D PTR_ERR(file); } - path_put(&path); out_putfd: if (error) { put_unused_fd(fd); fd =3D error; } - inode_unlock(d_inode(root)); + end_creating(path.dentry, root); if (!ro) mnt_drop_write(mnt); out_putname: @@ -957,7 +955,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name) int err; struct filename *name; struct dentry *dentry; - struct inode *inode =3D NULL; + struct inode *inode; struct ipc_namespace *ipc_ns =3D current->nsproxy->ipc_ns; struct vfsmount *mnt =3D ipc_ns->mq_mnt; =20 @@ -969,26 +967,20 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_nam= e) err =3D mnt_want_write(mnt); if (err) goto out_name; - inode_lock_nested(d_inode(mnt->mnt_root), I_MUTEX_PARENT); - dentry =3D lookup_noperm(&QSTR(name->name), mnt->mnt_root); + dentry =3D start_removing_noperm(mnt->mnt_root, &QSTR(name->name)); if (IS_ERR(dentry)) { err =3D PTR_ERR(dentry); - goto out_unlock; + goto out_drop_write; } =20 inode =3D d_inode(dentry); - if (!inode) { - err =3D -ENOENT; - } else { - ihold(inode); - err =3D vfs_unlink(&nop_mnt_idmap, d_inode(dentry->d_parent), - dentry, NULL); - } - dput(dentry); - -out_unlock: - inode_unlock(d_inode(mnt->mnt_root)); + ihold(inode); + err =3D vfs_unlink(&nop_mnt_idmap, d_inode(mnt->mnt_root), + dentry, NULL); + end_removing(dentry); iput(inode); + +out_drop_write: mnt_drop_write(mnt); out_name: putname(name); --=20 2.50.0.107.gf914562f5916.dirty From nobody Fri Dec 19 14:36:59 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 767E01EC01B; Thu, 6 Nov 2025 00:55:33 +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=1762390535; cv=none; b=L+BDjPccCo1OAbu/FVWr7GQP0TF4/XJPHKq9MGK1kZpIlk11n/vfN2dS80hU1aqq+eu+dhBheE2NYqPZmFYGLNUQBHzt5cJkLWrUm5EWKjL8j3U6MaLMe9lTcG5YMB9JW6MvVaPfwqZbS+fkH9wIpcZPAk/y6LGcDIxgN4gB5Us= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762390535; c=relaxed/simple; bh=6oyh/T8znBsPrAtlhgaRAtDGwmLMx11TbSc3rJJeWWU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=miP7nLrJ+4idyyZUcOt3LmUPLVGNTJmiko7l40EsDA27SNkVHBEBzd6gCxJREzb6GLvhdK7Jolktv1nDWmhGx4L0JVtLPGK0nkPhToBYGLVOzWnZ5EvRlmr0J4B/ikmsy7pmcQ+sq555cTmmteVkEE2Hur0c5YkXQw8w6rC899w= 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=J//lmioR; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=M4j3TyN2; 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="J//lmioR"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="M4j3TyN2" Received: from phl-compute-02.internal (phl-compute-02.internal [10.202.2.42]) by mailflow.stl.internal (Postfix) with ESMTP id B773213005FE; Wed, 5 Nov 2025 19:55:31 -0500 (EST) Received: from phl-mailfrontend-01 ([10.202.2.162]) by phl-compute-02.internal (MEProxy); Wed, 05 Nov 2025 19:55:32 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ownmail.net; h= cc:cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:reply-to:subject:subject:to:to; s=fm3; t=1762390531; x=1762397731; bh=wM+KYKPNKK5yORGqRgu9XPCurY4g3fiHErRLGI56I5k=; b= J//lmioR2DMS9b8gaJQok1JZHKrJoEmFeqFLfwMC28lL6zG3WKBJhLpy/dsG2Q+5 W84Cyibqtb8bpZvDjf8POAT+G/I2IfiIOqhfO28qXn1tAEovuoTwEsrrn8LCVLCP fM1mbSiNStscUlw5eoUPpTanX1WiXDR9uL+QHSgeL7vnNF8AtAJtwmtcUZOItKEX ZhMIIgMAhuQtgX2zU6hGeG9e9EVtZRxiAhgZqQWbL/bmES7PE/YY4ylY9DGZkSWp B0h+jR+bSXZaT9otaz+WFEsZJ8FFwZ3gDGq1OwhpfvUROL4/yAXkQlHWsQbFpBVc dCEdUlkpvHdVtm09r9HUXw== 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=1762390531; x=1762397731; bh=w M+KYKPNKK5yORGqRgu9XPCurY4g3fiHErRLGI56I5k=; b=M4j3TyN26gf8Or4xS VljNs3rsklA9+piKSnN77Ox13LfMsr/9JbhdczdBs9w9bya+bgJQ/rEpAGxADmos 6ZHc5gCyNuvgPwI5KHt2YVzrp6C4/7meh+CShJJfsp5zP8KdUPQTSENFcOiBNXpi CcoDp9uNFmmOlEJ2iH2hMvhbMUKunX3UHQWYw9zL2LjQSUisp4o0F6J0ssZsQ1H9 lDRbnlwj+EcaTkaraj+PP+Ogr5pOGkQy4X1qUICikOgmKGgjm8/GMMn8FbnKZZxb XwVXnHCZWKbDNAAYEpqGN+W0p85ICES1ShcU7HG4tTQ+Z/P9hsAvjGOkAYOmdcXV 9aTTg== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddukeehfeelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedtpdhmohguvgepshhmthhp ohhuthdprhgtphhtthhopehvihhrohesiigvnhhivhdrlhhinhhugidrohhrghdruhhkpd hrtghpthhtohepshgvlhhinhhugiesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdigfhhssehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoh eplhhinhhugidquhhnihhonhhfshesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdhsvggtuhhrihhthidqmhhoughulhgvsehvghgvrhdrkhgvrhhnvg hlrdhorhhgpdhrtghpthhtoheplhhinhhugidqnhhfshesvhhgvghrrdhkvghrnhgvlhdr ohhrghdprhgtphhtthhopehlihhnuhigqdhkvghrnhgvlhesvhhgvghrrdhkvghrnhgvlh drohhrghdprhgtphhtthhopehlihhnuhigqdhfshguvghvvghlsehvghgvrhdrkhgvrhhn vghlrdhorhhgpdhrtghpthhtoheplhhinhhugidqtghifhhssehvghgvrhdrkhgvrhhnvg hlrdhorhhg X-ME-Proxy: Feedback-ID: iab3e480c:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 5 Nov 2025 19:55:20 -0500 (EST) From: NeilBrown To: "Alexander Viro" , "Christian Brauner" , "Amir Goldstein" Cc: "Jan Kara" , linux-fsdevel@vger.kernel.org, Jeff Layton , Chris Mason , David Sterba , David Howells , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Tyler Hicks , Miklos Szeredi , Chuck Lever , Olga Kornievskaia , Dai Ngo , Namjae Jeon , Steve French , Sergey Senozhatsky , Carlos Maiolino , John Johansen , Paul Moore , James Morris , "Serge E. Hallyn" , Stephen Smalley , Ondrej Mosnacek , Mateusz Guzik , Lorenzo Stoakes , Stefan Berger , "Darrick J. Wong" , linux-kernel@vger.kernel.org, netfs@lists.linux.dev, ecryptfs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-unionfs@vger.kernel.org, linux-cifs@vger.kernel.org, linux-xfs@vger.kernel.org, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v5 07/14] VFS: introduce start_removing_dentry() Date: Thu, 6 Nov 2025 11:50:51 +1100 Message-ID: <20251106005333.956321-8-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251106005333.956321-1-neilb@ownmail.net> References: <20251106005333.956321-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(). cachefiles_bury_object() now gets an extra ref to the victim, which is drops. As it includes the needed end_removing() calls, the caller doesn't need them. Reviewed-by: Amir Goldstein Signed-off-by: NeilBrown Reviewed-by: Jeff Layton Reviewed-by: Namjae Jeon --- Changes since v4 Callers of cachefiles_bury_object() were incorrectly calling end_removing() after that call. The dput() was needed, the unlock wasn't. The dput() has been effectively moved into cachefiles_bury_object() by removing a dget() which is now not needed. --- fs/cachefiles/interface.c | 11 +++++++---- fs/cachefiles/namei.c | 30 ++++++++++++++---------------- fs/cachefiles/volume.c | 9 ++++++--- 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, 78 insertions(+), 60 deletions(-) diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c index 3e63cfe15874..a08250d244ea 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,13 @@ 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]; + struct dentry *obj; =20 - inode_lock_nested(d_inode(fan), I_MUTEX_PARENT); - cachefiles_bury_object(volume->cache, object, fan, - old_file->f_path.dentry, - FSCACHE_OBJECT_INVALIDATED); + 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); } fput(old_file); } diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index c7f0c6ab9b88..0104ac00485d 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -261,6 +261,7 @@ static int cachefiles_unlink(struct cachefiles_cache *c= ache, * - Directory backed objects are stuffed into the graveyard for userspace= to * delete * On entry dir must be locked. It will be unlocked on exit. + * On entry there must be at least 2 refs on rep, one will be dropped on e= xit. */ int cachefiles_bury_object(struct cachefiles_cache *cache, struct cachefiles_object *object, @@ -275,12 +276,6 @@ int cachefiles_bury_object(struct cachefiles_cache *ca= che, =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) { end_removing(rep); _leave(" =3D -ESTALE"); @@ -425,13 +420,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 +638,13 @@ 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); dput(dentry); if (ret < 0) return false; diff --git a/fs/cachefiles/volume.c b/fs/cachefiles/volume.c index 781aac4ef274..90ba926f488e 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,11 @@ 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); cachefiles_put_directory(volume->dentry); cond_resched(); goto retry; diff --git a/fs/namei.c b/fs/namei.c index da01b828ede6..729b42fb143b 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 20682afdbd20..6d1d0e94e287 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 0441f5921f87..d089e4e8fdd0 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -95,6 +95,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 Fri Dec 19 14:36:59 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 CCB601E5724; Thu, 6 Nov 2025 00:55:46 +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=1762390548; cv=none; b=k+frv62oawycHzQMr4jzCzkXyxpiUk0/13X7+Rw7AeR3Wk/IvAlQs/gDANGE7VusZ6NTZXt5a3B8YsJrCLwY8+LQj3bdlT7Heoct9RrPt3jFMbwEXkOU/rR+7T+Y4v0DBZkPQCTcv2+czBErNOnMsHbCKrPQBQI2rKkMcoOD4jg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762390548; c=relaxed/simple; bh=047jfL6s61LZaEIBYmzfucfb0b84bJ21twF4SyUYhPI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=NtjASyOQ0WEKMn7dOIv2horfJ58bGEXb+F5BYe7ymc+22aRw8fBTxr1+gNY68mpVQm5y+Ev+T+dKzcVFw5r7ocXwIkUQ4u/tkCoVCbig4Pw7Vu6TrFLrlne40hwpZ3jTtFIeafCAuHsfqxJcmBpCTxSZHPc0YxN25KY1MClzMTA= 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=XPJmGYK0; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=pbdtCLf9; 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="XPJmGYK0"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="pbdtCLf9" Received: from phl-compute-04.internal (phl-compute-04.internal [10.202.2.44]) by mailflow.stl.internal (Postfix) with ESMTP id 30D1213005FE; Wed, 5 Nov 2025 19:55:45 -0500 (EST) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-04.internal (MEProxy); Wed, 05 Nov 2025 19:55:46 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ownmail.net; h= cc:cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:reply-to:subject:subject:to:to; s=fm3; t=1762390545; x=1762397745; bh=6GxIRIn2Y+jgQEzm+nvqg4ydKhOE6rwcOntaL14shm0=; b= XPJmGYK0Q55AWbSjy4sPS1BQNuq9p9M0HTsm/6Yciv8joS+TD4uLENU6wDu+5z0N LjHu05jCju67OSnAF0M+GfkfDMzI3jiUpeRC5VxXHGC7YvoPaUc53+N+gojGNm2c Y+qhrDOZ1Ot1ZqmE9hIaKUinm5vD9JB4j7REo81l92u/v3YKS7IenDpVWoPIJZhm In+ftREaytuPokcxTn7oBHu09ZDCW5epNdkObjOgumztAQcMD/YXoZokXuxOjoQt XQ7cuOdAh4lPl/v7+UTrc03MKkqa8QNtBhibGk3qI+FO7WQzAeQi79AzX3tokhln NKpaoC+TAjsWc3x6dxQsZw== 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=1762390545; x=1762397745; bh=6 GxIRIn2Y+jgQEzm+nvqg4ydKhOE6rwcOntaL14shm0=; b=pbdtCLf96oRrEPxz2 08/9STwDJ5RmZlpiDaMOtchAvMif9s8Obm8Y04ypiuYq/yZSbnlzdAKGaGbR1R3w lwgkWvj3axpV0L/nAeW9bWCep8fGZ9pzN/sMemicM9YpcSa55hHxp+XkW31y24ZN CpCt3HcUPh8M37z0r6EK6iCx+WdvOkgvE+lLcZEzJG+alRmB870b48h6YbhMDmUb z7LvF/y91b0ODvLwZTn4ovTxyizM4K68fy8BGQrTsnSBiUJh1QAw2gRo+B9tr13U /YqwaJsWl96xNdLKxmZHnOs6S+SU5MuA0gN/G1fs9/d7jpHZBkaHpgEMAxQTQQFc jNNCA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddukeehgedtucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgepudenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedtpdhmohguvgepshhmthhp ohhuthdprhgtphhtthhopehvihhrohesiigvnhhivhdrlhhinhhugidrohhrghdruhhkpd hrtghpthhtohepshgvlhhinhhugiesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdigfhhssehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoh eplhhinhhugidquhhnihhonhhfshesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdhsvggtuhhrihhthidqmhhoughulhgvsehvghgvrhdrkhgvrhhnvg hlrdhorhhgpdhrtghpthhtoheplhhinhhugidqnhhfshesvhhgvghrrdhkvghrnhgvlhdr ohhrghdprhgtphhtthhopehlihhnuhigqdhkvghrnhgvlhesvhhgvghrrdhkvghrnhgvlh drohhrghdprhgtphhtthhopehlihhnuhigqdhfshguvghvvghlsehvghgvrhdrkhgvrhhn vghlrdhorhhgpdhrtghpthhtoheplhhinhhugidqtghifhhssehvghgvrhdrkhgvrhhnvg hlrdhorhhg X-ME-Proxy: Feedback-ID: iab3e480c:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 5 Nov 2025 19:55:34 -0500 (EST) From: NeilBrown To: "Alexander Viro" , "Christian Brauner" , "Amir Goldstein" Cc: "Jan Kara" , linux-fsdevel@vger.kernel.org, Jeff Layton , Chris Mason , David Sterba , David Howells , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Tyler Hicks , Miklos Szeredi , Chuck Lever , Olga Kornievskaia , Dai Ngo , Namjae Jeon , Steve French , Sergey Senozhatsky , Carlos Maiolino , John Johansen , Paul Moore , James Morris , "Serge E. Hallyn" , Stephen Smalley , Ondrej Mosnacek , Mateusz Guzik , Lorenzo Stoakes , Stefan Berger , "Darrick J. Wong" , linux-kernel@vger.kernel.org, netfs@lists.linux.dev, ecryptfs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-unionfs@vger.kernel.org, linux-cifs@vger.kernel.org, linux-xfs@vger.kernel.org, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v5 08/14] VFS: add start_creating_killable() and start_removing_killable() Date: Thu, 6 Nov 2025 11:50:52 +1100 Message-ID: <20251106005333.956321-9-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251106005333.956321-1-neilb@ownmail.net> References: <20251106005333.956321-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 Reviewed-by: Jeff Layton --- 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 8cb7d5a462ef..d0c3bb0423bb 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 729b42fb143b..e70d056b9543 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 d089e4e8fdd0..196c66156a8a 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -93,6 +93,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 Fri Dec 19 14:36:59 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 844CB1C862D; Thu, 6 Nov 2025 00:56:05 +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=1762390567; cv=none; b=DB5CqgBvhTZ7s1qJmMgaXQd8dCLMyWFgop3wVFxs1HjFHVObJ+9gi++4sF0MdjYoi7mWkq1sVhcMU3Q/0Pil2rEOP7sxi7xS3mmyesvk9DboXqweVYiIGksfYQV7snOr5GezT3xRreKLGCPZQjw1/BScpahWTtSsh4LVTvkWa3E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762390567; c=relaxed/simple; bh=KLe8I8s3oLGbtckFu+23hfRHg8rPWAvFBllhLA0WLbs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=VlmSUA1x5CEddGGjbN2ot2WBMNzc/W/pE0T7PAVI/RHv4JWgjXEPaAdpcd6/QtJCFVPaVn3XQdw8kSUmOwtYDZh5ns2EMPLSFMaZHDE6IGzsrYTplYbazM/8f1wyTOif0NZFMO6WYmOACTYME46n4YtBrx1Nj9b3OABcOmThmOk= 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=Z3uKUAO/; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=DDF3XZ3x; 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="Z3uKUAO/"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="DDF3XZ3x" Received: from phl-compute-04.internal (phl-compute-04.internal [10.202.2.44]) by mailflow.stl.internal (Postfix) with ESMTP id 015171300C4B; Wed, 5 Nov 2025 19:56:03 -0500 (EST) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-04.internal (MEProxy); Wed, 05 Nov 2025 19:56:04 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ownmail.net; h= cc:cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:reply-to:subject:subject:to:to; s=fm3; t=1762390563; x=1762397763; bh=mZN4W8cFkFhngHOKJMryTIcdBxwmXSZJ4ayFDNyGbHU=; b= Z3uKUAO/tekelwM1WYeIg77pslGkIVua3aGl1zAKmc7BifsZXmpYA58b/er4PViq nGYc9iP7NLHGfdR5fU7oMew8fBC9MJxB5ZsUUCVnAnqQ9dRPiYPWZVPd2nH8WFs/ vvfqAA9dKOTsl+MLE8S9foWmHIsn1cf2M0R1+IrJ4C89y+Z/UFrNM0HK7atatF68 huZhf8uYBPLL24oaPhjD3OUnS3OtC54rPyEidv5bNXUGatQC1Gm0fboAJfjXogEc 6+PHcCfXuj48iOinAaUX8Z2BJhxJigW6hu/Z0YRuuA1x0x5mev5mXmelv++6C/m+ Dw+A2+2WBBrnqhPkGNgvsg== 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=1762390563; x=1762397763; bh=m ZN4W8cFkFhngHOKJMryTIcdBxwmXSZJ4ayFDNyGbHU=; b=DDF3XZ3xaucTXicG5 u/gwRsPMX6JsXOMhcTOWZk8qdtMp51LIiG0s5G0NW6SHVwvgQmWJxXdphLn9EoXy DeXKddNEsMIt21t1XY6RSxONTkytV7G4HcSlvYIkv6ntpwcOz/htyI+rkZmY4jdH 0UtyM0NPlrimdNZur/MOLN5aJS/lX1P0PARIfEPnunJpvoFI4o6K7sASORiGeKiz JE5WBr9VJCGNrrIR9DP2Ijl1RrlJ7DIB2rzCbf9gViHJ0y7iz3JTzE+CcelVji0q 9hPhBXRVtsH6mh7OniK/d+ZaWLspb/5KAPovQ6EKWAhZbV2a968yf11LbAyiJtsu 1j56A== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddukeehgedtucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgepvdenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedtpdhmohguvgepshhmthhp ohhuthdprhgtphhtthhopehvihhrohesiigvnhhivhdrlhhinhhugidrohhrghdruhhkpd hrtghpthhtohepshgvlhhinhhugiesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdigfhhssehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoh eplhhinhhugidquhhnihhonhhfshesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdhsvggtuhhrihhthidqmhhoughulhgvsehvghgvrhdrkhgvrhhnvg hlrdhorhhgpdhrtghpthhtoheplhhinhhugidqnhhfshesvhhgvghrrdhkvghrnhgvlhdr ohhrghdprhgtphhtthhopehlihhnuhigqdhkvghrnhgvlhesvhhgvghrrdhkvghrnhgvlh drohhrghdprhgtphhtthhopehlihhnuhigqdhfshguvghvvghlsehvghgvrhdrkhgvrhhn vghlrdhorhhgpdhrtghpthhtoheplhhinhhugidqtghifhhssehvghgvrhdrkhgvrhhnvg hlrdhorhhg X-ME-Proxy: Feedback-ID: iab3e480c:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 5 Nov 2025 19:55:53 -0500 (EST) From: NeilBrown To: "Alexander Viro" , "Christian Brauner" , "Amir Goldstein" Cc: "Jan Kara" , linux-fsdevel@vger.kernel.org, Jeff Layton , Chris Mason , David Sterba , David Howells , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Tyler Hicks , Miklos Szeredi , Chuck Lever , Olga Kornievskaia , Dai Ngo , Namjae Jeon , Steve French , Sergey Senozhatsky , Carlos Maiolino , John Johansen , Paul Moore , James Morris , "Serge E. Hallyn" , Stephen Smalley , Ondrej Mosnacek , Mateusz Guzik , Lorenzo Stoakes , Stefan Berger , "Darrick J. Wong" , linux-kernel@vger.kernel.org, netfs@lists.linux.dev, ecryptfs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-unionfs@vger.kernel.org, linux-cifs@vger.kernel.org, linux-xfs@vger.kernel.org, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v5 09/14] VFS/nfsd/ovl: introduce start_renaming() and end_renaming() Date: Thu, 6 Nov 2025 11:50:53 +1100 Message-ID: <20251106005333.956321-10-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251106005333.956321-1-neilb@ownmail.net> References: <20251106005333.956321-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. Acked-by: Chuck Lever (for nfsd parts) 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. Reviewed-by: Jeff Layton --- 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 e70d056b9543..bad6c9d540f9 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 6291c371caa7..a993f1e54182 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 6d1d0e94e287..cf6fc48459f3 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 196c66156a8a..7fdd9fdcbd2b 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -157,6 +157,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 Fri Dec 19 14:36:59 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 98A801EDA0F; Thu, 6 Nov 2025 00:56:19 +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=1762390581; cv=none; b=iz7pdlR3mOLxBN6mD1Rgq7cj12thzva32oZFb38P8Q24JGuTreQ9Cw2UHXFhB/vO7gcPJ+tQ+ZaExhnUHigdPy09wkP0t3deZROi6H8WvoPdNJKDJ4z6aI91C5P6Y4FJ2LbwsyK+pU4UvxO+9KHRiEBXwoS8Gtg2AXI5Wi51dEY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762390581; c=relaxed/simple; bh=4d6qixx/YggQJVsbVY9C1M0J0nf7en8HJ++pKnViiZw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DgUO+Ev0bCni+k/IPw6zngf0L7ZhJjCFOj6Fg+d/uvvMOMwVLh9XIu6aRV0IzhG5DYY/VzyVpzLWknmTz/a/AWZdAC6S63UETZnr7T6x7MDtZRo/nuFUD1bzWAjIZAe2GxcGePa0LM3a9qwbxmBZ2o+JCwwjHUacTxufOLfF6N4= 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=F5Uvif4/; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=1JJuO3Sy; 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="F5Uvif4/"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="1JJuO3Sy" Received: from phl-compute-01.internal (phl-compute-01.internal [10.202.2.41]) by mailflow.stl.internal (Postfix) with ESMTP id EA6B113005FE; Wed, 5 Nov 2025 19:56:17 -0500 (EST) Received: from phl-mailfrontend-01 ([10.202.2.162]) by phl-compute-01.internal (MEProxy); Wed, 05 Nov 2025 19:56:18 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ownmail.net; h= cc:cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:reply-to:subject:subject:to:to; s=fm3; t=1762390577; x=1762397777; bh=Z7FpfZk1FT3SGgXvJyTVFThRWJ7E2AjUMqLCD/2CC00=; b= F5Uvif4/5xV3Lesqd90U4HAYMMiPwqaBTjlNTo55dJ44gjVzMiF5Efjvfly6UFz+ YGSe4IYp8D2QEiQ9FgRjMITRDSEeyq3wlNzIKvdso36Cffkjdg4d9VlC46gGMvgC RSmtnH3ln1mD7Ht1POu2angBfxSZskiA/O+x5wkk3G026eBxuG2DzEne2f25C9Js D8E/d4/Wuliu605JQbaNxCDC9gt0l25DoQ5rEZtg/pgFuZbSwNRgUV/XpeWPFuQV /NML+WG25eRjwJ2eaPu484GEKJsUzNwEEnLNGHhtVBG5ay/UbcAQqGwQq32GJysX 4qkqzR1KKm+RcyoJXsMSDw== 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=1762390577; x=1762397777; bh=Z 7FpfZk1FT3SGgXvJyTVFThRWJ7E2AjUMqLCD/2CC00=; b=1JJuO3Sy4vxiFx79l GKPyTdFUv5vccCpfZ7FiOUpAB3/ExEYbqIK60Y8zzD5edURFiJK+ioOEsc9y78lx 93JHwLvU2zzn3m5t1JMK8EOWiNe21At25WLWlC2jKms3LTgIvyTr0yCRWhb8kaRh fNVEJSg7Ycta53lAj79z8BndsEd2vmJSebBMTfjSf2pfWXZnvcmfF7XB9fs+9XmB TQRAoT3VaKI2A/oqalKITNq2cR63Nu9bki62VFq4INYnC1ms8UxuFIQViG/VvAdf H394tOlRg66DOpt2MP8/9np/JqVVzKBNbXatcVIOHsmEVemS8mhXLru7JI1r/wAG DzGMQ== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddukeehfeelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgepudenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedtpdhmohguvgepshhmthhp ohhuthdprhgtphhtthhopehvihhrohesiigvnhhivhdrlhhinhhugidrohhrghdruhhkpd hrtghpthhtohepshgvlhhinhhugiesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdigfhhssehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoh eplhhinhhugidquhhnihhonhhfshesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdhsvggtuhhrihhthidqmhhoughulhgvsehvghgvrhdrkhgvrhhnvg hlrdhorhhgpdhrtghpthhtoheplhhinhhugidqnhhfshesvhhgvghrrdhkvghrnhgvlhdr ohhrghdprhgtphhtthhopehlihhnuhigqdhkvghrnhgvlhesvhhgvghrrdhkvghrnhgvlh drohhrghdprhgtphhtthhopehlihhnuhigqdhfshguvghvvghlsehvghgvrhdrkhgvrhhn vghlrdhorhhgpdhrtghpthhtoheplhhinhhugidqtghifhhssehvghgvrhdrkhgvrhhnvg hlrdhorhhg X-ME-Proxy: Feedback-ID: iab3e480c:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 5 Nov 2025 19:56:07 -0500 (EST) From: NeilBrown To: "Alexander Viro" , "Christian Brauner" , "Amir Goldstein" Cc: "Jan Kara" , linux-fsdevel@vger.kernel.org, Jeff Layton , Chris Mason , David Sterba , David Howells , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Tyler Hicks , Miklos Szeredi , Chuck Lever , Olga Kornievskaia , Dai Ngo , Namjae Jeon , Steve French , Sergey Senozhatsky , Carlos Maiolino , John Johansen , Paul Moore , James Morris , "Serge E. Hallyn" , Stephen Smalley , Ondrej Mosnacek , Mateusz Guzik , Lorenzo Stoakes , Stefan Berger , "Darrick J. Wong" , linux-kernel@vger.kernel.org, netfs@lists.linux.dev, ecryptfs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-unionfs@vger.kernel.org, linux-cifs@vger.kernel.org, linux-xfs@vger.kernel.org, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v5 10/14] VFS/ovl/smb: introduce start_renaming_dentry() Date: Thu, 6 Nov 2025 11:50:54 +1100 Message-ID: <20251106005333.956321-11-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251106005333.956321-1-neilb@ownmail.net> References: <20251106005333.956321-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: Namjae Jeon (for ksmbd part) Reviewed-by: Amir Goldstein Signed-off-by: NeilBrown Reviewed-by: Jeff Layton --- 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 bad6c9d540f9..4b740048df97 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 e2bdac4317e7..9911a346b477 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 cf6fc48459f3..6b2f88edb497 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 2da1c035f716..fffc22859211 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 7fdd9fdcbd2b..c47713e9867c 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -159,6 +159,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 Fri Dec 19 14:36:59 2025 Received: from flow-a2-smtp.messagingengine.com (flow-a2-smtp.messagingengine.com [103.168.172.137]) (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 2A747212554; Thu, 6 Nov 2025 00:56:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=103.168.172.137 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762390595; cv=none; b=nr3O522kCRirdMJseozcVUsrWhgZyN6lOsFBQGKKfpYHm9upL6yBeqrUQkxZdbraz/ED+H2CUgXGp3lIoG5HBEZvHS5IxdQAzBitYVaROxIeFIK3p+GHsGmG3GYJoqy9gmsrJQjpLG+dQBOgOAXx5for6yWeFWmUdD30QLtlgyQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762390595; c=relaxed/simple; bh=cnYQMovU24Imj0/Yi5XOPwESZWBl3VGgoXYYabP6F1Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=VY659RFrq9MOU2+BN/ydDKzlMLz7JhVjeIp5EDR3yVJlkAgMzuBqriE4rRPvJ1X0IV1iSmc6ySJ5+ltZij9tgAteJflHCmd+HA6KZx4oPgOpF1CMaIf/qT0poqzVKcl5gds9W4krLJtFuHR+oDMbU1Bg9BBI9njd3tlwCD76uRU= 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=W+pCljlw; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=Wdugi8eV; arc=none smtp.client-ip=103.168.172.137 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="W+pCljlw"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="Wdugi8eV" Received: from phl-compute-01.internal (phl-compute-01.internal [10.202.2.41]) by mailflow.phl.internal (Postfix) with ESMTP id 4293F138067B; Wed, 5 Nov 2025 19:56:32 -0500 (EST) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-01.internal (MEProxy); Wed, 05 Nov 2025 19:56:32 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ownmail.net; h= cc:cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:reply-to:subject:subject:to:to; s=fm3; t=1762390592; x=1762397792; bh=G5X6FDpGtkAmNXYvk/JaLXiGh2JZ3DVZjZ19qFI5vTg=; b= W+pCljlwNPw3HP+H8zVneo44GP1UFCcFwD3uJwTj/oYI/nt5M4EcGBvrSXPtKvUt HXPr6BTRy9jcBjeg0em2Fg7CatiDX4sUms4Z55gzCaPTzdxodLuG2w/wDSHsikRR ewZvLRzvEK4nuuoEYQMJkLrz3ZEjvaBbiyAK4BRD7WkckBIZ6hfNUHjcBRN9xqb5 KHQL5/FIOjbn/S9/FM0KQgUnz9+P2bvZ0gIu8mxsdaTGEmd3lxo8JsyI6bzgud+s cXzUJvUGQvDddmOdimCZv9xKBre0GSSID65tGbYp+IYXy7uzusCGyrOhGNEZTg3b 8BFtwua6owAIqICt63th0g== 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=1762390592; x=1762397792; bh=G 5X6FDpGtkAmNXYvk/JaLXiGh2JZ3DVZjZ19qFI5vTg=; b=Wdugi8eVyMpadDxHy PEdcRBdH0mv+dZbTNboSm0nT0be/FCsKyjhyfn1WA5HP2vaO3H1KE8RfepaoXTAO X1vhhJT+zlj1IErmhQVuRqQ6UFoJqE52xYJZ7XAfkZYSBeCuWeHNbTv1Kl6+hm+m T9rQf6JCNkFVz7NOPTT4EHCRLaPGBxKmx7t19I8IBfs3+gCgJH1r1He9BLpcGPRU uabC8u5BUW5I1ptcjO4Litz5+BNYPcDnvQEpdy41aqSJvwKhtTj4fwvsWfqJL7Xq oObWIfzHNtMfUGzcAOtWwwfhZKJGouCH23OwHou8TfxLVGp1dPO5/OEBvXh0aoX3 LXMPg== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddukeehfeelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgepvdenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedtpdhmohguvgepshhmthhp ohhuthdprhgtphhtthhopehvihhrohesiigvnhhivhdrlhhinhhugidrohhrghdruhhkpd hrtghpthhtohepshgvlhhinhhugiesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdigfhhssehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoh eplhhinhhugidquhhnihhonhhfshesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdhsvggtuhhrihhthidqmhhoughulhgvsehvghgvrhdrkhgvrhhnvg hlrdhorhhgpdhrtghpthhtoheplhhinhhugidqnhhfshesvhhgvghrrdhkvghrnhgvlhdr ohhrghdprhgtphhtthhopehlihhnuhigqdhkvghrnhgvlhesvhhgvghrrdhkvghrnhgvlh drohhrghdprhgtphhtthhopehlihhnuhigqdhfshguvghvvghlsehvghgvrhdrkhgvrhhn vghlrdhorhhgpdhrtghpthhtoheplhhinhhugidqtghifhhssehvghgvrhdrkhgvrhhnvg hlrdhorhhg X-ME-Proxy: Feedback-ID: iab3e480c:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 5 Nov 2025 19:56:21 -0500 (EST) From: NeilBrown To: "Alexander Viro" , "Christian Brauner" , "Amir Goldstein" Cc: "Jan Kara" , linux-fsdevel@vger.kernel.org, Jeff Layton , Chris Mason , David Sterba , David Howells , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Tyler Hicks , Miklos Szeredi , Chuck Lever , Olga Kornievskaia , Dai Ngo , Namjae Jeon , Steve French , Sergey Senozhatsky , Carlos Maiolino , John Johansen , Paul Moore , James Morris , "Serge E. Hallyn" , Stephen Smalley , Ondrej Mosnacek , Mateusz Guzik , Lorenzo Stoakes , Stefan Berger , "Darrick J. Wong" , linux-kernel@vger.kernel.org, netfs@lists.linux.dev, ecryptfs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-unionfs@vger.kernel.org, linux-cifs@vger.kernel.org, linux-xfs@vger.kernel.org, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v5 11/14] Add start_renaming_two_dentries() Date: Thu, 6 Nov 2025 11:50:55 +1100 Message-ID: <20251106005333.956321-12-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251106005333.956321-1-neilb@ownmail.net> References: <20251106005333.956321-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(). In sel_make_policy_nodes() we now lock for rename twice instead of just once so the combined operation is no longer atomic w.r.t the parent directory locks. As selinux_state.policy_mutex is held across the whole operation this does open up any interesting races. Reviewed-by: Amir Goldstein Signed-off-by: NeilBrown Reviewed-by: Jeff Layton --- 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 4b740048df97..7f0384ceb976 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 6b2f88edb497..61e9484e4ab8 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 c47713e9867c..9104c7104191 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -161,6 +161,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 Fri Dec 19 14:36:59 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 A928321CA0D; Thu, 6 Nov 2025 00:56:46 +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=1762390610; cv=none; b=uPsxw9uKjJBSYcQp6XHUN0gaBJ971WwKEOoeAJ9Phajt+cOW6YRyr+HgDAFNMGHUBNpOMwDwlqKsuxNhUGS0esV3EtIxZoeZsJnl1s9pUT4CoB0BS6Wh3wbiNSQ4s+npmq38llMFvrzPnqDxjRKK1qnryfyHAT2imEZ/0zICvVA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762390610; c=relaxed/simple; bh=DWvhklOWx9+UcovcigTS5fzY29Sfu8jqlcYa5xHbdRQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=diUIhVCvJrO0nBsScJ/G+jUmulX94C74CQIJH5Owl2u2wDu0r3eG2gy2s5OVcvxtxKZ7J3Gp5wMR1ejjewYhsMjl7tm7YDr6ypwz4cQYzf+sUq97tx3cT4eERauEi6nuKPOKMvJ+Bb3tSKcwNnhqUEWAknxc2nd8PL633+0NTlg= 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=d91jkyPC; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=VQLQyARF; 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="d91jkyPC"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="VQLQyARF" Received: from phl-compute-12.internal (phl-compute-12.internal [10.202.2.52]) by mailflow.stl.internal (Postfix) with ESMTP id 34AB413005FE; Wed, 5 Nov 2025 19:56:45 -0500 (EST) Received: from phl-mailfrontend-01 ([10.202.2.162]) by phl-compute-12.internal (MEProxy); Wed, 05 Nov 2025 19:56:46 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ownmail.net; h= cc:cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:reply-to:subject:subject:to:to; s=fm3; t=1762390605; x=1762397805; bh=UWJZZpGwHERGNgTVe5CxEVFi5CuCBzj100TTcgR3OGk=; b= d91jkyPCaOuqkHjyBDU5S3rmDuJ0eSd+MohQ7Rth6Dz5aJKwZI+kBFaspKvMEYCK dt5QV7u/7IMGxGBIGbIuSUS47qPPhJcTy4Ikx716Op45kxYlfIDyRTX1tW8E0c4P 8JxkTYZwQkdfBzyBI5l9b6vJBLdm/WkbwVASyklMLyUqOBPgovUPxCMch/jkOJhb Fextbdp8eBYTo6uBRFzvQ3BsKY0ZzsYVBw+XyCaiNSCMk50BV9csaN4pmzSnuyPe V55HdMJp4Bl2YqB80X0Hr7Kg/HZuQAI4QMZHuigsqmLEObANnU2j/gp6rH81g0iw 19+EnJ1HPeXyyDiEoVFczg== 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=1762390605; x=1762397805; bh=U WJZZpGwHERGNgTVe5CxEVFi5CuCBzj100TTcgR3OGk=; b=VQLQyARFtKlkpofCa IjUNYayf0Ut0aTfI98uWqdRo4rryRS/vXBwbzNZncgQ0ApXTm3DcKwbCck/Lbv9m wyT0SRvinznNSr0htnzWZIb4jNrQzPZbBTDBq4TY9c04DTi+KnAfDpCZ5B8kYTny OoOctcLv57hqVuIhHOH/6zfPaW3KwzOzaClrlJ/S2RtfsLNF1qaCe88D3LmxaE7p jrLQGgbUYQOMLqtn2K3/oxt1Mw0TeRw2Bl75NN06nVGq6Fc0kwP274y+unT2brQO llVw+qTzw/qjcic3fQN/fHEGnwCEJZxlt6CYnd8pY1JXBjaD6yZ2sGI8D1Dm1rSW 43B8A== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddukeehgedtucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedtpdhmohguvgepshhmthhp ohhuthdprhgtphhtthhopehvihhrohesiigvnhhivhdrlhhinhhugidrohhrghdruhhkpd hrtghpthhtohepshgvlhhinhhugiesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdigfhhssehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoh eplhhinhhugidquhhnihhonhhfshesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdhsvggtuhhrihhthidqmhhoughulhgvsehvghgvrhdrkhgvrhhnvg hlrdhorhhgpdhrtghpthhtoheplhhinhhugidqnhhfshesvhhgvghrrdhkvghrnhgvlhdr ohhrghdprhgtphhtthhopehlihhnuhigqdhkvghrnhgvlhesvhhgvghrrdhkvghrnhgvlh drohhrghdprhgtphhtthhopehlihhnuhigqdhfshguvghvvghlsehvghgvrhdrkhgvrhhn vghlrdhorhhgpdhrtghpthhtoheplhhinhhugidqtghifhhssehvghgvrhdrkhgvrhhnvg hlrdhorhhg X-ME-Proxy: Feedback-ID: iab3e480c:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 5 Nov 2025 19:56:34 -0500 (EST) From: NeilBrown To: "Alexander Viro" , "Christian Brauner" , "Amir Goldstein" Cc: "Jan Kara" , linux-fsdevel@vger.kernel.org, Jeff Layton , Chris Mason , David Sterba , David Howells , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Tyler Hicks , Miklos Szeredi , Chuck Lever , Olga Kornievskaia , Dai Ngo , Namjae Jeon , Steve French , Sergey Senozhatsky , Carlos Maiolino , John Johansen , Paul Moore , James Morris , "Serge E. Hallyn" , Stephen Smalley , Ondrej Mosnacek , Mateusz Guzik , Lorenzo Stoakes , Stefan Berger , "Darrick J. Wong" , linux-kernel@vger.kernel.org, netfs@lists.linux.dev, ecryptfs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-unionfs@vger.kernel.org, linux-cifs@vger.kernel.org, linux-xfs@vger.kernel.org, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v5 12/14] ecryptfs: use new start_creating/start_removing APIs Date: Thu, 6 Nov 2025 11:50:56 +1100 Message-ID: <20251106005333.956321-13-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251106005333.956321-1-neilb@ownmail.net> References: <20251106005333.956321-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 Reviewed-by: Jeff Layton --- changes since v4 - two places in ecryptfs uses dget_parent(dentry->d_parent), thus incorrectly. getting grandparent. Changed to dget_parent(dentry). --- 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 fc6d37419753..37d6293600c7 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); + 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); + 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 7f0384ceb976..2444c7ddb926 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 9104c7104191..0e6b1b9afc26 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -101,6 +101,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 Fri Dec 19 14:36:59 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 A6A301E9B35; Thu, 6 Nov 2025 00:57:00 +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=1762390622; cv=none; b=gax16vwUj5OfV6XIP27JjnyRX5QPRAZb8MS6ePhKbw0IIT3xS8gjLjkl6aJRlGNE6EdNqgVSy1dgWGRsANq+v8iGR4VWuP6+eOBXUGYmRKEN1l+Dn7CtNcJq1tZ7jwFj4PqRsYXTDkJJTGkfopOrGsQ3oBCx5ErP/XzgcN9g2Ss= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762390622; c=relaxed/simple; bh=A1tgej96Mp14illHvlK3m02G5P06YWQbqmBOac0pDyE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=llNetAMShj7Gmn2lRDSPKTyiOaTwdMBIPXVwEZB9hskZHVTIIpw3TK7nF+xvu2vbKIYOI9HzdgoS2LqI0pa9VwJ4HFEkNHtvXl9k2NZdYaWmBxMOoOuSr8JUIedpHREFjWM9hMq14ZWi9g1s+hb5SKN03rm0A2HXKPd8niwCBdE= 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=GdzJ1nln; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=0ulPfTa6; 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="GdzJ1nln"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="0ulPfTa6" Received: from phl-compute-06.internal (phl-compute-06.internal [10.202.2.46]) by mailflow.stl.internal (Postfix) with ESMTP id 3765F13006BE; Wed, 5 Nov 2025 19:56:59 -0500 (EST) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-06.internal (MEProxy); Wed, 05 Nov 2025 19:57:00 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ownmail.net; h= cc:cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:reply-to:subject:subject:to:to; s=fm3; t=1762390619; x=1762397819; bh=gQNIkXGJ/3duZgz9+qpY2pD9VeeCFkuSeIVTt2ww9Pw=; b= GdzJ1nlnGmO5zYVoiHBiKU9TGsDtyZFNjHjfFa4i/spDfKl3jsoB311rHdu2IhBY 1roCTPI14vYqBW/bwlOwQCNG2dKwj1rxPU4NPyWoLhU/g9B9gQdJwHcAjy2ULcZj N0z8qkxAdyxfj72rcPRkmAMCcmATXY08HcvNGg/z3J7fNJKCv9RHxlfj6ta+vDzB UhhYBfgjjTnS5EOGvuN+WCNX627OZ7W+IsOmSPM8VcztXjy39Vab++lZMH5hJXpK aKT41cVVY2xlV5txE0Z3dM2gA+AqbJMpXzsT8F0Cpu7Bf40JK12FIioZEX69tqJd BgrO437eszTmUOZHTBf5Og== 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=1762390619; x=1762397819; bh=g QNIkXGJ/3duZgz9+qpY2pD9VeeCFkuSeIVTt2ww9Pw=; b=0ulPfTa6ogj0f83e0 8hG3h2QP3hVWYN3M0q/lLTl0K/JjBBUzEViC4NbXNCFDQ2x+UcjoGIaR5TxIrkww ioDT6HSvTNC4aJPg3fXNJ+lv2vdzJ3mS20YRqZdrpZCLJpWuHcd/htL4ircuUr9r dyXqLDMWBdtqDJV9nkGMWi1kB8a1nAM6wqQmZ8zvnO0YfTyjY0kpUji5b0QmYJQs YI71d/cMANyyDWhbw0IcX0HiN2uvN8hzwr4MX/j/nuyFuv8bdIp1G2oGuSyg1jpK H56rspaaYOCFFxMbiBnalR4TuCTMco8f9Ucr1wIAULFbLtvFPbUpmIf4lE3GI6bj FQj1Q== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddukeehgedtucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedtpdhmohguvgepshhmthhp ohhuthdprhgtphhtthhopehvihhrohesiigvnhhivhdrlhhinhhugidrohhrghdruhhkpd hrtghpthhtohepshgvlhhinhhugiesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdigfhhssehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoh eplhhinhhugidquhhnihhonhhfshesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdhsvggtuhhrihhthidqmhhoughulhgvsehvghgvrhdrkhgvrhhnvg hlrdhorhhgpdhrtghpthhtoheplhhinhhugidqnhhfshesvhhgvghrrdhkvghrnhgvlhdr ohhrghdprhgtphhtthhopehlihhnuhigqdhkvghrnhgvlhesvhhgvghrrdhkvghrnhgvlh drohhrghdprhgtphhtthhopehlihhnuhigqdhfshguvghvvghlsehvghgvrhdrkhgvrhhn vghlrdhorhhgpdhrtghpthhtoheplhhinhhugidqtghifhhssehvghgvrhdrkhgvrhhnvg hlrdhorhhg X-ME-Proxy: Feedback-ID: iab3e480c:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 5 Nov 2025 19:56:48 -0500 (EST) From: NeilBrown To: "Alexander Viro" , "Christian Brauner" , "Amir Goldstein" Cc: "Jan Kara" , linux-fsdevel@vger.kernel.org, Jeff Layton , Chris Mason , David Sterba , David Howells , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Tyler Hicks , Miklos Szeredi , Chuck Lever , Olga Kornievskaia , Dai Ngo , Namjae Jeon , Steve French , Sergey Senozhatsky , Carlos Maiolino , John Johansen , Paul Moore , James Morris , "Serge E. Hallyn" , Stephen Smalley , Ondrej Mosnacek , Mateusz Guzik , Lorenzo Stoakes , Stefan Berger , "Darrick J. Wong" , linux-kernel@vger.kernel.org, netfs@lists.linux.dev, ecryptfs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-unionfs@vger.kernel.org, linux-cifs@vger.kernel.org, linux-xfs@vger.kernel.org, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v5 13/14] VFS: change vfs_mkdir() to unlock on failure. Date: Thu, 6 Nov 2025 11:50:57 +1100 Message-ID: <20251106005333.956321-14-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251106005333.956321-1-neilb@ownmail.net> References: <20251106005333.956321-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. Reviewed-by: Jeff Layton --- 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 35f027981b21..d33429294252 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 d0c3bb0423bb..b138120feba3 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 0104ac00485d..59327618ac42 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 @@ -699,7 +701,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) @@ -733,7 +735,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 37d6293600c7..c951e723f24d 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 2444c7ddb926..e834486acff1 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 a993f1e54182..145f1c8d124d 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 9911a346b477..23216ed01325 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 61e9484e4ab8..a4a0dc261310 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 0e6b1b9afc26..b4d95b79b5a8 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -106,34 +106,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 Fri Dec 19 14:36:59 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 A847E1FE46D; Thu, 6 Nov 2025 00:57:14 +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=1762390636; cv=none; b=n87Kzlfpv36SFB5SFlHICkyTTVvugCr/CoPTrK/ny+W76rXGQj70g8N+o9lVZBJxl6SylopAT35NEsWX/fj7jwawuR2sMtfX40T+3UTYrE3f1mH1GGUgBKauq4pDVKlXvQRXnVvEO1Fx2oDIl2s2v/RXtm2P3QJXfTHh65U+tFY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762390636; c=relaxed/simple; bh=X2D3OwIKW4rR3cIHHnntV7DDsOoGo04YBA483px290I=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=SCfvo29P/A0dvsHBV9k3+0VOQ2GSxJs+6QLkoZ65jv6uE7ruk26wWh1gvYJPifmIfTS4bKBAglmIF3rxd/Ja0BVF8rvzk61WupZDTXoLlPPqx4a7RHjNF3zX78sQii/9toSDA9zqRAodSp8rAxZSSFc3Aub81nj6lRDV7PpPxfs= 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=PTST6WA8; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=ZJEcOsnA; 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="PTST6WA8"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="ZJEcOsnA" Received: from phl-compute-01.internal (phl-compute-01.internal [10.202.2.41]) by mailflow.stl.internal (Postfix) with ESMTP id E9AA113005FE; Wed, 5 Nov 2025 19:57:12 -0500 (EST) Received: from phl-mailfrontend-01 ([10.202.2.162]) by phl-compute-01.internal (MEProxy); Wed, 05 Nov 2025 19:57:13 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ownmail.net; h= cc:cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:reply-to:subject:subject:to:to; s=fm3; t=1762390632; x=1762397832; bh=6X4X+jO8scXaaR1fA21EGG74X2XQZWnQw+ie11lSXCw=; b= PTST6WA8hfLl1kp1qyqqgQgCaPl3oThCtjKzi+CutJCPiAO0qOZtCNkFXtOZbYIr 6/z7UAeFWK610e9P12o+aWGmoMgMBUXVfwQB5ewT+6rnXfz80ZHUWC4Ci5gSv7hl wWCTyCVcyzACiu+rfkAx8934wUDpptGJClj6DJhv3FWWECLZDPwAN+ZeJ2OXJzRM JthM/fejEKuFoSlzgzdp98QNLTP6J81Zn3brDKEMg/TiormExtTiIcWLTeK/L34D Fnzb5cclWtt/gVEEIRGSQHnrAftkUB/ecYH7cK1J88qYraX6ykEgcnDJsgZOPOeu LCVucLO90TPP/KNbMQJRPg== 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=1762390632; x=1762397832; bh=6 X4X+jO8scXaaR1fA21EGG74X2XQZWnQw+ie11lSXCw=; b=ZJEcOsnAfFH09d89e xMnEvMts0mGxlHriO4R2hiJGejWj2avaymUdFF/QjuJP+aOtLH601VFRHHSwMVcc 6z5nghlAOmInHpxMhXOIyd4qVNmRqKbOdvEoH6vDYx/tqLXTAImiKPu4jrKb1pTu pYcjRcxOM//owrYHfZ8iPi6eEJ8fzPecQ4FjjyG4v7BunczFu8jY0b88J0ScmbQV qw4r5H7jrAKrn0+5I9DtlG3GimpgUMhSnkmiBIR0Z66UCSfwz/wZpmmle5NyYNRQ XEr1bXgqDSmRNrzHz4BxVFEDE0BqRoa/DOu321Z6ZkGO+9I0CGHYUt3XQbfIVJ/0 P6zAw== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddukeehfeelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgepfeenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohepgedtpdhmohguvgepshhmthhp ohhuthdprhgtphhtthhopehvihhrohesiigvnhhivhdrlhhinhhugidrohhrghdruhhkpd hrtghpthhtohepshgvlhhinhhugiesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdigfhhssehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoh eplhhinhhugidquhhnihhonhhfshesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphht thhopehlihhnuhigqdhsvggtuhhrihhthidqmhhoughulhgvsehvghgvrhdrkhgvrhhnvg hlrdhorhhgpdhrtghpthhtoheplhhinhhugidqnhhfshesvhhgvghrrdhkvghrnhgvlhdr ohhrghdprhgtphhtthhopehlihhnuhigqdhkvghrnhgvlhesvhhgvghrrdhkvghrnhgvlh drohhrghdprhgtphhtthhopehlihhnuhigqdhfshguvghvvghlsehvghgvrhdrkhgvrhhn vghlrdhorhhgpdhrtghpthhtoheplhhinhhugidqtghifhhssehvghgvrhdrkhgvrhhnvg hlrdhorhhg X-ME-Proxy: Feedback-ID: iab3e480c:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 5 Nov 2025 19:57:02 -0500 (EST) From: NeilBrown To: "Alexander Viro" , "Christian Brauner" , "Amir Goldstein" Cc: "Jan Kara" , linux-fsdevel@vger.kernel.org, Jeff Layton , Chris Mason , David Sterba , David Howells , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Tyler Hicks , Miklos Szeredi , Chuck Lever , Olga Kornievskaia , Dai Ngo , Namjae Jeon , Steve French , Sergey Senozhatsky , Carlos Maiolino , John Johansen , Paul Moore , James Morris , "Serge E. Hallyn" , Stephen Smalley , Ondrej Mosnacek , Mateusz Guzik , Lorenzo Stoakes , Stefan Berger , "Darrick J. Wong" , linux-kernel@vger.kernel.org, netfs@lists.linux.dev, ecryptfs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-unionfs@vger.kernel.org, linux-cifs@vger.kernel.org, linux-xfs@vger.kernel.org, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Subject: [PATCH v5 14/14] VFS: introduce end_creating_keep() Date: Thu, 6 Nov 2025 11:50:58 +1100 Message-ID: <20251106005333.956321-15-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20251106005333.956321-1-neilb@ownmail.net> References: <20251106005333.956321-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 Reviewed-by: Jeff Layton --- 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 59327618ac42..ef22ac19545b 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 a4a0dc261310..50717ff8cac7 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 b4d95b79b5a8..58600cf234bc 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -126,6 +126,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