Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
6d66f5cd TH |
2 | * fs/sysfs/symlink.c - operations for initializing and mounting sysfs |
3 | * | |
4 | * Copyright (c) 2001-3 Patrick Mochel | |
5 | * Copyright (c) 2007 SUSE Linux Products GmbH | |
6 | * Copyright (c) 2007 Tejun Heo <teheo@suse.de> | |
7 | * | |
8 | * This file is released under the GPLv2. | |
9 | * | |
10 | * Please see Documentation/filesystems/sysfs.txt for more information. | |
1da177e4 LT |
11 | */ |
12 | ||
6b8fbde4 | 13 | #define DEBUG |
1da177e4 LT |
14 | |
15 | #include <linux/fs.h> | |
16 | #include <linux/mount.h> | |
17 | #include <linux/pagemap.h> | |
18 | #include <linux/init.h> | |
f1282c84 | 19 | #include <linux/module.h> |
8231f2f9 | 20 | #include <linux/magic.h> |
5a0e3ad6 | 21 | #include <linux/slab.h> |
87a8ebd6 | 22 | #include <linux/user_namespace.h> |
1da177e4 LT |
23 | |
24 | #include "sysfs.h" | |
25 | ||
1da177e4 | 26 | |
e18b890b | 27 | struct kmem_cache *sysfs_dir_cachep; |
1da177e4 | 28 | |
ee9b6d61 | 29 | static const struct super_operations sysfs_ops = { |
1da177e4 | 30 | .statfs = simple_statfs, |
90bc6135 | 31 | .drop_inode = generic_delete_inode, |
01cd9fef | 32 | .evict_inode = sysfs_evict_inode, |
1da177e4 LT |
33 | }; |
34 | ||
ba7443bc TH |
35 | static struct kernfs_root *sysfs_root; |
36 | struct sysfs_dirent *sysfs_root_sd; | |
061447a4 | 37 | |
ccc532dc | 38 | static int sysfs_fill_super(struct super_block *sb) |
1da177e4 | 39 | { |
df394fb5 | 40 | struct sysfs_super_info *info = sysfs_info(sb); |
1da177e4 LT |
41 | struct inode *inode; |
42 | struct dentry *root; | |
43 | ||
44 | sb->s_blocksize = PAGE_CACHE_SIZE; | |
45 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; | |
46 | sb->s_magic = SYSFS_MAGIC; | |
47 | sb->s_op = &sysfs_ops; | |
48 | sb->s_time_gran = 1; | |
1da177e4 | 49 | |
e080e436 | 50 | /* get root inode, initialize and unlock it */ |
4a67a1bc | 51 | mutex_lock(&sysfs_mutex); |
df394fb5 | 52 | inode = sysfs_get_inode(sb, info->root->sd); |
4a67a1bc | 53 | mutex_unlock(&sysfs_mutex); |
fc9f54b9 | 54 | if (!inode) { |
1da177e4 LT |
55 | pr_debug("sysfs: could not get root inode\n"); |
56 | return -ENOMEM; | |
57 | } | |
58 | ||
e080e436 | 59 | /* instantiate and link root dentry */ |
48fde701 | 60 | root = d_make_root(inode); |
1da177e4 | 61 | if (!root) { |
1b18dc2b | 62 | pr_debug("%s: could not get root dentry!\n", __func__); |
1da177e4 LT |
63 | return -ENOMEM; |
64 | } | |
df394fb5 TH |
65 | kernfs_get(info->root->sd); |
66 | root->d_fsdata = info->root->sd; | |
1da177e4 | 67 | sb->s_root = root; |
469796d1 | 68 | sb->s_d_op = &sysfs_dentry_ops; |
1da177e4 LT |
69 | return 0; |
70 | } | |
71 | ||
9e7fdd25 EB |
72 | static int sysfs_test_super(struct super_block *sb, void *data) |
73 | { | |
74 | struct sysfs_super_info *sb_info = sysfs_info(sb); | |
75 | struct sysfs_super_info *info = data; | |
3ff195b0 | 76 | |
df394fb5 | 77 | return sb_info->root == info->root && sb_info->ns == info->ns; |
9e7fdd25 EB |
78 | } |
79 | ||
80 | static int sysfs_set_super(struct super_block *sb, void *data) | |
81 | { | |
82 | int error; | |
83 | error = set_anon_super(sb, data); | |
84 | if (!error) | |
85 | sb->s_fs_info = data; | |
86 | return error; | |
87 | } | |
88 | ||
4b93dc9b TH |
89 | /** |
90 | * kernfs_super_ns - determine the namespace tag of a kernfs super_block | |
91 | * @sb: super_block of interest | |
92 | * | |
93 | * Return the namespace tag associated with kernfs super_block @sb. | |
94 | */ | |
95 | const void *kernfs_super_ns(struct super_block *sb) | |
a685e089 | 96 | { |
4b93dc9b TH |
97 | struct sysfs_super_info *info = sysfs_info(sb); |
98 | ||
99 | return info->ns; | |
a685e089 AV |
100 | } |
101 | ||
d0e46f88 AV |
102 | static struct dentry *sysfs_mount(struct file_system_type *fs_type, |
103 | int flags, const char *dev_name, void *data) | |
1da177e4 | 104 | { |
4b93dc9b TH |
105 | struct dentry *root; |
106 | void *ns; | |
9e7fdd25 | 107 | |
7dc5dbc8 EB |
108 | if (!(flags & MS_KERNMOUNT)) { |
109 | if (!capable(CAP_SYS_ADMIN) && !fs_fully_visible(fs_type)) | |
110 | return ERR_PTR(-EPERM); | |
111 | ||
c84a3b27 TH |
112 | if (!kobj_ns_current_may_mount(KOBJ_NS_TYPE_NET)) |
113 | return ERR_PTR(-EPERM); | |
7dc5dbc8 | 114 | } |
87a8ebd6 | 115 | |
4b93dc9b TH |
116 | ns = kobj_ns_grab_current(KOBJ_NS_TYPE_NET); |
117 | root = kernfs_mount_ns(fs_type, flags, sysfs_root, ns); | |
118 | if (IS_ERR(root)) | |
119 | kobj_ns_drop(KOBJ_NS_TYPE_NET, ns); | |
120 | return root; | |
121 | } | |
122 | ||
123 | /** | |
124 | * kernfs_mount_ns - kernfs mount helper | |
125 | * @fs_type: file_system_type of the fs being mounted | |
126 | * @flags: mount flags specified for the mount | |
127 | * @root: kernfs_root of the hierarchy being mounted | |
128 | * @ns: optional namespace tag of the mount | |
129 | * | |
130 | * This is to be called from each kernfs user's file_system_type->mount() | |
131 | * implementation, which should pass through the specified @fs_type and | |
132 | * @flags, and specify the hierarchy and namespace tag to mount via @root | |
133 | * and @ns, respectively. | |
134 | * | |
135 | * The return value can be passed to the vfs layer verbatim. | |
136 | */ | |
137 | struct dentry *kernfs_mount_ns(struct file_system_type *fs_type, int flags, | |
138 | struct kernfs_root *root, const void *ns) | |
139 | { | |
140 | struct super_block *sb; | |
141 | struct sysfs_super_info *info; | |
142 | int error; | |
143 | ||
9e7fdd25 EB |
144 | info = kzalloc(sizeof(*info), GFP_KERNEL); |
145 | if (!info) | |
d0e46f88 | 146 | return ERR_PTR(-ENOMEM); |
3ff195b0 | 147 | |
4b93dc9b TH |
148 | info->root = root; |
149 | info->ns = ns; | |
3ff195b0 | 150 | |
9249e17f | 151 | sb = sget(fs_type, sysfs_test_super, sysfs_set_super, flags, info); |
9e7fdd25 | 152 | if (IS_ERR(sb) || sb->s_fs_info != info) |
4b93dc9b | 153 | kfree(info); |
d0e46f88 AV |
154 | if (IS_ERR(sb)) |
155 | return ERR_CAST(sb); | |
9e7fdd25 | 156 | if (!sb->s_root) { |
ccc532dc | 157 | error = sysfs_fill_super(sb); |
9e7fdd25 EB |
158 | if (error) { |
159 | deactivate_locked_super(sb); | |
d0e46f88 | 160 | return ERR_PTR(error); |
9e7fdd25 EB |
161 | } |
162 | sb->s_flags |= MS_ACTIVE; | |
163 | } | |
164 | ||
d0e46f88 | 165 | return dget(sb->s_root); |
9e7fdd25 EB |
166 | } |
167 | ||
168 | static void sysfs_kill_sb(struct super_block *sb) | |
4b93dc9b TH |
169 | { |
170 | kernfs_kill_sb(sb); | |
171 | kobj_ns_drop(KOBJ_NS_TYPE_NET, (void *)kernfs_super_ns(sb)); | |
172 | } | |
173 | ||
174 | /** | |
175 | * kernfs_kill_sb - kill_sb for kernfs | |
176 | * @sb: super_block being killed | |
177 | * | |
178 | * This can be used directly for file_system_type->kill_sb(). If a kernfs | |
179 | * user needs extra cleanup, it can implement its own kill_sb() and call | |
180 | * this function at the end. | |
181 | */ | |
182 | void kernfs_kill_sb(struct super_block *sb) | |
9e7fdd25 EB |
183 | { |
184 | struct sysfs_super_info *info = sysfs_info(sb); | |
ba7443bc TH |
185 | struct sysfs_dirent *root_sd = sb->s_root->d_fsdata; |
186 | ||
187 | /* | |
188 | * Remove the superblock from fs_supers/s_instances | |
68d75ed4 EB |
189 | * so we can't find it, before freeing sysfs_super_info. |
190 | */ | |
9e7fdd25 | 191 | kill_anon_super(sb); |
4b93dc9b | 192 | kfree(info); |
ba7443bc | 193 | kernfs_put(root_sd); |
1da177e4 LT |
194 | } |
195 | ||
196 | static struct file_system_type sysfs_fs_type = { | |
197 | .name = "sysfs", | |
d0e46f88 | 198 | .mount = sysfs_mount, |
9e7fdd25 | 199 | .kill_sb = sysfs_kill_sb, |
4f326c00 | 200 | .fs_flags = FS_USERNS_MOUNT, |
1da177e4 LT |
201 | }; |
202 | ||
4b93dc9b | 203 | void __init kernfs_init(void) |
1da177e4 | 204 | { |
1da177e4 LT |
205 | sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache", |
206 | sizeof(struct sysfs_dirent), | |
4b93dc9b TH |
207 | 0, SLAB_PANIC, NULL); |
208 | sysfs_inode_init(); | |
209 | } | |
1da177e4 | 210 | |
4b93dc9b TH |
211 | int __init sysfs_init(void) |
212 | { | |
213 | int err; | |
e0bf68dd | 214 | |
ba7443bc | 215 | sysfs_root = kernfs_create_root(NULL); |
4b93dc9b TH |
216 | if (IS_ERR(sysfs_root)) |
217 | return PTR_ERR(sysfs_root); | |
218 | ||
ba7443bc TH |
219 | sysfs_root_sd = sysfs_root->sd; |
220 | ||
1da177e4 | 221 | err = register_filesystem(&sysfs_fs_type); |
4b93dc9b TH |
222 | if (err) { |
223 | kernfs_destroy_root(sysfs_root); | |
224 | return err; | |
225 | } | |
9e30cc95 TH |
226 | |
227 | return 0; | |
1da177e4 | 228 | } |