From: Christian Brauner <brauner@kernel.org>
To: Jori Koolstra <jkoolstra@xs4all.nl>
Cc: Christian Brauner <brauner@kernel.org>,
Jeff Layton <jlayton@kernel.org>,
Al Viro <viro@zeniv.linux.org.uk>,
Aleksa Sarai <aleksa@amutable.com>, NeilBrown <neil@brown.name>,
Amir Goldstein <amir73il@gmail.com>, Jan Kara <jack@suse.cz>,
linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v3 09/14] vfs: add O_CREAT|O_DIRECTORY to open*(2)
Date: Sun, 12 Jul 2026 14:39:53 +0200 [thread overview]
Message-ID: <20260712-pantoffeln-erbgut-aufnahmen-8f4c2f8d2124@brauner> (raw)
In-Reply-To: <1944666535.644663.1783709759572@kpc.webmail.kpnmail.nl>
On 2026-07-10 20:55 +0200, Jori Koolstra wrote:
>
> > Op 07-07-2026 12:51 CEST schreef Christian Brauner <brauner@kernel.org>:
> >
> >
> > > 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
> >
> > Eh, but that still means someone could've raced you and actually created
> > that directory. When you bring in a shared filesysem state with joint
> > cleanup responsibilities things aren't easily solved by fstat()ing.
> >
>
> Right, but can't I check with fstat() if the object satisfies all properties
> I need, like owner, group, inode, type, etc.?
>
> If it does, then why care how it was created?
Because you might not want to delete something that another task with
the same credentials created. There's always going to be some race with
splitting it into two operations that matters in subtle ways.
> > > 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:
> >
> > I think this doesn't spell out why that's done: afaict, netfses can't
> > currently deal with O_CREAT | O_DIRECTORY without protocol extensions.
> > So that means you force it into a fallback mode where it manages to open
> > an existing directory but fails with ENOENT otherwise. This should carry
> > a brief comment why you force that type of fallback.
> >
> > Also, how do you deal with O_EXCL? If you strip O_CREAT but leave O_EXCL
> > depending on where exactly you do it, might this risk opening a
> > directory giving userspace the impression they exclusively created it?
> >
>
> I think we're fine, in do_open() we still have:
>
> if (open_flag & O_CREAT) {
> if ((open_flag & O_EXCL) && !(file->f_mode & FMODE_CREATED))
> return -EEXIST;
>
> and I am stripping the local variable O_CREAT bit only. Also, do_open() is
> only reached if we didn't have an -ENOENT return (lookup succeeded).
>
> So it's either -ENOENT if the dir does not exist or if it does -EEXIST
> (with O_EXCL set).
>
> Agree?
Sounds good. Make sure that there are O_EXCL tests, please.
next prev parent reply other threads:[~2026-07-12 12:40 UTC|newest]
Thread overview: 62+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-04 16:41 [PATCH v3 00/14] " Jori Koolstra
2026-07-04 16:41 ` [PATCH v3 01/14] fs/namei.c: use trailing_slashes() Jori Koolstra
2026-07-06 3:56 ` NeilBrown
2026-07-06 7:29 ` David Laight
2026-07-06 20:55 ` Jori Koolstra
2026-07-07 7:42 ` David Laight
2026-07-07 10:51 ` Christian Brauner
2026-07-04 16:41 ` [PATCH v3 02/14] vfs: move create error && negative dentry case in lookup_open() up Jori Koolstra
2026-07-04 16:41 ` [PATCH v3 03/14] vfs: prepare vfs_creat|mkdir_no_perm for reuse in lookup_open() Jori Koolstra
2026-07-07 10:51 ` Christian Brauner
2026-07-04 16:41 ` [PATCH v3 04/14] vfs: call audit_inode_child() in lookup_open() on failure too Jori Koolstra
2026-07-06 5:33 ` NeilBrown
2026-07-06 21:20 ` Jori Koolstra
2026-07-06 23:02 ` NeilBrown
2026-07-07 10:51 ` Christian Brauner
2026-07-04 16:41 ` [PATCH v3 05/14] fs/namei.c: update docstring of atomic_open() Jori Koolstra
2026-07-06 5:36 ` NeilBrown
2026-07-07 10:51 ` Christian Brauner
2026-07-04 16:41 ` [PATCH v3 06/14] vfs: lookup_open(): move setting FMODE_CREATED down Jori Koolstra
2026-07-04 16:41 ` [PATCH v3 07/14] vfs: move ->create check in lookup_open() to before try_break_deleg() Jori Koolstra
2026-07-06 5:37 ` NeilBrown
2026-07-07 10:51 ` Christian Brauner
2026-07-04 16:41 ` [PATCH v3 08/14] vfs: lookup_open(): use vfs_create_no_perm() Jori Koolstra
2026-07-06 5:38 ` NeilBrown
2026-07-07 10:51 ` Christian Brauner
2026-07-04 16:41 ` [PATCH v3 09/14] vfs: add O_CREAT|O_DIRECTORY to open*(2) Jori Koolstra
2026-07-06 5:46 ` NeilBrown
2026-07-07 8:35 ` Pedro Falcato
2026-07-07 10:04 ` Christian Brauner
2026-07-07 13:04 ` Pedro Falcato
2026-07-07 13:30 ` Christian Brauner
2026-07-13 9:54 ` Christoph Hellwig
2026-07-13 21:35 ` NeilBrown
2026-07-14 5:10 ` Christoph Hellwig
2026-07-14 10:50 ` Jori Koolstra
2026-07-14 10:46 ` Jori Koolstra
2026-07-07 10:06 ` Jori Koolstra
2026-07-07 13:55 ` Pedro Falcato
2026-07-07 14:18 ` Jori Koolstra
2026-07-08 15:39 ` Pedro Falcato
2026-07-09 6:24 ` Christoph Hellwig
2026-07-09 10:51 ` Jori Koolstra
2026-07-09 14:21 ` Christian Brauner
2026-07-09 14:34 ` Jori Koolstra
2026-07-13 9:50 ` Christoph Hellwig
2026-07-07 10:51 ` Christian Brauner
2026-07-10 18:55 ` Jori Koolstra
2026-07-12 12:39 ` Christian Brauner [this message]
2026-07-04 16:41 ` [PATCH v3 10/14] vfs: move O_IS_MKDIR check from lookup_open() into individual filesystems Jori Koolstra
2026-07-06 5:50 ` NeilBrown
2026-07-07 10:51 ` Christian Brauner
2026-07-04 16:41 ` [PATCH v3 11/14] vfs: refuse O_CREAT for directories through a dangling symlink Jori Koolstra
2026-07-06 5:51 ` NeilBrown
2026-07-04 16:41 ` [PATCH v3 12/14] vfs: short-circuit MAY_WRITE access for O_DIRECTORY opens Jori Koolstra
2026-07-06 5:51 ` NeilBrown
2026-07-04 16:41 ` [PATCH v3 13/14] selftest: fix headers in fclog.c Jori Koolstra
2026-07-06 5:57 ` NeilBrown
2026-07-06 21:07 ` Jori Koolstra
2026-07-07 10:51 ` Christian Brauner
2026-07-04 16:41 ` [PATCH v3 14/14] selftest: add tests for open*(O_CREAT|O_DIRECTORY) Jori Koolstra
2026-07-07 10:51 ` Christian Brauner
2026-07-07 10:51 ` [PATCH v3 00/14] vfs: add O_CREAT|O_DIRECTORY to open*(2) Christian Brauner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260712-pantoffeln-erbgut-aufnahmen-8f4c2f8d2124@brauner \
--to=brauner@kernel.org \
--cc=aleksa@amutable.com \
--cc=amir73il@gmail.com \
--cc=jack@suse.cz \
--cc=jkoolstra@xs4all.nl \
--cc=jlayton@kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=neil@brown.name \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox