btrfs: Make btrfs handle security mount options internally to avoid losing security...
[cascardo/linux.git] / fs / btrfs / super.c
index c4124de..2250284 100644 (file)
@@ -60,6 +60,7 @@
 #include "backref.h"
 #include "tests/btrfs-tests.h"
 
+#include "qgroup.h"
 #define CREATE_TRACE_POINTS
 #include <trace/events/btrfs.h>
 
@@ -307,13 +308,7 @@ void __btrfs_panic(struct btrfs_fs_info *fs_info, const char *function,
 
 static void btrfs_put_super(struct super_block *sb)
 {
-       (void)close_ctree(btrfs_sb(sb)->tree_root);
-       /* FIXME: need to fix VFS to return error? */
-       /* AV: return it _where_?  ->put_super() can be triggered by any number
-        * of async events, up to and including delivery of SIGKILL to the
-        * last process that kept it busy.  Or segfault in the aforementioned
-        * process...  Whom would you report that to?
-        */
+       close_ctree(btrfs_sb(sb)->tree_root);
 }
 
 enum {
@@ -400,7 +395,6 @@ int btrfs_parse_options(struct btrfs_root *root, char *options)
        int ret = 0;
        char *compress_type;
        bool compress_force = false;
-       bool compress = false;
 
        cache_gen = btrfs_super_cache_generation(root->fs_info->super_copy);
        if (cache_gen)
@@ -478,7 +472,6 @@ int btrfs_parse_options(struct btrfs_root *root, char *options)
                        /* Fallthrough */
                case Opt_compress:
                case Opt_compress_type:
-                       compress = true;
                        if (token == Opt_compress ||
                            token == Opt_compress_force ||
                            strcmp(args[0].from, "zlib") == 0) {
@@ -508,11 +501,18 @@ int btrfs_parse_options(struct btrfs_root *root, char *options)
                                btrfs_set_and_info(root, FORCE_COMPRESS,
                                                   "force %s compression",
                                                   compress_type);
-                       } else if (compress) {
+                       } else {
                                if (!btrfs_test_opt(root, COMPRESS))
                                        btrfs_info(root->fs_info,
                                                   "btrfs: use %s compression",
                                                   compress_type);
+                               /*
+                                * If we remount from compress-force=xxx to
+                                * compress=xxx, we need clear FORCE_COMPRESS
+                                * flag, otherwise, there is no way for users
+                                * to disable forcible compression separately.
+                                */
+                               btrfs_clear_opt(info->mount_opt, FORCE_COMPRESS);
                        }
                        break;
                case Opt_ssd:
@@ -1014,7 +1014,7 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry)
                seq_puts(seq, ",nodatacow");
        if (btrfs_test_opt(root, NOBARRIER))
                seq_puts(seq, ",nobarrier");
-       if (info->max_inline != 8192 * 1024)
+       if (info->max_inline != BTRFS_DEFAULT_MAX_INLINE)
                seq_printf(seq, ",max_inline=%llu", info->max_inline);
        if (info->alloc_start != 0)
                seq_printf(seq, ",alloc_start=%llu", info->alloc_start);
@@ -1215,6 +1215,54 @@ static struct dentry *mount_subvol(const char *subvol_name, int flags,
        return root;
 }
 
+static int parse_security_options(char *orig_opts,
+                                 struct security_mnt_opts *sec_opts)
+{
+       char *secdata = NULL;
+       int ret = 0;
+
+       secdata = alloc_secdata();
+       if (!secdata)
+               return -ENOMEM;
+       ret = security_sb_copy_data(orig_opts, secdata);
+       if (ret) {
+               free_secdata(secdata);
+               return ret;
+       }
+       ret = security_sb_parse_opts_str(secdata, sec_opts);
+       free_secdata(secdata);
+       return ret;
+}
+
+static int setup_security_options(struct btrfs_fs_info *fs_info,
+                                 struct super_block *sb,
+                                 struct security_mnt_opts *sec_opts)
+{
+       int ret = 0;
+
+       /*
+        * Call security_sb_set_mnt_opts() to check whether new sec_opts
+        * is valid.
+        */
+       ret = security_sb_set_mnt_opts(sb, sec_opts, 0, NULL);
+       if (ret)
+               return ret;
+
+       if (!fs_info->security_opts.num_mnt_opts) {
+               /* first time security setup, copy sec_opts to fs_info */
+               memcpy(&fs_info->security_opts, sec_opts, sizeof(*sec_opts));
+       } else {
+               /*
+                * Since SELinux(the only one supports security_mnt_opts) does
+                * NOT support changing context during remount/mount same sb,
+                * This must be the same or part of the same security options,
+                * just free it.
+                */
+               security_free_mnt_opts(sec_opts);
+       }
+       return ret;
+}
+
 /*
  * Find a superblock for the given device / mount point.
  *
@@ -1229,6 +1277,7 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
        struct dentry *root;
        struct btrfs_fs_devices *fs_devices = NULL;
        struct btrfs_fs_info *fs_info = NULL;
+       struct security_mnt_opts new_sec_opts;
        fmode_t mode = FMODE_READ;
        char *subvol_name = NULL;
        u64 subvol_objectid = 0;
@@ -1251,9 +1300,16 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
                return root;
        }
 
+       security_init_mnt_opts(&new_sec_opts);
+       if (data) {
+               error = parse_security_options(data, &new_sec_opts);
+               if (error)
+                       return ERR_PTR(error);
+       }
+
        error = btrfs_scan_one_device(device_name, mode, fs_type, &fs_devices);
        if (error)
-               return ERR_PTR(error);
+               goto error_sec_opts;
 
        /*
         * Setup a dummy root and fs_info for test/set super.  This is because
@@ -1262,13 +1318,16 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
         * then open_ctree will properly initialize everything later.
         */
        fs_info = kzalloc(sizeof(struct btrfs_fs_info), GFP_NOFS);
-       if (!fs_info)
-               return ERR_PTR(-ENOMEM);
+       if (!fs_info) {
+               error = -ENOMEM;
+               goto error_sec_opts;
+       }
 
        fs_info->fs_devices = fs_devices;
 
        fs_info->super_copy = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_NOFS);
        fs_info->super_for_commit = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_NOFS);
