ext4: move check under lock scope to close a race.
[firefly-linux-kernel-4.4.55.git] / fs / ext4 / super.c
index 10e8c6b7ca08221bb1936da1f376c6b97e8a9d86..f9ebd58f40dda7128f335ce508df0bd5715c5d53 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/fs.h>
 #include <linux/time.h>
 #include <linux/vmalloc.h>
-#include <linux/jbd2.h>
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/blkdev.h>
@@ -323,22 +322,6 @@ static void save_error_info(struct super_block *sb, const char *func,
        ext4_commit_super(sb, 1);
 }
 
-/*
- * The del_gendisk() function uninitializes the disk-specific data
- * structures, including the bdi structure, without telling anyone
- * else.  Once this happens, any attempt to call mark_buffer_dirty()
- * (for example, by ext4_commit_super), will cause a kernel OOPS.
- * This is a kludge to prevent these oops until we can put in a proper
- * hook in del_gendisk() to inform the VFS and file system layers.
- */
-static int block_device_ejected(struct super_block *sb)
-{
-       struct inode *bd_inode = sb->s_bdev->bd_inode;
-       struct backing_dev_info *bdi = inode_to_bdi(bd_inode);
-
-       return bdi->dev == NULL;
-}
-
 static void ext4_journal_commit_callback(journal_t *journal, transaction_t *txn)
 {
        struct super_block              *sb = journal->j_private;
@@ -893,6 +876,9 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
        atomic_set(&ei->i_ioend_count, 0);
        atomic_set(&ei->i_unwritten, 0);
        INIT_WORK(&ei->i_rsv_conversion_work, ext4_end_io_rsv_work);
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+       ei->i_encryption_key.mode = EXT4_ENCRYPTION_MODE_INVALID;
+#endif
 
        return &ei->vfs_inode;
 }
@@ -1120,12 +1106,13 @@ enum {
        Opt_commit, Opt_min_batch_time, Opt_max_batch_time, Opt_journal_dev,
        Opt_journal_path, Opt_journal_checksum, Opt_journal_async_commit,
        Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback,
-       Opt_data_err_abort, Opt_data_err_ignore,
+       Opt_data_err_abort, Opt_data_err_ignore, Opt_test_dummy_encryption,
        Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota,
        Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_jqfmt_vfsv1, Opt_quota,
        Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err,
        Opt_usrquota, Opt_grpquota, Opt_i_version, Opt_dax,
        Opt_stripe, Opt_delalloc, Opt_nodelalloc, Opt_mblk_io_submit,
+       Opt_lazytime, Opt_nolazytime,
        Opt_nomblk_io_submit, Opt_block_validity, Opt_noblock_validity,
        Opt_inode_readahead_blks, Opt_journal_ioprio,
        Opt_dioread_nolock, Opt_dioread_lock,
@@ -1190,6 +1177,8 @@ static const match_table_t tokens = {
        {Opt_dax, "dax"},
        {Opt_stripe, "stripe=%u"},
        {Opt_delalloc, "delalloc"},
+       {Opt_lazytime, "lazytime"},
+       {Opt_nolazytime, "nolazytime"},
        {Opt_nodelalloc, "nodelalloc"},
        {Opt_removed, "mblk_io_submit"},
        {Opt_removed, "nomblk_io_submit"},
@@ -1208,6 +1197,7 @@ static const match_table_t tokens = {
        {Opt_init_itable, "init_itable"},
        {Opt_noinit_itable, "noinit_itable"},
        {Opt_max_dir_size_kb, "max_dir_size_kb=%u"},
+       {Opt_test_dummy_encryption, "test_dummy_encryption"},
        {Opt_removed, "check=none"},    /* mount option from ext2/3 */
        {Opt_removed, "nocheck"},       /* mount option from ext2/3 */
        {Opt_removed, "reservation"},   /* mount option from ext2/3 */
@@ -1409,6 +1399,7 @@ static const struct mount_opts {
        {Opt_jqfmt_vfsv0, QFMT_VFS_V0, MOPT_QFMT},
        {Opt_jqfmt_vfsv1, QFMT_VFS_V1, MOPT_QFMT},
        {Opt_max_dir_size_kb, 0, MOPT_GTE0},
+       {Opt_test_dummy_encryption, 0, MOPT_GTE0},
        {Opt_err, 0, 0}
 };
 
@@ -1448,6 +1439,12 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
        case Opt_i_version:
                sb->s_flags |= MS_I_VERSION;
                return 1;
+       case Opt_lazytime:
+               sb->s_flags |= MS_LAZYTIME;
+               return 1;
+       case Opt_nolazytime:
+               sb->s_flags &= ~MS_LAZYTIME;
+               return 1;
        }
 
        for (m = ext4_mount_opts; m->token != Opt_err; m++)
@@ -1579,6 +1576,15 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
                }
                *journal_ioprio =
                        IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, arg);
