From nobody Sat Jul 4 20:00:03 2026 Received: from ewsoutbound.kpnmail.nl (ewsoutbound.kpnmail.nl [195.121.94.167]) (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 8AEE9372EF5 for ; Sat, 4 Jul 2026 16:41:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.121.94.167 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183281; cv=none; b=kC1FgqwZdDUR9x6sxxoyDUpQJm3QDpUagIss9DSnqrt47FKs2vLN7wsbulsW6IHgYz+XBYoh66f5TD9WOdLmttj+QPnqrV9Aig2jJWCe3UyoOCIlzemI++dcdKsXUU88QPvAzi1TniwfXSGVOOeeHIvXyYKQCKJdEysEljYwe7A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183281; c=relaxed/simple; bh=NU71MByyrmLCm9wNzZEVqBW5nxKkAQQAxQYlce8eSxk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Qt/OPmHhI+zWKYAlygW2sC/fEJUJvkKwwBwJ+2BPrKNFAEnZFH+Z5G33FIA2wA9b2X7Bl4emcs9INJN3XSkPAvOaNjQ+LV6HjS9aPqLnP6NRV7cPAcqfTzZKBZ4NEtfTb7CeATzRh1/F4i25Dj7jzfurRby66b8m0vwT+HhYw6M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl; spf=pass smtp.mailfrom=xs4all.nl; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b=ThqT/Wa2; arc=none smtp.client-ip=195.121.94.167 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b="ThqT/Wa2" X-KPN-MessageId: 27ed4e0f-77c7-11f1-b2a8-005056abbe64 Received: from smtp.kpnmail.nl (unknown [10.31.155.38]) by ewsoutbound.so.kpn.org (Halon) with ESMTPS id 27ed4e0f-77c7-11f1-b2a8-005056abbe64; Sat, 04 Jul 2026 18:41:07 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xs4all.nl; s=xs4all01; h=mime-version:message-id:date:subject:to:from; bh=J1MPw7M4TP0cx2KZqlK96OWfzBhLyzBYB+ov8glzooU=; b=ThqT/Wa2CCHaHaXyeQiqp5/d4Z/7MVCkzVQyHTbnMR0CpQtaxmpPwONL+19hltyXNhztcerxORNZu teyUbJQ1jufuTuuiUQolVhfTRr18gRfwn6S4Gl48MsOpPf1YTv0dQQo5SX3ILw7f3OHm+IB9MI7m+x dZkUMvN9AGZW1wGQYWsnIL6LLA6lO8k38bco5lVcBHZr5wlbpo5b93M47qP8kuJlPdu8c2ifuzKNzq XY2SwgNCoPemAM7VEPk6bPOivMpY67OuDdwzHqTqxB9mAaNZX0jmRdA1QlSg/5DxD3kzs51AyVWCHZ bu6Y9zLphl4AVvNsJwdZgH1BUt3RBUg== X-KPN-MID: 33|EBT+rJpq5AxSEInYbC+DW0bYJlanJia9tVMyotH848I+QhL5xc3og8ilzF5B6d7 OcJpwbaRfb5mlqtAKo7ZJ0/pNVF3sAKo9xtjtzRgNCNA= X-KPN-VerifiedSender: Yes X-CMASSUN: 33|sQB56BN8R1fCVMaSguLylh16WQ0xxnerhBtm/cXsNaKmuck2f/HNqAx3Q/yVyzZ ldIW/kk6sQJHl4UCi2EG1sw== Received: from daedalus.home (unknown [178.230.195.42]) by smtp.xs4all.nl (Halon) with ESMTPSA id 27acbfdd-77c7-11f1-a998-005056abf0db; Sat, 04 Jul 2026 18:41:07 +0200 (CEST) From: Jori Koolstra To: Jeff Layton , Christian Brauner , Al Viro , Aleksa Sarai , NeilBrown , Amir Goldstein , Jan Kara Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Jori Koolstra Subject: [PATCH v3 01/14] fs/namei.c: use trailing_slashes() Date: Sat, 4 Jul 2026 18:41:35 +0200 Message-ID: <20260704164149.3480051-2-jkoolstra@xs4all.nl> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260704164149.3480051-1-jkoolstra@xs4all.nl> References: <20260704164149.3480051-1-jkoolstra@xs4all.nl> 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" There are several places in fs/namei.c that can use the trailing_slashes() function to improve intent. Signed-off-by: Jori Koolstra --- fs/namei.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 19ce43c9a6e6..a8890bc0ae21 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2781,9 +2781,14 @@ static const char *path_init(struct nameidata *nd, u= nsigned flags) return s; } =20 +static inline bool trailing_slashes(const struct qstr last) +{ + return (bool)last.name[last.len]; +} + static inline const char *lookup_last(struct nameidata *nd) { - if (nd->last_type =3D=3D LAST_NORM && nd->last.name[nd->last.len]) + if (nd->last_type =3D=3D LAST_NORM && trailing_slashes(nd->last)) nd->flags |=3D LOOKUP_FOLLOW | LOOKUP_DIRECTORY; =20 return walk_component(nd, WALK_TRAILING); @@ -4521,17 +4526,12 @@ static struct dentry *lookup_open(struct nameidata = *nd, struct file *file, return ERR_PTR(error); } =20 -static inline bool trailing_slashes(struct nameidata *nd) -{ - return (bool)nd->last.name[nd->last.len]; -} - static struct dentry *lookup_fast_for_open(struct nameidata *nd, int open_= flag) { struct dentry *dentry; =20 if (open_flag & O_CREAT) { - if (trailing_slashes(nd)) + if (trailing_slashes(nd->last)) return ERR_PTR(-EISDIR); =20 /* Don't bother on an O_EXCL create */ @@ -4539,7 +4539,7 @@ static struct dentry *lookup_fast_for_open(struct nam= eidata *nd, int open_flag) return NULL; } =20 - if (trailing_slashes(nd)) + if (trailing_slashes(nd->last)) nd->flags |=3D LOOKUP_FOLLOW | LOOKUP_DIRECTORY; =20 dentry =3D lookup_fast(nd); @@ -4950,7 +4950,7 @@ static struct dentry *filename_create(int dfd, struct= filename *name, * Do the final lookup. Suppress 'create' if there is a trailing * '/', and a directory wasn't requested. */ - if (last.name[last.len] && !want_dir) + if (trailing_slashes(last) && !want_dir) create_flags &=3D ~LOOKUP_CREATE; dentry =3D start_dirop(path->dentry, &last, reval_flag | create_flags); if (IS_ERR(dentry)) @@ -5569,7 +5569,7 @@ int filename_unlinkat(int dfd, struct filename *name) goto exit_drop_write; =20 /* Why not before? Because we want correct error value */ - if (unlikely(last.name[last.len])) { + if (unlikely(trailing_slashes(last))) { if (d_is_dir(dentry)) error =3D -EISDIR; else @@ -6171,16 +6171,16 @@ int filename_renameat2(int olddfd, struct filename = *from, if (flags & RENAME_EXCHANGE) { if (!d_is_dir(rd.new_dentry)) { error =3D -ENOTDIR; - if (new_last.name[new_last.len]) + if (trailing_slashes(new_last)) goto exit_unlock; } } /* unless the source is a directory trailing slashes give -ENOTDIR */ if (!d_is_dir(rd.old_dentry)) { error =3D -ENOTDIR; - if (old_last.name[old_last.len]) + if (trailing_slashes(old_last)) goto exit_unlock; - if (!(flags & RENAME_EXCHANGE) && new_last.name[new_last.len]) + if (!(flags & RENAME_EXCHANGE) && trailing_slashes(new_last)) goto exit_unlock; } =20 --=20 2.55.0 From nobody Sat Jul 4 20:00:03 2026 Received: from ewsoutbound.kpnmail.nl (ewsoutbound.kpnmail.nl [195.121.94.170]) (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 9598A25B091 for ; Sat, 4 Jul 2026 16:41:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.121.94.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183283; cv=none; b=mN3D4FipPDdSrVmo32GAXdiSLc6WOSrzyayEvSxa9HyNR//c64CTFJCBJR8vICjfX4ZbFnAjWrKLjLYkEk15Obdl2aYFL1eRuq+aExqKPkdqbslSIklT5C1tvn/+kO75k/+3ldP9NUUpcbWbZYkzBoO68xBNtP6NR2GcaMXhNOg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183283; c=relaxed/simple; bh=qOspL9DR/Ap3JjrdqfEG20fX9OHFrfSy/VqzZGrRr/c=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=dvc4jypn2rgCrp1aS07xem/AXM+r89JJBchJfgZrUlPrD6e0/VGZwnfXWDVsvyFMc9FsodZKp27CB185dklChKGaRuZMsN+ECzqf7fDD9otajvsJdyx99yNE7THPVtQ0v5gtUwlAU5OhQpagK/BdzTDSzTB2+jq10NNJSYC8BU8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl; spf=pass smtp.mailfrom=xs4all.nl; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b=kgR7hAUL; arc=none smtp.client-ip=195.121.94.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b="kgR7hAUL" X-KPN-MessageId: 2a793dab-77c7-11f1-83b1-005056ab378f Received: from smtp.kpnmail.nl (unknown [10.31.155.38]) by ewsoutbound.so.kpn.org (Halon) with ESMTPS id 2a793dab-77c7-11f1-83b1-005056ab378f; Sat, 04 Jul 2026 18:41:12 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xs4all.nl; s=xs4all01; h=mime-version:message-id:date:subject:to:from; bh=uWiGEwZZ76ghh+E+QhXpEXoQ376JG6nQ+5thiNgmtKs=; b=kgR7hAULSW6uwzAQnjeC2+jh+JX8nL2djDZZLoOYxFT5wcdKGBE2yQuXY72RRXrMbCsrlcT7GtfGr 0AudabSDFnzyHTCc69SHr5xL2iQX5TLp0bDOjJ/iSVZgA25graOY6NdKYGjTdwrLyab1BwQSA9b2q1 /4kD6Brq+OQTtNIo0TEc28C1Cch9FaqZIdfmZkpVDVPw/MrakQuXcvCZfXC3TVd4q0J4Y7uPzJU/di gZ1qECHGLD4uk8wvzmcINWlTSpW5vrmufomHrHVddk4RMovXHRMuUPTh1E6W7BfGO2fmzAGyRh0WZH tGExbSckgv2RIh9y52W/DCAqyjc1LYA== X-KPN-MID: 33|GUnIbBsuG8ZyTjDnZE2wBQGAMwVXvMO+gK9peMiABanxEtEmZbT32si3wHz4QDC Z4CY5GN49MvkTK9dxtp8exth/I9e2/4FJ0kh6ZHLU9Ak= X-KPN-VerifiedSender: Yes X-CMASSUN: 33|XdNqFvnKLFGWngnaCdvimuAkBAm+GQGCRTzBTU6khB2psraS8oSPU51wtfOUnzO JP7zXrM71LAX7PdD4FRMiYQ== Received: from daedalus.home (unknown [178.230.195.42]) by smtp.xs4all.nl (Halon) with ESMTPSA id 2a31420e-77c7-11f1-a998-005056abf0db; Sat, 04 Jul 2026 18:41:11 +0200 (CEST) From: Jori Koolstra To: Jeff Layton , Christian Brauner , Al Viro , Aleksa Sarai , NeilBrown , Amir Goldstein , Jan Kara Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Jori Koolstra Subject: [PATCH v3 02/14] vfs: move create error && negative dentry case in lookup_open() up Date: Sat, 4 Jul 2026 18:41:36 +0200 Message-ID: <20260704164149.3480051-3-jkoolstra@xs4all.nl> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260704164149.3480051-1-jkoolstra@xs4all.nl> References: <20260704164149.3480051-1-jkoolstra@xs4all.nl> 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" O_CREAT is stripped when create_error is set in lookup_open(), so when lookup does not return an inode, the case if (!dentry->d_inode && (open_flag & O_CREAT)) is always skipped. We can get rid of this cognitive step by handling the error case first. Reviewed-by: NeilBrown Signed-off-by: Jori Koolstra --- fs/namei.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index a8890bc0ae21..40f83a942e12 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4496,6 +4496,11 @@ static struct dentry *lookup_open(struct nameidata *= nd, struct file *file, } } =20 + if (unlikely(create_error) && !dentry->d_inode) { + error =3D create_error; + goto out_dput; + } + /* Negative dentry, just create the file */ if (!dentry->d_inode && (open_flag & O_CREAT)) { /* but break the directory lease first! */ @@ -4515,10 +4520,7 @@ static struct dentry *lookup_open(struct nameidata *= nd, struct file *file, if (error) goto out_dput; } - if (unlikely(create_error) && !dentry->d_inode) { - error =3D create_error; - goto out_dput; - } + return dentry; =20 out_dput: --=20 2.55.0 From nobody Sat Jul 4 20:00:03 2026 Received: from ewsoutbound.kpnmail.nl (ewsoutbound.kpnmail.nl [195.121.94.170]) (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 7E44436E478 for ; Sat, 4 Jul 2026 16:41:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.121.94.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183280; cv=none; b=B26GjkFXbOyTjK403E1cP/1X4UZZP6sZwW+d6rbsGymK4g56/Y1Z2b9Xk2+Fs/vQyLCHd5/o8tuoI2ZVKtNjoNwWX3xxD7S1xqPw9ueIyXO08PsEpataR1G99RoAXMIQZKk9Qz52hpIRPZqtemL/vCJoaAJBs9q2l59xVvC2/+c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183280; c=relaxed/simple; bh=4ENuc5ifJwZ3uXGviyr3ejReeZOnrI14Py2Unje2nMQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LRvhiDW1t+Sxmk0JvgciFy8DIwQh+TDpr0EZb9rT+UvvNRSRP+96wBVLOY6dAHwp47rMTBoC2iupYuwjkhsethc2qAlPZUpz3/LebcigHSr15eNJDvNkR+Cr6ShAMmaOISm6KUxWvQhIAzXUmpj6bL9VowSM8Waqpr843JNpslo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl; spf=pass smtp.mailfrom=xs4all.nl; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b=DxsOCZzK; arc=none smtp.client-ip=195.121.94.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b="DxsOCZzK" X-KPN-MessageId: 2b3dd835-77c7-11f1-83b1-005056ab378f Received: from smtp.kpnmail.nl (unknown [10.31.155.38]) by ewsoutbound.so.kpn.org (Halon) with ESMTPS id 2b3dd835-77c7-11f1-83b1-005056ab378f; Sat, 04 Jul 2026 18:41:13 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xs4all.nl; s=xs4all01; h=mime-version:message-id:date:subject:to:from; bh=GyXwzuUcpWx25LNe0OdWIXFmTCQDpeEwwQq8tv7hbZU=; b=DxsOCZzKbFdI5SAMXLX8ErpmdBK2PrxJjyMWYvUupsJ1H8rIcJOqUm2rg3B6vhXpUpL9Ll91ZkKAI 5zfNfAUe53GrpTdttxAAcyB8UmV5pQwZpxR3xUCrtsbjFu+2691Xh6a09B5ZRq8yeo9rqLl8chZ4je gL0un9584iw777kCDTSFw6NmJU0SMSSUTnc5p+8cjx/ex7ZPiCF39C9wBhYSmLOi5R9a015mKLUDB1 Y4KhIRKE8Dfmd2G9+PW0+7lFQSDkMEqmXuqb1F4bbyhxiBMzpT0+eaj5UaoQPmEDAjxsiw7MNlkVtu XzxWufuI/TXSu6aPxmotGbPWElIMKCg== X-KPN-MID: 33|U3l7v7NxhdQuDV+KYl3xO58Te1m9mP2e8qofn0GUVyojRuCR3O1xKscO9AcAj1+ 5cSVZSMvm5WMqIsBLfcyW+lpteu/+cRtPmtiZpBdlYKU= X-KPN-VerifiedSender: Yes X-CMASSUN: 33|lt6leT9yC9yowTa9o8InIf/LhCczEDA/unq/MYdpAyvTsQtgcWBRa3ZzIXZO6mB eHdTeAzsmuv7GhFtlJUUMrg== Received: from daedalus.home (unknown [178.230.195.42]) by smtp.xs4all.nl (Halon) with ESMTPSA id 2af5c5f6-77c7-11f1-a998-005056abf0db; Sat, 04 Jul 2026 18:41:13 +0200 (CEST) From: Jori Koolstra To: Jeff Layton , Christian Brauner , Al Viro , Aleksa Sarai , NeilBrown , Amir Goldstein , Jan Kara Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Jori Koolstra Subject: [PATCH v3 03/14] vfs: prepare vfs_creat|mkdir_no_perm for reuse in lookup_open() Date: Sat, 4 Jul 2026 18:41:37 +0200 Message-ID: <20260704164149.3480051-4-jkoolstra@xs4all.nl> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260704164149.3480051-1-jkoolstra@xs4all.nl> References: <20260704164149.3480051-1-jkoolstra@xs4all.nl> 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" To implement O_CREAT|O_DIRECTORY we will have to repeat some of the logic that is now in vfs_mkdir() (e.g. do error checks in the same order). Separate this out in vfs_mkdir_no_perm(), which does all the non-permission related work of vfs_mkdir(). Permission checking for the lookup_open() path is timed differently because we may just be doing an open and no create. Similar considerations give rise to vfs_create_no_perm(). Moving the fsnotify_* calls also allows us to deal with this in one place for each type of operation. This does mean that we also need to move the fsnotify_* calls into atomic_open() for the atomic open case, but this actually reduces duplicate code in open_last_lookups() and dentry_create(). Reviewed-by: NeilBrown Signed-off-by: Jori Koolstra --- fs/namei.c | 105 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 68 insertions(+), 37 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 40f83a942e12..ee47d3f5159f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4169,6 +4169,24 @@ static inline umode_t vfs_prepare_mode(struct mnt_id= map *idmap, return mode; } =20 +static inline +int vfs_create_no_perm(struct mnt_idmap *idmap, struct dentry *dentry, + umode_t mode, struct delegated_inode *di) +{ + struct inode *dir =3D d_inode(dentry->d_parent); + int error; + + error =3D try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, di); + if (error) + return error; + + error =3D dir->i_op->create(idmap, dir, dentry, mode, true); + if (!error) + fsnotify_create(dir, dentry); + + return error; +} + /** * vfs_create - create new file * @idmap: idmap of the mount the inode was found from @@ -4201,13 +4219,8 @@ int vfs_create(struct mnt_idmap *idmap, struct dentr= y *dentry, umode_t mode, error =3D security_inode_create(dir, dentry, mode); if (error) return error; - error =3D try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, di); - if (error) - return error; - error =3D dir->i_op->create(idmap, dir, dentry, mode, true); - if (!error) - fsnotify_create(dir, dentry); - return error; + + return vfs_create_no_perm(idmap, dentry, mode, di); } EXPORT_SYMBOL(vfs_create); =20 @@ -4384,10 +4397,17 @@ static struct dentry *atomic_open(const struct path= *path, struct dentry *dentry error =3D -ENOENT; } } + if (error) { dput(dentry); dentry =3D ERR_PTR(error); + } else { + if (file->f_mode & FMODE_CREATED) + fsnotify_create(dir, dentry); + if (file->f_mode & FMODE_OPENED) + fsnotify_open(file); } + return dentry; } =20 @@ -4519,6 +4539,8 @@ static struct dentry *lookup_open(struct nameidata *n= d, struct file *file, mode, open_flag & O_EXCL); if (error) goto out_dput; + + fsnotify_create(dir_inode, dentry); } =20 return dentry; @@ -4607,13 +4629,9 @@ static const char *open_last_lookups(struct nameidat= a *nd, inode_lock(dir->d_inode); else inode_lock_shared(dir->d_inode); + dentry =3D lookup_open(nd, file, op, got_write, &delegated_inode); - if (!IS_ERR(dentry)) { - if (file->f_mode & FMODE_CREATED) - fsnotify_create(dir->d_inode, dentry); - if (file->f_mode & FMODE_OPENED) - fsnotify_open(file); - } + if (open_flag & O_CREAT) inode_unlock(dir->d_inode); else @@ -5066,13 +5084,6 @@ struct file *dentry_create(struct path *path, int fl= ags, umode_t mode, if (unlikely(create_error) && error =3D=3D -ENOENT) error =3D create_error; =20 - if (!error) { - if (file->f_mode & FMODE_CREATED) - fsnotify_create(dir->d_inode, dentry); - if (file->f_mode & FMODE_OPENED) - fsnotify_open(file); - } - path->dentry =3D dentry; =20 } else { @@ -5224,6 +5235,39 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename= , umode_t, mode, unsigned, d return filename_mknodat(AT_FDCWD, name, mode, dev); } =20 +static inline +struct dentry *vfs_mkdir_no_perm(struct mnt_idmap *idmap, struct inode *di= r, + struct dentry *dentry, umode_t mode, + struct delegated_inode *di) +{ + int error; + struct dentry *de; + unsigned max_links =3D dir->i_sb->s_max_links; + + error =3D -EMLINK; + if (max_links && dir->i_nlink >=3D max_links) + goto err; + + error =3D try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, di); + if (error) + goto err; + + de =3D dir->i_op->mkdir(idmap, dir, dentry, mode); + if (IS_ERR(de)) { + error =3D PTR_ERR(de); + goto err; + } + if (de) { + dput(dentry); + dentry =3D de; + } + fsnotify_mkdir(dir, dentry); + return dentry; + +err: + return ERR_PTR(error); +} + /** * vfs_mkdir - create directory returning correct dentry if possible * @idmap: idmap of the mount the inode was found from @@ -5251,7 +5295,6 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, str= uct inode *dir, struct delegated_inode *delegated_inode) { int error; - unsigned max_links =3D dir->i_sb->s_max_links; struct dentry *de; =20 error =3D may_create_dentry(idmap, dir, dentry); @@ -5267,24 +5310,12 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, s= truct inode *dir, if (error) goto err; =20 - error =3D -EMLINK; - if (max_links && dir->i_nlink >=3D max_links) - goto err; - - error =3D try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, delegated_inode); - if (error) - goto err; - - de =3D dir->i_op->mkdir(idmap, dir, dentry, mode); - error =3D PTR_ERR(de); - if (IS_ERR(de)) + de =3D vfs_mkdir_no_perm(idmap, dir, dentry, mode, delegated_inode); + if (IS_ERR(de)) { + error =3D PTR_ERR(de); goto err; - if (de) { - dput(dentry); - dentry =3D de; } - fsnotify_mkdir(dir, dentry); - return dentry; + return de; =20 err: end_creating(dentry); --=20 2.55.0 From nobody Sat Jul 4 20:00:03 2026 Received: from ewsoutbound.kpnmail.nl (ewsoutbound.kpnmail.nl [195.121.94.170]) (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 7561D372B50 for ; Sat, 4 Jul 2026 16:41:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.121.94.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183280; cv=none; b=LiWThUNAfe/jkZRPsw0L7t2Jdt/e9ppXwc+EvXsOSSt6ZH4V6g3O/1foQNK35vesWs9ZRuPczQa+9/ZxhWEf1LCOh3+3C8qASESbhgJ8k/jjWoD2xu5/GEQZCyrrJPy7cnIW9ZpbXzeCzu89+dosOyxjGIGz/0Xu2o3tmZy8Tuw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183280; c=relaxed/simple; bh=bvYgTIQc9D2R4oaXpBX/K77LWIiYru+DdhNkOgbTsCw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=qY4DwwuasCO9GCkFxYj5eu1515VOd6CuKDzKz2texAmaEKiA11UBkrXDg4dKJkDFHpiwR3GGCJa+AfjaCXaX9Jbm4zh6VFDlu8L/Te6aGnODioEVFhKJ+lO+npDl+/bjbjK4eYCeiO5gesDgzGtnxyqYEoaU3gndo0TiE1wg/j0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl; spf=pass smtp.mailfrom=xs4all.nl; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b=LDXEQjva; arc=none smtp.client-ip=195.121.94.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b="LDXEQjva" X-KPN-MessageId: 2bd9fed4-77c7-11f1-83b1-005056ab378f Received: from smtp.kpnmail.nl (unknown [10.31.155.38]) by ewsoutbound.so.kpn.org (Halon) with ESMTPS id 2bd9fed4-77c7-11f1-83b1-005056ab378f; Sat, 04 Jul 2026 18:41:14 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xs4all.nl; s=xs4all01; h=mime-version:message-id:date:subject:to:from; bh=GtkjJ/4R2rxo8JEAlzIZ55lWA6ArrnaDppuG/xB4dRo=; b=LDXEQjva+BWMPgnvHIELRh2k3zUPbXt4NbTcSMwSPer3s/j26GLyLhYPLAM9Dw8CT0mH5YMdxtS1K v9D3Hi56o+sZVSBKdHb+Wzb587QBHGuE26EiF0/xScsBVAin4k85r5FPRw0IfxOkl6gLVoH672Q4v9 VjkzuBagWkNGoaahhCVSEE1kElRt9RessgDlKkpCkV9G6a3bXIbTL/5CzzrQCGEBtmyAbcYibb7hW1 u10IzqNnPPSkqUOkN+52PAu8FxnNyoc+0XOhdATRzRy/nEseG4neUSqtf208VON4sewnR6IHB1tqK5 TFGt9OHeqXzUrOoSTTlknYwRDBcFn5A== X-KPN-MID: 33|+nIcN2KopJPMxkIsR+EcW1HeifioMhtZZ/wdl0aosLsLh6ax6fAlne1JASji73i OOfUcOveTiURp3RbygR5Ql5NOnjviZzSvJvdV3vdTPjU= X-KPN-VerifiedSender: Yes X-CMASSUN: 33|iWEXKhD4H2F1HTAwT0mS+zz5KdSbSwCEnBZq0csl4/zJDmxySjdLUyj7Jb2uaoN Or776/MM8ECG9SAq4WIyEUQ== Received: from daedalus.home (unknown [178.230.195.42]) by smtp.xs4all.nl (Halon) with ESMTPSA id 2b90ca9b-77c7-11f1-a998-005056abf0db; Sat, 04 Jul 2026 18:41:14 +0200 (CEST) From: Jori Koolstra To: Jeff Layton , Christian Brauner , Al Viro , Aleksa Sarai , NeilBrown , Amir Goldstein , Jan Kara Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Jori Koolstra Subject: [PATCH v3 04/14] vfs: call audit_inode_child() in lookup_open() on failure too Date: Sat, 4 Jul 2026 18:41:38 +0200 Message-ID: <20260704164149.3480051-5-jkoolstra@xs4all.nl> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260704164149.3480051-1-jkoolstra@xs4all.nl> References: <20260704164149.3480051-1-jkoolstra@xs4all.nl> 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" audit_inode_child() is called in may_create_dentry() so that failed filesystem operations still register an audit entry. On success, the entry is overwritten when, for instance, fsnotify_create() is called. This is the calling convention in vfs_create() and vfs_mkdir(). In lookup_open(), however, when atomic_open() should have created a file but didn't, no call to audit_inode_child() is made. The same is true for the regular ->create() path. Fix the calling of audit_inode_child() in lookup_open() to match the vfs_create() path. For the ->atomic_open() filesystems this logic has been pushed into atomic_open(). This function is also reordered a bit to make the case distinction of the possible returns from ->atomic_open() more explicit (i.e. finish_open() or finish_no_open()). When retrying delegation breaking, audit_inode_child() could be called more than once, but this is OK because those entries are reused. Signed-off-by: Jori Koolstra --- fs/namei.c | 75 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index ee47d3f5159f..956adcd14c4a 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4369,10 +4369,11 @@ static int may_o_create(struct mnt_idmap *idmap, */ static struct dentry *atomic_open(const struct path *path, struct dentry *= dentry, struct file *file, - int open_flag, umode_t mode) + int open_flag, umode_t mode, bool create_error) { struct dentry *const DENTRY_NOT_SET =3D (void *) -1UL; struct inode *dir =3D path->dentry->d_inode; + bool failed_create =3D false; int error; =20 file->__f_path.dentry =3D DENTRY_NOT_SET; @@ -4382,23 +4383,33 @@ static struct dentry *atomic_open(const struct path= *path, struct dentry *dentry d_lookup_done(dentry); if (!error) { if (file->f_mode & FMODE_OPENED) { - if (unlikely(dentry !=3D file->f_path.dentry)) { + // finish_open() called + struct dentry *opened =3D file->f_path.dentry; + if (unlikely(opened !=3D dentry)) { dput(dentry); - dentry =3D dget(file->f_path.dentry); + dentry =3D dget(opened); } - } else if (WARN_ON(file->f_path.dentry =3D=3D DENTRY_NOT_SET)) { - error =3D -EIO; - } else { - if (file->f_path.dentry) { + } else if (likely(file->f_path.dentry !=3D DENTRY_NOT_SET)) { + // finish_no_open() called + struct dentry *replaced =3D file->f_path.dentry; + if (replaced) { dput(dentry); - dentry =3D file->f_path.dentry; + dentry =3D replaced; } if (unlikely(d_is_negative(dentry))) error =3D -ENOENT; + if (error && create_error) // should have created, but errored before + failed_create =3D true; + } else { + const char *fsname =3D dentry->d_sb->s_type->name; + WARN(1, "%s: ->atomic_open() left file->f_path.dentry unset!\n", fsname= ); + error =3D -EIO; } } =20 if (error) { + if (failed_create) + audit_inode_child(dir, dentry, AUDIT_TYPE_CHILD_CREATE); dput(dentry); dentry =3D ERR_PTR(error); } else { @@ -4462,7 +4473,7 @@ static struct dentry *lookup_open(struct nameidata *n= d, struct file *file, dentry =3D NULL; } if (dentry->d_inode) { - /* Cached positive dentry: will open in f_op->open */ + /* Cached positive dentry: will open in do_open(). */ return dentry; } =20 @@ -4496,7 +4507,7 @@ static struct dentry *lookup_open(struct nameidata *n= d, struct file *file, if (dir_inode->i_op->atomic_open) { if (nd->flags & LOOKUP_DIRECTORY) open_flag |=3D O_DIRECTORY; - dentry =3D atomic_open(&nd->path, dentry, file, open_flag, mode); + dentry =3D atomic_open(&nd->path, dentry, file, open_flag, mode, create_= error); if (unlikely(create_error) && dentry =3D=3D ERR_PTR(-ENOENT)) dentry =3D ERR_PTR(create_error); return dentry; @@ -4515,33 +4526,37 @@ static struct dentry *lookup_open(struct nameidata = *nd, struct file *file, dentry =3D res; } } + if (dentry->d_inode || !(op->open_flag & O_CREAT)) { + /* No need to create a file. If lookup returned a positive + * dentry, the file will be opened in do_open(). */ + return dentry; + } + + /* Negative dentry with O_CREAT flag set */ + audit_inode_child(dir->d_inode, dentry, AUDIT_TYPE_CHILD_CREATE); =20 - if (unlikely(create_error) && !dentry->d_inode) { + if (unlikely(create_error)) { + /* Should have done a create, but we already errored */ error =3D create_error; goto out_dput; } =20 - /* Negative dentry, just create the file */ - if (!dentry->d_inode && (open_flag & O_CREAT)) { - /* but break the directory lease first! */ - error =3D try_break_deleg(dir_inode, LEASE_BREAK_DIR_CREATE, delegated_i= node); - if (error) - goto out_dput; + error =3D try_break_deleg(dir_inode, LEASE_BREAK_DIR_CREATE, delegated_in= ode); + if (error) + goto out_dput; =20 - file->f_mode |=3D FMODE_CREATED; - audit_inode_child(dir_inode, dentry, AUDIT_TYPE_CHILD_CREATE); - if (!dir_inode->i_op->create) { - error =3D -EACCES; - goto out_dput; - } + file->f_mode |=3D FMODE_CREATED; + if (!dir_inode->i_op->create) { + error =3D -EACCES; + goto out_dput; + } =20 - error =3D dir_inode->i_op->create(idmap, dir_inode, dentry, - mode, open_flag & O_EXCL); - if (error) - goto out_dput; + error =3D dir_inode->i_op->create(idmap, dir_inode, dentry, + mode, open_flag & O_EXCL); + if (error) + goto out_dput; =20 - fsnotify_create(dir_inode, dentry); - } + fsnotify_create(dir_inode, dentry); =20 return dentry; =20 @@ -5071,7 +5086,7 @@ struct file *dentry_create(struct path *path, int fla= gs, umode_t mode, =20 /* atomic_open will dput(dentry) on error */ dget(orig_dentry); - dentry =3D atomic_open(path, dentry, file, flags, mode); + dentry =3D atomic_open(path, dentry, file, flags, mode, create_error); error =3D PTR_ERR_OR_ZERO(dentry); =20 if (IS_ERR(dentry)) --=20 2.55.0 From nobody Sat Jul 4 20:00:03 2026 Received: from ewsoutbound.kpnmail.nl (ewsoutbound.kpnmail.nl [195.121.94.167]) (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 298CE374E55 for ; Sat, 4 Jul 2026 16:41:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.121.94.167 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183284; cv=none; b=YhCMTZ7u14y7XvvytkjU0gt+CVnFcldipBHF+jZ8txvXU/4GzZ/bhJzHSvUPnDozP200GpucgOKo74HVMf8PbUjkLKcWuBgp5ptMsBm0+11PCokBZZNvYxDTo3gdsy/tSweK4CF+9sIoc4Bs5fbNJ4LyxtGtzi5WdFM96O3QkCM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183284; c=relaxed/simple; bh=6TbvUjm/vKZ8SuMokqtk9ryfZjQh1Ge0Cisl9CTuAns=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=bV5WLBiDNtdIahBWPGrXtXL1mBHLHLOPKSRgcjyTXLj0IkqLxNfx7NviBLPVUINgtdIvSNHegsw1ryu3EF3l7HXlZ6m9Ewe/BK55/Yt/Qxvgf5krtXdnfaJGTnKMy281+IRbfXW++60QxOdu6fIt9pGpVPggcMbVumXH6xJio34= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl; spf=pass smtp.mailfrom=xs4all.nl; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b=aq7EKR7e; arc=none smtp.client-ip=195.121.94.167 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b="aq7EKR7e" X-KPN-MessageId: 2c9a88c7-77c7-11f1-b2a8-005056abbe64 Received: from smtp.kpnmail.nl (unknown [10.31.155.38]) by ewsoutbound.so.kpn.org (Halon) with ESMTPS id 2c9a88c7-77c7-11f1-b2a8-005056abbe64; Sat, 04 Jul 2026 18:41:15 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xs4all.nl; s=xs4all01; h=mime-version:message-id:date:subject:to:from; bh=nUTpH0lAxQSMVbJ+SSd+bPpKLaf+crd5MTkWXk8H8io=; b=aq7EKR7ehCzXvP3t6BnVkQA6PAepqTFxwtGKyFspKqpc1xj95VtNfVqhaRok9gs7eQ/U8nwv59Cgf 3WG9x0L0Z5VtqTDZqKDdCsdHoe2EgwPmZimLdFTHhw7foP0FRoc1OUMag/1RGYXWqqju2gNH4IB6n8 73JmUXsHCAD4A5DrcNtvSXR8AxGzGaEENl6l1vC5aiNLiAtQHGRTBDtJZJZfUFo/b0yJoLOjnqMv9g KmEL58JJJKmTrYGjBdJtF/88TYtow3DbIz98d2njCBcOrxPAd5mmFe94OI+A2S4VCBV/mcHrOFYkaG cgoFk0WAFkNxj3aZoisRSmBuih9kBrg== X-KPN-MID: 33|kU4hVqoSl560N9dvQWdeZx2j+LdjHdRDR+jDVjSZlAMGCETvH12dXCRd0LHRRjy 7KDTsGG9OCCU20WNGkidZ+TMf3MbZA4MShk+8xleMITE= X-KPN-VerifiedSender: Yes X-CMASSUN: 33|4Z5oBN1BTKtP3UR4AntRXlrQp2HysXuVUoyGizciTyL3x45BkeRji49qsqmILwU wERm8xeAPDM42pMMYNGtinA== Received: from daedalus.home (unknown [178.230.195.42]) by smtp.xs4all.nl (Halon) with ESMTPSA id 2c57c941-77c7-11f1-a998-005056abf0db; Sat, 04 Jul 2026 18:41:15 +0200 (CEST) From: Jori Koolstra To: Jeff Layton , Christian Brauner , Al Viro , Aleksa Sarai , NeilBrown , Amir Goldstein , Jan Kara Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Jori Koolstra Subject: [PATCH v3 05/14] fs/namei.c: update docstring of atomic_open() Date: Sat, 4 Jul 2026 18:41:39 +0200 Message-ID: <20260704164149.3480051-6-jkoolstra@xs4all.nl> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260704164149.3480051-1-jkoolstra@xs4all.nl> References: <20260704164149.3480051-1-jkoolstra@xs4all.nl> 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" The docstring of atomic_open() contains several errors: - It does not return 0 if successful. - path is not updated Fix those and be more explicit about when FMODE_OPENED and FMODE_CREATED are set. Signed-off-by: Jori Koolstra --- fs/namei.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 956adcd14c4a..88d56a470832 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4355,17 +4355,24 @@ static int may_o_create(struct mnt_idmap *idmap, } =20 /* - * Attempt to atomically look up, create and open a file from a negative - * dentry. - * - * Returns 0 if successful. The file will have been created and attached = to - * @file by the filesystem calling finish_open(). - * - * If the file was looked up only or didn't need creating, FMODE_OPENED wo= n't - * be set. The caller will need to perform the open themselves. @path wi= ll - * have been updated to point to the new dentry. This may be negative. - * - * Returns an error code otherwise. + * atomic_open() - attempt to atomically look up, create and open a file + * from a negative dentry. + * @path: parent directory path + * @dentry: child to ->atomic_open() + * @open_flag: open flags + * @mode: create mode + * @create_error: return value from may_o_create() + * + * If FMODE_OPENED is set, the file will have been attached to @file by the + * filesystem calling finish_open(). If FMODE_OPENED isn't set, the + * filesystem instead called finish_no_open() and the caller will need to + * perform the open themselves. + * + * FMODE_CREATED is set when the call to ->atomic_open() actually created + * the file. + * + * Returns the opened/looked-up dentry on success or ERR_PTR(-E) on failur= e. + * On error, atomic_open() consumes @dentry. */ static struct dentry *atomic_open(const struct path *path, struct dentry *= dentry, struct file *file, --=20 2.55.0 From nobody Sat Jul 4 20:00:03 2026 Received: from ewsoutbound.kpnmail.nl (ewsoutbound.kpnmail.nl [195.121.94.167]) (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 6F381372B5A for ; Sat, 4 Jul 2026 16:41:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.121.94.167 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183283; cv=none; b=Uh3ez7QMniFqrsa18w+YRQeoFDa2UZ0CNG1be8RIJ7NXmwNlpmKNk5cVyz8qzycC3ElmEr7Sr67j+tnAiQt2CUe8wIPLNLLXlZS/wB4ImuwcHLwq65rqQaio42w9Rh6mO/e9QV1nB1UY/hhzXlt7bB2Cvh6q6h6RhKmqUltSVfQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183283; c=relaxed/simple; bh=QE0ultVNYqFGiMbEPhbEgh0kk6GJe6Pgk+g1wig9ULw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=C64mjMwi3cR6xahZUwY3RWdHgixwpDreqiNPq3Q+v3poCoDdnKF8Zn9Ad5WPGlPnGV2P9MTzq2osl1lDHnp7ae1a+XEet3xRRN5FxK3UU06jiKYnl7GNjAFsXqWe2OTVAP99R0JLNPQqcl+fKLF/w4wUGHyJdBaUiu4IJS0XQ+A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl; spf=pass smtp.mailfrom=xs4all.nl; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b=RIzQRxfY; arc=none smtp.client-ip=195.121.94.167 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b="RIzQRxfY" X-KPN-MessageId: 2d67eff4-77c7-11f1-b2a8-005056abbe64 Received: from smtp.kpnmail.nl (unknown [10.31.155.38]) by ewsoutbound.so.kpn.org (Halon) with ESMTPS id 2d67eff4-77c7-11f1-b2a8-005056abbe64; Sat, 04 Jul 2026 18:41:16 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xs4all.nl; s=xs4all01; h=mime-version:message-id:date:subject:to:from; bh=LwnIn4ELdcLX68aNwF9MMNAKMh8QHQXL8uFsHEy53gQ=; b=RIzQRxfY8SUiBKfEWrC2dlAeN7p1Nanm+ZN+Av5qSPBkCHnC2ja/DSzt1BvxKdHz7v4OEiUyxTs6D IPucfzxDw/Nlx+nb9OB1urRIU4jHiKukJaPejibN+iEpiwGBgZpYfHG2AolL4e72YNqtkuIWsBkk/W X9ryMVToQ62dmuuptiRjuL3mB+ro7BGEfPZkptyMFNBl5HBD7s0qQfEHHfdVxF7iJ3+vGtu7CeEUop 8VC8DlVj/uHS0ZIC5Rb6EMRCClKSrWnWjFkYM+FUFQ4HucpjwIlj3hahoMPhyexCU1jN15kolRT+o7 aM9PJv+OWd1cBt1PuaVxFqsZRLNvzog== X-KPN-MID: 33|G3IB9CRxfla0Ys6ZcRINT54mT/O6bLOogzGedhWftFXcftQUiPFEG5JlDUred+K Mg+ECaAmZtGlLL3O6cTGNhLzhHyp1Qjk36cY9gFjdJug= X-KPN-VerifiedSender: Yes X-CMASSUN: 33|fqmhZPoH2rj5vmZOjAXi5yUb45f7vr8zKoFYFGRcxTeH4mRC7phNp/H8gUHxl39 7jDwM/d6q0rbbfwZvURED+Q== Received: from daedalus.home (unknown [178.230.195.42]) by smtp.xs4all.nl (Halon) with ESMTPSA id 2d1ec6ac-77c7-11f1-a998-005056abf0db; Sat, 04 Jul 2026 18:41:16 +0200 (CEST) From: Jori Koolstra To: Jeff Layton , Christian Brauner , Al Viro , Aleksa Sarai , NeilBrown , Amir Goldstein , Jan Kara Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Jori Koolstra Subject: [PATCH v3 06/14] vfs: lookup_open(): move setting FMODE_CREATED down Date: Sat, 4 Jul 2026 18:41:40 +0200 Message-ID: <20260704164149.3480051-7-jkoolstra@xs4all.nl> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260704164149.3480051-1-jkoolstra@xs4all.nl> References: <20260704164149.3480051-1-jkoolstra@xs4all.nl> 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" In preparation for using vfs_create_no_perm() in lookup_open() we need to move setting FMODE_CREATED on the file mode to either before or after that call, as currently it is in the middle. If try_break_deleg() fails it is currently not set, but vfs_create_no_perm() includes a try_break_deleg(). Going up the call chain of lookup_open() we see that it is only used in open_last_lookups() if no error is returned from lookup_open(), so we can safely move it to after the filesystem create() call. This also makes more sense when reading the code as you don't have to wonder what the implications are of setting FMODE_CREATED before the create() call. Reviewed-by: NeilBrown Signed-off-by: Jori Koolstra --- fs/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/namei.c b/fs/namei.c index 88d56a470832..daf31845e3bb 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4552,7 +4552,6 @@ static struct dentry *lookup_open(struct nameidata *n= d, struct file *file, if (error) goto out_dput; =20 - file->f_mode |=3D FMODE_CREATED; if (!dir_inode->i_op->create) { error =3D -EACCES; goto out_dput; @@ -4564,6 +4563,7 @@ static struct dentry *lookup_open(struct nameidata *n= d, struct file *file, goto out_dput; =20 fsnotify_create(dir_inode, dentry); + file->f_mode |=3D FMODE_CREATED; =20 return dentry; =20 --=20 2.55.0 From nobody Sat Jul 4 20:00:03 2026 Received: from ewsoutbound.kpnmail.nl (ewsoutbound.kpnmail.nl [195.121.94.168]) (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 E798B37416B for ; Sat, 4 Jul 2026 16:41:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.121.94.168 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183292; cv=none; b=edrukQE2mJztc4zE2BlQzduokjBIIueZs8hrQRs2UFzcUPPgkdoe2S1A7QCX7+g3U5jk2STV5y61Ru5kOhK6F+thkoGabuFTNAhhncEmfXMegKSquRbWe/iZtJIj1FQRsGJd/ITGS17TY2sOJ+POd/tfK/DHTrj3cUmGIMw2vpk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183292; c=relaxed/simple; bh=jSWP6PXLEjEYsMMu0X9cBXmITpU6xcE+hMnhBqj9zUQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZLVFvXu743h+aAJdFS5Az7xQYuUUWsFZuXKgha4hh62d5FCnPAVYXQebxuygLADh3W8ByFK3BMKZcklWm8aFyTnmwYWcCovfsCw08xerSdO+RAW2d6v6t1GiVNZTBZb/w8A2QTymKsZLSFK8QgcMjYUzExnwevuCRipvwOjvTCU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl; spf=pass smtp.mailfrom=xs4all.nl; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b=Pwn15L4v; arc=none smtp.client-ip=195.121.94.168 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b="Pwn15L4v" X-KPN-MessageId: 2e146249-77c7-11f1-861c-005056aba152 Received: from smtp.kpnmail.nl (unknown [10.31.155.38]) by ewsoutbound.so.kpn.org (Halon) with ESMTPS id 2e146249-77c7-11f1-861c-005056aba152; Sat, 04 Jul 2026 18:41:18 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xs4all.nl; s=xs4all01; h=mime-version:message-id:date:subject:to:from; bh=gGZMZcKnxMJU/bFYMZhyKsdzdzN1KCVWyreiizlX7Ow=; b=Pwn15L4vMhbZ0Ny2wcnIyF2qeiww5MRZlM2uF36XkY172VXfhHUYEDpam92XzlbAX8UOBKonwn3Km IjeFJ+EdPFYRB4qlAl+f4/22HRda6ZQr+FztmHz9DK1FkbhjtdCQXPp5rDNx4MsR+tqGxKDlyWe3Qi 0x7MdHxp5ffahZKShto4X7mjTk1AjraoNeo1cANEoZOdYX0S/WVYscKSYXR8RXt4M64EcuoSDbLCap CW6OwJT5aW5JyQrocZF5NWo2Hgkd8jMbnwH1TG+RUjSemQXO86f9nLbnigv5cUaxvdDfrmC/cds570 Var/o+P5lJUKn9bn4O99VSMLYAXkMEA== X-KPN-MID: 33|yxBlcxajSf8OzBO6tcFDpcDz36vbDjgdiZXJplFSJbL98XmCiT/nVBvP4BU6Ezn Ke/0abmonOqQgZwHLIqHtpQ== X-KPN-VerifiedSender: Yes X-CMASSUN: 33|gLx0ucXtRX3S2JV3a42LFYswR8hwoenBA3TK7fMBw936QVy64Ls+KW2Mi/AjCNd iyQ3mgw9T9s6DC0QKFhPoXw== Received: from daedalus.home (unknown [178.230.195.42]) by smtp.xs4all.nl (Halon) with ESMTPSA id 2dd36b59-77c7-11f1-a998-005056abf0db; Sat, 04 Jul 2026 18:41:17 +0200 (CEST) From: Jori Koolstra To: Jeff Layton , Christian Brauner , Al Viro , Aleksa Sarai , NeilBrown , Amir Goldstein , Jan Kara Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Jori Koolstra Subject: [PATCH v3 07/14] vfs: move ->create check in lookup_open() to before try_break_deleg() Date: Sat, 4 Jul 2026 18:41:41 +0200 Message-ID: <20260704164149.3480051-8-jkoolstra@xs4all.nl> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260704164149.3480051-1-jkoolstra@xs4all.nl> References: <20260704164149.3480051-1-jkoolstra@xs4all.nl> 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" The i_op->create check in lookup_open() takes place after the try_break_deleg() call. This does not match the order when doing a regular file create via mknod(2). There the call order is: filename_mknodat() vfs_create() i_op->create check try_break_deleg() Move the i_op->create check to before try_break_deleg() in lookup_open(). Signed-off-by: Jori Koolstra --- fs/namei.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index daf31845e3bb..6601460d2772 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4548,15 +4548,15 @@ static struct dentry *lookup_open(struct nameidata = *nd, struct file *file, goto out_dput; } =20 - error =3D try_break_deleg(dir_inode, LEASE_BREAK_DIR_CREATE, delegated_in= ode); - if (error) - goto out_dput; - if (!dir_inode->i_op->create) { error =3D -EACCES; goto out_dput; } =20 + error =3D try_break_deleg(dir_inode, LEASE_BREAK_DIR_CREATE, delegated_in= ode); + if (error) + goto out_dput; + error =3D dir_inode->i_op->create(idmap, dir_inode, dentry, mode, open_flag & O_EXCL); if (error) --=20 2.55.0 From nobody Sat Jul 4 20:00:03 2026 Received: from ewsoutbound.kpnmail.nl (ewsoutbound.kpnmail.nl [195.121.94.168]) (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 E7A44374A0F for ; Sat, 4 Jul 2026 16:41:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.121.94.168 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183291; cv=none; b=jGbXf8d6qKnX8ZRA4mA6ie4ClcGaKWfVG69CJzQYYaOtIvcgIjr5e40+hfWZjYx1KPlkQm7SDqfWifHRO+ebp7HDmrBipARjcwWQqftpTJO/nCIoYOb9R62ezYjnjTuBuFCGeNabYqy/edh5RyIY/mtWJfENhzfuVmCIFr08WO0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183291; c=relaxed/simple; bh=1QKtYUmu8q/HWjVbbJIVz/0RMDyV3iKnajm3Db3L9DU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=h74h82PnCANwDFjQlqYh4Mp25WbghtsHEDW0KxYR0ii0z8EpZgoF+62+OoI30lcojGPTSMEtoILpAIGCIQj1WPwQ5CSqAetKnOjGPHqK5Xr1UAuhidn5ZN/KeLfLLTQS4STHmD4cY5UMA5TZW92oR9AuHx0BSKQinRlS2WGCnEs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl; spf=pass smtp.mailfrom=xs4all.nl; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b=ZrzKLYni; arc=none smtp.client-ip=195.121.94.168 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b="ZrzKLYni" X-KPN-MessageId: 2ef23137-77c7-11f1-861c-005056aba152 Received: from smtp.kpnmail.nl (unknown [10.31.155.38]) by ewsoutbound.so.kpn.org (Halon) with ESMTPS id 2ef23137-77c7-11f1-861c-005056aba152; Sat, 04 Jul 2026 18:41:19 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xs4all.nl; s=xs4all01; h=mime-version:message-id:date:subject:to:from; bh=nmJfJL5aIKkG516hFBygMhA360fsiR+gFeMro6rPD5g=; b=ZrzKLYniNPCBdeEifOw3UCjMosymhdGzJf2CqXYqw7ZspOKdwa7LTraYo+LIsGyyMir3fZr539tiU /TLmcaVLK86f+MuUGyK9KLVzUw2Jec1OcM7ul6AkcKfxqJVmA1jpkltheOjbM9gL+0XNqhCdcnyyG3 siBQMoYBOes8mO+tta6LjiyBAQJH/BZBhR/cs1sZof7jGrkDJBCvkOZv8k1W+JiuSz/GE/K4FDk1Zm 9F6O+tWDafZ6Fq8AmTdW4Y7u4cpGHXzHc9sk1KdwVVo146qNSah7TCnwxGxhsURfoqNGYdvWvMuPTy joxaf3+hYpeCrCT1DCa8qHfnu8s9GdQ== X-KPN-MID: 33|zguQ3ukVrHHmSFdlcbXw1pOxHVmVoW2oE1RMsCmT+cL0zlKiCNrdOVf0zf/ld0K YTSJiYwdE14A9zJ6vqmq3qg== X-KPN-VerifiedSender: Yes X-CMASSUN: 33|V5Gvte8mbYWesIzRP5Em7PL41nCPwQCe0wvWULSVdzsDH1Vkkni797QN3AU7glH 1UinpJXqXDAmIZY+IGhZnDw== Received: from daedalus.home (unknown [178.230.195.42]) by smtp.xs4all.nl (Halon) with ESMTPSA id 2eafbe58-77c7-11f1-a998-005056abf0db; Sat, 04 Jul 2026 18:41:19 +0200 (CEST) From: Jori Koolstra To: Jeff Layton , Christian Brauner , Al Viro , Aleksa Sarai , NeilBrown , Amir Goldstein , Jan Kara Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Jori Koolstra Subject: [PATCH v3 08/14] vfs: lookup_open(): use vfs_create_no_perm() Date: Sat, 4 Jul 2026 18:41:42 +0200 Message-ID: <20260704164149.3480051-9-jkoolstra@xs4all.nl> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260704164149.3480051-1-jkoolstra@xs4all.nl> References: <20260704164149.3480051-1-jkoolstra@xs4all.nl> 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" We can replace the code in the no create_error/negative dentry found from lookup case in lookup_open() with the vfs_create_no_perm() helper. It is safe here to always pass excl=3Dtrue to ->create via vfs_create_no_perm(). Local filesystems always ignore the excl flag, and cluster filesystems implement ->atomic_open, so that the ->create call is never reached in lookup_open(). Signed-off-by: Jori Koolstra --- fs/namei.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 6601460d2772..eaa0ade4a1b0 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4553,16 +4553,10 @@ static struct dentry *lookup_open(struct nameidata = *nd, struct file *file, goto out_dput; } =20 - error =3D try_break_deleg(dir_inode, LEASE_BREAK_DIR_CREATE, delegated_in= ode); + error =3D vfs_create_no_perm(idmap, dentry, mode, delegated_inode); if (error) goto out_dput; =20 - error =3D dir_inode->i_op->create(idmap, dir_inode, dentry, - mode, open_flag & O_EXCL); - if (error) - goto out_dput; - - fsnotify_create(dir_inode, dentry); file->f_mode |=3D FMODE_CREATED; =20 return dentry; --=20 2.55.0 From nobody Sat Jul 4 20:00:03 2026 Received: from ewsoutbound.kpnmail.nl (ewsoutbound.kpnmail.nl [195.121.94.168]) (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 3E09C2472A2 for ; Sat, 4 Jul 2026 16:41:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.121.94.168 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183293; cv=none; b=IRUEEOaC5NzniORcPIpCX97Kho6LiOrUYmndZOmpZBWwBfysqs0uSRIJQBoJxG4+pv/PQUzrc2xo55coWwmhZaAgnhjih987fUGrEnYD4NmzT+lcovpl1AjJMCiymhIPkctNSw/2wn4DgaafjgPVB0Qhn+7tpnThqoVR7yjfXrQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183293; c=relaxed/simple; bh=lPDteEwxsHU1piwmnsarATyysuiO06z09mKpVCsoINM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TDrpZjc1d4yH9gGv1CCnzE4p9lKGloecF6J2yc7YZoxjpuA8DpI5v30w1dHdS2dwTl/dltLbzoZoMDdutFvKBRyUieLnt/G3Ze1DZlN6UkQkVUznxWChU4lJDMUEPSB6tDXk48BXj9XqdPPRztF569ULQxPt4N5ns96IetTx+0I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl; spf=pass smtp.mailfrom=xs4all.nl; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b=k9vpeLJo; arc=none smtp.client-ip=195.121.94.168 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b="k9vpeLJo" X-KPN-MessageId: 2f9e3aef-77c7-11f1-861c-005056aba152 Received: from smtp.kpnmail.nl (unknown [10.31.155.38]) by ewsoutbound.so.kpn.org (Halon) with ESMTPS id 2f9e3aef-77c7-11f1-861c-005056aba152; Sat, 04 Jul 2026 18:41:20 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xs4all.nl; s=xs4all01; h=mime-version:message-id:date:subject:to:from; bh=EnAOc5GtW6hyNP+cqSXX7D/7rTzrI6y7XfKAeboZT+A=; b=k9vpeLJoYzEjF6di3GW/hzPADVmAzh/iqc49rZseYWW3/CQxkuft5rWxkMR0BJ+oZfperF2lh9Gwq SRs5/qdQ1fOL9TJQFdtAuNxl/kxL4wZQm/OT00DB13g7wc5uzqylP3/yTKshpvR+iVWCKDOFZbl3hR gHL5AjgFZQcKrfUnVZ05K3v0kDnFCvH5l0qs/3tPyxPzgrwSSi2n5tddBX2aD/PjUanruwwrFjI2hT 42gYSN9d4rArhOiFXvvbxE+HIalNdeRoao9a5Qtvsb7+lGHObahGsl7TZUGywOOhfcuOMwFWwGnQjo NuHfU/9/JSS/IFSr8vfQQzLUlNzrusw== X-KPN-MID: 33|uRB8tm71e13LDuaDUp+cUr6pVgN2HqjovTrkG2Ih+o9nEIIqek1GdAgDucejDLS +sPp3mKMGL9ShR5PBzALcJw== X-KPN-VerifiedSender: Yes X-CMASSUN: 33|m1/SObismygvFofnceBD6o/JLHo+606zf3Kq2ijRHTO70uF3VNlroYRUFTzri+D 7MXywR0njDtR5vkZYVqiduA== Received: from daedalus.home (unknown [178.230.195.42]) by smtp.xs4all.nl (Halon) with ESMTPSA id 2f5aa38c-77c7-11f1-a998-005056abf0db; Sat, 04 Jul 2026 18:41:20 +0200 (CEST) From: Jori Koolstra To: Jeff Layton , Christian Brauner , Al Viro , Aleksa Sarai , NeilBrown , Amir Goldstein , Jan Kara Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Jori Koolstra Subject: [PATCH v3 09/14] vfs: add O_CREAT|O_DIRECTORY to open*(2) Date: Sat, 4 Jul 2026 18:41:43 +0200 Message-ID: <20260704164149.3480051-10-jkoolstra@xs4all.nl> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260704164149.3480051-1-jkoolstra@xs4all.nl> References: <20260704164149.3480051-1-jkoolstra@xs4all.nl> 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" Currently there is no way to race-freely create and open a directory. For regular files we have open(O_CREAT) for creating a new file inode, and returning a pinning fd to it. The lack of such functionality for directories means that when populating a directory tree there's always a race involved: the inodes first need to be created, and then opened to adjust their permissions/ownership/labels/timestamps/acls/xattrs/..., but in the time window between the creation and the opening they might be replaced by something else. Addressing this race without proper APIs is possible (by immediately fstat()ing what was opened, to verify that it has the right inode type), but difficult to get right. Hence, adding support for a new flag combo O_CREAT|O_DIRECTORY to open*(2) that creates a directory (if it does not exist already) and returns an O_DIRECTORY fd is very useful. Historically, the O_CREAT|O_DIRECTORY behaviour was to return ENOTDIR if a regular file exists at the open path; EISDIR if a directory exists at the path; and to create a regular file if no file exists at the path. This behaviour changed accidentally with 973d4b73fbaf ("do_last(): rejoin the common path even earlier in FMODE_{OPENED,CREATED} case") causing ENOTDIR to return in the last case while still creating the file. As this change was not detected for a long time, Brauner proposed to adopt the more consistent NetBSD behaviour, i.e. to return EINVAL on the the O_CREAT|O_DIRECTORY combination. This change was applied in 43b450632676 ("open: return EINVAL for O_DIRECTORY | O_CREAT") in March, 2023. As the EINVAL behaviour has been in the kernel for about 3 year now, no rollback is expected as a result of userspace reliance on old behaviour, leaving us free to reassign the O_CREAT|O_DIRECTORY semantics. O_CREAT|O_DIRECTORY is made to reduce to a lookup on ->atomic_open() filesystems. This is implemented by stripping the O_CREAT bit. The other option of simply returning -EINVAL leads to inconsistent behaviour: before ->atomic_open() is called in lookup_open(), the dcache is queried. So returning -EINVAL there would make open(O_CREAT|O_DIRECTORY) dependent on the cache state of the dentry. By stripping the O_CREAT bit, lookup is consistently performed on ->atomic_open() filesystems. There is no separate sysctl for directory creation implemented currently. Therefore, for the S_ISDIR case, disabling sysctl_protected_regular is not enough to allow creating a directory in a sticky folder, because that may surprise users not expecting that O_CREAT|O_DIRECTORY is possible on newer kernels. This feature idea (and some of its description) is taken from the UAPI group: https://github.com/uapi-group/kernel-features?tab=3Dreadme-ov-file#race-fre= e-creation-and-opening-of-non-file-inodes Signed-off-by: Jori Koolstra --- fs/namei.c | 86 +++++++++++++++++++++++++++++++++++-------- fs/open.c | 25 +++++++------ include/linux/fcntl.h | 6 +++ 3 files changed, 90 insertions(+), 27 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index eaa0ade4a1b0..5113ac986f50 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1382,13 +1382,12 @@ int may_linkat(struct mnt_idmap *idmap, const struc= t path *link) =20 /** * may_create_in_sticky - Check whether an O_CREAT open in a sticky direct= ory - * should be allowed, or not, on files that already - * exist. + * should be allowed, or not * @idmap: idmap of the mount the inode was found from * @nd: nameidata pathwalk data * @inode: the inode of the file to open * - * Block an O_CREAT open of a FIFO (or a regular file) when: + * Block an O_CREAT open of a FIFO (or a regular file/directory) when: * - sysctl_protected_fifos (or sysctl_protected_regular) is enabled * - the file already exists * - we are in a sticky directory @@ -1416,6 +1415,14 @@ static int may_create_in_sticky(struct mnt_idmap *id= map, struct nameidata *nd, if (likely(!(dir_mode & S_ISVTX))) return 0; =20 + /* + * There is no separate sysctl for directory creation in sticky + * folders. Therefore, for the S_ISDIR case, disabling + * sysctl_protected_regular is not enough to allow creating a + * directory in a sticky folder, because that may surprise users + * not expecting that O_CREAT|O_DIRECTORY is possible on newer + * kernels. + */ if (S_ISREG(inode->i_mode) && !sysctl_protected_regular) return 0; =20 @@ -1447,6 +1454,12 @@ static int may_create_in_sticky(struct mnt_idmap *id= map, struct nameidata *nd, "sticky_create_regular"); return -EACCES; } + + if (S_ISDIR(inode->i_mode)) { + audit_log_path_denied(AUDIT_ANOM_CREAT, + "sticky_create_dir"); + return -EACCES; + } } =20 return 0; @@ -4337,21 +4350,40 @@ static inline int open_to_namei_flags(int flag) =20 static int may_o_create(struct mnt_idmap *idmap, const struct path *dir, struct dentry *dentry, - umode_t mode) + umode_t mode, bool create_dir) { - int error =3D security_path_mknod(dir, dentry, mode, 0); + struct inode *dir_inode =3D dir->dentry->d_inode; + int error; + + if (create_dir) + error =3D security_path_mkdir(dir, dentry, mode); + else + error =3D security_path_mknod(dir, dentry, mode, 0); if (error) return error; =20 if (!fsuidgid_has_mapping(dir->dentry->d_sb, idmap)) return -EOVERFLOW; =20 - error =3D inode_permission(idmap, dir->dentry->d_inode, - MAY_WRITE | MAY_EXEC); + error =3D inode_permission(idmap, dir_inode, MAY_WRITE | MAY_EXEC); if (error) return error; =20 - return security_inode_create(dir->dentry->d_inode, dentry, mode); + if (create_dir) + error =3D security_inode_mkdir(dir_inode, dentry, mode); + else + error =3D security_inode_create(dir_inode, dentry, mode); + + return error; +} + +static inline umode_t o_create_mode(struct mnt_idmap *idmap, + const struct inode *dir, umode_t mode, bool create_dir) +{ + if (create_dir) + return vfs_prepare_mode(idmap, dir, mode, S_IRWXUGO | S_ISVTX, 0); + else + return vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG); } =20 /* @@ -4385,6 +4417,7 @@ static struct dentry *atomic_open(const struct path *= path, struct dentry *dentry =20 file->__f_path.dentry =3D DENTRY_NOT_SET; file->__f_path.mnt =3D path->mnt; + error =3D dir->i_op->atomic_open(dir, dentry, file, open_to_namei_flags(open_flag), mode); d_lookup_done(dentry); @@ -4429,6 +4462,10 @@ static struct dentry *atomic_open(const struct path = *path, struct dentry *dentry return dentry; } =20 +static inline +struct dentry *vfs_mkdir_no_perm(struct mnt_idmap *, struct inode *, + struct dentry *, umode_t, + struct delegated_inode *); /* * Look up and maybe create and open the last component. * @@ -4455,10 +4492,14 @@ static struct dentry *lookup_open(struct nameidata = *nd, struct file *file, struct dentry *dentry; int error, create_error =3D 0; umode_t mode =3D op->mode; + bool create_dir =3D O_IS_MKDIR(open_flag); =20 if (unlikely(IS_DEADDIR(dir_inode))) return ERR_PTR(-ENOENT); =20 + if (O_IS_MKDIR(open_flag) && dir_inode->i_op->atomic_open) + open_flag &=3D ~O_CREAT; + file->f_mode &=3D ~FMODE_CREATED; dentry =3D d_lookup(dir, &nd->last); for (;;) { @@ -4502,10 +4543,10 @@ static struct dentry *lookup_open(struct nameidata = *nd, struct file *file, if (open_flag & O_CREAT) { if (open_flag & O_EXCL) open_flag &=3D ~O_TRUNC; - mode =3D vfs_prepare_mode(idmap, dir->d_inode, mode, mode, mode); + mode =3D o_create_mode(idmap, dir_inode, mode, create_dir); if (likely(got_write)) create_error =3D may_o_create(idmap, &nd->path, - dentry, mode); + dentry, mode, create_dir); else create_error =3D -EROFS; } @@ -4548,12 +4589,24 @@ static struct dentry *lookup_open(struct nameidata = *nd, struct file *file, goto out_dput; } =20 - if (!dir_inode->i_op->create) { + if ((create_dir && !dir_inode->i_op->mkdir) + || (!create_dir && !dir_inode->i_op->create)) { error =3D -EACCES; goto out_dput; } =20 - error =3D vfs_create_no_perm(idmap, dentry, mode, delegated_inode); + if (create_dir) { + struct dentry *res =3D vfs_mkdir_no_perm(idmap, dir_inode, dentry, + mode, delegated_inode); + if (IS_ERR(res)) { + error =3D PTR_ERR(res); + } else { + error =3D 0; + dentry =3D res; + } + } else { + error =3D vfs_create_no_perm(idmap, dentry, mode, delegated_inode); + } if (error) goto out_dput; =20 @@ -4571,7 +4624,7 @@ static struct dentry *lookup_fast_for_open(struct nam= eidata *nd, int open_flag) struct dentry *dentry; =20 if (open_flag & O_CREAT) { - if (trailing_slashes(nd->last)) + if (trailing_slashes(nd->last) && !(open_flag & O_DIRECTORY)) return ERR_PTR(-EISDIR); =20 /* Don't bother on an O_EXCL create */ @@ -4642,7 +4695,7 @@ static const char *open_last_lookups(struct nameidata= *nd, */ } if (open_flag & O_CREAT) - inode_lock(dir->d_inode); + inode_lock_nested(dir->d_inode, I_MUTEX_PARENT); else inode_lock_shared(dir->d_inode); =20 @@ -4705,8 +4758,9 @@ static int do_open(struct nameidata *nd, if (open_flag & O_CREAT) { if ((open_flag & O_EXCL) && !(file->f_mode & FMODE_CREATED)) return -EEXIST; - if (d_is_dir(nd->path.dentry)) + if (!(open_flag & O_DIRECTORY) && d_is_dir(nd->path.dentry)) return -EISDIR; + error =3D may_create_in_sticky(idmap, nd, d_backing_inode(nd->path.dentry)); if (unlikely(error)) @@ -5081,7 +5135,7 @@ struct file *dentry_create(struct path *path, int fla= gs, umode_t mode, path->dentry =3D dir; mode =3D vfs_prepare_mode(idmap, dir_inode, mode, S_IALLUGO, S_IFREG); =20 - create_error =3D may_o_create(idmap, path, dentry, mode); + create_error =3D may_o_create(idmap, path, dentry, mode, /*create_dir=3D= */ false); if (create_error) flags &=3D ~O_CREAT; =20 diff --git a/fs/open.c b/fs/open.c index 408925d7bd0b..9121aece78bc 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1190,29 +1190,30 @@ inline int build_open_flags(const struct open_how *= how, struct open_flags *op) if (WILL_CREATE(flags)) { if (how->mode & ~S_IALLUGO) return -EINVAL; - op->mode =3D how->mode | S_IFREG; + if (O_IS_MKDIR(flags)) + op->mode =3D how->mode | S_IFDIR; + else + op->mode =3D how->mode | S_IFREG; } else { if (how->mode !=3D 0) return -EINVAL; op->mode =3D 0; } =20 - /* - * Block bugs where O_DIRECTORY | O_CREAT created regular files. - * Note, that blocking O_DIRECTORY | O_CREAT here also protects - * O_TMPFILE below which requires O_DIRECTORY being raised. - */ - if ((flags & (O_DIRECTORY | O_CREAT)) =3D=3D (O_DIRECTORY | O_CREAT)) - return -EINVAL; - /* Now handle the creative implementation of O_TMPFILE. */ if (flags & __O_TMPFILE) { /* * In order to ensure programs get explicit errors when trying * to use O_TMPFILE on old kernels we enforce that O_DIRECTORY - * is raised alongside __O_TMPFILE. + * is raised alongside __O_TMPFILE, but without O_CREAT. The + * reason for disallowing O_CREAT|O_TMPFILE is that + * O_DIRECTORY|O_CREAT used to work and created a regular file + * if nothing existed at the open path. Hence, allowing the + * combination would have caused O_CREAT|O_TMPFILE to create a + * regular (non-temporary) file on old kernels, while the caller + * would believe they created an actual O_TMPFILE. */ - if (!(flags & O_DIRECTORY)) + if (!(flags & O_DIRECTORY) || (flags & O_CREAT)) return -EINVAL; if (!(acc_mode & MAY_WRITE)) return -EINVAL; @@ -1270,6 +1271,8 @@ inline int build_open_flags(const struct open_how *ho= w, struct open_flags *op) op->intent =3D flags & O_PATH ? 0 : LOOKUP_OPEN; =20 if (flags & O_CREAT) { + if ((flags & O_DIRECTORY) && (acc_mode & MAY_WRITE)) + return -EISDIR; op->intent |=3D LOOKUP_CREATE; if (flags & O_EXCL) { op->intent |=3D LOOKUP_EXCL; diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h index 6ad6b9e7a226..6b75277be559 100644 --- a/include/linux/fcntl.h +++ b/include/linux/fcntl.h @@ -30,6 +30,12 @@ */ #define __O_REGULAR (1 << 30) =20 +#define O_MKDIR_MASK (O_CREAT | O_DIRECTORY) +static inline bool O_IS_MKDIR(unsigned flags) +{ + return (flags & O_MKDIR_MASK) =3D=3D O_MKDIR_MASK; +} + /* List of all valid flags for the how->resolve argument: */ #define VALID_RESOLVE_FLAGS \ (RESOLVE_NO_XDEV | RESOLVE_NO_MAGICLINKS | RESOLVE_NO_SYMLINKS | \ --=20 2.55.0 From nobody Sat Jul 4 20:00:03 2026 Received: from ewsoutbound.kpnmail.nl (ewsoutbound.kpnmail.nl [195.121.94.169]) (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 1FCD7282F1D for ; Sat, 4 Jul 2026 16:41:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.121.94.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183294; cv=none; b=tvfaKwUvRCqf4421owUuYJ2uRVU3X5c/4ptM9yyO1kLAA+CBaas5c3Wx4ymBaQRpxnn2P3aiprsunWKTM0L95k/7RcTEAB8f0UX+y+k9h3VO0OsPK0vYmXY6RJVDqKz/9BamjIC03g/cwEZWnZ1JO6yGvZrp887KNcfy+GJ0Dvk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183294; c=relaxed/simple; bh=DvLSrDJxYiqTSealhyUBMIv6w22uIrpLQpXeDvAA0rM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=NwQs2UK8D/x5TauoU/1+XNJItyh3XRBIIzS+zq/49gNmVeo1Rw1lmJPrka3Tl9MTVdPIJztpweXTyQg4tIs0ZuB6l8UqJiP+PR4/Z9cHRUAzxouwLPQunu8MRs76vtDGOLKJHfk5K6GOH1+8/iDlFCqDbFL5LroY/6uV0CE5f4Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl; spf=pass smtp.mailfrom=xs4all.nl; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b=jt/v/g2p; arc=none smtp.client-ip=195.121.94.169 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b="jt/v/g2p" X-KPN-MessageId: 304abcc7-77c7-11f1-a055-005056abad63 Received: from smtp.kpnmail.nl (unknown [10.31.155.38]) by ewsoutbound.so.kpn.org (Halon) with ESMTPS id 304abcc7-77c7-11f1-a055-005056abad63; Sat, 04 Jul 2026 18:41:21 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xs4all.nl; s=xs4all01; h=mime-version:message-id:date:subject:to:from; bh=AMq8O+gKNN5H0SquJut4ZNq4o0eUhlZdYtPCOYC6LFo=; b=jt/v/g2pzbE/qasIBzxdk+Ueinu5hfEHCOsGJsRqgE2MXpsL/NfVqPv8ysdWZ6P10F7wNeInOJi1z efn75Z5q/Es4SdGihCpCHrCZUcpnmffy6fTuVH0LoPiC0A4GttvBOQfpRmXX0ym7dw2/sdJhf/t278 iUzH60EIU1FXYv03wOVlMwIZgpyiOR0PWVP+36SqhP2feKV2V4a8jUo/qnvGFnRCyWp/cCrHEzGYL1 fqhL5ZCeCKrOyGf8NJnCAPtsmHX0AKRCJTyo2kiiq5S0BKdn943CePPxwAavTolqAY6XmW7k8P6gq4 UlrNMDcHMu43O+ZiK4fxGvLRuXRUDvw== X-KPN-MID: 33|hRqqW7JhT90E97B5Xd7/wBXBYt6bUT+Oe+aESp5zdbdHkdJCfywj+21NKgix4M7 Lm8fqi6HbN19Djn4jaBMwDX/Ku6JLOwzJUXX7q4b/kmw= X-KPN-VerifiedSender: Yes X-CMASSUN: 33|jGvzPKakPrKe+eqg2nrAXWIBQdpOZcpG1DWmhvuIXVYwCk/KI4KeDcWVfeuRsox PhvI/CDCBkNHcr/3tcC0u1Q== Received: from daedalus.home (unknown [178.230.195.42]) by smtp.xs4all.nl (Halon) with ESMTPSA id 3004f667-77c7-11f1-a998-005056abf0db; Sat, 04 Jul 2026 18:41:21 +0200 (CEST) From: Jori Koolstra To: Jeff Layton , Christian Brauner , Al Viro , Aleksa Sarai , NeilBrown , Amir Goldstein , Jan Kara Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Jori Koolstra Subject: [PATCH v3 10/14] vfs: move O_IS_MKDIR check from lookup_open() into individual filesystems Date: Sat, 4 Jul 2026 18:41:44 +0200 Message-ID: <20260704164149.3480051-11-jkoolstra@xs4all.nl> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260704164149.3480051-1-jkoolstra@xs4all.nl> References: <20260704164149.3480051-1-jkoolstra@xs4all.nl> 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" Individual filesystems that implement ->atomic_open() need to get the chance to implement O_CREAT|O_DIRECTORY or not, rather than decide this at the VFS level in lookup_open(). Signed-off-by: Jori Koolstra --- fs/9p/vfs_inode.c | 3 +++ fs/9p/vfs_inode_dotl.c | 3 +++ fs/ceph/file.c | 3 +++ fs/fuse/dir.c | 3 +++ fs/gfs2/inode.c | 3 +++ fs/namei.c | 3 --- fs/nfs/dir.c | 6 ++++++ fs/smb/client/dir.c | 3 +++ fs/vboxsf/dir.c | 3 +++ 9 files changed, 27 insertions(+), 3 deletions(-) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 5783d0336f96..bb555e5ba261 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -777,6 +777,9 @@ v9fs_vfs_atomic_open(struct inode *dir, struct dentry *= dentry, struct inode *inode; int p9_omode; =20 + if (O_IS_MKDIR(flags)) + flags &=3D ~O_CREAT; + if (d_in_lookup(dentry)) { struct dentry *res =3D v9fs_vfs_lookup(dir, dentry, 0); if (res || d_really_is_positive(dentry)) diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index f7396d20cb6c..ff3facb420c2 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -239,6 +239,9 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct den= try *dentry, struct v9fs_session_info *v9ses; struct posix_acl *pacl =3D NULL, *dacl =3D NULL; =20 + if (O_IS_MKDIR(flags)) + flags &=3D ~O_CREAT; + if (d_in_lookup(dentry)) { struct dentry *res =3D v9fs_vfs_lookup(dir, dentry, 0); if (res || d_really_is_positive(dentry)) diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 71161f2b2151..e79abdbd9f61 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -811,6 +811,9 @@ int ceph_atomic_open(struct inode *dir, struct dentry *= dentry, dir, ceph_vinop(dir), dentry, dentry, d_unhashed(dentry) ? "unhashed" : "hashed", flags, mode); =20 + if (O_IS_MKDIR(flags)) + flags &=3D ~O_CREAT; + if (dentry->d_name.len > NAME_MAX) return -ENAMETOOLONG; =20 diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 0e2a1039fa43..e01869565d5e 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -935,6 +935,9 @@ static int fuse_atomic_open(struct inode *dir, struct d= entry *entry, struct mnt_idmap *idmap =3D file_mnt_idmap(file); struct fuse_conn *fc =3D get_fuse_conn(dir); =20 + if (O_IS_MKDIR(flags)) + flags &=3D ~O_CREAT; + if (fuse_is_bad(dir)) return -EIO; =20 diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 8a77794bbd4a..9859ae0341a9 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1387,6 +1387,9 @@ static int gfs2_atomic_open(struct inode *dir, struct= dentry *dentry, { bool excl =3D !!(flags & O_EXCL); =20 + if (O_IS_MKDIR(flags)) + flags &=3D ~O_CREAT; + if (d_in_lookup(dentry)) { struct dentry *d =3D __gfs2_lookup(dir, dentry, file); if (file->f_mode & FMODE_OPENED) { diff --git a/fs/namei.c b/fs/namei.c index 5113ac986f50..76916caa264d 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4497,9 +4497,6 @@ static struct dentry *lookup_open(struct nameidata *n= d, struct file *file, if (unlikely(IS_DEADDIR(dir_inode))) return ERR_PTR(-ENOENT); =20 - if (O_IS_MKDIR(open_flag) && dir_inode->i_op->atomic_open) - open_flag &=3D ~O_CREAT; - file->f_mode &=3D ~FMODE_CREATED; dentry =3D d_lookup(dir, &nd->last); for (;;) { diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index c7b723c18620..c815d9aa9b69 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -2121,6 +2121,9 @@ int nfs_atomic_open(struct inode *dir, struct dentry = *dentry, dfprintk(VFS, "NFS: atomic_open(%s/%llu), %pd\n", dir->i_sb->s_id, dir->i_ino, dentry); =20 + if (O_IS_MKDIR(open_flags)) + open_flags &=3D ~O_CREAT; + err =3D nfs_check_flags(open_flags); if (err) return err; @@ -2313,6 +2316,9 @@ int nfs_atomic_open_v23(struct inode *dir, struct den= try *dentry, */ int error =3D 0; =20 + if (O_IS_MKDIR(open_flags)) + open_flags &=3D ~O_CREAT; + if (dentry->d_name.len > NFS_SERVER(dir)->namelen) return -ENAMETOOLONG; =20 diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c index 88a4a1787ff0..75bf86cc0612 100644 --- a/fs/smb/client/dir.c +++ b/fs/smb/client/dir.c @@ -538,6 +538,9 @@ int cifs_atomic_open(struct inode *dir, struct dentry *= direntry, if (unlikely(cifs_forced_shutdown(cifs_sb))) return smb_EIO(smb_eio_trace_forced_shutdown); =20 + if (O_IS_MKDIR(oflags)) + oflags &=3D ~O_CREAT; + /* * Posix open is only called (at lookup time) for file create now. For * opens (rather than creates), because we do not know if it is a file diff --git a/fs/vboxsf/dir.c b/fs/vboxsf/dir.c index c5bd3271aa96..77ae2f696fd4 100644 --- a/fs/vboxsf/dir.c +++ b/fs/vboxsf/dir.c @@ -318,6 +318,9 @@ static int vboxsf_dir_atomic_open(struct inode *parent,= struct dentry *dentry, u64 handle; int err; =20 + if (O_IS_MKDIR(flags)) + flags &=3D ~O_CREAT; + if (d_in_lookup(dentry)) { struct dentry *res =3D vboxsf_dir_lookup(parent, dentry, 0); if (res || d_really_is_positive(dentry)) --=20 2.55.0 From nobody Sat Jul 4 20:00:03 2026 Received: from ewsoutbound.kpnmail.nl (ewsoutbound.kpnmail.nl [195.121.94.167]) (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 C8D5E3769E3 for ; Sat, 4 Jul 2026 16:41:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.121.94.167 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183292; cv=none; b=X6h3XXNmOGaFsUdi6GyxDocjbWzFvqPMh374APLJ2o81yUVZGAZbX6SbGhC1vfH0j3NaxyDvxu6+EC3oWuLdSjlpGm62mGjo0PDRFw8nRL1k7tXDuL5ZuNJYwxPUUU+CLF3+KKeUKgT96BQ4Y/0bbMbAznUMMFg4Q3cwBlH6CfY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183292; c=relaxed/simple; bh=F7L38lCf/OpLKbcGbmxAtmdyJiNzy26f8F4/Mi1lfGc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=lgqBuupD7TblSzYjrphNsei78Umet2sH1M+5hp3xUHyQmvXHiNb7FqKJXbBlRA8oBINO8uR/LsjgB0RvO+2YSzcWd4iBjgOXcJgrdAweBVFP1m1cD0w1SkqC/2q7T2C/y5vNImw0VN2vZHYvHLpCP8y8N3cEKicNYnHhynf85tY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl; spf=pass smtp.mailfrom=xs4all.nl; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b=iC6h5PC2; arc=none smtp.client-ip=195.121.94.167 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b="iC6h5PC2" X-KPN-MessageId: 3108d7a5-77c7-11f1-b2a8-005056abbe64 Received: from smtp.kpnmail.nl (unknown [10.31.155.38]) by ewsoutbound.so.kpn.org (Halon) with ESMTPS id 3108d7a5-77c7-11f1-b2a8-005056abbe64; Sat, 04 Jul 2026 18:41:23 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xs4all.nl; s=xs4all01; h=mime-version:message-id:date:subject:to:from; bh=Z8MFVZ5dMSGAHtKQiZ/I8XoLMKdyCwvjMjkCAQX2JMY=; b=iC6h5PC2Y7ADllK1oOWYGR+eL/Z+EclxvIlk4pv1v6ZpuKyMaQ8tf29wPpd/DuE5qLoD+xUF6gAna 3bZ0aOFV9QR2vwcRC4N3+6ddLXc76mNW5cZSzhJOBPjNkAFgqftmmwIIcaPIo0OSxWP4GxmXjy5DUv yDV3HbVCZqYvP7eeTM2Pe3PDVkK3uw7jjWxYO+/NK4CpFSelXyBiT32CU/xA/BRs3OUsqhOu9/lpV3 GUGsp2w+fO2LkcCgHDLmNl48+785cEEiG2A2E6w/gYms/SZ9rMmIQjJrdrGCO4FD1XqbLXWB9cHfgd uoOoXqM1kULRqaK7m1Bq1nmi4MJsPHQ== X-KPN-MID: 33|I2dvfO+ITrRPdV0i96iQkC+05EFHfp88qjmYlW2ddw8JOm4oZxHMjxOxY0zQsZ3 fWrZfReCW0HtKskjAaW+EeQ== X-KPN-VerifiedSender: Yes X-CMASSUN: 33|xPZaDSW55ANp7bPayp2GZIhNxCRp7ahFopKFmkMZTmeiCyydCAFoA5t/B/Lexse cCXmmyxju8G4UbMQiLUek1g== Received: from daedalus.home (unknown [178.230.195.42]) by smtp.xs4all.nl (Halon) with ESMTPSA id 30c2c1cf-77c7-11f1-a998-005056abf0db; Sat, 04 Jul 2026 18:41:22 +0200 (CEST) From: Jori Koolstra To: Jeff Layton , Christian Brauner , Al Viro , Aleksa Sarai , NeilBrown , Amir Goldstein , Jan Kara Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Jori Koolstra Subject: [PATCH v3 11/14] vfs: refuse O_CREAT for directories through a dangling symlink Date: Sat, 4 Jul 2026 18:41:45 +0200 Message-ID: <20260704164149.3480051-12-jkoolstra@xs4all.nl> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260704164149.3480051-1-jkoolstra@xs4all.nl> References: <20260704164149.3480051-1-jkoolstra@xs4all.nl> 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" open(O_CREAT) without O_EXCL follows a trailing symlink and, when the symlink target does not exist, creates it. Refuse to create through a dangling symlink for directories. In lookup_open() a negative target reached with nd->depth > 0 was arrived at by following a trailing symlink; since the dentry is negative the symlink is dangling. Set create_error to -EEXIST in that case (matching the errorno returned by mkdir(2).) Reusing the existing create_error path strips O_CREAT for both the generic and ->atomic_open create paths and only reports the error when the target is actually negative. Thus opening an existing target through a symlink, interior symlinks, and O_EXCL (which never follows the trailing link) are all unaffected. Suggested-by: Christian Brauner (Amutable) Signed-off-by: Jori Koolstra --- fs/namei.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/namei.c b/fs/namei.c index 76916caa264d..030cac4fbe55 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4546,6 +4546,11 @@ static struct dentry *lookup_open(struct nameidata *= nd, struct file *file, dentry, mode, create_dir); else create_error =3D -EROFS; + /* Refuse to create a directory through a dangling (trailing) + * symlink. For regular files this has been allowed historically + * on O_CREAT without O_EXCL. */ + if (unlikely(nd->depth) && create_dir && !create_error) + create_error =3D -EEXIST; } if (create_error) open_flag &=3D ~O_CREAT; --=20 2.55.0 From nobody Sat Jul 4 20:00:03 2026 Received: from ewsoutbound.kpnmail.nl (ewsoutbound.kpnmail.nl [195.121.94.170]) (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 7E48F2BDC1C for ; Sat, 4 Jul 2026 16:41:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.121.94.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183293; cv=none; b=DzGUnefyKZcvkBpA8ihkIKD9WYUKeWo5OnZj7pem3TqP1eq+tFOFwZpmSnpTXBRgwEiiEIFH8liZQ8kZGDjA7YSbJ/IsZgy+h8gabhlEiDsy0DpaxB1v1AS9oyvPm/Jy6z10QXRgUPI8CJeQlXksFzTuxRTwpz1gVOFEFqg9aHA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183293; c=relaxed/simple; bh=eK7U1Qg0LBeGWFlE9e0UvutLv2qHcuuOfD+HYQ5fxPU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=mJ4k4P/C8WmdjTvjFlj9z1IxM4c4tPYsRdyEUL16/i5iL54dc1GENzN5U8QQofjq5K0PC1o26QoKy3vRjgggqPx9pFLGnOMPM4FOBnzlC/1JwdpT/tDbHi9PlvJI5YPkROQ2GHj0XpZZ8FUkLx6vcYulLxBeBnHURZ8ZHGSPiHQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl; spf=pass smtp.mailfrom=xs4all.nl; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b=U9O7vaeE; arc=none smtp.client-ip=195.121.94.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b="U9O7vaeE" X-KPN-MessageId: 31bfbfea-77c7-11f1-83b1-005056ab378f Received: from smtp.kpnmail.nl (unknown [10.31.155.38]) by ewsoutbound.so.kpn.org (Halon) with ESMTPS id 31bfbfea-77c7-11f1-83b1-005056ab378f; Sat, 04 Jul 2026 18:41:24 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xs4all.nl; s=xs4all01; h=mime-version:message-id:date:subject:to:from; bh=n0FHXxZp7MxLpvcDw6t+L4Y7Ef0wpS0xiHIgwSMJZ8E=; b=U9O7vaeERSiBoQLKp6AT8tYhbW5hAvu2Po1xJklpwMVpA9Gdb+YEyPL+1gOdrcnTbdlivqoZppM/1 ri/xKVRjB4u6Tuxpti8TgeSs+/Ui0+cy3PI8nUUJ8ZNhqMVItZkxgYm4UJ1FfsNtk5rXV7tYSwAHiI 4XdvjiBll1jTEZa8vF1p2jNsd/kKJtMU7R7uSqLuIQl4fpBYIbAgMTalvszG441l8thISkyYRM/hwG WYqyamli1o2PxFd7NcgVoCE1euV+F4l2Xymv4yJIo5lQKPhfCyQA1IshAUNu+HsKh0vvbmyX3xL/3k jcPgXXVBlAfLGeAzuSf776N/EV+SFgA== X-KPN-MID: 33|dWTzLLNLU8fklMYlGaWRfuiy0CPpz2FGx3KD1jzrsB7vTxyVMzblnUvohBLnDmJ CcjX9Pva7fOMJZwcF8KBBjZYvxcs1e4YBFcKF0ous+hU= X-KPN-VerifiedSender: Yes X-CMASSUN: 33|pmhI8G1IUD0krHKUA4txqN2nnAHUAYucBKFTggT0vv3h14OGz5fkWZouMlVRfG0 Mz6YKCfE2+HzdCqlwcTMT7Q== Received: from daedalus.home (unknown [178.230.195.42]) by smtp.xs4all.nl (Halon) with ESMTPSA id 317334d8-77c7-11f1-a998-005056abf0db; Sat, 04 Jul 2026 18:41:24 +0200 (CEST) From: Jori Koolstra To: Jeff Layton , Christian Brauner , Al Viro , Aleksa Sarai , NeilBrown , Amir Goldstein , Jan Kara Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Jori Koolstra Subject: [PATCH v3 12/14] vfs: short-circuit MAY_WRITE access for O_DIRECTORY opens Date: Sat, 4 Jul 2026 18:41:46 +0200 Message-ID: <20260704164149.3480051-13-jkoolstra@xs4all.nl> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260704164149.3480051-1-jkoolstra@xs4all.nl> References: <20260704164149.3480051-1-jkoolstra@xs4all.nl> 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" Requesting write access on a directory can never succeed. Rather than performing a path-walk to determine whether the target is actually a directory (-EISDIR) or not (-ENOTDIR), or does not exist (-ENOENT), etc., we short-circuit to -ENOTDIR. Currently O_WRONLY for directories is only blocked in may_open(), which happens after we have the inode for the target, so after any create via O_CREAT|O_DIRECTORY. The advantage of short-circuiting is that we don't have to add even more logic to lookup_open() to differentiate -EISDIR/-ENOTDIR. Also, for filesystems that define atomic_open(), handling this cannot even be done at the VFS level, as we can't know ahead of calling ->atomic_open() what the result of the lookup is. Suggested-by: Christian Brauner (Amutable) Signed-off-by: Jori Koolstra --- fs/open.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/open.c b/fs/open.c index 9121aece78bc..87e8864d9480 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1270,9 +1270,16 @@ inline int build_open_flags(const struct open_how *h= ow, struct open_flags *op) =20 op->intent =3D flags & O_PATH ? 0 : LOOKUP_OPEN; =20 + /* + * Requesting write access on a directory can never succeed. Rather + * than performing a path-walk to determine whether the target is + * actually a directory (-EISDIR) or not (-ENOTDIR), we short-circuit + * to -ENOTDIR. + */ + if ((flags & O_DIRECTORY) && !(flags & __O_TMPFILE) && (acc_mode & MAY_WR= ITE)) + return -ENOTDIR; + if (flags & O_CREAT) { - if ((flags & O_DIRECTORY) && (acc_mode & MAY_WRITE)) - return -EISDIR; op->intent |=3D LOOKUP_CREATE; if (flags & O_EXCL) { op->intent |=3D LOOKUP_EXCL; --=20 2.55.0 From nobody Sat Jul 4 20:00:03 2026 Received: from ewsoutbound.kpnmail.nl (ewsoutbound.kpnmail.nl [195.121.94.167]) (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 61E15374A19 for ; Sat, 4 Jul 2026 16:41:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.121.94.167 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183294; cv=none; b=VhTptDlsel9/xE09silZqlMRgrImmN/eFyvnbAHN7gr2e/hUdYJRMGMiWxV7Tg5DnCoclM8/NA4l+tEGnvHNwDEYOmkinXCelkiS9XC3D8mjO5Jb0SqJSf43QdqJ1Y6AM8AHappIwYkRAFjecr+fxtgYa8tkRQ5HkSWsIFDrnTQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183294; c=relaxed/simple; bh=SOi1hRb7i/SFIuu1TRnSgGyZ0B0sBigIfWhXiCknJJM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RyC1asjNW2+3vJxOblCTnciflcDPcJsPLIAWDCeJx1ldYxU9+iap2ZU2UgHhy41OxMjyhRIFsgl4XJFy32A6R+y6Y33TzvJvJ2W4T55ViomuEkl8AYf3Y/rk8cbu7ASRx4rjHEO8qwnAYAS8gXpZtAK6AB/boGtO/OUNoDM34Fc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl; spf=pass smtp.mailfrom=xs4all.nl; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b=oeT0pZtv; arc=none smtp.client-ip=195.121.94.167 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b="oeT0pZtv" X-KPN-MessageId: 32848853-77c7-11f1-b2a8-005056abbe64 Received: from smtp.kpnmail.nl (unknown [10.31.155.38]) by ewsoutbound.so.kpn.org (Halon) with ESMTPS id 32848853-77c7-11f1-b2a8-005056abbe64; Sat, 04 Jul 2026 18:41:25 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xs4all.nl; s=xs4all01; h=mime-version:message-id:date:subject:to:from; bh=hinhVazh+P2rhKxj4kWTtHx78BM5i9joWic785ap8RA=; b=oeT0pZtv6MhCLoOcbeNHpUgmTJEM9A54it1TgEgJGcUfd1lrtkIVeL9DmWtPerG96LuPZMZyeOB+O 3g9qQ4cnx1yyAogtkWfePkWUpUHe9x8wkc3br0H3xGO7eU6eKWvHrqiqxQa62+K4eNmvPZEtGagST4 AQBzc4L9gHmu51hfQv/5VZACTwdUFPVMBm+rqf4BwhYfwq+uYl5qXvm74VXnvfuE1fBz3NPreoTin9 DfZbgkG/ApAwAF5N7+z7GADKtf1HUj1dxSGQG4gjYcQq04gRp6z9dAjmgEMp6tD7KiK4DDA4yNWdD5 RT+Kj5iIuIhz7qE8tI5VanK/Kojc1fA== X-KPN-MID: 33|si4WScxBOPa1MumFaMYoYiniaFEl2EN7F+9vQ3AEyG0ERebg0wUp+dTjpFls8c7 AWJf7yAEWmCxMcfifgLlSMdZGPaBKIZd6Y7AkErxKEeQ= X-KPN-VerifiedSender: Yes X-CMASSUN: 33|PW0cytL/lrR9c9O26b/Ws/FDMU6om2VLG2Wm5TUPhyGX8Tl8y0S4uHBBEZLfwCq bee7qU7pI4igKbck7OVz+rg== Received: from daedalus.home (unknown [178.230.195.42]) by smtp.xs4all.nl (Halon) with ESMTPSA id 32377546-77c7-11f1-a998-005056abf0db; Sat, 04 Jul 2026 18:41:25 +0200 (CEST) From: Jori Koolstra To: Jeff Layton , Christian Brauner , Al Viro , Aleksa Sarai , NeilBrown , Amir Goldstein , Jan Kara Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Jori Koolstra Subject: [PATCH v3 13/14] selftest: fix headers in fclog.c Date: Sat, 4 Jul 2026 18:41:47 +0200 Message-ID: <20260704164149.3480051-14-jkoolstra@xs4all.nl> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260704164149.3480051-1-jkoolstra@xs4all.nl> References: <20260704164149.3480051-1-jkoolstra@xs4all.nl> 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" Signed-off-by: Jori Koolstra --- tools/testing/selftests/filesystems/fclog.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/testing/selftests/filesystems/fclog.c b/tools/testing/se= lftests/filesystems/fclog.c index 551c4a0f395a..593a5136e991 100644 --- a/tools/testing/selftests/filesystems/fclog.c +++ b/tools/testing/selftests/filesystems/fclog.c @@ -6,10 +6,8 @@ =20 #include #include +#include #include -#include -#include -#include #include #include =20 --=20 2.55.0 From nobody Sat Jul 4 20:00:03 2026 Received: from ewsoutbound.kpnmail.nl (ewsoutbound.kpnmail.nl [195.121.94.168]) (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 25BBF372B27 for ; Sat, 4 Jul 2026 16:41:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.121.94.168 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183293; cv=none; b=Z31mw6Bnxrds40OT4PWYMDn/hUXC8OJa/wfI2AGTzril9bgFy+Rz8JLoyD7uo2faoID5wJEXtRfCzMqK3sB7RwcveWf41cKQssj1znAzvY/MHvsAcvjzHBVQALFccJX2kxZPeaXCf81j7UnwPMP+ESbVJikxKwBzlXxy7ppOHRw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783183293; c=relaxed/simple; bh=7WcYN0QqKL3hZV52Zledf9EXObr6W8P312PPfzKea38=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=W1+iahIKEtULYTUJtBfpUxU1efFrmO9yn+ZWCjFM3MxYIEz1CLYx5y7g8xM9tNxlVz/0WKru1fS0CA/8R7KXd7pvvhm3WcJD1vprSLrAvLGviyiGK/Jb0RR12mNUoONbOAxXXsyqaNjmC64Zu+AeYjeUJuvKrEzq8caja4hVWHo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl; spf=pass smtp.mailfrom=xs4all.nl; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b=cguoF4tQ; arc=none smtp.client-ip=195.121.94.168 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b="cguoF4tQ" X-KPN-MessageId: 3327cb93-77c7-11f1-861c-005056aba152 Received: from smtp.kpnmail.nl (unknown [10.31.155.38]) by ewsoutbound.so.kpn.org (Halon) with ESMTPS id 3327cb93-77c7-11f1-861c-005056aba152; Sat, 04 Jul 2026 18:41:26 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xs4all.nl; s=xs4all01; h=mime-version:message-id:date:subject:to:from; bh=BYGCMz9KLG4xzelA7NUOrTl5E4m+DuNGsUhuNWvILWI=; b=cguoF4tQFRm9siQJBjQ8ybU9eRZwjAtSLMZ15ns8kAoF4K+m2EDNVsKYbJNUW/CUqnZKc2Z8BskKp iDE5H6ZA9GVsUgjz31qH6SFEUFCeiR1WRRNHuX0UOf9NC8U8fHRXpkh4FutU+7+/Je7GV2hVx0/PLN UsEe8YTMMtyg7UfMKITro5KmfxgCiSyj0Jd0fp442QPaIcHy+vLDym+qGXAIbCbMM9vKdtlgBPlpDa 9MsKfwoXVtRDBxDqo6o1jjYZU9Jei6sU5R4z3tohHdPcHQwu8DMjGlAleSHCZrJB2LWoXl1opND3is 1t21X7RgsOiLeivU1wGmK/hDwKNNDxA== X-KPN-MID: 33|6xrN2a1wQ+R1o42+dSSnwONgkJcDnh1Xtx2AbuQJttQIfsy9Di+LE3z1ZKAtQO2 XgA2aAy4sb/D/iuYjY6vVlH8tUQPycKRSETBz/kWDFxg= X-KPN-VerifiedSender: Yes X-CMASSUN: 33|H0d+/c9q7lfOlh7GZpWTeRdH5Ug05IQsrbK7TwxvkIKQEgSzQZYxNT/OITXI7J8 aVLf7AYdF5QmECwq8djuMSw== Received: from daedalus.home (unknown [178.230.195.42]) by smtp.xs4all.nl (Halon) with ESMTPSA id 32e81b67-77c7-11f1-a998-005056abf0db; Sat, 04 Jul 2026 18:41:26 +0200 (CEST) From: Jori Koolstra To: Jeff Layton , Christian Brauner , Al Viro , Aleksa Sarai , NeilBrown , Amir Goldstein , Jan Kara Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Jori Koolstra Subject: [PATCH v3 14/14] selftest: add tests for open*(O_CREAT|O_DIRECTORY) Date: Sat, 4 Jul 2026 18:41:48 +0200 Message-ID: <20260704164149.3480051-15-jkoolstra@xs4all.nl> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260704164149.3480051-1-jkoolstra@xs4all.nl> References: <20260704164149.3480051-1-jkoolstra@xs4all.nl> 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" Add some tests for the new valid O_CREAT|O_DIRECTORY flag combination for open*(2) to test compliance and to showcase its behaviour. Signed-off-by: Jori Koolstra --- .../testing/selftests/filesystems/.gitignore | 1 + tools/testing/selftests/filesystems/Makefile | 2 +- .../filesystems/open_o_creat_o_dir.c | 197 ++++++++++++++++++ 3 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/filesystems/open_o_creat_o_dir.c diff --git a/tools/testing/selftests/filesystems/.gitignore b/tools/testing= /selftests/filesystems/.gitignore index a78f894157de..d779a7945cf8 100644 --- a/tools/testing/selftests/filesystems/.gitignore +++ b/tools/testing/selftests/filesystems/.gitignore @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +open_o_creat_o_dir dnotify_test devpts_pts fclog diff --git a/tools/testing/selftests/filesystems/Makefile b/tools/testing/s= elftests/filesystems/Makefile index a7ec2ba2dd83..b60950f8b15c 100644 --- a/tools/testing/selftests/filesystems/Makefile +++ b/tools/testing/selftests/filesystems/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 =20 CFLAGS +=3D $(KHDR_INCLUDES) -TEST_GEN_PROGS :=3D devpts_pts file_stressor anon_inode_test kernfs_test f= clog +TEST_GEN_PROGS :=3D open_o_creat_o_dir devpts_pts file_stressor anon_inode= _test kernfs_test fclog TEST_GEN_PROGS +=3D idmapped_tmpfile TEST_GEN_PROGS_EXTENDED :=3D dnotify_test =20 diff --git a/tools/testing/selftests/filesystems/open_o_creat_o_dir.c b/too= ls/testing/selftests/filesystems/open_o_creat_o_dir.c new file mode 100644 index 000000000000..df3cdbae2c85 --- /dev/null +++ b/tools/testing/selftests/filesystems/open_o_creat_o_dir.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include + +#include "kselftest_harness.h" + +static inline int open_o_creat_o_dir(int dfd, const char *pathname, + mode_t mode, unsigned int flags) +{ + return syscall(__NR_openat, dfd, pathname, + flags | O_DIRECTORY | O_CREAT, mode); +} + +#define open_o_creat_o_dir_checked_flags(dfd, pathname, flags) ({ \ + struct stat __st; \ + int __fd =3D open_o_creat_o_dir(dfd, pathname, S_IRWXU, flags); \ + ASSERT_GE(__fd, 0); \ + ASSERT_EQ(fstat(__fd, &__st), 0); \ + EXPECT_TRUE(S_ISDIR(__st.st_mode)); \ + __fd; \ +}) + +#define open_o_creat_o_dir_checked(dfd, pathname) \ + open_o_creat_o_dir_checked_flags(dfd, pathname, 0) + +FIXTURE(open_o_creat_o_dir) { + char dirpath[PATH_MAX]; + int dfd; +}; + +FIXTURE_SETUP(open_o_creat_o_dir) +{ + strcpy(self->dirpath, "/tmp/open_o_creat_o_dir_test.XXXXXX"); + ASSERT_NE(mkdtemp(self->dirpath), NULL); + self->dfd =3D open(self->dirpath, O_DIRECTORY); + ASSERT_GE(self->dfd, 0); +} + +FIXTURE_TEARDOWN(open_o_creat_o_dir) +{ + close(self->dfd); + rmdir(self->dirpath); +} + +/* Does open_o_creat_o_dir return a fd at all? */ +TEST_F(open_o_creat_o_dir, returns_fd) +{ + int fd =3D open_o_creat_o_dir_checked(self->dfd, "newdir"); + EXPECT_EQ(close(fd), 0); + EXPECT_EQ(unlinkat(self->dfd, "newdir", AT_REMOVEDIR), 0); +} + +/* The fd must refer to the directory that was just created. */ +TEST_F(open_o_creat_o_dir, fd_is_created_dir) +{ + int fd; + struct stat st_via_fd, st_via_path; + char path[PATH_MAX]; + + fd =3D open_o_creat_o_dir_checked(self->dfd, "checkdir"); + + ASSERT_EQ(fstat(fd, &st_via_fd), 0); + + snprintf(path, sizeof(path), "%s/checkdir", self->dirpath); + ASSERT_EQ(stat(path, &st_via_path), 0); + + EXPECT_EQ(st_via_fd.st_ino, st_via_path.st_ino); + EXPECT_EQ(st_via_fd.st_dev, st_via_path.st_dev); + + EXPECT_EQ(close(fd), 0); + EXPECT_EQ(rmdir(path), 0); +} + +/* Missing parent component must fail with ENOENT. */ +TEST_F(open_o_creat_o_dir, enoent_missing_parent) +{ + EXPECT_EQ(open_o_creat_o_dir(self->dfd, "nonexistent/child", S_IRWXU, 0),= -1); + EXPECT_EQ(errno, ENOENT); +} + +/* An invalid dfd must fail with EBADF. */ +TEST_F(open_o_creat_o_dir, ebadf) +{ + EXPECT_EQ(open_o_creat_o_dir(-42, "badfdir", S_IRWXU, 0), -1); + EXPECT_EQ(errno, EBADF); +} + +/* A dfd that points to a file (not a directory) must fail with ENOTDIR. */ +TEST_F(open_o_creat_o_dir, enotdir_dfd) +{ + int file_fd; + + file_fd =3D openat(self->dfd, "file", + O_CREAT | O_WRONLY, S_IRWXU); + ASSERT_GE(file_fd, 0); + + EXPECT_EQ(open_o_creat_o_dir(file_fd, "subdir", S_IRWXU, 0), -1); + EXPECT_EQ(errno, ENOTDIR); + + EXPECT_EQ(close(file_fd), 0); + EXPECT_EQ(unlinkat(self->dfd, "file", 0), 0); +} + +/* + * O_EXCL together with O_CREAT|O_DIRECTORY must fail with EEXIST when + * the target directory already exists. + */ +TEST_F(open_o_creat_o_dir, o_excl_eexist) +{ + int fd; + + fd =3D open_o_creat_o_dir_checked_flags(self->dfd, "excldir", O_EXCL); + EXPECT_EQ(close(fd), 0); + + EXPECT_EQ(open_o_creat_o_dir(self->dfd, "excldir", S_IRWXU, O_EXCL), -1); + EXPECT_EQ(errno, EEXIST); + + EXPECT_EQ(unlinkat(self->dfd, "excldir", AT_REMOVEDIR), 0); +} + +/* + * O_CREAT|O_DIRECTORY on a path that already exists as a regular file + * must fail with ENOTDIR. + */ +TEST_F(open_o_creat_o_dir, existing_file_enotdir) +{ + int file_fd; + + file_fd =3D openat(self->dfd, "regfile", + O_CREAT | O_WRONLY, S_IRWXU); + ASSERT_GE(file_fd, 0); + EXPECT_EQ(close(file_fd), 0); + + EXPECT_EQ(open_o_creat_o_dir(self->dfd, "regfile", S_IRWXU, 0), -1); + EXPECT_EQ(errno, ENOTDIR); + + EXPECT_EQ(unlinkat(self->dfd, "regfile", 0), 0); +} + +/* + * O_CREAT|O_DIRECTORY combined with a writable access mode must be + * rejected: a directory cannot be opened for writing. + */ +TEST_F(open_o_creat_o_dir, rejects_writable_acc_mode) +{ + EXPECT_EQ(open_o_creat_o_dir(self->dfd, "rdwrdir", S_IRWXU, O_RDWR), -1); + EXPECT_EQ(errno, ENOTDIR); + /* Clean up if the kernel created the directory anyway. */ + unlinkat(self->dfd, "rdwrdir", AT_REMOVEDIR); +} + +/* + * openat(O_CREAT) with a trailing slash but without O_DIRECTORY + * must fail with EISDIR and must not create anything at the path. + */ +TEST_F(open_o_creat_o_dir, trailing_slash_no_o_dir) +{ + int fd; + struct stat st; + + fd =3D openat(self->dfd, "trailing/", O_CREAT | O_WRONLY, S_IRWXU); + EXPECT_EQ(fd, -1); + EXPECT_EQ(errno, EISDIR); + + EXPECT_EQ(fstatat(self->dfd, "trailing", &st, 0), -1); + EXPECT_EQ(errno, ENOENT); + + /* Best-effort cleanup in case the kernel left a file behind. */ + if (fd >=3D 0) + close(fd); + unlinkat(self->dfd, "trailing", 0); +} + +/* + * The returned fd must be usable as a dfd for further *at() calls. + */ +TEST_F(open_o_creat_o_dir, fd_usable_as_dfd) +{ + int parent_fd, child_fd; + char path[PATH_MAX]; + + parent_fd =3D open_o_creat_o_dir_checked(self->dfd, "parent"); + child_fd =3D open_o_creat_o_dir_checked(parent_fd, "child"); + + EXPECT_EQ(close(child_fd), 0); + EXPECT_EQ(close(parent_fd), 0); + + snprintf(path, sizeof(path), "%s/parent/child", self->dirpath); + EXPECT_EQ(rmdir(path), 0); + snprintf(path, sizeof(path), "%s/parent", self->dirpath); + EXPECT_EQ(rmdir(path), 0); +} + +TEST_HARNESS_MAIN --=20 2.55.0