+       security_init_mnt_opts(&fs_info->security_opts);
        if (!fs_info->super_copy || !fs_info->super_for_commit) {
                error = -ENOMEM;
                goto error_fs_info;
@@ -1306,8 +1365,19 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
        }
 
        root = !error ? get_default_root(s, subvol_objectid) : ERR_PTR(error);
-       if (IS_ERR(root))
+       if (IS_ERR(root)) {
+               deactivate_locked_super(s);
+               error = PTR_ERR(root);
+               goto error_sec_opts;
+       }
+
+       fs_info = btrfs_sb(s);
+       error = setup_security_options(fs_info, s, &new_sec_opts);
+       if (error) {
+               dput(root);
                deactivate_locked_super(s);
+               goto error_sec_opts;
+       }
 
        return root;
 
@@ -1315,6 +1385,8 @@ error_close_devices:
        btrfs_close_devices(fs_devices);
 error_fs_info:
        free_fs_info(fs_info);
+error_sec_opts:
+       security_free_mnt_opts(&new_sec_opts);
        return ERR_PTR(error);
 }
 
@@ -1396,6 +1468,21 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
        sync_filesystem(sb);
        btrfs_remount_prepare(fs_info);
 
+       if (data) {
+               struct security_mnt_opts new_sec_opts;
+
+               security_init_mnt_opts(&new_sec_opts);
+               ret = parse_security_options(data, &new_sec_opts);
+               if (ret)
+                       goto restore;
+               ret = setup_security_options(fs_info, sb,
+                                            &new_sec_opts);
+               if (ret) {
+                       security_free_mnt_opts(&new_sec_opts);
+                       goto restore;
+               }
+       }
+
        ret = btrfs_parse_options(root, data);
        if (ret) {
                ret = -EINVAL;
@@ -1694,7 +1781,11 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf)
        struct btrfs_block_rsv *block_rsv = &fs_info->global_block_rsv;
        int ret;
 
-       /* holding chunk_muext to avoid allocating new chunks */
+       /*
+        * holding chunk_muext to avoid allocating new chunks, holding
+        * device_list_mutex to avoid the device being removed
+        */
+       mutex_lock(&fs_info->fs_devices->device_list_mutex);
        mutex_lock(&fs_info->chunk_mutex);
        rcu_read_lock();
        list_for_each_entry_rcu(found, head, list) {
@@ -1735,11 +1826,13 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf)
        ret = btrfs_calc_avail_data_space(fs_info->tree_root, &total_free_data);
        if (ret) {
                mutex_unlock(&fs_info->chunk_mutex);
+               mutex_unlock(&fs_info->fs_devices->device_list_mutex);
                return ret;
        }
        buf->f_bavail += div_u64(total_free_data, factor);
        buf->f_bavail = buf->f_bavail >> bits;
        mutex_unlock(&fs_info->chunk_mutex);
+       mutex_unlock(&fs_info->fs_devices->device_list_mutex);
 
        buf->f_type = BTRFS_SUPER_MAGIC;
        buf->f_bsize = dentry->d_sb->s_blocksize;
@@ -1769,7 +1862,7 @@ static struct file_system_type btrfs_fs_type = {
        .name           = "btrfs",
        .mount          = btrfs_mount,
        .kill_sb        = btrfs_kill_super,
-       .fs_flags       = FS_REQUIRES_DEV,
+       .fs_flags       = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA,
 };
 MODULE_ALIAS_FS("btrfs");
 
@@ -1992,12 +2085,16 @@ static int __init init_btrfs_fs(void)
                goto free_auto_defrag;
 
        err = btrfs_prelim_ref_init();
+       if (err)
+               goto free_delayed_ref;
+
+       err = btrfs_end_io_wq_init();
        if (err)
                goto free_prelim_ref;
 
        err = btrfs_interface_init();
        if (err)
-               goto free_delayed_ref;
+               goto free_end_io_wq;
 
        btrfs_init_lockdep();
 
@@ -2015,6 +2112,8 @@ static int __init init_btrfs_fs(void)
 
 unregister_ioctl:
        btrfs_interface_exit();
+free_end_io_wq:
+       btrfs_end_io_wq_exit();
 free_prelim_ref:
        btrfs_prelim_ref_exit();
 free_delayed_ref: