Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/fs/ext4/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 207 kB image not shown  

Quellcode-Bibliothek super.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 *  linux/fs/ext4/super.c
 *
 * Copyright (C) 1992, 1993, 1994, 1995
 * Remy Card (card@masi.ibp.fr)
 * Laboratoire MASI - Institut Blaise Pascal
 * Universite Pierre et Marie Curie (Paris VI)
 *
 *  from
 *
 *  linux/fs/minix/inode.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 *
 *  Big-endian to little-endian byte-swapping/bitmaps by
 *        David S. Miller (davem@caip.rutgers.edu), 1995
 */


#include <linux/module.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/time.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/blkdev.h>
#include <linux/backing-dev.h>
#include <linux/parser.h>
#include <linux/buffer_head.h>
#include <linux/exportfs.h>
#include <linux/vfs.h>
#include <linux/random.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/quotaops.h>
#include <linux/seq_file.h>
#include <linux/ctype.h>
#include <linux/log2.h>
#include <linux/crc16.h>
#include <linux/dax.h>
#include <linux/uaccess.h>
#include <linux/iversion.h>
#include <linux/unicode.h>
#include <linux/part_stat.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/fsnotify.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>

#include "ext4.h"
#include "ext4_extents.h" /* Needed for trace points definition */
#include "ext4_jbd2.h"
#include "xattr.h"
#include "acl.h"
#include "mballoc.h"
#include "fsmap.h"

#define CREATE_TRACE_POINTS
#include <trace/events/ext4.h>

static struct ext4_lazy_init *ext4_li_info;
static DEFINE_MUTEX(ext4_li_mtx);
static struct ratelimit_state ext4_mount_msg_ratelimit;

static int ext4_load_journal(struct super_block *, struct ext4_super_block *,
        unsigned long journal_devnum);
static int ext4_show_options(struct seq_file *seq, struct dentry *root);
static void ext4_update_super(struct super_block *sb);
static int ext4_commit_super(struct super_block *sb);
static int ext4_mark_recovery_complete(struct super_block *sb,
     struct ext4_super_block *es);
static int ext4_clear_journal_err(struct super_block *sb,
      struct ext4_super_block *es);
static int ext4_sync_fs(struct super_block *sb, int wait);
static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf);
static int ext4_unfreeze(struct super_block *sb);
static int ext4_freeze(struct super_block *sb);
static inline int ext2_feature_set_ok(struct super_block *sb);
static inline int ext3_feature_set_ok(struct super_block *sb);
static void ext4_unregister_li_request(struct super_block *sb);
static void ext4_clear_request_list(void);
static struct inode *ext4_get_journal_inode(struct super_block *sb,
         unsigned int journal_inum);
static int ext4_validate_options(struct fs_context *fc);
static int ext4_check_opt_consistency(struct fs_context *fc,
          struct super_block *sb);
static void ext4_apply_options(struct fs_context *fc, struct super_block *sb);
static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param);
static int ext4_get_tree(struct fs_context *fc);
static int ext4_reconfigure(struct fs_context *fc);
static void ext4_fc_free(struct fs_context *fc);
static int ext4_init_fs_context(struct fs_context *fc);
static void ext4_kill_sb(struct super_block *sb);
static const struct fs_parameter_spec ext4_param_specs[];

/*
 * Lock ordering
 *
 * page fault path:
 * mmap_lock -> sb_start_pagefault -> invalidate_lock (r) -> transaction start
 *   -> page lock -> i_data_sem (rw)
 *
 * buffered write path:
 * sb_start_write -> i_mutex -> mmap_lock
 * sb_start_write -> i_mutex -> transaction start -> page lock ->
 *   i_data_sem (rw)
 *
 * truncate:
 * sb_start_write -> i_mutex -> invalidate_lock (w) -> i_mmap_rwsem (w) ->
 *   page lock
 * sb_start_write -> i_mutex -> invalidate_lock (w) -> transaction start ->
 *   i_data_sem (rw)
 *
 * direct IO:
 * sb_start_write -> i_mutex -> mmap_lock
 * sb_start_write -> i_mutex -> transaction start -> i_data_sem (rw)
 *
 * writepages:
 * transaction start -> page lock(s) -> i_data_sem (rw)
 */


static const struct fs_context_operations ext4_context_ops = {
 .parse_param = ext4_parse_param,
 .get_tree = ext4_get_tree,
 .reconfigure = ext4_reconfigure,
 .free  = ext4_fc_free,
};


#if !defined(CONFIG_EXT2_FS) && !defined(CONFIG_EXT2_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT2)
static struct file_system_type ext2_fs_type = {
 .owner   = THIS_MODULE,
 .name   = "ext2",
 .init_fs_context = ext4_init_fs_context,
 .parameters  = ext4_param_specs,
 .kill_sb  = ext4_kill_sb,
 .fs_flags  = FS_REQUIRES_DEV,
};
MODULE_ALIAS_FS("ext2");
MODULE_ALIAS("ext2");
#define IS_EXT2_SB(sb) ((sb)->s_type == &ext2_fs_type)
#else
#define IS_EXT2_SB(sb) (0)
#endif


static struct file_system_type ext3_fs_type = {
 .owner   = THIS_MODULE,
 .name   = "ext3",
 .init_fs_context = ext4_init_fs_context,
 .parameters  = ext4_param_specs,
 .kill_sb  = ext4_kill_sb,
 .fs_flags  = FS_REQUIRES_DEV,
};
MODULE_ALIAS_FS("ext3");
MODULE_ALIAS("ext3");
#define IS_EXT3_SB(sb) ((sb)->s_type == &ext3_fs_type)


static inline void __ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
      bh_end_io_t *end_io, bool simu_fail)
{
 if (simu_fail) {
  clear_buffer_uptodate(bh);
  unlock_buffer(bh);
  return;
 }

 /*
 * buffer's verified bit is no longer valid after reading from
 * disk again due to write out error, clear it to make sure we
 * recheck the buffer contents.
 */

 clear_buffer_verified(bh);

 bh->b_end_io = end_io ? end_io : end_buffer_read_sync;
 get_bh(bh);
 submit_bh(REQ_OP_READ | op_flags, bh);
}

void ext4_read_bh_nowait(struct buffer_head *bh, blk_opf_t op_flags,
    bh_end_io_t *end_io, bool simu_fail)
{
 BUG_ON(!buffer_locked(bh));

 if (ext4_buffer_uptodate(bh)) {
  unlock_buffer(bh);
  return;
 }
 __ext4_read_bh(bh, op_flags, end_io, simu_fail);
}

int ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
   bh_end_io_t *end_io, bool simu_fail)
{
 BUG_ON(!buffer_locked(bh));

 if (ext4_buffer_uptodate(bh)) {
  unlock_buffer(bh);
  return 0;
 }

 __ext4_read_bh(bh, op_flags, end_io, simu_fail);

 wait_on_buffer(bh);
 if (buffer_uptodate(bh))
  return 0;
 return -EIO;
}

int ext4_read_bh_lock(struct buffer_head *bh, blk_opf_t op_flags, bool wait)
{
 lock_buffer(bh);
 if (!wait) {
  ext4_read_bh_nowait(bh, op_flags, NULL, false);
  return 0;
 }
 return ext4_read_bh(bh, op_flags, NULL, false);
}

/*
 * This works like __bread_gfp() except it uses ERR_PTR for error
 * returns.  Currently with sb_bread it's impossible to distinguish
 * between ENOMEM and EIO situations (since both result in a NULL
 * return.
 */

static struct buffer_head *__ext4_sb_bread_gfp(struct super_block *sb,
            sector_t block,
            blk_opf_t op_flags, gfp_t gfp)
{
 struct buffer_head *bh;
 int ret;

 bh = sb_getblk_gfp(sb, block, gfp);
 if (bh == NULL)
  return ERR_PTR(-ENOMEM);
 if (ext4_buffer_uptodate(bh))
  return bh;

 ret = ext4_read_bh_lock(bh, REQ_META | op_flags, true);
 if (ret) {
  put_bh(bh);
  return ERR_PTR(ret);
 }
 return bh;
}

struct buffer_head *ext4_sb_bread(struct super_block *sb, sector_t block,
       blk_opf_t op_flags)
{
 gfp_t gfp = mapping_gfp_constraint(sb->s_bdev->bd_mapping,
   ~__GFP_FS) | __GFP_MOVABLE;

 return __ext4_sb_bread_gfp(sb, block, op_flags, gfp);
}

struct buffer_head *ext4_sb_bread_unmovable(struct super_block *sb,
         sector_t block)
{
 gfp_t gfp = mapping_gfp_constraint(sb->s_bdev->bd_mapping,
   ~__GFP_FS);

 return __ext4_sb_bread_gfp(sb, block, 0, gfp);
}

struct buffer_head *ext4_sb_bread_nofail(struct super_block *sb,
      sector_t block)
{
 gfp_t gfp = mapping_gfp_constraint(sb->s_bdev->bd_mapping,
   ~__GFP_FS) | __GFP_MOVABLE | __GFP_NOFAIL;

 return __ext4_sb_bread_gfp(sb, block, 0, gfp);
}

void ext4_sb_breadahead_unmovable(struct super_block *sb, sector_t block)
{
 struct buffer_head *bh = bdev_getblk(sb->s_bdev, block,
   sb->s_blocksize, GFP_NOWAIT);

 if (likely(bh)) {
  if (trylock_buffer(bh))
   ext4_read_bh_nowait(bh, REQ_RAHEAD, NULL, false);
  brelse(bh);
 }
}

static int ext4_verify_csum_type(struct super_block *sb,
     struct ext4_super_block *es)
{
 if (!ext4_has_feature_metadata_csum(sb))
  return 1;

 return es->s_checksum_type == EXT4_CRC32C_CHKSUM;
}

__le32 ext4_superblock_csum(struct ext4_super_block *es)
{
 int offset = offsetof(struct ext4_super_block, s_checksum);
 __u32 csum;

 csum = ext4_chksum(~0, (char *)es, offset);

 return cpu_to_le32(csum);
}

static int ext4_superblock_csum_verify(struct super_block *sb,
           struct ext4_super_block *es)
{
 if (!ext4_has_feature_metadata_csum(sb))
  return 1;

 return es->s_checksum == ext4_superblock_csum(es);
}

void ext4_superblock_csum_set(struct super_block *sb)
{
 struct ext4_super_block *es = EXT4_SB(sb)->s_es;

 if (!ext4_has_feature_metadata_csum(sb))
  return;

 es->s_checksum = ext4_superblock_csum(es);
}

