Merge branch 'for-chris' of git://git.kernel.org/pub/scm/linux/kernel/git/arne/btrfs...
[pandora-kernel.git] / fs / btrfs / ioctl.c
index ffb48d6..e0a061d 100644 (file)
@@ -50,6 +50,7 @@
 #include "print-tree.h"
 #include "volumes.h"
 #include "locking.h"
+#include "inode-map.h"
 
 /* Mask out flags that are inappropriate for the given type of inode. */
 static inline __u32 btrfs_mask_flags(umode_t mode, __u32 flags)
@@ -81,6 +82,13 @@ static unsigned int btrfs_flags_to_ioctl(unsigned int flags)
                iflags |= FS_NOATIME_FL;
        if (flags & BTRFS_INODE_DIRSYNC)
                iflags |= FS_DIRSYNC_FL;
+       if (flags & BTRFS_INODE_NODATACOW)
+               iflags |= FS_NOCOW_FL;
+
+       if ((flags & BTRFS_INODE_COMPRESS) && !(flags & BTRFS_INODE_NOCOMPRESS))
+               iflags |= FS_COMPR_FL;
+       else if (flags & BTRFS_INODE_NOCOMPRESS)
+               iflags |= FS_NOCOMP_FL;
 
        return iflags;
 }
@@ -144,16 +152,13 @@ static int check_flags(unsigned int flags)
        if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
                      FS_NOATIME_FL | FS_NODUMP_FL | \
                      FS_SYNC_FL | FS_DIRSYNC_FL | \
-                     FS_NOCOMP_FL | FS_COMPR_FL | \
-                     FS_NOCOW_FL | FS_COW_FL))
+                     FS_NOCOMP_FL | FS_COMPR_FL |
+                     FS_NOCOW_FL))
                return -EOPNOTSUPP;
 
        if ((flags & FS_NOCOMP_FL) && (flags & FS_COMPR_FL))
                return -EINVAL;
 
-       if ((flags & FS_NOCOW_FL) && (flags & FS_COW_FL))
-               return -EINVAL;
-
        return 0;
 }
 
@@ -218,6 +223,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
                ip->flags |= BTRFS_INODE_DIRSYNC;
        else
                ip->flags &= ~BTRFS_INODE_DIRSYNC;
+       if (flags & FS_NOCOW_FL)
+               ip->flags |= BTRFS_INODE_NODATACOW;
+       else
+               ip->flags &= ~BTRFS_INODE_NODATACOW;
 
        /*
         * The COMPRESS flag can only be changed by users, while the NOCOMPRESS
@@ -230,11 +239,9 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
        } else if (flags & FS_COMPR_FL) {
                ip->flags |= BTRFS_INODE_COMPRESS;
                ip->flags &= ~BTRFS_INODE_NOCOMPRESS;
+       } else {
+               ip->flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS);
        }
-       if (flags & FS_NOCOW_FL)
-               ip->flags |= BTRFS_INODE_NODATACOW;
-       else if (flags & FS_COW_FL)
-               ip->flags &= ~BTRFS_INODE_NODATACOW;
 
        trans = btrfs_join_transaction(root, 1);
        BUG_ON(IS_ERR(trans));
@@ -323,8 +330,7 @@ static noinline int create_subvol(struct btrfs_root *root,
        u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID;
        u64 index = 0;
 
-       ret = btrfs_find_free_objectid(NULL, root->fs_info->tree_root,
-                                      0, &objectid);
+       ret = btrfs_find_free_objectid(root->fs_info->tree_root, &objectid);
        if (ret) {
                dput(parent);
                return ret;
@@ -416,7 +422,7 @@ static noinline int create_subvol(struct btrfs_root *root,
        BUG_ON(ret);
 
        ret = btrfs_insert_dir_item(trans, root,
-                                   name, namelen, dir->i_ino, &key,
+                                   name, namelen, dir, &key,
                                    BTRFS_FT_DIR, index);
        if (ret)
                goto fail;
@@ -427,7 +433,7 @@ static noinline int create_subvol(struct btrfs_root *root,
 
        ret = btrfs_add_root_ref(trans, root->fs_info->tree_root,
                                 objectid, root->root_key.objectid,
-                                dir->i_ino, index, name, namelen);
+                                btrfs_ino(dir), index, name, namelen);
 
        BUG_ON(ret);
 
@@ -1123,7 +1129,7 @@ static noinline int btrfs_ioctl_subvol_getflags(struct file *file,
        int ret = 0;
        u64 flags = 0;
 
-       if (inode->i_ino != BTRFS_FIRST_FREE_OBJECTID)
+       if (btrfs_ino(inode) != BTRFS_FIRST_FREE_OBJECTID)
                return -EINVAL;
 
        down_read(&root->fs_info->subvol_sem);
@@ -1150,7 +1156,7 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file,
        if (root->fs_info->sb->s_flags & MS_RDONLY)
                return -EROFS;
 
-       if (inode->i_ino != BTRFS_FIRST_FREE_OBJECTID)
+       if (btrfs_ino(inode) != BTRFS_FIRST_FREE_OBJECTID)
                return -EINVAL;
 
        if (copy_from_user(&flags, arg, sizeof(flags)))
@@ -1396,7 +1402,7 @@ static noinline int search_ioctl(struct inode *inode,
                }
                ret = copy_to_sk(root, path, &key, sk, args->buf,
                                 &sk_offset, &num_found);
-               btrfs_release_path(root, path);
+               btrfs_release_path(path);
                if (ret || num_found >= sk->nr_items)
                        break;
 
@@ -1503,7 +1509,7 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info,
                if (key.offset == BTRFS_FIRST_FREE_OBJECTID)
                        break;
 
-               btrfs_release_path(root, path);
+               btrfs_release_path(path);
                key.objectid = key.offset;
                key.offset = (u64)-1;
                dirid = key.objectid;
@@ -1633,7 +1639,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
                        goto out_dput;
        }
 
-       if (inode->i_ino != BTRFS_FIRST_FREE_OBJECTID) {
+       if (btrfs_ino(inode) != BTRFS_FIRST_FREE_OBJECTID) {
                err = -EINVAL;
                goto out_dput;
        }
@@ -1803,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)
 {
@@ -1919,7 +1994,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
        }
 
        /* clone data */
