]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge branch 'lzo-support' of git://repo.or.cz/linux-btrfs-devel into btrfs-38
authorChris Mason <chris.mason@oracle.com>
Sun, 16 Jan 2011 16:25:54 +0000 (11:25 -0500)
committerChris Mason <chris.mason@oracle.com>
Sun, 16 Jan 2011 16:25:54 +0000 (11:25 -0500)
1  2 
fs/btrfs/ctree.h
fs/btrfs/inode.c
fs/btrfs/ioctl.c
fs/btrfs/ioctl.h

diff --combined fs/btrfs/ctree.h
index 4403e5643d43669a6881c6153b2d9d2b29342f2a,53b984623983038eb03cf524401e20e184614c34..4acd4c611efa6a8f4659996867982e294ac4492d
@@@ -398,13 -398,15 +398,15 @@@ struct btrfs_super_block 
  #define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF  (1ULL << 0)
  #define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL (1ULL << 1)
  #define BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS   (1ULL << 2)
+ #define BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO   (1ULL << 3)
  
  #define BTRFS_FEATURE_COMPAT_SUPP             0ULL
  #define BTRFS_FEATURE_COMPAT_RO_SUPP          0ULL
  #define BTRFS_FEATURE_INCOMPAT_SUPP                   \
        (BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF |         \
         BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL |        \
-        BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS)
+        BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS |          \
+        BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO)
  
  /*
   * A leaf is full of items. offset and size tell us where to find
@@@ -551,9 -553,11 +553,11 @@@ struct btrfs_timespec 
  } __attribute__ ((__packed__));
  
  enum btrfs_compression_type {
-       BTRFS_COMPRESS_NONE = 0,
-       BTRFS_COMPRESS_ZLIB = 1,
-       BTRFS_COMPRESS_LAST = 2,
+       BTRFS_COMPRESS_NONE  = 0,
+       BTRFS_COMPRESS_ZLIB  = 1,
+       BTRFS_COMPRESS_LZO   = 2,
+       BTRFS_COMPRESS_TYPES = 2,
+       BTRFS_COMPRESS_LAST  = 3,
  };
  
  struct btrfs_inode_item {
@@@ -597,8 -601,6 +601,8 @@@ struct btrfs_dir_item 
        u8 type;
  } __attribute__ ((__packed__));
  
 +#define BTRFS_ROOT_SUBVOL_RDONLY      (1ULL << 0)
 +
  struct btrfs_root_item {
        struct btrfs_inode_item inode;
        __le64 generation;
@@@ -897,7 -899,8 +901,8 @@@ struct btrfs_fs_info 
         */
        u64 last_trans_log_full_commit;
        u64 open_ioctl_trans;
-       unsigned long mount_opt;
+       unsigned long mount_opt:20;
+       unsigned long compress_type:4;
        u64 max_inline;
        u64 alloc_start;
        struct btrfs_transaction *running_transaction;