ext4_fsblk_t ext4_block_bitmap(struct super_block *sb,
          struct ext4_group_desc *bg)
{
 return le32_to_cpu(bg->bg_block_bitmap_lo) |
  (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ?
   (ext4_fsblk_t)le32_to_cpu(bg->bg_block_bitmap_hi) << 32 : 0);
}

ext4_fsblk_t ext4_inode_bitmap(struct super_block *sb,
          struct ext4_group_desc *bg)
{
 return le32_to_cpu(bg->bg_inode_bitmap_lo) |
  (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ?
   (ext4_fsblk_t)le32_to_cpu(bg->bg_inode_bitmap_hi) << 32 : 0);
}

ext4_fsblk_t ext4_inode_table(struct super_block *sb,
         struct ext4_group_desc *bg)
{
 return le32_to_cpu(bg->bg_inode_table_lo) |
  (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ?
   (ext4_fsblk_t)le32_to_cpu(bg->bg_inode_table_hi) << 32 : 0);
}

__u32 ext4_free_group_clusters(struct super_block *sb,
          struct ext4_group_desc *bg)
{
 return le16_to_cpu(bg->bg_free_blocks_count_lo) |
  (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ?
   (__u32)le16_to_cpu(bg->bg_free_blocks_count_hi) << 16 : 0);
}

__u32 ext4_free_inodes_count(struct super_block *sb,
         struct ext4_group_desc *bg)
{
 return le16_to_cpu(READ_ONCE(bg->bg_free_inodes_count_lo)) |
  (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ?
   (__u32)le16_to_cpu(READ_ONCE(bg->bg_free_inodes_count_hi)) << 16 : 0);
}

__u32 ext4_used_dirs_count(struct super_block *sb,
         struct ext4_group_desc *bg)
{
 return le16_to_cpu(bg->bg_used_dirs_count_lo) |
  (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ?
   (__u32)le16_to_cpu(bg->bg_used_dirs_count_hi) << 16 : 0);
}

__u32 ext4_itable_unused_count(struct super_block *sb,
         struct ext4_group_desc *bg)
{
 return le16_to_cpu(bg->bg_itable_unused_lo) |
  (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ?
   (__u32)le16_to_cpu(bg->bg_itable_unused_hi) << 16 : 0);
}

void ext4_block_bitmap_set(struct super_block *sb,
      struct ext4_group_desc *bg, ext4_fsblk_t blk)
{
 bg->bg_block_bitmap_lo = cpu_to_le32((u32)blk);
 if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT)
  bg->bg_block_bitmap_hi = cpu_to_le32(blk >> 32);
}

void ext4_inode_bitmap_set(struct super_block *sb,
      struct ext4_group_desc *bg, ext4_fsblk_t blk)
{
 bg->bg_inode_bitmap_lo  = cpu_to_le32((u32)blk);
 if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT)
  bg->bg_inode_bitmap_hi = cpu_to_le32(blk >> 32);
}

void ext4_inode_table_set(struct super_block *sb,
     struct ext4_group_desc *bg, ext4_fsblk_t blk)
{
 bg->bg_inode_table_lo = cpu_to_le32((u32)blk);
 if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT)
  bg->bg_inode_table_hi = cpu_to_le32(blk >> 32);
}

void ext4_free_group_clusters_set(struct super_block *sb,
      struct ext4_group_desc *bg, __u32 count)
{
 bg->bg_free_blocks_count_lo = cpu_to_le16((__u16)count);
 if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT)
  bg->bg_free_blocks_count_hi = cpu_to_le16(count >> 16);
}

void ext4_free_inodes_set(struct super_block *sb,
     struct ext4_group_desc *bg, __u32 count)
{
 WRITE_ONCE(bg->bg_free_inodes_count_lo, cpu_to_le16((__u16)count));
 if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT)
  WRITE_ONCE(bg->bg_free_inodes_count_hi, cpu_to_le16(count >> 16));
}

void ext4_used_dirs_set(struct super_block *sb,
     struct ext4_group_desc *bg, __u32 count)
{
 bg->bg_used_dirs_count_lo = cpu_to_le16((__u16)count);
 if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT)
  bg->bg_used_dirs_count_hi = cpu_to_le16(count >> 16);
}

void ext4_itable_unused_set(struct super_block *sb,
     struct ext4_group_desc *bg, __u32 count)
{
 bg->bg_itable_unused_lo = cpu_to_le16((__u16)count);
 if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT)
  bg->bg_itable_unused_hi = cpu_to_le16(count >> 16);
}

static void __ext4_update_tstamp(__le32 *lo, __u8 *hi, time64_t now)
{
 now = clamp_val(now, 0, (1ull << 40) - 1);

 *lo = cpu_to_le32(lower_32_bits(now));
 *hi = upper_32_bits(now);
}

static time64_t __ext4_get_tstamp(__le32 *lo, __u8 *hi)
{
 return ((time64_t)(*hi) << 32) + le32_to_cpu(*lo);
}
#define ext4_update_tstamp(es, tstamp) \
 __ext4_update_tstamp(&(es)->tstamp, &(es)->tstamp ## _hi, \
        ktime_get_real_seconds())
