Merge branch 'for-chris' of git://git.kernel.org/pub/scm/linux/kernel/git/arne/btrfs...
[sfrench/cifs-2.6.git] / fs / btrfs / ioctl.c
index ed8c055ab70f06bec725a49cf06d068d7ec0aaa9..e0a061db435350ada13f4a5262f9453c8063c0a5 100644 (file)
@@ -1809,6 +1809,75 @@ static long btrfs_ioctl_rm_dev(struct btrfs_root *root, void __user *arg)
        return ret;
 }
 
+static long btrfs_ioctl_fs_info(struct btrfs_root *root, void __user *arg)
+{
+       struct btrfs_ioctl_fs_info_args fi_args;
+       struct btrfs_device *device;
+       struct btrfs_device *next;
+       struct btrfs_fs_devices *fs_devices = root->fs_info->fs_devices;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       fi_args.num_devices = fs_devices->num_devices;
+       fi_args.max_id = 0;
+       memcpy(&fi_args.fsid, root->fs_info->fsid, sizeof(fi_args.fsid));
+
+       mutex_lock(&fs_devices->device_list_mutex);
+       list_for_each_entry_safe(device, next, &fs_devices->devices, dev_list) {
+               if (device->devid > fi_args.max_id)
+                       fi_args.max_id = device->devid;
+       }
+       mutex_unlock(&fs_devices->device_list_mutex);
+
+       if (copy_to_user(arg, &fi_args, sizeof(fi_args)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static long btrfs_ioctl_dev_info(struct btrfs_root *root, void __user *arg)
+{
+       struct btrfs_ioctl_dev_info_args *di_args;
+       struct btrfs_device *dev;
+       struct btrfs_fs_devices *fs_devices = root->fs_info->fs_devices;
+       int ret = 0;
+       char *s_uuid = NULL;
+       char empty_uuid[BTRFS_UUID_SIZE] = {0};
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       di_args = memdup_user(arg, sizeof(*di_args));
+       if (IS_ERR(di_args))
+               return PTR_ERR(di_args);
+
+       if (memcmp(empty_uuid, di_args->uuid, BTRFS_UUID_SIZE) != 0)
+               s_uuid = di_args->uuid;
+
+       mutex_lock(&fs_devices->device_list_mutex);
+       dev = btrfs_find_device(root, di_args->devid, s_uuid, NULL);
+       mutex_unlock(&fs_devices->device_list_mutex);
+
+       if (!dev) {
+               ret = -ENODEV;
+               goto out;
+       }
+
+       di_args->devid = dev->devid;
+       di_args->bytes_used = dev->bytes_used;
+       di_args->total_bytes = dev->total_bytes;
+       memcpy(di_args->uuid, dev->uuid, sizeof(di_args->uuid));
+       strncpy(di_args->path, dev->name, sizeof(di_args->path));
+
+out:
+       if (ret == 0 && copy_to_user(arg, di_args, sizeof(*di_args)))
+               ret = -EFAULT;
+
+       kfree(di_args);
+       return ret;
+}
+
 static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
                                       u64 off, u64 olen, u64 destoff)
 {
@@ -2471,6 +2540,58 @@ static noinline long btrfs_ioctl_wait_sync(struct file *file, void __user *argp)
        return btrfs_wait_for_commit(root, transid);
 }
 
+static long btrfs_ioctl_scrub(struct btrfs_root *root, void __user *arg)
+{
+       int ret;
+       struct btrfs_ioctl_scrub_args *sa;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       sa = memdup_user(arg, sizeof(*sa));
+       if (IS_ERR(sa))
+               return PTR_ERR(sa);
+
+       ret = btrfs_scrub_dev(root, sa->devid, sa->start, sa->end,
+                             &sa->progress, sa->flags & BTRFS_SCRUB_READONLY);
+
+       if (copy_to_user(arg, sa, sizeof(*sa)))
+               ret = -EFAULT;
+
+       kfree(sa);
+       return ret;
+}
+
+static long btrfs_ioctl_scrub_cancel(struct btrfs_root *root, void __user *arg)
+{
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       return btrfs_scrub_cancel(root);
+}
+
+static long btrfs_ioctl_scrub_progress(struct btrfs_root *root,
+                                      void __user *arg)
+{
+       struct btrfs_ioctl_scrub_args *sa;
+       int ret;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       sa = memdup_user(arg, sizeof(*sa));
+       if (IS_ERR(sa))
+               return PTR_ERR(sa);
+
+       ret = btrfs_scrub_progress(root, sa->devid, &sa->progress);
+
+       if (copy_to_user(arg, sa, sizeof(*sa)))
+               ret = -EFAULT;
+
+       kfree(sa);
+       return ret;
+}
+
 long btrfs_ioctl(struct file *file, unsigned int
                cmd, unsigned long arg)
 {
@@ -2510,6 +2631,10 @@ long btrfs_ioctl(struct file *file, unsigned int
                return btrfs_ioctl_add_dev(root, argp);
        case BTRFS_IOC_RM_DEV:
                return btrfs_ioctl_rm_dev(root, argp);
+       case BTRFS_IOC_FS_INFO:
+               return btrfs_ioctl_fs_info(root, argp);
+       case BTRFS_IOC_DEV_INFO:
+               return btrfs_ioctl_dev_info(root, argp);
        case BTRFS_IOC_BALANCE:
                return btrfs_balance(root->fs_info->dev_root);
        case BTRFS_IOC_CLONE:
@@ -2533,6 +2658,12 @@ long btrfs_ioctl(struct file *file, unsigned int
                return btrfs_ioctl_start_sync(file, argp);
        case BTRFS_IOC_WAIT_SYNC:
                return btrfs_ioctl_wait_sync(file, argp);
+       case BTRFS_IOC_SCRUB:
+               return btrfs_ioctl_scrub(root, argp);
+       case BTRFS_IOC_SCRUB_CANCEL:
+               return btrfs_ioctl_scrub_cancel(root, argp);
+       case BTRFS_IOC_SCRUB_PROGRESS:
+               return btrfs_ioctl_scrub_progress(root, argp);
        }
 
        return -ENOTTY;