]> Pileus Git - ~andy/linux/blobdiff - fs/btrfs/ioctl.c
btrfs: fix null pointer deference at btrfs_sysfs_add_one+0x105
[~andy/linux] / fs / btrfs / ioctl.c
index 3970f32b2b8076a4116295a12598f47b241c31c3..9a9044585da705ccc28956b6d860d1cd2b92579a 100644 (file)
@@ -192,6 +192,9 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
        unsigned int i_oldflags;
        umode_t mode;
 
+       if (!inode_owner_or_capable(inode))
+               return -EPERM;
+
        if (btrfs_root_readonly(root))
                return -EROFS;
 
@@ -202,9 +205,6 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
        if (ret)
                return ret;
 
-       if (!inode_owner_or_capable(inode))
-               return -EACCES;
-
        ret = mnt_want_write_file(file);
        if (ret)
                return ret;
@@ -436,7 +436,9 @@ static noinline int create_subvol(struct inode *dir,
        trans = btrfs_start_transaction(root, 0);
        if (IS_ERR(trans)) {
                ret = PTR_ERR(trans);
-               goto out;
+               btrfs_subvolume_release_metadata(root, &block_rsv,
+                                                qgroup_reserved);
+               return ret;
        }
        trans->block_rsv = &block_rsv;
        trans->bytes_reserved = block_rsv.size;
@@ -561,6 +563,8 @@ static noinline int create_subvol(struct inode *dir,
 fail:
        trans->block_rsv = NULL;
        trans->bytes_reserved = 0;
+       btrfs_subvolume_release_metadata(root, &block_rsv, qgroup_reserved);
+
        if (async_transid) {
                *async_transid = trans->transid;
                err = btrfs_commit_transaction_async(trans, root, 1);
@@ -574,14 +578,10 @@ fail:
 
        if (!ret) {
                inode = btrfs_lookup_dentry(dir, dentry);
-               if (IS_ERR(inode)) {
-                       ret = PTR_ERR(inode);
-                       goto out;
-               }
+               if (IS_ERR(inode))
+                       return PTR_ERR(inode);
                d_instantiate(dentry, inode);
        }
-out:
-       btrfs_subvolume_release_metadata(root, &block_rsv, qgroup_reserved);
        return ret;
 }
 
@@ -1036,7 +1036,7 @@ out:
 static int cluster_pages_for_defrag(struct inode *inode,
                                    struct page **pages,
                                    unsigned long start_index,
-                                   int num_pages)
+                                   unsigned long num_pages)
 {
        unsigned long file_end;
        u64 isize = i_size_read(inode);
@@ -1194,8 +1194,8 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,
        int defrag_count = 0;
        int compress_type = BTRFS_COMPRESS_ZLIB;
        int extent_thresh = range->extent_thresh;
-       int max_cluster = (256 * 1024) >> PAGE_CACHE_SHIFT;
-       int cluster = max_cluster;
+       unsigned long max_cluster = (256 * 1024) >> PAGE_CACHE_SHIFT;
+       unsigned long cluster = max_cluster;
        u64 new_align = ~((u64)128 * 1024 - 1);
        struct page **pages = NULL;
 
@@ -1574,6 +1574,12 @@ static noinline int btrfs_ioctl_snap_create_transid(struct file *file,
                        btrfs_info(BTRFS_I(src_inode)->root->fs_info,
                                   "Snapshot src from another FS");
                        ret = -EINVAL;
+               } else if (!inode_owner_or_capable(src_inode)) {
+                       /*
+                        * Subvolume creation is not restricted, but snapshots
+                        * are limited to own subvolumes only
+                        */
+                       ret = -EPERM;
                } else {
                        ret = btrfs_mksubvol(&file->f_path, name, namelen,
                                             BTRFS_I(src_inode)->root,
@@ -1691,6 +1697,9 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file,
        u64 flags;
        int ret = 0;
 
+       if (!inode_owner_or_capable(inode))
+               return -EPERM;
+
        ret = mnt_want_write_file(file);
        if (ret)
                goto out;
@@ -1715,11 +1724,6 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file,
                goto out_drop_write;
        }
 
-       if (!inode_owner_or_capable(inode)) {
-               ret = -EACCES;
-               goto out_drop_write;
-       }
-
        down_write(&root->fs_info->subvol_sem);
 
        /* nothing to do */
@@ -2905,12 +2909,14 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
                 * note the key will change type as we walk through the
                 * tree.
                 */
+               path->leave_spinning = 1;
                ret = btrfs_search_slot(NULL, BTRFS_I(src)->root, &key, path,
                                0, 0);
                if (ret < 0)
                        goto out;
 
                nritems = btrfs_header_nritems(path->nodes[0]);
+process_slot:
                if (path->slots[0] >= nritems) {
                        ret = btrfs_next_leaf(BTRFS_I(src)->root, path);
                        if (ret < 0)
@@ -2937,11 +2943,6 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
                        u8 comp;
                        u64 endoff;
 
-                       size = btrfs_item_size_nr(leaf, slot);
-                       read_extent_buffer(leaf, buf,
-                                          btrfs_item_ptr_offset(leaf, slot),
-                                          size);
-
                        extent = btrfs_item_ptr(leaf, slot,
                                                struct btrfs_file_extent_item);
                        comp = btrfs_file_extent_compression(leaf, extent);
@@ -2960,11 +2961,20 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
                                datal = btrfs_file_extent_ram_bytes(leaf,
                                                                    extent);
                        }
-                       btrfs_release_path(path);
 
                        if (key.offset + datal <= off ||
-                           key.offset >= off + len - 1)
-                               goto next;
+                           key.offset >= off + len - 1) {
+                               path->slots[0]++;
+                               goto process_slot;
+                       }
+
+                       size = btrfs_item_size_nr(leaf, slot);
+                       read_extent_buffer(leaf, buf,
+                                          btrfs_item_ptr_offset(leaf, slot),
+                                          size);
+
+                       btrfs_release_path(path);
+                       path->leave_spinning = 0;
 
                        memcpy(&new_key, &key, sizeof(new_key));
                        new_key.objectid = btrfs_ino(inode);
@@ -3135,7 +3145,6 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
                        }
                        ret = btrfs_end_transaction(trans, root);
                }