#define ext4_get_tstamp(es, tstamp) \
 __ext4_get_tstamp(&(es)->tstamp, &(es)->tstamp ## _hi)

/*
 * The ext4_maybe_update_superblock() function checks and updates the
 * superblock if needed.
 *
 * This function is designed to update the on-disk superblock only under
 * certain conditions to prevent excessive disk writes and unnecessary
 * waking of the disk from sleep. The superblock will be updated if:
 * 1. More than sbi->s_sb_update_sec (def: 1 hour) has passed since the last
 *    superblock update
 * 2. More than sbi->s_sb_update_kb (def: 16MB) kbs have been written since the
 *    last superblock update.
 *
 * @sb: The superblock
 */

static void ext4_maybe_update_superblock(struct super_block *sb)
{
 struct ext4_sb_info *sbi = EXT4_SB(sb);
 struct ext4_super_block *es = sbi->s_es;
 journal_t *journal = sbi->s_journal;
 time64_t now;
 __u64 last_update;
 __u64 lifetime_write_kbytes;
 __u64 diff_size;

 if (ext4_emergency_state(sb) || sb_rdonly(sb) ||
     !(sb->s_flags & SB_ACTIVE) || !journal ||
     journal->j_flags & JBD2_UNMOUNT)
  return;

 now = ktime_get_real_seconds();
 last_update = ext4_get_tstamp(es, s_wtime);

 if (likely(now - last_update < sbi->s_sb_update_sec))
  return;

 lifetime_write_kbytes = sbi->s_kbytes_written +
  ((part_stat_read(sb->s_bdev, sectors[STAT_WRITE]) -
    sbi->s_sectors_written_start) >> 1);

 /* Get the number of kilobytes not written to disk to account
 * for statistics and compare with a multiple of 16 MB. This
 * is used to determine when the next superblock commit should
 * occur (i.e. not more often than once per 16MB if there was
 * less written in an hour).
 */

 diff_size = lifetime_write_kbytes - le64_to_cpu(es->s_kbytes_written);

 if (diff_size > sbi->s_sb_update_kb)
  schedule_work(&EXT4_SB(sb)->s_sb_upd_work);
}

static void ext4_journal_commit_callback(journal_t *journal, transaction_t *txn)
{
 struct super_block  *sb = journal->j_private;

 BUG_ON(txn->t_state == T_FINISHED);

 ext4_process_freed_data(sb, txn->t_tid);
 ext4_maybe_update_superblock(sb);
}

static bool ext4_journalled_writepage_needs_redirty(struct jbd2_inode *jinode,
  struct folio *folio)
{
 struct buffer_head *bh, *head;
 struct journal_head *jh;

 bh = head = folio_buffers(folio);
 do {
  /*
 * We have to redirty a page in these cases:
 * 1) If buffer is dirty, it means the page was dirty because it
 * contains a buffer that needs checkpointing. So the dirty bit
 * needs to be preserved so that checkpointing writes the buffer
 * properly.
 * 2) If buffer is not part of the committing transaction
 * (we may have just accidentally come across this buffer because
 * inode range tracking is not exact) or if the currently running
 * transaction already contains this buffer as well, dirty bit
 * needs to be preserved so that the buffer gets writeprotected
 * properly on running transaction's commit.
 */

  jh = bh2jh(bh);
  if (buffer_dirty(bh) ||
      (jh && (jh->b_transaction != jinode->i_transaction ||
       jh->b_next_transaction)))
   return true;
 } while ((bh = bh->b_this_page) != head);

 return false;
}

static int ext4_journalled_submit_inode_data_buffers(struct jbd2_inode *jinode)
{
 struct address_space *mapping = jinode->i_vfs_inode->i_mapping;
 struct writeback_control wbc = {
  .sync_mode =  WB_SYNC_ALL,
  .nr_to_write = LONG_MAX,
  .range_start = jinode->i_dirty_start,
  .range_end = jinode->i_dirty_end,
        };
 struct folio *folio = NULL;
 int error;

 /*
 * writeback_iter() already checks for dirty pages and calls
 * folio_clear_dirty_for_io(), which we want to write protect the
 * folios.
 *
 * However, we may have to redirty a folio sometimes.
 */

 while ((folio = writeback_iter(mapping, &wbc, folio, &error))) {
  if (ext4_journalled_writepage_needs_redirty(jinode, folio))
   folio_redirty_for_writepage(&wbc, folio);
  folio_unlock(folio);
 }

 return error;
}

static int ext4_journal_submit_inode_data_buffers(struct jbd2_inode *jinode)
{
 int ret;

 if (ext4_should_journal_data(jinode->i_vfs_inode))
  ret = ext4_journalled_submit_inode_data_buffers(jinode);
 else
  ret = ext4_normal_submit_inode_data_buffers(jinode);
 return ret;
}

static int ext4_journal_finish_inode_data_buffers(struct jbd2_inode *jinode)
{
 int ret = 0;

 if (!ext4_should_journal_data(jinode->i_vfs_inode))
  ret = jbd2_journal_finish_inode_data_buffers(jinode);

 return ret;
}

static bool system_going_down(void)
{
 return system_state == SYSTEM_HALT || system_state == SYSTEM_POWER_OFF
  || system_state == SYSTEM_RESTART;
}

struct ext4_err_translation {
 int code;
 int errno;
};

#define EXT4_ERR_TRANSLATE(err) { .code = EXT4_ERR_##err, .errno = err }

static struct ext4_err_translation err_translation[] = {
 EXT4_ERR_TRANSLATE(EIO),
 EXT4_ERR_TRANSLATE(ENOMEM),
 EXT4_ERR_TRANSLATE(EFSBADCRC),
 EXT4_ERR_TRANSLATE(EFSCORRUPTED),
 EXT4_ERR_TRANSLATE(ENOSPC),
 EXT4_ERR_TRANSLATE(ENOKEY),
 EXT4_ERR_TRANSLATE(EROFS),
 EXT4_ERR_TRANSLATE(EFBIG),
 EXT4_ERR_TRANSLATE(EEXIST),
 EXT4_ERR_TRANSLATE(ERANGE),
 EXT4_ERR_TRANSLATE(EOVERFLOW),
 EXT4_ERR_TRANSLATE(EBUSY),
 EXT4_ERR_TRANSLATE(ENOTDIR),
 EXT4_ERR_TRANSLATE(ENOTEMPTY),
 EXT4_ERR_TRANSLATE(ESHUTDOWN),
 EXT4_ERR_TRANSLATE(EFAULT),
};

static int ext4_errno_to_code(int errno)
{
 int i;

 for (i = 0; i < ARRAY_SIZE(err_translation); i++)
  if (err_translation[i].errno == errno)
   return err_translation[i].code;
 return EXT4_ERR_UNKNOWN;
}

static void save_error_info(struct super_block *sb, int error,
       __u32 ino, __u64 block,
       const char *func, unsigned int line)
{
 struct ext4_sb_info *sbi = EXT4_SB(sb);

 /* We default to EFSCORRUPTED error... */
 if (error == 0)
  error = EFSCORRUPTED;

 spin_lock(&sbi->s_error_lock);
 sbi->s_add_error_count++;
 sbi->s_last_error_code = error;
 sbi->s_last_error_line = line;
 sbi->s_last_error_ino = ino;
 sbi->s_last_error_block = block;
 sbi->s_last_error_func = func;
 sbi->s_last_error_time = ktime_get_real_seconds();
 if (!sbi->s_first_error_time) {
  sbi->s_first_error_code = error;
  sbi->s_first_error_line = line;
  sbi->s_first_error_ino = ino;
  sbi->s_first_error_block = block;
  sbi->s_first_error_func = func;
  sbi->s_first_error_time = sbi->s_last_error_time;
 }
 spin_unlock(&sbi->s_error_lock);
}

/* Deal with the reporting of failure conditions on a filesystem such as
 * inconsistencies detected or read IO failures.
 *
 * On ext2, we can store the error state of the filesystem in the
 * superblock.  That is not possible on ext4, because we may have other
 * write ordering constraints on the superblock which prevent us from
 * writing it out straight away; and given that the journal is about to
 * be aborted, we can't rely on the current, or future, transactions to
 * write out the superblock safely.
 *
 * We'll just use the jbd2_journal_abort() error code to record an error in
 * the journal instead.  On recovery, the journal will complain about
 * that error until we've noted it down and cleared it.
 *
 * If force_ro is set, we unconditionally force the filesystem into an
 * ABORT|READONLY state, unless the error response on the fs has been set to
 * panic in which case we take the easy way out and panic immediately. This is
 * used to deal with unrecoverable failures such as journal IO errors or ENOMEM
 * at a critical moment in log management.
 */

static void ext4_handle_error(struct super_block *sb, bool force_ro, int error,
         __u32 ino, __u64 block,
         const char *func, unsigned int line)
{
 journal_t *journal = EXT4_SB(sb)->s_journal;
 bool continue_fs = !force_ro && test_opt(sb, ERRORS_CONT);

 EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
 if (test_opt(sb, WARN_ON_ERROR))
  WARN_ON_ONCE(1);

 if (!continue_fs && !ext4_emergency_ro(sb) && journal)
  jbd2_journal_abort(journal, -EIO);

 if (!bdev_read_only(sb->s_bdev)) {
  save_error_info(sb, error, ino, block, func, line);
  /*
 * In case the fs should keep running, we need to writeout
 * superblock through the journal. Due to lock ordering
 * constraints, it may not be safe to do it right here so we
 * defer superblock flushing to a workqueue. We just need to be
 * careful when the journal is already shutting down. If we get
 * here in that case, just update the sb directly as the last
 * transaction won't commit anyway.
 */

  if (continue_fs && journal &&
      !ext4_test_mount_flag(sb, EXT4_MF_JOURNAL_DESTROY))
   schedule_work(&EXT4_SB(sb)->s_sb_upd_work);
  else
   ext4_commit_super(sb);
 }

 /*
 * We force ERRORS_RO behavior when system is rebooting. Otherwise we
 * could panic during 'reboot -f' as the underlying device got already
 * disabled.
 */

 if (test_opt(sb, ERRORS_PANIC) && !system_going_down()) {
  panic("EXT4-fs (device %s): panic forced after error\n",
   sb->s_id);
 }

 if (ext4_emergency_ro(sb) || continue_fs)
  return;

 ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
 /*
 * We don't set SB_RDONLY because that requires sb->s_umount
 * semaphore and setting it without proper remount procedure is
 * confusing code such as freeze_super() leading to deadlocks
 * and other problems.
 */

 set_bit(EXT4_FLAGS_EMERGENCY_RO, &EXT4_SB(sb)->s_ext4_flags);
}

static void update_super_work(struct work_struct *work)
{
 struct ext4_sb_info *sbi = container_of(work, struct ext4_sb_info,
      s_sb_upd_work);
 journal_t *journal = sbi->s_journal;
 handle_t *handle;

 /*
 * If the journal is still running, we have to write out superblock
 * through the journal to avoid collisions of other journalled sb
 * updates.
 *
 * We use directly jbd2 functions here to avoid recursing back into
 * ext4 error handling code during handling of previous errors.
 */

 if (!ext4_emergency_state(sbi->s_sb) &&
     !sb_rdonly(sbi->s_sb) && journal) {
  struct buffer_head *sbh = sbi->s_sbh;
  bool call_notify_err = false;

  handle = jbd2_journal_start(journal, 1);
  if (IS_ERR(handle))
   goto write_directly;
  if (jbd2_journal_get_write_access(handle, sbh)) {
   jbd2_journal_stop(handle);
   goto write_directly;
  }

  if (sbi->s_add_error_count > 0)
   call_notify_err = true;

  ext4_update_super(sbi->s_sb);
  if (buffer_write_io_error(sbh) || !buffer_uptodate(sbh)) {
   ext4_msg(sbi->s_sb, KERN_ERR, "previous I/O error to "
     "superblock detected");
   clear_buffer_write_io_error(sbh);
   set_buffer_uptodate(sbh);
  }

  if (jbd2_journal_dirty_metadata(handle, sbh)) {
   jbd2_journal_stop(handle);
   goto write_directly;
  }
  jbd2_journal_stop(handle);

  if (call_notify_err)
   ext4_notify_error_sysfs(sbi);

  return;
 }
write_directly:
 /*
 * Write through journal failed. Write sb directly to get error info
 * out and hope for the best.
 */

 ext4_commit_super(sbi->s_sb);
 ext4_notify_error_sysfs(sbi);
}

#define ext4_error_ratelimit(sb)     \
  ___ratelimit(&(EXT4_SB(sb)->s_err_ratelimit_state), \
        "EXT4-fs error")

void __ext4_error(struct super_block *sb, const char *function,
    unsigned int line, bool force_ro, int error, __u64 block,
    const char *fmt, ...)
{
 struct va_format vaf;
 va_list args;

 if (unlikely(ext4_emergency_state(sb)))
  return;

 trace_ext4_error(sb, function, line);
 if (ext4_error_ratelimit(sb)) {
  va_start(args, fmt);
  vaf.fmt = fmt;
  vaf.va = &args;
  printk(KERN_CRIT
         "EXT4-fs error (device %s): %s:%d: comm %s: %pV\n",
         sb->s_id, function, line, current->comm, &vaf);
  va_end(args);
 }
 fsnotify_sb_error(sb, NULL, error ? error : EFSCORRUPTED);

 ext4_handle_error(sb, force_ro, error, 0, block, function, line);
}

void __ext4_error_inode(struct inode *inode, const char *function,
   unsigned int line, ext4_fsblk_t block, int error,
   const char *fmt, ...)
{
 va_list args;
 struct va_format vaf;

 if (unlikely(ext4_emergency_state(inode->i_sb)))
  return;

 trace_ext4_error(inode->i_sb, function, line);
 if (ext4_error_ratelimit(inode->i_sb)) {
  va_start(args, fmt);
  vaf.fmt = fmt;
  vaf.va = &args;
  if (block)
   printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: "
          "inode #%lu: block %llu: comm %s: %pV\n",
          inode->i_sb->s_id, function, line, inode->i_ino,
          block, current->comm, &vaf);
  else
   printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: "
          "inode #%lu: comm %s: %pV\n",
          inode->i_sb->s_id, function, line, inode->i_ino,
          current->comm, &vaf);
  va_end(args);
 }
 fsnotify_sb_error(inode->i_sb, inode, error ? error : EFSCORRUPTED);

 ext4_handle_error(inode->i_sb, false, error, inode->i_ino, block,
     function, line);
}

void __ext4_error_file(struct file *file, const char *function,
         unsigned int line, ext4_fsblk_t block,
         const char *fmt, ...)
{
 va_list args;
 struct va_format vaf;
 struct inode *inode = file_inode(file);
 char pathname[80], *path;

 if (unlikely(ext4_emergency_state(inode->i_sb)))
  return;

 trace_ext4_error(inode->i_sb, function, line);
 if (ext4_error_ratelimit(inode->i_sb)) {
  path = file_path(file, pathname, sizeof(pathname));
  if (IS_ERR(path))
   path = "(unknown)";
  va_start(args, fmt);
  vaf.fmt = fmt;
  vaf.va = &args;
  if (block)
   printk(KERN_CRIT
          "EXT4-fs error (device %s): %s:%d: inode #%lu: "
          "block %llu: comm %s: path %s: %pV\n",
          inode->i_sb->s_id, function, line, inode->i_ino,
          block, current->comm, path, &vaf);
  else
   printk(KERN_CRIT
          "EXT4-fs error (device %s): %s:%d: inode #%lu: "
          "comm %s: path %s: %pV\n",
          inode->i_sb->s_id, function, line, inode->i_ino,
          current->comm, path, &vaf);
  va_end(args);
 }
 fsnotify_sb_error(inode->i_sb, inode, EFSCORRUPTED);

 ext4_handle_error(inode->i_sb, false, EFSCORRUPTED, inode->i_ino, block,
     function, line);
}

const char *ext4_decode_error(struct super_block *sb, int errno,
         char nbuf[16])
{
 char *errstr = NULL;

 switch (errno) {
 case -EFSCORRUPTED:
  errstr = "Corrupt filesystem";
  break;
 case -EFSBADCRC:
  errstr = "Filesystem failed CRC";
  break;
 case -EIO:
  errstr = "IO failure";
  break;
 case -ENOMEM:
  errstr = "Out of memory";
  break;
 case -EROFS:
  if (!sb || (EXT4_SB(sb)->s_journal &&
       EXT4_SB(sb)->s_journal->j_flags & JBD2_ABORT))
   errstr = "Journal has aborted";
  else
   errstr = "Readonly filesystem";
  break;
 default:
  /* If the caller passed in an extra buffer for unknown
 * errors, textualise them now.  Else we just return
 * NULL. */

  if (nbuf) {
   /* Check for truncated error codes... */
   if (snprintf(nbuf, 16, "error %d", -errno) >= 0)
    errstr = nbuf;
  }
  break;
 }

 return errstr;
}

/* __ext4_std_error decodes expected errors from journaling functions
 * automatically and invokes the appropriate error response.  */


void __ext4_std_error(struct super_block *sb, const char *function,
        unsigned int line, int errno)
{
 char nbuf[16];
 const char *errstr;

 if (unlikely(ext4_emergency_state(sb)))
  return;

 /* Special case: if the error is EROFS, and we're not already
 * inside a transaction, then there's really no point in logging
 * an error. */

 if (errno == -EROFS && journal_current_handle() == NULL && sb_rdonly(sb))
  return;

 if (ext4_error_ratelimit(sb)) {
  errstr = ext4_decode_error(sb, errno, nbuf);
  printk(KERN_CRIT "EXT4-fs error (device %s) in %s:%d: %s\n",
         sb->s_id, function, line, errstr);
 }
 fsnotify_sb_error(sb, NULL, errno ? errno : EFSCORRUPTED);

 ext4_handle_error(sb, false, -errno, 0, 0, function, line);
}

void __ext4_msg(struct super_block *sb,
  const char *prefix, const char *fmt, ...)
{
 struct va_format vaf;
 va_list args;

 if (sb) {
  atomic_inc(&EXT4_SB(sb)->s_msg_count);
  if (!___ratelimit(&(EXT4_SB(sb)->s_msg_ratelimit_state),
      "EXT4-fs"))
   return;
 }

 va_start(args, fmt);
 vaf.fmt = fmt;
 vaf.va = &args;
 if (sb)
  printk("%sEXT4-fs (%s): %pV\n", prefix, sb->s_id, &vaf);
 else
  printk("%sEXT4-fs: %pV\n", prefix, &vaf);
 va_end(args);
}

static int ext4_warning_ratelimit(struct super_block *sb)
{
 atomic_inc(&EXT4_SB(sb)->s_warning_count);
 return ___ratelimit(&(EXT4_SB(sb)->s_warning_ratelimit_state),
       "EXT4-fs warning");
}

void __ext4_warning(struct super_block *sb, const char *function,
      unsigned int line, const char *fmt, ...)
{
 struct va_format vaf;
 va_list args;

 if (!ext4_warning_ratelimit(sb))
  return;

 va_start(args, fmt);
 vaf.fmt = fmt;
 vaf.va = &args;
 printk(KERN_WARNING "EXT4-fs warning (device %s): %s:%d: %pV\n",
        sb->s_id, function, line, &vaf);
 va_end(args);
}

void __ext4_warning_inode(const struct inode *inode, const char *function,
     unsigned int line, const char *fmt, ...)
{
 struct va_format vaf;
 va_list args;

 if (!ext4_warning_ratelimit(inode->i_sb))
  return;

 va_start(args, fmt);
 vaf.fmt = fmt;
 vaf.va = &args;
 printk(KERN_WARNING "EXT4-fs warning (device %s): %s:%d: "
        "inode #%lu: comm %s: %pV\n", inode->i_sb->s_id,
        function, line, inode->i_ino, current->comm, &vaf);
 va_end(args);
}

void __ext4_grp_locked_error(const char *function, unsigned int line,
        struct super_block *sb, ext4_group_t grp,
        unsigned long ino, ext4_fsblk_t block,
        const char *fmt, ...)
__releases(bitlock)
__acquires(bitlock)
{
 struct va_format vaf;
 va_list args;

 if (unlikely(ext4_emergency_state(sb)))
  return;

 trace_ext4_error(sb, function, line);
 if (ext4_error_ratelimit(sb)) {
  va_start(args, fmt);
  vaf.fmt = fmt;
  vaf.va = &args;
  printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: group %u, ",
         sb->s_id, function, line, grp);
  if (ino)
   printk(KERN_CONT "inode %lu: ", ino);
  if (block)
   printk(KERN_CONT "block %llu:",
          (unsigned long long) block);
  printk(KERN_CONT "%pV\n", &vaf);
  va_end(args);
 }

 if (test_opt(sb, ERRORS_CONT)) {
  if (test_opt(sb, WARN_ON_ERROR))
   WARN_ON_ONCE(1);
  EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
  if (!bdev_read_only(sb->s_bdev)) {
   save_error_info(sb, EFSCORRUPTED, ino, block, function,
     line);
   schedule_work(&EXT4_SB(sb)->s_sb_upd_work);
  }
  return;
 }
 ext4_unlock_group(sb, grp);
 ext4_handle_error(sb, false, EFSCORRUPTED, ino, block, function, line);
 /*
 * We only get here in the ERRORS_RO case; relocking the group
 * may be dangerous, but nothing bad will happen since the
 * filesystem will have already been marked read/only and the
 * journal has been aborted.  We return 1 as a hint to callers
 * who might what to use the return value from
 * ext4_grp_locked_error() to distinguish between the
 * ERRORS_CONT and ERRORS_RO case, and perhaps return more
 * aggressively from the ext4 function in question, with a
 * more appropriate error code.
 */

 ext4_lock_group(sb, grp);
 return;
}

void ext4_mark_group_bitmap_corrupted(struct super_block *sb,
         ext4_group_t group,
         unsigned int flags)
{
 struct ext4_sb_info *sbi = EXT4_SB(sb);
 struct ext4_group_info *grp = ext4_get_group_info(sb, group);
 struct ext4_group_desc *gdp = ext4_get_group_desc(sb, group, NULL);
 int ret;

 if (!grp || !gdp)
  return;
 if (flags & EXT4_GROUP_INFO_BBITMAP_CORRUPT) {
  ret = ext4_test_and_set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT,
         &grp->bb_state);
  if (!ret)
   percpu_counter_sub(&sbi->s_freeclusters_counter,
        grp->bb_free);
 }

 if (flags & EXT4_GROUP_INFO_IBITMAP_CORRUPT) {
  ret = ext4_test_and_set_bit(EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT,
         &grp->bb_state);
  if (!ret && gdp) {
   int count;

   count = ext4_free_inodes_count(sb, gdp);
   percpu_counter_sub(&sbi->s_freeinodes_counter,
        count);
  }
 }
}

void ext4_update_dynamic_rev(struct super_block *sb)
{
 struct ext4_super_block *es = EXT4_SB(sb)->s_es;

 if (le32_to_cpu(es->s_rev_level) > EXT4_GOOD_OLD_REV)
  return;

 ext4_warning(sb,
       "updating to rev %d because of new feature flag, "
       "running e2fsck is recommended",
       EXT4_DYNAMIC_REV);

 es->s_first_ino = cpu_to_le32(EXT4_GOOD_OLD_FIRST_INO);
 es->s_inode_size = cpu_to_le16(EXT4_GOOD_OLD_INODE_SIZE);
 es->s_rev_level = cpu_to_le32(EXT4_DYNAMIC_REV);
 /* leave es->s_feature_*compat flags alone */
 /* es->s_uuid will be set by e2fsck if empty */

 /*
 * The rest of the superblock fields should be zero, and if not it
 * means they are likely already in use, so leave them alone.  We
 * can leave it up to e2fsck to clean up any inconsistencies there.
 */

}

static inline struct inode *orphan_list_entry(struct list_head *l)
{
 return &list_entry(l, struct ext4_inode_info, i_orphan)->vfs_inode;
}

static void dump_orphan_list(struct super_block *sb, struct ext4_sb_info *sbi)
{
 struct list_head *l;

 ext4_msg(sb, KERN_ERR, "sb orphan head is %d",
   le32_to_cpu(sbi->s_es->s_last_orphan));

 printk(KERN_ERR "sb_info orphan list:\n");
 list_for_each(l, &sbi->s_orphan) {
  struct inode *inode = orphan_list_entry(l);
  printk(KERN_ERR " "
         "inode %s:%lu at %p: mode %o, nlink %d, next %d\n",
         inode->i_sb->s_id, inode->i_ino, inode,
         inode->i_mode, inode->i_nlink,
         NEXT_ORPHAN(inode));
 }
}

#ifdef CONFIG_QUOTA
static int ext4_quota_off(struct super_block *sb, int type);

static inline void ext4_quotas_off(struct super_block *sb, int type)
{
 BUG_ON(type > EXT4_MAXQUOTAS);

 /* Use our quota_off function to clear inode flags etc. */
 for (type--; type >= 0; type--)
  ext4_quota_off(sb, type);
}

/*
 * This is a helper function which is used in the mount/remount
 * codepaths (which holds s_umount) to fetch the quota file name.
 */

static inline char *get_qf_name(struct super_block *sb,
    struct ext4_sb_info *sbi,
    int type)
{
 return rcu_dereference_protected(sbi->s_qf_names[type],
      lockdep_is_held(&sb->s_umount));
}
#else
static inline void ext4_quotas_off(struct super_block *sb, int type)
{
}
#endif

static int ext4_percpu_param_init(struct ext4_sb_info *sbi)
{
 ext4_fsblk_t block;
 int err;

 block = ext4_count_free_clusters(sbi->s_sb);
 ext4_free_blocks_count_set(sbi->s_es, EXT4_C2B(sbi, block));
 err = percpu_counter_init(&sbi->s_freeclusters_counter, block,
      GFP_KERNEL);
 if (!err) {
  unsigned long freei = ext4_count_free_inodes(sbi->s_sb);
  sbi->s_es->s_free_inodes_count = cpu_to_le32(freei);
  err = percpu_counter_init(&sbi->s_freeinodes_counter, freei,
       GFP_KERNEL);
 }
 if (!err)
  err = percpu_counter_init(&sbi->s_dirs_counter,
       ext4_count_dirs(sbi->s_sb), GFP_KERNEL);
 if (!err)
  err = percpu_counter_init(&sbi->s_dirtyclusters_counter, 0,
       GFP_KERNEL);
 if (!err)
  err = percpu_counter_init(&sbi->s_sra_exceeded_retry_limit, 0,
       GFP_KERNEL);
 if (!err)
  err = percpu_init_rwsem(&sbi->s_writepages_rwsem);

 if (err)
  ext4_msg(sbi->s_sb, KERN_ERR, "insufficient memory");

 return err;
}

