xref: /linux/fs/failfs.c (revision cd051cfe1e35a471fc2cdf6d32fae6ee23305ecb)
1fa0d6d94SChristian Brauner // SPDX-License-Identifier: GPL-2.0-only
2fa0d6d94SChristian Brauner /* Copyright (c) 2026 Christian Brauner <brauner@kernel.org> */
3fa0d6d94SChristian Brauner #include <linux/fs.h>
4fa0d6d94SChristian Brauner #include <linux/fs/super_types.h>
5fa0d6d94SChristian Brauner #include <linux/fs_context.h>
6*cdf930a0SChristian Brauner #include <linux/fs_struct.h>
7fa0d6d94SChristian Brauner #include <linux/magic.h>
8fa0d6d94SChristian Brauner #include <linux/mount.h>
9fa0d6d94SChristian Brauner 
10fa0d6d94SChristian Brauner #include "internal.h"
11fa0d6d94SChristian Brauner 
12fa0d6d94SChristian Brauner static struct path failfs_root_path = {};
13fa0d6d94SChristian Brauner 
14fa0d6d94SChristian Brauner void failfs_get_root(struct path *path)
15fa0d6d94SChristian Brauner {
16fa0d6d94SChristian Brauner 	*path = failfs_root_path;
17fa0d6d94SChristian Brauner 	path_get(path);
18fa0d6d94SChristian Brauner }
19fa0d6d94SChristian Brauner 
20fa0d6d94SChristian Brauner bool failfs_mnt(const struct vfsmount *mnt)
21fa0d6d94SChristian Brauner {
22fa0d6d94SChristian Brauner 	return mnt->mnt_sb == failfs_root_path.mnt->mnt_sb;
23fa0d6d94SChristian Brauner }
24fa0d6d94SChristian Brauner 
25fa0d6d94SChristian Brauner static int failfs_permission(struct mnt_idmap *idmap, struct inode *inode,
26fa0d6d94SChristian Brauner 			     int mask)
27fa0d6d94SChristian Brauner {
28fa0d6d94SChristian Brauner 	return -EOPNOTSUPP;
29fa0d6d94SChristian Brauner }
30fa0d6d94SChristian Brauner 
31fa0d6d94SChristian Brauner static struct dentry *failfs_lookup(struct inode *dir, struct dentry *dentry,
32fa0d6d94SChristian Brauner 				    unsigned int flags)
33fa0d6d94SChristian Brauner {
34fa0d6d94SChristian Brauner 	/* Unreachable: ->permission() already failed the walk. */
35fa0d6d94SChristian Brauner 	return ERR_PTR(-EOPNOTSUPP);
36fa0d6d94SChristian Brauner }
37fa0d6d94SChristian Brauner 
38fa0d6d94SChristian Brauner static int failfs_getattr(struct mnt_idmap *idmap, const struct path *path,
39fa0d6d94SChristian Brauner 			  struct kstat *stat, u32 request_mask,
40fa0d6d94SChristian Brauner 			  unsigned int query_flags)
41fa0d6d94SChristian Brauner {
42fa0d6d94SChristian Brauner 	return -EOPNOTSUPP;
43fa0d6d94SChristian Brauner }
44fa0d6d94SChristian Brauner 
45fa0d6d94SChristian Brauner static const struct inode_operations failfs_dir_inode_operations = {
46fa0d6d94SChristian Brauner 	.permission	= failfs_permission,
47fa0d6d94SChristian Brauner 	.lookup		= failfs_lookup,
48fa0d6d94SChristian Brauner 	.getattr	= failfs_getattr,
49fa0d6d94SChristian Brauner };
50fa0d6d94SChristian Brauner 
51fa0d6d94SChristian Brauner static const struct file_operations failfs_dir_operations = {};
52fa0d6d94SChristian Brauner 
53fa0d6d94SChristian Brauner static int failfs_d_weak_revalidate(struct dentry *dentry, unsigned int flags)
54fa0d6d94SChristian Brauner {
55fa0d6d94SChristian Brauner 	/*
56fa0d6d94SChristian Brauner 	 * The root is only ever reached as a path-walk terminal by jumping
57fa0d6d94SChristian Brauner 	 * to it: as "/" when it is the caller's root, or through a
58fa0d6d94SChristian Brauner 	 * /proc/<pid>/{root,cwd} magic link. ->permission() already fails
59fa0d6d94SChristian Brauner 	 * every walk of a component, but a jump lands on the root without
60fa0d6d94SChristian Brauner 	 * one. Refuse here too so the root cannot be pinned by an O_PATH
61fa0d6d94SChristian Brauner 	 * open or encoded into a file handle.
62fa0d6d94SChristian Brauner 	 */
63fa0d6d94SChristian Brauner 	return -EOPNOTSUPP;
64fa0d6d94SChristian Brauner }
65fa0d6d94SChristian Brauner 
66fa0d6d94SChristian Brauner static char *failfs_dname(struct dentry *dentry, char *buffer, int buflen)
67fa0d6d94SChristian Brauner {
68fa0d6d94SChristian Brauner 	return dynamic_dname(buffer, buflen, "failfs:/");
69fa0d6d94SChristian Brauner }
70fa0d6d94SChristian Brauner 
71fa0d6d94SChristian Brauner static const struct dentry_operations failfs_dentry_operations = {
72fa0d6d94SChristian Brauner 	.d_dname		= failfs_dname,
73fa0d6d94SChristian Brauner 	.d_weak_revalidate	= failfs_d_weak_revalidate,
74fa0d6d94SChristian Brauner };
75fa0d6d94SChristian Brauner 
76fa0d6d94SChristian Brauner static int failfs_statfs(struct dentry *dentry, struct kstatfs *buf)
77fa0d6d94SChristian Brauner {
78fa0d6d94SChristian Brauner 	return -EOPNOTSUPP;
79fa0d6d94SChristian Brauner }
80fa0d6d94SChristian Brauner 
81fa0d6d94SChristian Brauner static const struct super_operations failfs_super_operations = {
82fa0d6d94SChristian Brauner 	.statfs	= failfs_statfs,
83fa0d6d94SChristian Brauner };
84fa0d6d94SChristian Brauner 
85fa0d6d94SChristian Brauner static int failfs_fill_super(struct super_block *s, struct fs_context *fc)
86fa0d6d94SChristian Brauner {
87fa0d6d94SChristian Brauner 	struct inode *inode;
88fa0d6d94SChristian Brauner 
89fa0d6d94SChristian Brauner 	s->s_maxbytes		= MAX_LFS_FILESIZE;
90fa0d6d94SChristian Brauner 	s->s_blocksize		= PAGE_SIZE;
91fa0d6d94SChristian Brauner 	s->s_blocksize_bits	= PAGE_SHIFT;
92fa0d6d94SChristian Brauner 	s->s_magic		= FAIL_FS_MAGIC;
93fa0d6d94SChristian Brauner 	s->s_op			= &failfs_super_operations;
94fa0d6d94SChristian Brauner 	s->s_export_op		= NULL;
95fa0d6d94SChristian Brauner 	s->s_xattr		= NULL;
96fa0d6d94SChristian Brauner 	s->s_time_gran		= 1;
97fa0d6d94SChristian Brauner 	s->s_d_flags		= 0;
98fa0d6d94SChristian Brauner 
99fa0d6d94SChristian Brauner 	inode = new_inode(s);
100fa0d6d94SChristian Brauner 	if (!inode)
101fa0d6d94SChristian Brauner 		return -ENOMEM;
102fa0d6d94SChristian Brauner 
103fa0d6d94SChristian Brauner 	/* failfs supports no operations... */
104fa0d6d94SChristian Brauner 	inode->i_mode	= S_IFDIR;
105fa0d6d94SChristian Brauner 	set_nlink(inode, 2);
106fa0d6d94SChristian Brauner 	inode->i_op	= &failfs_dir_inode_operations;
107fa0d6d94SChristian Brauner 	inode->i_fop	= &failfs_dir_operations;
108fa0d6d94SChristian Brauner 	simple_inode_init_ts(inode);
109fa0d6d94SChristian Brauner 	inode->i_ino	= 1;
110fa0d6d94SChristian Brauner 	/* ... and is immutable. */
111fa0d6d94SChristian Brauner 	inode->i_flags |= S_IMMUTABLE;
112fa0d6d94SChristian Brauner 
113fa0d6d94SChristian Brauner 	set_default_d_op(s, &failfs_dentry_operations);
114fa0d6d94SChristian Brauner 	s->s_root = d_make_root(inode);
115fa0d6d94SChristian Brauner 	if (!s->s_root)
116fa0d6d94SChristian Brauner 		return -ENOMEM;
117fa0d6d94SChristian Brauner 
118fa0d6d94SChristian Brauner 	return 0;
119fa0d6d94SChristian Brauner }
120fa0d6d94SChristian Brauner 
121fa0d6d94SChristian Brauner static int failfs_get_tree(struct fs_context *fc)
122fa0d6d94SChristian Brauner {
123fa0d6d94SChristian Brauner 	return get_tree_single(fc, failfs_fill_super);
124fa0d6d94SChristian Brauner }
125fa0d6d94SChristian Brauner 
126fa0d6d94SChristian Brauner static const struct fs_context_operations failfs_context_ops = {
127fa0d6d94SChristian Brauner 	.get_tree	= failfs_get_tree,
128fa0d6d94SChristian Brauner };
129fa0d6d94SChristian Brauner 
130fa0d6d94SChristian Brauner static int failfs_init_fs_context(struct fs_context *fc)
131fa0d6d94SChristian Brauner {
132fa0d6d94SChristian Brauner 	fc->ops		= &failfs_context_ops;
133fa0d6d94SChristian Brauner 	fc->global	= true;
134fa0d6d94SChristian Brauner 	fc->sb_flags	|= SB_NOUSER;
135fa0d6d94SChristian Brauner 	fc->s_iflags	|= SB_I_NOEXEC | SB_I_NODEV;
136fa0d6d94SChristian Brauner 	return 0;
137fa0d6d94SChristian Brauner }
138fa0d6d94SChristian Brauner 
139*cdf930a0SChristian Brauner int failfs_current_chdir(void)
140*cdf930a0SChristian Brauner {
141*cdf930a0SChristian Brauner 	struct path path;
142*cdf930a0SChristian Brauner 
143*cdf930a0SChristian Brauner 	failfs_get_root(&path);
144*cdf930a0SChristian Brauner 	set_fs_pwd(current->fs, &path);
145*cdf930a0SChristian Brauner 	path_put(&path);
146*cdf930a0SChristian Brauner 	return 0;
147*cdf930a0SChristian Brauner }
148*cdf930a0SChristian Brauner 
149fa0d6d94SChristian Brauner static struct file_system_type failfs_fs_type = {
150fa0d6d94SChristian Brauner 	.name			= "failfs",
151fa0d6d94SChristian Brauner 	.init_fs_context	= failfs_init_fs_context,
152fa0d6d94SChristian Brauner 	.kill_sb		= kill_anon_super,
153fa0d6d94SChristian Brauner };
154fa0d6d94SChristian Brauner 
155fa0d6d94SChristian Brauner void __init failfs_init(void)
156fa0d6d94SChristian Brauner {
157fa0d6d94SChristian Brauner 	struct vfsmount *mnt;
158fa0d6d94SChristian Brauner 
159fa0d6d94SChristian Brauner 	/* A single instance that is member of no mount namespace. */
160fa0d6d94SChristian Brauner 	mnt = kern_mount(&failfs_fs_type);
161fa0d6d94SChristian Brauner 	if (IS_ERR(mnt))
162fa0d6d94SChristian Brauner 		panic("VFS: Failed to create failfs");
163fa0d6d94SChristian Brauner 
164fa0d6d94SChristian Brauner 	failfs_root_path.mnt	= mnt;
165fa0d6d94SChristian Brauner 	failfs_root_path.dentry	= mnt->mnt_root;
166fa0d6d94SChristian Brauner }
167