diff options
Diffstat (limited to 'fs/sysfs/mount.c')
-rw-r--r-- | fs/sysfs/mount.c | 184 |
1 files changed, 25 insertions, 159 deletions
diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 834ec2cdb7a3..6211230814fd 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c | |||
@@ -14,146 +14,41 @@ | |||
14 | 14 | ||
15 | #include <linux/fs.h> | 15 | #include <linux/fs.h> |
16 | #include <linux/mount.h> | 16 | #include <linux/mount.h> |
17 | #include <linux/pagemap.h> | ||
18 | #include <linux/init.h> | 17 | #include <linux/init.h> |
19 | #include <linux/module.h> | ||
20 | #include <linux/magic.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/user_namespace.h> | 18 | #include <linux/user_namespace.h> |
23 | 19 | ||
24 | #include "sysfs.h" | 20 | #include "sysfs.h" |
25 | 21 | ||
26 | 22 | static struct kernfs_root *sysfs_root; | |
27 | static struct vfsmount *sysfs_mnt; | 23 | struct kernfs_node *sysfs_root_kn; |
28 | struct kmem_cache *sysfs_dir_cachep; | ||
29 | |||
30 | static const struct super_operations sysfs_ops = { | ||
31 | .statfs = simple_statfs, | ||
32 | .drop_inode = generic_delete_inode, | ||
33 | .evict_inode = sysfs_evict_inode, | ||
34 | }; | ||
35 | |||
36 | struct sysfs_dirent sysfs_root = { | ||
37 | .s_name = "", | ||
38 | .s_count = ATOMIC_INIT(1), | ||
39 | .s_flags = SYSFS_DIR | (KOBJ_NS_TYPE_NONE << SYSFS_NS_TYPE_SHIFT), | ||
40 | .s_mode = S_IFDIR | S_IRUGO | S_IXUGO, | ||
41 | .s_ino = 1, | ||
42 | }; | ||
43 | |||
44 | static int sysfs_fill_super(struct super_block *sb, void *data, int silent) | ||
45 | { | ||
46 | struct inode *inode; | ||
47 | struct dentry *root; | ||
48 | |||
49 | sb->s_blocksize = PAGE_CACHE_SIZE; | ||
50 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; | ||
51 | sb->s_magic = SYSFS_MAGIC; | ||
52 | sb->s_op = &sysfs_ops; | ||
53 | sb->s_time_gran = 1; | ||
54 | |||
55 | /* get root inode, initialize and unlock it */ | ||
56 | mutex_lock(&sysfs_mutex); | ||
57 | inode = sysfs_get_inode(sb, &sysfs_root); | ||
58 | mutex_unlock(&sysfs_mutex); | ||
59 | if (!inode) { | ||
60 | pr_debug("sysfs: could not get root inode\n"); | ||
61 | return -ENOMEM; | ||
62 | } | ||
63 | |||
64 | /* instantiate and link root dentry */ | ||
65 | root = d_make_root(inode); | ||
66 | if (!root) { | ||
67 | pr_debug("%s: could not get root dentry!\n", __func__); | ||
68 | return -ENOMEM; | ||
69 | } | ||
70 | root->d_fsdata = &sysfs_root; | ||
71 | sb->s_root = root; | ||
72 | sb->s_d_op = &sysfs_dentry_ops; | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | static int sysfs_test_super(struct super_block *sb, void *data) | ||
77 | { | ||
78 | struct sysfs_super_info *sb_info = sysfs_info(sb); | ||
79 | struct sysfs_super_info *info = data; | ||
80 | enum kobj_ns_type type; | ||
81 | int found = 1; | ||
82 | |||
83 | for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) { | ||
84 | if (sb_info->ns[type] != info->ns[type]) | ||
85 | found = 0; | ||
86 | } | ||
87 | return found; | ||
88 | } | ||
89 | |||
90 | static int sysfs_set_super(struct super_block *sb, void *data) | ||
91 | { | ||
92 | int error; | ||
93 | error = set_anon_super(sb, data); | ||
94 | if (!error) | ||
95 | sb->s_fs_info = data; | ||
96 | return error; | ||
97 | } | ||
98 | |||
99 | static void free_sysfs_super_info(struct sysfs_super_info *info) | ||
100 | { | ||
101 | int type; | ||
102 | for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) | ||
103 | kobj_ns_drop(type, info->ns[type]); | ||
104 | kfree(info); | ||
105 | } | ||
106 | 24 | ||
107 | static struct dentry *sysfs_mount(struct file_system_type *fs_type, | 25 | static struct dentry *sysfs_mount(struct file_system_type *fs_type, |
108 | int flags, const char *dev_name, void *data) | 26 | int flags, const char *dev_name, void *data) |
109 | { | 27 | { |
110 | struct sysfs_super_info *info; | 28 | struct dentry *root; |
111 | enum kobj_ns_type type; | 29 | void *ns; |
112 | struct super_block *sb; | ||
113 | int error; | ||
114 | 30 | ||
115 | if (!(flags & MS_KERNMOUNT)) { | 31 | if (!(flags & MS_KERNMOUNT)) { |
116 | if (!capable(CAP_SYS_ADMIN) && !fs_fully_visible(fs_type)) | 32 | if (!capable(CAP_SYS_ADMIN) && !fs_fully_visible(fs_type)) |
117 | return ERR_PTR(-EPERM); | 33 | return ERR_PTR(-EPERM); |
118 | 34 | ||
119 | for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) { | 35 | if (!kobj_ns_current_may_mount(KOBJ_NS_TYPE_NET)) |
120 | if (!kobj_ns_current_may_mount(type)) | 36 | return ERR_PTR(-EPERM); |
121 | return ERR_PTR(-EPERM); | ||
122 | } | ||
123 | } | ||
124 | |||
125 | info = kzalloc(sizeof(*info), GFP_KERNEL); | ||
126 | if (!info) | ||
127 | return ERR_PTR(-ENOMEM); | ||
128 | |||
129 | for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) | ||
130 | info->ns[type] = kobj_ns_grab_current(type); | ||
131 | |||
132 | sb = sget(fs_type, sysfs_test_super, sysfs_set_super, flags, info); | ||
133 | if (IS_ERR(sb) || sb->s_fs_info != info) | ||
134 | free_sysfs_super_info(info); | ||
135 | if (IS_ERR(sb)) | ||
136 | return ERR_CAST(sb); | ||
137 | if (!sb->s_root) { | ||
138 | error = sysfs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0); | ||
139 | if (error) { | ||
140 | deactivate_locked_super(sb); | ||
141 | return ERR_PTR(error); | ||
142 | } | ||
143 | sb->s_flags |= MS_ACTIVE; | ||
144 | } | 37 | } |
145 | 38 | ||
146 | return dget(sb->s_root); | 39 | ns = kobj_ns_grab_current(KOBJ_NS_TYPE_NET); |
40 | root = kernfs_mount_ns(fs_type, flags, sysfs_root, ns); | ||
41 | if (IS_ERR(root)) | ||
42 | kobj_ns_drop(KOBJ_NS_TYPE_NET, ns); | ||
43 | return root; | ||
147 | } | 44 | } |
148 | 45 | ||
149 | static void sysfs_kill_sb(struct super_block *sb) | 46 | static void sysfs_kill_sb(struct super_block *sb) |
150 | { | 47 | { |
151 | struct sysfs_super_info *info = sysfs_info(sb); | 48 | void *ns = (void *)kernfs_super_ns(sb); |
152 | /* Remove the superblock from fs_supers/s_instances | 49 | |
153 | * so we can't find it, before freeing sysfs_super_info. | 50 | kernfs_kill_sb(sb); |
154 | */ | 51 | kobj_ns_drop(KOBJ_NS_TYPE_NET, ns); |
155 | kill_anon_super(sb); | ||
156 | free_sysfs_super_info(info); | ||
157 | } | 52 | } |
158 | 53 | ||
159 | static struct file_system_type sysfs_fs_type = { | 54 | static struct file_system_type sysfs_fs_type = { |
@@ -165,48 +60,19 @@ static struct file_system_type sysfs_fs_type = { | |||
165 | 60 | ||
166 | int __init sysfs_init(void) | 61 | int __init sysfs_init(void) |
167 | { | 62 | { |
168 | int err = -ENOMEM; | 63 | int err; |
169 | 64 | ||
170 | sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache", | 65 | sysfs_root = kernfs_create_root(NULL, NULL); |
171 | sizeof(struct sysfs_dirent), | 66 | if (IS_ERR(sysfs_root)) |
172 | 0, 0, NULL); | 67 | return PTR_ERR(sysfs_root); |
173 | if (!sysfs_dir_cachep) | ||
174 | goto out; | ||
175 | 68 | ||
176 | err = sysfs_inode_init(); | 69 | sysfs_root_kn = sysfs_root->kn; |
177 | if (err) | ||
178 | goto out_err; | ||
179 | 70 | ||
180 | err = register_filesystem(&sysfs_fs_type); | 71 | err = register_filesystem(&sysfs_fs_type); |
181 | if (!err) { | 72 | if (err) { |
182 | sysfs_mnt = kern_mount(&sysfs_fs_type); | 73 | kernfs_destroy_root(sysfs_root); |
183 | if (IS_ERR(sysfs_mnt)) { | 74 | return err; |
184 | printk(KERN_ERR "sysfs: could not mount!\n"); | 75 | } |
185 | err = PTR_ERR(sysfs_mnt); | ||
186 | sysfs_mnt = NULL; | ||
187 | unregister_filesystem(&sysfs_fs_type); | ||
188 | goto out_err; | ||
189 | } | ||
190 | } else | ||
191 | goto out_err; | ||
192 | out: | ||
193 | return err; | ||
194 | out_err: | ||
195 | kmem_cache_destroy(sysfs_dir_cachep); | ||
196 | sysfs_dir_cachep = NULL; | ||
197 | goto out; | ||
198 | } | ||
199 | |||
200 | #undef sysfs_get | ||
201 | struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd) | ||
202 | { | ||
203 | return __sysfs_get(sd); | ||
204 | } | ||
205 | EXPORT_SYMBOL_GPL(sysfs_get); | ||
206 | 76 | ||
207 | #undef sysfs_put | 77 | return 0; |
208 | void sysfs_put(struct sysfs_dirent *sd) | ||
209 | { | ||
210 | __sysfs_put(sd); | ||
211 | } | 78 | } |
212 | EXPORT_SYMBOL_GPL(sysfs_put); | ||