static void ext4_percpu_param_destroy(struct ext4_sb_info *sbi)
{
 percpu_counter_destroy(&sbi->s_freeclusters_counter);
 percpu_counter_destroy(&sbi->s_freeinodes_counter);
 percpu_counter_destroy(&sbi->s_dirs_counter);
 percpu_counter_destroy(&sbi->s_dirtyclusters_counter);
 percpu_counter_destroy(&sbi->s_sra_exceeded_retry_limit);
 percpu_free_rwsem(&sbi->s_writepages_rwsem);
}

static void ext4_group_desc_free(struct ext4_sb_info *sbi)
{
 struct buffer_head **group_desc;
 int i;

 rcu_read_lock();
 group_desc = rcu_dereference(sbi->s_group_desc);
 for (i = 0; i < sbi->s_gdb_count; i++)
  brelse(group_desc[i]);
 kvfree(group_desc);
 rcu_read_unlock();
}

static void ext4_flex_groups_free(struct ext4_sb_info *sbi)
{
 struct flex_groups **flex_groups;
 int i;

 rcu_read_lock();
 flex_groups = rcu_dereference(sbi->s_flex_groups);
 if (flex_groups) {
  for (i = 0; i < sbi->s_flex_groups_allocated; i++)
   kvfree(flex_groups[i]);
  kvfree(flex_groups);
 }
 rcu_read_unlock();
}

static void ext4_put_super(struct super_block *sb)
{
 struct ext4_sb_info *sbi = EXT4_SB(sb);
 struct ext4_super_block *es = sbi->s_es;
 int aborted = 0;
 int err;

 /*
 * Unregister sysfs before destroying jbd2 journal.
 * Since we could still access attr_journal_task attribute via sysfs
 * path which could have sbi->s_journal->j_task as NULL
 * Unregister sysfs before flush sbi->s_sb_upd_work.
 * Since user may read /proc/fs/ext4/xx/mb_groups during umount, If
 * read metadata verify failed then will queue error work.
 * update_super_work will call start_this_handle may trigger
 * BUG_ON.
 */

 ext4_unregister_sysfs(sb);

 if (___ratelimit(&ext4_mount_msg_ratelimit, "EXT4-fs unmount"))
  ext4_msg(sb, KERN_INFO, "unmounting filesystem %pU.",
    &sb->s_uuid);

 ext4_unregister_li_request(sb);
 ext4_quotas_off(sb, EXT4_MAXQUOTAS);

 destroy_workqueue(sbi->rsv_conversion_wq);
 ext4_release_orphan_info(sb);

 if (sbi->s_journal) {
  aborted = is_journal_aborted(sbi->s_journal);
  err = ext4_journal_destroy(sbi, sbi->s_journal);
  if ((err < 0) && !aborted) {
   ext4_abort(sb, -err, "Couldn't clean up the journal");
  }
 } else
  flush_work(&sbi->s_sb_upd_work);

 ext4_es_unregister_shrinker(sbi);
 timer_shutdown_sync(&sbi->s_err_report);
 ext4_release_system_zone(sb);
 ext4_mb_release(sb);
 ext4_ext_release(sb);

 if (!ext4_emergency_state(sb) && !sb_rdonly(sb)) {
  if (!aborted) {
   ext4_clear_feature_journal_needs_recovery(sb);
   ext4_clear_feature_orphan_present(sb);
   es->s_state = cpu_to_le16(sbi->s_mount_state);
  }
  ext4_commit_super(sb);
 }

 ext4_group_desc_free(sbi);
 ext4_flex_groups_free(sbi);

 WARN_ON_ONCE(!(sbi->s_mount_state & EXT4_ERROR_FS) &&
       percpu_counter_sum(&sbi->s_dirtyclusters_counter));
 ext4_percpu_param_destroy(sbi);
#ifdef CONFIG_QUOTA
 for (int i = 0; i < EXT4_MAXQUOTAS; i++)
  kfree(get_qf_name(sb, sbi, i));
#endif

 /* Debugging code just in case the in-memory inode orphan list
 * isn't empty.  The on-disk one can be non-empty if we've
 * detected an error and taken the fs readonly, but the
 * in-memory list had better be clean by this point. */

 if (!list_empty(&sbi->s_orphan))
  dump_orphan_list(sb, sbi);
 ASSERT(list_empty(&sbi->s_orphan));

 sync_blockdev(sb->s_bdev);
 invalidate_bdev(sb->s_bdev);
 if (sbi->s_journal_bdev_file) {
  /*
 * Invalidate the journal device's buffers.  We don't want them
 * floating about in memory - the physical journal device may
 * hotswapped, and it breaks the `ro-after' testing code.
 */

  sync_blockdev(file_bdev(sbi->s_journal_bdev_file));
  invalidate_bdev(file_bdev(sbi->s_journal_bdev_file));
 }

 ext4_xattr_destroy_cache(sbi->s_ea_inode_cache);
 sbi->s_ea_inode_cache = NULL;

 ext4_xattr_destroy_cache(sbi->s_ea_block_cache);
 sbi->s_ea_block_cache = NULL;

 ext4_stop_mmpd(sbi);

 brelse(sbi->s_sbh);
 sb->s_fs_info = NULL;
 /*
 * Now that we are completely done shutting down the
 * superblock, we need to actually destroy the kobject.
 */

 kobject_put(&sbi->s_kobj);
 wait_for_completion(&sbi->s_kobj_unregister);
 kfree(sbi->s_blockgroup_lock);
 fs_put_dax(sbi->s_daxdev, NULL);
 fscrypt_free_dummy_policy(&sbi->s_dummy_enc_policy);
#if IS_ENABLED(CONFIG_UNICODE)
 utf8_unload(sb->s_encoding);
#endif
 kfree(sbi);
}

static struct kmem_cache *ext4_inode_cachep;

/*
 * Called inside transaction, so use GFP_NOFS
 */

static struct inode *ext4_alloc_inode(struct super_block *sb)
{
 struct ext4_inode_info *ei;

 ei = alloc_inode_sb(sb, ext4_inode_cachep, GFP_NOFS);
 if (!ei)
  return NULL;

 inode_set_iversion(&ei->vfs_inode, 1);
 ei->i_flags = 0;
 spin_lock_init(&ei->i_raw_lock);
 ei->i_prealloc_node = RB_ROOT;
 atomic_set(&ei->i_prealloc_active, 0);
 rwlock_init(&ei->i_prealloc_lock);
 ext4_es_init_tree(&ei->i_es_tree);
 rwlock_init(&ei->i_es_lock);
 INIT_LIST_HEAD(&ei->i_es_list);
 ei->i_es_all_nr = 0;
 ei->i_es_shk_nr = 0;
 ei->i_es_shrink_lblk = 0;
 ei->i_reserved_data_blocks = 0;
 spin_lock_init(&(ei->i_block_reservation_lock));
 ext4_init_pending_tree(&ei->i_pending_tree);
#ifdef CONFIG_QUOTA
 ei->i_reserved_quota = 0;
 memset(&ei->i_dquot, 0, sizeof(ei->i_dquot));
#endif
 ei->jinode = NULL;
 INIT_LIST_HEAD(&ei->i_rsv_conversion_list);
 spin_lock_init(&ei->i_completed_io_lock);
 ei->i_sync_tid = 0;
 ei->i_datasync_tid = 0;
 INIT_WORK(&ei->i_rsv_conversion_work, ext4_end_io_rsv_work);
 ext4_fc_init_inode(&ei->vfs_inode);
 spin_lock_init(&ei->i_fc_lock);
 return &ei->vfs_inode;
}

static int ext4_drop_inode(struct inode *inode)
{
 int drop = generic_drop_inode(inode);

 if (!drop)
  drop = fscrypt_drop_inode(inode);

 trace_ext4_drop_inode(inode, drop);
 return drop;
}

static void ext4_free_in_core_inode(struct inode *inode)
{
 fscrypt_free_inode(inode);
 if (!list_empty(&(EXT4_I(inode)->i_fc_list))) {
  pr_warn("%s: inode %ld still in fc list",
   __func__, inode->i_ino);
 }
 kmem_cache_free(ext4_inode_cachep, EXT4_I(inode));
}

static void ext4_destroy_inode(struct inode *inode)
{
 if (ext4_inode_orphan_tracked(inode)) {
  ext4_msg(inode->i_sb, KERN_ERR,
    "Inode %lu (%p): inode tracked as orphan!",
    inode->i_ino, EXT4_I(inode));
  print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 16, 4,
    EXT4_I(inode), sizeof(struct ext4_inode_info),
    true);
  dump_stack();
 }

 if (!(EXT4_SB(inode->i_sb)->s_mount_state & EXT4_ERROR_FS) &&
     WARN_ON_ONCE(EXT4_I(inode)->i_reserved_data_blocks))
  ext4_msg(inode->i_sb, KERN_ERR,
    "Inode %lu (%p): i_reserved_data_blocks (%u) not cleared!",
    inode->i_ino, EXT4_I(inode),
    EXT4_I(inode)->i_reserved_data_blocks);
}

static void ext4_shutdown(struct super_block *sb)
{
       ext4_force_shutdown(sb, EXT4_GOING_FLAGS_NOLOGFLUSH);
}

static void init_once(void *foo)
{
 struct ext4_inode_info *ei = foo;

 INIT_LIST_HEAD(&ei->i_orphan);
 init_rwsem(&ei->xattr_sem);
 init_rwsem(&ei->i_data_sem);
 inode_init_once(&ei->vfs_inode);
 ext4_fc_init_inode(&ei->vfs_inode);
}

static int __init init_inodecache(void)
{
 ext4_inode_cachep = kmem_cache_create_usercopy("ext4_inode_cache",
    sizeof(struct ext4_inode_info), 0,
    SLAB_RECLAIM_ACCOUNT | SLAB_ACCOUNT,
    offsetof(struct ext4_inode_info, i_data),
    sizeof_field(struct ext4_inode_info, i_data),
    init_once);
 if (ext4_inode_cachep == NULL)
  return -ENOMEM;
 return 0;
}

static void destroy_inodecache(void)
{
 /*
 * Make sure all delayed rcu free inodes are flushed before we
 * destroy cache.
 */

 rcu_barrier();
 kmem_cache_destroy(ext4_inode_cachep);
}

