X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libbcachefs%2Fchardev.c;h=aa26588ed5edf0e193c10cc7e3d6a0d25f49a8e9;hb=b2a9318aa86edfe267805890c5ff1511caa59e70;hp=ac1ec5f62cea06c4b4bef44ef108af4d4ef03179;hpb=a10a41fa2b1a917b0f3b34d20175867f968b2d12;p=bcachefs-tools-debian diff --git a/libbcachefs/chardev.c b/libbcachefs/chardev.c index ac1ec5f..aa26588 100644 --- a/libbcachefs/chardev.c +++ b/libbcachefs/chardev.c @@ -1,10 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 #ifndef NO_BCACHEFS_CHARDEV #include "bcachefs.h" #include "bcachefs_ioctl.h" #include "buckets.h" #include "chardev.h" +#include "journal.h" #include "move.h" +#include "replicas.h" #include "super.h" #include "super-io.h" @@ -154,14 +157,20 @@ static long bch2_ioctl_query_uuid(struct bch_fs *c, #if 0 static long bch2_ioctl_start(struct bch_fs *c, struct bch_ioctl_start arg) { + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (arg.flags || arg.pad) return -EINVAL; - return bch2_fs_start(c) ? -EIO : 0; + return bch2_fs_start(c); } static long bch2_ioctl_stop(struct bch_fs *c) { + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + bch2_fs_stop(c); return 0; } @@ -172,6 +181,9 @@ static long bch2_ioctl_disk_add(struct bch_fs *c, struct bch_ioctl_disk arg) char *path; int ret; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (arg.flags || arg.pad) return -EINVAL; @@ -189,6 +201,9 @@ static long bch2_ioctl_disk_remove(struct bch_fs *c, struct bch_ioctl_disk arg) { struct bch_dev *ca; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if ((arg.flags & ~(BCH_FORCE_IF_DATA_LOST| BCH_FORCE_IF_METADATA_LOST| BCH_FORCE_IF_DEGRADED| @@ -208,6 +223,9 @@ static long bch2_ioctl_disk_online(struct bch_fs *c, struct bch_ioctl_disk arg) char *path; int ret; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (arg.flags || arg.pad) return -EINVAL; @@ -225,6 +243,9 @@ static long bch2_ioctl_disk_offline(struct bch_fs *c, struct bch_ioctl_disk arg) struct bch_dev *ca; int ret; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if ((arg.flags & ~(BCH_FORCE_IF_DATA_LOST| BCH_FORCE_IF_METADATA_LOST| BCH_FORCE_IF_DEGRADED| @@ -247,11 +268,15 @@ static long bch2_ioctl_disk_set_state(struct bch_fs *c, struct bch_dev *ca; int ret; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if ((arg.flags & ~(BCH_FORCE_IF_DATA_LOST| BCH_FORCE_IF_METADATA_LOST| BCH_FORCE_IF_DEGRADED| BCH_BY_INDEX)) || - arg.pad[0] || arg.pad[1] || arg.pad[2]) + arg.pad[0] || arg.pad[1] || arg.pad[2] || + arg.new_state >= BCH_MEMBER_STATE_NR) return -EINVAL; ca = bch2_device_lookup(c, arg.dev, arg.flags); @@ -302,8 +327,8 @@ static ssize_t bch2_data_job_read(struct file *file, char __user *buf, struct bch_ioctl_data_event e = { .type = BCH_DATA_EVENT_PROGRESS, .p.data_type = ctx->stats.data_type, - .p.btree_id = ctx->stats.iter.btree_id, - .p.pos = ctx->stats.iter.pos, + .p.btree_id = ctx->stats.btree_id, + .p.pos = ctx->stats.pos, .p.sectors_done = atomic64_read(&ctx->stats.sectors_seen), .p.sectors_total = bch2_fs_usage_read_short(c).used, }; @@ -328,6 +353,9 @@ static long bch2_ioctl_data(struct bch_fs *c, unsigned flags = O_RDONLY|O_CLOEXEC|O_NONBLOCK; int ret, fd = -1; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (arg.op >= BCH_DATA_OP_NR || arg.flags) return -EINVAL; @@ -338,7 +366,8 @@ static long bch2_ioctl_data(struct bch_fs *c, ctx->c = c; ctx->arg = arg; - ctx->thread = kthread_create(bch2_data_thread, ctx, "[bcachefs]"); + ctx->thread = kthread_create(bch2_data_thread, ctx, + "bch-data/%s", c->name); if (IS_ERR(ctx->thread)) { ret = PTR_ERR(ctx->thread); goto err; @@ -370,79 +399,120 @@ err: return ret; } -static long bch2_ioctl_usage(struct bch_fs *c, - struct bch_ioctl_usage __user *user_arg) +static long bch2_ioctl_fs_usage(struct bch_fs *c, + struct bch_ioctl_fs_usage __user *user_arg) { - struct bch_ioctl_usage arg; - struct bch_dev *ca; - unsigned i, j; - int ret; + struct bch_ioctl_fs_usage *arg = NULL; + struct bch_replicas_usage *dst_e, *dst_end; + struct bch_fs_usage_online *src; + u32 replica_entries_bytes; + unsigned i; + int ret = 0; if (!test_bit(BCH_FS_STARTED, &c->flags)) return -EINVAL; - if (copy_from_user(&arg, user_arg, sizeof(arg))) + if (get_user(replica_entries_bytes, &user_arg->replica_entries_bytes)) return -EFAULT; - for (i = 0; i < arg.nr_devices; i++) { - struct bch_ioctl_dev_usage dst = { .alive = 0 }; + arg = kzalloc(sizeof(*arg) + replica_entries_bytes, GFP_KERNEL); + if (!arg) + return -ENOMEM; - ret = copy_to_user(&user_arg->devs[i], &dst, sizeof(dst)); - if (ret) - return ret; + src = bch2_fs_usage_read(c); + if (!src) { + ret = -ENOMEM; + goto err; } - { - struct bch_fs_usage src = bch2_fs_usage_read(c); - struct bch_ioctl_fs_usage dst = { - .capacity = c->capacity, - .used = bch2_fs_sectors_used(c, src), - .online_reserved = src.s.online_reserved, - }; + arg->capacity = c->capacity; + arg->used = bch2_fs_sectors_used(c, src); + arg->online_reserved = src->online_reserved; - for (i = 0; i < BCH_REPLICAS_MAX; i++) { - dst.persistent_reserved[i] = - src.replicas[i].persistent_reserved; + for (i = 0; i < BCH_REPLICAS_MAX; i++) + arg->persistent_reserved[i] = src->u.persistent_reserved[i]; - for (j = 0; j < BCH_DATA_NR; j++) - dst.sectors[j][i] = src.replicas[i].data[j]; - } + dst_e = arg->replicas; + dst_end = (void *) arg->replicas + replica_entries_bytes; - ret = copy_to_user(&user_arg->fs, &dst, sizeof(dst)); - if (ret) - return ret; - } + for (i = 0; i < c->replicas.nr; i++) { + struct bch_replicas_entry *src_e = + cpu_replicas_entry(&c->replicas, i); - for_each_member_device(ca, c, i) { - struct bch_dev_usage src = bch2_dev_usage_read(c, ca); - struct bch_ioctl_dev_usage dst = { - .alive = 1, - .state = ca->mi.state, - .bucket_size = ca->mi.bucket_size, - .nr_buckets = ca->mi.nbuckets - ca->mi.first_bucket, - }; - - if (ca->dev_idx >= arg.nr_devices) { - percpu_ref_put(&ca->ref); - return -ERANGE; + /* check that we have enough space for one replicas entry */ + if (dst_e + 1 > dst_end) { + ret = -ERANGE; + break; } - if (percpu_ref_tryget(&ca->io_ref)) { - dst.dev = huge_encode_dev(ca->disk_sb.bdev->bd_dev); - percpu_ref_put(&ca->io_ref); - } + dst_e->sectors = src->u.replicas[i]; + dst_e->r = *src_e; - for (j = 0; j < BCH_DATA_NR; j++) { - dst.buckets[j] = src.buckets[j]; - dst.sectors[j] = src.sectors[j]; + /* recheck after setting nr_devs: */ + if (replicas_usage_next(dst_e) > dst_end) { + ret = -ERANGE; + break; } - ret = copy_to_user(&user_arg->devs[i], &dst, sizeof(dst)); - if (ret) - return ret; + memcpy(dst_e->r.devs, src_e->devs, src_e->nr_devs); + + dst_e = replicas_usage_next(dst_e); } - return 0; + arg->replica_entries_bytes = (void *) dst_e - (void *) arg->replicas; + + percpu_up_read(&c->mark_lock); + kfree(src); + + if (!ret) + ret = copy_to_user(user_arg, arg, + sizeof(*arg) + arg->replica_entries_bytes); +err: + kfree(arg); + return ret; +} + +static long bch2_ioctl_dev_usage(struct bch_fs *c, + struct bch_ioctl_dev_usage __user *user_arg) +{ + struct bch_ioctl_dev_usage arg; + struct bch_dev_usage src; + struct bch_dev *ca; + unsigned i; + + if (!test_bit(BCH_FS_STARTED, &c->flags)) + return -EINVAL; + + if (copy_from_user(&arg, user_arg, sizeof(arg))) + return -EFAULT; + + if ((arg.flags & ~BCH_BY_INDEX) || + arg.pad[0] || + arg.pad[1] || + arg.pad[2]) + return -EINVAL; + + ca = bch2_device_lookup(c, arg.dev, arg.flags); + if (IS_ERR(ca)) + return PTR_ERR(ca); + + src = bch2_dev_usage_read(ca); + + arg.state = ca->mi.state; + arg.bucket_size = ca->mi.bucket_size; + arg.nr_buckets = ca->mi.nbuckets - ca->mi.first_bucket; + arg.available_buckets = arg.nr_buckets - src.buckets_unavailable; + arg.ec_buckets = src.buckets_ec; + arg.ec_sectors = 0; + + for (i = 0; i < BCH_DATA_NR; i++) { + arg.buckets[i] = src.d[i].buckets; + arg.sectors[i] = src.d[i].sectors; + } + + percpu_ref_put(&ca->ref); + + return copy_to_user(user_arg, &arg, sizeof(arg)); } static long bch2_ioctl_read_super(struct bch_fs *c, @@ -452,6 +522,9 @@ static long bch2_ioctl_read_super(struct bch_fs *c, struct bch_sb *sb; int ret = 0; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if ((arg.flags & ~(BCH_BY_INDEX|BCH_READ_DEV)) || arg.pad) return -EINVAL; @@ -479,7 +552,7 @@ static long bch2_ioctl_read_super(struct bch_fs *c, ret = copy_to_user((void __user *)(unsigned long)arg.sb, sb, vstruct_bytes(sb)); err: - if (ca) + if (!IS_ERR_OR_NULL(ca)) percpu_ref_put(&ca->ref); mutex_unlock(&c->sb_lock); return ret; @@ -492,8 +565,14 @@ static long bch2_ioctl_disk_get_idx(struct bch_fs *c, struct bch_dev *ca; unsigned i; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (!dev) + return -EINVAL; + for_each_online_member(ca, c, i) - if (ca->disk_sb.bdev->bd_dev == dev) { + if (ca->dev == dev) { percpu_ref_put(&ca->io_ref); return i; } @@ -507,6 +586,9 @@ static long bch2_ioctl_disk_resize(struct bch_fs *c, struct bch_dev *ca; int ret; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if ((arg.flags & ~BCH_BY_INDEX) || arg.pad) return -EINVAL; @@ -521,6 +603,29 @@ static long bch2_ioctl_disk_resize(struct bch_fs *c, return ret; } +static long bch2_ioctl_disk_resize_journal(struct bch_fs *c, + struct bch_ioctl_disk_resize_journal arg) +{ + struct bch_dev *ca; + int ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if ((arg.flags & ~BCH_BY_INDEX) || + arg.pad) + return -EINVAL; + + ca = bch2_device_lookup(c, arg.dev, arg.flags); + if (IS_ERR(ca)) + return PTR_ERR(ca); + + ret = bch2_set_nr_journal_buckets(c, ca, arg.nbuckets); + + percpu_ref_put(&ca->ref); + return ret; +} + #define BCH_IOCTL(_name, _argtype) \ do { \ _argtype i; \ @@ -532,18 +637,13 @@ do { \ long bch2_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg) { - /* ioctls that don't require admin cap: */ switch (cmd) { case BCH_IOCTL_QUERY_UUID: return bch2_ioctl_query_uuid(c, arg); - case BCH_IOCTL_USAGE: - return bch2_ioctl_usage(c, arg); - } - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - switch (cmd) { + case BCH_IOCTL_FS_USAGE: + return bch2_ioctl_fs_usage(c, arg); + case BCH_IOCTL_DEV_USAGE: + return bch2_ioctl_dev_usage(c, arg); #if 0 case BCH_IOCTL_START: BCH_IOCTL(start, struct bch_ioctl_start); @@ -559,7 +659,6 @@ long bch2_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg) if (!test_bit(BCH_FS_STARTED, &c->flags)) return -EINVAL; - /* ioctls that do require admin cap: */ switch (cmd) { case BCH_IOCTL_DISK_ADD: BCH_IOCTL(disk_add, struct bch_ioctl_disk); @@ -575,6 +674,8 @@ long bch2_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg) BCH_IOCTL(data, struct bch_ioctl_data); case BCH_IOCTL_DISK_RESIZE: BCH_IOCTL(disk_resize, struct bch_ioctl_disk_resize); + case BCH_IOCTL_DISK_RESIZE_JOURNAL: + BCH_IOCTL(disk_resize_journal, struct bch_ioctl_disk_resize_journal); default: return -ENOTTY;