]> git.sesse.net Git - bcachefs-tools-debian/blob - libbcachefs/chardev.c
Update bcachefs sources to 5d7142b75a bcachefs: Redo filesystem usage ioctls
[bcachefs-tools-debian] / libbcachefs / chardev.c
1 // SPDX-License-Identifier: GPL-2.0
2 #ifndef NO_BCACHEFS_CHARDEV
3
4 #include "bcachefs.h"
5 #include "bcachefs_ioctl.h"
6 #include "buckets.h"
7 #include "chardev.h"
8 #include "move.h"
9 #include "replicas.h"
10 #include "super.h"
11 #include "super-io.h"
12
13 #include <linux/anon_inodes.h>
14 #include <linux/cdev.h>
15 #include <linux/device.h>
16 #include <linux/file.h>
17 #include <linux/fs.h>
18 #include <linux/ioctl.h>
19 #include <linux/kthread.h>
20 #include <linux/major.h>
21 #include <linux/sched/task.h>
22 #include <linux/slab.h>
23 #include <linux/uaccess.h>
24
25 /* returns with ref on ca->ref */
26 static struct bch_dev *bch2_device_lookup(struct bch_fs *c, u64 dev,
27                                           unsigned flags)
28 {
29         struct bch_dev *ca;
30
31         if (flags & BCH_BY_INDEX) {
32                 if (dev >= c->sb.nr_devices)
33                         return ERR_PTR(-EINVAL);
34
35                 rcu_read_lock();
36                 ca = rcu_dereference(c->devs[dev]);
37                 if (ca)
38                         percpu_ref_get(&ca->ref);
39                 rcu_read_unlock();
40
41                 if (!ca)
42                         return ERR_PTR(-EINVAL);
43         } else {
44                 char *path;
45
46                 path = strndup_user((const char __user *)
47                                     (unsigned long) dev, PATH_MAX);
48                 if (IS_ERR(path))
49                         return ERR_CAST(path);
50
51                 ca = bch2_dev_lookup(c, path);
52                 kfree(path);
53         }
54
55         return ca;
56 }
57
58 #if 0
59 static long bch2_ioctl_assemble(struct bch_ioctl_assemble __user *user_arg)
60 {
61         struct bch_ioctl_assemble arg;
62         struct bch_fs *c;
63         u64 *user_devs = NULL;
64         char **devs = NULL;
65         unsigned i;
66         int ret = -EFAULT;
67
68         if (copy_from_user(&arg, user_arg, sizeof(arg)))
69                 return -EFAULT;
70
71         if (arg.flags || arg.pad)
72                 return -EINVAL;
73
74         user_devs = kmalloc_array(arg.nr_devs, sizeof(u64), GFP_KERNEL);
75         if (!user_devs)
76                 return -ENOMEM;
77
78         devs = kcalloc(arg.nr_devs, sizeof(char *), GFP_KERNEL);
79
80         if (copy_from_user(user_devs, user_arg->devs,
81                            sizeof(u64) * arg.nr_devs))
82                 goto err;
83
84         for (i = 0; i < arg.nr_devs; i++) {
85                 devs[i] = strndup_user((const char __user *)(unsigned long)
86                                        user_devs[i],
87                                        PATH_MAX);
88                 if (!devs[i]) {
89                         ret = -ENOMEM;
90                         goto err;
91                 }
92         }
93
94         c = bch2_fs_open(devs, arg.nr_devs, bch2_opts_empty());
95         ret = PTR_ERR_OR_ZERO(c);
96         if (!ret)
97                 closure_put(&c->cl);
98 err:
99         if (devs)
100                 for (i = 0; i < arg.nr_devs; i++)
101                         kfree(devs[i]);
102         kfree(devs);
103         return ret;
104 }
105
106 static long bch2_ioctl_incremental(struct bch_ioctl_incremental __user *user_arg)
107 {
108         struct bch_ioctl_incremental arg;
109         const char *err;
110         char *path;
111
112         if (copy_from_user(&arg, user_arg, sizeof(arg)))
113                 return -EFAULT;
114
115         if (arg.flags || arg.pad)
116                 return -EINVAL;
117
118         path = strndup_user((const char __user *)(unsigned long) arg.dev, PATH_MAX);
119         if (!path)
120                 return -ENOMEM;
121
122         err = bch2_fs_open_incremental(path);
123         kfree(path);
124
125         if (err) {
126                 pr_err("Could not register bcachefs devices: %s", err);
127                 return -EINVAL;
128         }
129
130         return 0;
131 }
132 #endif
133
134 static long bch2_global_ioctl(unsigned cmd, void __user *arg)
135 {
136         switch (cmd) {
137 #if 0
138         case BCH_IOCTL_ASSEMBLE:
139                 return bch2_ioctl_assemble(arg);
140         case BCH_IOCTL_INCREMENTAL:
141                 return bch2_ioctl_incremental(arg);
142 #endif
143         default:
144                 return -ENOTTY;
145         }
146 }
147
148 static long bch2_ioctl_query_uuid(struct bch_fs *c,
149                         struct bch_ioctl_query_uuid __user *user_arg)
150 {
151         return copy_to_user(&user_arg->uuid,
152                             &c->sb.user_uuid,
153                             sizeof(c->sb.user_uuid));
154 }
155
156 #if 0
157 static long bch2_ioctl_start(struct bch_fs *c, struct bch_ioctl_start arg)
158 {
159         if (arg.flags || arg.pad)
160                 return -EINVAL;
161
162         return bch2_fs_start(c);
163 }
164
165 static long bch2_ioctl_stop(struct bch_fs *c)
166 {
167         bch2_fs_stop(c);
168         return 0;
169 }
170 #endif
171
172 static long bch2_ioctl_disk_add(struct bch_fs *c, struct bch_ioctl_disk arg)
173 {
174         char *path;
175         int ret;
176
177         if (arg.flags || arg.pad)
178                 return -EINVAL;
179
180         path = strndup_user((const char __user *)(unsigned long) arg.dev, PATH_MAX);
181         if (!path)
182                 return -ENOMEM;
183
184         ret = bch2_dev_add(c, path);
185         kfree(path);
186
187         return ret;
188 }
189
190 static long bch2_ioctl_disk_remove(struct bch_fs *c, struct bch_ioctl_disk arg)
191 {
192         struct bch_dev *ca;
193
194         if ((arg.flags & ~(BCH_FORCE_IF_DATA_LOST|
195                            BCH_FORCE_IF_METADATA_LOST|
196                            BCH_FORCE_IF_DEGRADED|
197                            BCH_BY_INDEX)) ||
198             arg.pad)
199                 return -EINVAL;
200
201         ca = bch2_device_lookup(c, arg.dev, arg.flags);
202         if (IS_ERR(ca))
203                 return PTR_ERR(ca);
204
205         return bch2_dev_remove(c, ca, arg.flags);
206 }
207
208 static long bch2_ioctl_disk_online(struct bch_fs *c, struct bch_ioctl_disk arg)
209 {
210         char *path;
211         int ret;
212
213         if (arg.flags || arg.pad)
214                 return -EINVAL;
215
216         path = strndup_user((const char __user *)(unsigned long) arg.dev, PATH_MAX);
217         if (!path)
218                 return -ENOMEM;
219
220         ret = bch2_dev_online(c, path);
221         kfree(path);
222         return ret;
223 }
224
225 static long bch2_ioctl_disk_offline(struct bch_fs *c, struct bch_ioctl_disk arg)
226 {
227         struct bch_dev *ca;
228         int ret;
229
230         if ((arg.flags & ~(BCH_FORCE_IF_DATA_LOST|
231                            BCH_FORCE_IF_METADATA_LOST|
232                            BCH_FORCE_IF_DEGRADED|
233                            BCH_BY_INDEX)) ||
234             arg.pad)
235                 return -EINVAL;
236
237         ca = bch2_device_lookup(c, arg.dev, arg.flags);
238         if (IS_ERR(ca))
239                 return PTR_ERR(ca);
240
241         ret = bch2_dev_offline(c, ca, arg.flags);
242         percpu_ref_put(&ca->ref);
243         return ret;
244 }
245
246 static long bch2_ioctl_disk_set_state(struct bch_fs *c,
247                         struct bch_ioctl_disk_set_state arg)
248 {
249         struct bch_dev *ca;
250         int ret;
251
252         if ((arg.flags & ~(BCH_FORCE_IF_DATA_LOST|
253                            BCH_FORCE_IF_METADATA_LOST|
254                            BCH_FORCE_IF_DEGRADED|
255                            BCH_BY_INDEX)) ||
256             arg.pad[0] || arg.pad[1] || arg.pad[2])
257                 return -EINVAL;
258
259         ca = bch2_device_lookup(c, arg.dev, arg.flags);
260         if (IS_ERR(ca))
261                 return PTR_ERR(ca);
262
263         ret = bch2_dev_set_state(c, ca, arg.new_state, arg.flags);
264
265         percpu_ref_put(&ca->ref);
266         return ret;
267 }
268
269 struct bch_data_ctx {
270         struct bch_fs                   *c;
271         struct bch_ioctl_data           arg;
272         struct bch_move_stats           stats;
273
274         int                             ret;
275
276         struct task_struct              *thread;
277 };
278
279 static int bch2_data_thread(void *arg)
280 {
281         struct bch_data_ctx *ctx = arg;
282
283         ctx->ret = bch2_data_job(ctx->c, &ctx->stats, ctx->arg);
284
285         ctx->stats.data_type = U8_MAX;
286         return 0;
287 }
288
289 static int bch2_data_job_release(struct inode *inode, struct file *file)
290 {
291         struct bch_data_ctx *ctx = file->private_data;
292
293         kthread_stop(ctx->thread);
294         put_task_struct(ctx->thread);
295         kfree(ctx);
296         return 0;
297 }
298
299 static ssize_t bch2_data_job_read(struct file *file, char __user *buf,
300                                   size_t len, loff_t *ppos)
301 {
302         struct bch_data_ctx *ctx = file->private_data;
303         struct bch_fs *c = ctx->c;
304         struct bch_ioctl_data_event e = {
305                 .type                   = BCH_DATA_EVENT_PROGRESS,
306                 .p.data_type            = ctx->stats.data_type,
307                 .p.btree_id             = ctx->stats.btree_id,
308                 .p.pos                  = ctx->stats.pos,
309                 .p.sectors_done         = atomic64_read(&ctx->stats.sectors_seen),
310                 .p.sectors_total        = bch2_fs_usage_read_short(c).used,
311         };
312
313         if (len < sizeof(e))
314                 return -EINVAL;
315
316         return copy_to_user(buf, &e, sizeof(e)) ?: sizeof(e);
317 }
318
319 static const struct file_operations bcachefs_data_ops = {
320         .release        = bch2_data_job_release,
321         .read           = bch2_data_job_read,
322         .llseek         = no_llseek,
323 };
324
325 static long bch2_ioctl_data(struct bch_fs *c,
326                             struct bch_ioctl_data arg)
327 {
328         struct bch_data_ctx *ctx = NULL;
329         struct file *file = NULL;
330         unsigned flags = O_RDONLY|O_CLOEXEC|O_NONBLOCK;
331         int ret, fd = -1;
332
333         if (arg.op >= BCH_DATA_OP_NR || arg.flags)
334                 return -EINVAL;
335
336         ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
337         if (!ctx)
338                 return -ENOMEM;
339
340         ctx->c = c;
341         ctx->arg = arg;
342
343         ctx->thread = kthread_create(bch2_data_thread, ctx, "[bcachefs]");
344         if (IS_ERR(ctx->thread)) {
345                 ret = PTR_ERR(ctx->thread);
346                 goto err;
347         }
348
349         ret = get_unused_fd_flags(flags);
350         if (ret < 0)
351                 goto err;
352         fd = ret;
353
354         file = anon_inode_getfile("[bcachefs]", &bcachefs_data_ops, ctx, flags);
355         if (IS_ERR(file)) {
356                 ret = PTR_ERR(file);
357                 goto err;
358         }
359
360         fd_install(fd, file);
361
362         get_task_struct(ctx->thread);
363         wake_up_process(ctx->thread);
364
365         return fd;
366 err:
367         if (fd >= 0)
368                 put_unused_fd(fd);
369         if (!IS_ERR_OR_NULL(ctx->thread))
370                 kthread_stop(ctx->thread);
371         kfree(ctx);
372         return ret;
373 }
374
375 static long bch2_ioctl_fs_usage(struct bch_fs *c,
376                                 struct bch_ioctl_fs_usage __user *user_arg)
377 {
378         struct bch_ioctl_fs_usage *arg = NULL;
379         struct bch_replicas_usage *dst_e, *dst_end;
380         struct bch_fs_usage *src;
381         u32 replica_entries_bytes;
382         unsigned i;
383         int ret = 0;
384
385         if (!test_bit(BCH_FS_STARTED, &c->flags))
386                 return -EINVAL;
387
388         if (get_user(replica_entries_bytes, &user_arg->replica_entries_bytes))
389                 return -EFAULT;
390
391         arg = kzalloc(sizeof(*arg) + replica_entries_bytes, GFP_KERNEL);
392         if (!arg)
393                 return -ENOMEM;
394
395         src = bch2_fs_usage_read(c);
396         if (!src) {
397                 ret = -ENOMEM;
398                 goto err;
399         }
400
401         arg->capacity           = c->capacity;
402         arg->used               = bch2_fs_sectors_used(c, src);
403         arg->online_reserved    = src->online_reserved;
404
405         for (i = 0; i < BCH_REPLICAS_MAX; i++)
406                 arg->persistent_reserved[i] = src->persistent_reserved[i];
407
408         dst_e   = arg->replicas;
409         dst_end = (void *) arg->replicas + replica_entries_bytes;
410
411         for (i = 0; i < c->replicas.nr; i++) {
412                 struct bch_replicas_entry *src_e =
413                         cpu_replicas_entry(&c->replicas, i);
414
415                 if (replicas_usage_next(dst_e) > dst_end) {
416                         ret = -ERANGE;
417                         break;
418                 }
419
420                 dst_e->sectors          = src->replicas[i];
421                 dst_e->r                = *src_e;
422
423                 /* recheck after setting nr_devs: */
424                 if (replicas_usage_next(dst_e) > dst_end) {
425                         ret = -ERANGE;
426                         break;
427                 }
428
429                 memcpy(dst_e->r.devs, src_e->devs, src_e->nr_devs);
430
431                 dst_e = replicas_usage_next(dst_e);
432         }
433
434         arg->replica_entries_bytes = (void *) dst_e - (void *) arg->replicas;
435
436         percpu_up_read(&c->mark_lock);
437         kfree(src);
438
439         if (!ret)
440                 ret = copy_to_user(user_arg, arg,
441                         sizeof(*arg) + arg->replica_entries_bytes);
442 err:
443         kfree(arg);
444         return ret;
445 }
446
447 static long bch2_ioctl_dev_usage(struct bch_fs *c,
448                                  struct bch_ioctl_dev_usage __user *user_arg)
449 {
450         struct bch_ioctl_dev_usage arg;
451         struct bch_dev_usage src;
452         struct bch_dev *ca;
453         unsigned i;
454
455         if (!test_bit(BCH_FS_STARTED, &c->flags))
456                 return -EINVAL;
457
458         if (copy_from_user(&arg, user_arg, sizeof(arg)))
459                 return -EFAULT;
460
461         if ((arg.flags & ~BCH_BY_INDEX) ||
462             arg.pad[0] ||
463             arg.pad[1] ||
464             arg.pad[2])
465                 return -EINVAL;
466
467         ca = bch2_device_lookup(c, arg.dev, arg.flags);
468         if (IS_ERR(ca))
469                 return PTR_ERR(ca);
470
471         src = bch2_dev_usage_read(c, ca);
472
473         arg.state       = ca->mi.state;
474         arg.bucket_size = ca->mi.bucket_size;
475         arg.nr_buckets  = ca->mi.nbuckets - ca->mi.first_bucket;
476
477         for (i = 0; i < BCH_DATA_NR; i++) {
478                 arg.buckets[i] = src.buckets[i];
479                 arg.sectors[i] = src.sectors[i];
480         }
481
482         percpu_ref_put(&ca->ref);
483
484         return copy_to_user(user_arg, &arg, sizeof(arg));
485 }
486
487 static long bch2_ioctl_read_super(struct bch_fs *c,
488                                   struct bch_ioctl_read_super arg)
489 {
490         struct bch_dev *ca = NULL;
491         struct bch_sb *sb;
492         int ret = 0;
493
494         if ((arg.flags & ~(BCH_BY_INDEX|BCH_READ_DEV)) ||
495             arg.pad)
496                 return -EINVAL;
497
498         mutex_lock(&c->sb_lock);
499
500         if (arg.flags & BCH_READ_DEV) {
501                 ca = bch2_device_lookup(c, arg.dev, arg.flags);
502
503                 if (IS_ERR(ca)) {
504                         ret = PTR_ERR(ca);
505                         goto err;
506                 }
507
508                 sb = ca->disk_sb.sb;
509         } else {
510                 sb = c->disk_sb.sb;
511         }
512
513         if (vstruct_bytes(sb) > arg.size) {
514                 ret = -ERANGE;
515                 goto err;
516         }
517
518         ret = copy_to_user((void __user *)(unsigned long)arg.sb,
519                            sb, vstruct_bytes(sb));
520 err:
521         if (ca)
522                 percpu_ref_put(&ca->ref);
523         mutex_unlock(&c->sb_lock);
524         return ret;
525 }
526
527 static long bch2_ioctl_disk_get_idx(struct bch_fs *c,
528                                     struct bch_ioctl_disk_get_idx arg)
529 {
530         dev_t dev = huge_decode_dev(arg.dev);
531         struct bch_dev *ca;
532         unsigned i;
533
534         for_each_online_member(ca, c, i)
535                 if (ca->disk_sb.bdev->bd_dev == dev) {
536                         percpu_ref_put(&ca->io_ref);
537                         return i;
538                 }
539
540         return -ENOENT;
541 }
542
543 static long bch2_ioctl_disk_resize(struct bch_fs *c,
544                                    struct bch_ioctl_disk_resize arg)
545 {
546         struct bch_dev *ca;
547         int ret;
548
549         if ((arg.flags & ~BCH_BY_INDEX) ||
550             arg.pad)
551                 return -EINVAL;
552
553         ca = bch2_device_lookup(c, arg.dev, arg.flags);
554         if (IS_ERR(ca))
555                 return PTR_ERR(ca);
556
557         ret = bch2_dev_resize(c, ca, arg.nbuckets);
558
559         percpu_ref_put(&ca->ref);
560         return ret;
561 }
562
563 #define BCH_IOCTL(_name, _argtype)                                      \
564 do {                                                                    \
565         _argtype i;                                                     \
566                                                                         \
567         if (copy_from_user(&i, arg, sizeof(i)))                         \
568                 return -EFAULT;                                         \
569         return bch2_ioctl_##_name(c, i);                                \
570 } while (0)
571
572 long bch2_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg)
573 {
574         /* ioctls that don't require admin cap: */
575         switch (cmd) {
576         case BCH_IOCTL_QUERY_UUID:
577                 return bch2_ioctl_query_uuid(c, arg);
578         case BCH_IOCTL_FS_USAGE:
579                 return bch2_ioctl_fs_usage(c, arg);
580         case BCH_IOCTL_DEV_USAGE:
581                 return bch2_ioctl_dev_usage(c, arg);
582         }
583
584         if (!capable(CAP_SYS_ADMIN))
585                 return -EPERM;
586
587         switch (cmd) {
588 #if 0
589         case BCH_IOCTL_START:
590                 BCH_IOCTL(start, struct bch_ioctl_start);
591         case BCH_IOCTL_STOP:
592                 return bch2_ioctl_stop(c);
593 #endif
594         case BCH_IOCTL_READ_SUPER:
595                 BCH_IOCTL(read_super, struct bch_ioctl_read_super);
596         case BCH_IOCTL_DISK_GET_IDX:
597                 BCH_IOCTL(disk_get_idx, struct bch_ioctl_disk_get_idx);
598         }
599
600         if (!test_bit(BCH_FS_STARTED, &c->flags))
601                 return -EINVAL;
602
603         /* ioctls that do require admin cap: */
604         switch (cmd) {
605         case BCH_IOCTL_DISK_ADD:
606                 BCH_IOCTL(disk_add, struct bch_ioctl_disk);
607         case BCH_IOCTL_DISK_REMOVE:
608                 BCH_IOCTL(disk_remove, struct bch_ioctl_disk);
609         case BCH_IOCTL_DISK_ONLINE:
610                 BCH_IOCTL(disk_online, struct bch_ioctl_disk);
611         case BCH_IOCTL_DISK_OFFLINE:
612                 BCH_IOCTL(disk_offline, struct bch_ioctl_disk);
613         case BCH_IOCTL_DISK_SET_STATE:
614                 BCH_IOCTL(disk_set_state, struct bch_ioctl_disk_set_state);
615         case BCH_IOCTL_DATA:
616                 BCH_IOCTL(data, struct bch_ioctl_data);
617         case BCH_IOCTL_DISK_RESIZE:
618                 BCH_IOCTL(disk_resize, struct bch_ioctl_disk_resize);
619
620         default:
621                 return -ENOTTY;
622         }
623 }
624
625 static DEFINE_IDR(bch_chardev_minor);
626
627 static long bch2_chardev_ioctl(struct file *filp, unsigned cmd, unsigned long v)
628 {
629         unsigned minor = iminor(file_inode(filp));
630         struct bch_fs *c = minor < U8_MAX ? idr_find(&bch_chardev_minor, minor) : NULL;
631         void __user *arg = (void __user *) v;
632
633         return c
634                 ? bch2_fs_ioctl(c, cmd, arg)
635                 : bch2_global_ioctl(cmd, arg);
636 }
637
638 static const struct file_operations bch_chardev_fops = {
639         .owner          = THIS_MODULE,
640         .unlocked_ioctl = bch2_chardev_ioctl,
641         .open           = nonseekable_open,
642 };
643
644 static int bch_chardev_major;
645 static struct class *bch_chardev_class;
646 static struct device *bch_chardev;
647
648 void bch2_fs_chardev_exit(struct bch_fs *c)
649 {
650         if (!IS_ERR_OR_NULL(c->chardev))
651                 device_unregister(c->chardev);
652         if (c->minor >= 0)
653                 idr_remove(&bch_chardev_minor, c->minor);
654 }
655
656 int bch2_fs_chardev_init(struct bch_fs *c)
657 {
658         c->minor = idr_alloc(&bch_chardev_minor, c, 0, 0, GFP_KERNEL);
659         if (c->minor < 0)
660                 return c->minor;
661
662         c->chardev = device_create(bch_chardev_class, NULL,
663                                    MKDEV(bch_chardev_major, c->minor), c,
664                                    "bcachefs%u-ctl", c->minor);
665         if (IS_ERR(c->chardev))
666                 return PTR_ERR(c->chardev);
667
668         return 0;
669 }
670
671 void bch2_chardev_exit(void)
672 {
673         if (!IS_ERR_OR_NULL(bch_chardev_class))
674                 device_destroy(bch_chardev_class,
675                                MKDEV(bch_chardev_major, U8_MAX));
676         if (!IS_ERR_OR_NULL(bch_chardev_class))
677                 class_destroy(bch_chardev_class);
678         if (bch_chardev_major > 0)
679                 unregister_chrdev(bch_chardev_major, "bcachefs");
680 }
681
682 int __init bch2_chardev_init(void)
683 {
684         bch_chardev_major = register_chrdev(0, "bcachefs-ctl", &bch_chardev_fops);
685         if (bch_chardev_major < 0)
686                 return bch_chardev_major;
687
688         bch_chardev_class = class_create(THIS_MODULE, "bcachefs");
689         if (IS_ERR(bch_chardev_class))
690                 return PTR_ERR(bch_chardev_class);
691
692         bch_chardev = device_create(bch_chardev_class, NULL,
693                                     MKDEV(bch_chardev_major, U8_MAX),
694                                     NULL, "bcachefs-ctl");
695         if (IS_ERR(bch_chardev))
696                 return PTR_ERR(bch_chardev);
697
698         return 0;
699 }
700
701 #endif /* NO_BCACHEFS_CHARDEV */