| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/10/16 22:28 | flow | patch-triage |
6mArgs:null Results: {
"EnableConfigs": [
"CGROUPS"
],
"FocusSymbols": [
"cgroup_mkdir",
"kernfs_get_inode_prealloc",
"kernfs_iop_mkdir",
"lookup_open",
"may_o_create",
"vfs_mkdir"
],
"KMSANReasoning": "The patch series implements support for atomic directory creation and opening via openat(..., O_CREAT | O_DIRECTORY | O_EXCL, ...). It updates VFS path resolution and directory creation helpers (lookup_open, finish_lookup, vfs_mkdir_no_perm, may_o_create), adjusts kernfs, cgroup, and rdtgroup mkdir hooks to return struct kernfs_node *, and adds the SB_I_NO_ATOMIC_MKDIR flag for filesystems that do not support this feature.\n\nNo uninitialized memory risks are introduced:\n1. No kernel structures or data buffers are copied to user space (the syscall only returns a file descriptor or negative errno).\n2. All new/modified stack and heap variables in the modified functions are explicitly initialized before use.\n3. Inode management in kernfs uses standard inode allocation (new_inode) and initialization (kernfs_init_inode).\n4. Any potential issues with reference counting (dentry/inode put/get), locking (mutexes and rwsems), or pointer handling are under the purview of KASAN and LOCKDEP rather than KMSAN.\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false,
"Reasoning": "The patch series introduces atomic directory creation and opening via openat/openat2 with flags O_CREAT | O_DIRECTORY | O_EXCL. It modifies core VFS path resolution and file creation logic in fs/namei.c and fs/open.c, adds new assertions (WARN_ON_ONCE in may_o_create and lookup_open), and completely rewrites kernfs_iop_mkdir in fs/kernfs/dir.c to preallocate and instantiate inodes with d_splice_alias, updating cgroup_mkdir accordingly. This code is fully reachable in standard virtualized environments via standard syscalls.",
"WorthFuzzing": true
} |
| 1/1 | 2026/10/16 22:28 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 4e3de42dfe78758a71522b9b86a2de3000cb64d7\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Oct 16 22:28:09 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/arch/alpha/include/uapi/asm/errno.h b/arch/alpha/include/uapi/asm/errno.h\nindex 1a99f38813c79..0384f69989748 100644\n--- a/arch/alpha/include/uapi/asm/errno.h\n+++ b/arch/alpha/include/uapi/asm/errno.h\n@@ -19,7 +19,7 @@\n #define\tENOPROTOOPT\t42\t/* Protocol not available */\n #define\tEPROTONOSUPPORT\t43\t/* Protocol not supported */\n #define\tESOCKTNOSUPPORT\t44\t/* Socket type not supported */\n-#define\tEOPNOTSUPP\t45\t/* Operation not supported on transport endpoint */\n+#define\tEOPNOTSUPP\t45\t/* Operation not supported */\n #define\tEPFNOSUPPORT\t46\t/* Protocol family not supported */\n #define\tEAFNOSUPPORT\t47\t/* Address family not supported by protocol */\n #define\tEADDRINUSE\t48\t/* Address already in use */\ndiff --git a/arch/mips/include/uapi/asm/errno.h b/arch/mips/include/uapi/asm/errno.h\nindex 1835a50b69cef..2b6813715d1e5 100644\n--- a/arch/mips/include/uapi/asm/errno.h\n+++ b/arch/mips/include/uapi/asm/errno.h\n@@ -75,7 +75,7 @@\n #define ENOPROTOOPT\t99\t/* Protocol not available */\n #define EPROTONOSUPPORT 120\t/* Protocol not supported */\n #define ESOCKTNOSUPPORT 121\t/* Socket type not supported */\n-#define EOPNOTSUPP\t122\t/* Operation not supported on transport endpoint */\n+#define EOPNOTSUPP\t122\t/* Operation not supported */\n #define EPFNOSUPPORT\t123\t/* Protocol family not supported */\n #define EAFNOSUPPORT\t124\t/* Address family not supported by protocol */\n #define EADDRINUSE\t125\t/* Address already in use */\ndiff --git a/arch/parisc/include/uapi/asm/errno.h b/arch/parisc/include/uapi/asm/errno.h\nindex 93194fbb0a80d..74bd65d791457 100644\n--- a/arch/parisc/include/uapi/asm/errno.h\n+++ b/arch/parisc/include/uapi/asm/errno.h\n@@ -84,7 +84,7 @@\n #define\tENOPROTOOPT\t220\t/* Protocol not available */\n #define\tEPROTONOSUPPORT\t221\t/* Protocol not supported */\n #define\tESOCKTNOSUPPORT\t222\t/* Socket type not supported */\n-#define\tEOPNOTSUPP\t223\t/* Operation not supported on transport endpoint */\n+#define\tEOPNOTSUPP\t223\t/* Operation not supported */\n #define\tEPFNOSUPPORT\t224\t/* Protocol family not supported */\n #define\tEAFNOSUPPORT\t225\t/* Address family not supported by protocol */\n #define\tEADDRINUSE\t226\t/* Address already in use */\ndiff --git a/arch/sparc/include/uapi/asm/errno.h b/arch/sparc/include/uapi/asm/errno.h\nindex 71940ec9130b4..699f648e1a356 100644\n--- a/arch/sparc/include/uapi/asm/errno.h\n+++ b/arch/sparc/include/uapi/asm/errno.h\n@@ -16,7 +16,7 @@\n #define\tENOPROTOOPT\t42\t/* Protocol not available */\n #define\tEPROTONOSUPPORT\t43\t/* Protocol not supported */\n #define\tESOCKTNOSUPPORT\t44\t/* Socket type not supported */\n-#define\tEOPNOTSUPP\t45\t/* Op not supported on transport endpoint */\n+#define\tEOPNOTSUPP\t45\t/* Operation not supported */\n #define\tEPFNOSUPPORT\t46\t/* Protocol family not supported */\n #define\tEAFNOSUPPORT\t47\t/* Address family not supported by protocol */\n #define\tEADDRINUSE\t48\t/* Address already in use */\ndiff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c\nindex 94d6b02c221b8..a3377bc34ac0e 100644\n--- a/fs/9p/vfs_super.c\n+++ b/fs/9p/vfs_super.c\n@@ -40,6 +40,7 @@ static int v9fs_fill_super(struct super_block *sb)\n \tsb-\u003es_blocksize_bits = fls(v9ses-\u003emaxdata - 1);\n \tsb-\u003es_blocksize = 1 \u003c\u003c sb-\u003es_blocksize_bits;\n \tsb-\u003es_magic = V9FS_MAGIC;\n+\tsb-\u003es_iflags |= SB_I_NO_ATOMIC_MKDIR;\n \tif (v9fs_proto_dotl(v9ses)) {\n \t\tsb-\u003es_op = \u0026v9fs_super_ops_dotl;\n \t\tif (!(v9ses-\u003eflags \u0026 V9FS_NO_XATTR))\ndiff --git a/fs/ceph/super.c b/fs/ceph/super.c\nindex 72935f665f11c..fa1cf1f4f57d6 100644\n--- a/fs/ceph/super.c\n+++ b/fs/ceph/super.c\n@@ -1226,6 +1226,7 @@ static int ceph_set_super(struct super_block *s, struct fs_context *fc)\n \tfsc-\u003emax_file_size = 1ULL \u003c\u003c 40; /* temp value until we get mdsmap */\n \n \ts-\u003es_op = \u0026ceph_super_ops;\n+\ts-\u003es_iflags |= SB_I_NO_ATOMIC_MKDIR;\n \tset_default_d_op(s, \u0026ceph_dentry_ops);\n \ts-\u003es_export_op = \u0026ceph_export_ops;\n \ndiff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c\nindex 5f37cddb956fb..8ed6076d50afd 100644\n--- a/fs/ecryptfs/main.c\n+++ b/fs/ecryptfs/main.c\n@@ -477,6 +477,7 @@ static int ecryptfs_get_tree(struct fs_context *fc)\n \t/* -\u003ekill_sb() will take care of sbi after that point */\n \tsbi = NULL;\n \ts-\u003es_op = \u0026ecryptfs_sops;\n+\ts-\u003es_iflags |= SB_I_NO_ATOMIC_MKDIR;\n \ts-\u003es_xattr = ecryptfs_xattr_handlers;\n \tset_default_d_op(s, \u0026ecryptfs_dops);\n \ndiff --git a/fs/fuse/inode.c b/fs/fuse/inode.c\nindex e9552be3637bc..cf766c6feea43 100644\n--- a/fs/fuse/inode.c\n+++ b/fs/fuse/inode.c\n@@ -1608,6 +1608,7 @@ static void fuse_sb_defaults(struct super_block *sb)\n \tsb-\u003es_iflags |= SB_I_IMA_UNVERIFIABLE_SIGNATURE;\n \tsb-\u003es_iflags |= SB_I_NOIDMAP;\n \tsb-\u003es_iflags |= SB_I_NO_DATA_INTEGRITY;\n+\tsb-\u003es_iflags |= SB_I_NO_ATOMIC_MKDIR;\n \tif (sb-\u003es_user_ns != \u0026init_user_ns)\n \t\tsb-\u003es_iflags |= SB_I_UNTRUSTED_MOUNTER;\n \tsb-\u003es_flags \u0026= ~(SB_NOSEC | SB_I_VERSION);\ndiff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c\nindex 718e0da7dfce0..695835819a5d5 100644\n--- a/fs/gfs2/ops_fstype.c\n+++ b/fs/gfs2/ops_fstype.c\n@@ -1139,6 +1139,7 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc)\n \tsb-\u003es_flags |= SB_NOSEC;\n \tsb-\u003es_magic = GFS2_MAGIC;\n \tsb-\u003es_op = \u0026gfs2_super_ops;\n+\tsb-\u003es_iflags |= SB_I_NO_ATOMIC_MKDIR;\n \n \tset_default_d_op(sb, \u0026gfs2_dops);\n \tsb-\u003es_export_op = \u0026gfs2_export_ops;\ndiff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c\nindex 82bbaeb326aab..42b3810a04bea 100644\n--- a/fs/kernfs/dir.c\n+++ b/fs/kernfs/dir.c\n@@ -1294,18 +1294,40 @@ static struct dentry *kernfs_iop_mkdir(struct mnt_idmap *idmap,\n {\n \tstruct kernfs_node *parent = dir-\u003ei_private;\n \tstruct kernfs_syscall_ops *scops = kernfs_root(parent)-\u003esyscall_ops;\n-\tint ret;\n+\tstruct kernfs_node *kn;\n+\tstruct inode *inode;\n \n \tif (!scops || !scops-\u003emkdir)\n \t\treturn ERR_PTR(-EPERM);\n \n-\tif (!kernfs_get_active(parent))\n+\tinode = new_inode(dir-\u003ei_sb);\n+\tif (!inode)\n+\t\treturn ERR_PTR(-ENOMEM);\n+\n+\tif (!kernfs_get_active(parent)) {\n+\t\tiput(inode);\n \t\treturn ERR_PTR(-ENODEV);\n+\t}\n \n-\tret = scops-\u003emkdir(parent, dentry-\u003ed_name.name, mode);\n+\tkn = scops-\u003emkdir(parent, dentry-\u003ed_name.name, mode);\n \n \tkernfs_put_active(parent);\n-\treturn ERR_PTR(ret);\n+\tif (IS_ERR(kn)) {\n+\t\tiput(inode);\n+\t\treturn ERR_CAST(kn);\n+\t}\n+\n+\tinode = kernfs_get_inode_prealloc(inode, kn);\n+\tkernfs_put(kn);\n+\n+\t/*\n+\t * We may race with kernfs_fh_to_dentry() which calls d_obtain_alias(),\n+\t * so the inode may already have a disconnected alias. Use\n+\t * d_splice_alias() to move it into place, which does not accept\n+\t * hashed dentries, so drop the negative dentry first.\n+\t */\n+\td_drop(dentry);\n+\treturn d_splice_alias(inode, dentry);\n }\n \n static int kernfs_iop_rmdir(struct inode *dir, struct dentry *dentry)\ndiff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c\nindex abb286bc3474b..a254090364926 100644\n--- a/fs/kernfs/inode.c\n+++ b/fs/kernfs/inode.c\n@@ -204,7 +204,8 @@ static void kernfs_init_inode(struct kernfs_node *kn, struct inode *inode)\n \tinode-\u003ei_generation = kernfs_gen(kn);\n \n \tset_default_inode_attr(inode, kn-\u003emode);\n-\tkernfs_refresh_inode(kn, inode);\n+\tscoped_guard(rwsem_read, \u0026kernfs_root(kn)-\u003ekernfs_iattr_rwsem)\n+\t\tkernfs_refresh_inode(kn, inode);\n \n \t/* initialize inode according to type */\n \tswitch (kernfs_type(kn)) {\n@@ -254,6 +255,40 @@ struct inode *kernfs_get_inode(struct super_block *sb, struct kernfs_node *kn)\n \treturn inode;\n }\n \n+/**\n+ *\tkernfs_get_inode_prealloc - get inode for kernfs_node\n+ *\t@prealloc_inode: inode allocated with new_inode() on the target superblock\n+ *\t@kn: kernfs_node to get the inode for\n+ *\n+ *\tLike kernfs_get_inode(), but cannot fail. @prealloc_inode is\n+ *\tinitialized for @kn, unless @kn already has an inode on the super\n+ *\tblock, in which case @prealloc_inode is released and the existing inode\n+ *\tis returned instead.\n+ *\n+ *\tLocking:\n+ *\tKernel thread context (may sleep).\n+ *\n+ *\tReturn:\n+ *\tPointer to the inode of @kn.\n+ */\n+struct inode *kernfs_get_inode_prealloc(struct inode *prealloc_inode,\n+\t\t\t\t\tstruct kernfs_node *kn)\n+{\n+\tstruct inode *old;\n+\n+\tprealloc_inode-\u003ei_ino = kernfs_ino(kn);\n+\twhile (insert_inode_locked(prealloc_inode)) {\n+\t\told = ilookup(prealloc_inode-\u003ei_sb, prealloc_inode-\u003ei_ino);\n+\t\tif (old) {\n+\t\t\tiput(prealloc_inode);\n+\t\t\treturn old;\n+\t\t}\n+\t}\n+\n+\tkernfs_init_inode(kn, prealloc_inode);\n+\treturn prealloc_inode;\n+}\n+\n /*\n * The kernfs_node serves as both an inode and a directory entry for\n * kernfs. To prevent the kernfs inode numbers from being freed\ndiff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h\nindex aa784b540b361..e4273f104a24d 100644\n--- a/fs/kernfs/kernfs-internal.h\n+++ b/fs/kernfs/kernfs-internal.h\n@@ -170,6 +170,8 @@ extern struct kmem_cache *kernfs_node_cache, *kernfs_iattrs_cache;\n * inode.c\n */\n extern const struct xattr_handler * const kernfs_xattr_handlers[];\n+struct inode *kernfs_get_inode_prealloc(struct inode *inode,\n+\t\t\t\t\tstruct kernfs_node *kn);\n void kernfs_evict_inode(struct inode *inode);\n int kernfs_iop_permission(struct mnt_idmap *idmap,\n \t\t\t struct inode *inode, int mask);\ndiff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c\nindex a57399021c8b0..202a5ca2bd341 100644\n--- a/fs/kernfs/mount.c\n+++ b/fs/kernfs/mount.c\n@@ -283,7 +283,6 @@ struct dentry *kernfs_node_dentry(struct kernfs_node *kn,\n static int kernfs_fill_super(struct super_block *sb, struct kernfs_fs_context *kfc)\n {\n \tstruct kernfs_super_info *info = kernfs_info(sb);\n-\tstruct kernfs_root *kf_root = kfc-\u003eroot;\n \tstruct inode *inode;\n \tstruct dentry *root;\n \n@@ -304,9 +303,7 @@ static int kernfs_fill_super(struct super_block *sb, struct kernfs_fs_context *k\n \tsb-\u003es_shrink-\u003eseeks = 0;\n \n \t/* get root inode, initialize and unlock it */\n-\tdown_read(\u0026kf_root-\u003ekernfs_rwsem);\n \tinode = kernfs_get_inode(sb, info-\u003eroot-\u003ekn);\n-\tup_read(\u0026kf_root-\u003ekernfs_rwsem);\n \tif (!inode) {\n \t\tpr_debug(\"kernfs: could not get root inode\\n\");\n \t\treturn -ENOMEM;\ndiff --git a/fs/namei.c b/fs/namei.c\nindex c87c14ee1a252..f21cd1028a8a3 100644\n--- a/fs/namei.c\n+++ b/fs/namei.c\n@@ -1889,12 +1889,34 @@ static struct dentry *lookup_fast(struct nameidata *nd)\n \treturn dentry;\n }\n \n+/*\n+ * If @dentry is in-lookup, look it up in @dir through -\u003elookup(). Returns\n+ * the dentry to use from now on: @dentry itself, or the dentry or ERR_PTR()\n+ * that -\u003elookup() returned instead, in which case @dentry is dropped.\n+ */\n+static struct dentry *finish_lookup(struct inode *dir, struct dentry *dentry,\n+\t\t\t\tunsigned int flags)\n+{\n+\tstruct dentry *res;\n+\n+\tif (!d_in_lookup(dentry))\n+\t\treturn dentry;\n+\n+\tres = dir-\u003ei_op-\u003elookup(dir, dentry, flags);\n+\td_lookup_done(dentry);\n+\tif (unlikely(res)) {\n+\t\tdput(dentry);\n+\t\treturn res;\n+\t}\n+\treturn dentry;\n+}\n+\n /* Fast lookup failed, do it the slow way */\n static struct dentry *__lookup_slow(const struct qstr *name,\n \t\t\t\t struct dentry *dir,\n \t\t\t\t unsigned int flags)\n {\n-\tstruct dentry *dentry, *old;\n+\tstruct dentry *dentry;\n \tstruct inode *inode = dir-\u003ed_inode;\n \n \t/* Don't go there if it's already dead */\n@@ -1913,17 +1935,10 @@ static struct dentry *__lookup_slow(const struct qstr *name,\n \t\t\t\tgoto again;\n \t\t\t}\n \t\t\tdput(dentry);\n-\t\t\tdentry = ERR_PTR(error);\n-\t\t}\n-\t} else {\n-\t\told = inode-\u003ei_op-\u003elookup(inode, dentry, flags);\n-\t\td_lookup_done(dentry);\n-\t\tif (unlikely(old)) {\n-\t\t\tdput(dentry);\n-\t\t\tdentry = old;\n+\t\t\treturn ERR_PTR(error);\n \t\t}\n \t}\n-\treturn dentry;\n+\treturn finish_lookup(inode, dentry, flags);\n }\n \n static noinline struct dentry *lookup_slow(const struct qstr *name,\n@@ -2810,9 +2825,16 @@ static const char *path_init(struct nameidata *nd, unsigned flags)\n \treturn s;\n }\n \n+static inline bool trailing_slashes(const struct qstr *last)\n+{\n+\t/* last-\u003elen is set by hash_name() to the length of the current\n+\t * component -\u003ename, terminating with '/' or a NUL character. */\n+\treturn (bool)last-\u003ename[last-\u003elen];\n+}\n+\n static inline const char *lookup_last(struct nameidata *nd)\n {\n-\tif (nd-\u003elast_type == LAST_NORM \u0026\u0026 nd-\u003elast.name[nd-\u003elast.len])\n+\tif (nd-\u003elast_type == LAST_NORM \u0026\u0026 trailing_slashes(\u0026nd-\u003elast))\n \t\tnd-\u003eflags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;\n \n \treturn walk_component(nd, WALK_TRAILING);\n@@ -4188,6 +4210,24 @@ static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap,\n \treturn mode;\n }\n \n+static inline\n+int vfs_create_no_perm(struct mnt_idmap *idmap, struct dentry *dentry,\n+\t\t umode_t mode, struct delegated_inode *di)\n+{\n+\tstruct inode *dir = d_inode(dentry-\u003ed_parent);\n+\tint error;\n+\n+\terror = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, di);\n+\tif (error)\n+\t\treturn error;\n+\n+\terror = dir-\u003ei_op-\u003ecreate(idmap, dir, dentry, mode);\n+\tif (!error)\n+\t\tfsnotify_create(dir, dentry);\n+\n+\treturn error;\n+}\n+\n /**\n * vfs_create - create new file\n * @idmap:\tidmap of the mount the inode was found from\n@@ -4214,19 +4254,14 @@ int vfs_create(struct mnt_idmap *idmap, struct dentry *dentry, umode_t mode,\n \t\treturn error;\n \n \tif (!dir-\u003ei_op-\u003ecreate)\n-\t\treturn -EACCES;\t/* shouldn't it be ENOSYS? */\n+\t\treturn -EOPNOTSUPP;\n \n \tmode = vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG);\n \terror = security_inode_create(dir, dentry, mode);\n \tif (error)\n \t\treturn error;\n-\terror = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, di);\n-\tif (error)\n-\t\treturn error;\n-\terror = dir-\u003ei_op-\u003ecreate(idmap, dir, dentry, mode);\n-\tif (!error)\n-\t\tfsnotify_create(dir, dentry);\n-\treturn error;\n+\n+\treturn vfs_create_no_perm(idmap, dentry, mode, di);\n }\n EXPORT_SYMBOL(vfs_create);\n \n@@ -4343,21 +4378,43 @@ static inline int open_to_namei_flags(int flag)\n \n static int may_o_create(struct mnt_idmap *idmap,\n \t\t\tconst struct path *dir, struct dentry *dentry,\n-\t\t\tumode_t mode)\n+\t\t\tint open_flag, umode_t mode)\n {\n-\tint error = security_path_mknod(dir, dentry, mode, 0);\n+\tstruct inode *dir_inode = dir-\u003edentry-\u003ed_inode;\n+\tbool create_dir = O_IS_MKDIR(open_flag);\n+\tint error;\n+\n+\tWARN_ON_ONCE(create_dir \u0026\u0026 !S_ISDIR(mode));\n+\n+\tif (create_dir)\n+\t\terror = security_path_mkdir(dir, dentry, mode);\n+\telse\n+\t\terror = security_path_mknod(dir, dentry, mode, 0);\n \tif (error)\n \t\treturn error;\n \n \tif (!fsuidgid_has_mapping(dir-\u003edentry-\u003ed_sb, idmap))\n \t\treturn -EOVERFLOW;\n \n-\terror = inode_permission(idmap, dir-\u003edentry-\u003ed_inode,\n-\t\t\t\t MAY_WRITE | MAY_EXEC);\n+\terror = inode_permission(idmap, dir_inode, MAY_WRITE | MAY_EXEC);\n \tif (error)\n \t\treturn error;\n \n-\treturn security_inode_create(dir-\u003edentry-\u003ed_inode, dentry, mode);\n+\tif (create_dir)\n+\t\terror = security_inode_mkdir(dir_inode, dentry, mode);\n+\telse\n+\t\terror = security_inode_create(dir_inode, dentry, mode);\n+\n+\treturn error;\n+}\n+\n+static inline umode_t o_create_mode(struct mnt_idmap *idmap,\n+\t\tconst struct inode *dir, int open_flag, umode_t mode)\n+{\n+\tif (O_IS_MKDIR(open_flag))\n+\t\treturn vfs_prepare_mode(idmap, dir, mode, S_IRWXUGO | S_ISVTX, S_IFDIR);\n+\telse\n+\t\treturn vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG);\n }\n \n /**\n@@ -4393,8 +4450,10 @@ static struct dentry *atomic_open(const struct path *path, struct dentry *dentry\n \n \tfile-\u003e__f_path.dentry = DENTRY_NOT_SET;\n \tfile-\u003e__f_path.mnt = path-\u003emnt;\n+\n \terror = dir_inode-\u003ei_op-\u003eatomic_open(dir_inode, dentry, file,\n-\t\t\t\t open_to_namei_flags(open_flag), mode);\n+\t\t\t\t\t open_to_namei_flags(open_flag), mode);\n+\n \td_lookup_done(dentry);\n \n \tif (!error) {\n@@ -4438,11 +4497,24 @@ static struct dentry *atomic_open(const struct path *path, struct dentry *dentry\n \t\t\terror = create_error;\n \t\t}\n \t\tdput(dentry);\n-\t\tdentry = ERR_PTR(error);\n+\t\treturn ERR_PTR(error);\n \t}\n+\n+\tif (file-\u003ef_mode \u0026 FMODE_CREATED) {\n+\t\tif (d_is_dir(dentry))\n+\t\t\tfsnotify_mkdir(dir_inode, dentry);\n+\t\telse\n+\t\t\tfsnotify_create(dir_inode, dentry);\n+\t}\n+\tif (file-\u003ef_mode \u0026 FMODE_OPENED)\n+\t\tfsnotify_open(file);\n+\n \treturn dentry;\n }\n \n+static inline\n+struct dentry *vfs_mkdir_no_perm(struct mnt_idmap *, struct inode *, struct dentry *,\n+\t\t\t\t umode_t, struct delegated_inode *);\n /*\n * Look up and maybe create and open the last component.\n *\n@@ -4464,6 +4536,7 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,\n \tstruct mnt_idmap *idmap;\n \tstruct dentry *dir = nd-\u003epath.dentry;\n \tstruct inode *dir_inode = dir-\u003ed_inode;\n+\tbool create_dir = O_IS_MKDIR(op-\u003eopen_flag);\n \tint open_flag;\n \tstruct dentry *dentry;\n \tint error, create_error;\n@@ -4476,6 +4549,9 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,\n \tmode = op-\u003emode;\n \tcreate_error = 0;\n \n+\tif (create_dir \u0026\u0026 (dir_inode-\u003ei_sb-\u003es_iflags \u0026 SB_I_NO_ATOMIC_MKDIR))\n+\t\treturn ERR_PTR(-EOPNOTSUPP);\n+\n \tif (open_flag \u0026 (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {\n \t\tgot_write = !mnt_want_write(nd-\u003epath.mnt);\n \t\t/*\n@@ -4484,7 +4560,7 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,\n \t\t */\n \t}\n \tif (open_flag \u0026 O_CREAT)\n-\t\tinode_lock(dir_inode);\n+\t\tinode_lock_nested(dir_inode, I_MUTEX_PARENT);\n \telse\n \t\tinode_lock_shared(dir_inode);\n \n@@ -4536,10 +4612,10 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,\n \tif (open_flag \u0026 O_CREAT) {\n \t\tif (open_flag \u0026 O_EXCL)\n \t\t\topen_flag \u0026= ~O_TRUNC;\n-\t\tmode = vfs_prepare_mode(idmap, dir_inode, mode, mode, mode);\n+\t\tmode = o_create_mode(idmap, dir_inode, open_flag, mode);\n \t\tif (likely(got_write))\n \t\t\tcreate_error = may_o_create(idmap, \u0026nd-\u003epath,\n-\t\t\t\t\t\t dentry, mode);\n+\t\t\t\t\t\t dentry, open_flag, mode);\n \t\telse\n \t\t\tcreate_error = -EROFS;\n \t}\n@@ -4553,19 +4629,10 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,\n \t\tgoto out;\n \t}\n \n-\tif (d_in_lookup(dentry)) {\n-\t\tstruct dentry *res = dir_inode-\u003ei_op-\u003elookup(dir_inode, dentry,\n-\t\t\t\t\t\t\t nd-\u003eflags);\n-\t\td_lookup_done(dentry);\n-\t\tif (unlikely(res)) {\n-\t\t\tif (IS_ERR(res)) {\n-\t\t\t\terror = PTR_ERR(res);\n-\t\t\t\tgoto out_dput;\n-\t\t\t}\n-\t\t\tdput(dentry);\n-\t\t\tdentry = res;\n-\t\t}\n-\t}\n+\tdentry = finish_lookup(dir_inode, dentry, nd-\u003eflags);\n+\tif (IS_ERR(dentry))\n+\t\tgoto out;\n+\n \tif (dentry-\u003ed_inode || !(op-\u003eopen_flag \u0026 O_CREAT)) {\n \t\t/*\n \t\t * No need to create a file. If lookup returned a positive\n@@ -4583,26 +4650,33 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,\n \t\tgoto out_dput;\n \t}\n \n-\terror = try_break_deleg(dir_inode, LEASE_BREAK_DIR_CREATE, \u0026delegated_inode);\n-\tif (error)\n-\t\tgoto out_dput;\n-\n-\tfile-\u003ef_mode |= FMODE_CREATED;\n-\tif (!dir_inode-\u003ei_op-\u003ecreate) {\n-\t\terror = -EACCES;\n+\tif ((create_dir \u0026\u0026 !dir_inode-\u003ei_op-\u003emkdir)\n+\t\t|| (!create_dir \u0026\u0026 !dir_inode-\u003ei_op-\u003ecreate)) {\n+\t\terror = -EOPNOTSUPP;\n \t\tgoto out_dput;\n \t}\n \n-\terror = dir_inode-\u003ei_op-\u003ecreate(idmap, dir_inode, dentry, mode);\n+\tif (create_dir) {\n+\t\tstruct dentry *res = vfs_mkdir_no_perm(idmap, dir_inode, dentry, mode,\n+\t\t\t\t\t\t \u0026delegated_inode);\n+\t\terror = PTR_ERR_OR_ZERO(res);\n+\t\tif (!error) {\n+\t\t\tdentry = res;\n+\t\t\t/*\n+\t\t\t * Filesystems whose -\u003emkdir() may leave the dentry\n+\t\t\t * negative must set SB_I_NO_ATOMIC_MKDIR.\n+\t\t\t */\n+\t\t\tif (WARN_ON_ONCE(d_is_negative(dentry)))\n+\t\t\t\terror = -EIO;\n+\t\t}\n+\t} else {\n+\t\terror = vfs_create_no_perm(idmap, dentry, mode, \u0026delegated_inode);\n+\t}\n \tif (error)\n \t\tgoto out_dput;\n+\n+\tfile-\u003ef_mode |= FMODE_CREATED;\n out:\n-\tif (!IS_ERR(dentry)) {\n-\t\tif (file-\u003ef_mode \u0026 FMODE_CREATED)\n-\t\t\tfsnotify_create(dir_inode, dentry);\n-\t\tif (file-\u003ef_mode \u0026 FMODE_OPENED)\n-\t\t\tfsnotify_open(file);\n-\t}\n \tif ((open_flag \u0026 O_CREAT) || create_error)\n \t\tinode_unlock(dir_inode);\n \telse\n@@ -4724,17 +4798,12 @@ struct file *vfs_lookup_open(struct path *parent, struct qstr *last,\n }\n EXPORT_SYMBOL_FOR_MODULES(vfs_lookup_open, \"nfsd\");\n \n-static inline bool trailing_slashes(struct nameidata *nd)\n-{\n-\treturn (bool)nd-\u003elast.name[nd-\u003elast.len];\n-}\n-\n static struct dentry *lookup_fast_for_open(struct nameidata *nd, int open_flag)\n {\n \tstruct dentry *dentry;\n \n \tif (open_flag \u0026 O_CREAT) {\n-\t\tif (trailing_slashes(nd))\n+\t\tif (trailing_slashes(\u0026nd-\u003elast) \u0026\u0026 !(open_flag \u0026 O_DIRECTORY))\n \t\t\treturn ERR_PTR(-EISDIR);\n \n \t\t/* Don't bother on an O_EXCL create */\n@@ -4742,7 +4811,7 @@ static struct dentry *lookup_fast_for_open(struct nameidata *nd, int open_flag)\n \t\t\treturn NULL;\n \t}\n \n-\tif (trailing_slashes(nd))\n+\tif (trailing_slashes(\u0026nd-\u003elast))\n \t\tnd-\u003eflags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;\n \n \tdentry = lookup_fast(nd);\n@@ -4772,6 +4841,13 @@ static const char *open_last_lookups(struct nameidata *nd,\n \tif (nd-\u003elast_type != LAST_NORM) {\n \t\tif (nd-\u003edepth)\n \t\t\tput_link(nd);\n+\t\t/*\n+\t\t * Return -EOPNOTSUPP consistently, not -EEXIST, for paths\n+\t\t * ending in . or .., as lookup_open() does for other names.\n+\t\t */\n+\t\tif (O_IS_MKDIR(open_flag) \u0026\u0026\n+\t\t (nd-\u003epath.dentry-\u003ed_sb-\u003es_iflags \u0026 SB_I_NO_ATOMIC_MKDIR))\n+\t\t\treturn ERR_PTR(-EOPNOTSUPP);\n \t\treturn handle_dots(nd, nd-\u003elast_type);\n \t}\n \n@@ -4835,7 +4911,7 @@ static int do_open(struct nameidata *nd,\n \tif (open_flag \u0026 O_CREAT) {\n \t\tif ((open_flag \u0026 O_EXCL) \u0026\u0026 !(file-\u003ef_mode \u0026 FMODE_CREATED))\n \t\t\treturn -EEXIST;\n-\t\tif (d_is_dir(nd-\u003epath.dentry))\n+\t\tif (!(open_flag \u0026 O_DIRECTORY) \u0026\u0026 d_is_dir(nd-\u003epath.dentry))\n \t\t\treturn -EISDIR;\n \t\terror = may_create_in_sticky(idmap, nd,\n \t\t\t\t\t d_backing_inode(nd-\u003epath.dentry));\n@@ -5116,7 +5192,7 @@ static struct dentry *filename_create(int dfd, struct filename *name,\n \t * Do the final lookup. Suppress 'create' if there is a trailing\n \t * '/', and a directory wasn't requested.\n \t */\n-\tif (last.name[last.len] \u0026\u0026 !want_dir)\n+\tif (trailing_slashes(\u0026last) \u0026\u0026 !want_dir)\n \t\tcreate_flags \u0026= ~LOOKUP_CREATE;\n \tdentry = start_dirop(path-\u003edentry, \u0026last, reval_flag | create_flags);\n \tif (IS_ERR(dentry))\n@@ -5174,7 +5250,7 @@ inline struct dentry *start_creating_user_path(\n EXPORT_SYMBOL(start_creating_user_path);\n \n /**\n- * dentry_create - Create and open a file\n+ * dentry_create - Create and open a regular file\n * @path: path to create\n * @flags: O\\_ flags\n * @mode: mode bits for new file\n@@ -5211,7 +5287,7 @@ struct file *dentry_create(struct path *path, int flags, umode_t mode,\n \t\tpath-\u003edentry = dir;\n \t\tmode = vfs_prepare_mode(idmap, dir_inode, mode, S_IALLUGO, S_IFREG);\n \n-\t\tcreate_error = may_o_create(idmap, path, dentry, mode);\n+\t\tcreate_error = may_o_create(idmap, path, dentry, flags, mode);\n \t\tif (create_error)\n \t\t\tflags \u0026= ~O_CREAT;\n \n@@ -5227,13 +5303,6 @@ struct file *dentry_create(struct path *path, int flags, umode_t mode,\n \t\t\t/* Drop the extra reference */\n \t\t\tdput(orig_dentry);\n \n-\t\tif (!error) {\n-\t\t\tif (file-\u003ef_mode \u0026 FMODE_CREATED)\n-\t\t\t\tfsnotify_create(dir-\u003ed_inode, dentry);\n-\t\t\tif (file-\u003ef_mode \u0026 FMODE_OPENED)\n-\t\t\t\tfsnotify_open(file);\n-\t\t}\n-\n \t\tpath-\u003edentry = dentry;\n \n \t} else {\n@@ -5385,6 +5454,33 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d\n \treturn filename_mknodat(AT_FDCWD, name, mode, dev);\n }\n \n+static inline\n+struct dentry *vfs_mkdir_no_perm(struct mnt_idmap *idmap, struct inode *dir,\n+\t\t\t\t struct dentry *dentry, umode_t mode,\n+\t\t\t\t struct delegated_inode *di)\n+{\n+\tunsigned max_links = dir-\u003ei_sb-\u003es_max_links;\n+\tstruct dentry *de;\n+\tint error;\n+\n+\tif (max_links \u0026\u0026 dir-\u003ei_nlink \u003e= max_links)\n+\t\treturn ERR_PTR(-EMLINK);\n+\n+\terror = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, di);\n+\tif (error)\n+\t\treturn ERR_PTR(error);\n+\n+\tde = dir-\u003ei_op-\u003emkdir(idmap, dir, dentry, mode);\n+\tif (IS_ERR(de))\n+\t\treturn de;\n+\tif (de) {\n+\t\tdput(dentry);\n+\t\tdentry = de;\n+\t}\n+\tfsnotify_mkdir(dir, dentry);\n+\treturn dentry;\n+}\n+\n /**\n * vfs_mkdir - create directory returning correct dentry if possible\n * @idmap:\t\tidmap of the mount the inode was found from\n@@ -5412,14 +5508,13 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,\n \t\t\t struct delegated_inode *delegated_inode)\n {\n \tint error;\n-\tunsigned max_links = dir-\u003ei_sb-\u003es_max_links;\n \tstruct dentry *de;\n \n \terror = may_create_dentry(idmap, dir, dentry);\n \tif (error)\n \t\tgoto err;\n \n-\terror = -EPERM;\n+\terror = -EOPNOTSUPP;\n \tif (!dir-\u003ei_op-\u003emkdir)\n \t\tgoto err;\n \n@@ -5428,24 +5523,12 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,\n \tif (error)\n \t\tgoto err;\n \n-\terror = -EMLINK;\n-\tif (max_links \u0026\u0026 dir-\u003ei_nlink \u003e= max_links)\n+\tde = vfs_mkdir_no_perm(idmap, dir, dentry, mode, delegated_inode);\n+\tif (IS_ERR(de)) {\n+\t\terror = PTR_ERR(de);\n \t\tgoto err;\n-\n-\terror = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, delegated_inode);\n-\tif (error)\n-\t\tgoto err;\n-\n-\tde = dir-\u003ei_op-\u003emkdir(idmap, dir, dentry, mode);\n-\terror = PTR_ERR(de);\n-\tif (IS_ERR(de))\n-\t\tgoto err;\n-\tif (de) {\n-\t\tdput(dentry);\n-\t\tdentry = de;\n \t}\n-\tfsnotify_mkdir(dir, dentry);\n-\treturn dentry;\n+\treturn de;\n \n err:\n \tend_creating(dentry);\n@@ -5732,7 +5815,7 @@ int filename_unlinkat(int dfd, struct filename *name)\n \t\tgoto exit_drop_write;\n \n \t/* Why not before? Because we want correct error value */\n-\tif (unlikely(last.name[last.len])) {\n+\tif (unlikely(trailing_slashes(\u0026last))) {\n \t\tif (d_is_dir(dentry))\n \t\t\terror = -EISDIR;\n \t\telse\n@@ -6334,16 +6417,16 @@ int filename_renameat2(int olddfd, struct filename *from,\n \tif (flags \u0026 RENAME_EXCHANGE) {\n \t\tif (!d_is_dir(rd.new_dentry)) {\n \t\t\terror = -ENOTDIR;\n-\t\t\tif (new_last.name[new_last.len])\n+\t\t\tif (trailing_slashes(\u0026new_last))\n \t\t\t\tgoto exit_unlock;\n \t\t}\n \t}\n \t/* unless the source is a directory trailing slashes give -ENOTDIR */\n \tif (!d_is_dir(rd.old_dentry)) {\n \t\terror = -ENOTDIR;\n-\t\tif (old_last.name[old_last.len])\n+\t\tif (trailing_slashes(\u0026old_last))\n \t\t\tgoto exit_unlock;\n-\t\tif (!(flags \u0026 RENAME_EXCHANGE) \u0026\u0026 new_last.name[new_last.len])\n+\t\tif (!(flags \u0026 RENAME_EXCHANGE) \u0026\u0026 trailing_slashes(\u0026new_last))\n \t\t\tgoto exit_unlock;\n \t}\n \ndiff --git a/fs/nfs/super.c b/fs/nfs/super.c\nindex cb19f1540d984..f4452b630f52b 100644\n--- a/fs/nfs/super.c\n+++ b/fs/nfs/super.c\n@@ -1077,6 +1077,7 @@ static void nfs_fill_super(struct super_block *sb, struct nfs_fs_context *ctx)\n \tsb-\u003es_blocksize = 0;\n \tsb-\u003es_xattr = server-\u003enfs_client-\u003ecl_nfs_mod-\u003exattr;\n \tsb-\u003es_op = server-\u003enfs_client-\u003ecl_nfs_mod-\u003esops;\n+\tsb-\u003es_iflags |= SB_I_NO_ATOMIC_MKDIR;\n \tif (server-\u003ebsize)\n \t\tsb-\u003es_blocksize =\n \t\t\tnfs_block_size(server-\u003ebsize, \u0026sb-\u003es_blocksize_bits);\ndiff --git a/fs/open.c b/fs/open.c\nindex 6b1c14e684a93..e8cb9153ea572 100644\n--- a/fs/open.c\n+++ b/fs/open.c\n@@ -1239,29 +1239,30 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op)\n \tif (WILL_CREATE(flags)) {\n \t\tif (how-\u003emode \u0026 ~S_IALLUGO)\n \t\t\treturn -EINVAL;\n-\t\top-\u003emode = how-\u003emode | S_IFREG;\n+\t\tif (O_IS_MKDIR(flags))\n+\t\t\top-\u003emode = how-\u003emode | S_IFDIR;\n+\t\telse\n+\t\t\top-\u003emode = how-\u003emode | S_IFREG;\n \t} else {\n \t\tif (how-\u003emode != 0)\n \t\t\treturn -EINVAL;\n \t\top-\u003emode = 0;\n \t}\n \n-\t/*\n-\t * Block bugs where O_DIRECTORY | O_CREAT created regular files.\n-\t * Note, that blocking O_DIRECTORY | O_CREAT here also protects\n-\t * O_TMPFILE below which requires O_DIRECTORY being raised.\n-\t */\n-\tif ((flags \u0026 (O_DIRECTORY | O_CREAT)) == (O_DIRECTORY | O_CREAT))\n-\t\treturn -EINVAL;\n-\n \t/* Now handle the creative implementation of O_TMPFILE. */\n \tif (flags \u0026 __O_TMPFILE) {\n \t\t/*\n \t\t * In order to ensure programs get explicit errors when trying\n \t\t * to use O_TMPFILE on old kernels we enforce that O_DIRECTORY\n-\t\t * is raised alongside __O_TMPFILE.\n+\t\t * is raised alongside __O_TMPFILE, but without O_CREAT. The\n+\t\t * reason for disallowing O_CREAT|O_TMPFILE is that\n+\t\t * O_DIRECTORY|O_CREAT used to work and created a regular file\n+\t\t * if nothing existed at the open path. Hence, allowing the\n+\t\t * combination would have caused O_CREAT|O_TMPFILE to create a\n+\t\t * regular (non-temporary) file on old kernels, while the caller\n+\t\t * would believe they created an actual O_TMPFILE.\n \t\t */\n-\t\tif (!(flags \u0026 O_DIRECTORY))\n+\t\tif (!(flags \u0026 O_DIRECTORY) || (flags \u0026 O_CREAT))\n \t\t\treturn -EINVAL;\n \t\tif (!(acc_mode \u0026 MAY_WRITE))\n \t\t\treturn -EINVAL;\n@@ -1318,12 +1319,29 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op)\n \n \top-\u003eintent = flags \u0026 O_PATH ? 0 : LOOKUP_OPEN;\n \n+\t/*\n+\t * Requesting write access on a directory can never succeed. Rather\n+\t * than performing a path-walk to determine whether the target is\n+\t * actually a directory (-EISDIR) or not (-ENOTDIR), we short-circuit\n+\t * to -ENOTDIR.\n+\t */\n+\tif ((flags \u0026 O_DIRECTORY) \u0026\u0026 !(flags \u0026 __O_TMPFILE) \u0026\u0026 (acc_mode \u0026 MAY_WRITE))\n+\t\treturn -ENOTDIR;\n+\n \tif (flags \u0026 O_CREAT) {\n-\t\top-\u003eintent |= LOOKUP_CREATE;\n+\t\tif (flags \u0026 O_DIRECTORY) {\n+\t\t\t/*\n+\t\t\t * O_CREAT|O_DIRECTORY is provided to prevent TOCTOUs, so\n+\t\t\t * it makes little sense to allow it without O_EXCL.\n+\t\t\t */\n+\t\t\tif (!(flags \u0026 O_EXCL))\n+\t\t\t\treturn -EINVAL;\n+\t\t}\n \t\tif (flags \u0026 O_EXCL) {\n \t\t\top-\u003eintent |= LOOKUP_EXCL;\n \t\t\tflags |= O_NOFOLLOW;\n \t\t}\n+\t\top-\u003eintent |= LOOKUP_CREATE;\n \t}\n \n \tif (flags \u0026 O_DIRECTORY)\ndiff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c\nindex 5dcbb0a964e81..d7b13b63c6adf 100644\n--- a/fs/resctrl/rdtgroup.c\n+++ b/fs/resctrl/rdtgroup.c\n@@ -4042,15 +4042,16 @@ static void mkdir_rdt_prepare_clean(struct rdtgroup *rgrp)\n * and monitor group(ctrl_mon). This is a resource group\n * to monitor a subset of tasks and cpus in its parent ctrl_mon group.\n */\n-static int rdtgroup_mkdir_mon(struct kernfs_node *parent_kn,\n-\t\t\t const char *name, umode_t mode)\n+static struct kernfs_node *rdtgroup_mkdir_mon(struct kernfs_node *parent_kn,\n+\t\t\t\t\t const char *name, umode_t mode)\n {\n \tstruct rdtgroup *rdtgrp, *prgrp;\n+\tstruct kernfs_node *kn;\n \tint ret;\n \n \tret = mkdir_rdt_prepare(parent_kn, name, mode, RDTMON_GROUP, \u0026rdtgrp);\n \tif (ret)\n-\t\treturn ret;\n+\t\treturn ERR_PTR(ret);\n \n \tprgrp = rdtgrp-\u003emon.parent;\n \trdtgrp-\u003eclosid = prgrp-\u003eclosid;\n@@ -4069,17 +4070,22 @@ static int rdtgroup_mkdir_mon(struct kernfs_node *parent_kn,\n \t */\n \tlist_add_tail(\u0026rdtgrp-\u003emon.crdtgrp_list, \u0026prgrp-\u003emon.crdtgrp_list);\n \n+\tkn = rdtgrp-\u003ekn;\n+\tkernfs_get(kn);\n+\trdtgroup_kn_unlock(parent_kn);\n+\treturn kn;\n+\n out_unlock:\n \trdtgroup_kn_unlock(parent_kn);\n-\treturn ret;\n+\treturn ERR_PTR(ret);\n }\n \n /*\n * These are rdtgroups created under the root directory. Can be used\n * to allocate and monitor resources.\n */\n-static int rdtgroup_mkdir_ctrl_mon(struct kernfs_node *parent_kn,\n-\t\t\t\t const char *name, umode_t mode)\n+static struct kernfs_node *rdtgroup_mkdir_ctrl_mon(struct kernfs_node *parent_kn,\n+\t\t\t\t\t\t const char *name, umode_t mode)\n {\n \tstruct rdtgroup *rdtgrp;\n \tstruct kernfs_node *kn;\n@@ -4088,7 +4094,7 @@ static int rdtgroup_mkdir_ctrl_mon(struct kernfs_node *parent_kn,\n \n \tret = mkdir_rdt_prepare(parent_kn, name, mode, RDTCTRL_GROUP, \u0026rdtgrp);\n \tif (ret)\n-\t\treturn ret;\n+\t\treturn ERR_PTR(ret);\n \n \tkn = rdtgrp-\u003ekn;\n \tret = closid_alloc();\n@@ -4127,7 +4133,9 @@ static int rdtgroup_mkdir_ctrl_mon(struct kernfs_node *parent_kn,\n \t\t\trdtgrp-\u003emba_mbps_event = mba_mbps_default_event;\n \t}\n \n-\tgoto out_unlock;\n+\tkernfs_get(kn);\n+\trdtgroup_kn_unlock(parent_kn);\n+\treturn kn;\n \n out_del_list:\n \tlist_del(\u0026rdtgrp-\u003erdtgroup_list);\n@@ -4137,17 +4145,16 @@ static int rdtgroup_mkdir_ctrl_mon(struct kernfs_node *parent_kn,\n \tclosid_free(closid);\n out_common_fail:\n \tmkdir_rdt_prepare_clean(rdtgrp);\n-out_unlock:\n \trdtgroup_kn_unlock(parent_kn);\n-\treturn ret;\n+\treturn ERR_PTR(ret);\n }\n \n-static int rdtgroup_mkdir(struct kernfs_node *parent_kn, const char *name,\n-\t\t\t umode_t mode)\n+static struct kernfs_node *rdtgroup_mkdir(struct kernfs_node *parent_kn,\n+\t\t\t\t\t const char *name, umode_t mode)\n {\n \t/* Do not accept '\\n' to avoid unparsable situation. */\n \tif (strchr(name, '\\n'))\n-\t\treturn -EINVAL;\n+\t\treturn ERR_PTR(-EINVAL);\n \n \t/*\n \t * If the parent directory is the root directory and RDT\n@@ -4161,7 +4168,7 @@ static int rdtgroup_mkdir(struct kernfs_node *parent_kn, const char *name,\n \tif (resctrl_arch_mon_capable())\n \t\treturn rdtgroup_mkdir_mon(parent_kn, name, mode);\n \n-\treturn -EPERM;\n+\treturn ERR_PTR(-EPERM);\n }\n \n static int rdtgroup_rmdir_mon(struct rdtgroup *rdtgrp, cpumask_var_t tmpmask)\ndiff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c\nindex 98b610b2a1142..93a5ead81c827 100644\n--- a/fs/smb/client/cifsfs.c\n+++ b/fs/smb/client/cifsfs.c\n@@ -247,6 +247,7 @@ cifs_read_super(struct super_block *sb)\n \n \tsb-\u003es_magic = CIFS_SUPER_MAGIC;\n \tsb-\u003es_op = \u0026cifs_super_ops;\n+\tsb-\u003es_iflags |= SB_I_NO_ATOMIC_MKDIR;\n \tsb-\u003es_xattr = cifs_xattr_handlers;\n \trc = super_setup_bdi(sb);\n \tif (rc)\ndiff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c\nindex f3d6188a3b7b2..0f64310c30952 100644\n--- a/fs/tracefs/inode.c\n+++ b/fs/tracefs/inode.c\n@@ -471,6 +471,7 @@ static int tracefs_fill_super(struct super_block *sb, struct fs_context *fc)\n \t\treturn err;\n \n \tsb-\u003es_op = \u0026tracefs_super_operations;\n+\tsb-\u003es_iflags |= SB_I_NO_ATOMIC_MKDIR;\n \ttracefs_apply_options(sb, false);\n \tset_default_d_op(sb, \u0026tracefs_dentry_operations);\n \ndiff --git a/fs/vboxsf/super.c b/fs/vboxsf/super.c\nindex a61fbab51d370..190b76f04d4f8 100644\n--- a/fs/vboxsf/super.c\n+++ b/fs/vboxsf/super.c\n@@ -196,6 +196,7 @@ static int vboxsf_fill_super(struct super_block *sb, struct fs_context *fc)\n \tsb-\u003es_blocksize = 1024;\n \tsb-\u003es_maxbytes = MAX_LFS_FILESIZE;\n \tsb-\u003es_op = \u0026vboxsf_super_ops;\n+\tsb-\u003es_iflags |= SB_I_NO_ATOMIC_MKDIR;\n \tset_default_d_op(sb, \u0026vboxsf_dentry_ops);\n \n \tiroot = iget_locked(sb, 0);\ndiff --git a/include/linux/fcntl.h b/include/linux/fcntl.h\nindex 6ad6b9e7a226a..204e16bbe2634 100644\n--- a/include/linux/fcntl.h\n+++ b/include/linux/fcntl.h\n@@ -30,6 +30,12 @@\n */\n #define __O_REGULAR\t\t(1 \u003c\u003c 30)\n \n+#define O_MKDIR_MASK\t(O_CREAT | O_DIRECTORY)\n+static inline bool O_IS_MKDIR(unsigned int flags)\n+{\n+\treturn (flags \u0026 O_MKDIR_MASK) == O_MKDIR_MASK;\n+}\n+\n /* List of all valid flags for the how-\u003eresolve argument: */\n #define VALID_RESOLVE_FLAGS \\\n \t(RESOLVE_NO_XDEV | RESOLVE_NO_MAGICLINKS | RESOLVE_NO_SYMLINKS | \\\ndiff --git a/include/linux/fs/super_types.h b/include/linux/fs/super_types.h\nindex ecd96aeb1cee7..521b00c47d0aa 100644\n--- a/include/linux/fs/super_types.h\n+++ b/include/linux/fs/super_types.h\n@@ -352,5 +352,6 @@ struct super_block {\n #define SB_I_NOIDMAP\t0x00002000\t/* No idmapped mounts on this superblock */\n #define SB_I_ALLOW_HSM\t0x00004000\t/* Allow HSM events on this superblock */\n #define SB_I_NO_DATA_INTEGRITY\t0x00008000 /* fs cannot guarantee data persistence on sync */\n+#define SB_I_NO_ATOMIC_MKDIR\t0x00010000 /* no O_CREAT|O_DIRECTORY on this fs */\n \n #endif /* _LINUX_FS_SUPER_TYPES_H */\ndiff --git a/include/linux/kernfs.h b/include/linux/kernfs.h\nindex 351a5101c8628..0aced50043405 100644\n--- a/include/linux/kernfs.h\n+++ b/include/linux/kernfs.h\n@@ -241,13 +241,14 @@ struct kernfs_node {\n * syscalls. These optional callbacks are invoked on the matching syscalls\n * and can perform any kernfs operations which don't necessarily have to be\n * the exact operation requested. An active reference is held for each\n- * kernfs_node parameter.\n+ * kernfs_node parameter. @mkdir returns the kernfs_node of the new\n+ * directory with a reference held, or an ERR_PTR() on failure.\n */\n struct kernfs_syscall_ops {\n \tint (*show_options)(struct seq_file *sf, struct kernfs_root *root);\n \n-\tint (*mkdir)(struct kernfs_node *parent, const char *name,\n-\t\t umode_t mode);\n+\tstruct kernfs_node *(*mkdir)(struct kernfs_node *parent,\n+\t\t\t\t const char *name, umode_t mode);\n \tint (*rmdir)(struct kernfs_node *kn);\n \tint (*rename)(struct kernfs_node *kn, struct kernfs_node *new_parent,\n \t\t const char *new_name);\ndiff --git a/include/uapi/asm-generic/errno.h b/include/uapi/asm-generic/errno.h\nindex bd78e69e0a43f..c84ebf89c8b6e 100644\n--- a/include/uapi/asm-generic/errno.h\n+++ b/include/uapi/asm-generic/errno.h\n@@ -76,7 +76,7 @@\n #define\tENOPROTOOPT\t92\t/* Protocol not available */\n #define\tEPROTONOSUPPORT\t93\t/* Protocol not supported */\n #define\tESOCKTNOSUPPORT\t94\t/* Socket type not supported */\n-#define\tEOPNOTSUPP\t95\t/* Operation not supported on transport endpoint */\n+#define\tEOPNOTSUPP\t95\t/* Operation not supported */\n #define\tEPFNOSUPPORT\t96\t/* Protocol family not supported */\n #define\tEAFNOSUPPORT\t97\t/* Address family not supported by protocol */\n #define\tEADDRINUSE\t98\t/* Address already in use */\ndiff --git a/kernel/cgroup/cgroup-internal.h b/kernel/cgroup/cgroup-internal.h\nindex 58797123b752f..eca8d30885391 100644\n--- a/kernel/cgroup/cgroup-internal.h\n+++ b/kernel/cgroup/cgroup-internal.h\n@@ -256,7 +256,8 @@ void cgroup_procs_write_finish(struct task_struct *task,\n \n void cgroup_lock_and_drain_offline(struct cgroup *cgrp);\n \n-int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, umode_t mode);\n+struct kernfs_node *cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,\n+\t\t\t\t umode_t mode);\n int cgroup_rmdir(struct kernfs_node *kn);\n int cgroup_show_path(struct seq_file *sf, struct kernfs_node *kf_node,\n \t\t struct kernfs_root *kf_root);\ndiff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c\nindex 227d09704ca59..b3793b5842ab9 100644\n--- a/kernel/cgroup/cgroup.c\n+++ b/kernel/cgroup/cgroup.c\n@@ -6113,18 +6113,20 @@ static bool cgroup_check_hierarchy_limits(struct cgroup *parent)\n \treturn ret;\n }\n \n-int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, umode_t mode)\n+struct kernfs_node *cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,\n+\t\t\t\t umode_t mode)\n {\n \tstruct cgroup *parent, *cgrp;\n+\tstruct kernfs_node *kn;\n \tint ret;\n \n \t/* do not accept '\\n' to prevent making /proc/\u003cpid\u003e/cgroup unparsable */\n \tif (strchr(name, '\\n'))\n-\t\treturn -EINVAL;\n+\t\treturn ERR_PTR(-EINVAL);\n \n \tparent = cgroup_kn_lock_live(parent_kn, false);\n \tif (!parent)\n-\t\treturn -ENODEV;\n+\t\treturn ERR_PTR(-ENODEV);\n \n \tif (!cgroup_check_hierarchy_limits(parent)) {\n \t\tret = -EAGAIN;\n@@ -6156,14 +6158,16 @@ int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, umode_t mode)\n \t/* let's create and online css's */\n \tkernfs_activate(cgrp-\u003ekn);\n \n-\tret = 0;\n-\tgoto out_unlock;\n+\tkn = cgrp-\u003ekn;\n+\tkernfs_get(kn);\n+\tcgroup_kn_unlock(parent_kn);\n+\treturn kn;\n \n out_destroy:\n \tcgroup_destroy_locked(cgrp);\n out_unlock:\n \tcgroup_kn_unlock(parent_kn);\n-\treturn ret;\n+\treturn ERR_PTR(ret);\n }\n \n /*\ndiff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c\nindex 5b42140e12e87..8ae096cc1af67 100644\n--- a/security/apparmor/apparmorfs.c\n+++ b/security/apparmor/apparmorfs.c\n@@ -248,6 +248,7 @@ static int apparmorfs_fill_super(struct super_block *sb, struct fs_context *fc)\n \tif (error)\n \t\treturn error;\n \tsb-\u003es_op = \u0026aafs_super_ops;\n+\tsb-\u003es_iflags |= SB_I_NO_ATOMIC_MKDIR;\n \n \treturn 0;\n }\ndiff --git a/tools/testing/selftests/filesystems/.gitignore b/tools/testing/selftests/filesystems/.gitignore\nindex 62f7b1c466497..e0ae02a5d2d11 100644\n--- a/tools/testing/selftests/filesystems/.gitignore\n+++ b/tools/testing/selftests/filesystems/.gitignore\n@@ -1,4 +1,5 @@\n # SPDX-License-Identifier: GPL-2.0-only\n+open_o_creat_o_dir\n dnotify_test\n devpts_pts\n fclog\ndiff --git a/tools/testing/selftests/filesystems/Makefile b/tools/testing/selftests/filesystems/Makefile\nindex 93e2cc9123b04..a8b48cb6afd64 100644\n--- a/tools/testing/selftests/filesystems/Makefile\n+++ b/tools/testing/selftests/filesystems/Makefile\n@@ -1,7 +1,7 @@\n # SPDX-License-Identifier: GPL-2.0\n \n CFLAGS += $(KHDR_INCLUDES)\n-TEST_GEN_PROGS := devpts_pts file_stressor anon_inode_test kernfs_test fclog ustat_test rw_hint_test\n+TEST_GEN_PROGS := open_o_creat_o_dir devpts_pts file_stressor anon_inode_test kernfs_test fclog ustat_test rw_hint_test\n TEST_GEN_PROGS += idmapped_tmpfile\n TEST_GEN_PROGS_EXTENDED := dnotify_test\n \ndiff --git a/tools/testing/selftests/filesystems/open_o_creat_o_dir.c b/tools/testing/selftests/filesystems/open_o_creat_o_dir.c\nnew file mode 100644\nindex 0000000000000..93c0f4dea266a\n--- /dev/null\n+++ b/tools/testing/selftests/filesystems/open_o_creat_o_dir.c\n@@ -0,0 +1,346 @@\n+// SPDX-License-Identifier: GPL-2.0\n+#include \u003csys/stat.h\u003e\n+#include \u003csys/vfs.h\u003e\n+#include \u003cerrno.h\u003e\n+#include \u003climits.h\u003e\n+#include \u003cfcntl.h\u003e\n+#include \u003cstdio.h\u003e\n+#include \u003cstring.h\u003e\n+#include \u003cunistd.h\u003e\n+#include \u003clinux/magic.h\u003e\n+\n+#include \"kselftest_harness.h\"\n+#include \"wrappers.h\"\n+\n+#define openat_o_mkdir_checked_flags(dfd, pathname, flags) ({\t\\\n+\tstruct stat __st;\t\t\t\t\t\t\\\n+\tint __fd = openat_o_mkdir(dfd, pathname, flags, S_IRWXU);\t\\\n+\tASSERT_GE(__fd, 0);\t\t\t\t\t\t\\\n+\tASSERT_EQ(fstat(__fd, \u0026__st), 0);\t\t\t\t\\\n+\tEXPECT_TRUE(S_ISDIR(__st.st_mode));\t\t\t\t\\\n+\t__fd;\t\t\t\t\t\t\t\t\\\n+})\n+\n+#define openat_o_mkdir_checked(dfd, pathname) \\\n+\topenat_o_mkdir_checked_flags(dfd, pathname, O_RDONLY)\n+\n+FIXTURE(open_o_creat_o_dir) {\n+\tchar dirpath[PATH_MAX];\n+\tint dfd;\n+};\n+\n+FIXTURE_SETUP(open_o_creat_o_dir)\n+{\n+\tstrcpy(self-\u003edirpath, \"/tmp/open_o_creat_o_dir_test.XXXXXX\");\n+\tASSERT_NE(mkdtemp(self-\u003edirpath), NULL);\n+\tself-\u003edfd = open(self-\u003edirpath, O_DIRECTORY);\n+\tASSERT_GE(self-\u003edfd, 0);\n+}\n+\n+FIXTURE_TEARDOWN(open_o_creat_o_dir)\n+{\n+\tclose(self-\u003edfd);\n+\trmdir(self-\u003edirpath);\n+}\n+\n+/* Does open_o_creat_o_dir return a fd at all? */\n+TEST_F(open_o_creat_o_dir, returns_fd)\n+{\n+\tint fd = openat_o_mkdir_checked(self-\u003edfd, \"newdir\");\n+\tEXPECT_EQ(close(fd), 0);\n+\tEXPECT_EQ(unlinkat(self-\u003edfd, \"newdir\", AT_REMOVEDIR), 0);\n+}\n+\n+/* The fd must refer to the directory that was just created. */\n+TEST_F(open_o_creat_o_dir, fd_is_created_dir)\n+{\n+\tint fd;\n+\tstruct stat st_via_fd, st_via_path;\n+\tchar path[PATH_MAX];\n+\n+\tfd = openat_o_mkdir_checked(self-\u003edfd, \"checkdir\");\n+\n+\tASSERT_EQ(fstat(fd, \u0026st_via_fd), 0);\n+\n+\tsnprintf(path, sizeof(path), \"%s/checkdir\", self-\u003edirpath);\n+\tASSERT_EQ(stat(path, \u0026st_via_path), 0);\n+\n+\tEXPECT_EQ(st_via_fd.st_ino, st_via_path.st_ino);\n+\tEXPECT_EQ(st_via_fd.st_dev, st_via_path.st_dev);\n+\n+\tEXPECT_EQ(close(fd), 0);\n+\tEXPECT_EQ(rmdir(path), 0);\n+}\n+\n+/* Missing parent component must fail with ENOENT. */\n+TEST_F(open_o_creat_o_dir, enoent_missing_parent)\n+{\n+\tEXPECT_EQ(openat_o_mkdir(self-\u003edfd, \"nonexistent/child\", O_RDONLY, S_IRWXU), -1);\n+\tEXPECT_EQ(errno, ENOENT);\n+}\n+\n+/* An invalid dfd must fail with EBADF. */\n+TEST_F(open_o_creat_o_dir, ebadf)\n+{\n+\tEXPECT_EQ(openat_o_mkdir(FD_INVALID, \"badfdir\", O_RDONLY, S_IRWXU), -1);\n+\tEXPECT_EQ(errno, EBADF);\n+}\n+\n+/* A dfd that points to a file (not a directory) must fail with ENOTDIR. */\n+TEST_F(open_o_creat_o_dir, enotdir_dfd)\n+{\n+\tint file_fd;\n+\n+\tfile_fd = openat(self-\u003edfd, \"file\",\n+\t\t\t O_CREAT | O_RDONLY, S_IRWXU);\n+\tASSERT_GE(file_fd, 0);\n+\n+\tEXPECT_EQ(openat_o_mkdir(file_fd, \"subdir\", O_RDONLY, S_IRWXU), -1);\n+\tEXPECT_EQ(errno, ENOTDIR);\n+\n+\tEXPECT_EQ(close(file_fd), 0);\n+\tEXPECT_EQ(unlinkat(self-\u003edfd, \"file\", 0), 0);\n+}\n+\n+/*\n+ * O_CREAT|O_DIRECTORY without O_EXCL must fail with EINVAL and must not\n+ * create anything at the path.\n+ */\n+TEST_F(open_o_creat_o_dir, requires_o_excl)\n+{\n+\tstruct stat st;\n+\n+\tEXPECT_EQ(openat(self-\u003edfd, \"noexcl\", O_CREAT | O_DIRECTORY | O_RDONLY,\n+\t\t\t S_IRWXU), -1);\n+\tEXPECT_EQ(errno, EINVAL);\n+\n+\tEXPECT_EQ(fstatat(self-\u003edfd, \"noexcl\", \u0026st, 0), -1);\n+\tEXPECT_EQ(errno, ENOENT);\n+}\n+\n+/*\n+ * Once the directory exists, repeating O_CREAT|O_DIRECTORY|O_EXCL must\n+ * fail with EEXIST.\n+ */\n+TEST_F(open_o_creat_o_dir, existing_dir_eexist)\n+{\n+\tint excldir_fd;\n+\n+\texcldir_fd = openat_o_mkdir_checked(self-\u003edfd, \"excldir\");\n+\n+\tEXPECT_EQ(openat_o_mkdir(self-\u003edfd, \"excldir\", O_RDONLY, S_IRWXU), -1);\n+\tEXPECT_EQ(errno, EEXIST);\n+\n+\tEXPECT_EQ(close(excldir_fd), 0);\n+\tEXPECT_EQ(unlinkat(self-\u003edfd, \"excldir\", AT_REMOVEDIR), 0);\n+}\n+\n+/*\n+ * O_CREAT|O_DIRECTORY combined with a writable access mode must be\n+ * rejected: a directory cannot be opened for writing.\n+ */\n+TEST_F(open_o_creat_o_dir, rejects_writable_acc_mode)\n+{\n+\tEXPECT_EQ(openat_o_mkdir(self-\u003edfd, \"rdwrdir\", O_RDWR, S_IRWXU), -1);\n+\tEXPECT_EQ(errno, ENOTDIR);\n+}\n+\n+/*\n+ * openat(O_CREAT|O_DIRECTORY) with a trailing slash should work.\n+ */\n+TEST_F(open_o_creat_o_dir, trailing_slash)\n+{\n+\tint fd = openat_o_mkdir_checked(self-\u003edfd, \"newdir/\");\n+\tEXPECT_EQ(close(fd), 0);\n+\tEXPECT_EQ(unlinkat(self-\u003edfd, \"newdir\", AT_REMOVEDIR), 0);\n+}\n+\n+/*\n+ * openat(O_CREAT) with a trailing slash but without O_DIRECTORY\n+ * must fail with EISDIR and must not create anything at the path.\n+ */\n+TEST_F(open_o_creat_o_dir, trailing_slash_no_o_dir)\n+{\n+\tint fd;\n+\tstruct stat st;\n+\n+\tfd = openat(self-\u003edfd, \"trailing/\", O_CREAT | O_RDONLY, S_IRWXU);\n+\tEXPECT_EQ(fd, -1);\n+\tEXPECT_EQ(errno, EISDIR);\n+\n+\tEXPECT_EQ(fstatat(self-\u003edfd, \"trailing\", \u0026st, 0), -1);\n+\tEXPECT_EQ(errno, ENOENT);\n+}\n+\n+/*\n+ * The returned fd must be usable as a dfd for further *at() calls.\n+ */\n+TEST_F(open_o_creat_o_dir, fd_usable_as_dfd)\n+{\n+\tint parent_fd, child_fd;\n+\tchar path[PATH_MAX];\n+\n+\tparent_fd = openat_o_mkdir_checked(self-\u003edfd, \"parent\");\n+\tchild_fd = openat_o_mkdir_checked(parent_fd, \"child\");\n+\n+\tEXPECT_EQ(close(child_fd), 0);\n+\tEXPECT_EQ(close(parent_fd), 0);\n+\n+\tsnprintf(path, sizeof(path), \"%s/parent/child\", self-\u003edirpath);\n+\tEXPECT_EQ(rmdir(path), 0);\n+\tsnprintf(path, sizeof(path), \"%s/parent\", self-\u003edirpath);\n+\tEXPECT_EQ(rmdir(path), 0);\n+}\n+\n+/*\n+ * O_EXCL means a trailing symlink is never followed, so O_CREAT|O_DIRECTORY\n+ * must refuse to create through a dangling one, and must not create\n+ * anything at the symlink target.\n+ */\n+TEST_F(open_o_creat_o_dir, dangling_symlink_eexist)\n+{\n+\tstruct stat st;\n+\n+\tASSERT_EQ(symlinkat(\"danglink_target\", self-\u003edfd, \"danglink\"), 0);\n+\n+\tEXPECT_EQ(openat_o_mkdir(self-\u003edfd, \"danglink\", O_RDONLY, S_IRWXU), -1);\n+\tEXPECT_EQ(errno, EEXIST);\n+\n+\t/* Nothing must have been created at the target. */\n+\tEXPECT_EQ(fstatat(self-\u003edfd, \"danglink_target\", \u0026st, 0), -1);\n+\tEXPECT_EQ(errno, ENOENT);\n+\n+\tEXPECT_EQ(unlinkat(self-\u003edfd, \"danglink\", 0), 0);\n+}\n+\n+/*\n+ * A trailing symlink is an existing entry, even if it resolves to a\n+ * directory, so O_CREAT|O_DIRECTORY must fail with EEXIST.\n+ */\n+TEST_F(open_o_creat_o_dir, symlink_to_dir_eexist)\n+{\n+\tASSERT_EQ(mkdirat(self-\u003edfd, \"realdir\", 0700), 0);\n+\tASSERT_EQ(symlinkat(\"realdir\", self-\u003edfd, \"dirlink\"), 0);\n+\n+\tEXPECT_EQ(openat_o_mkdir(self-\u003edfd, \"dirlink\", O_RDONLY, S_IRWXU), -1);\n+\tEXPECT_EQ(errno, EEXIST);\n+\n+\tEXPECT_EQ(unlinkat(self-\u003edfd, \"dirlink\", 0), 0);\n+\tEXPECT_EQ(unlinkat(self-\u003edfd, \"realdir\", AT_REMOVEDIR), 0);\n+}\n+\n+/*\n+ * O_TMPFILE is encoded as __O_TMPFILE|O_DIRECTORY. Now that O_CREAT is no\n+ * longer rejected alongside O_DIRECTORY, O_TMPFILE|O_CREAT must still be\n+ * rejected explicitly so that it cannot create a persistent file on kernels\n+ * that predate O_TMPFILE.\n+ */\n+TEST_F(open_o_creat_o_dir, tmpfile_with_o_creat_einval)\n+{\n+\tEXPECT_EQ(openat(self-\u003edfd, \".\", O_TMPFILE | O_CREAT | O_RDWR, S_IRWXU),\n+\t\t -1);\n+\tEXPECT_EQ(errno, EINVAL);\n+}\n+\n+/*\n+ * Open the first of @paths that is a mounted filesystem of type @magic.\n+ * Returns -1 if there is none.\n+ */\n+static int open_fs_root(const char *const *paths, long magic)\n+{\n+\tfor (; *paths; paths++) {\n+\t\tstruct statfs sfs;\n+\t\tint fd;\n+\n+\t\tfd = open(*paths, O_DIRECTORY | O_RDONLY);\n+\t\tif (fd \u003c 0)\n+\t\t\tcontinue;\n+\t\tif (!fstatfs(fd, \u0026sfs) \u0026\u0026 sfs.f_type == magic)\n+\t\t\treturn fd;\n+\t\tclose(fd);\n+\t}\n+\treturn -1;\n+}\n+\n+/*\n+ * kernfs: O_CREAT|O_DIRECTORY on cgroup2 must create a cgroup and return a\n+ * fd to that very cgroup.\n+ */\n+TEST(kernfs_cgroup2)\n+{\n+\tstatic const char *const paths[] = {\n+\t\t\"/sys/fs/cgroup\", \"/sys/fs/cgroup/unified\", NULL\n+\t};\n+\tstruct stat st_fd, st_path;\n+\tint root_fd, fd, procs_fd;\n+\tchar name[64];\n+\n+\tif (geteuid() != 0)\n+\t\tSKIP(return, \"needs root to create a cgroup\");\n+\troot_fd = open_fs_root(paths, CGROUP2_SUPER_MAGIC);\n+\tif (root_fd \u003c 0)\n+\t\tSKIP(return, \"cgroup2 is not mounted\");\n+\n+\tsnprintf(name, sizeof(name), \"open_o_creat_o_dir.%d\", getpid());\n+\n+\tfd = openat_o_mkdir_checked(root_fd, name);\n+\n+\t/* The fd refers to the cgroup that is now visible at the name. */\n+\tASSERT_EQ(fstat(fd, \u0026st_fd), 0);\n+\tASSERT_EQ(fstatat(root_fd, name, \u0026st_path, 0), 0);\n+\tEXPECT_EQ(st_fd.st_ino, st_path.st_ino);\n+\tEXPECT_EQ(st_fd.st_dev, st_path.st_dev);\n+\n+\t/* It is a populated cgroup, not just an empty directory. */\n+\tprocs_fd = openat(fd, \"cgroup.procs\", O_RDONLY);\n+\tEXPECT_GE(procs_fd, 0);\n+\tif (procs_fd \u003e= 0)\n+\t\tEXPECT_EQ(close(procs_fd), 0);\n+\n+\tEXPECT_EQ(close(fd), 0);\n+\tEXPECT_EQ(unlinkat(root_fd, name, AT_REMOVEDIR), 0);\n+\tEXPECT_EQ(close(root_fd), 0);\n+}\n+\n+/*\n+ * tracefs sets SB_I_NO_ATOMIC_MKDIR: O_CREAT|O_DIRECTORY is refused with\n+ * EOPNOTSUPP before any lookup, whether or not the name exists, and\n+ * creates nothing.\n+ */\n+TEST(tracefs_eopnotsupp)\n+{\n+\tstatic const char *const paths[] = {\n+\t\t\"/sys/kernel/tracing\", \"/sys/kernel/debug/tracing\", NULL\n+\t};\n+\tint tracing_fd, inst_fd;\n+\tchar name[64];\n+\tstruct stat st;\n+\n+\tif (geteuid() != 0)\n+\t\tSKIP(return, \"needs root to access tracefs\");\n+\ttracing_fd = open_fs_root(paths, TRACEFS_MAGIC);\n+\tif (tracing_fd \u003c 0)\n+\t\tSKIP(return, \"tracefs is not mounted\");\n+\tinst_fd = openat(tracing_fd, \"instances\", O_DIRECTORY | O_RDONLY);\n+\tif (inst_fd \u003c 0) {\n+\t\tclose(tracing_fd);\n+\t\tSKIP(return, \"no tracefs instances directory\");\n+\t}\n+\n+\tsnprintf(name, sizeof(name), \"open_o_creat_o_dir.%d\", getpid());\n+\n+\t/* Nonexistent name, where mkdir(2) would create an instance */\n+\tEXPECT_EQ(openat_o_mkdir(inst_fd, name, O_RDONLY, S_IRWXU), -1);\n+\tEXPECT_EQ(errno, EOPNOTSUPP);\n+\tEXPECT_EQ(fstatat(inst_fd, name, \u0026st, 0), -1);\n+\tEXPECT_EQ(errno, ENOENT);\n+\n+\t/* Existing name */\n+\tEXPECT_EQ(openat_o_mkdir(tracing_fd, \"instances\", O_RDONLY, S_IRWXU), -1);\n+\tEXPECT_EQ(errno, EOPNOTSUPP);\n+\n+\tEXPECT_EQ(close(inst_fd), 0);\n+\tEXPECT_EQ(close(tracing_fd), 0);\n+}\n+\n+TEST_HARNESS_MAIN\ndiff --git a/tools/testing/selftests/filesystems/wrappers.h b/tools/testing/selftests/filesystems/wrappers.h\nindex 420ae4f908cf2..bc58bdb800631 100644\n--- a/tools/testing/selftests/filesystems/wrappers.h\n+++ b/tools/testing/selftests/filesystems/wrappers.h\n@@ -13,6 +13,10 @@\n #define STATX_MNT_ID_UNIQUE 0x00004000U /* Want/got extended stx_mount_id */\n #endif\n \n+#ifndef FD_INVALID\n+#define FD_INVALID -10009\n+#endif\n+\n static inline int sys_fsopen(const char *fsname, unsigned int flags)\n {\n \treturn syscall(__NR_fsopen, fsname, flags);\n@@ -105,4 +109,11 @@ static inline int sys_open_tree(int dfd, const char *filename, unsigned int flag\n \treturn syscall(__NR_open_tree, dfd, filename, flags);\n }\n \n+static inline int openat_o_mkdir(int dfd, const char *pathname,\n+\t\t\t\t unsigned int flags, mode_t mode)\n+{\n+\treturn syscall(__NR_openat, dfd, pathname,\n+\t\t flags | O_DIRECTORY | O_CREAT | O_EXCL, mode);\n+}\n+\n #endif\n"
} |
| 2/1 | 2026/10/16 22:28 | agent | patch-evaluator |
5mModel:core Args: null Results: {
"EnableConfigs": [
"CGROUPS"
],
"FocusSymbols": [
"cgroup_mkdir",
"kernfs_get_inode_prealloc",
"kernfs_iop_mkdir",
"lookup_open",
"may_o_create",
"vfs_mkdir"
],
"Reasoning": "The patch series introduces atomic directory creation and opening via openat/openat2 with flags O_CREAT | O_DIRECTORY | O_EXCL. It modifies core VFS path resolution and file creation logic in fs/namei.c and fs/open.c, adds new assertions (WARN_ON_ONCE in may_o_create and lookup_open), and completely rewrites kernfs_iop_mkdir in fs/kernfs/dir.c to preallocate and instantiate inodes with d_splice_alias, updating cgroup_mkdir accordingly. This code is fully reachable in standard virtualized environments via standard syscalls.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit 4e3de42dfe78758a71522b9b86a2de3000cb64d7
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Oct 16 22:28:09 2026 +0000
syz-cluster: applied patch under review
diff --git a/arch/alpha/include/uapi/asm/errno.h b/arch/alpha/include/uapi/asm/errno.h
index 1a99f38813c79..0384f69989748 100644
--- a/arch/alpha/include/uapi/asm/errno.h
+++ b/arch/alpha/include/uapi/asm/errno.h
@@ -19,7 +19,7 @@
#define ENOPROTOOPT 42 /* Protocol not available */
#define EPROTONOSUPPORT 43 /* Protocol not supported */
#define ESOCKTNOSUPPORT 44 /* Socket type not supported */
-#define EOPNOTSUPP 45 /* Operation not supported on transport endpoint */
+#define EOPNOTSUPP 45 /* Operation not supported */
#define EPFNOSUPPORT 46 /* Protocol family not supported */
#define EAFNOSUPPORT 47 /* Address family not supported by protocol */
#define EADDRINUSE 48 /* Address already in use */
diff --git a/arch/mips/include/uapi/asm/errno.h b/arch/mips/include/uapi/asm/errno.h
index 1835a50b69cef..2b6813715d1e5 100644
--- a/arch/mips/include/uapi/asm/errno.h
+++ b/arch/mips/include/uapi/asm/errno.h
@@ -75,7 +75,7 @@
#define ENOPROTOOPT 99 /* Protocol not available */
#define EPROTONOSUPPORT 120 /* Protocol not supported */
#define ESOCKTNOSUPPORT 121 /* Socket type not supported */
-#define EOPNOTSUPP 122 /* Operation not supported on transport endpoint */
+#define EOPNOTSUPP 122 /* Operation not supported */
#define EPFNOSUPPORT 123 /* Protocol family not supported */
#define EAFNOSUPPORT 124 /* Address family not supported by protocol */
#define EADDRINUSE 125 /* Address already in use */
diff --git a/arch/parisc/include/uapi/asm/errno.h b/arch/parisc/include/uapi/asm/errno.h
index 93194fbb0a80d..74bd65d791457 100644
--- a/arch/parisc/include/uapi/asm/errno.h
+++ b/arch/parisc/include/uapi/asm/errno.h
@@ -84,7 +84,7 @@
#define ENOPROTOOPT 220 /* Protocol not available */
#define EPROTONOSUPPORT 221 /* Protocol not supported */
#define ESOCKTNOSUPPORT 222 /* Socket type not supported */
-#define EOPNOTSUPP 223 /* Operation not supported on transport endpoint */
+#define EOPNOTSUPP 223 /* Operation not supported */
#define EPFNOSUPPORT 224 /* Protocol family not supported */
#define EAFNOSUPPORT 225 /* Address family not supported by protocol */
#define EADDRINUSE 226 /* Address already in use */
diff --git a/arch/sparc/include/uapi/asm/errno.h b/arch/sparc/include/uapi/asm/errno.h
index 71940ec9130b4..699f648e1a356 100644
--- a/arch/sparc/include/uapi/asm/errno.h
+++ b/arch/sparc/include/uapi/asm/errno.h
@@ -16,7 +16,7 @@
#define ENOPROTOOPT 42 /* Protocol not available */
#define EPROTONOSUPPORT 43 /* Protocol not supported */
#define ESOCKTNOSUPPORT 44 /* Socket type not supported */
-#define EOPNOTSUPP 45 /* Op not supported on transport endpoint */
+#define EOPNOTSUPP 45 /* Operation not supported */
#define EPFNOSUPPORT 46 /* Protocol family not supported */
#define EAFNOSUPPORT 47 /* Address family not supported by protocol */
#define EADDRINUSE 48 /* Address already in use */
diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c
index 94d6b02c221b8..a3377bc34ac0e 100644
--- a/fs/9p/vfs_super.c
+++ b/fs/9p/vfs_super.c
@@ -40,6 +40,7 @@ static int v9fs_fill_super(struct super_block *sb)
sb->s_blocksize_bits = fls(v9ses->maxdata - 1);
sb->s_blocksize = 1 << sb->s_blocksize_bits;
sb->s_magic = V9FS_MAGIC;
+ sb->s_iflags |= SB_I_NO_ATOMIC_MKDIR;
if (v9fs_proto_dotl(v9ses)) {
sb->s_op = &v9fs_super_ops_dotl;
if (!(v9ses->flags & V9FS_NO_XATTR))
diff --git a/fs/ceph/super.c b/fs/ceph/super.c
index 72935f665f11c..fa1cf1f4f57d6 100644
--- a/fs/ceph/super.c
+++ b/fs/ceph/super.c
@@ -1226,6 +1226,7 @@ static int ceph_set_super(struct super_block *s, struct fs_context *fc)
fsc->max_file_size = 1ULL << 40; /* temp value until we get mdsmap */
s->s_op = &ceph_super_ops;
+ s->s_iflags |= SB_I_NO_ATOMIC_MKDIR;
set_default_d_op(s, &ceph_dentry_ops);
s->s_export_op = &ceph_export_ops;
diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
index 5f37cddb956fb..8ed6076d50afd 100644
--- a/fs/ecryptfs/main.c
+++ b/fs/ecryptfs/main.c
@@ -477,6 +477,7 @@ static int ecryptfs_get_tree(struct fs_context *fc)
/* ->kill_sb() will take care of sbi after that point */
sbi = NULL;
s->s_op = &ecryptfs_sops;
+ s->s_iflags |= SB_I_NO_ATOMIC_MKDIR;
s->s_xattr = ecryptfs_xattr_handlers;
set_default_d_op(s, &ecryptfs_dops);
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index e9552be3637bc..cf766c6feea43 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -1608,6 +1608,7 @@ static void fuse_sb_defaults(struct super_block *sb)
sb->s_iflags |= SB_I_IMA_UNVERIFIABLE_SIGNATURE;
sb->s_iflags |= SB_I_NOIDMAP;
sb->s_iflags |= SB_I_NO_DATA_INTEGRITY;
+ sb->s_iflags |= SB_I_NO_ATOMIC_MKDIR;
if (sb->s_user_ns != &init_user_ns)
sb->s_iflags |= SB_I_UNTRUSTED_MOUNTER;
sb->s_flags &= ~(SB_NOSEC | SB_I_VERSION);
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
index 718e0da7dfce0..695835819a5d5 100644
--- a/fs/gfs2/ops_fstype.c
+++ b/fs/gfs2/ops_fstype.c
@@ -1139,6 +1139,7 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_flags |= SB_NOSEC;
sb->s_magic = GFS2_MAGIC;
sb->s_op = &gfs2_super_ops;
+ sb->s_iflags |= SB_I_NO_ATOMIC_MKDIR;
set_default_d_op(sb, &gfs2_dops);
sb->s_export_op = &gfs2_export_ops;
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 82bbaeb326aab..42b3810a04bea 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -1294,18 +1294,40 @@ static struct dentry *kernfs_iop_mkdir(struct mnt_idmap *idmap,
{
struct kernfs_node *parent = dir->i_private;
struct kernfs_syscall_ops *scops = kernfs_root(parent)->syscall_ops;
- int ret;
+ struct kernfs_node *kn;
+ struct inode *inode;
if (!scops || !scops->mkdir)
return ERR_PTR(-EPERM);
- if (!kernfs_get_active(parent))
+ inode = new_inode(dir->i_sb);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+
+ if (!kernfs_get_active(parent)) {
+ iput(inode);
return ERR_PTR(-ENODEV);
+ }
- ret = scops->mkdir(parent, dentry->d_name.name, mode);
+ kn = scops->mkdir(parent, dentry->d_name.name, mode);
kernfs_put_active(parent);
- return ERR_PTR(ret);
+ if (IS_ERR(kn)) {
+ iput(inode);
+ return ERR_CAST(kn);
+ }
+
+ inode = kernfs_get_inode_prealloc(inode, kn);
+ kernfs_put(kn);
+
+ /*
+ * We may race with kernfs_fh_to_dentry() which calls d_obtain_alias(),
+ * so the inode may already have a disconnected alias. Use
+ * d_splice_alias() to move it into place, which does not accept
+ * hashed dentries, so drop the negative dentry first.
+ */
+ d_drop(dentry);
+ return d_splice_alias(inode, dentry);
}
static int kernfs_iop_rmdir(struct inode *dir, struct dentry *dentry)
diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c
index abb286bc3474b..a254090364926 100644
--- a/fs/kernfs/inode.c
+++ b/fs/kernfs/inode.c
@@ -204,7 +204,8 @@ static void kernfs_init_inode(struct kernfs_node *kn, struct inode *inode)
inode->i_generation = kernfs_gen(kn);
set_default_inode_attr(inode, kn->mode);
- kernfs_refresh_inode(kn, inode);
+ scoped_guard(rwsem_read, &kernfs_root(kn)->kernfs_iattr_rwsem)
+ kernfs_refresh_inode(kn, inode);
/* initialize inode according to type */
switch (kernfs_type(kn)) {
@@ -254,6 +255,40 @@ struct inode *kernfs_get_inode(struct super_block *sb, struct kernfs_node *kn)
return inode;
}
+/**
+ * kernfs_get_inode_prealloc - get inode for kernfs_node
+ * @prealloc_inode: inode allocated with new_inode() on the target superblock
+ * @kn: kernfs_node to get the inode for
+ *
+ * Like kernfs_get_inode(), but cannot fail. @prealloc_inode is
+ * initialized for @kn, unless @kn already has an inode on the super
+ * block, in which case @prealloc_inode is released and the existing inode
+ * is returned instead.
+ *
+ * Locking:
+ * Kernel thread context (may sleep).
+ *
+ * Return:
+ * Pointer to the inode of @kn.
+ */
+struct inode *kernfs_get_inode_prealloc(struct inode *prealloc_inode,
+ struct kernfs_node *kn)
+{
+ struct inode *old;
+
+ prealloc_inode->i_ino = kernfs_ino(kn);
+ while (insert_inode_locked(prealloc_inode)) {
+ old = ilookup(prealloc_inode->i_sb, prealloc_inode->i_ino);
+ if (old) {
+ iput(prealloc_inode);
+ return old;
+ }
+ }
+
+ kernfs_init_inode(kn, prealloc_inode);
+ return prealloc_inode;
+}
+
/*
* The kernfs_node serves as both an inode and a directory entry for
* kernfs. To prevent the kernfs inode numbers from being freed
diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h
index aa784b540b361..e4273f104a24d 100644
--- a/fs/kernfs/kernfs-internal.h
+++ b/fs/kernfs/kernfs-internal.h
@@ -170,6 +170,8 @@ extern struct kmem_cache *kernfs_node_cache, *kernfs_iattrs_cache;
* inode.c
*/
extern const struct xattr_handler * const kernfs_xattr_handlers[];
+struct inode *kernfs_get_inode_prealloc(struct inode *inode,
+ struct kernfs_node *kn);
void kernfs_evict_inode(struct inode *inode);
int kernfs_iop_permission(struct mnt_idmap *idmap,
struct inode *inode, int mask);
diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c
index a57399021c8b0..202a5ca2bd341 100644
--- a/fs/kernfs/mount.c
+++ b/fs/kernfs/mount.c
@@ -283,7 +283,6 @@ struct dentry *kernfs_node_dentry(struct kernfs_node *kn,
static int kernfs_fill_super(struct super_block *sb, struct kernfs_fs_context *kfc)
{
struct kernfs_super_info *info = kernfs_info(sb);
- struct kernfs_root *kf_root = kfc->root;
struct inode *inode;
struct dentry *root;
@@ -304,9 +303,7 @@ static int kernfs_fill_super(struct super_block *sb, struct kernfs_fs_context *k
sb->s_shrink->seeks = 0;
/* get root inode, initialize and unlock it */
- down_read(&kf_root->kernfs_rwsem);
inode = kernfs_get_inode(sb, info->root->kn);
- up_read(&kf_root->kernfs_rwsem);
if (!inode) {
pr_debug("kernfs: could not get root inode\n");
return -ENOMEM;
diff --git a/fs/namei.c b/fs/namei.c
index c87c14ee1a252..f21cd1028a8a3 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1889,12 +1889,34 @@ static struct dentry *lookup_fast(struct nameidata *nd)
return dentry;
}
+/*
+ * If @dentry is in-lookup, look it up in @dir through ->lookup(). Returns
+ * the dentry to use from now on: @dentry itself, or the dentry or ERR_PTR()
+ * that ->lookup() returned instead, in which case @dentry is dropped.
+ */
+static struct dentry *finish_lookup(struct inode *dir, struct dentry *dentry,
+ unsigned int flags)
+{
+ struct dentry *res;
+
+ if (!d_in_lookup(dentry))
+ return dentry;
+
+ res = dir->i_op->lookup(dir, dentry, flags);
+ d_lookup_done(dentry);
+ if (unlikely(res)) {
+ dput(dentry);
+ return res;
+ }
+ return dentry;
+}
+
/* Fast lookup failed, do it the slow way */
static struct dentry *__lookup_slow(const struct qstr *name,
struct dentry *dir,
unsigned int flags)
{
- struct dentry *dentry, *old;
+ struct dentry *dentry;
struct inode *inode = dir->d_inode;
/* Don't go there if it's already dead */
@@ -1913,17 +1935,10 @@ static struct dentry *__lookup_slow(const struct qstr *name,
goto again;
}
dput(dentry);
- dentry = ERR_PTR(error);
- }
- } else {
- old = inode->i_op->lookup(inode, dentry, flags);
- d_lookup_done(dentry);
- if (unlikely(old)) {
- dput(dentry);
- dentry = old;
+ return ERR_PTR(error);
}
}
- return dentry;
+ return finish_lookup(inode, dentry, flags);
}
static noinline struct dentry *lookup_slow(const struct qstr *name,
@@ -2810,9 +2825,16 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
return s;
}
+static inline bool trailing_slashes(const struct qstr *last)
+{
+ /* last->len is set by hash_name() to the length of the current
+ * component ->name, terminating with '/' or a NUL character. */
+ return (bool)last->name[last->len];
+}
+
static inline const char *lookup_last(struct nameidata *nd)
{
- if (nd->last_type == LAST_NORM && nd->last.name[nd->last.len])
+ if (nd->last_type == LAST_NORM && trailing_slashes(&nd->last))
nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
return walk_component(nd, WALK_TRAILING);
@@ -4188,6 +4210,24 @@ static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap,
return mode;
}
+static inline
+int vfs_create_no_perm(struct mnt_idmap *idmap, struct dentry *dentry,
+ umode_t mode, struct delegated_inode *di)
+{
+ struct inode *dir = d_inode(dentry->d_parent);
+ int error;
+
+ error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, di);
+ if (error)
+ return error;
+
+ error = dir->i_op->create(idmap, dir, dentry, mode);
+ if (!error)
+ fsnotify_create(dir, dentry);
+
+ return error;
+}
+
/**
* vfs_create - create new file
* @idmap: idmap of the mount the inode was found from
@@ -4214,19 +4254,14 @@ int vfs_create(struct mnt_idmap *idmap, struct dentry *dentry, umode_t mode,
return error;
if (!dir->i_op->create)
- return -EACCES; /* shouldn't it be ENOSYS? */
+ return -EOPNOTSUPP;
mode = vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG);
error = security_inode_create(dir, dentry, mode);
if (error)
return error;
- error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, di);
- if (error)
- return error;
- error = dir->i_op->create(idmap, dir, dentry, mode);
- if (!error)
- fsnotify_create(dir, dentry);
- return error;
+
+ return vfs_create_no_perm(idmap, dentry, mode, di);
}
EXPORT_SYMBOL(vfs_create);
@@ -4343,21 +4378,43 @@ static inline int open_to_namei_flags(int flag)
static int may_o_create(struct mnt_idmap *idmap,
const struct path *dir, struct dentry *dentry,
- umode_t mode)
+ int open_flag, umode_t mode)
{
- int error = security_path_mknod(dir, dentry, mode, 0);
+ struct inode *dir_inode = dir->dentry->d_inode;
+ bool create_dir = O_IS_MKDIR(open_flag);
+ int error;
+
+ WARN_ON_ONCE(create_dir && !S_ISDIR(mode));
+
+ if (create_dir)
+ error = security_path_mkdir(dir, dentry, mode);
+ else
+ error = security_path_mknod(dir, dentry, mode, 0);
if (error)
return error;
if (!fsuidgid_has_mapping(dir->dentry->d_sb, idmap))
return -EOVERFLOW;
- error = inode_permission(idmap, dir->dentry->d_inode,
- MAY_WRITE | MAY_EXEC);
+ error = inode_permission(idmap, dir_inode, MAY_WRITE | MAY_EXEC);
if (error)
return error;
- return security_inode_create(dir->dentry->d_inode, dentry, mode);
+ if (create_dir)
+ error = security_inode_mkdir(dir_inode, dentry, mode);
+ else
+ error = security_inode_create(dir_inode, dentry, mode);
+
+ return error;
+}
+
+static inline umode_t o_create_mode(struct mnt_idmap *idmap,
+ const struct inode *dir, int open_flag, umode_t mode)
+{
+ if (O_IS_MKDIR(open_flag))
+ return vfs_prepare_mode(idmap, dir, mode, S_IRWXUGO | S_ISVTX, S_IFDIR);
+ else
+ return vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG);
}
/**
@@ -4393,8 +4450,10 @@ static struct dentry *atomic_open(const struct path *path, struct dentry *dentry
file->__f_path.dentry = DENTRY_NOT_SET;
file->__f_path.mnt = path->mnt;
+
error = dir_inode->i_op->atomic_open(dir_inode, dentry, file,
- open_to_namei_flags(open_flag), mode);
+ open_to_namei_flags(open_flag), mode);
+
d_lookup_done(dentry);
if (!error) {
@@ -4438,11 +4497,24 @@ static struct dentry *atomic_open(const struct path *path, struct dentry *dentry
error = create_error;
}
dput(dentry);
- dentry = ERR_PTR(error);
+ return ERR_PTR(error);
}
+
+ if (file->f_mode & FMODE_CREATED) {
+ if (d_is_dir(dentry))
+ fsnotify_mkdir(dir_inode, dentry);
+ else
+ fsnotify_create(dir_inode, dentry);
+ }
+ if (file->f_mode & FMODE_OPENED)
+ fsnotify_open(file);
+
return dentry;
}
+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.
*
@@ -4464,6 +4536,7 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
struct mnt_idmap *idmap;
struct dentry *dir = nd->path.dentry;
struct inode *dir_inode = dir->d_inode;
+ bool create_dir = O_IS_MKDIR(op->open_flag);
int open_flag;
struct dentry *dentry;
int error, create_error;
@@ -4476,6 +4549,9 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
mode = op->mode;
create_error = 0;
+ if (create_dir && (dir_inode->i_sb->s_iflags & SB_I_NO_ATOMIC_MKDIR))
+ return ERR_PTR(-EOPNOTSUPP);
+
if (open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {
got_write = !mnt_want_write(nd->path.mnt);
/*
@@ -4484,7 +4560,7 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
*/
}
if (open_flag & O_CREAT)
- inode_lock(dir_inode);
+ inode_lock_nested(dir_inode, I_MUTEX_PARENT);
else
inode_lock_shared(dir_inode);
@@ -4536,10 +4612,10 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
if (open_flag & O_CREAT) {
if (open_flag & O_EXCL)
open_flag &= ~O_TRUNC;
- mode = vfs_prepare_mode(idmap, dir_inode, mode, mode, mode);
+ mode = o_create_mode(idmap, dir_inode, open_flag, mode);
if (likely(got_write))
create_error = may_o_create(idmap, &nd->path,
- dentry, mode);
+ dentry, open_flag, mode);
else
create_error = -EROFS;
}
@@ -4553,19 +4629,10 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
goto out;
}
- if (d_in_lookup(dentry)) {
- struct dentry *res = dir_inode->i_op->lookup(dir_inode, dentry,
- nd->flags);
- d_lookup_done(dentry);
- if (unlikely(res)) {
- if (IS_ERR(res)) {
- error = PTR_ERR(res);
- goto out_dput;
- }
- dput(dentry);
- dentry = res;
- }
- }
+ dentry = finish_lookup(dir_inode, dentry, nd->flags);
+ if (IS_ERR(dentry))
+ goto out;
+
if (dentry->d_inode || !(op->open_flag & O_CREAT)) {
/*
* No need to create a file. If lookup returned a positive
@@ -4583,26 +4650,33 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
goto out_dput;
}
- error = try_break_deleg(dir_inode, LEASE_BREAK_DIR_CREATE, &delegated_inode);
- if (error)
- goto out_dput;
-
- file->f_mode |= FMODE_CREATED;
- if (!dir_inode->i_op->create) {
- error = -EACCES;
+ if ((create_dir && !dir_inode->i_op->mkdir)
+ || (!create_dir && !dir_inode->i_op->create)) {
+ error = -EOPNOTSUPP;
goto out_dput;
}
- error = dir_inode->i_op->create(idmap, dir_inode, dentry, mode);
+ if (create_dir) {
+ struct dentry *res = vfs_mkdir_no_perm(idmap, dir_inode, dentry, mode,
+ &delegated_inode);
+ error = PTR_ERR_OR_ZERO(res);
+ if (!error) {
+ dentry = res;
+ /*
+ * Filesystems whose ->mkdir() may leave the dentry
+ * negative must set SB_I_NO_ATOMIC_MKDIR.
+ */
+ if (WARN_ON_ONCE(d_is_negative(dentry)))
+ error = -EIO;
+ }
+ } else {
+ error = vfs_create_no_perm(idmap, dentry, mode, &delegated_inode);
+ }
if (error)
goto out_dput;
+
+ file->f_mode |= FMODE_CREATED;
out:
- if (!IS_ERR(dentry)) {
- if (file->f_mode & FMODE_CREATED)
- fsnotify_create(dir_inode, dentry);
- if (file->f_mode & FMODE_OPENED)
- fsnotify_open(file);
- }
if ((open_flag & O_CREAT) || create_error)
inode_unlock(dir_inode);
else
@@ -4724,17 +4798,12 @@ struct file *vfs_lookup_open(struct path *parent, struct qstr *last,
}
EXPORT_SYMBOL_FOR_MODULES(vfs_lookup_open, "nfsd");
-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;
if (open_flag & O_CREAT) {
- if (trailing_slashes(nd))
+ if (trailing_slashes(&nd->last) && !(open_flag & O_DIRECTORY))
return ERR_PTR(-EISDIR);
/* Don't bother on an O_EXCL create */
@@ -4742,7 +4811,7 @@ static struct dentry *lookup_fast_for_open(struct nameidata *nd, int open_flag)
return NULL;
}
- if (trailing_slashes(nd))
+ if (trailing_slashes(&nd->last))
nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
dentry = lookup_fast(nd);
@@ -4772,6 +4841,13 @@ static const char *open_last_lookups(struct nameidata *nd,
if (nd->last_type != LAST_NORM) {
if (nd->depth)
put_link(nd);
+ /*
+ * Return -EOPNOTSUPP consistently, not -EEXIST, for paths
+ * ending in . or .., as lookup_open() does for other names.
+ */
+ if (O_IS_MKDIR(open_flag) &&
+ (nd->path.dentry->d_sb->s_iflags & SB_I_NO_ATOMIC_MKDIR))
+ return ERR_PTR(-EOPNOTSUPP);
return handle_dots(nd, nd->last_type);
}
@@ -4835,7 +4911,7 @@ 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 = may_create_in_sticky(idmap, nd,
d_backing_inode(nd->path.dentry));
@@ -5116,7 +5192,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 &= ~LOOKUP_CREATE;
dentry = start_dirop(path->dentry, &last, reval_flag | create_flags);
if (IS_ERR(dentry))
@@ -5174,7 +5250,7 @@ inline struct dentry *start_creating_user_path(
EXPORT_SYMBOL(start_creating_user_path);
/**
- * dentry_create - Create and open a file
+ * dentry_create - Create and open a regular file
* @path: path to create
* @flags: O\_ flags
* @mode: mode bits for new file
@@ -5211,7 +5287,7 @@ struct file *dentry_create(struct path *path, int flags, umode_t mode,
path->dentry = dir;
mode = vfs_prepare_mode(idmap, dir_inode, mode, S_IALLUGO, S_IFREG);
- create_error = may_o_create(idmap, path, dentry, mode);
+ create_error = may_o_create(idmap, path, dentry, flags, mode);
if (create_error)
flags &= ~O_CREAT;
@@ -5227,13 +5303,6 @@ struct file *dentry_create(struct path *path, int flags, umode_t mode,
/* Drop the extra reference */
dput(orig_dentry);
- 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 = dentry;
} else {
@@ -5385,6 +5454,33 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
return filename_mknodat(AT_FDCWD, name, mode, dev);
}
+static inline
+struct dentry *vfs_mkdir_no_perm(struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, umode_t mode,
+ struct delegated_inode *di)
+{
+ unsigned max_links = dir->i_sb->s_max_links;
+ struct dentry *de;
+ int error;
+
+ if (max_links && dir->i_nlink >= max_links)
+ return ERR_PTR(-EMLINK);
+
+ error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, di);
+ if (error)
+ return ERR_PTR(error);
+
+ de = dir->i_op->mkdir(idmap, dir, dentry, mode);
+ if (IS_ERR(de))
+ return de;
+ if (de) {
+ dput(dentry);
+ dentry = de;
+ }
+ fsnotify_mkdir(dir, dentry);
+ return dentry;
+}
+
/**
* vfs_mkdir - create directory returning correct dentry if possible
* @idmap: idmap of the mount the inode was found from
@@ -5412,14 +5508,13 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
struct delegated_inode *delegated_inode)
{
int error;
- unsigned max_links = dir->i_sb->s_max_links;
struct dentry *de;
error = may_create_dentry(idmap, dir, dentry);
if (error)
goto err;
- error = -EPERM;
+ error = -EOPNOTSUPP;
if (!dir->i_op->mkdir)
goto err;
@@ -5428,24 +5523,12 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
if (error)
goto err;
- error = -EMLINK;
- if (max_links && dir->i_nlink >= max_links)
+ de = vfs_mkdir_no_perm(idmap, dir, dentry, mode, delegated_inode);
+ if (IS_ERR(de)) {
+ error = PTR_ERR(de);
goto err;
-
- error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, delegated_inode);
- if (error)
- goto err;
-
- de = dir->i_op->mkdir(idmap, dir, dentry, mode);
- error = PTR_ERR(de);
- if (IS_ERR(de))
- goto err;
- if (de) {
- dput(dentry);
- dentry = de;
}
- fsnotify_mkdir(dir, dentry);
- return dentry;
+ return de;
err:
end_creating(dentry);
@@ -5732,7 +5815,7 @@ int filename_unlinkat(int dfd, struct filename *name)
goto exit_drop_write;
/* 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 = -EISDIR;
else
@@ -6334,16 +6417,16 @@ int filename_renameat2(int olddfd, struct filename *from,
if (flags & RENAME_EXCHANGE) {
if (!d_is_dir(rd.new_dentry)) {
error = -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 = -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;
}
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index cb19f1540d984..f4452b630f52b 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -1077,6 +1077,7 @@ static void nfs_fill_super(struct super_block *sb, struct nfs_fs_context *ctx)
sb->s_blocksize = 0;
sb->s_xattr = server->nfs_client->cl_nfs_mod->xattr;
sb->s_op = server->nfs_client->cl_nfs_mod->sops;
+ sb->s_iflags |= SB_I_NO_ATOMIC_MKDIR;
if (server->bsize)
sb->s_blocksize =
nfs_block_size(server->bsize, &sb->s_blocksize_bits);
diff --git a/fs/open.c b/fs/open.c
index 6b1c14e684a93..e8cb9153ea572 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -1239,29 +1239,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 = how->mode | S_IFREG;
+ if (O_IS_MKDIR(flags))
+ op->mode = how->mode | S_IFDIR;
+ else
+ op->mode = how->mode | S_IFREG;
} else {
if (how->mode != 0)
return -EINVAL;
op->mode = 0;
}
- /*
- * 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)) == (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;
@@ -1318,12 +1319,29 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op)
op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN;
+ /*
+ * 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_WRITE))
+ return -ENOTDIR;
+
if (flags & O_CREAT) {
- op->intent |= LOOKUP_CREATE;
+ if (flags & O_DIRECTORY) {
+ /*
+ * O_CREAT|O_DIRECTORY is provided to prevent TOCTOUs, so
+ * it makes little sense to allow it without O_EXCL.
+ */
+ if (!(flags & O_EXCL))
+ return -EINVAL;
+ }
if (flags & O_EXCL) {
op->intent |= LOOKUP_EXCL;
flags |= O_NOFOLLOW;
}
+ op->intent |= LOOKUP_CREATE;
}
if (flags & O_DIRECTORY)
diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c
index 5dcbb0a964e81..d7b13b63c6adf 100644
--- a/fs/resctrl/rdtgroup.c
+++ b/fs/resctrl/rdtgroup.c
@@ -4042,15 +4042,16 @@ static void mkdir_rdt_prepare_clean(struct rdtgroup *rgrp)
* and monitor group(ctrl_mon). This is a resource group
* to monitor a subset of tasks and cpus in its parent ctrl_mon group.
*/
-static int rdtgroup_mkdir_mon(struct kernfs_node *parent_kn,
- const char *name, umode_t mode)
+static struct kernfs_node *rdtgroup_mkdir_mon(struct kernfs_node *parent_kn,
+ const char *name, umode_t mode)
{
struct rdtgroup *rdtgrp, *prgrp;
+ struct kernfs_node *kn;
int ret;
ret = mkdir_rdt_prepare(parent_kn, name, mode, RDTMON_GROUP, &rdtgrp);
if (ret)
- return ret;
+ return ERR_PTR(ret);
prgrp = rdtgrp->mon.parent;
rdtgrp->closid = prgrp->closid;
@@ -4069,17 +4070,22 @@ static int rdtgroup_mkdir_mon(struct kernfs_node *parent_kn,
*/
list_add_tail(&rdtgrp->mon.crdtgrp_list, &prgrp->mon.crdtgrp_list);
+ kn = rdtgrp->kn;
+ kernfs_get(kn);
+ rdtgroup_kn_unlock(parent_kn);
+ return kn;
+
out_unlock:
rdtgroup_kn_unlock(parent_kn);
- return ret;
+ return ERR_PTR(ret);
}
/*
* These are rdtgroups created under the root directory. Can be used
* to allocate and monitor resources.
*/
-static int rdtgroup_mkdir_ctrl_mon(struct kernfs_node *parent_kn,
- const char *name, umode_t mode)
+static struct kernfs_node *rdtgroup_mkdir_ctrl_mon(struct kernfs_node *parent_kn,
+ const char *name, umode_t mode)
{
struct rdtgroup *rdtgrp;
struct kernfs_node *kn;
@@ -4088,7 +4094,7 @@ static int rdtgroup_mkdir_ctrl_mon(struct kernfs_node *parent_kn,
ret = mkdir_rdt_prepare(parent_kn, name, mode, RDTCTRL_GROUP, &rdtgrp);
if (ret)
- return ret;
+ return ERR_PTR(ret);
kn = rdtgrp->kn;
ret = closid_alloc();
@@ -4127,7 +4133,9 @@ static int rdtgroup_mkdir_ctrl_mon(struct kernfs_node *parent_kn,
rdtgrp->mba_mbps_event = mba_mbps_default_event;
}
- goto out_unlock;
+ kernfs_get(kn);
+ rdtgroup_kn_unlock(parent_kn);
+ return kn;
out_del_list:
list_del(&rdtgrp->rdtgroup_list);
@@ -4137,17 +4145,16 @@ static int rdtgroup_mkdir_ctrl_mon(struct kernfs_node *parent_kn,
closid_free(closid);
out_common_fail:
mkdir_rdt_prepare_clean(rdtgrp);
-out_unlock:
rdtgroup_kn_unlock(parent_kn);
- return ret;
+ return ERR_PTR(ret);
}
-static int rdtgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
- umode_t mode)
+static struct kernfs_node *rdtgroup_mkdir(struct kernfs_node *parent_kn,
+ const char *name, umode_t mode)
{
/* Do not accept '\n' to avoid unparsable situation. */
if (strchr(name, '\n'))
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
/*
* If the parent directory is the root directory and RDT
@@ -4161,7 +4168,7 @@ static int rdtgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
if (resctrl_arch_mon_capable())
return rdtgroup_mkdir_mon(parent_kn, name, mode);
- return -EPERM;
+ return ERR_PTR(-EPERM);
}
static int rdtgroup_rmdir_mon(struct rdtgroup *rdtgrp, cpumask_var_t tmpmask)
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index 98b610b2a1142..93a5ead81c827 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -247,6 +247,7 @@ cifs_read_super(struct super_block *sb)
sb->s_magic = CIFS_SUPER_MAGIC;
sb->s_op = &cifs_super_ops;
+ sb->s_iflags |= SB_I_NO_ATOMIC_MKDIR;
sb->s_xattr = cifs_xattr_handlers;
rc = super_setup_bdi(sb);
if (rc)
diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c
index f3d6188a3b7b2..0f64310c30952 100644
--- a/fs/tracefs/inode.c
+++ b/fs/tracefs/inode.c
@@ -471,6 +471,7 @@ static int tracefs_fill_super(struct super_block *sb, struct fs_context *fc)
return err;
sb->s_op = &tracefs_super_operations;
+ sb->s_iflags |= SB_I_NO_ATOMIC_MKDIR;
tracefs_apply_options(sb, false);
set_default_d_op(sb, &tracefs_dentry_operations);
diff --git a/fs/vboxsf/super.c b/fs/vboxsf/super.c
index a61fbab51d370..190b76f04d4f8 100644
--- a/fs/vboxsf/super.c
+++ b/fs/vboxsf/super.c
@@ -196,6 +196,7 @@ static int vboxsf_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_blocksize = 1024;
sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_op = &vboxsf_super_ops;
+ sb->s_iflags |= SB_I_NO_ATOMIC_MKDIR;
set_default_d_op(sb, &vboxsf_dentry_ops);
iroot = iget_locked(sb, 0);
diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h
index 6ad6b9e7a226a..204e16bbe2634 100644
--- a/include/linux/fcntl.h
+++ b/include/linux/fcntl.h
@@ -30,6 +30,12 @@
*/
#define __O_REGULAR (1 << 30)
+#define O_MKDIR_MASK (O_CREAT | O_DIRECTORY)
+static inline bool O_IS_MKDIR(unsigned int flags)
+{
+ return (flags & O_MKDIR_MASK) == 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 | \
diff --git a/include/linux/fs/super_types.h b/include/linux/fs/super_types.h
index ecd96aeb1cee7..521b00c47d0aa 100644
--- a/include/linux/fs/super_types.h
+++ b/include/linux/fs/super_types.h
@@ -352,5 +352,6 @@ struct super_block {
#define SB_I_NOIDMAP 0x00002000 /* No idmapped mounts on this superblock */
#define SB_I_ALLOW_HSM 0x00004000 /* Allow HSM events on this superblock */
#define SB_I_NO_DATA_INTEGRITY 0x00008000 /* fs cannot guarantee data persistence on sync */
+#define SB_I_NO_ATOMIC_MKDIR 0x00010000 /* no O_CREAT|O_DIRECTORY on this fs */
#endif /* _LINUX_FS_SUPER_TYPES_H */
diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h
index 351a5101c8628..0aced50043405 100644
--- a/include/linux/kernfs.h
+++ b/include/linux/kernfs.h
@@ -241,13 +241,14 @@ struct kernfs_node {
* syscalls. These optional callbacks are invoked on the matching syscalls
* and can perform any kernfs operations which don't necessarily have to be
* the exact operation requested. An active reference is held for each
- * kernfs_node parameter.
+ * kernfs_node parameter. @mkdir returns the kernfs_node of the new
+ * directory with a reference held, or an ERR_PTR() on failure.
*/
struct kernfs_syscall_ops {
int (*show_options)(struct seq_file *sf, struct kernfs_root *root);
- int (*mkdir)(struct kernfs_node *parent, const char *name,
- umode_t mode);
+ struct kernfs_node *(*mkdir)(struct kernfs_node *parent,
+ const char *name, umode_t mode);
int (*rmdir)(struct kernfs_node *kn);
int (*rename)(struct kernfs_node *kn, struct kernfs_node *new_parent,
const char *new_name);
diff --git a/include/uapi/asm-generic/errno.h b/include/uapi/asm-generic/errno.h
index bd78e69e0a43f..c84ebf89c8b6e 100644
--- a/include/uapi/asm-generic/errno.h
+++ b/include/uapi/asm-generic/errno.h
@@ -76,7 +76,7 @@
#define ENOPROTOOPT 92 /* Protocol not available */
#define EPROTONOSUPPORT 93 /* Protocol not supported */
#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
-#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
+#define EOPNOTSUPP 95 /* Operation not supported */
#define EPFNOSUPPORT 96 /* Protocol family not supported */
#define EAFNOSUPPORT 97 /* Address family not supported by protocol */
#define EADDRINUSE 98 /* Address already in use */
diff --git a/kernel/cgroup/cgroup-internal.h b/kernel/cgroup/cgroup-internal.h
index 58797123b752f..eca8d30885391 100644
--- a/kernel/cgroup/cgroup-internal.h
+++ b/kernel/cgroup/cgroup-internal.h
@@ -256,7 +256,8 @@ void cgroup_procs_write_finish(struct task_struct *task,
void cgroup_lock_and_drain_offline(struct cgroup *cgrp);
-int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, umode_t mode);
+struct kernfs_node *cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
+ umode_t mode);
int cgroup_rmdir(struct kernfs_node *kn);
int cgroup_show_path(struct seq_file *sf, struct kernfs_node *kf_node,
struct kernfs_root *kf_root);
diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index 227d09704ca59..b3793b5842ab9 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -6113,18 +6113,20 @@ static bool cgroup_check_hierarchy_limits(struct cgroup *parent)
return ret;
}
-int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, umode_t mode)
+struct kernfs_node *cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
+ umode_t mode)
{
struct cgroup *parent, *cgrp;
+ struct kernfs_node *kn;
int ret;
/* do not accept '\n' to prevent making /proc/<pid>/cgroup unparsable */
if (strchr(name, '\n'))
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
parent = cgroup_kn_lock_live(parent_kn, false);
if (!parent)
- return -ENODEV;
+ return ERR_PTR(-ENODEV);
if (!cgroup_check_hierarchy_limits(parent)) {
ret = -EAGAIN;
@@ -6156,14 +6158,16 @@ int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, umode_t mode)
/* let's create and online css's */
kernfs_activate(cgrp->kn);
- ret = 0;
- goto out_unlock;
+ kn = cgrp->kn;
+ kernfs_get(kn);
+ cgroup_kn_unlock(parent_kn);
+ return kn;
out_destroy:
cgroup_destroy_locked(cgrp);
out_unlock:
cgroup_kn_unlock(parent_kn);
- return ret;
+ return ERR_PTR(ret);
}
/*
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 5b42140e12e87..8ae096cc1af67 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -248,6 +248,7 @@ static int apparmorfs_fill_super(struct super_block *sb, struct fs_context *fc)
if (error)
return error;
sb->s_op = &aafs_super_ops;
+ sb->s_iflags |= SB_I_NO_ATOMIC_MKDIR;
return 0;
}
diff --git a/tools/testing/selftests/filesystems/.gitignore b/tools/testing/selftests/filesystems/.gitignore
index 62f7b1c466497..e0ae02a5d2d11 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/selftests/filesystems/Makefile
index 93e2cc9123b04..a8b48cb6afd64 100644
--- a/tools/testing/selftests/filesystems/Makefile
+++ b/tools/testing/selftests/filesystems/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
CFLAGS += $(KHDR_INCLUDES)
-TEST_GEN_PROGS := devpts_pts file_stressor anon_inode_test kernfs_test fclog ustat_test rw_hint_test
+TEST_GEN_PROGS := open_o_creat_o_dir devpts_pts file_stressor anon_inode_test kernfs_test fclog ustat_test rw_hint_test
TEST_GEN_PROGS += idmapped_tmpfile
TEST_GEN_PROGS_EXTENDED := dnotify_test
diff --git a/tools/testing/selftests/filesystems/open_o_creat_o_dir.c b/tools/testing/selftests/filesystems/open_o_creat_o_dir.c
new file mode 100644
index 0000000000000..93c0f4dea266a
--- /dev/null
+++ b/tools/testing/selftests/filesystems/open_o_creat_o_dir.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <errno.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/magic.h>
+
+#include "kselftest_harness.h"
+#include "wrappers.h"
+
+#define openat_o_mkdir_checked_flags(dfd, pathname, flags) ({ \
+ struct stat __st; \
+ int __fd = openat_o_mkdir(dfd, pathname, flags, S_IRWXU); \
+ ASSERT_GE(__fd, 0); \
+ ASSERT_EQ(fstat(__fd, &__st), 0); \
+ EXPECT_TRUE(S_ISDIR(__st.st_mode)); \
+ __fd; \
+})
+
+#define openat_o_mkdir_checked(dfd, pathname) \
+ openat_o_mkdir_checked_flags(dfd, pathname, O_RDONLY)
+
+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 = 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 = openat_o_mkdir_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 = openat_o_mkdir_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(openat_o_mkdir(self->dfd, "nonexistent/child", O_RDONLY, S_IRWXU), -1);
+ EXPECT_EQ(errno, ENOENT);
+}
+
+/* An invalid dfd must fail with EBADF. */
+TEST_F(open_o_creat_o_dir, ebadf)
+{
+ EXPECT_EQ(openat_o_mkdir(FD_INVALID, "badfdir", O_RDONLY, S_IRWXU), -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 = openat(self->dfd, "file",
+ O_CREAT | O_RDONLY, S_IRWXU);
+ ASSERT_GE(file_fd, 0);
+
+ EXPECT_EQ(openat_o_mkdir(file_fd, "subdir", O_RDONLY, S_IRWXU), -1);
+ EXPECT_EQ(errno, ENOTDIR);
+
+ EXPECT_EQ(close(file_fd), 0);
+ EXPECT_EQ(unlinkat(self->dfd, "file", 0), 0);
+}
+
+/*
+ * O_CREAT|O_DIRECTORY without O_EXCL must fail with EINVAL and must not
+ * create anything at the path.
+ */
+TEST_F(open_o_creat_o_dir, requires_o_excl)
+{
+ struct stat st;
+
+ EXPECT_EQ(openat(self->dfd, "noexcl", O_CREAT | O_DIRECTORY | O_RDONLY,
+ S_IRWXU), -1);
+ EXPECT_EQ(errno, EINVAL);
+
+ EXPECT_EQ(fstatat(self->dfd, "noexcl", &st, 0), -1);
+ EXPECT_EQ(errno, ENOENT);
+}
+
+/*
+ * Once the directory exists, repeating O_CREAT|O_DIRECTORY|O_EXCL must
+ * fail with EEXIST.
+ */
+TEST_F(open_o_creat_o_dir, existing_dir_eexist)
+{
+ int excldir_fd;
+
+ excldir_fd = openat_o_mkdir_checked(self->dfd, "excldir");
+
+ EXPECT_EQ(openat_o_mkdir(self->dfd, "excldir", O_RDONLY, S_IRWXU), -1);
+ EXPECT_EQ(errno, EEXIST);
+
+ EXPECT_EQ(close(excldir_fd), 0);
+ EXPECT_EQ(unlinkat(self->dfd, "excldir", AT_REMOVEDIR), 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(openat_o_mkdir(self->dfd, "rdwrdir", O_RDWR, S_IRWXU), -1);
+ EXPECT_EQ(errno, ENOTDIR);
+}
+
+/*
+ * openat(O_CREAT|O_DIRECTORY) with a trailing slash should work.
+ */
+TEST_F(open_o_creat_o_dir, trailing_slash)
+{
+ int fd = openat_o_mkdir_checked(self->dfd, "newdir/");
+ EXPECT_EQ(close(fd), 0);
+ EXPECT_EQ(unlinkat(self->dfd, "newdir", AT_REMOVEDIR), 0);
+}
+
+/*
+ * 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 = openat(self->dfd, "trailing/", O_CREAT | O_RDONLY, S_IRWXU);
+ EXPECT_EQ(fd, -1);
+ EXPECT_EQ(errno, EISDIR);
+
+ EXPECT_EQ(fstatat(self->dfd, "trailing", &st, 0), -1);
+ EXPECT_EQ(errno, ENOENT);
+}
+
+/*
+ * 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 = openat_o_mkdir_checked(self->dfd, "parent");
+ child_fd = openat_o_mkdir_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);
+}
+
+/*
+ * O_EXCL means a trailing symlink is never followed, so O_CREAT|O_DIRECTORY
+ * must refuse to create through a dangling one, and must not create
+ * anything at the symlink target.
+ */
+TEST_F(open_o_creat_o_dir, dangling_symlink_eexist)
+{
+ struct stat st;
+
+ ASSERT_EQ(symlinkat("danglink_target", self->dfd, "danglink"), 0);
+
+ EXPECT_EQ(openat_o_mkdir(self->dfd, "danglink", O_RDONLY, S_IRWXU), -1);
+ EXPECT_EQ(errno, EEXIST);
+
+ /* Nothing must have been created at the target. */
+ EXPECT_EQ(fstatat(self->dfd, "danglink_target", &st, 0), -1);
+ EXPECT_EQ(errno, ENOENT);
+
+ EXPECT_EQ(unlinkat(self->dfd, "danglink", 0), 0);
+}
+
+/*
+ * A trailing symlink is an existing entry, even if it resolves to a
+ * directory, so O_CREAT|O_DIRECTORY must fail with EEXIST.
+ */
+TEST_F(open_o_creat_o_dir, symlink_to_dir_eexist)
+{
+ ASSERT_EQ(mkdirat(self->dfd, "realdir", 0700), 0);
+ ASSERT_EQ(symlinkat("realdir", self->dfd, "dirlink"), 0);
+
+ EXPECT_EQ(openat_o_mkdir(self->dfd, "dirlink", O_RDONLY, S_IRWXU), -1);
+ EXPECT_EQ(errno, EEXIST);
+
+ EXPECT_EQ(unlinkat(self->dfd, "dirlink", 0), 0);
+ EXPECT_EQ(unlinkat(self->dfd, "realdir", AT_REMOVEDIR), 0);
+}
+
+/*
+ * O_TMPFILE is encoded as __O_TMPFILE|O_DIRECTORY. Now that O_CREAT is no
+ * longer rejected alongside O_DIRECTORY, O_TMPFILE|O_CREAT must still be
+ * rejected explicitly so that it cannot create a persistent file on kernels
+ * that predate O_TMPFILE.
+ */
+TEST_F(open_o_creat_o_dir, tmpfile_with_o_creat_einval)
+{
+ EXPECT_EQ(openat(self->dfd, ".", O_TMPFILE | O_CREAT | O_RDWR, S_IRWXU),
+ -1);
+ EXPECT_EQ(errno, EINVAL);
+}
+
+/*
+ * Open the first of @paths that is a mounted filesystem of type @magic.
+ * Returns -1 if there is none.
+ */
+static int open_fs_root(const char *const *paths, long magic)
+{
+ for (; *paths; paths++) {
+ struct statfs sfs;
+ int fd;
+
+ fd = open(*paths, O_DIRECTORY | O_RDONLY);
+ if (fd < 0)
+ continue;
+ if (!fstatfs(fd, &sfs) && sfs.f_type == magic)
+ return fd;
+ close(fd);
+ }
+ return -1;
+}
+
+/*
+ * kernfs: O_CREAT|O_DIRECTORY on cgroup2 must create a cgroup and return a
+ * fd to that very cgroup.
+ */
+TEST(kernfs_cgroup2)
+{
+ static const char *const paths[] = {
+ "/sys/fs/cgroup", "/sys/fs/cgroup/unified", NULL
+ };
+ struct stat st_fd, st_path;
+ int root_fd, fd, procs_fd;
+ char name[64];
+
+ if (geteuid() != 0)
+ SKIP(return, "needs root to create a cgroup");
+ root_fd = open_fs_root(paths, CGROUP2_SUPER_MAGIC);
+ if (root_fd < 0)
+ SKIP(return, "cgroup2 is not mounted");
+
+ snprintf(name, sizeof(name), "open_o_creat_o_dir.%d", getpid());
+
+ fd = openat_o_mkdir_checked(root_fd, name);
+
+ /* The fd refers to the cgroup that is now visible at the name. */
+ ASSERT_EQ(fstat(fd, &st_fd), 0);
+ ASSERT_EQ(fstatat(root_fd, name, &st_path, 0), 0);
+ EXPECT_EQ(st_fd.st_ino, st_path.st_ino);
+ EXPECT_EQ(st_fd.st_dev, st_path.st_dev);
+
+ /* It is a populated cgroup, not just an empty directory. */
+ procs_fd = openat(fd, "cgroup.procs", O_RDONLY);
+ EXPECT_GE(procs_fd, 0);
+ if (procs_fd >= 0)
+ EXPECT_EQ(close(procs_fd), 0);
+
+ EXPECT_EQ(close(fd), 0);
+ EXPECT_EQ(unlinkat(root_fd, name, AT_REMOVEDIR), 0);
+ EXPECT_EQ(close(root_fd), 0);
+}
+
+/*
+ * tracefs sets SB_I_NO_ATOMIC_MKDIR: O_CREAT|O_DIRECTORY is refused with
+ * EOPNOTSUPP before any lookup, whether or not the name exists, and
+ * creates nothing.
+ */
+TEST(tracefs_eopnotsupp)
+{
+ static const char *const paths[] = {
+ "/sys/kernel/tracing", "/sys/kernel/debug/tracing", NULL
+ };
+ int tracing_fd, inst_fd;
+ char name[64];
+ struct stat st;
+
+ if (geteuid() != 0)
+ SKIP(return, "needs root to access tracefs");
+ tracing_fd = open_fs_root(paths, TRACEFS_MAGIC);
+ if (tracing_fd < 0)
+ SKIP(return, "tracefs is not mounted");
+ inst_fd = openat(tracing_fd, "instances", O_DIRECTORY | O_RDONLY);
+ if (inst_fd < 0) {
+ close(tracing_fd);
+ SKIP(return, "no tracefs instances directory");
+ }
+
+ snprintf(name, sizeof(name), "open_o_creat_o_dir.%d", getpid());
+
+ /* Nonexistent name, where mkdir(2) would create an instance */
+ EXPECT_EQ(openat_o_mkdir(inst_fd, name, O_RDONLY, S_IRWXU), -1);
+ EXPECT_EQ(errno, EOPNOTSUPP);
+ EXPECT_EQ(fstatat(inst_fd, name, &st, 0), -1);
+ EXPECT_EQ(errno, ENOENT);
+
+ /* Existing name */
+ EXPECT_EQ(openat_o_mkdir(tracing_fd, "instances", O_RDONLY, S_IRWXU), -1);
+ EXPECT_EQ(errno, EOPNOTSUPP);
+
+ EXPECT_EQ(close(inst_fd), 0);
+ EXPECT_EQ(close(tracing_fd), 0);
+}
+
+TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/filesystems/wrappers.h b/tools/testing/selftests/filesystems/wrappers.h
index 420ae4f908cf2..bc58bdb800631 100644
--- a/tools/testing/selftests/filesystems/wrappers.h
+++ b/tools/testing/selftests/filesystems/wrappers.h
@@ -13,6 +13,10 @@
#define STATX_MNT_ID_UNIQUE 0x00004000U /* Want/got extended stx_mount_id */
#endif
+#ifndef FD_INVALID
+#define FD_INVALID -10009
+#endif
+
static inline int sys_fsopen(const char *fsname, unsigned int flags)
{
return syscall(__NR_fsopen, fsname, flags);
@@ -105,4 +109,11 @@ static inline int sys_open_tree(int dfd, const char *filename, unsigned int flag
return syscall(__NR_open_tree, dfd, filename, flags);
}
+static inline int openat_o_mkdir(int dfd, const char *pathname,
+ unsigned int flags, mode_t mode)
+{
+ return syscall(__NR_openat, dfd, pathname,
+ flags | O_DIRECTORY | O_CREAT | O_EXCL, mode);
+}
+
#endif
|
| 3/2 | 2026/10/16 22:28 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 23903 output: 64 thoughts: 1964 Thoughts: **Patch Analysis: Syzkaller Fuzzing Potential** Alright, let's break this down. My initial assessment screams "fuzz me!" This patch is introducing new functionality with `O_CREAT | O_DIRECTORY`, which is atomic mkdir and open, into the VFS, touching core file system components. I see changes in error handling, new macros and inline functions, and modifications to path resolution, locking, and directory creation in `fs/namei.c`, `fs/open.c`, and the `kernfs` and `cgroup` subsystems. It's a fundamental change that looks highly vulnerable. The code modifications are all over the place. I see `EOPNOTSUPP` comments being changed, flags being added, and important logic changes in `fs/open.c`. Particularly interesting are the updates to `build_open_flags`, the removal of the `EINVAL` check, the treatment of `O_TMPFILE`, and the disallowing of write access on directories when `O_DIRECTORY` is set. The changes in `fs/namei.c` are even more critical, this file is the core of file system lookups and creation. The changes to `may_o_create`, `atomic_open`, `lookup_open`, and the `vfs_mkdir_no_perm` and `vfs_mkdir` family of functions are particularly noteworthy, and the addition of `WARN_ON_ONCE` in that file is a red flag. The re-write of `kernfs_iop_mkdir`, and additions like `kernfs_get_inode_prealloc` look juicy and probably require a lot of care to get right. Plus the updates to cgroup creation functions, too. Given the scope, I think this is reachable code by userspace through `openat`, `openat2`, and the VFS core functions. It looks like it is modifying common code paths. I see that it touches cgroups and kernfs which are also common. To focus the fuzzing effort, I need to identify the key functions or symbols that are part of this new feature. I will focus on the functions in `fs/open.c`, like `build_open_flags`. From `fs/namei.c`, I'm looking at `lookup_open`, `atomic_open`, `finish_lookup`, `may_o_create`, and `vfs_mkdir`. I have to also check `kernfs_iop_mkdir` and `kernfs_get_inode_prealloc` in `fs/kernfs`. Finally, I'll examine `cgroup_mkdir` and `rdtgroup_mkdir` for any direct connections. Now let's see which of these are valid symbols. |
| 4/2 | 2026/10/16 22:29 | tool | grepper |
0mArgs:{
"Expression": "kernfs_iop_mkdir|kernfs_get_inode_prealloc|cgroup_mkdir|lookup_open|do_open|finish_lookup",
"PathPrefixes": [
"fs/",
"kernel/cgroup/"
]
}Results: {
"Output": "\nFull output is too long, showing 200 out of 331 lines.\nUse more precise expression or PathPrefixes if possible.\n\nNumber of matching lines per file (18 files in total):\nfs/ceph/dir.c:2\nfs/ceph/file.c:1\nfs/ceph/super.h:1\nfs/exec.c:5\nfs/fuse/cuse.c:1\nfs/fuse/dir.c:1\nfs/fuse/file.c:3\nfs/fuse/fuse_i.h:1\nfs/kernfs/dir.c:3\nfs/kernfs/inode.c:2\nfs/kernfs/kernfs-internal.h:1\nfs/namei.c:16\nfs/nfs/dir.c:4\nfs/nfs/nfs4proc.c:12\nfs/nfsd/nfs4proc.c:8\nkernel/cgroup/cgroup-internal.h:1\nkernel/cgroup/cgroup-v1.c:1\nkernel/cgroup/cgroup.c:2\n\nfs/ceph/dir.c=727=struct dentry *ceph_handle_snapdir(struct ceph_mds_request *req,\n--\nfs/ceph/dir.c-760- */\nfs/ceph/dir.c:761:struct dentry *ceph_finish_lookup(struct ceph_mds_request *req,\nfs/ceph/dir.c-762-\t\t\t\t struct dentry *dentry, int err)\n--\nfs/ceph/dir.c=806=static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry,\n--\nfs/ceph/dir.c-890-\t}\nfs/ceph/dir.c:891:\tdentry = ceph_finish_lookup(req, dentry, err);\nfs/ceph/dir.c-892-\tceph_mdsc_put_request(req); /* will dput(dentry) */\n--\nfs/ceph/file.c=795=int ceph_atomic_open(struct inode *dir, struct dentry *dentry,\n--\nfs/ceph/file.c-970-\tif (d_in_lookup(dentry)) {\nfs/ceph/file.c:971:\t\tdn = ceph_finish_lookup(req, dentry, err);\nfs/ceph/file.c-972-\t\tif (IS_ERR(dn))\n--\nfs/ceph/super.h=1414=extern struct dentry *ceph_handle_snapdir(struct ceph_mds_request *req,\nfs/ceph/super.h-1415-\t\t\t struct dentry *dentry);\nfs/ceph/super.h:1416:extern struct dentry *ceph_finish_lookup(struct ceph_mds_request *req,\nfs/ceph/super.h-1417-\t\t\t\t\t struct dentry *dentry, int err);\n--\nfs/exec.c=761=EXPORT_SYMBOL(transfer_args_to_stack);\n--\nfs/exec.c-768- */\nfs/exec.c:769:static struct file *do_open_execat(int fd, struct filename *name, int flags)\nfs/exec.c-770-{\n--\nfs/exec.c-814- *\nfs/exec.c:815: * As this is a wrapper for the internal do_open_execat(), callers\nfs/exec.c-816- * must call exe_file_allow_write_access() before fput() on release. Also see\n--\nfs/exec.c=819=struct file *open_exec(const char *name)\n--\nfs/exec.c-821-\tCLASS(filename_kernel, filename)(name);\nfs/exec.c:822:\treturn do_open_execat(AT_FDCWD, filename, 0);\nfs/exec.c-823-}\n--\nfs/exec.c=1422=static int prepare_bprm_creds(struct linux_binprm *bprm)\n--\nfs/exec.c-1434-\nfs/exec.c:1435:/* Matches do_open_execat() */\nfs/exec.c-1436-static void do_close_execat(struct file *file)\n--\nfs/exec.c=1510=static struct linux_binprm *alloc_bprm(int fd, struct filename *filename, int flags)\n--\nfs/exec.c-1515-\nfs/exec.c:1516:\tfile = do_open_execat(fd, filename, flags);\nfs/exec.c-1517-\tif (IS_ERR(file))\n--\nfs/fuse/cuse.c=116=static int cuse_open(struct inode *inode, struct file *file)\n--\nfs/fuse/cuse.c-139-\t */\nfs/fuse/cuse.c:140:\trc = fuse_do_open(\u0026cc-\u003efm, 0, file, 0);\nfs/fuse/cuse.c-141-\tif (rc)\n--\nfs/fuse/dir.c=1900=static int fuse_dir_open(struct inode *inode, struct file *file)\n--\nfs/fuse/dir.c-1911-\nfs/fuse/dir.c:1912:\terr = fuse_do_open(fm, get_node_id(inode), file, true);\nfs/fuse/dir.c-1913-\tif (!err) {\n--\nfs/fuse/file.c=133=struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid,\n--\nfs/fuse/file.c-188-\nfs/fuse/file.c:189:int fuse_do_open(struct fuse_mount *fm, u64 nodeid, struct file *file,\nfs/fuse/file.c-190-\t\t bool isdir)\n--\nfs/fuse/file.c-198-}\nfs/fuse/file.c:199:EXPORT_SYMBOL_GPL(fuse_do_open);\nfs/fuse/file.c-200-\n--\nfs/fuse/file.c=250=static int fuse_open(struct inode *inode, struct file *file)\n--\nfs/fuse/file.c-280-\nfs/fuse/file.c:281:\terr = fuse_do_open(fm, get_node_id(inode), file, false);\nfs/fuse/file.c-282-\tif (!err) {\n--\nfs/fuse/fuse_i.h=1164=void fuse_try_prune_one_inode(struct fuse_conn *fc, u64 nodeid);\nfs/fuse/fuse_i.h-1165-\nfs/fuse/fuse_i.h:1166:int fuse_do_open(struct fuse_mount *fm, u64 nodeid, struct file *file,\nfs/fuse/fuse_i.h-1167-\t\t bool isdir);\n--\nfs/kernfs/dir.c=1248=static struct dentry *kernfs_iop_lookup(struct inode *dir,\n--\nfs/kernfs/dir.c-1290-\nfs/kernfs/dir.c:1291:static struct dentry *kernfs_iop_mkdir(struct mnt_idmap *idmap,\nfs/kernfs/dir.c-1292-\t\t\t\t struct inode *dir, struct dentry *dentry,\n--\nfs/kernfs/dir.c-1319-\nfs/kernfs/dir.c:1320:\tinode = kernfs_get_inode_prealloc(inode, kn);\nfs/kernfs/dir.c-1321-\tkernfs_put(kn);\n--\nfs/kernfs/dir.c=1382=const struct inode_operations kernfs_dir_iops = {\n--\nfs/kernfs/dir.c-1388-\nfs/kernfs/dir.c:1389:\t.mkdir\t\t= kernfs_iop_mkdir,\nfs/kernfs/dir.c-1390-\t.rmdir\t\t= kernfs_iop_rmdir,\n--\nfs/kernfs/inode.c=247=struct inode *kernfs_get_inode(struct super_block *sb, struct kernfs_node *kn)\n--\nfs/kernfs/inode.c-258-/**\nfs/kernfs/inode.c:259: *\tkernfs_get_inode_prealloc - get inode for kernfs_node\nfs/kernfs/inode.c-260- *\t@prealloc_inode: inode allocated with new_inode() on the target superblock\n--\nfs/kernfs/inode.c-273- */\nfs/kernfs/inode.c:274:struct inode *kernfs_get_inode_prealloc(struct inode *prealloc_inode,\nfs/kernfs/inode.c-275-\t\t\t\t\tstruct kernfs_node *kn)\n--\nfs/kernfs/kernfs-internal.h=172=extern const struct xattr_handler * const kernfs_xattr_handlers[];\nfs/kernfs/kernfs-internal.h:173:struct inode *kernfs_get_inode_prealloc(struct inode *inode,\nfs/kernfs/kernfs-internal.h-174-\t\t\t\t\tstruct kernfs_node *kn);\n--\nfs/namei.c=1843=static struct dentry *lookup_fast(struct nameidata *nd)\n--\nfs/namei.c-1896- */\nfs/namei.c:1897:static struct dentry *finish_lookup(struct inode *dir, struct dentry *dentry,\nfs/namei.c-1898-\t\t\t\tunsigned int flags)\n--\nfs/namei.c=1915=static struct dentry *__lookup_slow(const struct qstr *name,\n--\nfs/namei.c-1940-\t}\nfs/namei.c:1941:\treturn finish_lookup(inode, dentry, flags);\nfs/namei.c-1942-}\n--\nfs/namei.c=4516=struct dentry *vfs_mkdir_no_perm(struct mnt_idmap *, struct inode *, struct dentry *,\n--\nfs/namei.c-4531- */\nfs/namei.c:4532:static struct dentry *lookup_open(struct nameidata *nd, struct file *file,\nfs/namei.c-4533-\t\t\t\t const struct open_flags *op)\n--\nfs/namei.c-4592-\tif (dentry-\u003ed_inode) {\nfs/namei.c:4593:\t\t/* Cached positive dentry: will open in do_open(). */\nfs/namei.c-4594-\t\tgoto out;\n--\nfs/namei.c-4631-\nfs/namei.c:4632:\tdentry = finish_lookup(dir_inode, dentry, nd-\u003eflags);\nfs/namei.c-4633-\tif (IS_ERR(dentry))\n--\nfs/namei.c-4638-\t\t * No need to create a file. If lookup returned a positive\nfs/namei.c:4639:\t\t * dentry, the file will be opened in do_open().\nfs/namei.c-4640-\t\t */\n--\nfs/namei.c-4705-/**\nfs/namei.c:4706: * vfs_lookup_open - open and possibly create a regular file\nfs/namei.c-4707- * @parent: directory to contain file\n--\nfs/namei.c-4727- */\nfs/namei.c:4728:struct file *vfs_lookup_open(struct path *parent, struct qstr *last,\nfs/namei.c-4729-\t\t\t int open_flag, umode_t mode)\n--\nfs/namei.c-4761-\top.mode = mode;\nfs/namei.c:4762:\tdentry = lookup_open(\u0026nd, file, \u0026op);\nfs/namei.c-4763-\n--\nfs/namei.c-4798-}\nfs/namei.c:4799:EXPORT_SYMBOL_FOR_MODULES(vfs_lookup_open, \"nfsd\");\nfs/namei.c-4800-\n--\nfs/namei.c=4832=static const char *open_last_lookups(struct nameidata *nd,\n--\nfs/namei.c-4845-\t\t * Return -EOPNOTSUPP consistently, not -EEXIST, for paths\nfs/namei.c:4846:\t\t * ending in . or .., as lookup_open() does for other names.\nfs/namei.c-4847-\t\t */\n--\nfs/namei.c-4859-\tif (likely(dentry))\nfs/namei.c:4860:\t\tgoto finish_lookup;\nfs/namei.c-4861-\n--\nfs/namei.c-4871-\nfs/namei.c:4872:\tdentry = lookup_open(nd, file, op);\nfs/namei.c-4873-\tif (IS_ERR(dentry))\n--\nfs/namei.c-4881-\nfs/namei.c:4882:finish_lookup:\nfs/namei.c-4883-\tif (nd-\u003edepth)\n--\nfs/namei.c-4893- */\nfs/namei.c:4894:static int do_open(struct nameidata *nd,\nfs/namei.c-4895-\t\t struct file *file, const struct open_flags *op)\n--\nfs/namei.c=5085=static struct file *path_openat(struct nameidata *nd,\n--\nfs/namei.c-5104-\t\tif (!error)\nfs/namei.c:5105:\t\t\terror = do_open(nd, file, op);\nfs/namei.c-5106-\t\tterminate_walk(nd);\n--\nfs/nfs/dir.c=1854=static void block_revalidate(struct dentry *dentry)\n--\nfs/nfs/dir.c-1859-\t/* Any new reference that could lead to an open\nfs/nfs/dir.c:1860:\t * will take -\u003ed_lock in lookup_open() -\u003e d_lookup().\nfs/nfs/dir.c-1861-\t * Holding this lock ensures we cannot race with\n--\nfs/nfs/dir.c=2076=static struct nfs_open_context *create_nfs_open_context(struct dentry *dentry, int open_flags, struct file *filp)\n--\nfs/nfs/dir.c-2080-\nfs/nfs/dir.c:2081:static int do_open(struct inode *inode, struct file *filp)\nfs/nfs/dir.c-2082-{\n--\n\n"
} |
| 5/2 | 2026/10/16 22:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 30343 output: 30 thoughts: 17 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Cgroup Source Code Verification** Alright, time to get down to brass tacks. First things first, let's verify our assumptions about the cgroup core implementation. I want to zero in on `kernel/cgroup/cgroup.c`. This is the beating heart of the cgroup functionality, so a thorough check is absolutely essential. A mismatch here could mean disaster down the line, so careful verification is a must! I need to ensure everything is operating as expected within this crucial piece of the architecture. |
| 6/2 | 2026/10/16 22:29 | tool | grepper |
0mArgs:{
"Expression": "cgroup_mkdir",
"PathPrefixes": [
"kernel/cgroup/"
]
}Results: {
"Output": "kernel/cgroup/cgroup-internal.h=257=void cgroup_lock_and_drain_offline(struct cgroup *cgrp);\nkernel/cgroup/cgroup-internal.h-258-\nkernel/cgroup/cgroup-internal.h:259:struct kernfs_node *cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,\nkernel/cgroup/cgroup-internal.h-260-\t\t\t\t umode_t mode);\n--\nkernel/cgroup/cgroup-v1.c=1148=struct kernfs_syscall_ops cgroup1_kf_syscall_ops = {\n--\nkernel/cgroup/cgroup-v1.c-1150-\t.show_options\t\t= cgroup1_show_options,\nkernel/cgroup/cgroup-v1.c:1151:\t.mkdir\t\t\t= cgroup_mkdir,\nkernel/cgroup/cgroup-v1.c-1152-\t.rmdir\t\t\t= cgroup_rmdir,\n--\nkernel/cgroup/cgroup.c=6093=static bool cgroup_check_hierarchy_limits(struct cgroup *parent)\n--\nkernel/cgroup/cgroup.c-6115-\nkernel/cgroup/cgroup.c:6116:struct kernfs_node *cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,\nkernel/cgroup/cgroup.c-6117-\t\t\t\t umode_t mode)\n--\nkernel/cgroup/cgroup.c=6418=static struct kernfs_syscall_ops cgroup_kf_syscall_ops = {\nkernel/cgroup/cgroup.c-6419-\t.show_options\t\t= cgroup_show_options,\nkernel/cgroup/cgroup.c:6420:\t.mkdir\t\t\t= cgroup_mkdir,\nkernel/cgroup/cgroup.c-6421-\t.rmdir\t\t\t= cgroup_rmdir,\n"
} |
| 7/2 | 2026/10/16 22:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 31020 output: 31 thoughts: 15 Thoughts: Okay, let's dive into this. Here's my thought process on what we're looking at, summarized as you requested: **Navigating the RDT Landscape: fs/resctrl/rdtgroup.c** Alright, the next stop in this investigation has to be `fs/resctrl/rdtgroup.c`. Given the context, this is where I'd expect to find the core logic for Resource Director Technology (RDT) group management within the filesystem. We're talking about controlling and organizing how resources like cache and memory bandwidth are allocated and managed. `rdtgroup.c` is the likely location for the code that handles creating, deleting, and configuring these RDT groups within the filesystem hierarchy. It’s where the kernel likely enforces the policies and restrictions that govern how processes and containers can leverage RDT capabilities. I'll be particularly interested in how it interacts with the underlying hardware capabilities, the interface it provides to userspace, and how it handles the interaction with the CPU resource allocation mechanisms. Let's see what gems are contained within. |
| 8/2 | 2026/10/16 22:29 | tool | grepper |
0mArgs:{
"Expression": "rdtgroup_mkdir",
"PathPrefixes": [
"fs/resctrl/"
]
}Results: {
"Output": "fs/resctrl/rdtgroup.c=2415=static int resctrl_mkdir_event_configs(struct rdt_resource *r, struct kernfs_node *l3_mon_kn)\n--\nfs/resctrl/rdtgroup.c-2448-\nfs/resctrl/rdtgroup.c:2449:static int rdtgroup_mkdir_info_resdir(void *priv, char *name,\nfs/resctrl/rdtgroup.c-2450-\t\t\t\t unsigned long fflags)\n--\nfs/resctrl/rdtgroup.c=2505=static int rdtgroup_create_info_dir(struct kernfs_node *parent_kn)\n--\nfs/resctrl/rdtgroup.c-2525-\t\tfflags = fflags_from_resource(r) | RFTYPE_CTRL_INFO;\nfs/resctrl/rdtgroup.c:2526:\t\tret = rdtgroup_mkdir_info_resdir(s, s-\u003ename, fflags);\nfs/resctrl/rdtgroup.c-2527-\t\tif (ret)\n--\nfs/resctrl/rdtgroup.c-2533-\t\tsprintf(name, \"%s_MON\", r-\u003ename);\nfs/resctrl/rdtgroup.c:2534:\t\tret = rdtgroup_mkdir_info_resdir(r, name, fflags);\nfs/resctrl/rdtgroup.c-2535-\t\tif (ret)\n--\nfs/resctrl/rdtgroup.c=4034=static void mkdir_rdt_prepare_clean(struct rdtgroup *rgrp)\n--\nfs/resctrl/rdtgroup.c-4044- */\nfs/resctrl/rdtgroup.c:4045:static struct kernfs_node *rdtgroup_mkdir_mon(struct kernfs_node *parent_kn,\nfs/resctrl/rdtgroup.c-4046-\t\t\t\t\t const char *name, umode_t mode)\n--\nfs/resctrl/rdtgroup.c-4086- */\nfs/resctrl/rdtgroup.c:4087:static struct kernfs_node *rdtgroup_mkdir_ctrl_mon(struct kernfs_node *parent_kn,\nfs/resctrl/rdtgroup.c-4088-\t\t\t\t\t\t const char *name, umode_t mode)\n--\nfs/resctrl/rdtgroup.c-4151-\nfs/resctrl/rdtgroup.c:4152:static struct kernfs_node *rdtgroup_mkdir(struct kernfs_node *parent_kn,\nfs/resctrl/rdtgroup.c-4153-\t\t\t\t\t const char *name, umode_t mode)\n--\nfs/resctrl/rdtgroup.c-4164-\tif (resctrl_arch_alloc_capable() \u0026\u0026 parent_kn == rdtgroup_default.kn)\nfs/resctrl/rdtgroup.c:4165:\t\treturn rdtgroup_mkdir_ctrl_mon(parent_kn, name, mode);\nfs/resctrl/rdtgroup.c-4166-\n--\nfs/resctrl/rdtgroup.c-4168-\tif (resctrl_arch_mon_capable())\nfs/resctrl/rdtgroup.c:4169:\t\treturn rdtgroup_mkdir_mon(parent_kn, name, mode);\nfs/resctrl/rdtgroup.c-4170-\n--\nfs/resctrl/rdtgroup.c=4465=static struct kernfs_syscall_ops rdtgroup_kf_syscall_ops = {\nfs/resctrl/rdtgroup.c:4466:\t.mkdir\t\t= rdtgroup_mkdir,\nfs/resctrl/rdtgroup.c-4467-\t.rmdir\t\t= rdtgroup_rmdir,\n"
} |
| 9/2 | 2026/10/16 22:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 32261 output: 35 thoughts: 20 Thoughts: Here's my thought process, as if I were considering this issue: **Investigating `scops->mkdir` Implementations** Okay, so we need to get a handle on how `scops->mkdir` is being used throughout the kernel. This is a common pattern when I'm dealing with potential issues in a subsystem's operation, or when I'm looking to optimize or refactor a critical component. We need to understand the landscape of implementations before diving deeper. Let's do a quick search – I'll use `git grep` and some targeted keywords to see where this function is called and defined. I'll need to look at both the calls and the implementations themselves to get a good feel for the existing code. Are there any unexpected uses? Any surprising patterns? This investigation is critical to ensure we don't inadvertently break existing functionality or introduce unexpected side effects down the line. We want to identify the places that *use* this feature. |
| 10/2 | 2026/10/16 22:29 | tool | grepper |
0mArgs:{
"Expression": "\\.mkdir[ \\t]*=",
"PathPrefixes": [
"fs/",
"kernel/"
]
}Results: {
"Output": "fs/9p/vfs_inode.c=1365=static const struct inode_operations v9fs_dir_inode_operations_dotu = {\n--\nfs/9p/vfs_inode.c-1371-\t.unlink = v9fs_vfs_unlink,\nfs/9p/vfs_inode.c:1372:\t.mkdir = v9fs_vfs_mkdir,\nfs/9p/vfs_inode.c-1373-\t.rmdir = v9fs_vfs_rmdir,\n--\nfs/9p/vfs_inode.c=1380=static const struct inode_operations v9fs_dir_inode_operations = {\n--\nfs/9p/vfs_inode.c-1384-\t.unlink = v9fs_vfs_unlink,\nfs/9p/vfs_inode.c:1385:\t.mkdir = v9fs_vfs_mkdir,\nfs/9p/vfs_inode.c-1386-\t.rmdir = v9fs_vfs_rmdir,\n--\nfs/9p/vfs_inode_dotl.c=961=const struct inode_operations v9fs_dir_inode_operations_dotl = {\n--\nfs/9p/vfs_inode_dotl.c-967-\t.unlink = v9fs_vfs_unlink,\nfs/9p/vfs_inode_dotl.c:968:\t.mkdir = v9fs_vfs_mkdir_dotl,\nfs/9p/vfs_inode_dotl.c-969-\t.rmdir = v9fs_vfs_rmdir,\n--\nfs/ceph/dir.c=2255=const struct inode_operations ceph_dir_iops = {\n--\nfs/ceph/dir.c-2264-\t.symlink = ceph_symlink,\nfs/ceph/dir.c:2265:\t.mkdir = ceph_mkdir,\nfs/ceph/dir.c-2266-\t.link = ceph_link,\n--\nfs/ceph/dir.c=2274=const struct inode_operations ceph_snapdir_iops = {\n--\nfs/ceph/dir.c-2277-\t.getattr = ceph_getattr,\nfs/ceph/dir.c:2278:\t.mkdir = ceph_mkdir,\nfs/ceph/dir.c-2279-\t.rmdir = ceph_unlink,\n--\nfs/ecryptfs/inode.c=1123=const struct inode_operations ecryptfs_dir_iops = {\n--\nfs/ecryptfs/inode.c-1128-\t.symlink = ecryptfs_symlink,\nfs/ecryptfs/inode.c:1129:\t.mkdir = ecryptfs_mkdir,\nfs/ecryptfs/inode.c-1130-\t.rmdir = ecryptfs_rmdir,\n--\nfs/gfs2/inode.c=2307=static const struct inode_operations gfs2_dir_iops = {\n--\nfs/gfs2/inode.c-2312-\t.symlink = gfs2_symlink,\nfs/gfs2/inode.c:2313:\t.mkdir = gfs2_mkdir,\nfs/gfs2/inode.c-2314-\t.rmdir = gfs2_unlink,\n--\nfs/jffs2/dir.c=56=const struct inode_operations jffs2_dir_inode_operations =\n--\nfs/jffs2/dir.c-62-\t.symlink =\tjffs2_symlink,\nfs/jffs2/dir.c:63:\t.mkdir =\tjffs2_mkdir,\nfs/jffs2/dir.c-64-\t.rmdir =\tjffs2_rmdir,\n--\nfs/omfs/dir.c=448=const struct inode_operations omfs_dir_inops = {\nfs/omfs/dir.c-449-\t.lookup = omfs_lookup,\nfs/omfs/dir.c:450:\t.mkdir = omfs_mkdir,\nfs/omfs/dir.c-451-\t.rename = omfs_rename,\n--\nfs/orangefs/namei.c=416=const struct inode_operations orangefs_dir_inode_operations = {\n--\nfs/orangefs/namei.c-422-\t.symlink = orangefs_symlink,\nfs/orangefs/namei.c:423:\t.mkdir = orangefs_mkdir,\nfs/orangefs/namei.c-424-\t.rmdir = orangefs_unlink,\n--\nfs/overlayfs/overlayfs.h=250=static inline struct dentry *ovl_do_mkdir(struct ovl_fs *ofs,\n--\nfs/overlayfs/overlayfs.h-259-\tret = vfs_mkdir(ovl_upper_mnt_idmap(ofs), dir, dentry, mode, NULL);\nfs/overlayfs/overlayfs.h:260:\tpr_debug(\"...mkdir = %i\\n\", PTR_ERR_OR_ZERO(ret));\nfs/overlayfs/overlayfs.h-261-\treturn ret;\n--\nfs/smb/client/cifsfs.c=1244=const struct inode_operations cifs_dir_inode_ops = {\n--\nfs/smb/client/cifsfs.c-1251-\t.link = cifs_hardlink,\nfs/smb/client/cifsfs.c:1252:\t.mkdir = cifs_mkdir,\nfs/smb/client/cifsfs.c-1253-\t.rmdir = cifs_rmdir,\n--\nfs/smb/client/smb1ops.c=1399=struct smb_version_operations smb1_operations = {\n--\nfs/smb/client/smb1ops.c-1442-\t.echo = CIFSSMBEcho,\nfs/smb/client/smb1ops.c:1443:\t.mkdir = CIFSSMBMkDir,\nfs/smb/client/smb1ops.c-1444-\t.mkdir_setinfo = cifs_mkdir_setinfo,\n--\nfs/smb/client/smb2ops.c=5763=struct smb_version_operations smb20_operations = {\n--\nfs/smb/client/smb2ops.c-5805-\t.set_compression = smb2_set_compression,\nfs/smb/client/smb2ops.c:5806:\t.mkdir = smb2_mkdir,\nfs/smb/client/smb2ops.c-5807-\t.mkdir_setinfo = smb2_mkdir_setinfo,\n--\nfs/smb/client/smb2ops.c=5865=struct smb_version_operations smb21_operations = {\n--\nfs/smb/client/smb2ops.c-5908-\t.set_compression = smb2_set_compression,\nfs/smb/client/smb2ops.c:5909:\t.mkdir = smb2_mkdir,\nfs/smb/client/smb2ops.c-5910-\t.mkdir_setinfo = smb2_mkdir_setinfo,\n--\nfs/smb/client/smb2ops.c=5969=struct smb_version_operations smb30_operations = {\n--\nfs/smb/client/smb2ops.c-6015-\t.set_compression = smb2_set_compression,\nfs/smb/client/smb2ops.c:6016:\t.mkdir = smb2_mkdir,\nfs/smb/client/smb2ops.c-6017-\t.mkdir_setinfo = smb2_mkdir_setinfo,\n--\nfs/smb/client/smb2ops.c=6085=struct smb_version_operations smb311_operations = {\n--\nfs/smb/client/smb2ops.c-6130-\t.set_compression = smb2_set_compression,\nfs/smb/client/smb2ops.c:6131:\t.mkdir = smb2_mkdir,\nfs/smb/client/smb2ops.c-6132-\t.mkdir_setinfo = smb2_mkdir_setinfo,\n--\nfs/tracefs/inode.c=725=__init struct dentry *tracefs_create_instance_dir(const char *name,\n--\nfs/tracefs/inode.c-739-\nfs/tracefs/inode.c:740:\ttracefs_ops.mkdir = mkdir;\nfs/tracefs/inode.c-741-\ttracefs_ops.rmdir = rmdir;\n--\nfs/ubifs/dir.c=1742=const struct inode_operations ubifs_dir_inode_operations = {\n--\nfs/ubifs/dir.c-1747-\t.unlink = ubifs_unlink,\nfs/ubifs/dir.c:1748:\t.mkdir = ubifs_mkdir,\nfs/ubifs/dir.c-1749-\t.rmdir = ubifs_rmdir,\n--\nfs/vboxsf/dir.c=469=const struct inode_operations vboxsf_dir_iops = {\n--\nfs/vboxsf/dir.c-471-\t.create = vboxsf_dir_mkfile,\nfs/vboxsf/dir.c:472:\t.mkdir = vboxsf_dir_mkdir,\nfs/vboxsf/dir.c-473-\t.atomic_open = vboxsf_dir_atomic_open,\n"
} |
| 11/2 | 2026/10/16 22:29 | llm | patch-evaluator |
1mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 35000 output: 20 thoughts: 65 Thoughts: Here's my summary, as if I were thinking it: **Digging into Kernel Filesystem Calls** Alright, let's get down to brass tacks. I need to figure out what's going on with these `kernfs_syscall_ops` structures and, specifically, their `.mkdir` implementation. My initial hunch, after a quick memory jog, is that it's likely being handled by `cgroup_kf_syscall_ops` or `rdtgroup_kf_syscall_ops`. They were the only two that immediately sprang to mind as defining a `.mkdir` function. However, I can't rely on memory alone, I need verification. Therefore, the next logical step is to run a quick, thorough grepper search across the entire kernel source tree. This will allow me to comprehensively scan for any instances of `struct kernfs_syscall_ops`. This should turn up all uses and all implementations, giving me a definitive list to work from, and let me be absolutely sure I'm not missing anything. I need to make sure I get a good, solid picture of how this struct is used. |
| 12/2 | 2026/10/16 22:30 | tool | grepper |
0mArgs:{
"Expression": "kernfs_syscall_ops"
}Results: {
"Output": "fs/kernfs/dir.c=1003=unsigned int kernfs_root_flags(struct kernfs_node *kn)\n--\nfs/kernfs/dir.c-1016- */\nfs/kernfs/dir.c:1017:struct kernfs_root *kernfs_create_root(struct kernfs_syscall_ops *scops,\nfs/kernfs/dir.c-1018-\t\t\t\t unsigned int flags, void *priv)\n--\nfs/kernfs/dir.c=1291=static struct dentry *kernfs_iop_mkdir(struct mnt_idmap *idmap,\n--\nfs/kernfs/dir.c-1295-\tstruct kernfs_node *parent = dir-\u003ei_private;\nfs/kernfs/dir.c:1296:\tstruct kernfs_syscall_ops *scops = kernfs_root(parent)-\u003esyscall_ops;\nfs/kernfs/dir.c-1297-\tstruct kernfs_node *kn;\n--\nfs/kernfs/dir.c=1333=static int kernfs_iop_rmdir(struct inode *dir, struct dentry *dentry)\n--\nfs/kernfs/dir.c-1335-\tstruct kernfs_node *kn = kernfs_dentry_node(dentry);\nfs/kernfs/dir.c:1336:\tstruct kernfs_syscall_ops *scops = kernfs_root(kn)-\u003esyscall_ops;\nfs/kernfs/dir.c-1337-\tint ret;\n--\nfs/kernfs/dir.c=1351=static int kernfs_iop_rename(struct mnt_idmap *idmap,\n--\nfs/kernfs/dir.c-1357-\tstruct kernfs_node *new_parent = new_dir-\u003ei_private;\nfs/kernfs/dir.c:1358:\tstruct kernfs_syscall_ops *scops = kernfs_root(kn)-\u003esyscall_ops;\nfs/kernfs/dir.c-1359-\tint ret;\n--\nfs/kernfs/kernfs-internal.h=33=struct kernfs_root {\n--\nfs/kernfs/kernfs-internal.h-42-\tu32\t\t\tid_highbits;\nfs/kernfs/kernfs-internal.h:43:\tstruct kernfs_syscall_ops *syscall_ops;\nfs/kernfs/kernfs-internal.h-44-\n--\nfs/kernfs/mount.c=28=static int kernfs_sop_show_options(struct seq_file *sf, struct dentry *dentry)\n--\nfs/kernfs/mount.c-30-\tstruct kernfs_root *root = kernfs_root(kernfs_dentry_node(dentry));\nfs/kernfs/mount.c:31:\tstruct kernfs_syscall_ops *scops = root-\u003esyscall_ops;\nfs/kernfs/mount.c-32-\n--\nfs/kernfs/mount.c=38=static int kernfs_sop_show_path(struct seq_file *sf, struct dentry *dentry)\n--\nfs/kernfs/mount.c-41-\tstruct kernfs_root *root = kernfs_root(node);\nfs/kernfs/mount.c:42:\tstruct kernfs_syscall_ops *scops = root-\u003esyscall_ops;\nfs/kernfs/mount.c-43-\n--\nfs/resctrl/rdtgroup.c=4448=static int rdtgroup_show_options(struct seq_file *seq, struct kernfs_root *kf)\n--\nfs/resctrl/rdtgroup.c-4464-\nfs/resctrl/rdtgroup.c:4465:static struct kernfs_syscall_ops rdtgroup_kf_syscall_ops = {\nfs/resctrl/rdtgroup.c-4466-\t.mkdir\t\t= rdtgroup_mkdir,\n--\ninclude/linux/kernfs.h=199=struct kernfs_node {\n--\ninclude/linux/kernfs.h-239-/*\ninclude/linux/kernfs.h:240: * kernfs_syscall_ops may be specified on kernfs_create_root() to support\ninclude/linux/kernfs.h-241- * syscalls. These optional callbacks are invoked on the matching syscalls\n--\ninclude/linux/kernfs.h-246- */\ninclude/linux/kernfs.h:247:struct kernfs_syscall_ops {\ninclude/linux/kernfs.h-248-\tint (*show_options)(struct seq_file *sf, struct kernfs_root *root);\n--\ninclude/linux/kernfs.h=426=struct dentry *kernfs_node_dentry(struct kernfs_node *kn,\ninclude/linux/kernfs.h-427-\t\t\t\t struct super_block *sb);\ninclude/linux/kernfs.h:428:struct kernfs_root *kernfs_create_root(struct kernfs_syscall_ops *scops,\ninclude/linux/kernfs.h-429-\t\t\t\t unsigned int flags, void *priv);\n--\ninclude/linux/kernfs.h=526=static inline struct kernfs_root *\ninclude/linux/kernfs.h:527:kernfs_create_root(struct kernfs_syscall_ops *scops, unsigned int flags,\ninclude/linux/kernfs.h-528-\t\t void *priv)\n--\nkernel/cgroup/cgroup-internal.h=284=extern struct cftype cgroup1_base_files[];\nkernel/cgroup/cgroup-internal.h:285:extern struct kernfs_syscall_ops cgroup1_kf_syscall_ops;\nkernel/cgroup/cgroup-internal.h-286-extern const struct fs_parameter_spec cgroup1_fs_parameters[];\n--\nkernel/cgroup/cgroup-v1.c=1092=int cgroup1_reconfigure(struct fs_context *fc)\n--\nkernel/cgroup/cgroup-v1.c-1147-\nkernel/cgroup/cgroup-v1.c:1148:struct kernfs_syscall_ops cgroup1_kf_syscall_ops = {\nkernel/cgroup/cgroup-v1.c-1149-\t.rename\t\t\t= cgroup1_rename,\n--\nkernel/cgroup/cgroup.c=1532=struct cgroup *task_cgroup_from_root(struct task_struct *task,\n--\nkernel/cgroup/cgroup.c-1567-\nkernel/cgroup/cgroup.c:1568:static struct kernfs_syscall_ops cgroup_kf_syscall_ops;\nkernel/cgroup/cgroup.c-1569-\n--\nkernel/cgroup/cgroup.c=2150=int cgroup_setup_root(struct cgroup_root *root, u32 ss_mask)\n--\nkernel/cgroup/cgroup.c-2153-\tstruct cgroup *root_cgrp = \u0026root-\u003ecgrp;\nkernel/cgroup/cgroup.c:2154:\tstruct kernfs_syscall_ops *kf_sops;\nkernel/cgroup/cgroup.c-2155-\tstruct css_set *cset;\n--\nkernel/cgroup/cgroup.c=6401=int cgroup_rmdir(struct kernfs_node *kn)\n--\nkernel/cgroup/cgroup.c-6417-\nkernel/cgroup/cgroup.c:6418:static struct kernfs_syscall_ops cgroup_kf_syscall_ops = {\nkernel/cgroup/cgroup.c-6419-\t.show_options\t\t= cgroup_show_options,\n"
} |
| 13/2 | 2026/10/16 22:31 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 37259 output: 28 thoughts: 1605 Thoughts: **Analyzing the mkdir Implementation Changes** Okay, so I'm digging into these changes related to the `mkdir` functionality within the kernel. It seems like `cgroup` and `rdtgroup` are the only ones with `mkdir` within `kernfs_syscall_ops`, so that's a good starting point. Let's break down the patch: 1. **`fs/open.c`**: This seems to be where the groundwork is laid. `build_open_flags` handles the `O_IS_MKDIR` flag. It's setting `S_IFDIR` correctly, which is good. There are several checks for flag combinations (`O_CREAT`, `O_DIRECTORY`, `O_EXCL`, `O_TMPFILE`) to ensure things work as expected and that there are no contradictions in the file open flags. The checks are pretty standard to prevent abuse of the open flags. 2. **`fs/namei.c`**: This is where the meat of the `mkdir` implementation lies. The patch introduces `may_o_create`, which now utilizes `create_dir` (derived from `O_IS_MKDIR`). It calls into `security_path_mkdir` and `security_inode_mkdir`, which is the correct path for security checks and inode operations. I see it includes a warning if the mode isn't a directory, which is a good sanity check. The patch is now using a `vfs_create` helper to create the regular file, now updated to call `vfs_create_no_perm`. The main entry point, `lookup_open`, checks for the `SB_I_NO_ATOMIC_MKDIR` flag, uses proper locking (`inode_lock_nested`), and sets the mode correctly. Crucially, if `create_dir` is set, `vfs_mkdir_no_perm` is called, and I see a `WARN_ON_ONCE` for a negative dentry, which is a key sanity check. The extraction of `vfs_mkdir_no_perm` is likely for code reuse and clearer structure. `atomic_open` now handles file system notifications correctly with `fsnotify_mkdir` and `fsnotify_create`, based on whether it is a directory or regular file. I noticed that the code also now uses `SB_I_NO_ATOMIC_MKDIR` for `O_IS_MKDIR` as well. The code also fixes the check within `do_open` to ensure that it has the appropriate directory flags. 3. **`fs/kernfs/dir.c`**: This is where things get interesting in `kernfs`. The `kernfs_iop_mkdir` function seems to have been completely revamped. Instead of returning an error code (now cast to an error pointer), it now preallocates an inode, gets a reference, and calls `scops->mkdir`. The code is then instantiating the dentry with `d_splice_alias(inode, dentry)`. This is a *significant* change because kernfs now creates a proper dentry. This is a very interesting change in terms of race conditions and the atomicity of the operation, which I'll have to consider. 4. **`fs/kernfs/inode.c`**: Added a `kernfs_get_inode_prealloc` function. The code also now uses a read/write lock for the kernfs attributes. 5. **`fs/kernfs/mount.c`**: Removed read/write lock down/up operations from `kernfs_fill_super`. 6. **`kernel/cgroup/cgroup.c`**: `cgroup_mkdir` returns a `struct kernfs_node *` now instead of an `int`. 7. **`fs/resctrl/rdtgroup.c`**: `rdtgroup_mkdir` also now returns a `struct kernfs_node *` instead of an `int`. 8. **`SB_I_NO_ATOMIC_MKDIR` Filesystems**: These filesystems, including 9p, ceph, and nfs, are now explicitly marked as not supporting atomic `mkdir`, indicating a potential compatibility concern with the updated behavior. It looks like the `WorthFuzzing` flag is true, so I need to think about which functions to mark as "focus symbols". I need to avoid the generic stuff and target the specific logic added or modified. I'm especially interested in those new `kernfs` changes, since this could open up interesting fuzzing opportunities. Let's look at `fs/open.c` again. I see `build_open_flags` is an inline function. I need to make |
| 14/2 | 2026/10/16 22:31 | tool | grepper |
0mArgs:{
"Expression": "build_open_flags",
"PathPrefixes": [
"fs/"
]
}Results: {
"Output": "fs/internal.h=198=extern struct open_how build_open_how(int flags, umode_t mode);\nfs/internal.h:199:extern int build_open_flags(const struct open_how *how, struct open_flags *op);\nfs/internal.h-200-struct file *file_close_fd_locked(struct files_struct *files, unsigned fd);\n--\nfs/open.c=1185=inline struct open_how build_open_how(int flags, umode_t mode)\n--\nfs/open.c-1200-\nfs/open.c:1201:inline int build_open_flags(const struct open_how *how, struct open_flags *op)\nfs/open.c-1202-{\n--\nfs/open.c-1225-\t * Older syscalls implicitly clear all of the invalid flags or argument\nfs/open.c:1226:\t * values before calling build_open_flags(), but openat2(2) checks all\nfs/open.c-1227-\t * of its arguments.\n--\nfs/open.c=1386=struct file *file_open_name(struct filename *name, int flags, umode_t mode)\n--\nfs/open.c-1389-\tstruct open_how how = build_open_how(flags, mode);\nfs/open.c:1390:\tint err = build_open_flags(\u0026how, \u0026op);\nfs/open.c-1391-\tif (err)\n--\nfs/open.c=1414=struct file *file_open_root(const struct path *root,\n--\nfs/open.c-1418-\tstruct open_how how = build_open_how(flags, mode);\nfs/open.c:1419:\tint err = build_open_flags(\u0026how, \u0026op);\nfs/open.c-1420-\tif (err)\n--\nfs/open.c=1426=static int do_sys_openat2(int dfd, const char __user *filename,\n--\nfs/open.c-1429-\tstruct open_flags op;\nfs/open.c:1430:\tint err = build_open_flags(how, \u0026op);\nfs/open.c-1431-\tif (unlikely(err))\n"
} |
| 15/2 | 2026/10/16 22:31 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 40518 output: 30 thoughts: 337 Thoughts: **Analyzing the Potential Impact of `build_open_flags` on Fuzzing Efficiency** Okay, so I'm looking at `build_open_flags`. It's `inline` in `fs/open.c` but declared `extern` in `fs/internal.h`. Typical C99/GNU C, the `inline` might mean it's either inlined within `fs/open.c` or produces an external definition. The key thing here is `do_sys_openat2` calls `build_open_flags` in `fs/open.c`. And `do_sys_openat2` is involved in *every* `open` call. So, `build_open_flags` is called on *every* open, effectively every single program that makes an open syscall uses `build_open_flags`. It's a hugely prevalent function! That triggers a red flag immediately. What about ubiquitous functions? I remember the guidelines: "AVOID UBIQUITOUS LIFECYCLE HOT-PATHS." Don't list these functions! Don't list general memory allocators, deallocators, page fault and trap handlers or core synchronization primitives. Listing them messes up the fuzzing because all these unrelated tests are classified as "focused". So, if I were to list `do_sys_openat2` or `build_open_flags`, *every single* syzkaller program, almost without exception, would be classified as "focused" because they all open files. Effectively, the fuzzing would be diluted to uselessness. Okay, a thought... What about `lookup_open`? Is `lookup_open` called on every open? I need to check how frequently `lookup_open` is called. I need to look into `fs/namei.c`. |
| 16/2 | 2026/10/16 22:32 | tool | grepper |
0mArgs:{
"Expression": "lookup_open",
"PathPrefixes": [
"fs/namei.c"
]
}Results: {
"Output": "fs/namei.c=4516=struct dentry *vfs_mkdir_no_perm(struct mnt_idmap *, struct inode *, struct dentry *,\n--\nfs/namei.c-4531- */\nfs/namei.c:4532:static struct dentry *lookup_open(struct nameidata *nd, struct file *file,\nfs/namei.c-4533-\t\t\t\t const struct open_flags *op)\n--\nfs/namei.c-4705-/**\nfs/namei.c:4706: * vfs_lookup_open - open and possibly create a regular file\nfs/namei.c-4707- * @parent: directory to contain file\n--\nfs/namei.c-4727- */\nfs/namei.c:4728:struct file *vfs_lookup_open(struct path *parent, struct qstr *last,\nfs/namei.c-4729-\t\t\t int open_flag, umode_t mode)\n--\nfs/namei.c-4761-\top.mode = mode;\nfs/namei.c:4762:\tdentry = lookup_open(\u0026nd, file, \u0026op);\nfs/namei.c-4763-\n--\nfs/namei.c-4798-}\nfs/namei.c:4799:EXPORT_SYMBOL_FOR_MODULES(vfs_lookup_open, \"nfsd\");\nfs/namei.c-4800-\n--\nfs/namei.c=4832=static const char *open_last_lookups(struct nameidata *nd,\n--\nfs/namei.c-4845-\t\t * Return -EOPNOTSUPP consistently, not -EEXIST, for paths\nfs/namei.c:4846:\t\t * ending in . or .., as lookup_open() does for other names.\nfs/namei.c-4847-\t\t */\n--\nfs/namei.c-4871-\nfs/namei.c:4872:\tdentry = lookup_open(nd, file, op);\nfs/namei.c-4873-\tif (IS_ERR(dentry))\n"
} |
| 17/2 | 2026/10/16 22:32 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 41838 output: 35 thoughts: 29 Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind the user is an expert: **Investigating the Call Site of `lookup_open`** Alright, I'm trying to trace the invocation of `lookup_open` within this codebase. My immediate thought is, when does this specific function actually *run*? My experience says the best place to start looking for its use is around line 4872, where I recall seeing the `open_last_lookups` function, which, given the naming convention, seems likely to be a related function or a place where `lookup_open` might be called. Let's start there. |
| 18/2 | 2026/10/16 22:32 | tool | read-file |
0mArgs:{
"File": "fs/namei.c",
"FirstLine": 4840,
"LineCount": 45
}Results: {
"Contents": "4840:\t\n4841:\t\tif (nd-\u003elast_type != LAST_NORM) {\n4842:\t\t\tif (nd-\u003edepth)\n4843:\t\t\t\tput_link(nd);\n4844:\t\t\t/*\n4845:\t\t\t * Return -EOPNOTSUPP consistently, not -EEXIST, for paths\n4846:\t\t\t * ending in . or .., as lookup_open() does for other names.\n4847:\t\t\t */\n4848:\t\t\tif (O_IS_MKDIR(open_flag) \u0026\u0026\n4849:\t\t\t (nd-\u003epath.dentry-\u003ed_sb-\u003es_iflags \u0026 SB_I_NO_ATOMIC_MKDIR))\n4850:\t\t\t\treturn ERR_PTR(-EOPNOTSUPP);\n4851:\t\t\treturn handle_dots(nd, nd-\u003elast_type);\n4852:\t\t}\n4853:\t\n4854:\t\t/* We _can_ be in RCU mode here */\n4855:\t\tdentry = lookup_fast_for_open(nd, open_flag);\n4856:\t\tif (IS_ERR(dentry))\n4857:\t\t\treturn ERR_CAST(dentry);\n4858:\t\n4859:\t\tif (likely(dentry))\n4860:\t\t\tgoto finish_lookup;\n4861:\t\n4862:\t\tif (!(open_flag \u0026 O_CREAT)) {\n4863:\t\t\tif (WARN_ON_ONCE(nd-\u003eflags \u0026 LOOKUP_RCU))\n4864:\t\t\t\treturn ERR_PTR(-ECHILD);\n4865:\t\t} else {\n4866:\t\t\tif (nd-\u003eflags \u0026 LOOKUP_RCU) {\n4867:\t\t\t\tif (!try_to_unlazy(nd))\n4868:\t\t\t\t\treturn ERR_PTR(-ECHILD);\n4869:\t\t\t}\n4870:\t\t}\n4871:\t\n4872:\t\tdentry = lookup_open(nd, file, op);\n4873:\t\tif (IS_ERR(dentry))\n4874:\t\t\treturn ERR_CAST(dentry);\n4875:\t\n4876:\t\tif (file-\u003ef_mode \u0026 (FMODE_OPENED | FMODE_CREATED)) {\n4877:\t\t\tdput(nd-\u003epath.dentry);\n4878:\t\t\tnd-\u003epath.dentry = dentry;\n4879:\t\t\treturn NULL;\n4880:\t\t}\n4881:\t\n4882:\tfinish_lookup:\n4883:\t\tif (nd-\u003edepth)\n4884:\t\t\tput_link(nd);\n"
} |
| 19/2 | 2026/10/16 22:32 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 42726 output: 35 thoughts: 626 Thoughts: **Analyzing File Creation Logic and Potential Issues** Okay, so I'm digging into how the kernel handles file creation, specifically when using `O_CREAT` and `O_EXCL` flags. My initial observation is that in `open_last_lookups`, `lookup_open` gets called when a `dentry` isn't found in the fast lookup path. This makes sense – if the file doesn't exist (like with `O_CREAT`) or the fast lookup fails, we need the slower, more thorough method. The crucial part here is in `lookup_fast_for_open`. I'm seeing that with `O_CREAT | O_EXCL` *every* time, it returns `NULL`. That triggers a call to `lookup_open`, which is what I suspected. Now, `may_o_create` gets called *inside* `lookup_open`. Is `may_o_create` static? I need to know the scope of this function. Line 4378 of `fs/namei.c` confirms it is static, but static functions in a .c file can still be symbols in vmlinux unless the compiler inlines them. But, let's see which functions actually call `may_o_create`? It's `lookup_open` and `dentry_create`. Good to know. Now my mind is racing about other instances of code similar to this; and which ones were modified, especially in `fs/kernfs/`. Wait, what about `kernfs_iop_mkdir`? It's static, but it's assigned to `kernfs_dir_iops.mkdir`. Aha, a critical point! What about `kernfs_get_inode_prealloc`? *That* one's non-static and exported in `kernfs-internal.h`. It's defined in `fs/kernfs/inode.c`. That's important. And there's `cgroup_mkdir` from `kernel/cgroup/cgroup.c`. It's non-static too. Interesting. What about `rdtgroup_mkdir`? It's static, in `fs/resctrl/rdtgroup.c`. I need to confirm if resctrl is enabled on amd64. That seems like the logical next step – check `fs/resctrl/Kconfig`. I'll need to revisit this analysis with that knowledge. This is critical to the potential impact of all this. |
| 20/2 | 2026/10/16 22:32 | tool | grepper |
0mArgs:{
"Expression": "config RESCTRL",
"PathPrefixes": [
"fs/",
"arch/x86/"
]
}Results: {
"Output": "fs/resctrl/Kconfig:1:config RESCTRL_FS\nfs/resctrl/Kconfig-2-\tbool \"CPU Resource Control Filesystem (resctrl)\"\n--\nfs/resctrl/Kconfig-25-\nfs/resctrl/Kconfig:26:config RESCTRL_FS_PSEUDO_LOCK\nfs/resctrl/Kconfig-27-\tbool\n--\nfs/resctrl/Kconfig-32-\nfs/resctrl/Kconfig:33:config RESCTRL_RMID_DEPENDS_ON_CLOSID\nfs/resctrl/Kconfig-34-\tbool\n"
} |
| 21/2 | 2026/10/16 22:32 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 44080 output: 33 thoughts: 21 Thoughts: Okay, let's dive into this. Here's my understanding, presented as my own mental processing: **Examining RESCTRL_FS Dependencies in fs/resctrl/Kconfig** Alright, time to get to it. My initial thought is to make sure I understand the full scope of `RESCTRL_FS` within the kernel. The first logical step is to check its dependencies. That means, I'm going to head directly into the `fs/resctrl/Kconfig` file. I need to figure out what other kernel features, flags, or configuration options are *required* for `RESCTRL_FS` to function correctly. I'm looking for the "requires" and "depends on" lines in the Kconfig file, of course. I need to build a clear picture of what needs to be in place before I even start to think about enabling or modifying `RESCTRL_FS`. That way I avoid any unexpected issues and I understand its relationship within the overall system. Time to get to work! |
| 22/2 | 2026/10/16 22:32 | tool | read-file |
0mArgs:{
"File": "fs/resctrl/Kconfig",
"FirstLine": 1,
"LineCount": 25
}Results: {
"Contents": " 1:\tconfig RESCTRL_FS\n 2:\t\tbool \"CPU Resource Control Filesystem (resctrl)\"\n 3:\t\tdepends on ARCH_HAS_CPU_RESCTRL\n 4:\t\tselect KERNFS\n 5:\t\tselect PROC_CPU_RESCTRL if PROC_FS\n 6:\t\thelp\n 7:\t\t Some architectures provide hardware facilities to group tasks and\n 8:\t\t monitor and control their usage of memory system resources such as\n 9:\t\t caches and memory bandwidth. Examples of such facilities include\n 10:\t\t Intel's Resource Director Technology (Intel(R) RDT) and AMD's\n 11:\t\t Platform Quality of Service (AMD QoS).\n 12:\t\n 13:\t\t If your system has the necessary support and you want to be able to\n 14:\t\t assign tasks to groups and manipulate the associated resource\n 15:\t\t monitors and controls from userspace, say Y here to get a mountable\n 16:\t\t 'resctrl' filesystem that lets you do just that.\n 17:\t\n 18:\t\t If nothing mounts or prods the 'resctrl' filesystem, resource\n 19:\t\t controls and monitors are left in a quiescent, permissive state.\n 20:\t\n 21:\t\t On architectures where this can be disabled independently, it is\n 22:\t\t safe to say N.\n 23:\t\n 24:\t\t See \u003cfile:Documentation/filesystems/resctrl.rst\u003e for more information.\n 25:\t\n"
} |
| 23/2 | 2026/10/16 22:32 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 44725 output: 29 thoughts: 1450 Thoughts: **Analyzing Kernel Configuration Requirements** Okay, so I need to figure out the minimal kernel configurations required to reach the code modifications. Let's break this down. First, cgroup… `CONFIG_CGROUPS` is the standard for cgroups. Wait, but what else is modified? I see `fs/namei.c`, `fs/open.c`, `fs/kernfs/`, `kernel/cgroup/`, `fs/resctrl/rdtgroup.c`, `fs/9p/`, `fs/ceph/`, `fs/ecryptfs/`, `fs/fuse/`, `fs/gfs2/`, `fs/nfs/`, `fs/smb/client/`, `fs/tracefs/`, `fs/vboxsf/`, and `security/apparmor/`. Alright, let's see, core VFS is always compiled, so that's base. But if the target is *new* features, like atomic mkdir on kernfs/cgroup, `CONFIG_CGROUPS` is definitely needed. Now, what does the prompt actually *want*? Right, identify specific `CONFIG_` options required to compile and reach the modified code. No assumptions, check the `Kconfig` files and `#ifdef` guards, and include any non-standard parent subsystem configs. And remove the "CONFIG_" prefix. Can I hit the core functionality (like `O_CREAT | O_DIRECTORY`) without any non-default configs? Yes, on ext4, tmpfs, etc., that's standard. But, let's think about the *modified* filesystems. Why did those filesystems add `SB_I_NO_ATOMIC_MKDIR`? Let's check `fs/namei.c`. Ah, the comment: "Filesystems whose ->mkdir() may leave the dentry negative must set `SB_I_NO_ATOMIC_MKDIR`." Okay, kernfs was modified because it *used* to leave the dentry negative! It didn't instantiate the dentry initially. Now, the patch adds dentry instantiation. And cgroup and resctrl implement `scops->mkdir`, returning a `kernfs_node *`. Other filesystems set `SB_I_NO_ATOMIC_MKDIR` if they may leave the dentry negative or do not support atomic mkdir (9p, ceph, ecryptfs, fuse, gfs2, nfs, cifs, tracefs, vboxsf, apparmorfs). What about filesystems that *do* support atomic mkdir? Ext4, btrfs, xfs, tmpfs, ramfs, kernfs (cgroup2)! These instantiate the dentry. In kernfs, `cgroup_mkdir` is enabled when `CONFIG_CGROUPS` is enabled. Is `CONFIG_CGROUPS` needed? `init/Kconfig` confirms it is standard. And `CONFIG_TMPFS` is always Y. So, I should be listing configs like `CGROUPS`. Based on those instructions, and the code modifications, if the code includes `kernel/cgroup/cgroup.c` (like `cgroup_mkdir`) and `fs/kernfs/`, what config is needed to compile `kernel/cgroup/cgroup.c`? Let's check `kernel/cgroup/Makefile`. |
| 24/2 | 2026/10/16 22:32 | tool | grepper |
0mArgs:{
"Expression": "cgroup",
"PathPrefixes": [
"kernel/cgroup/Makefile"
]
}Results: {
"Output": "kernel/cgroup/Makefile-1-# SPDX-License-Identifier: GPL-2.0\nkernel/cgroup/Makefile:2:obj-y := cgroup.o rstat.o namespace.o cgroup-v1.o freezer.o\nkernel/cgroup/Makefile-3-\n"
} |
| 25/2 | 2026/10/16 22:32 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 47002 output: 26 thoughts: 9 Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person: **Initial Assessment: Kernel Makefile Examination** Alright, the game plan is clear. My immediate instinct is to dive into the `kernel/Makefile`. It's the central hub, the command center, the place where everything gets orchestrated. I need to get a high-level view of how this project's kernel is being built, how the various components are linked, and what dependencies are in play. I'm going to quickly scan it for key variables, include directives, and build targets. I want to see how the build process is structured from the ground up, and pinpoint any areas that seem atypical or potentially problematic. Specifically, I'll be looking for things like: the main build targets (e.g., `vmlinux`, modules), how the compiler and linker flags are configured, and whether there are any custom or unusual build rules defined. Let's see what secrets it holds. |
| 26/2 | 2026/10/16 22:32 | tool | grepper |
0mArgs:{
"Expression": "cgroup",
"PathPrefixes": [
"kernel/Makefile"
]
}Results: {
"Output": "kernel/Makefile=89=obj-$(CONFIG_COMPAT) += compat.o\nkernel/Makefile:90:obj-$(CONFIG_CGROUPS) += cgroup/\nkernel/Makefile-91-obj-$(CONFIG_UTS_NS) += utsname.o\n"
} |
| 27/2 | 2026/10/16 22:32 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 47323 output: 26 thoughts: 44 Thoughts: Okay, here's my take on those thoughts, framed from my perspective as a kernel expert: **Investigating Kernel Build Dependencies** Alright, so `CONFIG_CGROUPS` is clearly going to pull in all the necessary code from `kernel/cgroup/`, that's straightforward. Now, my immediate thought is, what about `fs/kernfs/`? It's often intertwined with cgroups functionality, so I need to verify that it's correctly handled during the build process. Let's start by quickly checking the Makefiles. I'll need to open up `fs/kernfs/Makefile` to see if there are any explicit dependencies declared there, and then double-check the top-level `fs/Makefile` to ensure everything gets linked in the correct order. I'm looking for a smooth, clean integration here, so any unexpected build steps would be a red flag. I want to confirm the dependencies and how they interact to have a good overview. |
| 28/2 | 2026/10/16 22:32 | tool | grepper |
0mArgs:{
"Expression": "kernfs",
"PathPrefixes": [
"fs/Makefile"
]
}Results: {
"Output": "fs/Makefile=55=obj-$(CONFIG_PROC_FS)\t\t+= proc/\nfs/Makefile:56:obj-$(CONFIG_KERNFS)\t\t+= kernfs/\nfs/Makefile-57-obj-$(CONFIG_SYSFS)\t\t+= sysfs/\n"
} |
| 29/2 | 2026/10/16 22:32 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 47674 output: 34 thoughts: 21 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person: **Kernel Filesystem Selection Inquiry** Alright, so the question is, *what* is actually responsible for pulling in `KERNFS` into the kernel's build? Let me get my bearings... my first thought is to dig into the obvious location: the Kconfig files. Specifically, that file that's going to be defining the features within the kernfs subsystem, so that would be `fs/kernfs/Kconfig`. Time to see what conditions are there. |
| 30/2 | 2026/10/16 22:32 | tool | grepper |
0mArgs:{
"Expression": "KERNFS",
"PathPrefixes": [
"fs/Kconfig",
"init/Kconfig"
]
}Results: {
"Output": "init/Kconfig=729=config PSI\ninit/Kconfig-730-\tbool \"Pressure stall information tracking\"\ninit/Kconfig:731:\tselect KERNFS\ninit/Kconfig-732-\thelp\n--\ninit/Kconfig=1061=menuconfig CGROUPS\ninit/Kconfig-1062-\tbool \"Control Group support\"\ninit/Kconfig:1063:\tselect KERNFS\ninit/Kconfig-1064-\thelp\n"
} |
| 31/2 | 2026/10/16 22:32 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 47985 output: 26 thoughts: 36 Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person: **Kernel Filesystem Exploration: CGROUPS, KERNFS, and RESCTRL** Alright, so I'm looking at how things are wired together here. The initial clue is that `CGROUPS` is pointing towards `KERNFS`. Not unexpected, but always good to confirm the direct linkage. Now, my mind immediately jumps to `fs/resctrl/`. Given the current system, I'm trying to figure out how it fits into the whole picture. To clarify the situation I'll need to check the `fs/Makefile` to see how `resctrl` is built and included, if at all. It's the most reliable way to trace the build dependencies at this point. That should clear up the relationship between `CGROUPS`, `KERNFS`, and `resctrl` within the filesystem hierarchy. |
| 32/2 | 2026/10/16 22:32 | tool | grepper |
0mArgs:{
"Expression": "resctrl",
"PathPrefixes": [
"fs/Makefile"
]
}Results: {
"Output": "fs/Makefile=129=obj-$(CONFIG_BPF_LSM)\t\t+= bpf_fs_kfuncs.o\nfs/Makefile:130:obj-$(CONFIG_RESCTRL_FS)\t+= resctrl/\n"
} |
| 33/2 | 2026/10/16 22:32 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 48311 output: 31 thoughts: 4804 Thoughts: **Focusing on Key Functions in the Atomic Directory Creation Patch**
Okay, here's my thought process on identifying the critical symbols for focus coverage in this atomic directory creation patch. The primary goal is to avoid ubiquitous functions that dilute fuzzing efforts while pinpointing the core logic changes.
Initially, I considered `CONFIG_RESCTRL_FS`, but quickly realized its reliance on hardware features (CPU support for CAT/MBA) makes it less relevant for a general test environment like QEMU. On the other hand, `CGROUPS` is purely software-based and a clear choice.
Now, let's analyze the functions modified or added by the patch:
1. `fs/namei.c`:
* **`lookup_open`**: This is a critical function, significantly modified to handle the new atomic directory creation logic ( `O_CREAT | O_DIRECTORY | O_EXCL` ). It includes checks for `SB_I_NO_ATOMIC_MKDIR`, prepares modes, calls `vfs_mkdir_no_perm`, and ensures that `dentry` is not negative. Though `static`, it's large and complex, making it unlikely to be inlined and is a central function for this patch.
* **`vfs_mkdir`**: Refactored to use `vfs_mkdir_no_perm`. This is an exported symbol.
* **`finish_lookup`**: This static function (called by `__lookup_slow` and `lookup_open`) may be inlined.
* **`may_o_create`**: This is static, but is critical for permissions.
2. `fs/kernfs/dir.c`:
* **`kernfs_iop_mkdir`**: This function is completely rewritten to implement kernfs directory creation. Since it's address is taken by a function pointer, it can't be inlined and is definitely a symbol in the vmlinux.
3. `fs/kernfs/inode.c`:
* **`kernfs_get_inode_prealloc`**: This is a new, non-static function essential for inode initialization in kernfs. Because it's a non-static exported symbol, this function is a key element.
* **`kernfs_init_inode`**: Was slightly changed with the addition of scoped guards.
4. `kernel/cgroup/cgroup.c`:
* **`cgroup_mkdir`**: This non-static function is changed to return a `kernfs_node *`. It is a distinct symbol in vmlinux.
5. `fs/resctrl/rdtgroup.c`:
* `rdtgroup_mkdir`
* `rdtgroup_mkdir_ctrl_mon`
* `rdtgroup_mkdir_mon` - These were also changed to return `kernfs_node *`.
6. `fs/open.c`:
* **`build_open_flags`**: Modified to handle `O_IS_MKDIR`, enforce `O_EXCL`, etc. HOWEVER, it's ubiquitous, called by every open syscall, so we definitely should avoid it.
Regarding `lookup_open`, I debated its "ubiquitous" status. While any program that opens or creates a file *could* call it, existing files that are cached don't use it, and only the creation of new files calls it. The logic within `lookup_open` specifically implements the patch's central functionality: atomic directory creation. This makes it crucial for targeted fuzzing. It's not a generic low-level function like `do_sys_openat2`, `sys_openat`, or memory allocators, which every syscall utilizes.
**Summary of Relevant Symbols:**
To ensure focused fuzzing of this patch, the following functions should be included as FocusSymbols:
* `lookup_open`
* `vfs_mkdir`
* `kernfs_iop_mkdir`
* `kernfs_get_inode_prealloc`
* `cgroup_mkdir`
* `finish_lookup`
* `rdtgroup_mkdir`
* `rdtgroup_mkdir_ctrl_mon`
* `rdtgroup_mkdir_mon`
**EnableConfigs:**
We also need `CONFIG_CGROUPS` to test kernfs/cgroup specific atomic mkdir functionality.
|
| 34/2 | 2026/10/16 22:33 | tool | grepper |
0mArgs:{
"Expression": "ARCH_HAS_CPU_RESCTRL",
"PathPrefixes": [
"arch/"
]
}Results: {
"Output": "arch/Kconfig=1624=config ARCH_HAS_PHYS_TO_DMA\n--\narch/Kconfig-1626-\narch/Kconfig:1627:config ARCH_HAS_CPU_RESCTRL\narch/Kconfig-1628-\tbool\n--\narch/arm64/Kconfig=2137=config ARM64_MPAM\n--\narch/arm64/Kconfig-2139-\tselect ARM64_MPAM_DRIVER\narch/arm64/Kconfig:2140:\tselect ARCH_HAS_CPU_RESCTRL\narch/arm64/Kconfig-2141-\thelp\n--\narch/x86/Kconfig=519=config X86_CPU_RESCTRL\n--\narch/x86/Kconfig-522-\tdepends on MISC_FILESYSTEMS\narch/x86/Kconfig:523:\tselect ARCH_HAS_CPU_RESCTRL\narch/x86/Kconfig-524-\tselect RESCTRL_FS\n"
} |
| 35/2 | 2026/10/16 22:33 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 54382 output: 33 thoughts: 384 Thoughts: **My Analysis of the Kernel Configuration Dependencies** Okay, let me break down these kernel configuration dependencies. My initial focus was on `X86_CPU_RESCTRL`, and I saw that it hinges on `MISC_FILESYSTEMS` and then selects `ARCH_HAS_CPU_RESCTRL` and `RESCTRL_FS`. But hang on… `RESCTRL` requires hardware support, which a standard QEMU CPU won’t provide. That's a dead end for my testing unless I'm running something with RDT/AMD QoS. In contrast, `CGROUPS` is pure software, always available. That's something I can use. Now, what about these other filesystems affected by the patch? `9P_FS`, `CEPH_FS`, `ECRYPT_FS`, `FUSE_FS`, `GFS2_FS`, `NFS_FS`, `CIFS`, `TRACING`, `VBOXSF_FS`, `SECURITY_APPARMOR` – they all have this `SB_I_NO_ATOMIC_MKDIR` flag added during superblock initialization. My first question now is, is any specific configuration *required* to reach the modified code? I see that even with a minimal kernel configuration, the `lookup_open` and `vfs_mkdir` functions in `fs/namei.c` are compiled and reached. But the key thing is that `CGROUPS` is definitely required to reach the actual changes within `cgroup_mkdir`, `kernfs_iop_mkdir`, and `kernfs_get_inode_prealloc`! So, enabling `CGROUPS` is *crucial* for thoroughly testing the changes introduced by this patch series. Alright, what else is needed? Let’s double-check the `CGROUPS` configuration itself. Time to consult `init/Kconfig`. I'll go through the `menuconfig CGROUPS` block and see if it has any `depends on` clauses. I'll start checking around line 1061. |
| 36/2 | 2026/10/16 22:33 | tool | read-file |
0mArgs:{
"File": "init/Kconfig",
"FirstLine": 1055,
"LineCount": 20
}Results: {
"Contents": "1055:\t\t If set, automatic NUMA balancing will be enabled if running on a NUMA\n1056:\t\t machine.\n1057:\t\n1058:\tconfig SLAB_OBJ_EXT\n1059:\t\tbool\n1060:\t\n1061:\tmenuconfig CGROUPS\n1062:\t\tbool \"Control Group support\"\n1063:\t\tselect KERNFS\n1064:\t\thelp\n1065:\t\t This option adds support for grouping sets of processes together, for\n1066:\t\t use with process control subsystems such as Cpusets, CFS, memory\n1067:\t\t controls or device isolation.\n1068:\t\t See\n1069:\t\t\t- Documentation/scheduler/sched-design-CFS.rst\t(CFS)\n1070:\t\t\t- Documentation/admin-guide/cgroup-v1/ (features for grouping, isolation\n1071:\t\t\t\t\t\t and resource control)\n1072:\t\n1073:\t\t Say N if unsure.\n1074:\t\n"
} |
| 37/2 | 2026/10/16 22:33 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 55520 output: 216 thoughts: 2291 Thoughts: **Analysis of Atomic Directory Creation Patch** Okay, let's break this down. My goal is to identify the key aspects of this patch that make it fuzz-worthy. First, it looks like `CGROUPS` is the core config we need, as it directly leads us to the modified `cgroup_mkdir` and then into the `kernfs` code. We don't need `CONFIG_CGROUP_PIDS` or any other specific controller – the fundamental cgroup directory creation is enough. It's also worth noting the code modifications involve filesystems with the `SB_I_NO_ATOMIC_MKDIR` flag. I see a few key functions are modified: `lookup_open`, which handles the atomic open and directory creation with the `O_CREAT` flag; `may_o_create`, which checks permissions; `vfs_mkdir`, the exported VFS directory creation API; `kernfs_iop_mkdir` and `kernfs_get_inode_prealloc`, which manage the kernfs-specific inode operations during directory creation; and, of course, `cgroup_mkdir`, the cgroup callback. `atomic_open` appears to be updated, but not heavily modified. Out of these, `cgroup_mkdir`, `kernfs_iop_mkdir`, and `kernfs_get_inode_prealloc` are the most specific, linked directly to kernfs directory creation. `may_o_create` and `lookup_open` are also critical, triggered by `open` with `O_CREAT`. The patch definitely seems fuzz-worthy. It adds atomic directory creation support through `openat(..., O_CREAT | O_DIRECTORY | O_EXCL, ...)` which is standard VFS syscalls (`open`, `openat`, `openat2`). It also updates `kernfs` and `cgroup` directory creation, which makes me think it will be easily reachable in standard virtualized environments like QEMU. The refactoring of directory creation and inode handling in `kernfs` suggests new code paths and logic. The new `WARN_ON_ONCE` assertions in `may_o_create` and `lookup_open` are a nice bonus, as they can catch unexpected behavior. So, here's my concise assessment: This patch introduces atomic directory creation, reachable via standard syscalls and cgroupfs. `CGROUPS` is the key configuration. There are new assertions. We should focus on exercising these new code paths to detect any potential issues, bugs, or race conditions. |
| 38/2 | 2026/10/16 22:33 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"CGROUPS"
],
"FocusSymbols": [
"cgroup_mkdir",
"kernfs_get_inode_prealloc",
"kernfs_iop_mkdir",
"lookup_open",
"may_o_create",
"vfs_mkdir"
],
"Reasoning": "The patch series introduces atomic directory creation and opening via openat/openat2 with flags O_CREAT | O_DIRECTORY | O_EXCL. It modifies core VFS path resolution and file creation logic in fs/namei.c and fs/open.c, adds new assertions (WARN_ON_ONCE in may_o_create and lookup_open), and completely rewrites kernfs_iop_mkdir in fs/kernfs/dir.c to preallocate and instantiate inodes with d_splice_alias, updating cgroup_mkdir accordingly. This code is fully reachable in standard virtualized environments via standard syscalls.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"CGROUPS"
],
"FocusSymbols": [
"cgroup_mkdir",
"kernfs_get_inode_prealloc",
"kernfs_iop_mkdir",
"lookup_open",
"may_o_create",
"vfs_mkdir"
],
"Reasoning": "The patch series introduces atomic directory creation and opening via openat/openat2 with flags O_CREAT | O_DIRECTORY | O_EXCL. It modifies core VFS path resolution and file creation logic in fs/namei.c and fs/open.c, adds new assertions (WARN_ON_ONCE in may_o_create and lookup_open), and completely rewrites kernfs_iop_mkdir in fs/kernfs/dir.c to preallocate and instantiate inodes with d_splice_alias, updating cgroup_mkdir accordingly. This code is fully reachable in standard virtualized environments via standard syscalls.",
"WorthFuzzing": true
} |
| 39/1 | 2026/10/16 22:33 | action | If |
0mArgs:{
"WorthFuzzing": true
}Results: null |
| 40/2 | 2026/10/16 22:33 | agent | kmsan-evaluator |
0mModel:core Args: null Results: {
"KMSANReasoning": "The patch series implements support for atomic directory creation and opening via openat(..., O_CREAT | O_DIRECTORY | O_EXCL, ...). It updates VFS path resolution and directory creation helpers (lookup_open, finish_lookup, vfs_mkdir_no_perm, may_o_create), adjusts kernfs, cgroup, and rdtgroup mkdir hooks to return struct kernfs_node *, and adds the SB_I_NO_ATOMIC_MKDIR flag for filesystems that do not support this feature.\n\nNo uninitialized memory risks are introduced:\n1. No kernel structures or data buffers are copied to user space (the syscall only returns a file descriptor or negative errno).\n2. All new/modified stack and heap variables in the modified functions are explicitly initialized before use.\n3. Inode management in kernfs uses standard inode allocation (new_inode) and initialization (kernfs_init_inode).\n4. Any potential issues with reference counting (dentry/inode put/get), locking (mutexes and rwsems), or pointer handling are under the purview of KASAN and LOCKDEP rather than KMSAN.\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
}Instruction: You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.
CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
a comprehensive suite of debugging tools and sanitizers, including KASAN
(out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
(locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
or page allocations) and kernel-to-user memory info-leaks.
Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.
Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.
Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
attributes, ioctl output arguments, socket options, or BPF buffers) where fields
or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
uninitialized bytes of existing buffers.
Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
dereferences, locking deadlocks, or use-after-free bugs (these are already caught
by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.
Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit 4e3de42dfe78758a71522b9b86a2de3000cb64d7
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Oct 16 22:28:09 2026 +0000
syz-cluster: applied patch under review
diff --git a/arch/alpha/include/uapi/asm/errno.h b/arch/alpha/include/uapi/asm/errno.h
index 1a99f38813c79..0384f69989748 100644
--- a/arch/alpha/include/uapi/asm/errno.h
+++ b/arch/alpha/include/uapi/asm/errno.h
@@ -19,7 +19,7 @@
#define ENOPROTOOPT 42 /* Protocol not available */
#define EPROTONOSUPPORT 43 /* Protocol not supported */
#define ESOCKTNOSUPPORT 44 /* Socket type not supported */
-#define EOPNOTSUPP 45 /* Operation not supported on transport endpoint */
+#define EOPNOTSUPP 45 /* Operation not supported */
#define EPFNOSUPPORT 46 /* Protocol family not supported */
#define EAFNOSUPPORT 47 /* Address family not supported by protocol */
#define EADDRINUSE 48 /* Address already in use */
diff --git a/arch/mips/include/uapi/asm/errno.h b/arch/mips/include/uapi/asm/errno.h
index 1835a50b69cef..2b6813715d1e5 100644
--- a/arch/mips/include/uapi/asm/errno.h
+++ b/arch/mips/include/uapi/asm/errno.h
@@ -75,7 +75,7 @@
#define ENOPROTOOPT 99 /* Protocol not available */
#define EPROTONOSUPPORT 120 /* Protocol not supported */
#define ESOCKTNOSUPPORT 121 /* Socket type not supported */
-#define EOPNOTSUPP 122 /* Operation not supported on transport endpoint */
+#define EOPNOTSUPP 122 /* Operation not supported */
#define EPFNOSUPPORT 123 /* Protocol family not supported */
#define EAFNOSUPPORT 124 /* Address family not supported by protocol */
#define EADDRINUSE 125 /* Address already in use */
diff --git a/arch/parisc/include/uapi/asm/errno.h b/arch/parisc/include/uapi/asm/errno.h
index 93194fbb0a80d..74bd65d791457 100644
--- a/arch/parisc/include/uapi/asm/errno.h
+++ b/arch/parisc/include/uapi/asm/errno.h
@@ -84,7 +84,7 @@
#define ENOPROTOOPT 220 /* Protocol not available */
#define EPROTONOSUPPORT 221 /* Protocol not supported */
#define ESOCKTNOSUPPORT 222 /* Socket type not supported */
-#define EOPNOTSUPP 223 /* Operation not supported on transport endpoint */
+#define EOPNOTSUPP 223 /* Operation not supported */
#define EPFNOSUPPORT 224 /* Protocol family not supported */
#define EAFNOSUPPORT 225 /* Address family not supported by protocol */
#define EADDRINUSE 226 /* Address already in use */
diff --git a/arch/sparc/include/uapi/asm/errno.h b/arch/sparc/include/uapi/asm/errno.h
index 71940ec9130b4..699f648e1a356 100644
--- a/arch/sparc/include/uapi/asm/errno.h
+++ b/arch/sparc/include/uapi/asm/errno.h
@@ -16,7 +16,7 @@
#define ENOPROTOOPT 42 /* Protocol not available */
#define EPROTONOSUPPORT 43 /* Protocol not supported */
#define ESOCKTNOSUPPORT 44 /* Socket type not supported */
-#define EOPNOTSUPP 45 /* Op not supported on transport endpoint */
+#define EOPNOTSUPP 45 /* Operation not supported */
#define EPFNOSUPPORT 46 /* Protocol family not supported */
#define EAFNOSUPPORT 47 /* Address family not supported by protocol */
#define EADDRINUSE 48 /* Address already in use */
diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c
index 94d6b02c221b8..a3377bc34ac0e 100644
--- a/fs/9p/vfs_super.c
+++ b/fs/9p/vfs_super.c
@@ -40,6 +40,7 @@ static int v9fs_fill_super(struct super_block *sb)
sb->s_blocksize_bits = fls(v9ses->maxdata - 1);
sb->s_blocksize = 1 << sb->s_blocksize_bits;
sb->s_magic = V9FS_MAGIC;
+ sb->s_iflags |= SB_I_NO_ATOMIC_MKDIR;
if (v9fs_proto_dotl(v9ses)) {
sb->s_op = &v9fs_super_ops_dotl;
if (!(v9ses->flags & V9FS_NO_XATTR))
diff --git a/fs/ceph/super.c b/fs/ceph/super.c
index 72935f665f11c..fa1cf1f4f57d6 100644
--- a/fs/ceph/super.c
+++ b/fs/ceph/super.c
@@ -1226,6 +1226,7 @@ static int ceph_set_super(struct super_block *s, struct fs_context *fc)
fsc->max_file_size = 1ULL << 40; /* temp value until we get mdsmap */
s->s_op = &ceph_super_ops;
+ s->s_iflags |= SB_I_NO_ATOMIC_MKDIR;
set_default_d_op(s, &ceph_dentry_ops);
s->s_export_op = &ceph_export_ops;
diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
index 5f37cddb956fb..8ed6076d50afd 100644
--- a/fs/ecryptfs/main.c
+++ b/fs/ecryptfs/main.c
@@ -477,6 +477,7 @@ static int ecryptfs_get_tree(struct fs_context *fc)
/* ->kill_sb() will take care of sbi after that point */
sbi = NULL;
s->s_op = &ecryptfs_sops;
+ s->s_iflags |= SB_I_NO_ATOMIC_MKDIR;
s->s_xattr = ecryptfs_xattr_handlers;
set_default_d_op(s, &ecryptfs_dops);
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index e9552be3637bc..cf766c6feea43 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -1608,6 +1608,7 @@ static void fuse_sb_defaults(struct super_block *sb)
sb->s_iflags |= SB_I_IMA_UNVERIFIABLE_SIGNATURE;
sb->s_iflags |= SB_I_NOIDMAP;
sb->s_iflags |= SB_I_NO_DATA_INTEGRITY;
+ sb->s_iflags |= SB_I_NO_ATOMIC_MKDIR;
if (sb->s_user_ns != &init_user_ns)
sb->s_iflags |= SB_I_UNTRUSTED_MOUNTER;
sb->s_flags &= ~(SB_NOSEC | SB_I_VERSION);
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
index 718e0da7dfce0..695835819a5d5 100644
--- a/fs/gfs2/ops_fstype.c
+++ b/fs/gfs2/ops_fstype.c
@@ -1139,6 +1139,7 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_flags |= SB_NOSEC;
sb->s_magic = GFS2_MAGIC;
sb->s_op = &gfs2_super_ops;
+ sb->s_iflags |= SB_I_NO_ATOMIC_MKDIR;
set_default_d_op(sb, &gfs2_dops);
sb->s_export_op = &gfs2_export_ops;
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 82bbaeb326aab..42b3810a04bea 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -1294,18 +1294,40 @@ static struct dentry *kernfs_iop_mkdir(struct mnt_idmap *idmap,
{
struct kernfs_node *parent = dir->i_private;
struct kernfs_syscall_ops *scops = kernfs_root(parent)->syscall_ops;
- int ret;
+ struct kernfs_node *kn;
+ struct inode *inode;
if (!scops || !scops->mkdir)
return ERR_PTR(-EPERM);
- if (!kernfs_get_active(parent))
+ inode = new_inode(dir->i_sb);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+
+ if (!kernfs_get_active(parent)) {
+ iput(inode);
return ERR_PTR(-ENODEV);
+ }
- ret = scops->mkdir(parent, dentry->d_name.name, mode);
+ kn = scops->mkdir(parent, dentry->d_name.name, mode);
kernfs_put_active(parent);
- return ERR_PTR(ret);
+ if (IS_ERR(kn)) {
+ iput(inode);
+ return ERR_CAST(kn);
+ }
+
+ inode = kernfs_get_inode_prealloc(inode, kn);
+ kernfs_put(kn);
+
+ /*
+ * We may race with kernfs_fh_to_dentry() which calls d_obtain_alias(),
+ * so the inode may already have a disconnected alias. Use
+ * d_splice_alias() to move it into place, which does not accept
+ * hashed dentries, so drop the negative dentry first.
+ */
+ d_drop(dentry);
+ return d_splice_alias(inode, dentry);
}
static int kernfs_iop_rmdir(struct inode *dir, struct dentry *dentry)
diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c
index abb286bc3474b..a254090364926 100644
--- a/fs/kernfs/inode.c
+++ b/fs/kernfs/inode.c
@@ -204,7 +204,8 @@ static void kernfs_init_inode(struct kernfs_node *kn, struct inode *inode)
inode->i_generation = kernfs_gen(kn);
set_default_inode_attr(inode, kn->mode);
- kernfs_refresh_inode(kn, inode);
+ scoped_guard(rwsem_read, &kernfs_root(kn)->kernfs_iattr_rwsem)
+ kernfs_refresh_inode(kn, inode);
/* initialize inode according to type */
switch (kernfs_type(kn)) {
@@ -254,6 +255,40 @@ struct inode *kernfs_get_inode(struct super_block *sb, struct kernfs_node *kn)
return inode;
}
+/**
+ * kernfs_get_inode_prealloc - get inode for kernfs_node
+ * @prealloc_inode: inode allocated with new_inode() on the target superblock
+ * @kn: kernfs_node to get the inode for
+ *
+ * Like kernfs_get_inode(), but cannot fail. @prealloc_inode is
+ * initialized for @kn, unless @kn already has an inode on the super
+ * block, in which case @prealloc_inode is released and the existing inode
+ * is returned instead.
+ *
+ * Locking:
+ * Kernel thread context (may sleep).
+ *
+ * Return:
+ * Pointer to the inode of @kn.
+ */
+struct inode *kernfs_get_inode_prealloc(struct inode *prealloc_inode,
+ struct kernfs_node *kn)
+{
+ struct inode *old;
+
+ prealloc_inode->i_ino = kernfs_ino(kn);
+ while (insert_inode_locked(prealloc_inode)) {
+ old = ilookup(prealloc_inode->i_sb, prealloc_inode->i_ino);
+ if (old) {
+ iput(prealloc_inode);
+ return old;
+ }
+ }
+
+ kernfs_init_inode(kn, prealloc_inode);
+ return prealloc_inode;
+}
+
/*
* The kernfs_node serves as both an inode and a directory entry for
* kernfs. To prevent the kernfs inode numbers from being freed
diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h
index aa784b540b361..e4273f104a24d 100644
--- a/fs/kernfs/kernfs-internal.h
+++ b/fs/kernfs/kernfs-internal.h
@@ -170,6 +170,8 @@ extern struct kmem_cache *kernfs_node_cache, *kernfs_iattrs_cache;
* inode.c
*/
extern const struct xattr_handler * const kernfs_xattr_handlers[];
+struct inode *kernfs_get_inode_prealloc(struct inode *inode,
+ struct kernfs_node *kn);
void kernfs_evict_inode(struct inode *inode);
int kernfs_iop_permission(struct mnt_idmap *idmap,
struct inode *inode, int mask);
diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c
index a57399021c8b0..202a5ca2bd341 100644
--- a/fs/kernfs/mount.c
+++ b/fs/kernfs/mount.c
@@ -283,7 +283,6 @@ struct dentry *kernfs_node_dentry(struct kernfs_node *kn,
static int kernfs_fill_super(struct super_block *sb, struct kernfs_fs_context *kfc)
{
struct kernfs_super_info *info = kernfs_info(sb);
- struct kernfs_root *kf_root = kfc->root;
struct inode *inode;
struct dentry *root;
@@ -304,9 +303,7 @@ static int kernfs_fill_super(struct super_block *sb, struct kernfs_fs_context *k
sb->s_shrink->seeks = 0;
/* get root inode, initialize and unlock it */
- down_read(&kf_root->kernfs_rwsem);
inode = kernfs_get_inode(sb, info->root->kn);
- up_read(&kf_root->kernfs_rwsem);
if (!inode) {
pr_debug("kernfs: could not get root inode\n");
return -ENOMEM;
diff --git a/fs/namei.c b/fs/namei.c
index c87c14ee1a252..f21cd1028a8a3 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1889,12 +1889,34 @@ static struct dentry *lookup_fast(struct nameidata *nd)
return dentry;
}
+/*
+ * If @dentry is in-lookup, look it up in @dir through ->lookup(). Returns
+ * the dentry to use from now on: @dentry itself, or the dentry or ERR_PTR()
+ * that ->lookup() returned instead, in which case @dentry is dropped.
+ */
+static struct dentry *finish_lookup(struct inode *dir, struct dentry *dentry,
+ unsigned int flags)
+{
+ struct dentry *res;
+
+ if (!d_in_lookup(dentry))
+ return dentry;
+
+ res = dir->i_op->lookup(dir, dentry, flags);
+ d_lookup_done(dentry);
+ if (unlikely(res)) {
+ dput(dentry);
+ return res;
+ }
+ return dentry;
+}
+
/* Fast lookup failed, do it the slow way */
static struct dentry *__lookup_slow(const struct qstr *name,
struct dentry *dir,
unsigned int flags)
{
- struct dentry *dentry, *old;
+ struct dentry *dentry;
struct inode *inode = dir->d_inode;
/* Don't go there if it's already dead */
@@ -1913,17 +1935,10 @@ static struct dentry *__lookup_slow(const struct qstr *name,
goto again;
}
dput(dentry);
- dentry = ERR_PTR(error);
- }
- } else {
- old = inode->i_op->lookup(inode, dentry, flags);
- d_lookup_done(dentry);
- if (unlikely(old)) {
- dput(dentry);
- dentry = old;
+ return ERR_PTR(error);
}
}
- return dentry;
+ return finish_lookup(inode, dentry, flags);
}
static noinline struct dentry *lookup_slow(const struct qstr *name,
@@ -2810,9 +2825,16 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
return s;
}
+static inline bool trailing_slashes(const struct qstr *last)
+{
+ /* last->len is set by hash_name() to the length of the current
+ * component ->name, terminating with '/' or a NUL character. */
+ return (bool)last->name[last->len];
+}
+
static inline const char *lookup_last(struct nameidata *nd)
{
- if (nd->last_type == LAST_NORM && nd->last.name[nd->last.len])
+ if (nd->last_type == LAST_NORM && trailing_slashes(&nd->last))
nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
return walk_component(nd, WALK_TRAILING);
@@ -4188,6 +4210,24 @@ static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap,
return mode;
}
+static inline
+int vfs_create_no_perm(struct mnt_idmap *idmap, struct dentry *dentry,
+ umode_t mode, struct delegated_inode *di)
+{
+ struct inode *dir = d_inode(dentry->d_parent);
+ int error;
+
+ error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, di);
+ if (error)
+ return error;
+
+ error = dir->i_op->create(idmap, dir, dentry, mode);
+ if (!error)
+ fsnotify_create(dir, dentry);
+
+ return error;
+}
+
/**
* vfs_create - create new file
* @idmap: idmap of the mount the inode was found from
@@ -4214,19 +4254,14 @@ int vfs_create(struct mnt_idmap *idmap, struct dentry *dentry, umode_t mode,
return error;
if (!dir->i_op->create)
- return -EACCES; /* shouldn't it be ENOSYS? */
+ return -EOPNOTSUPP;
mode = vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG);
error = security_inode_create(dir, dentry, mode);
if (error)
return error;
- error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, di);
- if (error)
- return error;
- error = dir->i_op->create(idmap, dir, dentry, mode);
- if (!error)
- fsnotify_create(dir, dentry);
- return error;
+
+ return vfs_create_no_perm(idmap, dentry, mode, di);
}
EXPORT_SYMBOL(vfs_create);
@@ -4343,21 +4378,43 @@ static inline int open_to_namei_flags(int flag)
static int may_o_create(struct mnt_idmap *idmap,
const struct path *dir, struct dentry *dentry,
- umode_t mode)
+ int open_flag, umode_t mode)
{
- int error = security_path_mknod(dir, dentry, mode, 0);
+ struct inode *dir_inode = dir->dentry->d_inode;
+ bool create_dir = O_IS_MKDIR(open_flag);
+ int error;
+
+ WARN_ON_ONCE(create_dir && !S_ISDIR(mode));
+
+ if (create_dir)
+ error = security_path_mkdir(dir, dentry, mode);
+ else
+ error = security_path_mknod(dir, dentry, mode, 0);
if (error)
return error;
if (!fsuidgid_has_mapping(dir->dentry->d_sb, idmap))
return -EOVERFLOW;
- error = inode_permission(idmap, dir->dentry->d_inode,
- MAY_WRITE | MAY_EXEC);
+ error = inode_permission(idmap, dir_inode, MAY_WRITE | MAY_EXEC);
if (error)
return error;
- return security_inode_create(dir->dentry->d_inode, dentry, mode);
+ if (create_dir)
+ error = security_inode_mkdir(dir_inode, dentry, mode);
+ else
+ error = security_inode_create(dir_inode, dentry, mode);
+
+ return error;
+}
+
+static inline umode_t o_create_mode(struct mnt_idmap *idmap,
+ const struct inode *dir, int open_flag, umode_t mode)
+{
+ if (O_IS_MKDIR(open_flag))
+ return vfs_prepare_mode(idmap, dir, mode, S_IRWXUGO | S_ISVTX, S_IFDIR);
+ else
+ return vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG);
}
/**
@@ -4393,8 +4450,10 @@ static struct dentry *atomic_open(const struct path *path, struct dentry *dentry
file->__f_path.dentry = DENTRY_NOT_SET;
file->__f_path.mnt = path->mnt;
+
error = dir_inode->i_op->atomic_open(dir_inode, dentry, file,
- open_to_namei_flags(open_flag), mode);
+ open_to_namei_flags(open_flag), mode);
+
d_lookup_done(dentry);
if (!error) {
@@ -4438,11 +4497,24 @@ static struct dentry *atomic_open(const struct path *path, struct dentry *dentry
error = create_error;
}
dput(dentry);
- dentry = ERR_PTR(error);
+ return ERR_PTR(error);
}
+
+ if (file->f_mode & FMODE_CREATED) {
+ if (d_is_dir(dentry))
+ fsnotify_mkdir(dir_inode, dentry);
+ else
+ fsnotify_create(dir_inode, dentry);
+ }
+ if (file->f_mode & FMODE_OPENED)
+ fsnotify_open(file);
+
return dentry;
}
+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.
*
@@ -4464,6 +4536,7 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
struct mnt_idmap *idmap;
struct dentry *dir = nd->path.dentry;
struct inode *dir_inode = dir->d_inode;
+ bool create_dir = O_IS_MKDIR(op->open_flag);
int open_flag;
struct dentry *dentry;
int error, create_error;
@@ -4476,6 +4549,9 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
mode = op->mode;
create_error = 0;
+ if (create_dir && (dir_inode->i_sb->s_iflags & SB_I_NO_ATOMIC_MKDIR))
+ return ERR_PTR(-EOPNOTSUPP);
+
if (open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {
got_write = !mnt_want_write(nd->path.mnt);
/*
@@ -4484,7 +4560,7 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
*/
}
if (open_flag & O_CREAT)
- inode_lock(dir_inode);
+ inode_lock_nested(dir_inode, I_MUTEX_PARENT);
else
inode_lock_shared(dir_inode);
@@ -4536,10 +4612,10 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
if (open_flag & O_CREAT) {
if (open_flag & O_EXCL)
open_flag &= ~O_TRUNC;
- mode = vfs_prepare_mode(idmap, dir_inode, mode, mode, mode);
+ mode = o_create_mode(idmap, dir_inode, open_flag, mode);
if (likely(got_write))
create_error = may_o_create(idmap, &nd->path,
- dentry, mode);
+ dentry, open_flag, mode);
else
create_error = -EROFS;
}
@@ -4553,19 +4629,10 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
goto out;
}
- if (d_in_lookup(dentry)) {
- struct dentry *res = dir_inode->i_op->lookup(dir_inode, dentry,
- nd->flags);
- d_lookup_done(dentry);
- if (unlikely(res)) {
- if (IS_ERR(res)) {
- error = PTR_ERR(res);
- goto out_dput;
- }
- dput(dentry);
- dentry = res;
- }
- }
+ dentry = finish_lookup(dir_inode, dentry, nd->flags);
+ if (IS_ERR(dentry))
+ goto out;
+
if (dentry->d_inode || !(op->open_flag & O_CREAT)) {
/*
* No need to create a file. If lookup returned a positive
@@ -4583,26 +4650,33 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
goto out_dput;
}
- error = try_break_deleg(dir_inode, LEASE_BREAK_DIR_CREATE, &delegated_inode);
- if (error)
- goto out_dput;
-
- file->f_mode |= FMODE_CREATED;
- if (!dir_inode->i_op->create) {
- error = -EACCES;
+ if ((create_dir && !dir_inode->i_op->mkdir)
+ || (!create_dir && !dir_inode->i_op->create)) {
+ error = -EOPNOTSUPP;
goto out_dput;
}
- error = dir_inode->i_op->create(idmap, dir_inode, dentry, mode);
+ if (create_dir) {
+ struct dentry *res = vfs_mkdir_no_perm(idmap, dir_inode, dentry, mode,
+ &delegated_inode);
+ error = PTR_ERR_OR_ZERO(res);
+ if (!error) {
+ dentry = res;
+ /*
+ * Filesystems whose ->mkdir() may leave the dentry
+ * negative must set SB_I_NO_ATOMIC_MKDIR.
+ */
+ if (WARN_ON_ONCE(d_is_negative(dentry)))
+ error = -EIO;
+ }
+ } else {
+ error = vfs_create_no_perm(idmap, dentry, mode, &delegated_inode);
+ }
if (error)
goto out_dput;
+
+ file->f_mode |= FMODE_CREATED;
out:
- if (!IS_ERR(dentry)) {
- if (file->f_mode & FMODE_CREATED)
- fsnotify_create(dir_inode, dentry);
- if (file->f_mode & FMODE_OPENED)
- fsnotify_open(file);
- }
if ((open_flag & O_CREAT) || create_error)
inode_unlock(dir_inode);
else
@@ -4724,17 +4798,12 @@ struct file *vfs_lookup_open(struct path *parent, struct qstr *last,
}
EXPORT_SYMBOL_FOR_MODULES(vfs_lookup_open, "nfsd");
-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;
if (open_flag & O_CREAT) {
- if (trailing_slashes(nd))
+ if (trailing_slashes(&nd->last) && !(open_flag & O_DIRECTORY))
return ERR_PTR(-EISDIR);
/* Don't bother on an O_EXCL create */
@@ -4742,7 +4811,7 @@ static struct dentry *lookup_fast_for_open(struct nameidata *nd, int open_flag)
return NULL;
}
- if (trailing_slashes(nd))
+ if (trailing_slashes(&nd->last))
nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
dentry = lookup_fast(nd);
@@ -4772,6 +4841,13 @@ static const char *open_last_lookups(struct nameidata *nd,
if (nd->last_type != LAST_NORM) {
if (nd->depth)
put_link(nd);
+ /*
+ * Return -EOPNOTSUPP consistently, not -EEXIST, for paths
+ * ending in . or .., as lookup_open() does for other names.
+ */
+ if (O_IS_MKDIR(open_flag) &&
+ (nd->path.dentry->d_sb->s_iflags & SB_I_NO_ATOMIC_MKDIR))
+ return ERR_PTR(-EOPNOTSUPP);
return handle_dots(nd, nd->last_type);
}
@@ -4835,7 +4911,7 @@ 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 = may_create_in_sticky(idmap, nd,
d_backing_inode(nd->path.dentry));
@@ -5116,7 +5192,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 &= ~LOOKUP_CREATE;
dentry = start_dirop(path->dentry, &last, reval_flag | create_flags);
if (IS_ERR(dentry))
@@ -5174,7 +5250,7 @@ inline struct dentry *start_creating_user_path(
EXPORT_SYMBOL(start_creating_user_path);
/**
- * dentry_create - Create and open a file
+ * dentry_create - Create and open a regular file
* @path: path to create
* @flags: O\_ flags
* @mode: mode bits for new file
@@ -5211,7 +5287,7 @@ struct file *dentry_create(struct path *path, int flags, umode_t mode,
path->dentry = dir;
mode = vfs_prepare_mode(idmap, dir_inode, mode, S_IALLUGO, S_IFREG);
- create_error = may_o_create(idmap, path, dentry, mode);
+ create_error = may_o_create(idmap, path, dentry, flags, mode);
if (create_error)
flags &= ~O_CREAT;
@@ -5227,13 +5303,6 @@ struct file *dentry_create(struct path *path, int flags, umode_t mode,
/* Drop the extra reference */
dput(orig_dentry);
- 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 = dentry;
} else {
@@ -5385,6 +5454,33 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
return filename_mknodat(AT_FDCWD, name, mode, dev);
}
+static inline
+struct dentry *vfs_mkdir_no_perm(struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, umode_t mode,
+ struct delegated_inode *di)
+{
+ unsigned max_links = dir->i_sb->s_max_links;
+ struct dentry *de;
+ int error;
+
+ if (max_links && dir->i_nlink >= max_links)
+ return ERR_PTR(-EMLINK);
+
+ error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, di);
+ if (error)
+ return ERR_PTR(error);
+
+ de = dir->i_op->mkdir(idmap, dir, dentry, mode);
+ if (IS_ERR(de))
+ return de;
+ if (de) {
+ dput(dentry);
+ dentry = de;
+ }
+ fsnotify_mkdir(dir, dentry);
+ return dentry;
+}
+
/**
* vfs_mkdir - create directory returning correct dentry if possible
* @idmap: idmap of the mount the inode was found from
@@ -5412,14 +5508,13 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
struct delegated_inode *delegated_inode)
{
int error;
- unsigned max_links = dir->i_sb->s_max_links;
struct dentry *de;
error = may_create_dentry(idmap, dir, dentry);
if (error)
goto err;
- error = -EPERM;
+ error = -EOPNOTSUPP;
if (!dir->i_op->mkdir)
goto err;
@@ -5428,24 +5523,12 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
if (error)
goto err;
- error = -EMLINK;
- if (max_links && dir->i_nlink >= max_links)
+ de = vfs_mkdir_no_perm(idmap, dir, dentry, mode, delegated_inode);
+ if (IS_ERR(de)) {
+ error = PTR_ERR(de);
goto err;
-
- error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, delegated_inode);
- if (error)
- goto err;
-
- de = dir->i_op->mkdir(idmap, dir, dentry, mode);
- error = PTR_ERR(de);
- if (IS_ERR(de))
- goto err;
- if (de) {
- dput(dentry);
- dentry = de;
}
- fsnotify_mkdir(dir, dentry);
- return dentry;
+ return de;
err:
end_creating(dentry);
@@ -5732,7 +5815,7 @@ int filename_unlinkat(int dfd, struct filename *name)
goto exit_drop_write;
/* 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 = -EISDIR;
else
@@ -6334,16 +6417,16 @@ int filename_renameat2(int olddfd, struct filename *from,
if (flags & RENAME_EXCHANGE) {
if (!d_is_dir(rd.new_dentry)) {
error = -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 = -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;
}
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index cb19f1540d984..f4452b630f52b 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -1077,6 +1077,7 @@ static void nfs_fill_super(struct super_block *sb, struct nfs_fs_context *ctx)
sb->s_blocksize = 0;
sb->s_xattr = server->nfs_client->cl_nfs_mod->xattr;
sb->s_op = server->nfs_client->cl_nfs_mod->sops;
+ sb->s_iflags |= SB_I_NO_ATOMIC_MKDIR;
if (server->bsize)
sb->s_blocksize =
nfs_block_size(server->bsize, &sb->s_blocksize_bits);
diff --git a/fs/open.c b/fs/open.c
index 6b1c14e684a93..e8cb9153ea572 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -1239,29 +1239,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 = how->mode | S_IFREG;
+ if (O_IS_MKDIR(flags))
+ op->mode = how->mode | S_IFDIR;
+ else
+ op->mode = how->mode | S_IFREG;
} else {
if (how->mode != 0)
return -EINVAL;
op->mode = 0;
}
- /*
- * 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)) == (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;
@@ -1318,12 +1319,29 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op)
op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN;
+ /*
+ * 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_WRITE))
+ return -ENOTDIR;
+
if (flags & O_CREAT) {
- op->intent |= LOOKUP_CREATE;
+ if (flags & O_DIRECTORY) {
+ /*
+ * O_CREAT|O_DIRECTORY is provided to prevent TOCTOUs, so
+ * it makes little sense to allow it without O_EXCL.
+ */
+ if (!(flags & O_EXCL))
+ return -EINVAL;
+ }
if (flags & O_EXCL) {
op->intent |= LOOKUP_EXCL;
flags |= O_NOFOLLOW;
}
+ op->intent |= LOOKUP_CREATE;
}
if (flags & O_DIRECTORY)
diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c
index 5dcbb0a964e81..d7b13b63c6adf 100644
--- a/fs/resctrl/rdtgroup.c
+++ b/fs/resctrl/rdtgroup.c
@@ -4042,15 +4042,16 @@ static void mkdir_rdt_prepare_clean(struct rdtgroup *rgrp)
* and monitor group(ctrl_mon). This is a resource group
* to monitor a subset of tasks and cpus in its parent ctrl_mon group.
*/
-static int rdtgroup_mkdir_mon(struct kernfs_node *parent_kn,
- const char *name, umode_t mode)
+static struct kernfs_node *rdtgroup_mkdir_mon(struct kernfs_node *parent_kn,
+ const char *name, umode_t mode)
{
struct rdtgroup *rdtgrp, *prgrp;
+ struct kernfs_node *kn;
int ret;
ret = mkdir_rdt_prepare(parent_kn, name, mode, RDTMON_GROUP, &rdtgrp);
if (ret)
- return ret;
+ return ERR_PTR(ret);
prgrp = rdtgrp->mon.parent;
rdtgrp->closid = prgrp->closid;
@@ -4069,17 +4070,22 @@ static int rdtgroup_mkdir_mon(struct kernfs_node *parent_kn,
*/
list_add_tail(&rdtgrp->mon.crdtgrp_list, &prgrp->mon.crdtgrp_list);
+ kn = rdtgrp->kn;
+ kernfs_get(kn);
+ rdtgroup_kn_unlock(parent_kn);
+ return kn;
+
out_unlock:
rdtgroup_kn_unlock(parent_kn);
- return ret;
+ return ERR_PTR(ret);
}
/*
* These are rdtgroups created under the root directory. Can be used
* to allocate and monitor resources.
*/
-static int rdtgroup_mkdir_ctrl_mon(struct kernfs_node *parent_kn,
- const char *name, umode_t mode)
+static struct kernfs_node *rdtgroup_mkdir_ctrl_mon(struct kernfs_node *parent_kn,
+ const char *name, umode_t mode)
{
struct rdtgroup *rdtgrp;
struct kernfs_node *kn;
@@ -4088,7 +4094,7 @@ static int rdtgroup_mkdir_ctrl_mon(struct kernfs_node *parent_kn,
ret = mkdir_rdt_prepare(parent_kn, name, mode, RDTCTRL_GROUP, &rdtgrp);
if (ret)
- return ret;
+ return ERR_PTR(ret);
kn = rdtgrp->kn;
ret = closid_alloc();
@@ -4127,7 +4133,9 @@ static int rdtgroup_mkdir_ctrl_mon(struct kernfs_node *parent_kn,
rdtgrp->mba_mbps_event = mba_mbps_default_event;
}
- goto out_unlock;
+ kernfs_get(kn);
+ rdtgroup_kn_unlock(parent_kn);
+ return kn;
out_del_list:
list_del(&rdtgrp->rdtgroup_list);
@@ -4137,17 +4145,16 @@ static int rdtgroup_mkdir_ctrl_mon(struct kernfs_node *parent_kn,
closid_free(closid);
out_common_fail:
mkdir_rdt_prepare_clean(rdtgrp);
-out_unlock:
rdtgroup_kn_unlock(parent_kn);
- return ret;
+ return ERR_PTR(ret);
}
-static int rdtgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
- umode_t mode)
+static struct kernfs_node *rdtgroup_mkdir(struct kernfs_node *parent_kn,
+ const char *name, umode_t mode)
{
/* Do not accept '\n' to avoid unparsable situation. */
if (strchr(name, '\n'))
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
/*
* If the parent directory is the root directory and RDT
@@ -4161,7 +4168,7 @@ static int rdtgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
if (resctrl_arch_mon_capable())
return rdtgroup_mkdir_mon(parent_kn, name, mode);
- return -EPERM;
+ return ERR_PTR(-EPERM);
}
static int rdtgroup_rmdir_mon(struct rdtgroup *rdtgrp, cpumask_var_t tmpmask)
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index 98b610b2a1142..93a5ead81c827 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -247,6 +247,7 @@ cifs_read_super(struct super_block *sb)
sb->s_magic = CIFS_SUPER_MAGIC;
sb->s_op = &cifs_super_ops;
+ sb->s_iflags |= SB_I_NO_ATOMIC_MKDIR;
sb->s_xattr = cifs_xattr_handlers;
rc = super_setup_bdi(sb);
if (rc)
diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c
index f3d6188a3b7b2..0f64310c30952 100644
--- a/fs/tracefs/inode.c
+++ b/fs/tracefs/inode.c
@@ -471,6 +471,7 @@ static int tracefs_fill_super(struct super_block *sb, struct fs_context *fc)
return err;
sb->s_op = &tracefs_super_operations;
+ sb->s_iflags |= SB_I_NO_ATOMIC_MKDIR;
tracefs_apply_options(sb, false);
set_default_d_op(sb, &tracefs_dentry_operations);
diff --git a/fs/vboxsf/super.c b/fs/vboxsf/super.c
index a61fbab51d370..190b76f04d4f8 100644
--- a/fs/vboxsf/super.c
+++ b/fs/vboxsf/super.c
@@ -196,6 +196,7 @@ static int vboxsf_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_blocksize = 1024;
sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_op = &vboxsf_super_ops;
+ sb->s_iflags |= SB_I_NO_ATOMIC_MKDIR;
set_default_d_op(sb, &vboxsf_dentry_ops);
iroot = iget_locked(sb, 0);
diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h
index 6ad6b9e7a226a..204e16bbe2634 100644
--- a/include/linux/fcntl.h
+++ b/include/linux/fcntl.h
@@ -30,6 +30,12 @@
*/
#define __O_REGULAR (1 << 30)
+#define O_MKDIR_MASK (O_CREAT | O_DIRECTORY)
+static inline bool O_IS_MKDIR(unsigned int flags)
+{
+ return (flags & O_MKDIR_MASK) == 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 | \
diff --git a/include/linux/fs/super_types.h b/include/linux/fs/super_types.h
index ecd96aeb1cee7..521b00c47d0aa 100644
--- a/include/linux/fs/super_types.h
+++ b/include/linux/fs/super_types.h
@@ -352,5 +352,6 @@ struct super_block {
#define SB_I_NOIDMAP 0x00002000 /* No idmapped mounts on this superblock */
#define SB_I_ALLOW_HSM 0x00004000 /* Allow HSM events on this superblock */
#define SB_I_NO_DATA_INTEGRITY 0x00008000 /* fs cannot guarantee data persistence on sync */
+#define SB_I_NO_ATOMIC_MKDIR 0x00010000 /* no O_CREAT|O_DIRECTORY on this fs */
#endif /* _LINUX_FS_SUPER_TYPES_H */
diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h
index 351a5101c8628..0aced50043405 100644
--- a/include/linux/kernfs.h
+++ b/include/linux/kernfs.h
@@ -241,13 +241,14 @@ struct kernfs_node {
* syscalls. These optional callbacks are invoked on the matching syscalls
* and can perform any kernfs operations which don't necessarily have to be
* the exact operation requested. An active reference is held for each
- * kernfs_node parameter.
+ * kernfs_node parameter. @mkdir returns the kernfs_node of the new
+ * directory with a reference held, or an ERR_PTR() on failure.
*/
struct kernfs_syscall_ops {
int (*show_options)(struct seq_file *sf, struct kernfs_root *root);
- int (*mkdir)(struct kernfs_node *parent, const char *name,
- umode_t mode);
+ struct kernfs_node *(*mkdir)(struct kernfs_node *parent,
+ const char *name, umode_t mode);
int (*rmdir)(struct kernfs_node *kn);
int (*rename)(struct kernfs_node *kn, struct kernfs_node *new_parent,
const char *new_name);
diff --git a/include/uapi/asm-generic/errno.h b/include/uapi/asm-generic/errno.h
index bd78e69e0a43f..c84ebf89c8b6e 100644
--- a/include/uapi/asm-generic/errno.h
+++ b/include/uapi/asm-generic/errno.h
@@ -76,7 +76,7 @@
#define ENOPROTOOPT 92 /* Protocol not available */
#define EPROTONOSUPPORT 93 /* Protocol not supported */
#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
-#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
+#define EOPNOTSUPP 95 /* Operation not supported */
#define EPFNOSUPPORT 96 /* Protocol family not supported */
#define EAFNOSUPPORT 97 /* Address family not supported by protocol */
#define EADDRINUSE 98 /* Address already in use */
diff --git a/kernel/cgroup/cgroup-internal.h b/kernel/cgroup/cgroup-internal.h
index 58797123b752f..eca8d30885391 100644
--- a/kernel/cgroup/cgroup-internal.h
+++ b/kernel/cgroup/cgroup-internal.h
@@ -256,7 +256,8 @@ void cgroup_procs_write_finish(struct task_struct *task,
void cgroup_lock_and_drain_offline(struct cgroup *cgrp);
-int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, umode_t mode);
+struct kernfs_node *cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
+ umode_t mode);
int cgroup_rmdir(struct kernfs_node *kn);
int cgroup_show_path(struct seq_file *sf, struct kernfs_node *kf_node,
struct kernfs_root *kf_root);
diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index 227d09704ca59..b3793b5842ab9 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -6113,18 +6113,20 @@ static bool cgroup_check_hierarchy_limits(struct cgroup *parent)
return ret;
}
-int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, umode_t mode)
+struct kernfs_node *cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
+ umode_t mode)
{
struct cgroup *parent, *cgrp;
+ struct kernfs_node *kn;
int ret;
/* do not accept '\n' to prevent making /proc/<pid>/cgroup unparsable */
if (strchr(name, '\n'))
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
parent = cgroup_kn_lock_live(parent_kn, false);
if (!parent)
- return -ENODEV;
+ return ERR_PTR(-ENODEV);
if (!cgroup_check_hierarchy_limits(parent)) {
ret = -EAGAIN;
@@ -6156,14 +6158,16 @@ int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, umode_t mode)
/* let's create and online css's */
kernfs_activate(cgrp->kn);
- ret = 0;
- goto out_unlock;
+ kn = cgrp->kn;
+ kernfs_get(kn);
+ cgroup_kn_unlock(parent_kn);
+ return kn;
out_destroy:
cgroup_destroy_locked(cgrp);
out_unlock:
cgroup_kn_unlock(parent_kn);
- return ret;
+ return ERR_PTR(ret);
}
/*
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 5b42140e12e87..8ae096cc1af67 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -248,6 +248,7 @@ static int apparmorfs_fill_super(struct super_block *sb, struct fs_context *fc)
if (error)
return error;
sb->s_op = &aafs_super_ops;
+ sb->s_iflags |= SB_I_NO_ATOMIC_MKDIR;
return 0;
}
diff --git a/tools/testing/selftests/filesystems/.gitignore b/tools/testing/selftests/filesystems/.gitignore
index 62f7b1c466497..e0ae02a5d2d11 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/selftests/filesystems/Makefile
index 93e2cc9123b04..a8b48cb6afd64 100644
--- a/tools/testing/selftests/filesystems/Makefile
+++ b/tools/testing/selftests/filesystems/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
CFLAGS += $(KHDR_INCLUDES)
-TEST_GEN_PROGS := devpts_pts file_stressor anon_inode_test kernfs_test fclog ustat_test rw_hint_test
+TEST_GEN_PROGS := open_o_creat_o_dir devpts_pts file_stressor anon_inode_test kernfs_test fclog ustat_test rw_hint_test
TEST_GEN_PROGS += idmapped_tmpfile
TEST_GEN_PROGS_EXTENDED := dnotify_test
diff --git a/tools/testing/selftests/filesystems/open_o_creat_o_dir.c b/tools/testing/selftests/filesystems/open_o_creat_o_dir.c
new file mode 100644
index 0000000000000..93c0f4dea266a
--- /dev/null
+++ b/tools/testing/selftests/filesystems/open_o_creat_o_dir.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <errno.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/magic.h>
+
+#include "kselftest_harness.h"
+#include "wrappers.h"
+
+#define openat_o_mkdir_checked_flags(dfd, pathname, flags) ({ \
+ struct stat __st; \
+ int __fd = openat_o_mkdir(dfd, pathname, flags, S_IRWXU); \
+ ASSERT_GE(__fd, 0); \
+ ASSERT_EQ(fstat(__fd, &__st), 0); \
+ EXPECT_TRUE(S_ISDIR(__st.st_mode)); \
+ __fd; \
+})
+
+#define openat_o_mkdir_checked(dfd, pathname) \
+ openat_o_mkdir_checked_flags(dfd, pathname, O_RDONLY)
+
+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 = 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 = openat_o_mkdir_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 = openat_o_mkdir_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(openat_o_mkdir(self->dfd, "nonexistent/child", O_RDONLY, S_IRWXU), -1);
+ EXPECT_EQ(errno, ENOENT);
+}
+
+/* An invalid dfd must fail with EBADF. */
+TEST_F(open_o_creat_o_dir, ebadf)
+{
+ EXPECT_EQ(openat_o_mkdir(FD_INVALID, "badfdir", O_RDONLY, S_IRWXU), -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 = openat(self->dfd, "file",
+ O_CREAT | O_RDONLY, S_IRWXU);
+ ASSERT_GE(file_fd, 0);
+
+ EXPECT_EQ(openat_o_mkdir(file_fd, "subdir", O_RDONLY, S_IRWXU), -1);
+ EXPECT_EQ(errno, ENOTDIR);
+
+ EXPECT_EQ(close(file_fd), 0);
+ EXPECT_EQ(unlinkat(self->dfd, "file", 0), 0);
+}
+
+/*
+ * O_CREAT|O_DIRECTORY without O_EXCL must fail with EINVAL and must not
+ * create anything at the path.
+ */
+TEST_F(open_o_creat_o_dir, requires_o_excl)
+{
+ struct stat st;
+
+ EXPECT_EQ(openat(self->dfd, "noexcl", O_CREAT | O_DIRECTORY | O_RDONLY,
+ S_IRWXU), -1);
+ EXPECT_EQ(errno, EINVAL);
+
+ EXPECT_EQ(fstatat(self->dfd, "noexcl", &st, 0), -1);
+ EXPECT_EQ(errno, ENOENT);
+}
+
+/*
+ * Once the directory exists, repeating O_CREAT|O_DIRECTORY|O_EXCL must
+ * fail with EEXIST.
+ */
+TEST_F(open_o_creat_o_dir, existing_dir_eexist)
+{
+ int excldir_fd;
+
+ excldir_fd = openat_o_mkdir_checked(self->dfd, "excldir");
+
+ EXPECT_EQ(openat_o_mkdir(self->dfd, "excldir", O_RDONLY, S_IRWXU), -1);
+ EXPECT_EQ(errno, EEXIST);
+
+ EXPECT_EQ(close(excldir_fd), 0);
+ EXPECT_EQ(unlinkat(self->dfd, "excldir", AT_REMOVEDIR), 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(openat_o_mkdir(self->dfd, "rdwrdir", O_RDWR, S_IRWXU), -1);
+ EXPECT_EQ(errno, ENOTDIR);
+}
+
+/*
+ * openat(O_CREAT|O_DIRECTORY) with a trailing slash should work.
+ */
+TEST_F(open_o_creat_o_dir, trailing_slash)
+{
+ int fd = openat_o_mkdir_checked(self->dfd, "newdir/");
+ EXPECT_EQ(close(fd), 0);
+ EXPECT_EQ(unlinkat(self->dfd, "newdir", AT_REMOVEDIR), 0);
+}
+
+/*
+ * 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 = openat(self->dfd, "trailing/", O_CREAT | O_RDONLY, S_IRWXU);
+ EXPECT_EQ(fd, -1);
+ EXPECT_EQ(errno, EISDIR);
+
+ EXPECT_EQ(fstatat(self->dfd, "trailing", &st, 0), -1);
+ EXPECT_EQ(errno, ENOENT);
+}
+
+/*
+ * 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 = openat_o_mkdir_checked(self->dfd, "parent");
+ child_fd = openat_o_mkdir_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);
+}
+
+/*
+ * O_EXCL means a trailing symlink is never followed, so O_CREAT|O_DIRECTORY
+ * must refuse to create through a dangling one, and must not create
+ * anything at the symlink target.
+ */
+TEST_F(open_o_creat_o_dir, dangling_symlink_eexist)
+{
+ struct stat st;
+
+ ASSERT_EQ(symlinkat("danglink_target", self->dfd, "danglink"), 0);
+
+ EXPECT_EQ(openat_o_mkdir(self->dfd, "danglink", O_RDONLY, S_IRWXU), -1);
+ EXPECT_EQ(errno, EEXIST);
+
+ /* Nothing must have been created at the target. */
+ EXPECT_EQ(fstatat(self->dfd, "danglink_target", &st, 0), -1);
+ EXPECT_EQ(errno, ENOENT);
+
+ EXPECT_EQ(unlinkat(self->dfd, "danglink", 0), 0);
+}
+
+/*
+ * A trailing symlink is an existing entry, even if it resolves to a
+ * directory, so O_CREAT|O_DIRECTORY must fail with EEXIST.
+ */
+TEST_F(open_o_creat_o_dir, symlink_to_dir_eexist)
+{
+ ASSERT_EQ(mkdirat(self->dfd, "realdir", 0700), 0);
+ ASSERT_EQ(symlinkat("realdir", self->dfd, "dirlink"), 0);
+
+ EXPECT_EQ(openat_o_mkdir(self->dfd, "dirlink", O_RDONLY, S_IRWXU), -1);
+ EXPECT_EQ(errno, EEXIST);
+
+ EXPECT_EQ(unlinkat(self->dfd, "dirlink", 0), 0);
+ EXPECT_EQ(unlinkat(self->dfd, "realdir", AT_REMOVEDIR), 0);
+}
+
+/*
+ * O_TMPFILE is encoded as __O_TMPFILE|O_DIRECTORY. Now that O_CREAT is no
+ * longer rejected alongside O_DIRECTORY, O_TMPFILE|O_CREAT must still be
+ * rejected explicitly so that it cannot create a persistent file on kernels
+ * that predate O_TMPFILE.
+ */
+TEST_F(open_o_creat_o_dir, tmpfile_with_o_creat_einval)
+{
+ EXPECT_EQ(openat(self->dfd, ".", O_TMPFILE | O_CREAT | O_RDWR, S_IRWXU),
+ -1);
+ EXPECT_EQ(errno, EINVAL);
+}
+
+/*
+ * Open the first of @paths that is a mounted filesystem of type @magic.
+ * Returns -1 if there is none.
+ */
+static int open_fs_root(const char *const *paths, long magic)
+{
+ for (; *paths; paths++) {
+ struct statfs sfs;
+ int fd;
+
+ fd = open(*paths, O_DIRECTORY | O_RDONLY);
+ if (fd < 0)
+ continue;
+ if (!fstatfs(fd, &sfs) && sfs.f_type == magic)
+ return fd;
+ close(fd);
+ }
+ return -1;
+}
+
+/*
+ * kernfs: O_CREAT|O_DIRECTORY on cgroup2 must create a cgroup and return a
+ * fd to that very cgroup.
+ */
+TEST(kernfs_cgroup2)
+{
+ static const char *const paths[] = {
+ "/sys/fs/cgroup", "/sys/fs/cgroup/unified", NULL
+ };
+ struct stat st_fd, st_path;
+ int root_fd, fd, procs_fd;
+ char name[64];
+
+ if (geteuid() != 0)
+ SKIP(return, "needs root to create a cgroup");
+ root_fd = open_fs_root(paths, CGROUP2_SUPER_MAGIC);
+ if (root_fd < 0)
+ SKIP(return, "cgroup2 is not mounted");
+
+ snprintf(name, sizeof(name), "open_o_creat_o_dir.%d", getpid());
+
+ fd = openat_o_mkdir_checked(root_fd, name);
+
+ /* The fd refers to the cgroup that is now visible at the name. */
+ ASSERT_EQ(fstat(fd, &st_fd), 0);
+ ASSERT_EQ(fstatat(root_fd, name, &st_path, 0), 0);
+ EXPECT_EQ(st_fd.st_ino, st_path.st_ino);
+ EXPECT_EQ(st_fd.st_dev, st_path.st_dev);
+
+ /* It is a populated cgroup, not just an empty directory. */
+ procs_fd = openat(fd, "cgroup.procs", O_RDONLY);
+ EXPECT_GE(procs_fd, 0);
+ if (procs_fd >= 0)
+ EXPECT_EQ(close(procs_fd), 0);
+
+ EXPECT_EQ(close(fd), 0);
+ EXPECT_EQ(unlinkat(root_fd, name, AT_REMOVEDIR), 0);
+ EXPECT_EQ(close(root_fd), 0);
+}
+
+/*
+ * tracefs sets SB_I_NO_ATOMIC_MKDIR: O_CREAT|O_DIRECTORY is refused with
+ * EOPNOTSUPP before any lookup, whether or not the name exists, and
+ * creates nothing.
+ */
+TEST(tracefs_eopnotsupp)
+{
+ static const char *const paths[] = {
+ "/sys/kernel/tracing", "/sys/kernel/debug/tracing", NULL
+ };
+ int tracing_fd, inst_fd;
+ char name[64];
+ struct stat st;
+
+ if (geteuid() != 0)
+ SKIP(return, "needs root to access tracefs");
+ tracing_fd = open_fs_root(paths, TRACEFS_MAGIC);
+ if (tracing_fd < 0)
+ SKIP(return, "tracefs is not mounted");
+ inst_fd = openat(tracing_fd, "instances", O_DIRECTORY | O_RDONLY);
+ if (inst_fd < 0) {
+ close(tracing_fd);
+ SKIP(return, "no tracefs instances directory");
+ }
+
+ snprintf(name, sizeof(name), "open_o_creat_o_dir.%d", getpid());
+
+ /* Nonexistent name, where mkdir(2) would create an instance */
+ EXPECT_EQ(openat_o_mkdir(inst_fd, name, O_RDONLY, S_IRWXU), -1);
+ EXPECT_EQ(errno, EOPNOTSUPP);
+ EXPECT_EQ(fstatat(inst_fd, name, &st, 0), -1);
+ EXPECT_EQ(errno, ENOENT);
+
+ /* Existing name */
+ EXPECT_EQ(openat_o_mkdir(tracing_fd, "instances", O_RDONLY, S_IRWXU), -1);
+ EXPECT_EQ(errno, EOPNOTSUPP);
+
+ EXPECT_EQ(close(inst_fd), 0);
+ EXPECT_EQ(close(tracing_fd), 0);
+}
+
+TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/filesystems/wrappers.h b/tools/testing/selftests/filesystems/wrappers.h
index 420ae4f908cf2..bc58bdb800631 100644
--- a/tools/testing/selftests/filesystems/wrappers.h
+++ b/tools/testing/selftests/filesystems/wrappers.h
@@ -13,6 +13,10 @@
#define STATX_MNT_ID_UNIQUE 0x00004000U /* Want/got extended stx_mount_id */
#endif
+#ifndef FD_INVALID
+#define FD_INVALID -10009
+#endif
+
static inline int sys_fsopen(const char *fsname, unsigned int flags)
{
return syscall(__NR_fsopen, fsname, flags);
@@ -105,4 +109,11 @@ static inline int sys_open_tree(int dfd, const char *filename, unsigned int flag
return syscall(__NR_open_tree, dfd, filename, flags);
}
+static inline int openat_o_mkdir(int dfd, const char *pathname,
+ unsigned int flags, mode_t mode)
+{
+ return syscall(__NR_openat, dfd, pathname,
+ flags | O_DIRECTORY | O_CREAT | O_EXCL, mode);
+}
+
#endif
|
| 41/3 | 2026/10/16 22:33 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 23115 output: 282 thoughts: 4040 Thoughts: **Analysis of the "openat O_CREAT | O_DIRECTORY | O_EXCL" Patch for KMSAN Needs** Okay, so this patch series focuses on adding atomic directory creation and opening functionality using `openat`. I've gone through the code changes meticulously, focusing on whether they introduce any uninitialized memory issues that KMSAN is designed to catch. My assessment is that `NeedsKMSAN` should be set to `false`. Let me break down my reasoning: First, this patch primarily deals with adding a new `openat` flag. It touches various filesystems and VFS components, including how directories are created (`kernfs`, `namei`, `vfs`, `cgroup`, and several filesystems to signal no-support). I looked closely at how the code handles inode allocation, dentry lookups, and error returns. The primary focus is path resolution and the eventual opening or creation of a file descriptor. Second, and most importantly, I carefully examined the modified functions for any potential uninitialized memory issues. I went through the relevant functions. No data is directly copied to userspace. All variables used (e.g., `inode`, `dentry`, `error`, `kn`) are either initialized at declaration or assigned values before being used. The patch does not introduce any new kernel structures sent to userspace via `copy_to_user`, `put_user`, netlink, ioctl, or other similar mechanisms. All the data that's being set or read will be tracked. The potential problems here are related to existing locking or reference counting bugs, for example dentry refcount leaks or inode refcount leaks. Finally, while the patch modifies several functions, the primary risk here, as usual, is with refcounting, locking, and error handling. These types of bugs are typically caught by KASAN, LOCKDEP, and standard kernel debugging facilities, not KMSAN. The focus of KMSAN is on detecting uninitialized memory reads or information leaks to userspace. Since this patch does *not* introduce these kinds of issues, and all potentially affected parts of existing paths are carefully checked. There is nothing to report. I am confident that `NeedsKMSAN` should be false. |
| 42/3 | 2026/10/16 22:34 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch series implements support for atomic directory creation and opening via openat(..., O_CREAT | O_DIRECTORY | O_EXCL, ...). It updates VFS path resolution and directory creation helpers (lookup_open, finish_lookup, vfs_mkdir_no_perm, may_o_create), adjusts kernfs, cgroup, and rdtgroup mkdir hooks to return struct kernfs_node *, and adds the SB_I_NO_ATOMIC_MKDIR flag for filesystems that do not support this feature.\n\nNo uninitialized memory risks are introduced:\n1. No kernel structures or data buffers are copied to user space (the syscall only returns a file descriptor or negative errno).\n2. All new/modified stack and heap variables in the modified functions are explicitly initialized before use.\n3. Inode management in kernfs uses standard inode allocation (new_inode) and initialization (kernfs_init_inode).\n4. Any potential issues with reference counting (dentry/inode put/get), locking (mutexes and rwsems), or pointer handling are under the purview of KASAN and LOCKDEP rather than KMSAN.\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch series implements support for atomic directory creation and opening via openat(..., O_CREAT | O_DIRECTORY | O_EXCL, ...). It updates VFS path resolution and directory creation helpers (lookup_open, finish_lookup, vfs_mkdir_no_perm, may_o_create), adjusts kernfs, cgroup, and rdtgroup mkdir hooks to return struct kernfs_node *, and adds the SB_I_NO_ATOMIC_MKDIR flag for filesystems that do not support this feature.\n\nNo uninitialized memory risks are introduced:\n1. No kernel structures or data buffers are copied to user space (the syscall only returns a file descriptor or negative errno).\n2. All new/modified stack and heap variables in the modified functions are explicitly initialized before use.\n3. Inode management in kernfs uses standard inode allocation (new_inode) and initialization (kernfs_init_inode).\n4. Any potential issues with reference counting (dentry/inode put/get), locking (mutexes and rwsems), or pointer handling are under the purview of KASAN and LOCKDEP rather than KMSAN.\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|