void ext4_clear_inode(struct inode *inode)
{
 ext4_fc_del(inode);
 invalidate_inode_buffers(inode);
 clear_inode(inode);
 ext4_discard_preallocations(inode);
 ext4_es_remove_extent(inode, 0, EXT_MAX_BLOCKS);
 dquot_drop(inode);
 if (EXT4_I(inode)->jinode) {
  jbd2_journal_release_jbd_inode(EXT4_JOURNAL(inode),
            EXT4_I(inode)->jinode);
  jbd2_free_inode(EXT4_I(inode)->jinode);
  EXT4_I(inode)->jinode = NULL;
 }
 fscrypt_put_encryption_info(inode);
 fsverity_cleanup_inode(inode);
}

static struct inode *ext4_nfs_get_inode(struct super_block *sb,
     u64 ino, u32 generation)
{
 struct inode *inode;

 /*
 * Currently we don't know the generation for parent directory, so
 * a generation of 0 means "accept any"
 */

 inode = ext4_iget(sb, ino, EXT4_IGET_HANDLE);
 if (IS_ERR(inode))
  return ERR_CAST(inode);
 if (generation && inode->i_generation != generation) {
  iput(inode);
  return ERR_PTR(-ESTALE);
 }

 return inode;
}

static struct dentry *ext4_fh_to_dentry(struct super_block *sb, struct fid *fid,
     int fh_len, int fh_type)
{
 return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
        ext4_nfs_get_inode);
}

static struct dentry *ext4_fh_to_parent(struct super_block *sb, struct fid *fid,
     int fh_len, int fh_type)
{
 return generic_fh_to_parent(sb, fid, fh_len, fh_type,
        ext4_nfs_get_inode);
}

static int ext4_nfs_commit_metadata(struct inode *inode)
{
 struct writeback_control wbc = {
  .sync_mode = WB_SYNC_ALL
 };

 trace_ext4_nfs_commit_metadata(inode);
 return ext4_write_inode(inode, &wbc);
}

#ifdef CONFIG_QUOTA
static const char * const quotatypes[] = INITQFNAMES;
#define QTYPE2NAME(t) (quotatypes[t])

static int ext4_write_dquot(struct dquot *dquot);
static int ext4_acquire_dquot(struct dquot *dquot);
static int ext4_release_dquot(struct dquot *dquot);
static int ext4_mark_dquot_dirty(struct dquot *dquot);
static int ext4_write_info(struct super_block *sb, int type);
static int ext4_quota_on(struct super_block *sb, int type, int format_id,
    const struct path *path);
static ssize_t ext4_quota_read(struct super_block *sb, int type, char *data,
          size_t len, loff_t off);
static ssize_t ext4_quota_write(struct super_block *sb, int type,
    const char *data, size_t len, loff_t off);
static int ext4_quota_enable(struct super_block *sb, int type, int format_id,
        unsigned int flags);

static struct dquot __rcu **ext4_get_dquots(struct inode *inode)
{
 return EXT4_I(inode)->i_dquot;
}

static const struct dquot_operations ext4_quota_operations = {
 .get_reserved_space = ext4_get_reserved_space,
 .write_dquot  = ext4_write_dquot,
 .acquire_dquot  = ext4_acquire_dquot,
 .release_dquot  = ext4_release_dquot,
 .mark_dirty  = ext4_mark_dquot_dirty,
 .write_info  = ext4_write_info,
 .alloc_dquot  = dquot_alloc,
 .destroy_dquot  = dquot_destroy,
 .get_projid  = ext4_get_projid,
 .get_inode_usage = ext4_get_inode_usage,
 .get_next_id  = dquot_get_next_id,
};

static const struct quotactl_ops ext4_qctl_operations = {
 .quota_on = ext4_quota_on,
 .quota_off = ext4_quota_off,
 .quota_sync = dquot_quota_sync,
 .get_state = dquot_get_state,
 .set_info = dquot_set_dqinfo,
 .get_dqblk = dquot_get_dqblk,
 .set_dqblk = dquot_set_dqblk,
 .get_nextdqblk = dquot_get_next_dqblk,
};
#endif

static const struct super_operations ext4_sops = {
 .alloc_inode = ext4_alloc_inode,
 .free_inode = ext4_free_in_core_inode,
 .destroy_inode = ext4_destroy_inode,
 .write_inode = ext4_write_inode,
 .dirty_inode = ext4_dirty_inode,
 .drop_inode = ext4_drop_inode,
 .evict_inode = ext4_evict_inode,
 .put_super = ext4_put_super,
 .sync_fs = ext4_sync_fs,
 .freeze_fs = ext4_freeze,
 .unfreeze_fs = ext4_unfreeze,
 .statfs  = ext4_statfs,
 .show_options = ext4_show_options,
 .shutdown = ext4_shutdown,
#ifdef CONFIG_QUOTA
 .quota_read = ext4_quota_read,
 .quota_write = ext4_quota_write,
 .get_dquots = ext4_get_dquots,
#endif
};

static const struct export_operations ext4_export_ops = {
 .encode_fh = generic_encode_ino32_fh,
 .fh_to_dentry = ext4_fh_to_dentry,
 .fh_to_parent = ext4_fh_to_parent,
 .get_parent = ext4_get_parent,
 .commit_metadata = ext4_nfs_commit_metadata,
};

enum {
 Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid,
 Opt_resgid, Opt_resuid, Opt_sb,
 Opt_nouid32, Opt_debug, Opt_removed,
 Opt_user_xattr, Opt_acl,
 Opt_auto_da_alloc, Opt_noauto_da_alloc, Opt_noload,
 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_test_dummy_encryption,
 Opt_inlinecrypt,
 Opt_usrjquota, Opt_grpjquota, Opt_quota,
 Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err,
 Opt_usrquota, Opt_grpquota, Opt_prjquota,
 Opt_dax, Opt_dax_always, Opt_dax_inode, Opt_dax_never,
 Opt_stripe, Opt_delalloc, Opt_nodelalloc, Opt_warn_on_error,
 Opt_nowarn_on_error, Opt_mblk_io_submit, Opt_debug_want_extra_isize,
 Opt_nomblk_io_submit, Opt_block_validity, Opt_noblock_validity,
 Opt_inode_readahead_blks, Opt_journal_ioprio,
 Opt_dioread_nolock, Opt_dioread_lock,
 Opt_discard, Opt_nodiscard, Opt_init_itable, Opt_noinit_itable,
 Opt_max_dir_size_kb, Opt_nojournal_checksum, Opt_nombcache,
 Opt_no_prefetch_block_bitmaps, Opt_mb_optimize_scan,
 Opt_errors, Opt_data, Opt_data_err, Opt_jqfmt, Opt_dax_type,
#ifdef CONFIG_EXT4_DEBUG
 Opt_fc_debug_max_replay, Opt_fc_debug_force
#endif
};

static const struct constant_table ext4_param_errors[] = {
 {"continue", EXT4_MOUNT_ERRORS_CONT},
 {"panic", EXT4_MOUNT_ERRORS_PANIC},
 {"remount-ro", EXT4_MOUNT_ERRORS_RO},
 {}
};

static const struct constant_table ext4_param_data[] = {
 {"journal", EXT4_MOUNT_JOURNAL_DATA},
 {"ordered", EXT4_MOUNT_ORDERED_DATA},
 {"writeback", EXT4_MOUNT_WRITEBACK_DATA},
 {}
};

static const struct constant_table ext4_param_data_err[] = {
 {"abort", Opt_data_err_abort},
 {"ignore", Opt_data_err_ignore},
 {}
};

static const struct constant_table ext4_param_jqfmt[] = {
 {"vfsold", QFMT_VFS_OLD},
 {"vfsv0", QFMT_VFS_V0},
 {"vfsv1", QFMT_VFS_V1},
 {}
};

static const struct constant_table ext4_param_dax[] = {
 {"always", Opt_dax_always},
 {"inode", Opt_dax_inode},
 {"never", Opt_dax_never},
 {}
};

/*
 * Mount option specification
 * We don't use fsparam_flag_no because of the way we set the
 * options and the way we show them in _ext4_show_options(). To
 * keep the changes to a minimum, let's keep the negative options
 * separate for now.
 */