@@@ -1895,11 -1898,6 +1900,11 @@@ BTRFS_SETGET_STACK_FUNCS(root_limit, st
  BTRFS_SETGET_STACK_FUNCS(root_last_snapshot, struct btrfs_root_item,
                         last_snapshot, 64);
  
 +static inline bool btrfs_root_readonly(struct btrfs_root *root)
 +{
 +      return root->root_item.flags & BTRFS_ROOT_SUBVOL_RDONLY;
 +}
 +
  /* struct btrfs_super_block */
  
  BTRFS_SETGET_STACK_FUNCS(super_bytenr, struct btrfs_super_block, bytenr, 64);
diff --combined fs/btrfs/inode.c
index 956f1eb913b19bfda46de7659da04106360a1753,ba563b2a5d6cfdc533e8f6a859632d39c12ff22f..1562765c8e6a9e418df3347257e9ecca879a577f
@@@ -122,10 -122,10 +122,10 @@@ static noinline int insert_inline_exten
        size_t cur_size = size;
        size_t datasize;
        unsigned long offset;
-       int use_compress = 0;
+       int compress_type = BTRFS_COMPRESS_NONE;
  
        if (compressed_size && compressed_pages) {
-               use_compress = 1;
+               compress_type = root->fs_info->compress_type;
                cur_size = compressed_size;
        }
  
        btrfs_set_file_extent_ram_bytes(leaf, ei, size);
        ptr = btrfs_file_extent_inline_start(ei);
  
-       if (use_compress) {
+       if (compress_type != BTRFS_COMPRESS_NONE) {
                struct page *cpage;
                int i = 0;
                while (compressed_size > 0) {
                        compressed_size -= cur_size;
                }
                btrfs_set_file_extent_compression(leaf, ei,
-                                                 BTRFS_COMPRESS_ZLIB);
+                                                 compress_type);
        } else {
                page = find_get_page(inode->i_mapping,
                                     start >> PAGE_CACHE_SHIFT);
@@@ -263,6 -263,7 +263,7 @@@ struct async_extent 
        u64 compressed_size;
        struct page **pages;
        unsigned long nr_pages;
+       int compress_type;
        struct list_head list;
  };
  
@@@ -280,7 -281,8 +281,8 @@@ static noinline int add_async_extent(st
                                     u64 start, u64 ram_size,
                                     u64 compressed_size,
                                     struct page **pages,
-                                    unsigned long nr_pages)
+                                    unsigned long nr_pages,
+                                    int compress_type)
  {
        struct async_extent *async_extent;
  
        async_extent->compressed_size = compressed_size;
        async_extent->pages = pages;
        async_extent->nr_pages = nr_pages;
+       async_extent->compress_type = compress_type;
        list_add_tail(&async_extent->list, &cow->extents);
        return 0;
  }
@@@ -332,6 -335,7 +335,7 @@@ static noinline int compress_file_range
        unsigned long max_uncompressed = 128 * 1024;
        int i;
        int will_compress;
+       int compress_type = root->fs_info->compress_type;
  
        actual_end = min_t(u64, isize, end + 1);
  again:
                WARN_ON(pages);
                pages = kzalloc(sizeof(struct page *) * nr_pages, GFP_NOFS);
  
-               ret = btrfs_zlib_compress_pages(inode->i_mapping, start,
-                                               total_compressed, pages,
-                                               nr_pages, &nr_pages_ret,
-                                               &total_in,
-                                               &total_compressed,
-                                               max_compressed);
+               if (BTRFS_I(inode)->force_compress)
+                       compress_type = BTRFS_I(inode)->force_compress;
+               ret = btrfs_compress_pages(compress_type,
+                                          inode->i_mapping, start,
+                                          total_compressed, pages,
+                                          nr_pages, &nr_pages_ret,
+                                          &total_in,
+                                          &total_compressed,
+                                          max_compressed);
  
                if (!ret) {
                        unsigned long offset = total_compressed &
                 * and will submit them to the elevator.
                 */
                add_async_extent(async_cow, start, num_bytes,
-                                total_compressed, pages, nr_pages_ret);
+                                total_compressed, pages, nr_pages_ret,
+                                compress_type);
  
                if (start + num_bytes < end) {
                        start += num_bytes;
@@@ -515,7 -524,8 +524,8 @@@ cleanup_and_bail_uncompressed
                        __set_page_dirty_nobuffers(locked_page);
                        /* unlocked later on in the async handlers */
                }
-               add_async_extent(async_cow, start, end - start + 1, 0, NULL, 0);
+               add_async_extent(async_cow, start, end - start + 1,
+                                0, NULL, 0, BTRFS_COMPRESS_NONE);
                *num_added += 1;
        }
  
@@@ -640,6 -650,7 +650,7 @@@ retry
                em->block_start = ins.objectid;
                em->block_len = ins.offset;
                em->bdev = root->fs_info->fs_devices->latest_bdev;
