18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2017-2018 HUAWEI, Inc. 48c2ecf20Sopenharmony_ci * https://www.huawei.com/ 58c2ecf20Sopenharmony_ci * Created by Gao Xiang <gaoxiang25@huawei.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/buffer_head.h> 98c2ecf20Sopenharmony_ci#include <linux/statfs.h> 108c2ecf20Sopenharmony_ci#include <linux/parser.h> 118c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 128c2ecf20Sopenharmony_ci#include <linux/crc32c.h> 138c2ecf20Sopenharmony_ci#include <linux/fs_context.h> 148c2ecf20Sopenharmony_ci#include <linux/fs_parser.h> 158c2ecf20Sopenharmony_ci#include "xattr.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS 188c2ecf20Sopenharmony_ci#include <trace/events/erofs.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic struct kmem_cache *erofs_inode_cachep __read_mostly; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_civoid _erofs_err(struct super_block *sb, const char *function, 238c2ecf20Sopenharmony_ci const char *fmt, ...) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci struct va_format vaf; 268c2ecf20Sopenharmony_ci va_list args; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci va_start(args, fmt); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci vaf.fmt = fmt; 318c2ecf20Sopenharmony_ci vaf.va = &args; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci pr_err("(device %s): %s: %pV", sb->s_id, function, &vaf); 348c2ecf20Sopenharmony_ci va_end(args); 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_civoid _erofs_info(struct super_block *sb, const char *function, 388c2ecf20Sopenharmony_ci const char *fmt, ...) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct va_format vaf; 418c2ecf20Sopenharmony_ci va_list args; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci va_start(args, fmt); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci vaf.fmt = fmt; 468c2ecf20Sopenharmony_ci vaf.va = &args; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci pr_info("(device %s): %pV", sb->s_id, &vaf); 498c2ecf20Sopenharmony_ci va_end(args); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic int erofs_superblock_csum_verify(struct super_block *sb, void *sbdata) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct erofs_super_block *dsb; 558c2ecf20Sopenharmony_ci u32 expected_crc, crc; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci dsb = kmemdup(sbdata + EROFS_SUPER_OFFSET, 588c2ecf20Sopenharmony_ci EROFS_BLKSIZ - EROFS_SUPER_OFFSET, GFP_KERNEL); 598c2ecf20Sopenharmony_ci if (!dsb) 608c2ecf20Sopenharmony_ci return -ENOMEM; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci expected_crc = le32_to_cpu(dsb->checksum); 638c2ecf20Sopenharmony_ci dsb->checksum = 0; 648c2ecf20Sopenharmony_ci /* to allow for x86 boot sectors and other oddities. */ 658c2ecf20Sopenharmony_ci crc = crc32c(~0, dsb, EROFS_BLKSIZ - EROFS_SUPER_OFFSET); 668c2ecf20Sopenharmony_ci kfree(dsb); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (crc != expected_crc) { 698c2ecf20Sopenharmony_ci erofs_err(sb, "invalid checksum 0x%08x, 0x%08x expected", 708c2ecf20Sopenharmony_ci crc, expected_crc); 718c2ecf20Sopenharmony_ci return -EBADMSG; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci return 0; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void erofs_inode_init_once(void *ptr) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct erofs_inode *vi = ptr; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci inode_init_once(&vi->vfs_inode); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic struct inode *erofs_alloc_inode(struct super_block *sb) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct erofs_inode *vi = 868c2ecf20Sopenharmony_ci kmem_cache_alloc(erofs_inode_cachep, GFP_KERNEL); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (!vi) 898c2ecf20Sopenharmony_ci return NULL; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* zero out everything except vfs_inode */ 928c2ecf20Sopenharmony_ci memset(vi, 0, offsetof(struct erofs_inode, vfs_inode)); 938c2ecf20Sopenharmony_ci return &vi->vfs_inode; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic void erofs_free_inode(struct inode *inode) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct erofs_inode *vi = EROFS_I(inode); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* be careful of RCU symlink path */ 1018c2ecf20Sopenharmony_ci if (inode->i_op == &erofs_fast_symlink_iops) 1028c2ecf20Sopenharmony_ci kfree(inode->i_link); 1038c2ecf20Sopenharmony_ci kfree(vi->xattr_shared_xattrs); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci kmem_cache_free(erofs_inode_cachep, vi); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic bool check_layout_compatibility(struct super_block *sb, 1098c2ecf20Sopenharmony_ci struct erofs_super_block *dsb) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci const unsigned int feature = le32_to_cpu(dsb->feature_incompat); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci EROFS_SB(sb)->feature_incompat = feature; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* check if current kernel meets all mandatory requirements */ 1168c2ecf20Sopenharmony_ci if (feature & (~EROFS_ALL_FEATURE_INCOMPAT)) { 1178c2ecf20Sopenharmony_ci erofs_err(sb, 1188c2ecf20Sopenharmony_ci "unidentified incompatible feature %x, please upgrade kernel version", 1198c2ecf20Sopenharmony_ci feature & ~EROFS_ALL_FEATURE_INCOMPAT); 1208c2ecf20Sopenharmony_ci return false; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci return true; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int erofs_read_superblock(struct super_block *sb) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct erofs_sb_info *sbi; 1288c2ecf20Sopenharmony_ci struct page *page; 1298c2ecf20Sopenharmony_ci struct erofs_super_block *dsb; 1308c2ecf20Sopenharmony_ci unsigned int blkszbits; 1318c2ecf20Sopenharmony_ci void *data; 1328c2ecf20Sopenharmony_ci int ret; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci page = read_mapping_page(sb->s_bdev->bd_inode->i_mapping, 0, NULL); 1358c2ecf20Sopenharmony_ci if (IS_ERR(page)) { 1368c2ecf20Sopenharmony_ci erofs_err(sb, "cannot read erofs superblock"); 1378c2ecf20Sopenharmony_ci return PTR_ERR(page); 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci sbi = EROFS_SB(sb); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci data = kmap(page); 1438c2ecf20Sopenharmony_ci dsb = (struct erofs_super_block *)(data + EROFS_SUPER_OFFSET); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci ret = -EINVAL; 1468c2ecf20Sopenharmony_ci if (le32_to_cpu(dsb->magic) != EROFS_SUPER_MAGIC_V1) { 1478c2ecf20Sopenharmony_ci erofs_err(sb, "cannot find valid erofs superblock"); 1488c2ecf20Sopenharmony_ci goto out; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci sbi->feature_compat = le32_to_cpu(dsb->feature_compat); 1528c2ecf20Sopenharmony_ci if (sbi->feature_compat & EROFS_FEATURE_COMPAT_SB_CHKSUM) { 1538c2ecf20Sopenharmony_ci ret = erofs_superblock_csum_verify(sb, data); 1548c2ecf20Sopenharmony_ci if (ret) 1558c2ecf20Sopenharmony_ci goto out; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci ret = -EINVAL; 1598c2ecf20Sopenharmony_ci blkszbits = dsb->blkszbits; 1608c2ecf20Sopenharmony_ci /* 9(512 bytes) + LOG_SECTORS_PER_BLOCK == LOG_BLOCK_SIZE */ 1618c2ecf20Sopenharmony_ci if (blkszbits != LOG_BLOCK_SIZE) { 1628c2ecf20Sopenharmony_ci erofs_err(sb, "blkszbits %u isn't supported on this platform", 1638c2ecf20Sopenharmony_ci blkszbits); 1648c2ecf20Sopenharmony_ci goto out; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (!check_layout_compatibility(sb, dsb)) 1688c2ecf20Sopenharmony_ci goto out; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci sbi->blocks = le32_to_cpu(dsb->blocks); 1718c2ecf20Sopenharmony_ci sbi->meta_blkaddr = le32_to_cpu(dsb->meta_blkaddr); 1728c2ecf20Sopenharmony_ci#ifdef CONFIG_EROFS_FS_XATTR 1738c2ecf20Sopenharmony_ci sbi->xattr_blkaddr = le32_to_cpu(dsb->xattr_blkaddr); 1748c2ecf20Sopenharmony_ci#endif 1758c2ecf20Sopenharmony_ci sbi->islotbits = ilog2(sizeof(struct erofs_inode_compact)); 1768c2ecf20Sopenharmony_ci sbi->root_nid = le16_to_cpu(dsb->root_nid); 1778c2ecf20Sopenharmony_ci sbi->inos = le64_to_cpu(dsb->inos); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci sbi->build_time = le64_to_cpu(dsb->build_time); 1808c2ecf20Sopenharmony_ci sbi->build_time_nsec = le32_to_cpu(dsb->build_time_nsec); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci memcpy(&sb->s_uuid, dsb->uuid, sizeof(dsb->uuid)); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci ret = strscpy(sbi->volume_name, dsb->volume_name, 1858c2ecf20Sopenharmony_ci sizeof(dsb->volume_name)); 1868c2ecf20Sopenharmony_ci if (ret < 0) { /* -E2BIG */ 1878c2ecf20Sopenharmony_ci erofs_err(sb, "bad volume name without NIL terminator"); 1888c2ecf20Sopenharmony_ci ret = -EFSCORRUPTED; 1898c2ecf20Sopenharmony_ci goto out; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci ret = 0; 1928c2ecf20Sopenharmony_ciout: 1938c2ecf20Sopenharmony_ci kunmap(page); 1948c2ecf20Sopenharmony_ci put_page(page); 1958c2ecf20Sopenharmony_ci return ret; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/* set up default EROFS parameters */ 1998c2ecf20Sopenharmony_cistatic void erofs_default_options(struct erofs_fs_context *ctx) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci#ifdef CONFIG_EROFS_FS_ZIP 2028c2ecf20Sopenharmony_ci ctx->cache_strategy = EROFS_ZIP_CACHE_READAROUND; 2038c2ecf20Sopenharmony_ci ctx->max_sync_decompress_pages = 3; 2048c2ecf20Sopenharmony_ci#endif 2058c2ecf20Sopenharmony_ci#ifdef CONFIG_EROFS_FS_XATTR 2068c2ecf20Sopenharmony_ci set_opt(ctx, XATTR_USER); 2078c2ecf20Sopenharmony_ci#endif 2088c2ecf20Sopenharmony_ci#ifdef CONFIG_EROFS_FS_POSIX_ACL 2098c2ecf20Sopenharmony_ci set_opt(ctx, POSIX_ACL); 2108c2ecf20Sopenharmony_ci#endif 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cienum { 2148c2ecf20Sopenharmony_ci Opt_user_xattr, 2158c2ecf20Sopenharmony_ci Opt_acl, 2168c2ecf20Sopenharmony_ci Opt_cache_strategy, 2178c2ecf20Sopenharmony_ci Opt_err 2188c2ecf20Sopenharmony_ci}; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic const struct constant_table erofs_param_cache_strategy[] = { 2218c2ecf20Sopenharmony_ci {"disabled", EROFS_ZIP_CACHE_DISABLED}, 2228c2ecf20Sopenharmony_ci {"readahead", EROFS_ZIP_CACHE_READAHEAD}, 2238c2ecf20Sopenharmony_ci {"readaround", EROFS_ZIP_CACHE_READAROUND}, 2248c2ecf20Sopenharmony_ci {} 2258c2ecf20Sopenharmony_ci}; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic const struct fs_parameter_spec erofs_fs_parameters[] = { 2288c2ecf20Sopenharmony_ci fsparam_flag_no("user_xattr", Opt_user_xattr), 2298c2ecf20Sopenharmony_ci fsparam_flag_no("acl", Opt_acl), 2308c2ecf20Sopenharmony_ci fsparam_enum("cache_strategy", Opt_cache_strategy, 2318c2ecf20Sopenharmony_ci erofs_param_cache_strategy), 2328c2ecf20Sopenharmony_ci {} 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic int erofs_fc_parse_param(struct fs_context *fc, 2368c2ecf20Sopenharmony_ci struct fs_parameter *param) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct erofs_fs_context *ctx __maybe_unused = fc->fs_private; 2398c2ecf20Sopenharmony_ci struct fs_parse_result result; 2408c2ecf20Sopenharmony_ci int opt; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci opt = fs_parse(fc, erofs_fs_parameters, param, &result); 2438c2ecf20Sopenharmony_ci if (opt < 0) 2448c2ecf20Sopenharmony_ci return opt; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci switch (opt) { 2478c2ecf20Sopenharmony_ci case Opt_user_xattr: 2488c2ecf20Sopenharmony_ci#ifdef CONFIG_EROFS_FS_XATTR 2498c2ecf20Sopenharmony_ci if (result.boolean) 2508c2ecf20Sopenharmony_ci set_opt(ctx, XATTR_USER); 2518c2ecf20Sopenharmony_ci else 2528c2ecf20Sopenharmony_ci clear_opt(ctx, XATTR_USER); 2538c2ecf20Sopenharmony_ci#else 2548c2ecf20Sopenharmony_ci errorfc(fc, "{,no}user_xattr options not supported"); 2558c2ecf20Sopenharmony_ci#endif 2568c2ecf20Sopenharmony_ci break; 2578c2ecf20Sopenharmony_ci case Opt_acl: 2588c2ecf20Sopenharmony_ci#ifdef CONFIG_EROFS_FS_POSIX_ACL 2598c2ecf20Sopenharmony_ci if (result.boolean) 2608c2ecf20Sopenharmony_ci set_opt(ctx, POSIX_ACL); 2618c2ecf20Sopenharmony_ci else 2628c2ecf20Sopenharmony_ci clear_opt(ctx, POSIX_ACL); 2638c2ecf20Sopenharmony_ci#else 2648c2ecf20Sopenharmony_ci errorfc(fc, "{,no}acl options not supported"); 2658c2ecf20Sopenharmony_ci#endif 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci case Opt_cache_strategy: 2688c2ecf20Sopenharmony_ci#ifdef CONFIG_EROFS_FS_ZIP 2698c2ecf20Sopenharmony_ci ctx->cache_strategy = result.uint_32; 2708c2ecf20Sopenharmony_ci#else 2718c2ecf20Sopenharmony_ci errorfc(fc, "compression not supported, cache_strategy ignored"); 2728c2ecf20Sopenharmony_ci#endif 2738c2ecf20Sopenharmony_ci break; 2748c2ecf20Sopenharmony_ci default: 2758c2ecf20Sopenharmony_ci return -ENOPARAM; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci return 0; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci#ifdef CONFIG_EROFS_FS_ZIP 2818c2ecf20Sopenharmony_cistatic const struct address_space_operations managed_cache_aops; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int erofs_managed_cache_releasepage(struct page *page, gfp_t gfp_mask) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci int ret = 1; /* 0 - busy */ 2868c2ecf20Sopenharmony_ci struct address_space *const mapping = page->mapping; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci DBG_BUGON(!PageLocked(page)); 2898c2ecf20Sopenharmony_ci DBG_BUGON(mapping->a_ops != &managed_cache_aops); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (PagePrivate(page)) 2928c2ecf20Sopenharmony_ci ret = erofs_try_to_free_cached_page(mapping, page); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return ret; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic void erofs_managed_cache_invalidatepage(struct page *page, 2988c2ecf20Sopenharmony_ci unsigned int offset, 2998c2ecf20Sopenharmony_ci unsigned int length) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci const unsigned int stop = length + offset; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci DBG_BUGON(!PageLocked(page)); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* Check for potential overflow in debug mode */ 3068c2ecf20Sopenharmony_ci DBG_BUGON(stop > PAGE_SIZE || stop < length); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (offset == 0 && stop == PAGE_SIZE) 3098c2ecf20Sopenharmony_ci while (!erofs_managed_cache_releasepage(page, GFP_NOFS)) 3108c2ecf20Sopenharmony_ci cond_resched(); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic const struct address_space_operations managed_cache_aops = { 3148c2ecf20Sopenharmony_ci .releasepage = erofs_managed_cache_releasepage, 3158c2ecf20Sopenharmony_ci .invalidatepage = erofs_managed_cache_invalidatepage, 3168c2ecf20Sopenharmony_ci}; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int erofs_init_managed_cache(struct super_block *sb) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct erofs_sb_info *const sbi = EROFS_SB(sb); 3218c2ecf20Sopenharmony_ci struct inode *const inode = new_inode(sb); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (!inode) 3248c2ecf20Sopenharmony_ci return -ENOMEM; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci set_nlink(inode, 1); 3278c2ecf20Sopenharmony_ci inode->i_size = OFFSET_MAX; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci inode->i_mapping->a_ops = &managed_cache_aops; 3308c2ecf20Sopenharmony_ci mapping_set_gfp_mask(inode->i_mapping, 3318c2ecf20Sopenharmony_ci GFP_NOFS | __GFP_HIGHMEM | __GFP_MOVABLE); 3328c2ecf20Sopenharmony_ci sbi->managed_cache = inode; 3338c2ecf20Sopenharmony_ci return 0; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci#else 3368c2ecf20Sopenharmony_cistatic int erofs_init_managed_cache(struct super_block *sb) { return 0; } 3378c2ecf20Sopenharmony_ci#endif 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct inode *inode; 3428c2ecf20Sopenharmony_ci struct erofs_sb_info *sbi; 3438c2ecf20Sopenharmony_ci struct erofs_fs_context *ctx = fc->fs_private; 3448c2ecf20Sopenharmony_ci int err; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci sb->s_magic = EROFS_SUPER_MAGIC; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (!sb_set_blocksize(sb, EROFS_BLKSIZ)) { 3498c2ecf20Sopenharmony_ci erofs_err(sb, "failed to set erofs blksize"); 3508c2ecf20Sopenharmony_ci return -EINVAL; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); 3548c2ecf20Sopenharmony_ci if (!sbi) 3558c2ecf20Sopenharmony_ci return -ENOMEM; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci sb->s_fs_info = sbi; 3588c2ecf20Sopenharmony_ci err = erofs_read_superblock(sb); 3598c2ecf20Sopenharmony_ci if (err) 3608c2ecf20Sopenharmony_ci return err; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci sb->s_flags |= SB_RDONLY | SB_NOATIME; 3638c2ecf20Sopenharmony_ci sb->s_maxbytes = MAX_LFS_FILESIZE; 3648c2ecf20Sopenharmony_ci sb->s_time_gran = 1; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci sb->s_op = &erofs_sops; 3678c2ecf20Sopenharmony_ci sb->s_xattr = erofs_xattr_handlers; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (test_opt(ctx, POSIX_ACL)) 3708c2ecf20Sopenharmony_ci sb->s_flags |= SB_POSIXACL; 3718c2ecf20Sopenharmony_ci else 3728c2ecf20Sopenharmony_ci sb->s_flags &= ~SB_POSIXACL; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci sbi->ctx = *ctx; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci#ifdef CONFIG_EROFS_FS_ZIP 3778c2ecf20Sopenharmony_ci xa_init(&sbi->managed_pslots); 3788c2ecf20Sopenharmony_ci#endif 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* get the root inode */ 3818c2ecf20Sopenharmony_ci inode = erofs_iget(sb, ROOT_NID(sbi), true); 3828c2ecf20Sopenharmony_ci if (IS_ERR(inode)) 3838c2ecf20Sopenharmony_ci return PTR_ERR(inode); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (!S_ISDIR(inode->i_mode)) { 3868c2ecf20Sopenharmony_ci erofs_err(sb, "rootino(nid %llu) is not a directory(i_mode %o)", 3878c2ecf20Sopenharmony_ci ROOT_NID(sbi), inode->i_mode); 3888c2ecf20Sopenharmony_ci iput(inode); 3898c2ecf20Sopenharmony_ci return -EINVAL; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci sb->s_root = d_make_root(inode); 3938c2ecf20Sopenharmony_ci if (!sb->s_root) 3948c2ecf20Sopenharmony_ci return -ENOMEM; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci erofs_shrinker_register(sb); 3978c2ecf20Sopenharmony_ci /* sb->s_umount is already locked, SB_ACTIVE and SB_BORN are not set */ 3988c2ecf20Sopenharmony_ci err = erofs_init_managed_cache(sb); 3998c2ecf20Sopenharmony_ci if (err) 4008c2ecf20Sopenharmony_ci return err; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci erofs_info(sb, "mounted with root inode @ nid %llu.", ROOT_NID(sbi)); 4038c2ecf20Sopenharmony_ci return 0; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic int erofs_fc_get_tree(struct fs_context *fc) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci return get_tree_bdev(fc, erofs_fc_fill_super); 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic int erofs_fc_reconfigure(struct fs_context *fc) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct super_block *sb = fc->root->d_sb; 4148c2ecf20Sopenharmony_ci struct erofs_sb_info *sbi = EROFS_SB(sb); 4158c2ecf20Sopenharmony_ci struct erofs_fs_context *ctx = fc->fs_private; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci DBG_BUGON(!sb_rdonly(sb)); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (test_opt(ctx, POSIX_ACL)) 4208c2ecf20Sopenharmony_ci fc->sb_flags |= SB_POSIXACL; 4218c2ecf20Sopenharmony_ci else 4228c2ecf20Sopenharmony_ci fc->sb_flags &= ~SB_POSIXACL; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci sbi->ctx = *ctx; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci fc->sb_flags |= SB_RDONLY; 4278c2ecf20Sopenharmony_ci return 0; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic void erofs_fc_free(struct fs_context *fc) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci kfree(fc->fs_private); 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic const struct fs_context_operations erofs_context_ops = { 4368c2ecf20Sopenharmony_ci .parse_param = erofs_fc_parse_param, 4378c2ecf20Sopenharmony_ci .get_tree = erofs_fc_get_tree, 4388c2ecf20Sopenharmony_ci .reconfigure = erofs_fc_reconfigure, 4398c2ecf20Sopenharmony_ci .free = erofs_fc_free, 4408c2ecf20Sopenharmony_ci}; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int erofs_init_fs_context(struct fs_context *fc) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci fc->fs_private = kzalloc(sizeof(struct erofs_fs_context), GFP_KERNEL); 4458c2ecf20Sopenharmony_ci if (!fc->fs_private) 4468c2ecf20Sopenharmony_ci return -ENOMEM; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* set default mount options */ 4498c2ecf20Sopenharmony_ci erofs_default_options(fc->fs_private); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci fc->ops = &erofs_context_ops; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci return 0; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci/* 4578c2ecf20Sopenharmony_ci * could be triggered after deactivate_locked_super() 4588c2ecf20Sopenharmony_ci * is called, thus including umount and failed to initialize. 4598c2ecf20Sopenharmony_ci */ 4608c2ecf20Sopenharmony_cistatic void erofs_kill_sb(struct super_block *sb) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct erofs_sb_info *sbi; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci WARN_ON(sb->s_magic != EROFS_SUPER_MAGIC); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci kill_block_super(sb); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci sbi = EROFS_SB(sb); 4698c2ecf20Sopenharmony_ci if (!sbi) 4708c2ecf20Sopenharmony_ci return; 4718c2ecf20Sopenharmony_ci kfree(sbi); 4728c2ecf20Sopenharmony_ci sb->s_fs_info = NULL; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci/* called when ->s_root is non-NULL */ 4768c2ecf20Sopenharmony_cistatic void erofs_put_super(struct super_block *sb) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci struct erofs_sb_info *const sbi = EROFS_SB(sb); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci DBG_BUGON(!sbi); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci erofs_shrinker_unregister(sb); 4838c2ecf20Sopenharmony_ci#ifdef CONFIG_EROFS_FS_ZIP 4848c2ecf20Sopenharmony_ci iput(sbi->managed_cache); 4858c2ecf20Sopenharmony_ci sbi->managed_cache = NULL; 4868c2ecf20Sopenharmony_ci#endif 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic struct file_system_type erofs_fs_type = { 4908c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4918c2ecf20Sopenharmony_ci .name = "erofs", 4928c2ecf20Sopenharmony_ci .init_fs_context = erofs_init_fs_context, 4938c2ecf20Sopenharmony_ci .kill_sb = erofs_kill_sb, 4948c2ecf20Sopenharmony_ci .fs_flags = FS_REQUIRES_DEV, 4958c2ecf20Sopenharmony_ci}; 4968c2ecf20Sopenharmony_ciMODULE_ALIAS_FS("erofs"); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic int __init erofs_module_init(void) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci int err; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci erofs_check_ondisk_layout_definitions(); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci erofs_inode_cachep = kmem_cache_create("erofs_inode", 5058c2ecf20Sopenharmony_ci sizeof(struct erofs_inode), 0, 5068c2ecf20Sopenharmony_ci SLAB_RECLAIM_ACCOUNT, 5078c2ecf20Sopenharmony_ci erofs_inode_init_once); 5088c2ecf20Sopenharmony_ci if (!erofs_inode_cachep) { 5098c2ecf20Sopenharmony_ci err = -ENOMEM; 5108c2ecf20Sopenharmony_ci goto icache_err; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci err = erofs_init_shrinker(); 5148c2ecf20Sopenharmony_ci if (err) 5158c2ecf20Sopenharmony_ci goto shrinker_err; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci err = z_erofs_init_zip_subsystem(); 5188c2ecf20Sopenharmony_ci if (err) 5198c2ecf20Sopenharmony_ci goto zip_err; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci err = register_filesystem(&erofs_fs_type); 5228c2ecf20Sopenharmony_ci if (err) 5238c2ecf20Sopenharmony_ci goto fs_err; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci return 0; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cifs_err: 5288c2ecf20Sopenharmony_ci z_erofs_exit_zip_subsystem(); 5298c2ecf20Sopenharmony_cizip_err: 5308c2ecf20Sopenharmony_ci erofs_exit_shrinker(); 5318c2ecf20Sopenharmony_cishrinker_err: 5328c2ecf20Sopenharmony_ci kmem_cache_destroy(erofs_inode_cachep); 5338c2ecf20Sopenharmony_ciicache_err: 5348c2ecf20Sopenharmony_ci return err; 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic void __exit erofs_module_exit(void) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci unregister_filesystem(&erofs_fs_type); 5408c2ecf20Sopenharmony_ci z_erofs_exit_zip_subsystem(); 5418c2ecf20Sopenharmony_ci erofs_exit_shrinker(); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* Ensure all RCU free inodes are safe before cache is destroyed. */ 5448c2ecf20Sopenharmony_ci rcu_barrier(); 5458c2ecf20Sopenharmony_ci kmem_cache_destroy(erofs_inode_cachep); 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci/* get filesystem statistics */ 5498c2ecf20Sopenharmony_cistatic int erofs_statfs(struct dentry *dentry, struct kstatfs *buf) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci struct super_block *sb = dentry->d_sb; 5528c2ecf20Sopenharmony_ci struct erofs_sb_info *sbi = EROFS_SB(sb); 5538c2ecf20Sopenharmony_ci u64 id = huge_encode_dev(sb->s_bdev->bd_dev); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci buf->f_type = sb->s_magic; 5568c2ecf20Sopenharmony_ci buf->f_bsize = EROFS_BLKSIZ; 5578c2ecf20Sopenharmony_ci buf->f_blocks = sbi->blocks; 5588c2ecf20Sopenharmony_ci buf->f_bfree = buf->f_bavail = 0; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci buf->f_files = ULLONG_MAX; 5618c2ecf20Sopenharmony_ci buf->f_ffree = ULLONG_MAX - sbi->inos; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci buf->f_namelen = EROFS_NAME_LEN; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci buf->f_fsid = u64_to_fsid(id); 5668c2ecf20Sopenharmony_ci return 0; 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic int erofs_show_options(struct seq_file *seq, struct dentry *root) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci struct erofs_sb_info *sbi __maybe_unused = EROFS_SB(root->d_sb); 5728c2ecf20Sopenharmony_ci struct erofs_fs_context *ctx __maybe_unused = &sbi->ctx; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci#ifdef CONFIG_EROFS_FS_XATTR 5758c2ecf20Sopenharmony_ci if (test_opt(ctx, XATTR_USER)) 5768c2ecf20Sopenharmony_ci seq_puts(seq, ",user_xattr"); 5778c2ecf20Sopenharmony_ci else 5788c2ecf20Sopenharmony_ci seq_puts(seq, ",nouser_xattr"); 5798c2ecf20Sopenharmony_ci#endif 5808c2ecf20Sopenharmony_ci#ifdef CONFIG_EROFS_FS_POSIX_ACL 5818c2ecf20Sopenharmony_ci if (test_opt(ctx, POSIX_ACL)) 5828c2ecf20Sopenharmony_ci seq_puts(seq, ",acl"); 5838c2ecf20Sopenharmony_ci else 5848c2ecf20Sopenharmony_ci seq_puts(seq, ",noacl"); 5858c2ecf20Sopenharmony_ci#endif 5868c2ecf20Sopenharmony_ci#ifdef CONFIG_EROFS_FS_ZIP 5878c2ecf20Sopenharmony_ci if (ctx->cache_strategy == EROFS_ZIP_CACHE_DISABLED) 5888c2ecf20Sopenharmony_ci seq_puts(seq, ",cache_strategy=disabled"); 5898c2ecf20Sopenharmony_ci else if (ctx->cache_strategy == EROFS_ZIP_CACHE_READAHEAD) 5908c2ecf20Sopenharmony_ci seq_puts(seq, ",cache_strategy=readahead"); 5918c2ecf20Sopenharmony_ci else if (ctx->cache_strategy == EROFS_ZIP_CACHE_READAROUND) 5928c2ecf20Sopenharmony_ci seq_puts(seq, ",cache_strategy=readaround"); 5938c2ecf20Sopenharmony_ci#endif 5948c2ecf20Sopenharmony_ci return 0; 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ciconst struct super_operations erofs_sops = { 5988c2ecf20Sopenharmony_ci .put_super = erofs_put_super, 5998c2ecf20Sopenharmony_ci .alloc_inode = erofs_alloc_inode, 6008c2ecf20Sopenharmony_ci .free_inode = erofs_free_inode, 6018c2ecf20Sopenharmony_ci .statfs = erofs_statfs, 6028c2ecf20Sopenharmony_ci .show_options = erofs_show_options, 6038c2ecf20Sopenharmony_ci}; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cimodule_init(erofs_module_init); 6068c2ecf20Sopenharmony_cimodule_exit(erofs_module_exit); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Enhanced ROM File System"); 6098c2ecf20Sopenharmony_ciMODULE_AUTHOR("Gao Xiang, Chao Yu, Miao Xie, CONSUMER BG, HUAWEI Inc."); 6108c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6118c2ecf20Sopenharmony_ci 612