static const struct fs_parameter_spec ext4_param_specs[] = {
 fsparam_flag ("bsddf",  Opt_bsd_df),
 fsparam_flag ("minixdf",  Opt_minix_df),
 fsparam_flag ("grpid",  Opt_grpid),
 fsparam_flag ("bsdgroups",  Opt_grpid),
 fsparam_flag ("nogrpid",  Opt_nogrpid),
 fsparam_flag ("sysvgroups",  Opt_nogrpid),
 fsparam_gid ("resgid",  Opt_resgid),
 fsparam_uid ("resuid",  Opt_resuid),
 fsparam_u32 ("sb",   Opt_sb),
 fsparam_enum ("errors",  Opt_errors, ext4_param_errors),
 fsparam_flag ("nouid32",  Opt_nouid32),
 fsparam_flag ("debug",  Opt_debug),
 fsparam_flag ("oldalloc",  Opt_removed),
 fsparam_flag ("orlov",  Opt_removed),
 fsparam_flag ("user_xattr",  Opt_user_xattr),
 fsparam_flag ("acl",   Opt_acl),
 fsparam_flag ("norecovery",  Opt_noload),
 fsparam_flag ("noload",  Opt_noload),
 fsparam_flag ("bh",   Opt_removed),
 fsparam_flag ("nobh",  Opt_removed),
 fsparam_u32 ("commit",  Opt_commit),
 fsparam_u32 ("min_batch_time", Opt_min_batch_time),
 fsparam_u32 ("max_batch_time", Opt_max_batch_time),
 fsparam_u32 ("journal_dev",  Opt_journal_dev),
 fsparam_bdev ("journal_path", Opt_journal_path),
 fsparam_flag ("journal_checksum", Opt_journal_checksum),
 fsparam_flag ("nojournal_checksum", Opt_nojournal_checksum),
 fsparam_flag ("journal_async_commit",Opt_journal_async_commit),
 fsparam_flag ("abort",  Opt_abort),
 fsparam_enum ("data",  Opt_data, ext4_param_data),
 fsparam_enum ("data_err",  Opt_data_err,
      ext4_param_data_err),
 fsparam_string_empty
   ("usrjquota",  Opt_usrjquota),
 fsparam_string_empty
   ("grpjquota",  Opt_grpjquota),
 fsparam_enum ("jqfmt",  Opt_jqfmt, ext4_param_jqfmt),
 fsparam_flag ("grpquota",  Opt_grpquota),
 fsparam_flag ("quota",  Opt_quota),
 fsparam_flag ("noquota",  Opt_noquota),
 fsparam_flag ("usrquota",  Opt_usrquota),
 fsparam_flag ("prjquota",  Opt_prjquota),
 fsparam_flag ("barrier",  Opt_barrier),
 fsparam_u32 ("barrier",  Opt_barrier),
 fsparam_flag ("nobarrier",  Opt_nobarrier),
 fsparam_flag ("i_version",  Opt_removed),
 fsparam_flag ("dax",   Opt_dax),
 fsparam_enum ("dax",   Opt_dax_type, ext4_param_dax),
 fsparam_u32 ("stripe",  Opt_stripe),
 fsparam_flag ("delalloc",  Opt_delalloc),
 fsparam_flag ("nodelalloc",  Opt_nodelalloc),
 fsparam_flag ("warn_on_error", Opt_warn_on_error),
 fsparam_flag ("nowarn_on_error", Opt_nowarn_on_error),
 fsparam_u32 ("debug_want_extra_isize",
      Opt_debug_want_extra_isize),
 fsparam_flag ("mblk_io_submit", Opt_removed),
 fsparam_flag ("nomblk_io_submit", Opt_removed),
 fsparam_flag ("block_validity", Opt_block_validity),
 fsparam_flag ("noblock_validity", Opt_noblock_validity),
 fsparam_u32 ("inode_readahead_blks",
      Opt_inode_readahead_blks),
 fsparam_u32 ("journal_ioprio", Opt_journal_ioprio),
 fsparam_u32 ("auto_da_alloc", Opt_auto_da_alloc),
 fsparam_flag ("auto_da_alloc", Opt_auto_da_alloc),
 fsparam_flag ("noauto_da_alloc", Opt_noauto_da_alloc),
 fsparam_flag ("dioread_nolock", Opt_dioread_nolock),
 fsparam_flag ("nodioread_nolock", Opt_dioread_lock),
 fsparam_flag ("dioread_lock", Opt_dioread_lock),
 fsparam_flag ("discard",  Opt_discard),
 fsparam_flag ("nodiscard",  Opt_nodiscard),
 fsparam_u32 ("init_itable",  Opt_init_itable),
 fsparam_flag ("init_itable",  Opt_init_itable),
 fsparam_flag ("noinit_itable", Opt_noinit_itable),
#ifdef CONFIG_EXT4_DEBUG
 fsparam_flag ("fc_debug_force", Opt_fc_debug_force),
 fsparam_u32 ("fc_debug_max_replay", Opt_fc_debug_max_replay),
#endif
 fsparam_u32 ("max_dir_size_kb", Opt_max_dir_size_kb),
 fsparam_flag ("test_dummy_encryption",
      Opt_test_dummy_encryption),
 fsparam_string ("test_dummy_encryption",
      Opt_test_dummy_encryption),
 fsparam_flag ("inlinecrypt",  Opt_inlinecrypt),
 fsparam_flag ("nombcache",  Opt_nombcache),
 fsparam_flag ("no_mbcache",  Opt_nombcache), /* for backward compatibility */
 fsparam_flag ("prefetch_block_bitmaps",
      Opt_removed),
 fsparam_flag ("no_prefetch_block_bitmaps",
      Opt_no_prefetch_block_bitmaps),
 fsparam_s32 ("mb_optimize_scan", Opt_mb_optimize_scan),
 fsparam_string ("check",  Opt_removed), /* mount option from ext2/3 */
 fsparam_flag ("nocheck",  Opt_removed), /* mount option from ext2/3 */
 fsparam_flag ("reservation",  Opt_removed), /* mount option from ext2/3 */
 fsparam_flag ("noreservation", Opt_removed), /* mount option from ext2/3 */
 fsparam_u32 ("journal",  Opt_removed), /* mount option from ext2/3 */
 {}
};


#define MOPT_SET 0x0001
#define MOPT_CLEAR 0x0002
#define MOPT_NOSUPPORT 0x0004
#define MOPT_EXPLICIT 0x0008
#ifdef CONFIG_QUOTA
#define MOPT_Q  0
#define MOPT_QFMT 0x0010
#else
#define MOPT_Q  MOPT_NOSUPPORT
#define MOPT_QFMT MOPT_NOSUPPORT
#endif
#define MOPT_NO_EXT2 0x0020
#define MOPT_NO_EXT3 0x0040
#define MOPT_EXT4_ONLY (MOPT_NO_EXT2 | MOPT_NO_EXT3)
#define MOPT_SKIP 0x0080
#define MOPT_2  0x0100

static const struct mount_opts {
 int token;
 int mount_opt;
 int flags;
} ext4_mount_opts[] = {
 {Opt_minix_df, EXT4_MOUNT_MINIX_DF, MOPT_SET},
 {Opt_bsd_df, EXT4_MOUNT_MINIX_DF, MOPT_CLEAR},
 {Opt_grpid, EXT4_MOUNT_GRPID, MOPT_SET},
 {Opt_nogrpid, EXT4_MOUNT_GRPID, MOPT_CLEAR},
 {Opt_block_validity, EXT4_MOUNT_BLOCK_VALIDITY, MOPT_SET},
 {Opt_noblock_validity, EXT4_MOUNT_BLOCK_VALIDITY, MOPT_CLEAR},
 {Opt_dioread_nolock, EXT4_MOUNT_DIOREAD_NOLOCK,
  MOPT_EXT4_ONLY | MOPT_SET},
 {Opt_dioread_lock, EXT4_MOUNT_DIOREAD_NOLOCK,
  MOPT_EXT4_ONLY | MOPT_CLEAR},
 {Opt_discard, EXT4_MOUNT_DISCARD, MOPT_SET},
 {Opt_nodiscard, EXT4_MOUNT_DISCARD, MOPT_CLEAR},
 {Opt_delalloc, EXT4_MOUNT_DELALLOC,
  MOPT_EXT4_ONLY | MOPT_SET | MOPT_EXPLICIT},
 {Opt_nodelalloc, EXT4_MOUNT_DELALLOC,
  MOPT_EXT4_ONLY | MOPT_CLEAR},
 {Opt_warn_on_error, EXT4_MOUNT_WARN_ON_ERROR, MOPT_SET},
 {Opt_nowarn_on_error, EXT4_MOUNT_WARN_ON_ERROR, MOPT_CLEAR},
 {Opt_commit, 0, MOPT_NO_EXT2},
 {Opt_nojournal_checksum, EXT4_MOUNT_JOURNAL_CHECKSUM,
  MOPT_EXT4_ONLY | MOPT_CLEAR},
 {Opt_journal_checksum, EXT4_MOUNT_JOURNAL_CHECKSUM,
  MOPT_EXT4_ONLY | MOPT_SET | MOPT_EXPLICIT},
 {Opt_journal_async_commit, (EXT4_MOUNT_JOURNAL_ASYNC_COMMIT |
        EXT4_MOUNT_JOURNAL_CHECKSUM),
  MOPT_EXT4_ONLY | MOPT_SET | MOPT_EXPLICIT},
 {Opt_noload, EXT4_MOUNT_NOLOAD, MOPT_NO_EXT2 | MOPT_SET},
 {Opt_data_err, EXT4_MOUNT_DATA_ERR_ABORT, MOPT_NO_EXT2},
 {Opt_barrier, EXT4_MOUNT_BARRIER, MOPT_SET},
 {Opt_nobarrier, EXT4_MOUNT_BARRIER, MOPT_CLEAR},
 {Opt_noauto_da_alloc, EXT4_MOUNT_NO_AUTO_DA_ALLOC, MOPT_SET},
 {Opt_auto_da_alloc, EXT4_MOUNT_NO_AUTO_DA_ALLOC, MOPT_CLEAR},
 {Opt_noinit_itable, EXT4_MOUNT_INIT_INODE_TABLE, MOPT_CLEAR},
 {Opt_dax_type, 0, MOPT_EXT4_ONLY},
 {Opt_journal_dev, 0, MOPT_NO_EXT2},
 {Opt_journal_path, 0, MOPT_NO_EXT2},
 {Opt_journal_ioprio, 0, MOPT_NO_EXT2},
 {Opt_data, 0, MOPT_NO_EXT2},
 {Opt_user_xattr, EXT4_MOUNT_XATTR_USER, MOPT_SET},
#ifdef CONFIG_EXT4_FS_POSIX_ACL
 {Opt_acl, EXT4_MOUNT_POSIX_ACL, MOPT_SET},
#else
 {Opt_acl, 0, MOPT_NOSUPPORT},
#endif
 {Opt_nouid32, EXT4_MOUNT_NO_UID32, MOPT_SET},
 {Opt_debug, EXT4_MOUNT_DEBUG, MOPT_SET},
 {Opt_quota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA, MOPT_SET | MOPT_Q},
 {Opt_usrquota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA,
       MOPT_SET | MOPT_Q},
 {Opt_grpquota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_GRPQUOTA,
       MOPT_SET | MOPT_Q},
 {Opt_prjquota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_PRJQUOTA,
       MOPT_SET | MOPT_Q},
 {Opt_noquota, (EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA |
         EXT4_MOUNT_GRPQUOTA | EXT4_MOUNT_PRJQUOTA),
       MOPT_CLEAR | MOPT_Q},
 {Opt_usrjquota, 0, MOPT_Q},
 {Opt_grpjquota, 0, MOPT_Q},
 {Opt_jqfmt, 0, MOPT_QFMT},
 {Opt_nombcache, EXT4_MOUNT_NO_MBCACHE, MOPT_SET},
 {Opt_no_prefetch_block_bitmaps, EXT4_MOUNT_NO_PREFETCH_BLOCK_BITMAPS,
  MOPT_SET},
#ifdef CONFIG_EXT4_DEBUG
 {Opt_fc_debug_force, EXT4_MOUNT2_JOURNAL_FAST_COMMIT,
  MOPT_SET | MOPT_2 | MOPT_EXT4_ONLY},
#endif
 {Opt_abort, EXT4_MOUNT2_ABORT, MOPT_SET | MOPT_2},
 {Opt_err, 0, 0}
};

#if IS_ENABLED(CONFIG_UNICODE)
static const struct ext4_sb_encodings {
 __u16 magic;
 char *name;
 unsigned int version;
} ext4_sb_encoding_map[] = {
 {EXT4_ENC_UTF8_12_1, "utf8", UNICODE_AGE(12, 1, 0)},
};

static const struct ext4_sb_encodings *
ext4_sb_read_encoding(const struct ext4_super_block *es)
{
 __u16 magic = le16_to_cpu(es->s_encoding);
 int i;

 for (i = 0; i < ARRAY_SIZE(ext4_sb_encoding_map); i++)
  if (magic == ext4_sb_encoding_map[i].magic)
   return &ext4_sb_encoding_map[i];

 return NULL;
}
#endif