+               em->compress_type = async_extent->compress_type;
                set_bit(EXTENT_FLAG_PINNED, &em->flags);
                set_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
  
                                                async_extent->ram_size - 1, 0);
                }
  
-               ret = btrfs_add_ordered_extent(inode, async_extent->start,
-                                              ins.objectid,
-                                              async_extent->ram_size,
-                                              ins.offset,
-                                              BTRFS_ORDERED_COMPRESSED);
+               ret = btrfs_add_ordered_extent_compress(inode,
+                                               async_extent->start,
+                                               ins.objectid,
+                                               async_extent->ram_size,
+                                               ins.offset,
+                                               BTRFS_ORDERED_COMPRESSED,
+                                               async_extent->compress_type);
                BUG_ON(ret);
  
                /*
@@@ -1670,7 -1683,7 +1683,7 @@@ static int btrfs_finish_ordered_io(stru
        struct btrfs_ordered_extent *ordered_extent = NULL;
        struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
        struct extent_state *cached_state = NULL;
-       int compressed = 0;
+       int compress_type = 0;
        int ret;
        bool nolock = false;
  
        trans->block_rsv = &root->fs_info->delalloc_block_rsv;
  
        if (test_bit(BTRFS_ORDERED_COMPRESSED, &ordered_extent->flags))
-               compressed = 1;
+               compress_type = ordered_extent->compress_type;
        if (test_bit(BTRFS_ORDERED_PREALLOC, &ordered_extent->flags)) {
-               BUG_ON(compressed);
+               BUG_ON(compress_type);
                ret = btrfs_mark_extent_written(trans, inode,
                                                ordered_extent->file_offset,
                                                ordered_extent->file_offset +
                                                ordered_extent->disk_len,
                                                ordered_extent->len,
                                                ordered_extent->len,
-                                               compressed, 0, 0,
+                                               compress_type, 0, 0,
                                                BTRFS_FILE_EXTENT_REG);
                unpin_extent_cache(&BTRFS_I(inode)->extent_tree,
                                   ordered_extent->file_offset,
@@@ -1829,6 -1842,8 +1842,8 @@@ static int btrfs_io_failed_hook(struct 
                if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)) {
                        logical = em->block_start;
                        failrec->bio_flags = EXTENT_BIO_COMPRESSED;
+                       extent_set_compress_type(&failrec->bio_flags,
+                                                em->compress_type);
                }
                failrec->logical = logical;
                free_extent_map(em);
@@@ -3671,12 -3686,8 +3686,12 @@@ static int btrfs_setattr_size(struct in
  static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
  {
        struct inode *inode = dentry->d_inode;
 +      struct btrfs_root *root = BTRFS_I(inode)->root;
        int err;
  
 +      if (btrfs_root_readonly(root))
 +              return -EROFS;
 +
        err = inode_change_ok(inode, attr);
        if (err)
                return err;
@@@ -4934,8 -4945,10 +4949,10 @@@ static noinline int uncompress_inline(s
        size_t max_size;
        unsigned long inline_size;
        unsigned long ptr;
+       int compress_type;
  
        WARN_ON(pg_offset != 0);
+       compress_type = btrfs_file_extent_compression(leaf, item);
        max_size = btrfs_file_extent_ram_bytes(leaf, item);
        inline_size = btrfs_file_extent_inline_item_len(leaf,
                                        btrfs_item_nr(leaf, path->slots[0]));
        read_extent_buffer(leaf, tmp, ptr, inline_size);
  
        max_size = min_t(unsigned long, PAGE_CACHE_SIZE, max_size);
-       ret = btrfs_zlib_decompress(tmp, page, extent_offset,
-                                   inline_size, max_size);
+       ret = btrfs_decompress(compress_type, tmp, page,
+                              extent_offset, inline_size, max_size);
        if (ret) {
                char *kaddr = kmap_atomic(page, KM_USER0);
                unsigned long copy_size = min_t(u64,
@@@ -4988,7 -5001,7 +5005,7 @@@ struct extent_map *btrfs_get_extent(str
        struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
        struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
        struct btrfs_trans_handle *trans = NULL;
-       int compressed;
+       int compress_type;
  
  again:
        read_lock(&em_tree->lock);
  
        found_type = btrfs_file_extent_type(leaf, item);
        extent_start = found_key.offset;
-       compressed = btrfs_file_extent_compression(leaf, item);
+       compress_type = btrfs_file_extent_compression(leaf, item);
        if (found_type == BTRFS_FILE_EXTENT_REG ||
            found_type == BTRFS_FILE_EXTENT_PREALLOC) {
                extent_end = extent_start +
                        em->block_start = EXTENT_MAP_HOLE;
                        goto insert;
                }
-               if (compressed) {
+               if (compress_type != BTRFS_COMPRESS_NONE) {
                        set_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
+                       em->compress_type = compress_type;
                        em->block_start = bytenr;
                        em->block_len = btrfs_file_extent_disk_num_bytes(leaf,
                                                                         item);
                em->len = (copy_size + root->sectorsize - 1) &
                        ~((u64)root->sectorsize - 1);
                em->orig_start = EXTENT_MAP_INLINE;
-               if (compressed)
+               if (compress_type) {
                        set_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
+                       em->compress_type = compress_type;
+               }
                ptr = btrfs_file_extent_inline_start(item) + extent_offset;
                if (create == 0 && !PageUptodate(page)) {
-                       if (btrfs_file_extent_compression(leaf, item) ==
-                           BTRFS_COMPRESS_ZLIB) {
+                       if (btrfs_file_extent_compression(leaf, item) !=
+                           BTRFS_COMPRESS_NONE) {
                                ret = uncompress_inline(path, inode, page,
                                                        pg_offset,
                                                        extent_offset, item);
@@@ -6483,7 -6499,7 +6503,7 @@@ struct inode *btrfs_alloc_inode(struct 
        ei->ordered_data_close = 0;
        ei->orphan_meta_reserved = 0;
        ei->dummy_inode = 0;
-       ei->force_compress = 0;
+       ei->force_compress = BTRFS_COMPRESS_NONE;
  
        inode = &ei->vfs_inode;
        extent_map_tree_init(&ei->extent_tree, GFP_NOFS);
@@@ -7210,10 -7226,6 +7230,10 @@@ static int btrfs_set_page_dirty(struct 
  
  static int btrfs_permission(struct inode *inode, int mask)
  {
 +      struct btrfs_root *root = BTRFS_I(inode)->root;
 +
 +      if (btrfs_root_readonly(root) && (mask & MAY_WRITE))
 +              return -EROFS;
        if ((BTRFS_I(inode)->flags & BTRFS_INODE_READONLY) && (mask & MAY_WRITE))
                return -EACCES;
        return generic_permission(inode, mask, btrfs_check_acl);
diff --combined fs/btrfs/ioctl.c
index ad1983524f97d7d97de7c5cafc6934736187fb72,b6985d33eedee09c870b6e178b34494802802d5d..a506a22b522a1c6836b0fcca04d7bf4441dec633
@@@ -147,9 -147,6 +147,9 @@@ static int btrfs_ioctl_setflags(struct 
        unsigned int flags, oldflags;
        int ret;
  
 +      if (btrfs_root_readonly(root))
 +              return -EROFS;
 +
        if (copy_from_user(&flags, arg, sizeof(flags)))
                return -EFAULT;
  
@@@ -363,8 -360,7 +363,8 @@@ fail
  }
  
  static int create_snapshot(struct btrfs_root *root, struct dentry *dentry,
 -                         char *name, int namelen, u64 *async_transid)
 +                         char *name, int namelen, u64 *async_transid,
 +                         bool readonly)
  {
        struct inode *inode;
        struct dentry *parent;
        btrfs_init_block_rsv(&pending_snapshot->block_rsv);
        pending_snapshot->dentry = dentry;
        pending_snapshot->root = root;
 +      pending_snapshot->readonly = readonly;
  
        trans = btrfs_start_transaction(root->fs_info->extent_root, 5);
        if (IS_ERR(trans)) {
@@@ -514,7 -509,7 +514,7 @@@ static inline int btrfs_may_create(stru
  static noinline int btrfs_mksubvol(struct path *parent,
                                   char *name, int namelen,
                                   struct btrfs_root *snap_src,
 -                                 u64 *async_transid)
 +                                 u64 *async_transid, bool readonly)
  {
        struct inode *dir  = parent->dentry->d_inode;
        struct dentry *dentry;
  
        if (snap_src) {
                error = create_snapshot(snap_src, dentry,
 -                                      name, namelen, async_transid);
 +                                      name, namelen, async_transid, readonly);
        } else {
                error = create_subvol(BTRFS_I(dir)->root, dentry,
                                      name, namelen, async_transid);
@@@ -643,9 -638,11 +643,11 @@@ static int btrfs_defrag_file(struct fil
        struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
        struct btrfs_ordered_extent *ordered;
        struct page *page;
+       struct btrfs_super_block *disk_super;
        unsigned long last_index;
        unsigned long ra_pages = root->fs_info->bdi.ra_pages;
        unsigned long total_read = 0;
+       u64 features;
        u64 page_start;
        u64 page_end;
        u64 last_len = 0;
        u64 defrag_end = 0;
        unsigned long i;
        int ret;
+       int compress_type = BTRFS_COMPRESS_ZLIB;
+       if (range->flags & BTRFS_DEFRAG_RANGE_COMPRESS) {
+               if (range->compress_type > BTRFS_COMPRESS_TYPES)
+                       return -EINVAL;
+               if (range->compress_type)
+                       compress_type = range->compress_type;
+       }
  
        if (inode->i_size == 0)
                return 0;
                total_read++;
                mutex_lock(&inode->i_mutex);
                if (range->flags & BTRFS_DEFRAG_RANGE_COMPRESS)
-                       BTRFS_I(inode)->force_compress = 1;
+                       BTRFS_I(inode)->force_compress = compress_type;
  
                ret  = btrfs_delalloc_reserve_space(inode, PAGE_CACHE_SIZE);
                if (ret)
@@@ -786,10 -791,17 +796,17 @@@ loop_unlock
                atomic_dec(&root->fs_info->async_submit_draining);
  
                mutex_lock(&inode->i_mutex);
-               BTRFS_I(inode)->force_compress = 0;
+               BTRFS_I(inode)->force_compress = BTRFS_COMPRESS_NONE;
                mutex_unlock(&inode->i_mutex);
        }
  
+       disk_super = &root->fs_info->super_copy;
+       features = btrfs_super_incompat_flags(disk_super);
+       if (range->compress_type == BTRFS_COMPRESS_LZO) {
+               features |= BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO;
+               btrfs_set_super_incompat_flags(disk_super, features);
+       }
        return 0;
  
  err_reservations:
@@@ -906,8 -918,7 +923,8 @@@ static noinline int btrfs_ioctl_snap_cr
                                                    char *name,
                                                    unsigned long fd,
                                                    int subvol,
 -                                                  u64 *transid)
 +                                                  u64 *transid,
 +                                                  bool readonly)
  {
        struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;
        struct file *src_file;
  
        if (subvol) {
                ret = btrfs_mksubvol(&file->f_path, name, namelen,
 -                                   NULL, transid);
 +                                   NULL, transid, readonly);
        } else {
                struct inode *src_inode;
                src_file = fget(fd);
                }
                ret = btrfs_mksubvol(&file->f_path, name, namelen,
                                     BTRFS_I(src_inode)->root,
 -                                   transid);
 +                                   transid, readonly);
                fput(src_file);
        }
  out:
  }
  
  static noinline int btrfs_ioctl_snap_create(struct file *file,
 -                                          void __user *arg, int subvol,
 -                                          int v2)
 +                                          void __user *arg, int subvol)
  {
 -      struct btrfs_ioctl_vol_args *vol_args = NULL;
 -      struct btrfs_ioctl_vol_args_v2 *vol_args_v2 = NULL;
 -      char *name;
 -      u64 fd;
 +      struct btrfs_ioctl_vol_args *vol_args;
        int ret;
  
 -      if (v2) {
 -              u64 transid = 0;
 -              u64 *ptr = NULL;
 -
 -              vol_args_v2 = memdup_user(arg, sizeof(*vol_args_v2));
 -              if (IS_ERR(vol_args_v2))
 -                      return PTR_ERR(vol_args_v2);
 +      vol_args = memdup_user(arg, sizeof(*vol_args));
 +      if (IS_ERR(vol_args))
 +              return PTR_ERR(vol_args);
 +      vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
  
 -              if (vol_args_v2->flags & ~BTRFS_SUBVOL_CREATE_ASYNC) {
 -                      ret = -EINVAL;
 -                      goto out;
 -              }
 +      ret = btrfs_ioctl_snap_create_transid(file, vol_args->name,
 +                                            vol_args->fd, subvol,
 +                                            NULL, false);
  
 -              name = vol_args_v2->name;
 -              fd = vol_args_v2->fd;
 -              vol_args_v2->name[BTRFS_SUBVOL_NAME_MAX] = '\0';
 +      kfree(vol_args);
 +      return ret;
 +}
  
 -              if (vol_args_v2->flags & BTRFS_SUBVOL_CREATE_ASYNC)
 -                      ptr = &transid;
 +static noinline int btrfs_ioctl_snap_create_v2(struct file *file,
 +                                             void __user *arg, int subvol)
 +{
 +      struct btrfs_ioctl_vol_args_v2 *vol_args;
 +      int ret;
 +      u64 transid = 0;
 +      u64 *ptr = NULL;
 +      bool readonly = false;
  
 -              ret = btrfs_ioctl_snap_create_transid(file, name, fd,
 -                                                    subvol, ptr);
 +      vol_args = memdup_user(arg, sizeof(*vol_args));
 +      if (IS_ERR(vol_args))
 +              return PTR_ERR(vol_args);
 +      vol_args->name[BTRFS_SUBVOL_NAME_MAX] = '\0';
  
 -              if (ret == 0 && ptr &&
 -                  copy_to_user(arg +
 -                               offsetof(struct btrfs_ioctl_vol_args_v2,
 -                                        transid), ptr, sizeof(*ptr)))
 -                      ret = -EFAULT;
 -      } else {
 -              vol_args = memdup_user(arg, sizeof(*vol_args));
 -              if (IS_ERR(vol_args))
 -                      return PTR_ERR(vol_args);
 -              name = vol_args->name;
 -              fd = vol_args->fd;
 -              vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
 -
 -              ret = btrfs_ioctl_snap_create_transid(file, name, fd,
 -                                                    subvol, NULL);
 +      if (vol_args->flags &
 +          ~(BTRFS_SUBVOL_CREATE_ASYNC | BTRFS_SUBVOL_RDONLY)) {
 +              ret = -EOPNOTSUPP;
 +              goto out;
        }
 +
 +      if (vol_args->flags & BTRFS_SUBVOL_CREATE_ASYNC)
 +              ptr = &transid;
 +      if (vol_args->flags & BTRFS_SUBVOL_RDONLY)
 +              readonly = true;
 +
 +      ret = btrfs_ioctl_snap_create_transid(file, vol_args->name,
 +                                            vol_args->fd, subvol,
 +                                            ptr, readonly);
 +
 +      if (ret == 0 && ptr &&
 +          copy_to_user(arg +
 +                       offsetof(struct btrfs_ioctl_vol_args_v2,
 +                                transid), ptr, sizeof(*ptr)))
 +              ret = -EFAULT;
  out:
        kfree(vol_args);
 -      kfree(vol_args_v2);
 +      return ret;
 +}
 +
 +static noinline int btrfs_ioctl_subvol_getflags(struct file *file,
 +                                              void __user *arg)
 +{
 +      struct inode *inode = fdentry(file)->d_inode;
 +      struct btrfs_root *root = BTRFS_I(inode)->root;
 +      int ret = 0;
 +      u64 flags = 0;
 +
 +      if (inode->i_ino != BTRFS_FIRST_FREE_OBJECTID)
 +              return -EINVAL;
 +
 +      down_read(&root->fs_info->subvol_sem);
 +      if (btrfs_root_readonly(root))
 +              flags |= BTRFS_SUBVOL_RDONLY;
 +      up_read(&root->fs_info->subvol_sem);
 +
 +      if (copy_to_user(arg, &flags, sizeof(flags)))
 +              ret = -EFAULT;
 +
 +      return ret;
 +}
 +
 +static noinline int btrfs_ioctl_subvol_setflags(struct file *file,
 +                                            void __user *arg)
 +{
 +      struct inode *inode = fdentry(file)->d_inode;
 +      struct btrfs_root *root = BTRFS_I(inode)->root;
 +      struct btrfs_trans_handle *trans;
 +      u64 root_flags;
 +      u64 flags;
 +      int ret = 0;
 +
 +      if (root->fs_info->sb->s_flags & MS_RDONLY)
 +              return -EROFS;
 +
 +      if (inode->i_ino != BTRFS_FIRST_FREE_OBJECTID)
 +              return -EINVAL;
 +
 +      if (copy_from_user(&flags, arg, sizeof(flags)))
 +              return -EFAULT;
 +
 +      if (flags & ~BTRFS_SUBVOL_CREATE_ASYNC)
 +              return -EINVAL;
 +
 +      if (flags & ~BTRFS_SUBVOL_RDONLY)
 +              return -EOPNOTSUPP;
 +
 +      down_write(&root->fs_info->subvol_sem);
 +
 +      /* nothing to do */
 +      if (!!(flags & BTRFS_SUBVOL_RDONLY) == btrfs_root_readonly(root))
 +              goto out;
 +
 +      root_flags = btrfs_root_flags(&root->root_item);
 +      if (flags & BTRFS_SUBVOL_RDONLY)
 +              btrfs_set_root_flags(&root->root_item,
 +                                   root_flags | BTRFS_ROOT_SUBVOL_RDONLY);
 +      else
 +              btrfs_set_root_flags(&root->root_item,
 +                                   root_flags & ~BTRFS_ROOT_SUBVOL_RDONLY);
 +
 +      trans = btrfs_start_transaction(root, 1);
 +      if (IS_ERR(trans)) {
 +              ret = PTR_ERR(trans);
 +              goto out_reset;
 +      }
  
 +      ret = btrfs_update_root(trans, root,
 +                              &root->root_key, &root->root_item);
 +
 +      btrfs_commit_transaction(trans, root);
 +out_reset:
 +      if (ret)
 +              btrfs_set_root_flags(&root->root_item, root_flags);
 +out:
 +      up_write(&root->fs_info->subvol_sem);
        return ret;
  }
  
