summaryrefslogtreecommitdiff
path: root/libbcachefs/chardev.c
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2017-04-05 17:31:05 -0800
committerKent Overstreet <kent.overstreet@gmail.com>2017-04-05 18:20:50 -0800
commit2615d73a7428a08abdd058c875067847bf93d55c (patch)
treee65a58885a8c6c535b6576c8c44cbac6a38829d1 /libbcachefs/chardev.c
parentbc85a9411404d979feff56680509ef28e93ab2a9 (diff)
Allow disk commands to specify member by index
Diffstat (limited to 'libbcachefs/chardev.c')
-rw-r--r--libbcachefs/chardev.c185
1 files changed, 92 insertions, 93 deletions
diff --git a/libbcachefs/chardev.c b/libbcachefs/chardev.c
index 24b92a29..d3cfb00b 100644
--- a/libbcachefs/chardev.c
+++ b/libbcachefs/chardev.c
@@ -12,6 +12,51 @@
#include <linux/uaccess.h>
#include <linux/slab.h>
+/* returns with ref on ca->ref */
+static struct bch_dev *bch2_device_lookup(struct bch_fs *c, u64 dev,
+ unsigned flags)
+{
+ struct bch_dev *ca;
+
+ if (flags & BCH_BY_INDEX) {
+ if (dev >= c->sb.nr_devices)
+ return ERR_PTR(-EINVAL);
+
+ rcu_read_lock();
+ ca = c->devs[dev];
+ if (ca)
+ percpu_ref_get(&ca->ref);
+ rcu_read_unlock();
+
+ if (!ca)
+ return ERR_PTR(-EINVAL);
+ } else {
+ struct block_device *bdev;
+ char *path;
+ unsigned i;
+
+ path = strndup_user((const char __user *)
+ (unsigned long) dev, PATH_MAX);
+ if (!path)
+ return ERR_PTR(-ENOMEM);
+
+ bdev = lookup_bdev(strim(path));
+ kfree(path);
+ if (IS_ERR(bdev))
+ return ERR_CAST(bdev);
+
+ for_each_member_device(ca, c, i)
+ if (ca->disk_sb.bdev == bdev)
+ goto found;
+
+ ca = NULL;
+found:
+ bdput(bdev);
+ }
+
+ return ca;
+}
+
static long bch2_ioctl_assemble(struct bch_ioctl_assemble __user *user_arg)
{
struct bch_ioctl_assemble arg;
@@ -110,13 +155,8 @@ static long bch2_ioctl_query_uuid(struct bch_fs *c,
sizeof(c->sb.user_uuid));
}
-static long bch2_ioctl_start(struct bch_fs *c, struct bch_ioctl_start __user *user_arg)
+static long bch2_ioctl_start(struct bch_fs *c, struct bch_ioctl_start arg)
{
- struct bch_ioctl_start arg;
-
- if (copy_from_user(&arg, user_arg, sizeof(arg)))
- return -EFAULT;
-
if (arg.flags || arg.pad)
return -EINVAL;
@@ -129,60 +169,11 @@ static long bch2_ioctl_stop(struct bch_fs *c)
return 0;
}
-/* returns with ref on ca->ref */
-static struct bch_dev *bch2_device_lookup(struct bch_fs *c,
- const char __user *dev)
+static long bch2_ioctl_disk_add(struct bch_fs *c, struct bch_ioctl_disk arg)
{
- struct block_device *bdev;
- struct bch_dev *ca;
- char *path;
- unsigned i;
-
- path = strndup_user(dev, PATH_MAX);
- if (!path)
- return ERR_PTR(-ENOMEM);
-
- bdev = lookup_bdev(strim(path));
- kfree(path);
- if (IS_ERR(bdev))
- return ERR_CAST(bdev);
-
- for_each_member_device(ca, c, i)
- if (ca->disk_sb.bdev == bdev)
- goto found;
-
- ca = NULL;
-found:
- bdput(bdev);
- return ca;
-}
-
-#if 0
-static struct bch_member *bch2_uuid_lookup(struct bch_fs *c, uuid_le uuid)
-{
- struct bch_sb_field_members *mi = bch2_sb_get_members(c->disk_sb);
- unsigned i;
-
- lockdep_assert_held(&c->sb_lock);
-
- for (i = 0; i < c->disk_sb->nr_devices; i++)
- if (!memcmp(&mi->members[i].uuid, &uuid, sizeof(uuid)))
- return &mi->members[i];
-
- return NULL;
-}
-#endif
-
-static long bch2_ioctl_disk_add(struct bch_fs *c,
- struct bch_ioctl_disk __user *user_arg)
-{
- struct bch_ioctl_disk arg;
char *path;
int ret;
- if (copy_from_user(&arg, user_arg, sizeof(arg)))
- return -EFAULT;
-
if (arg.flags || arg.pad)
return -EINVAL;
@@ -196,32 +187,29 @@ static long bch2_ioctl_disk_add(struct bch_fs *c,
return ret;
}
-static long bch2_ioctl_disk_remove(struct bch_fs *c,
- struct bch_ioctl_disk __user *user_arg)
+static long bch2_ioctl_disk_remove(struct bch_fs *c, struct bch_ioctl_disk arg)
{
- struct bch_ioctl_disk arg;
struct bch_dev *ca;
- if (copy_from_user(&arg, user_arg, sizeof(arg)))
- return -EFAULT;
+ if ((arg.flags & ~(BCH_FORCE_IF_DATA_LOST|
+ BCH_FORCE_IF_METADATA_LOST|
+ BCH_FORCE_IF_DEGRADED|
+ BCH_BY_INDEX)) ||
+ arg.pad)
+ return -EINVAL;
- ca = bch2_device_lookup(c, (const char __user *)(unsigned long) arg.dev);
+ ca = bch2_device_lookup(c, arg.dev, arg.flags);
if (IS_ERR(ca))
return PTR_ERR(ca);
return bch2_dev_remove(c, ca, arg.flags);
}
-static long bch2_ioctl_disk_online(struct bch_fs *c,
- struct bch_ioctl_disk __user *user_arg)
+static long bch2_ioctl_disk_online(struct bch_fs *c, struct bch_ioctl_disk arg)
{
- struct bch_ioctl_disk arg;
char *path;
int ret;
- if (copy_from_user(&arg, user_arg, sizeof(arg)))
- return -EFAULT;
-
if (arg.flags || arg.pad)
return -EINVAL;
@@ -234,20 +222,19 @@ static long bch2_ioctl_disk_online(struct bch_fs *c,
return ret;
}
-static long bch2_ioctl_disk_offline(struct bch_fs *c,
- struct bch_ioctl_disk __user *user_arg)
+static long bch2_ioctl_disk_offline(struct bch_fs *c, struct bch_ioctl_disk arg)
{
- struct bch_ioctl_disk arg;
struct bch_dev *ca;
int ret;
- if (copy_from_user(&arg, user_arg, sizeof(arg)))
- return -EFAULT;
-
- if (arg.pad)
+ if ((arg.flags & ~(BCH_FORCE_IF_DATA_LOST|
+ BCH_FORCE_IF_METADATA_LOST|
+ BCH_FORCE_IF_DEGRADED|
+ BCH_BY_INDEX)) ||
+ arg.pad)
return -EINVAL;
- ca = bch2_device_lookup(c, (const char __user *)(unsigned long) arg.dev);
+ ca = bch2_device_lookup(c, arg.dev, arg.flags);
if (IS_ERR(ca))
return PTR_ERR(ca);
@@ -257,16 +244,19 @@ static long bch2_ioctl_disk_offline(struct bch_fs *c,
}
static long bch2_ioctl_disk_set_state(struct bch_fs *c,
- struct bch_ioctl_disk_set_state __user *user_arg)
+ struct bch_ioctl_disk_set_state arg)
{
- struct bch_ioctl_disk_set_state arg;
struct bch_dev *ca;
int ret;
- if (copy_from_user(&arg, user_arg, sizeof(arg)))
- return -EFAULT;
+ 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])
+ return -EINVAL;
- ca = bch2_device_lookup(c, (const char __user *)(unsigned long) arg.dev);
+ ca = bch2_device_lookup(c, arg.dev, arg.flags);
if (IS_ERR(ca))
return PTR_ERR(ca);
@@ -277,16 +267,16 @@ static long bch2_ioctl_disk_set_state(struct bch_fs *c,
}
static long bch2_ioctl_disk_evacuate(struct bch_fs *c,
- struct bch_ioctl_disk __user *user_arg)
+ struct bch_ioctl_disk arg)
{
- struct bch_ioctl_disk arg;
struct bch_dev *ca;
int ret;
- if (copy_from_user(&arg, user_arg, sizeof(arg)))
- return -EFAULT;
+ if ((arg.flags & ~BCH_BY_INDEX) ||
+ arg.pad)
+ return -EINVAL;
- ca = bch2_device_lookup(c, (const char __user *)(unsigned long) arg.dev);
+ ca = bch2_device_lookup(c, arg.dev, arg.flags);
if (IS_ERR(ca))
return PTR_ERR(ca);
@@ -296,6 +286,15 @@ static long bch2_ioctl_disk_evacuate(struct bch_fs *c,
return ret;
}
+#define BCH_IOCTL(_name, _argtype) \
+do { \
+ _argtype i; \
+ \
+ if (copy_from_user(&i, arg, sizeof(i))) \
+ return -EFAULT; \
+ return bch2_ioctl_##_name(c, i); \
+} while (0)
+
long bch2_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg)
{
/* ioctls that don't require admin cap: */
@@ -310,22 +309,22 @@ long bch2_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg)
/* ioctls that do require admin cap: */
switch (cmd) {
case BCH_IOCTL_START:
- return bch2_ioctl_start(c, arg);
+ BCH_IOCTL(start, struct bch_ioctl_start);
case BCH_IOCTL_STOP:
return bch2_ioctl_stop(c);
case BCH_IOCTL_DISK_ADD:
- return bch2_ioctl_disk_add(c, arg);
+ BCH_IOCTL(disk_add, struct bch_ioctl_disk);
case BCH_IOCTL_DISK_REMOVE:
- return bch2_ioctl_disk_remove(c, arg);
+ BCH_IOCTL(disk_remove, struct bch_ioctl_disk);
case BCH_IOCTL_DISK_ONLINE:
- return bch2_ioctl_disk_online(c, arg);
+ BCH_IOCTL(disk_online, struct bch_ioctl_disk);
case BCH_IOCTL_DISK_OFFLINE:
- return bch2_ioctl_disk_offline(c, arg);
+ BCH_IOCTL(disk_offline, struct bch_ioctl_disk);
case BCH_IOCTL_DISK_SET_STATE:
- return bch2_ioctl_disk_set_state(c, arg);
+ BCH_IOCTL(disk_set_state, struct bch_ioctl_disk_set_state);
case BCH_IOCTL_DISK_EVACUATE:
- return bch2_ioctl_disk_evacuate(c, arg);
+ BCH_IOCTL(disk_evacuate, struct bch_ioctl_disk);
default:
return -ENOTTY;