-       key.objectid = src->i_ino;
+       key.objectid = btrfs_ino(src);
        key.type = BTRFS_EXTENT_DATA_KEY;
        key.offset = 0;
 
@@ -1946,7 +2021,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
 
                btrfs_item_key_to_cpu(leaf, &key, slot);
                if (btrfs_key_type(&key) > BTRFS_EXTENT_DATA_KEY ||
-                   key.objectid != src->i_ino)
+                   key.objectid != btrfs_ino(src))
                        break;
 
                if (btrfs_key_type(&key) == BTRFS_EXTENT_DATA_KEY) {
@@ -1982,14 +2057,14 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
                                datal = btrfs_file_extent_ram_bytes(leaf,
                                                                    extent);
                        }
-                       btrfs_release_path(root, path);
+                       btrfs_release_path(path);
 
                        if (key.offset + datal <= off ||
                            key.offset >= off+len)
                                goto next;
 
                        memcpy(&new_key, &key, sizeof(new_key));
-                       new_key.objectid = inode->i_ino;
+                       new_key.objectid = btrfs_ino(inode);
                        if (off <= key.offset)
                                new_key.offset = key.offset + destoff - off;
                        else
@@ -2043,7 +2118,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
                                        ret = btrfs_inc_extent_ref(trans, root,
                                                        disko, diskl, 0,
                                                        root->root_key.objectid,
-                                                       inode->i_ino,
+                                                       btrfs_ino(inode),
                                                        new_key.offset - datao);
                                        BUG_ON(ret);
                                }
@@ -2092,7 +2167,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
                        }
 
                        btrfs_mark_buffer_dirty(leaf);
-                       btrfs_release_path(root, path);
+                       btrfs_release_path(path);
 
                        inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 
@@ -2113,12 +2188,12 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
                        btrfs_end_transaction(trans, root);
                }
 next:
-               btrfs_release_path(root, path);
+               btrfs_release_path(path);
                key.offset++;
        }
        ret = 0;
 out:
-       btrfs_release_path(root, path);
+       btrfs_release_path(path);
        unlock_extent(&BTRFS_I(src)->io_tree, off, off+len, GFP_NOFS);
 out_unlock:
        mutex_unlock(&src->i_mutex);
@@ -2465,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)
 {
@@ -2504,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:
@@ -2527,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;