@@@ -1596,9 -1526,6 +1613,9 @@@ static int btrfs_ioctl_defrag(struct fi
        struct btrfs_ioctl_defrag_range_args *range;
        int ret;
  
 +      if (btrfs_root_readonly(root))
 +              return -EROFS;
 +
        ret = mnt_want_write(file->f_path.mnt);
        if (ret)
                return ret;
@@@ -1727,9 -1654,6 +1744,9 @@@ static noinline long btrfs_ioctl_clone(
        if (!(file->f_mode & FMODE_WRITE) || (file->f_flags & O_APPEND))
                return -EINVAL;
  
 +      if (btrfs_root_readonly(root))
 +              return -EROFS;
 +
        ret = mnt_want_write(file->f_path.mnt);
        if (ret)
                return ret;
@@@ -2051,10 -1975,6 +2068,10 @@@ static long btrfs_ioctl_trans_start(str
        if (file->private_data)
                goto out;
  
 +      ret = -EROFS;
 +      if (btrfs_root_readonly(root))
 +              goto out;
 +
        ret = mnt_want_write(file->f_path.mnt);
        if (ret)
                goto out;
@@@ -2354,17 -2274,13 +2371,17 @@@ long btrfs_ioctl(struct file *file, uns
        case FS_IOC_GETVERSION:
                return btrfs_ioctl_getversion(file, argp);
        case BTRFS_IOC_SNAP_CREATE:
 -              return btrfs_ioctl_snap_create(file, argp, 0, 0);
 +              return btrfs_ioctl_snap_create(file, argp, 0);
        case BTRFS_IOC_SNAP_CREATE_V2:
 -              return btrfs_ioctl_snap_create(file, argp, 0, 1);
 +              return btrfs_ioctl_snap_create_v2(file, argp, 0);
        case BTRFS_IOC_SUBVOL_CREATE:
 -              return btrfs_ioctl_snap_create(file, argp, 1, 0);
 +              return btrfs_ioctl_snap_create(file, argp, 1);
        case BTRFS_IOC_SNAP_DESTROY:
                return btrfs_ioctl_snap_destroy(file, argp);
 +      case BTRFS_IOC_SUBVOL_GETFLAGS:
 +              return btrfs_ioctl_subvol_getflags(file, argp);
 +      case BTRFS_IOC_SUBVOL_SETFLAGS:
 +              return btrfs_ioctl_subvol_setflags(file, argp);
        case BTRFS_IOC_DEFAULT_SUBVOL:
                return btrfs_ioctl_default_subvol(file, argp);
        case BTRFS_IOC_DEFRAG:
diff --combined fs/btrfs/ioctl.h
index 1223223351fa08bcf1f38b31b8f1a3bb087ee65a,24d0f4628240d6101cc50c662ba6e4dfc91e43c7..8fb382167b13b55670e6411785f006defccc9d0d
@@@ -31,7 -31,6 +31,7 @@@ struct btrfs_ioctl_vol_args 
  };
  
  #define BTRFS_SUBVOL_CREATE_ASYNC     (1ULL << 0)
 +#define BTRFS_SUBVOL_RDONLY           (1ULL << 1)
  
  #define BTRFS_SUBVOL_NAME_MAX 4039
  struct btrfs_ioctl_vol_args_v2 {
@@@ -134,8 -133,15 +134,15 @@@ struct btrfs_ioctl_defrag_range_args 
         */
        __u32 extent_thresh;
  
+       /*
+        * which compression method to use if turning on compression
+        * for this defrag operation.  If unspecified, zlib will
+        * be used
+        */
+       __u32 compress_type;
        /* spare for later */
-       __u32 unused[5];
+       __u32 unused[4];
  };
  
  struct btrfs_ioctl_space_info {
@@@ -194,6 -200,4 +201,6 @@@ struct btrfs_ioctl_space_args 
  #define BTRFS_IOC_WAIT_SYNC  _IOW(BTRFS_IOCTL_MAGIC, 22, __u64)
  #define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \
                                   struct btrfs_ioctl_vol_args_v2)
 +#define BTRFS_IOC_SUBVOL_GETFLAGS _IOW(BTRFS_IOCTL_MAGIC, 25, __u64)
 +#define BTRFS_IOC_SUBVOL_SETFLAGS _IOW(BTRFS_IOCTL_MAGIC, 26, __u64)
  #endif