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