+       } else if (token == Opt_test_dummy_encryption) {
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+               sbi->s_mount_flags |= EXT4_MF_TEST_DUMMY_ENCRYPTION;
+               ext4_msg(sb, KERN_WARNING,
+                        "Test dummy encryption mode enabled");
+#else
+               ext4_msg(sb, KERN_WARNING,
+                        "Test dummy encryption mount option ignored");
+#endif
        } else if (m->flags & MOPT_DATAJ) {
                if (is_remount) {
                        if (!sbi->s_journal)
@@ -2676,11 +2682,13 @@ static struct attribute *ext4_attrs[] = {
 EXT4_INFO_ATTR(lazy_itable_init);
 EXT4_INFO_ATTR(batched_discard);
 EXT4_INFO_ATTR(meta_bg_resize);
+EXT4_INFO_ATTR(encryption);
 
 static struct attribute *ext4_feat_attrs[] = {
        ATTR_LIST(lazy_itable_init),
        ATTR_LIST(batched_discard),
        ATTR_LIST(meta_bg_resize),
+       ATTR_LIST(encryption),
        NULL,
 };
 
@@ -2770,6 +2778,12 @@ static int ext4_feature_set_ok(struct super_block *sb, int readonly)
        if (readonly)
                return 1;
 
+       if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_READONLY)) {
+               ext4_msg(sb, KERN_INFO, "filesystem is read-only");
+               sb->s_flags |= MS_RDONLY;
+               return 1;
+       }
+
        /* Check that feature set is OK for a read-write mount */
        if (EXT4_HAS_RO_COMPAT_FEATURE(sb, ~EXT4_FEATURE_RO_COMPAT_SUPP)) {
                ext4_msg(sb, KERN_ERR, "couldn't mount RDWR because of "
@@ -3433,6 +3447,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        if (sb->s_bdev->bd_part)
                sbi->s_sectors_written_start =
                        part_stat_read(sb->s_bdev->bd_part, sectors[1]);
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+       /* Modes of operations for file and directory encryption. */
+       sbi->s_file_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
+       sbi->s_dir_encryption_mode = EXT4_ENCRYPTION_MODE_INVALID;
+#endif
 
        /* Cleanup superblock name */
        for (cp = sb->s_id; (cp = strchr(cp, '/'));)
@@ -3677,6 +3696,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                }
        }
 
+       if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_ENCRYPT) &&
+           es->s_encryption_level) {
+               ext4_msg(sb, KERN_ERR, "Unsupported encryption level %d",
+                        es->s_encryption_level);
+               goto failed_mount;
+       }
+
        if (sb->s_blocksize != blocksize) {
                /* Validate the filesystem blocksize */
                if (!sb_set_blocksize(sb, blocksize)) {
@@ -3927,9 +3953,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        get_random_bytes(&sbi->s_next_generation, sizeof(u32));
        spin_lock_init(&sbi->s_next_gen_lock);
 
-       init_timer(&sbi->s_err_report);
-       sbi->s_err_report.function = print_daily_error_info;
-       sbi->s_err_report.data = (unsigned long) sb;
+       setup_timer(&sbi->s_err_report, print_daily_error_info,
+               (unsigned long) sb);
 
        /* Register extent status tree shrinker */
        if (ext4_es_register_shrinker(sbi))
@@ -4040,6 +4065,13 @@ no_journal:
                }
        }
 
+       if (unlikely(sbi->s_mount_flags & EXT4_MF_TEST_DUMMY_ENCRYPTION) &&
+           !(sb->s_flags & MS_RDONLY) &&
+           !EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_ENCRYPT)) {
+               EXT4_SET_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_ENCRYPT);
+               ext4_commit_super(sb, 1);
+       }
+
        /*
         * Get the # of file system overhead blocks from the
         * superblock if present.
@@ -4556,7 +4588,7 @@ static int ext4_commit_super(struct super_block *sb, int sync)
        struct buffer_head *sbh = EXT4_SB(sb)->s_sbh;
        int error = 0;
 
-       if (!sbh || block_device_ejected(sb))
+       if (!sbh)
                return error;
        if (buffer_write_io_error(sbh)) {
                /*
@@ -4857,9 +4889,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
        if (sbi->s_journal && sbi->s_journal->j_task->io_context)
                journal_ioprio = sbi->s_journal->j_task->io_context->ioprio;
 
-       /*
-        * Allow the "check" option to be passed as a remount option.
-        */
        if (!parse_options(data, sb, NULL, &journal_ioprio, 1)) {
                err = -EINVAL;
                goto restore_opts;
@@ -4868,17 +4897,8 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
        if ((old_opts.s_mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) ^
            test_opt(sb, JOURNAL_CHECKSUM)) {
                ext4_msg(sb, KERN_ERR, "changing journal_checksum "
-                        "during remount not supported");
-               err = -EINVAL;
-               goto restore_opts;
-       }
-
-       if ((old_opts.s_mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) ^
-           test_opt(sb, JOURNAL_CHECKSUM)) {
-               ext4_msg(sb, KERN_ERR, "changing journal_checksum "
-                        "during remount not supported");
-               err = -EINVAL;
-               goto restore_opts;
+                        "during remount not supported; ignoring");
+               sbi->s_mount_opt ^= EXT4_MOUNT_JOURNAL_CHECKSUM;
        }
 
        if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) {
@@ -4954,7 +4974,9 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
                                ext4_mark_recovery_complete(sb, es);
                } else {
                        /* Make sure we can mount this feature set readwrite */
-                       if (!ext4_feature_set_ok(sb, 0)) {
+                       if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                                       EXT4_FEATURE_RO_COMPAT_READONLY) ||
+                           !ext4_feature_set_ok(sb, 0)) {
                                err = -EROFS;
                                goto restore_opts;
                        }
@@ -5044,6 +5066,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
        }
 #endif
 
+       *flags = (*flags & ~MS_LAZYTIME) | (sb->s_flags & MS_LAZYTIME);
        ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s", orig_data);
        kfree(orig_data);
        return 0;