#define EXT4_SPEC_JQUOTA   (1 <<  0)
#define EXT4_SPEC_JQFMT    (1 <<  1)
#define EXT4_SPEC_DATAJ    (1 <<  2)
#define EXT4_SPEC_SB_BLOCK   (1 <<  3)
#define EXT4_SPEC_JOURNAL_DEV   (1 <<  4)
#define EXT4_SPEC_JOURNAL_IOPRIO  (1 <<  5)
#define EXT4_SPEC_s_want_extra_isize  (1 <<  7)
#define EXT4_SPEC_s_max_batch_time  (1 <<  8)
#define EXT4_SPEC_s_min_batch_time  (1 <<  9)
#define EXT4_SPEC_s_inode_readahead_blks (1 << 10)
#define EXT4_SPEC_s_li_wait_mult  (1 << 11)
#define EXT4_SPEC_s_max_dir_size_kb  (1 << 12)
#define EXT4_SPEC_s_stripe   (1 << 13)
#define EXT4_SPEC_s_resuid   (1 << 14)
#define EXT4_SPEC_s_resgid   (1 << 15)
#define EXT4_SPEC_s_commit_interval  (1 << 16)
#define EXT4_SPEC_s_fc_debug_max_replay  (1 << 17)
#define EXT4_SPEC_s_sb_block   (1 << 18)
#define EXT4_SPEC_mb_optimize_scan  (1 << 19)

struct ext4_fs_context {
 char  *s_qf_names[EXT4_MAXQUOTAS];
 struct fscrypt_dummy_policy dummy_enc_policy;
 int  s_jquota_fmt; /* Format of quota to use */
#ifdef CONFIG_EXT4_DEBUG
 int s_fc_debug_max_replay;
#endif
 unsigned short qname_spec;
 unsigned long vals_s_flags; /* Bits to set in s_flags */
 unsigned long mask_s_flags; /* Bits changed in s_flags */
 unsigned long journal_devnum;
 unsigned long s_commit_interval;
 unsigned long s_stripe;
 unsigned int s_inode_readahead_blks;
 unsigned int s_want_extra_isize;
 unsigned int s_li_wait_mult;
 unsigned int s_max_dir_size_kb;
 unsigned int journal_ioprio;
 unsigned int vals_s_mount_opt;
 unsigned int mask_s_mount_opt;
 unsigned int vals_s_mount_opt2;
 unsigned int mask_s_mount_opt2;
 unsigned int opt_flags; /* MOPT flags */
 unsigned int spec;
 u32  s_max_batch_time;
 u32  s_min_batch_time;
 kuid_t  s_resuid;
 kgid_t  s_resgid;
 ext4_fsblk_t s_sb_block;
};

static void ext4_fc_free(struct fs_context *fc)
{
 struct ext4_fs_context *ctx = fc->fs_private;
 int i;

 if (!ctx)
  return;

 for (i = 0; i < EXT4_MAXQUOTAS; i++)
  kfree(ctx->s_qf_names[i]);

 fscrypt_free_dummy_policy(&ctx->dummy_enc_policy);
 kfree(ctx);
}

int ext4_init_fs_context(struct fs_context *fc)
{
 struct ext4_fs_context *ctx;

 ctx = kzalloc(sizeof(struct ext4_fs_context), GFP_KERNEL);
 if (!ctx)
  return -ENOMEM;

 fc->fs_private = ctx;
 fc->ops = &ext4_context_ops;

 /* i_version is always enabled now */
 fc->sb_flags |= SB_I_VERSION;

 return 0;
}

#ifdef CONFIG_QUOTA
/*
 * Note the name of the specified quota file.
 */

static int note_qf_name(struct fs_context *fc, int qtype,
         struct fs_parameter *param)
{
 struct ext4_fs_context *ctx = fc->fs_private;
 char *qname;

 if (param->size < 1) {
  ext4_msg(NULL, KERN_ERR, "Missing quota name");
  return -EINVAL;
 }
 if (strchr(param->string, '/')) {
  ext4_msg(NULL, KERN_ERR,
    "quotafile must be on filesystem root");
  return -EINVAL;
 }
 if (ctx->s_qf_names[qtype]) {
  if (strcmp(ctx->s_qf_names[qtype], param->string) != 0) {
   ext4_msg(NULL, KERN_ERR,
     "%s quota file already specified",
     QTYPE2NAME(qtype));
   return -EINVAL;
  }
  return 0;
 }

 qname = kmemdup_nul(param->string, param->size, GFP_KERNEL);
 if (!qname) {
  ext4_msg(NULL, KERN_ERR,
    "Not enough memory for storing quotafile name");
  return -ENOMEM;
 }
 ctx->s_qf_names[qtype] = qname;
 ctx->qname_spec |= 1 << qtype;
 ctx->spec |= EXT4_SPEC_JQUOTA;
 return 0;
}

/*
 * Clear the name of the specified quota file.
 */

static int unnote_qf_name(struct fs_context *fc, int qtype)
{
 struct ext4_fs_context *ctx = fc->fs_private;

 kfree(ctx->s_qf_names[qtype]);

 ctx->s_qf_names[qtype] = NULL;
 ctx->qname_spec |= 1 << qtype;
 ctx->spec |= EXT4_SPEC_JQUOTA;
 return 0;
}
#endif

static int ext4_parse_test_dummy_encryption(const struct fs_parameter *param,
         struct ext4_fs_context *ctx)
{
 int err;

 if (!IS_ENABLED(CONFIG_FS_ENCRYPTION)) {
  ext4_msg(NULL, KERN_WARNING,
    "test_dummy_encryption option not supported");
  return -EINVAL;
 }
 err = fscrypt_parse_test_dummy_encryption(param,
        &ctx->dummy_enc_policy);
 if (err == -EINVAL) {
  ext4_msg(NULL, KERN_WARNING,
    "Value of option \"%s\" is unrecognized", param->key);
 } else if (err == -EEXIST) {
  ext4_msg(NULL, KERN_WARNING,
    "Conflicting test_dummy_encryption options");
  return -EINVAL;
 }
 return err;
}

#define EXT4_SET_CTX(name)      \
static inline __maybe_unused      \
void ctx_set_##name(struct ext4_fs_context *ctx, unsigned long flag) \
{         \
 ctx->mask_s_##name |= flag;     \
 ctx->vals_s_##name |= flag;     \
}

#define EXT4_CLEAR_CTX(name)      \
static inline __maybe_unused      \
void ctx_clear_##name(struct ext4_fs_context *ctx, unsigned long flag) \
{         \
 ctx->mask_s_##name |= flag;     \
 ctx->vals_s_##name &= ~flag;     \
}

#define EXT4_TEST_CTX(name)      \
static inline unsigned long      \
ctx_test_##name(struct ext4_fs_context *ctx, unsigned long flag) \
{         \
 return (ctx->vals_s_##name & flag);    \
}

EXT4_SET_CTX(flags); /* set only */
EXT4_SET_CTX(mount_opt);
EXT4_CLEAR_CTX(mount_opt);
EXT4_TEST_CTX(mount_opt);
EXT4_SET_CTX(mount_opt2);
EXT4_CLEAR_CTX(mount_opt2);
EXT4_TEST_CTX(mount_opt2);

static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
{
 struct ext4_fs_context *ctx = fc->fs_private;
 struct fs_parse_result result;
 const struct mount_opts *m;
 int is_remount;
 int token;

 token = fs_parse(fc, ext4_param_specs, param, &result);
 if (token < 0)
  return token;
 is_remount = fc->purpose == FS_CONTEXT_FOR_RECONFIGURE;

 for (m = ext4_mount_opts; m->token != Opt_err; m++)
  if (token == m->token)
   break;

 ctx->opt_flags |= m->flags;

 if (m->flags & MOPT_EXPLICIT) {
  if (m->mount_opt & EXT4_MOUNT_DELALLOC) {
   ctx_set_mount_opt2(ctx, EXT4_MOUNT2_EXPLICIT_DELALLOC);
  } else if (m->mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) {
   ctx_set_mount_opt2(ctx,
           EXT4_MOUNT2_EXPLICIT_JOURNAL_CHECKSUM);
  } else
   return -EINVAL;
 }

 if (m->flags & MOPT_NOSUPPORT) {
  ext4_msg(NULL, KERN_ERR, "%s option not supported",
    param->key);
  return 0;
 }

 switch (token) {
#ifdef CONFIG_QUOTA
 case Opt_usrjquota:
  if (!*param->string)
   return unnote_qf_name(fc, USRQUOTA);
  else
   return note_qf_name(fc, USRQUOTA, param);
 case Opt_grpjquota:
  if (!*param->string)
   return unnote_qf_name(fc, GRPQUOTA);
  else
   return note_qf_name(fc, GRPQUOTA, param);
#endif
 case Opt_sb:
  if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
   ext4_msg(NULL, KERN_WARNING,
     "Ignoring %s option on remount", param->key);
  } else {
   ctx->s_sb_block = result.uint_32;
   ctx->spec |= EXT4_SPEC_s_sb_block;
  }
  return 0;
 case Opt_removed:
  ext4_msg(NULL, KERN_WARNING, "Ignoring removed %s option",
    param->key);
  return 0;
 case Opt_inlinecrypt:
#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
  ctx_set_flags(ctx, SB_INLINECRYPT);
#else
  ext4_msg(NULL, KERN_ERR, "inline encryption not supported");
#endif
  return 0;
 case Opt_errors:
  ctx_clear_mount_opt(ctx, EXT4_MOUNT_ERRORS_MASK);
  ctx_set_mount_opt(ctx, result.uint_32);
  return 0;
#ifdef CONFIG_QUOTA
 case Opt_jqfmt:
  ctx->s_jquota_fmt = result.uint_32;
  ctx->spec |= EXT4_SPEC_JQFMT;
  return 0;
#endif
 case Opt_data:
  ctx_clear_mount_opt(ctx, EXT4_MOUNT_DATA_FLAGS);
  ctx_set_mount_opt(ctx, result.uint_32);
  ctx->spec |= EXT4_SPEC_DATAJ;
  return 0;
 case Opt_commit:
  if (result.uint_32 == 0)
   result.uint_32 = JBD2_DEFAULT_MAX_COMMIT_AGE;
  else if (result.uint_32 > INT_MAX / HZ) {
   ext4_msg(NULL, KERN_ERR,
     "Invalid commit interval %d, "
     "must be smaller than %d",
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=89 G=92

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.24Angebot  Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können  ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.