-next:
                btrfs_release_path(path);
                key.offset++;
        }
@@ -3263,9 +3272,17 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
 
        unlock_extent(&BTRFS_I(src)->io_tree, off, off + len - 1);
 out_unlock:
-       mutex_unlock(&src->i_mutex);
-       if (!same_inode)
-               mutex_unlock(&inode->i_mutex);
+       if (!same_inode) {
+               if (inode < src) {
+                       mutex_unlock(&src->i_mutex);
+                       mutex_unlock(&inode->i_mutex);
+               } else {
+                       mutex_unlock(&inode->i_mutex);
+                       mutex_unlock(&src->i_mutex);
+               }
+       } else {
+               mutex_unlock(&src->i_mutex);
+       }
 out_fput:
        fdput(src_file);
 out_drop_write:
@@ -3542,20 +3559,6 @@ out:
        return ret;
 }
 
-static long btrfs_ioctl_global_rsv(struct btrfs_root *root, void __user *arg)
-{
-       struct btrfs_block_rsv *block_rsv = &root->fs_info->global_block_rsv;
-       u64 reserved;
-
-       spin_lock(&block_rsv->lock);
-       reserved = block_rsv->reserved;
-       spin_unlock(&block_rsv->lock);
-
-       if (arg && copy_to_user(arg, &reserved, sizeof(reserved)))
-               return -EFAULT;
-       return 0;
-}
-
 /*
  * there are many ways the trans_start and trans_end ioctls can lead
  * to deadlocks.  They should only be used by applications that
@@ -4384,6 +4387,9 @@ static long btrfs_ioctl_set_received_subvol(struct file *file,
        int ret = 0;
        int received_uuid_changed;
 
+       if (!inode_owner_or_capable(inode))
+               return -EPERM;
+
        ret = mnt_want_write_file(file);
        if (ret < 0)
                return ret;
@@ -4400,11 +4406,6 @@ static long btrfs_ioctl_set_received_subvol(struct file *file,
                goto out;
        }
 
-       if (!inode_owner_or_capable(inode)) {
-               ret = -EACCES;
-               goto out;
-       }
-
        sa = memdup_user(arg, sizeof(*sa));
        if (IS_ERR(sa)) {
                ret = PTR_ERR(sa);
@@ -4532,7 +4533,7 @@ static int btrfs_ioctl_set_fslabel(struct file *file, void __user *arg)
        spin_lock(&root->fs_info->super_lock);
        strcpy(super_block->label, label);
        spin_unlock(&root->fs_info->super_lock);
-       ret = btrfs_end_transaction(trans, root);
+       ret = btrfs_commit_transaction(trans, root);
 
 out_unlock:
        mnt_drop_write_file(file);
@@ -4675,7 +4676,7 @@ static int btrfs_ioctl_set_features(struct file *file, void __user *arg)
        if (ret)
                return ret;
 
-       trans = btrfs_start_transaction(root, 1);
+       trans = btrfs_start_transaction(root, 0);
        if (IS_ERR(trans))
                return PTR_ERR(trans);
 
@@ -4696,7 +4697,7 @@ static int btrfs_ioctl_set_features(struct file *file, void __user *arg)
        btrfs_set_super_incompat_flags(super_block, newflags);
        spin_unlock(&root->fs_info->super_lock);
 
-       return btrfs_end_transaction(trans, root);
+       return btrfs_commit_transaction(trans, root);
 }
 
 long btrfs_ioctl(struct file *file, unsigned int
@@ -4764,8 +4765,6 @@ long btrfs_ioctl(struct file *file, unsigned int
                return btrfs_ioctl_logical_to_ino(root, argp);
        case BTRFS_IOC_SPACE_INFO:
                return btrfs_ioctl_space_info(root, argp);
-       case BTRFS_IOC_GLOBAL_RSV:
-               return btrfs_ioctl_global_rsv(root, argp);
        case BTRFS_IOC_SYNC: {
                int ret;