]> git.sesse.net Git - bcachefs-tools-debian/blob - libbcachefs/chardev.c
Rename from bcache-tools to bcachefs-tools
[bcachefs-tools-debian] / libbcachefs / chardev.c
1 #include "bcachefs.h"
2 #include "bcachefs_ioctl.h"
3 #include "super.h"
4 #include "super-io.h"
5
6 #include <linux/module.h>
7 #include <linux/fs.h>
8 #include <linux/major.h>
9 #include <linux/cdev.h>
10 #include <linux/device.h>
11 #include <linux/ioctl.h>
12 #include <linux/uaccess.h>
13 #include <linux/slab.h>
14
15 static long bch2_ioctl_assemble(struct bch_ioctl_assemble __user *user_arg)
16 {
17         struct bch_ioctl_assemble arg;
18         const char *err;
19         u64 *user_devs = NULL;
20         char **devs = NULL;
21         unsigned i;
22         int ret = -EFAULT;
23
24         if (copy_from_user(&arg, user_arg, sizeof(arg)))
25                 return -EFAULT;
26
27         if (arg.flags || arg.pad)
28                 return -EINVAL;
29
30         user_devs = kmalloc_array(arg.nr_devs, sizeof(u64), GFP_KERNEL);
31         if (!devs)
32                 return -ENOMEM;
33
34         devs = kcalloc(arg.nr_devs, sizeof(char *), GFP_KERNEL);
35
36         if (copy_from_user(user_devs, user_arg->devs,
37                            sizeof(u64) * arg.nr_devs))
38                 goto err;
39
40         for (i = 0; i < arg.nr_devs; i++) {
41                 devs[i] = strndup_user((const char __user *)(unsigned long)
42                                        user_devs[i],
43                                        PATH_MAX);
44                 if (!devs[i]) {
45                         ret = -ENOMEM;
46                         goto err;
47                 }
48         }
49
50         err = bch2_fs_open(devs, arg.nr_devs, bch2_opts_empty(), NULL);
51         if (err) {
52                 pr_err("Could not open filesystem: %s", err);
53                 ret = -EINVAL;
54                 goto err;
55         }
56
57         ret = 0;
58 err:
59         if (devs)
60                 for (i = 0; i < arg.nr_devs; i++)
61                         kfree(devs[i]);
62         kfree(devs);
63         return ret;
64 }
65
66 static long bch2_ioctl_incremental(struct bch_ioctl_incremental __user *user_arg)
67 {
68         struct bch_ioctl_incremental arg;
69         const char *err;
70         char *path;
71
72         if (copy_from_user(&arg, user_arg, sizeof(arg)))
73                 return -EFAULT;
74
75         if (arg.flags || arg.pad)
76                 return -EINVAL;
77
78         path = strndup_user((const char __user *)(unsigned long) arg.dev, PATH_MAX);
79         if (!path)
80                 return -ENOMEM;
81
82         err = bch2_fs_open_incremental(path);
83         kfree(path);
84
85         if (err) {
86                 pr_err("Could not register bcachefs devices: %s", err);
87                 return -EINVAL;
88         }
89
90         return 0;
91 }
92
93 static long bch2_global_ioctl(unsigned cmd, void __user *arg)
94 {
95         switch (cmd) {
96         case BCH_IOCTL_ASSEMBLE:
97                 return bch2_ioctl_assemble(arg);
98         case BCH_IOCTL_INCREMENTAL:
99                 return bch2_ioctl_incremental(arg);
100         default:
101                 return -ENOTTY;
102         }
103 }
104
105 static long bch2_ioctl_query_uuid(struct bch_fs *c,
106                         struct bch_ioctl_query_uuid __user *user_arg)
107 {
108         return copy_to_user(&user_arg->uuid,
109                             &c->sb.user_uuid,
110                             sizeof(c->sb.user_uuid));
111 }
112
113 static long bch2_ioctl_start(struct bch_fs *c, struct bch_ioctl_start __user *user_arg)
114 {
115         struct bch_ioctl_start arg;
116
117         if (copy_from_user(&arg, user_arg, sizeof(arg)))
118                 return -EFAULT;
119
120         if (arg.flags || arg.pad)
121                 return -EINVAL;
122
123         return bch2_fs_start(c) ? -EIO : 0;
124 }
125
126 static long bch2_ioctl_stop(struct bch_fs *c)
127 {
128         bch2_fs_stop(c);
129         return 0;
130 }
131
132 /* returns with ref on ca->ref */
133 static struct bch_dev *bch2_device_lookup(struct bch_fs *c,
134                                          const char __user *dev)
135 {
136         struct block_device *bdev;
137         struct bch_dev *ca;
138         char *path;
139         unsigned i;
140
141         path = strndup_user(dev, PATH_MAX);
142         if (!path)
143                 return ERR_PTR(-ENOMEM);
144
145         bdev = lookup_bdev(strim(path));
146         kfree(path);
147         if (IS_ERR(bdev))
148                 return ERR_CAST(bdev);
149
150         for_each_member_device(ca, c, i)
151                 if (ca->disk_sb.bdev == bdev)
152                         goto found;
153
154         ca = NULL;
155 found:
156         bdput(bdev);
157         return ca;
158 }
159
160 #if 0
161 static struct bch_member *bch2_uuid_lookup(struct bch_fs *c, uuid_le uuid)
162 {
163         struct bch_sb_field_members *mi = bch2_sb_get_members(c->disk_sb);
164         unsigned i;
165
166         lockdep_assert_held(&c->sb_lock);
167
168         for (i = 0; i < c->disk_sb->nr_devices; i++)
169                 if (!memcmp(&mi->members[i].uuid, &uuid, sizeof(uuid)))
170                         return &mi->members[i];
171
172         return NULL;
173 }
174 #endif
175
176 static long bch2_ioctl_disk_add(struct bch_fs *c,
177                         struct bch_ioctl_disk __user *user_arg)
178 {
179         struct bch_ioctl_disk arg;
180         char *path;
181         int ret;
182
183         if (copy_from_user(&arg, user_arg, sizeof(arg)))
184                 return -EFAULT;
185
186         if (arg.flags || arg.pad)
187                 return -EINVAL;
188
189         path = strndup_user((const char __user *)(unsigned long) arg.dev, PATH_MAX);
190         if (!path)
191                 return -ENOMEM;
192
193         ret = bch2_dev_add(c, path);
194         kfree(path);
195
196         return ret;
197 }
198
199 static long bch2_ioctl_disk_remove(struct bch_fs *c,
200                         struct bch_ioctl_disk __user *user_arg)
201 {
202         struct bch_ioctl_disk arg;
203         struct bch_dev *ca;
204
205         if (copy_from_user(&arg, user_arg, sizeof(arg)))
206                 return -EFAULT;
207
208         ca = bch2_device_lookup(c, (const char __user *)(unsigned long) arg.dev);
209         if (IS_ERR(ca))
210                 return PTR_ERR(ca);
211
212         return bch2_dev_remove(c, ca, arg.flags);
213 }
214
215 static long bch2_ioctl_disk_online(struct bch_fs *c,
216                         struct bch_ioctl_disk __user *user_arg)
217 {
218         struct bch_ioctl_disk arg;
219         char *path;
220         int ret;
221
222         if (copy_from_user(&arg, user_arg, sizeof(arg)))
223                 return -EFAULT;
224
225         if (arg.flags || arg.pad)
226                 return -EINVAL;
227
228         path = strndup_user((const char __user *)(unsigned long) arg.dev, PATH_MAX);
229         if (!path)
230                 return -ENOMEM;
231
232         ret = bch2_dev_online(c, path);
233         kfree(path);
234         return ret;
235 }
236
237 static long bch2_ioctl_disk_offline(struct bch_fs *c,
238                         struct bch_ioctl_disk __user *user_arg)
239 {
240         struct bch_ioctl_disk arg;
241         struct bch_dev *ca;
242         int ret;
243
244         if (copy_from_user(&arg, user_arg, sizeof(arg)))
245                 return -EFAULT;
246
247         if (arg.pad)
248                 return -EINVAL;
249
250         ca = bch2_device_lookup(c, (const char __user *)(unsigned long) arg.dev);
251         if (IS_ERR(ca))
252                 return PTR_ERR(ca);
253
254         ret = bch2_dev_offline(c, ca, arg.flags);
255         percpu_ref_put(&ca->ref);
256         return ret;
257 }
258
259 static long bch2_ioctl_disk_set_state(struct bch_fs *c,
260                         struct bch_ioctl_disk_set_state __user *user_arg)
261 {
262         struct bch_ioctl_disk_set_state arg;
263         struct bch_dev *ca;
264         int ret;
265
266         if (copy_from_user(&arg, user_arg, sizeof(arg)))
267                 return -EFAULT;
268
269         ca = bch2_device_lookup(c, (const char __user *)(unsigned long) arg.dev);
270         if (IS_ERR(ca))
271                 return PTR_ERR(ca);
272
273         ret = bch2_dev_set_state(c, ca, arg.new_state, arg.flags);
274
275         percpu_ref_put(&ca->ref);
276         return ret;
277 }
278
279 static long bch2_ioctl_disk_evacuate(struct bch_fs *c,
280                         struct bch_ioctl_disk __user *user_arg)
281 {
282         struct bch_ioctl_disk arg;
283         struct bch_dev *ca;
284         int ret;
285
286         if (copy_from_user(&arg, user_arg, sizeof(arg)))
287                 return -EFAULT;
288
289         ca = bch2_device_lookup(c, (const char __user *)(unsigned long) arg.dev);
290         if (IS_ERR(ca))
291                 return PTR_ERR(ca);
292
293         ret = bch2_dev_evacuate(c, ca);
294
295         percpu_ref_put(&ca->ref);
296         return ret;
297 }
298
299 long bch2_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg)
300 {
301         /* ioctls that don't require admin cap: */
302         switch (cmd) {
303         case BCH_IOCTL_QUERY_UUID:
304                 return bch2_ioctl_query_uuid(c, arg);
305         }
306
307         if (!capable(CAP_SYS_ADMIN))
308                 return -EPERM;
309
310         /* ioctls that do require admin cap: */
311         switch (cmd) {
312         case BCH_IOCTL_START:
313                 return bch2_ioctl_start(c, arg);
314         case BCH_IOCTL_STOP:
315                 return bch2_ioctl_stop(c);
316
317         case BCH_IOCTL_DISK_ADD:
318                 return bch2_ioctl_disk_add(c, arg);
319         case BCH_IOCTL_DISK_REMOVE:
320                 return bch2_ioctl_disk_remove(c, arg);
321         case BCH_IOCTL_DISK_ONLINE:
322                 return bch2_ioctl_disk_online(c, arg);
323         case BCH_IOCTL_DISK_OFFLINE:
324                 return bch2_ioctl_disk_offline(c, arg);
325         case BCH_IOCTL_DISK_SET_STATE:
326                 return bch2_ioctl_disk_set_state(c, arg);
327         case BCH_IOCTL_DISK_EVACUATE:
328                 return bch2_ioctl_disk_evacuate(c, arg);
329
330         default:
331                 return -ENOTTY;
332         }
333 }
334
335 static long bch2_chardev_ioctl(struct file *filp, unsigned cmd, unsigned long v)
336 {
337         struct bch_fs *c = filp->private_data;
338         void __user *arg = (void __user *) v;
339
340         return c
341                 ? bch2_fs_ioctl(c, cmd, arg)
342                 : bch2_global_ioctl(cmd, arg);
343 }
344
345 static const struct file_operations bch_chardev_fops = {
346         .owner          = THIS_MODULE,
347         .unlocked_ioctl = bch2_chardev_ioctl,
348         .open           = nonseekable_open,
349 };
350
351 static int bch_chardev_major;
352 static struct class *bch_chardev_class;
353 static struct device *bch_chardev;
354 static DEFINE_IDR(bch_chardev_minor);
355
356 void bch2_fs_chardev_exit(struct bch_fs *c)
357 {
358         if (!IS_ERR_OR_NULL(c->chardev))
359                 device_unregister(c->chardev);
360         if (c->minor >= 0)
361                 idr_remove(&bch_chardev_minor, c->minor);
362 }
363
364 int bch2_fs_chardev_init(struct bch_fs *c)
365 {
366         c->minor = idr_alloc(&bch_chardev_minor, c, 0, 0, GFP_KERNEL);
367         if (c->minor < 0)
368                 return c->minor;
369
370         c->chardev = device_create(bch_chardev_class, NULL,
371                                    MKDEV(bch_chardev_major, c->minor), NULL,
372                                    "bcachefs%u-ctl", c->minor);
373         if (IS_ERR(c->chardev))
374                 return PTR_ERR(c->chardev);
375
376         return 0;
377 }
378
379 void bch2_chardev_exit(void)
380 {
381         if (!IS_ERR_OR_NULL(bch_chardev_class))
382                 device_destroy(bch_chardev_class,
383                                MKDEV(bch_chardev_major, 255));
384         if (!IS_ERR_OR_NULL(bch_chardev_class))
385                 class_destroy(bch_chardev_class);
386         if (bch_chardev_major > 0)
387                 unregister_chrdev(bch_chardev_major, "bcachefs");
388 }
389
390 int __init bch2_chardev_init(void)
391 {
392         bch_chardev_major = register_chrdev(0, "bcachefs-ctl", &bch_chardev_fops);
393         if (bch_chardev_major < 0)
394                 return bch_chardev_major;
395
396         bch_chardev_class = class_create(THIS_MODULE, "bcachefs");
397         if (IS_ERR(bch_chardev_class))
398                 return PTR_ERR(bch_chardev_class);
399
400         bch_chardev = device_create(bch_chardev_class, NULL,
401                                     MKDEV(bch_chardev_major, 255),
402                                     NULL, "bcachefs-ctl");
403         if (IS_ERR(bch_chardev))
404                 return PTR_ERR(bch_chardev);
405
406         return 0;
407 }