1.ext4文件系统注册
路径
fs\ext4\super.c
源码
static int __init ext4_init_fs(void){int i, err;ratelimit_state_init(&ext4_mount_msg_ratelimit, 30 * HZ, 64);ext4_li_info = NULL;mutex_init(&ext4_li_mtx);/* Build-time check for flags consistency */ext4_check_flag_values();for (i = 0; i < EXT4_WQ_HASH_SZ; i++)init_waitqueue_head(&ext4__ioend_wq[i]);err = ext4_init_es();if (err)return err;err = ext4_init_pending();if (err)goto out7;err = ext4_init_post_read_processing();if (err)goto out6;err = ext4_init_pageio();if (err)goto out5;err = ext4_init_system_zone();if (err)goto out4;err = ext4_init_sysfs();if (err)goto out3;err = ext4_init_mballoc();if (err)goto out2;err = init_inodecache();if (err)goto out1;err = ext4_fc_init_dentry_cache();if (err)goto out05;register_as_ext3();register_as_ext2();err = register_filesystem(&ext4_fs_type);if (err)goto out;return 0;out:unregister_as_ext2();unregister_as_ext3();ext4_fc_destroy_dentry_cache();out05:destroy_inodecache();out1:ext4_exit_mballoc();out2:ext4_exit_sysfs();out3:ext4_exit_system_zone();out4:ext4_exit_pageio();out5:ext4_exit_post_read_processing();out6:ext4_exit_pending();out7:ext4_exit_es();return err;}
分析
->ext4_init_fs
->ratelimit_state_init
->mutex_init
->for (i = 0; i < EXT4_WQ_HASH_SZ; i++) //初始化等待队列
init_waitqueue_head(&ext4__ioend_wq[i]);
->ext4_init_pageio
->ext4_init_system_zone
->ext4_init_mballoc
->register_as_ext3
->register_filesystem(&ext3_fs_type)
->register_as_ext2
->register_filesystem(&ext2_fs_type)
->register_filesystem(&ext4_fs_type) //注册ext4文件系统,ext4_fs_type文件类型。
2.挂载文件ext4系统
static struct file_system_type ext4_fs_type = {.owner= THIS_MODULE,.name= "ext4",.mount= ext4_mount,.kill_sb= kill_block_super,.fs_flags= FS_REQUIRES_DEV,};MODULE_ALIAS_FS("ext4");
ext4_mount
挂载ext4文件系统
static struct dentry *ext4_mount(struct file_system_type *fs_type, int flags,const char *dev_name, void *data){return mount_bdev(fs_type, flags, dev_name, data, ext4_fill_super);}
ext4_file_super
填充ext4超级块信息,代码量比较大,逐次分析
static int ext4_fill_super(struct super_block *sb, void *data, int silent){struct dax_device *dax_dev = fs_dax_get_by_bdev(sb->s_bdev);char *orig_data = kstrdup(data, GFP_KERNEL);struct buffer_head *bh, **group_desc;struct ext4_super_block *es = NULL;struct ext4_sb_info *sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);struct flex_groups **flex_groups;ext4_fsblk_t block;ext4_fsblk_t sb_block = get_sb_block(&data);ext4_fsblk_t logical_sb_block;unsigned long offset = 0;unsigned long journal_devnum = 0;unsigned long def_mount_opts;struct inode *root;const char *descr;int ret = -ENOMEM;int blocksize, clustersize;unsigned int db_count;unsigned int i;int needs_recovery, has_huge_files;__u64 blocks_count;int err = 0;unsigned int journal_ioprio = DEFAULT_JOURNAL_IOPRIO;ext4_group_t first_not_zeroed;if ((data && !orig_data) || !sbi)goto out_free_base;sbi->s_daxdev = dax_dev;sbi->s_blockgroup_lock =kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL);if (!sbi->s_blockgroup_lock)goto out_free_base;sb->s_fs_info = sbi;sbi->s_sb = sb;sbi->s_inode_readahead_blks = EXT4_DEF_INODE_READAHEAD_BLKS;sbi->s_sb_block = sb_block;if (sb->s_bdev->bd_part)sbi->s_sectors_written_start =part_stat_read(sb->s_bdev->bd_part, sectors[STAT_WRITE]);/* Cleanup superblock name */strreplace(sb->s_id, '/', '!');/* -EINVAL is default */ret = -EINVAL;blocksize = sb_min_blocksize(sb, EXT4_MIN_BLOCK_SIZE);if (!blocksize) {ext4_msg(sb, KERN_ERR, "unable to set blocksize");goto out_fail;}/** The ext4 superblock will not be buffer aligned for other than 1kB* block sizes. We need to calculate the offset from buffer start.*/if (blocksize != EXT4_MIN_BLOCK_SIZE) {logical_sb_block = sb_block * EXT4_MIN_BLOCK_SIZE;offset = do_div(logical_sb_block, blocksize);} else {logical_sb_block = sb_block;}bh = ext4_sb_bread_unmovable(sb, logical_sb_block);if (IS_ERR(bh)) {ext4_msg(sb, KERN_ERR, "unable to read superblock");ret = PTR_ERR(bh);bh = NULL;goto out_fail;}/** Note: s_es must be initialized as soon as possible because* some ext4 macro-instructions depend on its value*/es = (struct ext4_super_block *) (bh->b_data + offset);sbi->s_es = es;sb->s_magic = le16_to_cpu(es->s_magic);if (sb->s_magic != EXT4_SUPER_MAGIC)goto cantfind_ext4;sbi->s_kbytes_written = le64_to_cpu(es->s_kbytes_written);/* Warn if metadata_csum and gdt_csum are both set. */if (ext4_has_feature_metadata_csum(sb) &&ext4_has_feature_gdt_csum(sb))ext4_warning(sb, "metadata_csum and uninit_bg are ""redundant flags; please run fsck.");/* Check for a known checksum algorithm */if (!ext4_verify_csum_type(sb, es)) {ext4_msg(sb, KERN_ERR, "VFS: Found ext4 filesystem with ""unknown checksum algorithm.");silent = 1;goto cantfind_ext4;}/* Load the checksum driver */sbi->s_chksum_driver = crypto_alloc_shash("crc32c", 0, 0);if (IS_ERR(sbi->s_chksum_driver)) {ext4_msg(sb, KERN_ERR, "Cannot load crc32c driver.");ret = PTR_ERR(sbi->s_chksum_driver);sbi->s_chksum_driver = NULL;goto failed_mount;}/* Check superblock checksum */if (!ext4_superblock_csum_verify(sb, es)) {ext4_msg(sb, KERN_ERR, "VFS: Found ext4 filesystem with ""invalid superblock checksum. Run e2fsck?");silent = 1;ret = -EFSBADCRC;goto cantfind_ext4;}/* Precompute checksum seed for all metadata */if (ext4_has_feature_csum_seed(sb))sbi->s_csum_seed = le32_to_cpu(es->s_checksum_seed);else if (ext4_has_metadata_csum(sb) || ext4_has_feature_ea_inode(sb))sbi->s_csum_seed = ext4_chksum(sbi, ~0, es->s_uuid,sizeof(es->s_uuid));/* Set defaults before we parse the mount options */def_mount_opts = le32_to_cpu(es->s_default_mount_opts);set_opt(sb, INIT_INODE_TABLE);if (def_mount_opts & EXT4_DEFM_DEBUG)set_opt(sb, DEBUG);if (def_mount_opts & EXT4_DEFM_BSDGROUPS)set_opt(sb, GRPID);if (def_mount_opts & EXT4_DEFM_UID16)set_opt(sb, NO_UID32);/* xattr user namespace & acls are now defaulted on */set_opt(sb, XATTR_USER);#ifdef CONFIG_EXT4_FS_POSIX_ACLset_opt(sb, POSIX_ACL);#endifif (ext4_has_feature_fast_commit(sb))set_opt2(sb, JOURNAL_FAST_COMMIT);/* don't forget to enable journal_csum when metadata_csum is enabled. */if (ext4_has_metadata_csum(sb))set_opt(sb, JOURNAL_CHECKSUM);if ((def_mount_opts & EXT4_DEFM_JMODE) == EXT4_DEFM_JMODE_DATA)set_opt(sb, JOURNAL_DATA);else if ((def_mount_opts & EXT4_DEFM_JMODE) == EXT4_DEFM_JMODE_ORDERED)set_opt(sb, ORDERED_DATA);else if ((def_mount_opts & EXT4_DEFM_JMODE) == EXT4_DEFM_JMODE_WBACK)set_opt(sb, WRITEBACK_DATA);if (le16_to_cpu(sbi->s_es->s_errors) == EXT4_ERRORS_PANIC)set_opt(sb, ERRORS_PANIC);else if (le16_to_cpu(sbi->s_es->s_errors) == EXT4_ERRORS_CONTINUE)set_opt(sb, ERRORS_CONT);elseset_opt(sb, ERRORS_RO);/* block_validity enabled by default; disable with noblock_validity */set_opt(sb, BLOCK_VALIDITY);if (def_mount_opts & EXT4_DEFM_DISCARD)set_opt(sb, DISCARD);sbi->s_resuid = make_kuid(&init_user_ns, le16_to_cpu(es->s_def_resuid));sbi->s_resgid = make_kgid(&init_user_ns, le16_to_cpu(es->s_def_resgid));sbi->s_commit_interval = JBD2_DEFAULT_MAX_COMMIT_AGE * HZ;sbi->s_min_batch_time = EXT4_DEF_MIN_BATCH_TIME;sbi->s_max_batch_time = EXT4_DEF_MAX_BATCH_TIME;if ((def_mount_opts & EXT4_DEFM_NOBARRIER) == 0)set_opt(sb, BARRIER);/** enable delayed allocation by default* Use -o nodelalloc to turn it off*/if (!IS_EXT3_SB(sb) && !IS_EXT2_SB(sb) &&((def_mount_opts & EXT4_DEFM_NODELALLOC) == 0))set_opt(sb, DELALLOC);/** set default s_li_wait_mult for lazyinit, for the case there is* no mount option specified.*/sbi->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT;if (le32_to_cpu(es->s_log_block_size) >(EXT4_MAX_BLOCK_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {ext4_msg(sb, KERN_ERR,"Invalid log block size: %u",le32_to_cpu(es->s_log_block_size));goto failed_mount;}if (le32_to_cpu(es->s_log_cluster_size) >(EXT4_MAX_CLUSTER_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {ext4_msg(sb, KERN_ERR,"Invalid log cluster size: %u",le32_to_cpu(es->s_log_cluster_size));goto failed_mount;}blocksize = EXT4_MIN_BLOCK_SIZE << le32_to_cpu(es->s_log_block_size);if (blocksize == PAGE_SIZE)set_opt(sb, DIOREAD_NOLOCK);if (le32_to_cpu(es->s_rev_level) == EXT4_GOOD_OLD_REV) {sbi->s_inode_size = EXT4_GOOD_OLD_INODE_SIZE;sbi->s_first_ino = EXT4_GOOD_OLD_FIRST_INO;} else {sbi->s_inode_size = le16_to_cpu(es->s_inode_size);sbi->s_first_ino = le32_to_cpu(es->s_first_ino);if (sbi->s_first_ino < EXT4_GOOD_OLD_FIRST_INO) {ext4_msg(sb, KERN_ERR, "invalid first ino: %u",sbi->s_first_ino);goto failed_mount;}if ((sbi->s_inode_size < EXT4_GOOD_OLD_INODE_SIZE) ||(!is_power_of_2(sbi->s_inode_size)) ||(sbi->s_inode_size > blocksize)) {ext4_msg(sb, KERN_ERR,"unsupported inode size: %d",sbi->s_inode_size);ext4_msg(sb, KERN_ERR, "blocksize: %d", blocksize);goto failed_mount;}/** i_atime_extra is the last extra field available for* [acm]times in struct ext4_inode. Checking for that* field should suffice to ensure we have extra space* for all three.*/if (sbi->s_inode_size >= offsetof(struct ext4_inode, i_atime_extra) +sizeof(((struct ext4_inode *)0)->i_atime_extra)) {sb->s_time_gran = 1;sb->s_time_max = EXT4_EXTRA_TIMESTAMP_MAX;} else {sb->s_time_gran = NSEC_PER_SEC;sb->s_time_max = EXT4_NON_EXTRA_TIMESTAMP_MAX;}sb->s_time_min = EXT4_TIMESTAMP_MIN;}if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE) {sbi->s_want_extra_isize = sizeof(struct ext4_inode) -EXT4_GOOD_OLD_INODE_SIZE;if (ext4_has_feature_extra_isize(sb)) {unsigned v, max = (sbi->s_inode_size -EXT4_GOOD_OLD_INODE_SIZE);v = le16_to_cpu(es->s_want_extra_isize);if (v > max) {ext4_msg(sb, KERN_ERR,"bad s_want_extra_isize: %d", v);goto failed_mount;}if (sbi->s_want_extra_isize < v)sbi->s_want_extra_isize = v;v = le16_to_cpu(es->s_min_extra_isize);if (v > max) {ext4_msg(sb, KERN_ERR,"bad s_min_extra_isize: %d", v);goto failed_mount;}if (sbi->s_want_extra_isize < v)sbi->s_want_extra_isize = v;}}if (sbi->s_es->s_mount_opts[0]) {char *s_mount_opts = kstrndup(sbi->s_es->s_mount_opts,sizeof(sbi->s_es->s_mount_opts),GFP_KERNEL);if (!s_mount_opts)goto failed_mount;if (!parse_options(s_mount_opts, sb, &journal_devnum,&journal_ioprio, 0)) {ext4_msg(sb, KERN_WARNING,"failed to parse options in superblock: %s",s_mount_opts);}kfree(s_mount_opts);}sbi->s_def_mount_opt = sbi->s_mount_opt;if (!parse_options((char *) data, sb, &journal_devnum,&journal_ioprio, 0))goto failed_mount;#ifdef CONFIG_UNICODEif (ext4_has_feature_casefold(sb) && !sb->s_encoding) {const struct ext4_sb_encodings *encoding_info;struct unicode_map *encoding;__u16 encoding_flags;if (ext4_has_feature_encrypt(sb)) {ext4_msg(sb, KERN_ERR,"Can't mount with encoding and encryption");goto failed_mount;}if (ext4_sb_read_encoding(es, &encoding_info,&encoding_flags)) {ext4_msg(sb, KERN_ERR,"Encoding requested by superblock is unknown");goto failed_mount;}encoding = utf8_load(encoding_info->version);if (IS_ERR(encoding)) {ext4_msg(sb, KERN_ERR,"can't mount with superblock charset: %s-%s ""not supported by the kernel. flags: 0x%x.",encoding_info->name, encoding_info->version,encoding_flags);goto failed_mount;}ext4_msg(sb, KERN_INFO,"Using encoding defined by superblock: ""%s-%s with flags 0x%hx", encoding_info->name,encoding_info->version?:"\b", encoding_flags);sb->s_encoding = encoding;sb->s_encoding_flags = encoding_flags;}#endifif (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) {printk_once(KERN_WARNING "EXT4-fs: Warning: mounting with data=journal disables delayed allocation, dioread_nolock, O_DIRECT and fast_commit support!\n");/* can't mount with both data=journal and dioread_nolock. */clear_opt(sb, DIOREAD_NOLOCK);clear_opt2(sb, JOURNAL_FAST_COMMIT);if (test_opt2(sb, EXPLICIT_DELALLOC)) {ext4_msg(sb, KERN_ERR, "can't mount with ""both data=journal and delalloc");goto failed_mount;}if (test_opt(sb, DAX_ALWAYS)) {ext4_msg(sb, KERN_ERR, "can't mount with ""both data=journal and dax");goto failed_mount;}if (ext4_has_feature_encrypt(sb)) {ext4_msg(sb, KERN_WARNING,"encrypted files will use data=ordered ""instead of data journaling mode");}if (test_opt(sb, DELALLOC))clear_opt(sb, DELALLOC);} else {sb->s_iflags |= SB_I_CGROUPWB;}sb->s_flags = (sb->s_flags & ~SB_POSIXACL) |(test_opt(sb, POSIX_ACL) ? SB_POSIXACL : 0);if (le32_to_cpu(es->s_rev_level) == EXT4_GOOD_OLD_REV &&(ext4_has_compat_features(sb) ||ext4_has_ro_compat_features(sb) ||ext4_has_incompat_features(sb)))ext4_msg(sb, KERN_WARNING,"feature flags set on rev 0 fs, ""running e2fsck is recommended");if (es->s_creator_os == cpu_to_le32(EXT4_OS_HURD)) {set_opt2(sb, HURD_COMPAT);if (ext4_has_feature_64bit(sb)) {ext4_msg(sb, KERN_ERR,"The Hurd can't support 64-bit file systems");goto failed_mount;}/** ea_inode feature uses l_i_version field which is not* available in HURD_COMPAT mode.*/if (ext4_has_feature_ea_inode(sb)) {ext4_msg(sb, KERN_ERR,"ea_inode feature is not supported for Hurd");goto failed_mount;}}if (IS_EXT2_SB(sb)) {if (ext2_feature_set_ok(sb))ext4_msg(sb, KERN_INFO, "mounting ext2 file system ""using the ext4 subsystem");else {/** If we're probing be silent, if this looks like* it's actually an ext[34] filesystem.*/if (silent && ext4_feature_set_ok(sb, sb_rdonly(sb)))goto failed_mount;ext4_msg(sb, KERN_ERR, "couldn't mount as ext2 due ""to feature incompatibilities");goto failed_mount;}}if (IS_EXT3_SB(sb)) {if (ext3_feature_set_ok(sb))ext4_msg(sb, KERN_INFO, "mounting ext3 file system ""using the ext4 subsystem");else {/** If we're probing be silent, if this looks like* it's actually an ext4 filesystem.*/if (silent && ext4_feature_set_ok(sb, sb_rdonly(sb)))goto failed_mount;ext4_msg(sb, KERN_ERR, "couldn't mount as ext3 due ""to feature incompatibilities");goto failed_mount;}}/** Check feature flags regardless of the revision level, since we* previously didn't change the revision level when setting the flags,* so there is a chance incompat flags are set on a rev 0 filesystem.*/if (!ext4_feature_set_ok(sb, (sb_rdonly(sb))))goto failed_mount;if (le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) > (blocksize / 4)) {ext4_msg(sb, KERN_ERR,"Number of reserved GDT blocks insanely large: %d",le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks));goto failed_mount;}if (bdev_dax_supported(sb->s_bdev, blocksize))set_bit(EXT4_FLAGS_BDEV_IS_DAX, &sbi->s_ext4_flags);if (sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS) {if (ext4_has_feature_inline_data(sb)) {ext4_msg(sb, KERN_ERR, "Cannot use DAX on a filesystem"" that may contain inline data");goto failed_mount;}if (!test_bit(EXT4_FLAGS_BDEV_IS_DAX, &sbi->s_ext4_flags)) {ext4_msg(sb, KERN_ERR,"DAX unsupported by block device.");goto failed_mount;}}if (ext4_has_feature_encrypt(sb) && 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) {/** bh must be released before kill_bdev(), otherwise* it won't be freed and its page also. kill_bdev()* is called by sb_set_blocksize().*/brelse(bh);/* Validate the filesystem blocksize */if (!sb_set_blocksize(sb, blocksize)) {ext4_msg(sb, KERN_ERR, "bad block size %d",blocksize);bh = NULL;goto failed_mount;}logical_sb_block = sb_block * EXT4_MIN_BLOCK_SIZE;offset = do_div(logical_sb_block, blocksize);bh = ext4_sb_bread_unmovable(sb, logical_sb_block);if (IS_ERR(bh)) {ext4_msg(sb, KERN_ERR,"Can't read superblock on 2nd try");ret = PTR_ERR(bh);bh = NULL;goto failed_mount;}es = (struct ext4_super_block *)(bh->b_data + offset);sbi->s_es = es;if (es->s_magic != cpu_to_le16(EXT4_SUPER_MAGIC)) {ext4_msg(sb, KERN_ERR,"Magic mismatch, very weird!");goto failed_mount;}}has_huge_files = ext4_has_feature_huge_file(sb);sbi->s_bitmap_maxbytes = ext4_max_bitmap_size(sb->s_blocksize_bits,has_huge_files);sb->s_maxbytes = ext4_max_size(sb->s_blocksize_bits, has_huge_files);sbi->s_desc_size = le16_to_cpu(es->s_desc_size);if (ext4_has_feature_64bit(sb)) {if (sbi->s_desc_size < EXT4_MIN_DESC_SIZE_64BIT ||sbi->s_desc_size > EXT4_MAX_DESC_SIZE ||!is_power_of_2(sbi->s_desc_size)) {ext4_msg(sb, KERN_ERR,"unsupported descriptor size %lu",sbi->s_desc_size);goto failed_mount;}} elsesbi->s_desc_size = EXT4_MIN_DESC_SIZE;sbi->s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group);sbi->s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group);sbi->s_inodes_per_block = blocksize / EXT4_INODE_SIZE(sb);if (sbi->s_inodes_per_block == 0)goto cantfind_ext4;if (sbi->s_inodes_per_group < sbi->s_inodes_per_block ||sbi->s_inodes_per_group > blocksize * 8) {ext4_msg(sb, KERN_ERR, "invalid inodes per group: %lu\n",sbi->s_inodes_per_group);goto failed_mount;}sbi->s_itb_per_group = sbi->s_inodes_per_group /sbi->s_inodes_per_block;sbi->s_desc_per_block = blocksize / EXT4_DESC_SIZE(sb);sbi->s_sbh = bh;sbi->s_mount_state = le16_to_cpu(es->s_state) & ~EXT4_FC_REPLAY;sbi->s_addr_per_block_bits = ilog2(EXT4_ADDR_PER_BLOCK(sb));sbi->s_desc_per_block_bits = ilog2(EXT4_DESC_PER_BLOCK(sb));for (i = 0; i < 4; i++)sbi->s_hash_seed[i] = le32_to_cpu(es->s_hash_seed[i]);sbi->s_def_hash_version = es->s_def_hash_version;if (ext4_has_feature_dir_index(sb)) {i = le32_to_cpu(es->s_flags);if (i & EXT2_FLAGS_UNSIGNED_HASH)sbi->s_hash_unsigned = 3;else if ((i & EXT2_FLAGS_SIGNED_HASH) == 0) {#ifdef __CHAR_UNSIGNED__if (!sb_rdonly(sb))es->s_flags |=cpu_to_le32(EXT2_FLAGS_UNSIGNED_HASH);sbi->s_hash_unsigned = 3;#elseif (!sb_rdonly(sb))es->s_flags |=cpu_to_le32(EXT2_FLAGS_SIGNED_HASH);#endif}}/* Handle clustersize */clustersize = BLOCK_SIZE << le32_to_cpu(es->s_log_cluster_size);if (ext4_has_feature_bigalloc(sb)) {if (clustersize < blocksize) {ext4_msg(sb, KERN_ERR,"cluster size (%d) smaller than ""block size (%d)", clustersize, blocksize);goto failed_mount;}sbi->s_cluster_bits = le32_to_cpu(es->s_log_cluster_size) -le32_to_cpu(es->s_log_block_size);sbi->s_clusters_per_group =le32_to_cpu(es->s_clusters_per_group);if (sbi->s_clusters_per_group > blocksize * 8) {ext4_msg(sb, KERN_ERR,"#clusters per group too big: %lu",sbi->s_clusters_per_group);goto failed_mount;}if (sbi->s_blocks_per_group !=(sbi->s_clusters_per_group * (clustersize / blocksize))) {ext4_msg(sb, KERN_ERR, "blocks per group (%lu) and ""clusters per group (%lu) inconsistent",sbi->s_blocks_per_group,sbi->s_clusters_per_group);goto failed_mount;}} else {if (clustersize != blocksize) {ext4_msg(sb, KERN_ERR,"fragment/cluster size (%d) != ""block size (%d)", clustersize, blocksize);goto failed_mount;}if (sbi->s_blocks_per_group > blocksize * 8) {ext4_msg(sb, KERN_ERR,"#blocks per group too big: %lu",sbi->s_blocks_per_group);goto failed_mount;}sbi->s_clusters_per_group = sbi->s_blocks_per_group;sbi->s_cluster_bits = 0;}sbi->s_cluster_ratio = clustersize / blocksize;/* Do we have standard group size of clustersize * 8 blocks ? */if (sbi->s_blocks_per_group == clustersize << 3)set_opt2(sb, STD_GROUP_SIZE);/** Test whether we have more sectors than will fit in sector_t,* and whether the max offset is addressable by the page cache.*/err = generic_check_addressable(sb->s_blocksize_bits,ext4_blocks_count(es));if (err) {ext4_msg(sb, KERN_ERR, "filesystem"" too large to mount safely on this system");goto failed_mount;}if (EXT4_BLOCKS_PER_GROUP(sb) == 0)goto cantfind_ext4;/* check blocks count against device size */blocks_count = sb->s_bdev->bd_inode->i_size >> sb->s_blocksize_bits;if (blocks_count && ext4_blocks_count(es) > blocks_count) {ext4_msg(sb, KERN_WARNING, "bad geometry: block count %llu ""exceeds size of device (%llu blocks)",ext4_blocks_count(es), blocks_count);goto failed_mount;}/** It makes no sense for the first data block to be beyond the end* of the filesystem.*/if (le32_to_cpu(es->s_first_data_block) >= ext4_blocks_count(es)) {ext4_msg(sb, KERN_WARNING, "bad geometry: first data ""block %u is beyond end of filesystem (%llu)",le32_to_cpu(es->s_first_data_block),ext4_blocks_count(es));goto failed_mount;}if ((es->s_first_data_block == 0) && (es->s_log_block_size == 0) &&(sbi->s_cluster_ratio == 1)) {ext4_msg(sb, KERN_WARNING, "bad geometry: first data ""block is 0 with a 1k block and cluster size");goto failed_mount;}blocks_count = (ext4_blocks_count(es) -le32_to_cpu(es->s_first_data_block) +EXT4_BLOCKS_PER_GROUP(sb) - 1);do_div(blocks_count, EXT4_BLOCKS_PER_GROUP(sb));if (blocks_count > ((uint64_t)1<<32) - EXT4_DESC_PER_BLOCK(sb)) {ext4_msg(sb, KERN_WARNING, "groups count too large: %llu ""(block count %llu, first data block %u, ""blocks per group %lu)", blocks_count,ext4_blocks_count(es),le32_to_cpu(es->s_first_data_block),EXT4_BLOCKS_PER_GROUP(sb));goto failed_mount;}sbi->s_groups_count = blocks_count;sbi->s_blockfile_groups = min_t(ext4_group_t, sbi->s_groups_count,(EXT4_MAX_BLOCK_FILE_PHYS / EXT4_BLOCKS_PER_GROUP(sb)));if (((u64)sbi->s_groups_count * sbi->s_inodes_per_group) !=le32_to_cpu(es->s_inodes_count)) {ext4_msg(sb, KERN_ERR, "inodes count not valid: %u vs %llu",le32_to_cpu(es->s_inodes_count),((u64)sbi->s_groups_count * sbi->s_inodes_per_group));ret = -EINVAL;goto failed_mount;}db_count = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) /EXT4_DESC_PER_BLOCK(sb);if (ext4_has_feature_meta_bg(sb)) {if (le32_to_cpu(es->s_first_meta_bg) > db_count) {ext4_msg(sb, KERN_WARNING,"first meta block group too large: %u ""(group descriptor block count %u)",le32_to_cpu(es->s_first_meta_bg), db_count);goto failed_mount;}}rcu_assign_pointer(sbi->s_group_desc,kvmalloc_array(db_count,sizeof(struct buffer_head *),GFP_KERNEL));if (sbi->s_group_desc == NULL) {ext4_msg(sb, KERN_ERR, "not enough memory");ret = -ENOMEM;goto failed_mount;}bgl_lock_init(sbi->s_blockgroup_lock);/* Pre-read the descriptors into the buffer cache */for (i = 0; i < db_count; i++) {block = descriptor_loc(sb, logical_sb_block, i);ext4_sb_breadahead_unmovable(sb, block);}for (i = 0; i < db_count; i++) {struct buffer_head *bh;block = descriptor_loc(sb, logical_sb_block, i);bh = ext4_sb_bread_unmovable(sb, block);if (IS_ERR(bh)) {ext4_msg(sb, KERN_ERR,"can't read group descriptor %d", i);db_count = i;ret = PTR_ERR(bh);bh = NULL;goto failed_mount2;}rcu_read_lock();rcu_dereference(sbi->s_group_desc)[i] = bh;rcu_read_unlock();}sbi->s_gdb_count = db_count;if (!ext4_check_descriptors(sb, logical_sb_block, &first_not_zeroed)) {ext4_msg(sb, KERN_ERR, "group descriptors corrupted!");ret = -EFSCORRUPTED;goto failed_mount2;}timer_setup(&sbi->s_err_report, print_daily_error_info, 0);/* Register extent status tree shrinker */if (ext4_es_register_shrinker(sbi))goto failed_mount3;sbi->s_stripe = ext4_get_stripe_size(sbi);sbi->s_extent_max_zeroout_kb = 32;/** set up enough so that it can read an inode*/sb->s_op = &ext4_sops;sb->s_export_op = &ext4_export_ops;sb->s_xattr = ext4_xattr_handlers;#ifdef CONFIG_FS_ENCRYPTIONsb->s_cop = &ext4_cryptops;#endif#ifdef CONFIG_FS_VERITYsb->s_vop = &ext4_verityops;#endif#ifdef CONFIG_QUOTAsb->dq_op = &ext4_quota_operations;if (ext4_has_feature_quota(sb))sb->s_qcop = &dquot_quotactl_sysfile_ops;elsesb->s_qcop = &ext4_qctl_operations;sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP | QTYPE_MASK_PRJ;#endifmemcpy(&sb->s_uuid, es->s_uuid, sizeof(es->s_uuid));INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */mutex_init(&sbi->s_orphan_lock);/* Initialize fast commit stuff */atomic_set(&sbi->s_fc_subtid, 0);atomic_set(&sbi->s_fc_ineligible_updates, 0);INIT_LIST_HEAD(&sbi->s_fc_q[FC_Q_MAIN]);INIT_LIST_HEAD(&sbi->s_fc_q[FC_Q_STAGING]);INIT_LIST_HEAD(&sbi->s_fc_dentry_q[FC_Q_MAIN]);INIT_LIST_HEAD(&sbi->s_fc_dentry_q[FC_Q_STAGING]);sbi->s_fc_bytes = 0;ext4_clear_mount_flag(sb, EXT4_MF_FC_INELIGIBLE);ext4_clear_mount_flag(sb, EXT4_MF_FC_COMMITTING);spin_lock_init(&sbi->s_fc_lock);memset(&sbi->s_fc_stats, 0, sizeof(sbi->s_fc_stats));sbi->s_fc_replay_state.fc_regions = NULL;sbi->s_fc_replay_state.fc_regions_size = 0;sbi->s_fc_replay_state.fc_regions_used = 0;sbi->s_fc_replay_state.fc_regions_valid = 0;sbi->s_fc_replay_state.fc_modified_inodes = NULL;sbi->s_fc_replay_state.fc_modified_inodes_size = 0;sbi->s_fc_replay_state.fc_modified_inodes_used = 0;sb->s_root = NULL;needs_recovery = (es->s_last_orphan != 0 ||ext4_has_feature_journal_needs_recovery(sb));if (ext4_has_feature_mmp(sb) && !sb_rdonly(sb))if (ext4_multi_mount_protect(sb, le64_to_cpu(es->s_mmp_block)))goto failed_mount3a;/** The first inode we look at is the journal inode. Don't try* root first: it may be modified in the journal!*/if (!test_opt(sb, NOLOAD) && ext4_has_feature_journal(sb)) {err = ext4_load_journal(sb, es, journal_devnum);if (err)goto failed_mount3a;} else if (test_opt(sb, NOLOAD) && !sb_rdonly(sb) &&ext4_has_feature_journal_needs_recovery(sb)) {ext4_msg(sb, KERN_ERR, "required journal recovery ""suppressed and not mounted read-only");goto failed_mount_wq;} else {/* Nojournal mode, all journal mount options are illegal */if (test_opt2(sb, EXPLICIT_JOURNAL_CHECKSUM)) {ext4_msg(sb, KERN_ERR, "can't mount with ""journal_checksum, fs mounted w/o journal");goto failed_mount_wq;}if (test_opt(sb, JOURNAL_ASYNC_COMMIT)) {ext4_msg(sb, KERN_ERR, "can't mount with ""journal_async_commit, fs mounted w/o journal");goto failed_mount_wq;}if (sbi->s_commit_interval != JBD2_DEFAULT_MAX_COMMIT_AGE*HZ) {ext4_msg(sb, KERN_ERR, "can't mount with ""commit=%lu, fs mounted w/o journal",sbi->s_commit_interval / HZ);goto failed_mount_wq;}if (EXT4_MOUNT_DATA_FLAGS &(sbi->s_mount_opt ^ sbi->s_def_mount_opt)) {ext4_msg(sb, KERN_ERR, "can't mount with ""data=, fs mounted w/o journal");goto failed_mount_wq;}sbi->s_def_mount_opt &= ~EXT4_MOUNT_JOURNAL_CHECKSUM;clear_opt(sb, JOURNAL_CHECKSUM);clear_opt(sb, DATA_FLAGS);clear_opt2(sb, JOURNAL_FAST_COMMIT);sbi->s_journal = NULL;needs_recovery = 0;goto no_journal;}if (ext4_has_feature_64bit(sb) &&!jbd2_journal_set_features(EXT4_SB(sb)->s_journal, 0, 0,JBD2_FEATURE_INCOMPAT_64BIT)) {ext4_msg(sb, KERN_ERR, "Failed to set 64-bit journal feature");goto failed_mount_wq;}if (!set_journal_csum_feature_set(sb)) {ext4_msg(sb, KERN_ERR, "Failed to set journal checksum ""feature set");goto failed_mount_wq;}if (test_opt2(sb, JOURNAL_FAST_COMMIT) &&!jbd2_journal_set_features(EXT4_SB(sb)->s_journal, 0, 0,JBD2_FEATURE_INCOMPAT_FAST_COMMIT)) {ext4_msg(sb, KERN_ERR,"Failed to set fast commit journal feature");goto failed_mount_wq;}/* We have now updated the journal if required, so we can* validate the data journaling mode. */switch (test_opt(sb, DATA_FLAGS)) {case 0:/* No mode set, assume a default based on the journal* capabilities: ORDERED_DATA if the journal can* cope, else JOURNAL_DATA*/if (jbd2_journal_check_available_features(sbi->s_journal, 0, 0, JBD2_FEATURE_INCOMPAT_REVOKE)) {set_opt(sb, ORDERED_DATA);sbi->s_def_mount_opt |= EXT4_MOUNT_ORDERED_DATA;} else {set_opt(sb, JOURNAL_DATA);sbi->s_def_mount_opt |= EXT4_MOUNT_JOURNAL_DATA;}break;case EXT4_MOUNT_ORDERED_DATA:case EXT4_MOUNT_WRITEBACK_DATA:if (!jbd2_journal_check_available_features(sbi->s_journal, 0, 0, JBD2_FEATURE_INCOMPAT_REVOKE)) {ext4_msg(sb, KERN_ERR, "Journal does not support ""requested data journaling mode");goto failed_mount_wq;}default:break;}if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_ORDERED_DATA &&test_opt(sb, JOURNAL_ASYNC_COMMIT)) {ext4_msg(sb, KERN_ERR, "can't mount with ""journal_async_commit in data=ordered mode");goto failed_mount_wq;}set_task_ioprio(sbi->s_journal->j_task, journal_ioprio);sbi->s_journal->j_submit_inode_data_buffers =ext4_journal_submit_inode_data_buffers;sbi->s_journal->j_finish_inode_data_buffers =ext4_journal_finish_inode_data_buffers;no_journal:if (!test_opt(sb, NO_MBCACHE)) {sbi->s_ea_block_cache = ext4_xattr_create_cache();if (!sbi->s_ea_block_cache) {ext4_msg(sb, KERN_ERR,"Failed to create ea_block_cache");goto failed_mount_wq;}if (ext4_has_feature_ea_inode(sb)) {sbi->s_ea_inode_cache = ext4_xattr_create_cache();if (!sbi->s_ea_inode_cache) {ext4_msg(sb, KERN_ERR,"Failed to create ea_inode_cache");goto failed_mount_wq;}}}if (ext4_has_feature_verity(sb) && blocksize != PAGE_SIZE) {ext4_msg(sb, KERN_ERR, "Unsupported blocksize for fs-verity");goto failed_mount_wq;}/** Get the # of file system overhead blocks from the* superblock if present.*/sbi->s_overhead = le32_to_cpu(es->s_overhead_clusters);/* ignore the precalculated value if it is ridiculous */if (sbi->s_overhead > ext4_blocks_count(es))sbi->s_overhead = 0;/** If the bigalloc feature is not enabled recalculating the* overhead doesn't take long, so we might as well just redo* it to make sure we are using the correct value.*/if (!ext4_has_feature_bigalloc(sb))sbi->s_overhead = 0;if (sbi->s_overhead == 0) {err = ext4_calculate_overhead(sb);if (err)goto failed_mount_wq;}/** The maximum number of concurrent works can be high and* concurrency isn't really necessary. Limit it to 1.*/EXT4_SB(sb)->rsv_conversion_wq =alloc_workqueue("ext4-rsv-conversion", WQ_MEM_RECLAIM | WQ_UNBOUND, 1);if (!EXT4_SB(sb)->rsv_conversion_wq) {printk(KERN_ERR "EXT4-fs: failed to create workqueue\n");ret = -ENOMEM;goto failed_mount4;}/** The jbd2_journal_load will have done any necessary log recovery,* so we can safely mount the rest of the filesystem now.*/root = ext4_iget(sb, EXT4_ROOT_INO, EXT4_IGET_SPECIAL);if (IS_ERR(root)) {ext4_msg(sb, KERN_ERR, "get root inode failed");ret = PTR_ERR(root);root = NULL;goto failed_mount4;}if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) {ext4_msg(sb, KERN_ERR, "corrupt root inode, run e2fsck");iput(root);goto failed_mount4;}#ifdef CONFIG_UNICODEif (sb->s_encoding)sb->s_d_op = &ext4_dentry_ops;#endifsb->s_root = d_make_root(root);if (!sb->s_root) {ext4_msg(sb, KERN_ERR, "get root dentry failed");ret = -ENOMEM;goto failed_mount4;}ret = ext4_setup_super(sb, es, sb_rdonly(sb));if (ret == -EROFS) {sb->s_flags |= SB_RDONLY;ret = 0;} else if (ret)goto failed_mount4a;ext4_set_resv_clusters(sb);if (test_opt(sb, BLOCK_VALIDITY)) {err = ext4_setup_system_zone(sb);if (err) {ext4_msg(sb, KERN_ERR, "failed to initialize system ""zone (%d)", err);goto failed_mount4a;}}ext4_fc_replay_cleanup(sb);ext4_ext_init(sb);err = ext4_mb_init(sb);if (err) {ext4_msg(sb, KERN_ERR, "failed to initialize mballoc (%d)",err);goto failed_mount5;}/** We can only set up the journal commit callback once* mballoc is initialized*/if (sbi->s_journal)sbi->s_journal->j_commit_callback =ext4_journal_commit_callback;block = ext4_count_free_clusters(sb);ext4_free_blocks_count_set(sbi->s_es, EXT4_C2B(sbi, block));ext4_superblock_csum_set(sb);err = percpu_counter_init(&sbi->s_freeclusters_counter, block,GFP_KERNEL);if (!err) {unsigned long freei = ext4_count_free_inodes(sb);sbi->s_es->s_free_inodes_count = cpu_to_le32(freei);ext4_superblock_csum_set(sb);err = percpu_counter_init(&sbi->s_freeinodes_counter, freei,GFP_KERNEL);}if (!err)err = percpu_counter_init(&sbi->s_dirs_counter,ext4_count_dirs(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(sb, KERN_ERR, "insufficient memory");goto failed_mount6;}if (ext4_has_feature_flex_bg(sb))if (!ext4_fill_flex_info(sb)) {ext4_msg(sb, KERN_ERR,"unable to initialize ""flex_bg meta info!");ret = -ENOMEM;goto failed_mount6;}err = ext4_register_li_request(sb, first_not_zeroed);if (err)goto failed_mount6;err = ext4_register_sysfs(sb);if (err)goto failed_mount7;#ifdef CONFIG_QUOTA/* Enable quota usage during mount. */if (ext4_has_feature_quota(sb) && !sb_rdonly(sb)) {err = ext4_enable_quotas(sb);if (err)goto failed_mount8;}#endif /* CONFIG_QUOTA *//** Save the original bdev mapping's wb_err value which could be* used to detect the metadata async write error.*/spin_lock_init(&sbi->s_bdev_wb_lock);errseq_check_and_advance(&sb->s_bdev->bd_inode->i_mapping->wb_err,&sbi->s_bdev_wb_err);sb->s_bdev->bd_super = sb;EXT4_SB(sb)->s_mount_state |= EXT4_ORPHAN_FS;ext4_orphan_cleanup(sb, es);EXT4_SB(sb)->s_mount_state &= ~EXT4_ORPHAN_FS;if (needs_recovery) {ext4_msg(sb, KERN_INFO, "recovery complete");err = ext4_mark_recovery_complete(sb, es);if (err)goto failed_mount8;}if (EXT4_SB(sb)->s_journal) {if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)descr = " journalled data mode";else if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_ORDERED_DATA)descr = " ordered data mode";elsedescr = " writeback data mode";} elsedescr = "out journal";if (test_opt(sb, DISCARD)) {struct request_queue *q = bdev_get_queue(sb->s_bdev);if (!blk_queue_discard(q))ext4_msg(sb, KERN_WARNING,"mounting with \"discard\" option, but ""the device does not support discard");}if (___ratelimit(&ext4_mount_msg_ratelimit, "EXT4-fs mount"))ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. ""Opts: %.*s%s%s", descr,(int) sizeof(sbi->s_es->s_mount_opts),sbi->s_es->s_mount_opts,*sbi->s_es->s_mount_opts ? "; " : "", orig_data);if (es->s_error_count)mod_timer(&sbi->s_err_report, jiffies + 300*HZ); /* 5 minutes *//* Enable message ratelimiting. Default is 10 messages per 5 secs. */ratelimit_state_init(&sbi->s_err_ratelimit_state, 5 * HZ, 10);ratelimit_state_init(&sbi->s_warning_ratelimit_state, 5 * HZ, 10);ratelimit_state_init(&sbi->s_msg_ratelimit_state, 5 * HZ, 10);atomic_set(&sbi->s_warning_count, 0);atomic_set(&sbi->s_msg_count, 0);kfree(orig_data);return 0;cantfind_ext4:if (!silent)ext4_msg(sb, KERN_ERR, "VFS: Can't find ext4 filesystem");goto failed_mount;failed_mount8:ext4_unregister_sysfs(sb);kobject_put(&sbi->s_kobj);failed_mount7:ext4_unregister_li_request(sb);failed_mount6:ext4_mb_release(sb);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();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);failed_mount5:ext4_ext_release(sb);ext4_release_system_zone(sb);failed_mount4a:dput(sb->s_root);sb->s_root = NULL;failed_mount4:ext4_msg(sb, KERN_ERR, "mount failed");if (EXT4_SB(sb)->rsv_conversion_wq)destroy_workqueue(EXT4_SB(sb)->rsv_conversion_wq);failed_mount_wq: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;if (sbi->s_journal) {jbd2_journal_destroy(sbi->s_journal);sbi->s_journal = NULL;}failed_mount3a:ext4_es_unregister_shrinker(sbi);failed_mount3:del_timer_sync(&sbi->s_err_report);ext4_stop_mmpd(sbi);failed_mount2:rcu_read_lock();group_desc = rcu_dereference(sbi->s_group_desc);for (i = 0; i < db_count; i++)brelse(group_desc[i]);kvfree(group_desc);rcu_read_unlock();failed_mount:if (sbi->s_chksum_driver)crypto_free_shash(sbi->s_chksum_driver);#ifdef CONFIG_UNICODEutf8_unload(sb->s_encoding);#endif#ifdef CONFIG_QUOTAfor (i = 0; i < EXT4_MAXQUOTAS; i++)kfree(get_qf_name(sb, sbi, i));#endiffscrypt_free_dummy_policy(&sbi->s_dummy_enc_policy);/* ext4_blkdev_remove() calls kill_bdev(), release bh before it. */brelse(bh);ext4_blkdev_remove(sbi);out_fail:sb->s_fs_info = NULL;kfree(sbi->s_blockgroup_lock);out_free_base:kfree(sbi);kfree(orig_data);fs_put_dax(dax_dev);return err ? err : ret;}/** Setup any per-fs journal parameters now. We'll do this both on* initial mount, once the journal has been initialised but before we've* done any recovery; and again on any subsequent remount.*/static void ext4_init_journal_params(struct super_block *sb, journal_t *journal){struct ext4_sb_info *sbi = EXT4_SB(sb);journal->j_commit_interval = sbi->s_commit_interval;journal->j_min_batch_time = sbi->s_min_batch_time;journal->j_max_batch_time = sbi->s_max_batch_time;ext4_fc_init(sb, journal);write_lock(&journal->j_state_lock);if (test_opt(sb, BARRIER))journal->j_flags |= JBD2_BARRIER;elsejournal->j_flags &= ~JBD2_BARRIER;if (test_opt(sb, DATA_ERR_ABORT))journal->j_flags |= JBD2_ABORT_ON_SYNCDATA_ERR;elsejournal->j_flags &= ~JBD2_ABORT_ON_SYNCDATA_ERR;write_unlock(&journal->j_state_lock);}