Merge branch 'qgroup' of git://git.jan-o-sch.net/btrfs-unstable into for-linus
[linux.git] / fs / btrfs / ioctl.c
index a98f7d252829aa14ef6c530885d7dadc98f5bb12..e54b663fd3aab34972ee5a4d3ee21a54617df289 100644 (file)
@@ -336,7 +336,8 @@ static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg)
 static noinline int create_subvol(struct btrfs_root *root,
                                  struct dentry *dentry,
                                  char *name, int namelen,
-                                 u64 *async_transid)
+                                 u64 *async_transid,
+                                 struct btrfs_qgroup_inherit **inherit)
 {
        struct btrfs_trans_handle *trans;
        struct btrfs_key key;
@@ -368,6 +369,11 @@ static noinline int create_subvol(struct btrfs_root *root,
        if (IS_ERR(trans))
                return PTR_ERR(trans);
 
+       ret = btrfs_qgroup_inherit(trans, root->fs_info, 0, objectid,
+                                  inherit ? *inherit : NULL);
+       if (ret)
+               goto fail;
+
        leaf = btrfs_alloc_free_block(trans, root, root->leafsize,
                                      0, objectid, NULL, 0, 0, 0);
        if (IS_ERR(leaf)) {
@@ -484,7 +490,7 @@ fail:
 
 static int create_snapshot(struct btrfs_root *root, struct dentry *dentry,
                           char *name, int namelen, u64 *async_transid,
-                          bool readonly)
+                          bool readonly, struct btrfs_qgroup_inherit **inherit)
 {
        struct inode *inode;
        struct btrfs_pending_snapshot *pending_snapshot;
@@ -502,6 +508,10 @@ static int create_snapshot(struct btrfs_root *root, struct dentry *dentry,
        pending_snapshot->dentry = dentry;
        pending_snapshot->root = root;
        pending_snapshot->readonly = readonly;
+       if (inherit) {
+               pending_snapshot->inherit = *inherit;
+               *inherit = NULL;        /* take responsibility to free it */
+       }
 
        trans = btrfs_start_transaction(root->fs_info->extent_root, 5);
        if (IS_ERR(trans)) {
@@ -635,7 +645,8 @@ static inline int btrfs_may_create(struct inode *dir, struct dentry *child)
 static noinline int btrfs_mksubvol(struct path *parent,
                                   char *name, int namelen,
                                   struct btrfs_root *snap_src,
-                                  u64 *async_transid, bool readonly)
+                                  u64 *async_transid, bool readonly,
+                                  struct btrfs_qgroup_inherit **inherit)
 {
        struct inode *dir  = parent->dentry->d_inode;
        struct dentry *dentry;
@@ -652,13 +663,9 @@ static noinline int btrfs_mksubvol(struct path *parent,
        if (dentry->d_inode)
                goto out_dput;
 
-       error = mnt_want_write(parent->mnt);
-       if (error)
-               goto out_dput;
-
        error = btrfs_may_create(dir, dentry);
        if (error)
-               goto out_drop_write;
+               goto out_dput;
 
        down_read(&BTRFS_I(dir)->root->fs_info->subvol_sem);
 
@@ -666,18 +673,16 @@ static noinline int btrfs_mksubvol(struct path *parent,
                goto out_up_read;
 
        if (snap_src) {
-               error = create_snapshot(snap_src, dentry,
-                                       name, namelen, async_transid, readonly);
+               error = create_snapshot(snap_src, dentry, name, namelen,
+                                       async_transid, readonly, inherit);
        } else {
                error = create_subvol(BTRFS_I(dir)->root, dentry,
-                                     name, namelen, async_transid);
+                                     name, namelen, async_transid, inherit);
        }
        if (!error)
                fsnotify_mkdir(dir, dentry);
 out_up_read:
        up_read(&BTRFS_I(dir)->root->fs_info->subvol_sem);
-out_drop_write:
-       mnt_drop_write(parent->mnt);
 out_dput:
        dput(dentry);
 out_unlock:
@@ -832,7 +837,8 @@ static bool defrag_check_next_extent(struct inode *inode, struct extent_map *em)
 }
 
 static int should_defrag_range(struct inode *inode, u64 start, int thresh,
-                              u64 *last_len, u64 *skip, u64 *defrag_end)
+                              u64 *last_len, u64 *skip, u64 *defrag_end,
+                              int compress)
 {
        struct extent_map *em;
        int ret = 1;
@@ -863,7 +869,7 @@ static int should_defrag_range(struct inode *inode, u64 start, int thresh,
         * we hit a real extent, if it is big or the next extent is not a
         * real extent, don't bother defragging it
         */
-       if ((*last_len == 0 || *last_len >= thresh) &&
+       if (!compress && (*last_len == 0 || *last_len >= thresh) &&
            (em->len >= thresh || !next_mergeable))
                ret = 0;
 out:
@@ -1145,7 +1151,8 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,
 
                if (!should_defrag_range(inode, (u64)i << PAGE_CACHE_SHIFT,
                                         extent_thresh, &last_len, &skip,
-                                        &defrag_end)) {
+                                        &defrag_end, range->flags &
+                                        BTRFS_DEFRAG_RANGE_COMPRESS)) {
                        unsigned long next;
                        /*
                         * the should_defrag function tells us how much to skip
@@ -1306,6 +1313,14 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root,
                ret = -EINVAL;
                goto out_free;
        }
+       if (device->fs_devices && device->fs_devices->seeding) {
+               printk(KERN_INFO "btrfs: resizer unable to apply on "
+                      "seeding device %llu\n",
+                      (unsigned long long)devid);
+               ret = -EINVAL;
+               goto out_free;
+       }
+
        if (!strcmp(sizestr, "max"))
                new_size = device->bdev->bd_inode->i_size;
        else {
@@ -1371,41 +1386,39 @@ out:
 }
 
 static noinline int btrfs_ioctl_snap_create_transid(struct file *file,
-                                                   char *name,
-                                                   unsigned long fd,
-                                                   int subvol,
-                                                   u64 *transid,
-                                                   bool readonly)
+                               char *name, unsigned long fd, int subvol,
+                               u64 *transid, bool readonly,
+                               struct btrfs_qgroup_inherit **inherit)
 {
-       struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;
        struct file *src_file;
        int namelen;
        int ret = 0;
 
-       if (root->fs_info->sb->s_flags & MS_RDONLY)
-               return -EROFS;
+       ret = mnt_want_write_file(file);
+       if (ret)
+               goto out;
 
        namelen = strlen(name);
        if (strchr(name, '/')) {
                ret = -EINVAL;
-               goto out;
+               goto out_drop_write;
        }
 
        if (name[0] == '.' &&
           (namelen == 1 || (name[1] == '.' && namelen == 2))) {
                ret = -EEXIST;
-               goto out;
+               goto out_drop_write;
        }
 
        if (subvol) {
                ret = btrfs_mksubvol(&file->f_path, name, namelen,
-                                    NULL, transid, readonly);
+                                    NULL, transid, readonly, inherit);
        } else {
                struct inode *src_inode;
                src_file = fget(fd);
                if (!src_file) {
                        ret = -EINVAL;
-                       goto out;
+                       goto out_drop_write;
                }
 
                src_inode = src_file->f_path.dentry->d_inode;
@@ -1414,13 +1427,15 @@ static noinline int btrfs_ioctl_snap_create_transid(struct file *file,
                               "another FS\n");
                        ret = -EINVAL;
                        fput(src_file);
-                       goto out;
+                       goto out_drop_write;
                }
                ret = btrfs_mksubvol(&file->f_path, name, namelen,
                                     BTRFS_I(src_inode)->root,
-                                    transid, readonly);
+                                    transid, readonly, inherit);
                fput(src_file);
        }
+out_drop_write:
+       mnt_drop_write_file(file);
 out:
        return ret;
 }
@@ -1438,7 +1453,7 @@ static noinline int btrfs_ioctl_snap_create(struct file *file,
 
        ret = btrfs_ioctl_snap_create_transid(file, vol_args->name,
                                              vol_args->fd, subvol,
-                                             NULL, false);
+                                             NULL, false, NULL);
 
        kfree(vol_args);
        return ret;
@@ -1452,6 +1467,7 @@ static noinline int btrfs_ioctl_snap_create_v2(struct file *file,
        u64 transid = 0;
        u64 *ptr = NULL;
        bool readonly = false;
+       struct btrfs_qgroup_inherit *inherit = NULL;
 
        vol_args = memdup_user(arg, sizeof(*vol_args));
        if (IS_ERR(vol_args))
@@ -1459,7 +1475,8 @@ static noinline int btrfs_ioctl_snap_create_v2(struct file *file,
        vol_args->name[BTRFS_SUBVOL_NAME_MAX] = '\0';
 
        if (vol_args->flags &
-           ~(BTRFS_SUBVOL_CREATE_ASYNC | BTRFS_SUBVOL_RDONLY)) {
+           ~(BTRFS_SUBVOL_CREATE_ASYNC | BTRFS_SUBVOL_RDONLY |
+             BTRFS_SUBVOL_QGROUP_INHERIT)) {
                ret = -EOPNOTSUPP;
                goto out;
        }
@@ -1468,10 +1485,21 @@ static noinline int btrfs_ioctl_snap_create_v2(struct file *file,
                ptr = &transid;
        if (vol_args->flags & BTRFS_SUBVOL_RDONLY)
                readonly = true;
+       if (vol_args->flags & BTRFS_SUBVOL_QGROUP_INHERIT) {
+               if (vol_args->size > PAGE_CACHE_SIZE) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+               inherit = memdup_user(vol_args->qgroup_inherit, vol_args->size);
+               if (IS_ERR(inherit)) {
+                       ret = PTR_ERR(inherit);
+                       goto out;
+               }
+       }
 
        ret = btrfs_ioctl_snap_create_transid(file, vol_args->name,
-                                             vol_args->fd, subvol,
-                                             ptr, readonly);
+                                             vol_args->fd, subvol, ptr,
+                                             readonly, &inherit);
 
        if (ret == 0 && ptr &&
            copy_to_user(arg +
@@ -1480,6 +1508,7 @@ static noinline int btrfs_ioctl_snap_create_v2(struct file *file,
                ret = -EFAULT;
 out:
        kfree(vol_args);
+       kfree(inherit);
        return ret;
 }
 
@@ -1515,29 +1544,40 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file,
        u64 flags;
        int ret = 0;
 
-       if (root->fs_info->sb->s_flags & MS_RDONLY)
-               return -EROFS;
+       ret = mnt_want_write_file(file);
+       if (ret)
+               goto out;
 
-       if (btrfs_ino(inode) != BTRFS_FIRST_FREE_OBJECTID)
-               return -EINVAL;
+       if (btrfs_ino(inode) != BTRFS_FIRST_FREE_OBJECTID) {
+               ret = -EINVAL;
+               goto out_drop_write;
+       }
 
-       if (copy_from_user(&flags, arg, sizeof(flags)))
-               return -EFAULT;
+       if (copy_from_user(&flags, arg, sizeof(flags))) {
+               ret = -EFAULT;
+               goto out_drop_write;
+       }
 
-       if (flags & BTRFS_SUBVOL_CREATE_ASYNC)
-               return -EINVAL;
+       if (flags & BTRFS_SUBVOL_CREATE_ASYNC) {
+               ret = -EINVAL;
+               goto out_drop_write;
+       }
 
-       if (flags & ~BTRFS_SUBVOL_RDONLY)
-               return -EOPNOTSUPP;
+       if (flags & ~BTRFS_SUBVOL_RDONLY) {
+               ret = -EOPNOTSUPP;
+               goto out_drop_write;
+       }
 
-       if (!inode_owner_or_capable(inode))
-               return -EACCES;
+       if (!inode_owner_or_capable(inode)) {
+               ret = -EACCES;
+               goto out_drop_write;
+       }
 
        down_write(&root->fs_info->subvol_sem);
 
        /* nothing to do */
        if (!!(flags & BTRFS_SUBVOL_RDONLY) == btrfs_root_readonly(root))
-               goto out;
+               goto out_drop_sem;
 
        root_flags = btrfs_root_flags(&root->root_item);
        if (flags & BTRFS_SUBVOL_RDONLY)
@@ -1560,8 +1600,11 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file,
 out_reset:
        if (ret)
                btrfs_set_root_flags(&root->root_item, root_flags);
-out:
+out_drop_sem:
        up_write(&root->fs_info->subvol_sem);
+out_drop_write:
+       mnt_drop_write_file(file);
+out:
        return ret;
 }
 
@@ -3055,19 +3098,21 @@ static long btrfs_ioctl_scrub_progress(struct btrfs_root *root,
 }
 
 static long btrfs_ioctl_get_dev_stats(struct btrfs_root *root,
-                                     void __user *arg, int reset_after_read)
+                                     void __user *arg)
 {
        struct btrfs_ioctl_get_dev_stats *sa;
        int ret;
 
-       if (reset_after_read && !capable(CAP_SYS_ADMIN))
-               return -EPERM;
-
        sa = memdup_user(arg, sizeof(*sa));
        if (IS_ERR(sa))
                return PTR_ERR(sa);
 
-       ret = btrfs_get_dev_stats(root, sa, reset_after_read);
+       if ((sa->flags & BTRFS_DEV_STATS_RESET) && !capable(CAP_SYS_ADMIN)) {
+               kfree(sa);
+               return -EPERM;
+       }
+
+       ret = btrfs_get_dev_stats(root, sa);
 
        if (copy_to_user(arg, sa, sizeof(*sa)))
                ret = -EFAULT;
@@ -3257,10 +3302,7 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg)
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
 
-       if (fs_info->sb->s_flags & MS_RDONLY)
-               return -EROFS;
-
-       ret = mnt_want_write(file->f_path.mnt);
+       ret = mnt_want_write_file(file);
        if (ret)
                return ret;
 
@@ -3330,7 +3372,7 @@ out_bargs:
 out:
        mutex_unlock(&fs_info->balance_mutex);
        mutex_unlock(&fs_info->volume_mutex);
-       mnt_drop_write(file->f_path.mnt);
+       mnt_drop_write_file(file);
        return ret;
 }
 
@@ -3382,6 +3424,183 @@ out:
        return ret;
 }
 
+static long btrfs_ioctl_quota_ctl(struct btrfs_root *root, void __user *arg)
+{
+       struct btrfs_ioctl_quota_ctl_args *sa;
+       struct btrfs_trans_handle *trans = NULL;
+       int ret;
+       int err;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (root->fs_info->sb->s_flags & MS_RDONLY)
+               return -EROFS;
+
+       sa = memdup_user(arg, sizeof(*sa));
+       if (IS_ERR(sa))
+               return PTR_ERR(sa);
+
+       if (sa->cmd != BTRFS_QUOTA_CTL_RESCAN) {
+               trans = btrfs_start_transaction(root, 2);
+               if (IS_ERR(trans)) {
+                       ret = PTR_ERR(trans);
+                       goto out;
+               }
+       }
+
+       switch (sa->cmd) {
+       case BTRFS_QUOTA_CTL_ENABLE:
+               ret = btrfs_quota_enable(trans, root->fs_info);
+               break;
+       case BTRFS_QUOTA_CTL_DISABLE:
+               ret = btrfs_quota_disable(trans, root->fs_info);
+               break;
+       case BTRFS_QUOTA_CTL_RESCAN:
+               ret = btrfs_quota_rescan(root->fs_info);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       if (copy_to_user(arg, sa, sizeof(*sa)))
+               ret = -EFAULT;
+
+       if (trans) {
+               err = btrfs_commit_transaction(trans, root);
+               if (err && !ret)
+                       ret = err;
+       }
+
+out:
+       kfree(sa);
+       return ret;
+}
+
+static long btrfs_ioctl_qgroup_assign(struct btrfs_root *root, void __user *arg)
+{
+       struct btrfs_ioctl_qgroup_assign_args *sa;
+       struct btrfs_trans_handle *trans;
+       int ret;
+       int err;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (root->fs_info->sb->s_flags & MS_RDONLY)
+               return -EROFS;
+
+       sa = memdup_user(arg, sizeof(*sa));
+       if (IS_ERR(sa))
+               return PTR_ERR(sa);
+
+       trans = btrfs_join_transaction(root);
+       if (IS_ERR(trans)) {
+               ret = PTR_ERR(trans);
+               goto out;
+       }
+
+       /* FIXME: check if the IDs really exist */
+       if (sa->assign) {
+               ret = btrfs_add_qgroup_relation(trans, root->fs_info,
+                                               sa->src, sa->dst);
+       } else {
+               ret = btrfs_del_qgroup_relation(trans, root->fs_info,
+                                               sa->src, sa->dst);
+       }
+
+       err = btrfs_end_transaction(trans, root);
+       if (err && !ret)
+               ret = err;
+
+out:
+       kfree(sa);
+       return ret;
+}
+
+static long btrfs_ioctl_qgroup_create(struct btrfs_root *root, void __user *arg)
+{
+       struct btrfs_ioctl_qgroup_create_args *sa;
+       struct btrfs_trans_handle *trans;
+       int ret;
+       int err;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (root->fs_info->sb->s_flags & MS_RDONLY)
+               return -EROFS;
+
+       sa = memdup_user(arg, sizeof(*sa));
+       if (IS_ERR(sa))
+               return PTR_ERR(sa);
+
+       trans = btrfs_join_transaction(root);
+       if (IS_ERR(trans)) {
+               ret = PTR_ERR(trans);
+               goto out;
+       }
+
+       /* FIXME: check if the IDs really exist */
+       if (sa->create) {
+               ret = btrfs_create_qgroup(trans, root->fs_info, sa->qgroupid,
+                                         NULL);
+       } else {
+               ret = btrfs_remove_qgroup(trans, root->fs_info, sa->qgroupid);
+       }
+
+       err = btrfs_end_transaction(trans, root);
+       if (err && !ret)
+               ret = err;
+
+out:
+       kfree(sa);
+       return ret;
+}
+
+static long btrfs_ioctl_qgroup_limit(struct btrfs_root *root, void __user *arg)
+{
+       struct btrfs_ioctl_qgroup_limit_args *sa;
+       struct btrfs_trans_handle *trans;
+       int ret;
+       int err;
+       u64 qgroupid;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (root->fs_info->sb->s_flags & MS_RDONLY)
+               return -EROFS;
+
+       sa = memdup_user(arg, sizeof(*sa));
+       if (IS_ERR(sa))
+               return PTR_ERR(sa);
+
+       trans = btrfs_join_transaction(root);
+       if (IS_ERR(trans)) {
+               ret = PTR_ERR(trans);
+               goto out;
+       }
+
+       qgroupid = sa->qgroupid;
+       if (!qgroupid) {
+               /* take the current subvol as qgroup */
+               qgroupid = root->root_key.objectid;
+       }
+
+       /* FIXME: check if the IDs really exist */
+       ret = btrfs_limit_qgroup(trans, root->fs_info, qgroupid, &sa->lim);
+
+       err = btrfs_end_transaction(trans, root);
+       if (err && !ret)
+               ret = err;
+
+out:
+       kfree(sa);
+       return ret;
+}
+
 long btrfs_ioctl(struct file *file, unsigned int
                cmd, unsigned long arg)
 {
@@ -3403,6 +3622,8 @@ long btrfs_ioctl(struct file *file, unsigned int
                return btrfs_ioctl_snap_create_v2(file, argp, 0);
        case BTRFS_IOC_SUBVOL_CREATE:
                return btrfs_ioctl_snap_create(file, argp, 1);
+       case BTRFS_IOC_SUBVOL_CREATE_V2:
+               return btrfs_ioctl_snap_create_v2(file, argp, 1);
        case BTRFS_IOC_SNAP_DESTROY:
                return btrfs_ioctl_snap_destroy(file, argp);
        case BTRFS_IOC_SUBVOL_GETFLAGS:
@@ -3465,9 +3686,15 @@ long btrfs_ioctl(struct file *file, unsigned int
        case BTRFS_IOC_BALANCE_PROGRESS:
                return btrfs_ioctl_balance_progress(root, argp);
        case BTRFS_IOC_GET_DEV_STATS:
-               return btrfs_ioctl_get_dev_stats(root, argp, 0);
-       case BTRFS_IOC_GET_AND_RESET_DEV_STATS:
-               return btrfs_ioctl_get_dev_stats(root, argp, 1);
+               return btrfs_ioctl_get_dev_stats(root, argp);
+       case BTRFS_IOC_QUOTA_CTL:
+               return btrfs_ioctl_quota_ctl(root, argp);
+       case BTRFS_IOC_QGROUP_ASSIGN:
+               return btrfs_ioctl_qgroup_assign(root, argp);
+       case BTRFS_IOC_QGROUP_CREATE:
+               return btrfs_ioctl_qgroup_create(root, argp);
+       case BTRFS_IOC_QGROUP_LIMIT:
+               return btrfs_ioctl_qgroup_limit(root, argp);
        }
 
        return -ENOTTY;