Re: [PATCH v3 04/14] vfs: call audit_inode_child() in lookup_open() on failure too

From: Christian Brauner

Date: Tue Jul 07 2026 - 07:05:53 EST


> 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.

So what does audit_inode_child() do now? Does it only audit on failure
or unconditionally? It seems that to be useful you want both? In any
case, it reads like this might not belong into this series and should be
split into a separate fix if it's broken.

> 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 <jkoolstra@xxxxxxxxx>
>
> 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 = (void *) -1UL;
> struct inode *dir = path->dentry->d_inode;
> + bool failed_create = false;
> int error;
>
> file->__f_path.dentry = 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 != file->f_path.dentry)) {
> + // finish_open() called
> + struct dentry *opened = file->f_path.dentry;
> + if (unlikely(opened != dentry)) {
> dput(dentry);
> - dentry = dget(file->f_path.dentry);
> + dentry = dget(opened);
> }
> - } else if (WARN_ON(file->f_path.dentry == DENTRY_NOT_SET)) {
> - error = -EIO;
> - } else {
> - if (file->f_path.dentry) {
> + } else if (likely(file->f_path.dentry != DENTRY_NOT_SET)) {
> + // finish_no_open() called
> + struct dentry *replaced = file->f_path.dentry;
> + if (replaced) {
> dput(dentry);
> - dentry = file->f_path.dentry;
> + dentry = replaced;
> }
> if (unlikely(d_is_negative(dentry)))
> error = -ENOENT;


> + if (error && create_error) // should have created, but errored before
> + failed_create = true;


> + } else {


> + const char *fsname = dentry->d_sb->s_type->name;
> + WARN(1, "%s: ->atomic_open() left file->f_path.dentry unset!\n", fsname);
> + error = -EIO;
> }
> }
>
> if (error) {
> + if (failed_create)
> + audit_inode_child(dir, dentry, AUDIT_TYPE_CHILD_CREATE);


> dput(dentry);
> dentry = ERR_PTR(error);
> } else {
> @@ -4462,7 +4473,7 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
> dentry = NULL;
> }
> if (dentry->d_inode) {
> - /* Cached positive dentry: will open in f_op->open */
> + /* Cached positive dentry: will open in do_open(). */
> return dentry;
> }
>
> @@ -4496,7 +4507,7 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
> if (dir_inode->i_op->atomic_open) {
> if (nd->flags & LOOKUP_DIRECTORY)
> open_flag |= O_DIRECTORY;
> - dentry = atomic_open(&nd->path, dentry, file, open_flag, mode);
> + dentry = atomic_open(&nd->path, dentry, file, open_flag, mode, create_error);
> if (unlikely(create_error) && dentry == ERR_PTR(-ENOENT))
> dentry = ERR_PTR(create_error);
> return dentry;
> @@ -4515,33 +4526,37 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
> dentry = res;
> }
> }
> + if (dentry->d_inode || !(op->open_flag & O_CREAT)) {

d_really_is_positive()

--
Christian Brauner <brauner@xxxxxxxxxx>