162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2017-2018 HUAWEI, Inc.
462306a36Sopenharmony_ci *             https://www.huawei.com/
562306a36Sopenharmony_ci * Copyright (C) 2021, Alibaba Cloud
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/statfs.h>
962306a36Sopenharmony_ci#include <linux/parser.h>
1062306a36Sopenharmony_ci#include <linux/seq_file.h>
1162306a36Sopenharmony_ci#include <linux/crc32c.h>
1262306a36Sopenharmony_ci#include <linux/fs_context.h>
1362306a36Sopenharmony_ci#include <linux/fs_parser.h>
1462306a36Sopenharmony_ci#include <linux/dax.h>
1562306a36Sopenharmony_ci#include <linux/exportfs.h>
1662306a36Sopenharmony_ci#include "xattr.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define CREATE_TRACE_POINTS
1962306a36Sopenharmony_ci#include <trace/events/erofs.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic struct kmem_cache *erofs_inode_cachep __read_mostly;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_civoid _erofs_err(struct super_block *sb, const char *func, const char *fmt, ...)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	struct va_format vaf;
2662306a36Sopenharmony_ci	va_list args;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	va_start(args, fmt);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	vaf.fmt = fmt;
3162306a36Sopenharmony_ci	vaf.va = &args;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	pr_err("(device %s): %s: %pV", sb->s_id, func, &vaf);
3462306a36Sopenharmony_ci	va_end(args);
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_civoid _erofs_info(struct super_block *sb, const char *func, const char *fmt, ...)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	struct va_format vaf;
4062306a36Sopenharmony_ci	va_list args;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	va_start(args, fmt);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	vaf.fmt = fmt;
4562306a36Sopenharmony_ci	vaf.va = &args;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	pr_info("(device %s): %pV", sb->s_id, &vaf);
4862306a36Sopenharmony_ci	va_end(args);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic int erofs_superblock_csum_verify(struct super_block *sb, void *sbdata)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	size_t len = 1 << EROFS_SB(sb)->blkszbits;
5462306a36Sopenharmony_ci	struct erofs_super_block *dsb;
5562306a36Sopenharmony_ci	u32 expected_crc, crc;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (len > EROFS_SUPER_OFFSET)
5862306a36Sopenharmony_ci		len -= EROFS_SUPER_OFFSET;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	dsb = kmemdup(sbdata + EROFS_SUPER_OFFSET, len, GFP_KERNEL);
6162306a36Sopenharmony_ci	if (!dsb)
6262306a36Sopenharmony_ci		return -ENOMEM;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	expected_crc = le32_to_cpu(dsb->checksum);
6562306a36Sopenharmony_ci	dsb->checksum = 0;
6662306a36Sopenharmony_ci	/* to allow for x86 boot sectors and other oddities. */
6762306a36Sopenharmony_ci	crc = crc32c(~0, dsb, len);
6862306a36Sopenharmony_ci	kfree(dsb);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (crc != expected_crc) {
7162306a36Sopenharmony_ci		erofs_err(sb, "invalid checksum 0x%08x, 0x%08x expected",
7262306a36Sopenharmony_ci			  crc, expected_crc);
7362306a36Sopenharmony_ci		return -EBADMSG;
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci	return 0;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void erofs_inode_init_once(void *ptr)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct erofs_inode *vi = ptr;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	inode_init_once(&vi->vfs_inode);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic struct inode *erofs_alloc_inode(struct super_block *sb)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct erofs_inode *vi =
8862306a36Sopenharmony_ci		alloc_inode_sb(sb, erofs_inode_cachep, GFP_KERNEL);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (!vi)
9162306a36Sopenharmony_ci		return NULL;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/* zero out everything except vfs_inode */
9462306a36Sopenharmony_ci	memset(vi, 0, offsetof(struct erofs_inode, vfs_inode));
9562306a36Sopenharmony_ci	return &vi->vfs_inode;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic void erofs_free_inode(struct inode *inode)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct erofs_inode *vi = EROFS_I(inode);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (inode->i_op == &erofs_fast_symlink_iops)
10362306a36Sopenharmony_ci		kfree(inode->i_link);
10462306a36Sopenharmony_ci	kfree(vi->xattr_shared_xattrs);
10562306a36Sopenharmony_ci	kmem_cache_free(erofs_inode_cachep, vi);
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic bool check_layout_compatibility(struct super_block *sb,
10962306a36Sopenharmony_ci				       struct erofs_super_block *dsb)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	const unsigned int feature = le32_to_cpu(dsb->feature_incompat);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	EROFS_SB(sb)->feature_incompat = feature;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	/* check if current kernel meets all mandatory requirements */
11662306a36Sopenharmony_ci	if (feature & (~EROFS_ALL_FEATURE_INCOMPAT)) {
11762306a36Sopenharmony_ci		erofs_err(sb, "unidentified incompatible feature %x, please upgrade kernel",
11862306a36Sopenharmony_ci			   feature & ~EROFS_ALL_FEATURE_INCOMPAT);
11962306a36Sopenharmony_ci		return false;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci	return true;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/* read variable-sized metadata, offset will be aligned by 4-byte */
12562306a36Sopenharmony_civoid *erofs_read_metadata(struct super_block *sb, struct erofs_buf *buf,
12662306a36Sopenharmony_ci			  erofs_off_t *offset, int *lengthp)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	u8 *buffer, *ptr;
12962306a36Sopenharmony_ci	int len, i, cnt;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	*offset = round_up(*offset, 4);
13262306a36Sopenharmony_ci	ptr = erofs_bread(buf, erofs_blknr(sb, *offset), EROFS_KMAP);
13362306a36Sopenharmony_ci	if (IS_ERR(ptr))
13462306a36Sopenharmony_ci		return ptr;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	len = le16_to_cpu(*(__le16 *)&ptr[erofs_blkoff(sb, *offset)]);
13762306a36Sopenharmony_ci	if (!len)
13862306a36Sopenharmony_ci		len = U16_MAX + 1;
13962306a36Sopenharmony_ci	buffer = kmalloc(len, GFP_KERNEL);
14062306a36Sopenharmony_ci	if (!buffer)
14162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
14262306a36Sopenharmony_ci	*offset += sizeof(__le16);
14362306a36Sopenharmony_ci	*lengthp = len;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	for (i = 0; i < len; i += cnt) {
14662306a36Sopenharmony_ci		cnt = min_t(int, sb->s_blocksize - erofs_blkoff(sb, *offset),
14762306a36Sopenharmony_ci			    len - i);
14862306a36Sopenharmony_ci		ptr = erofs_bread(buf, erofs_blknr(sb, *offset), EROFS_KMAP);
14962306a36Sopenharmony_ci		if (IS_ERR(ptr)) {
15062306a36Sopenharmony_ci			kfree(buffer);
15162306a36Sopenharmony_ci			return ptr;
15262306a36Sopenharmony_ci		}
15362306a36Sopenharmony_ci		memcpy(buffer + i, ptr + erofs_blkoff(sb, *offset), cnt);
15462306a36Sopenharmony_ci		*offset += cnt;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci	return buffer;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci#ifndef CONFIG_EROFS_FS_ZIP
16062306a36Sopenharmony_cistatic int z_erofs_parse_cfgs(struct super_block *sb,
16162306a36Sopenharmony_ci			      struct erofs_super_block *dsb)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	if (!dsb->u1.available_compr_algs)
16462306a36Sopenharmony_ci		return 0;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	erofs_err(sb, "compression disabled, unable to mount compressed EROFS");
16762306a36Sopenharmony_ci	return -EOPNOTSUPP;
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci#endif
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic int erofs_init_device(struct erofs_buf *buf, struct super_block *sb,
17262306a36Sopenharmony_ci			     struct erofs_device_info *dif, erofs_off_t *pos)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	struct erofs_sb_info *sbi = EROFS_SB(sb);
17562306a36Sopenharmony_ci	struct erofs_fscache *fscache;
17662306a36Sopenharmony_ci	struct erofs_deviceslot *dis;
17762306a36Sopenharmony_ci	struct bdev_handle *bdev_handle;
17862306a36Sopenharmony_ci	void *ptr;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	ptr = erofs_read_metabuf(buf, sb, erofs_blknr(sb, *pos), EROFS_KMAP);
18162306a36Sopenharmony_ci	if (IS_ERR(ptr))
18262306a36Sopenharmony_ci		return PTR_ERR(ptr);
18362306a36Sopenharmony_ci	dis = ptr + erofs_blkoff(sb, *pos);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	if (!sbi->devs->flatdev && !dif->path) {
18662306a36Sopenharmony_ci		if (!dis->tag[0]) {
18762306a36Sopenharmony_ci			erofs_err(sb, "empty device tag @ pos %llu", *pos);
18862306a36Sopenharmony_ci			return -EINVAL;
18962306a36Sopenharmony_ci		}
19062306a36Sopenharmony_ci		dif->path = kmemdup_nul(dis->tag, sizeof(dis->tag), GFP_KERNEL);
19162306a36Sopenharmony_ci		if (!dif->path)
19262306a36Sopenharmony_ci			return -ENOMEM;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (erofs_is_fscache_mode(sb)) {
19662306a36Sopenharmony_ci		fscache = erofs_fscache_register_cookie(sb, dif->path, 0);
19762306a36Sopenharmony_ci		if (IS_ERR(fscache))
19862306a36Sopenharmony_ci			return PTR_ERR(fscache);
19962306a36Sopenharmony_ci		dif->fscache = fscache;
20062306a36Sopenharmony_ci	} else if (!sbi->devs->flatdev) {
20162306a36Sopenharmony_ci		bdev_handle = bdev_open_by_path(dif->path, BLK_OPEN_READ,
20262306a36Sopenharmony_ci						sb->s_type, NULL);
20362306a36Sopenharmony_ci		if (IS_ERR(bdev_handle))
20462306a36Sopenharmony_ci			return PTR_ERR(bdev_handle);
20562306a36Sopenharmony_ci		dif->bdev_handle = bdev_handle;
20662306a36Sopenharmony_ci		dif->dax_dev = fs_dax_get_by_bdev(bdev_handle->bdev,
20762306a36Sopenharmony_ci				&dif->dax_part_off, NULL, NULL);
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	dif->blocks = le32_to_cpu(dis->blocks);
21162306a36Sopenharmony_ci	dif->mapped_blkaddr = le32_to_cpu(dis->mapped_blkaddr);
21262306a36Sopenharmony_ci	sbi->total_blocks += dif->blocks;
21362306a36Sopenharmony_ci	*pos += EROFS_DEVT_SLOT_SIZE;
21462306a36Sopenharmony_ci	return 0;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic int erofs_scan_devices(struct super_block *sb,
21862306a36Sopenharmony_ci			      struct erofs_super_block *dsb)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	struct erofs_sb_info *sbi = EROFS_SB(sb);
22162306a36Sopenharmony_ci	unsigned int ondisk_extradevs;
22262306a36Sopenharmony_ci	erofs_off_t pos;
22362306a36Sopenharmony_ci	struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
22462306a36Sopenharmony_ci	struct erofs_device_info *dif;
22562306a36Sopenharmony_ci	int id, err = 0;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	sbi->total_blocks = sbi->primarydevice_blocks;
22862306a36Sopenharmony_ci	if (!erofs_sb_has_device_table(sbi))
22962306a36Sopenharmony_ci		ondisk_extradevs = 0;
23062306a36Sopenharmony_ci	else
23162306a36Sopenharmony_ci		ondisk_extradevs = le16_to_cpu(dsb->extra_devices);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (sbi->devs->extra_devices &&
23462306a36Sopenharmony_ci	    ondisk_extradevs != sbi->devs->extra_devices) {
23562306a36Sopenharmony_ci		erofs_err(sb, "extra devices don't match (ondisk %u, given %u)",
23662306a36Sopenharmony_ci			  ondisk_extradevs, sbi->devs->extra_devices);
23762306a36Sopenharmony_ci		return -EINVAL;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci	if (!ondisk_extradevs)
24062306a36Sopenharmony_ci		return 0;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (!sbi->devs->extra_devices && !erofs_is_fscache_mode(sb))
24362306a36Sopenharmony_ci		sbi->devs->flatdev = true;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	sbi->device_id_mask = roundup_pow_of_two(ondisk_extradevs + 1) - 1;
24662306a36Sopenharmony_ci	pos = le16_to_cpu(dsb->devt_slotoff) * EROFS_DEVT_SLOT_SIZE;
24762306a36Sopenharmony_ci	down_read(&sbi->devs->rwsem);
24862306a36Sopenharmony_ci	if (sbi->devs->extra_devices) {
24962306a36Sopenharmony_ci		idr_for_each_entry(&sbi->devs->tree, dif, id) {
25062306a36Sopenharmony_ci			err = erofs_init_device(&buf, sb, dif, &pos);
25162306a36Sopenharmony_ci			if (err)
25262306a36Sopenharmony_ci				break;
25362306a36Sopenharmony_ci		}
25462306a36Sopenharmony_ci	} else {
25562306a36Sopenharmony_ci		for (id = 0; id < ondisk_extradevs; id++) {
25662306a36Sopenharmony_ci			dif = kzalloc(sizeof(*dif), GFP_KERNEL);
25762306a36Sopenharmony_ci			if (!dif) {
25862306a36Sopenharmony_ci				err = -ENOMEM;
25962306a36Sopenharmony_ci				break;
26062306a36Sopenharmony_ci			}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci			err = idr_alloc(&sbi->devs->tree, dif, 0, 0, GFP_KERNEL);
26362306a36Sopenharmony_ci			if (err < 0) {
26462306a36Sopenharmony_ci				kfree(dif);
26562306a36Sopenharmony_ci				break;
26662306a36Sopenharmony_ci			}
26762306a36Sopenharmony_ci			++sbi->devs->extra_devices;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci			err = erofs_init_device(&buf, sb, dif, &pos);
27062306a36Sopenharmony_ci			if (err)
27162306a36Sopenharmony_ci				break;
27262306a36Sopenharmony_ci		}
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci	up_read(&sbi->devs->rwsem);
27562306a36Sopenharmony_ci	erofs_put_metabuf(&buf);
27662306a36Sopenharmony_ci	return err;
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic int erofs_read_superblock(struct super_block *sb)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	struct erofs_sb_info *sbi;
28262306a36Sopenharmony_ci	struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
28362306a36Sopenharmony_ci	struct erofs_super_block *dsb;
28462306a36Sopenharmony_ci	void *data;
28562306a36Sopenharmony_ci	int ret;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	data = erofs_read_metabuf(&buf, sb, 0, EROFS_KMAP);
28862306a36Sopenharmony_ci	if (IS_ERR(data)) {
28962306a36Sopenharmony_ci		erofs_err(sb, "cannot read erofs superblock");
29062306a36Sopenharmony_ci		return PTR_ERR(data);
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	sbi = EROFS_SB(sb);
29462306a36Sopenharmony_ci	dsb = (struct erofs_super_block *)(data + EROFS_SUPER_OFFSET);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	ret = -EINVAL;
29762306a36Sopenharmony_ci	if (le32_to_cpu(dsb->magic) != EROFS_SUPER_MAGIC_V1) {
29862306a36Sopenharmony_ci		erofs_err(sb, "cannot find valid erofs superblock");
29962306a36Sopenharmony_ci		goto out;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	sbi->blkszbits  = dsb->blkszbits;
30362306a36Sopenharmony_ci	if (sbi->blkszbits < 9 || sbi->blkszbits > PAGE_SHIFT) {
30462306a36Sopenharmony_ci		erofs_err(sb, "blkszbits %u isn't supported", sbi->blkszbits);
30562306a36Sopenharmony_ci		goto out;
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci	if (dsb->dirblkbits) {
30862306a36Sopenharmony_ci		erofs_err(sb, "dirblkbits %u isn't supported", dsb->dirblkbits);
30962306a36Sopenharmony_ci		goto out;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	sbi->feature_compat = le32_to_cpu(dsb->feature_compat);
31362306a36Sopenharmony_ci	if (erofs_sb_has_sb_chksum(sbi)) {
31462306a36Sopenharmony_ci		ret = erofs_superblock_csum_verify(sb, data);
31562306a36Sopenharmony_ci		if (ret)
31662306a36Sopenharmony_ci			goto out;
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	ret = -EINVAL;
32062306a36Sopenharmony_ci	if (!check_layout_compatibility(sb, dsb))
32162306a36Sopenharmony_ci		goto out;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	sbi->sb_size = 128 + dsb->sb_extslots * EROFS_SB_EXTSLOT_SIZE;
32462306a36Sopenharmony_ci	if (sbi->sb_size > PAGE_SIZE - EROFS_SUPER_OFFSET) {
32562306a36Sopenharmony_ci		erofs_err(sb, "invalid sb_extslots %u (more than a fs block)",
32662306a36Sopenharmony_ci			  sbi->sb_size);
32762306a36Sopenharmony_ci		goto out;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci	sbi->primarydevice_blocks = le32_to_cpu(dsb->blocks);
33062306a36Sopenharmony_ci	sbi->meta_blkaddr = le32_to_cpu(dsb->meta_blkaddr);
33162306a36Sopenharmony_ci#ifdef CONFIG_EROFS_FS_XATTR
33262306a36Sopenharmony_ci	sbi->xattr_blkaddr = le32_to_cpu(dsb->xattr_blkaddr);
33362306a36Sopenharmony_ci	sbi->xattr_prefix_start = le32_to_cpu(dsb->xattr_prefix_start);
33462306a36Sopenharmony_ci	sbi->xattr_prefix_count = dsb->xattr_prefix_count;
33562306a36Sopenharmony_ci	sbi->xattr_filter_reserved = dsb->xattr_filter_reserved;
33662306a36Sopenharmony_ci#endif
33762306a36Sopenharmony_ci	sbi->islotbits = ilog2(sizeof(struct erofs_inode_compact));
33862306a36Sopenharmony_ci	sbi->root_nid = le16_to_cpu(dsb->root_nid);
33962306a36Sopenharmony_ci	sbi->packed_nid = le64_to_cpu(dsb->packed_nid);
34062306a36Sopenharmony_ci	sbi->inos = le64_to_cpu(dsb->inos);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	sbi->build_time = le64_to_cpu(dsb->build_time);
34362306a36Sopenharmony_ci	sbi->build_time_nsec = le32_to_cpu(dsb->build_time_nsec);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	memcpy(&sb->s_uuid, dsb->uuid, sizeof(dsb->uuid));
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	ret = strscpy(sbi->volume_name, dsb->volume_name,
34862306a36Sopenharmony_ci		      sizeof(dsb->volume_name));
34962306a36Sopenharmony_ci	if (ret < 0) {	/* -E2BIG */
35062306a36Sopenharmony_ci		erofs_err(sb, "bad volume name without NIL terminator");
35162306a36Sopenharmony_ci		ret = -EFSCORRUPTED;
35262306a36Sopenharmony_ci		goto out;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	/* parse on-disk compression configurations */
35662306a36Sopenharmony_ci	ret = z_erofs_parse_cfgs(sb, dsb);
35762306a36Sopenharmony_ci	if (ret < 0)
35862306a36Sopenharmony_ci		goto out;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	/* handle multiple devices */
36162306a36Sopenharmony_ci	ret = erofs_scan_devices(sb, dsb);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	if (erofs_is_fscache_mode(sb))
36462306a36Sopenharmony_ci		erofs_info(sb, "EXPERIMENTAL fscache-based on-demand read feature in use. Use at your own risk!");
36562306a36Sopenharmony_ciout:
36662306a36Sopenharmony_ci	erofs_put_metabuf(&buf);
36762306a36Sopenharmony_ci	return ret;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic void erofs_default_options(struct erofs_fs_context *ctx)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci#ifdef CONFIG_EROFS_FS_ZIP
37362306a36Sopenharmony_ci	ctx->opt.cache_strategy = EROFS_ZIP_CACHE_READAROUND;
37462306a36Sopenharmony_ci	ctx->opt.max_sync_decompress_pages = 3;
37562306a36Sopenharmony_ci	ctx->opt.sync_decompress = EROFS_SYNC_DECOMPRESS_AUTO;
37662306a36Sopenharmony_ci#endif
37762306a36Sopenharmony_ci#ifdef CONFIG_EROFS_FS_XATTR
37862306a36Sopenharmony_ci	set_opt(&ctx->opt, XATTR_USER);
37962306a36Sopenharmony_ci#endif
38062306a36Sopenharmony_ci#ifdef CONFIG_EROFS_FS_POSIX_ACL
38162306a36Sopenharmony_ci	set_opt(&ctx->opt, POSIX_ACL);
38262306a36Sopenharmony_ci#endif
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cienum {
38662306a36Sopenharmony_ci	Opt_user_xattr,
38762306a36Sopenharmony_ci	Opt_acl,
38862306a36Sopenharmony_ci	Opt_cache_strategy,
38962306a36Sopenharmony_ci	Opt_dax,
39062306a36Sopenharmony_ci	Opt_dax_enum,
39162306a36Sopenharmony_ci	Opt_device,
39262306a36Sopenharmony_ci	Opt_fsid,
39362306a36Sopenharmony_ci	Opt_domain_id,
39462306a36Sopenharmony_ci	Opt_err
39562306a36Sopenharmony_ci};
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic const struct constant_table erofs_param_cache_strategy[] = {
39862306a36Sopenharmony_ci	{"disabled",	EROFS_ZIP_CACHE_DISABLED},
39962306a36Sopenharmony_ci	{"readahead",	EROFS_ZIP_CACHE_READAHEAD},
40062306a36Sopenharmony_ci	{"readaround",	EROFS_ZIP_CACHE_READAROUND},
40162306a36Sopenharmony_ci	{}
40262306a36Sopenharmony_ci};
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic const struct constant_table erofs_dax_param_enums[] = {
40562306a36Sopenharmony_ci	{"always",	EROFS_MOUNT_DAX_ALWAYS},
40662306a36Sopenharmony_ci	{"never",	EROFS_MOUNT_DAX_NEVER},
40762306a36Sopenharmony_ci	{}
40862306a36Sopenharmony_ci};
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic const struct fs_parameter_spec erofs_fs_parameters[] = {
41162306a36Sopenharmony_ci	fsparam_flag_no("user_xattr",	Opt_user_xattr),
41262306a36Sopenharmony_ci	fsparam_flag_no("acl",		Opt_acl),
41362306a36Sopenharmony_ci	fsparam_enum("cache_strategy",	Opt_cache_strategy,
41462306a36Sopenharmony_ci		     erofs_param_cache_strategy),
41562306a36Sopenharmony_ci	fsparam_flag("dax",             Opt_dax),
41662306a36Sopenharmony_ci	fsparam_enum("dax",		Opt_dax_enum, erofs_dax_param_enums),
41762306a36Sopenharmony_ci	fsparam_string("device",	Opt_device),
41862306a36Sopenharmony_ci	fsparam_string("fsid",		Opt_fsid),
41962306a36Sopenharmony_ci	fsparam_string("domain_id",	Opt_domain_id),
42062306a36Sopenharmony_ci	{}
42162306a36Sopenharmony_ci};
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic bool erofs_fc_set_dax_mode(struct fs_context *fc, unsigned int mode)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci#ifdef CONFIG_FS_DAX
42662306a36Sopenharmony_ci	struct erofs_fs_context *ctx = fc->fs_private;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	switch (mode) {
42962306a36Sopenharmony_ci	case EROFS_MOUNT_DAX_ALWAYS:
43062306a36Sopenharmony_ci		warnfc(fc, "DAX enabled. Warning: EXPERIMENTAL, use at your own risk");
43162306a36Sopenharmony_ci		set_opt(&ctx->opt, DAX_ALWAYS);
43262306a36Sopenharmony_ci		clear_opt(&ctx->opt, DAX_NEVER);
43362306a36Sopenharmony_ci		return true;
43462306a36Sopenharmony_ci	case EROFS_MOUNT_DAX_NEVER:
43562306a36Sopenharmony_ci		set_opt(&ctx->opt, DAX_NEVER);
43662306a36Sopenharmony_ci		clear_opt(&ctx->opt, DAX_ALWAYS);
43762306a36Sopenharmony_ci		return true;
43862306a36Sopenharmony_ci	default:
43962306a36Sopenharmony_ci		DBG_BUGON(1);
44062306a36Sopenharmony_ci		return false;
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci#else
44362306a36Sopenharmony_ci	errorfc(fc, "dax options not supported");
44462306a36Sopenharmony_ci	return false;
44562306a36Sopenharmony_ci#endif
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic int erofs_fc_parse_param(struct fs_context *fc,
44962306a36Sopenharmony_ci				struct fs_parameter *param)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	struct erofs_fs_context *ctx = fc->fs_private;
45262306a36Sopenharmony_ci	struct fs_parse_result result;
45362306a36Sopenharmony_ci	struct erofs_device_info *dif;
45462306a36Sopenharmony_ci	int opt, ret;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	opt = fs_parse(fc, erofs_fs_parameters, param, &result);
45762306a36Sopenharmony_ci	if (opt < 0)
45862306a36Sopenharmony_ci		return opt;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	switch (opt) {
46162306a36Sopenharmony_ci	case Opt_user_xattr:
46262306a36Sopenharmony_ci#ifdef CONFIG_EROFS_FS_XATTR
46362306a36Sopenharmony_ci		if (result.boolean)
46462306a36Sopenharmony_ci			set_opt(&ctx->opt, XATTR_USER);
46562306a36Sopenharmony_ci		else
46662306a36Sopenharmony_ci			clear_opt(&ctx->opt, XATTR_USER);
46762306a36Sopenharmony_ci#else
46862306a36Sopenharmony_ci		errorfc(fc, "{,no}user_xattr options not supported");
46962306a36Sopenharmony_ci#endif
47062306a36Sopenharmony_ci		break;
47162306a36Sopenharmony_ci	case Opt_acl:
47262306a36Sopenharmony_ci#ifdef CONFIG_EROFS_FS_POSIX_ACL
47362306a36Sopenharmony_ci		if (result.boolean)
47462306a36Sopenharmony_ci			set_opt(&ctx->opt, POSIX_ACL);
47562306a36Sopenharmony_ci		else
47662306a36Sopenharmony_ci			clear_opt(&ctx->opt, POSIX_ACL);
47762306a36Sopenharmony_ci#else
47862306a36Sopenharmony_ci		errorfc(fc, "{,no}acl options not supported");
47962306a36Sopenharmony_ci#endif
48062306a36Sopenharmony_ci		break;
48162306a36Sopenharmony_ci	case Opt_cache_strategy:
48262306a36Sopenharmony_ci#ifdef CONFIG_EROFS_FS_ZIP
48362306a36Sopenharmony_ci		ctx->opt.cache_strategy = result.uint_32;
48462306a36Sopenharmony_ci#else
48562306a36Sopenharmony_ci		errorfc(fc, "compression not supported, cache_strategy ignored");
48662306a36Sopenharmony_ci#endif
48762306a36Sopenharmony_ci		break;
48862306a36Sopenharmony_ci	case Opt_dax:
48962306a36Sopenharmony_ci		if (!erofs_fc_set_dax_mode(fc, EROFS_MOUNT_DAX_ALWAYS))
49062306a36Sopenharmony_ci			return -EINVAL;
49162306a36Sopenharmony_ci		break;
49262306a36Sopenharmony_ci	case Opt_dax_enum:
49362306a36Sopenharmony_ci		if (!erofs_fc_set_dax_mode(fc, result.uint_32))
49462306a36Sopenharmony_ci			return -EINVAL;
49562306a36Sopenharmony_ci		break;
49662306a36Sopenharmony_ci	case Opt_device:
49762306a36Sopenharmony_ci		dif = kzalloc(sizeof(*dif), GFP_KERNEL);
49862306a36Sopenharmony_ci		if (!dif)
49962306a36Sopenharmony_ci			return -ENOMEM;
50062306a36Sopenharmony_ci		dif->path = kstrdup(param->string, GFP_KERNEL);
50162306a36Sopenharmony_ci		if (!dif->path) {
50262306a36Sopenharmony_ci			kfree(dif);
50362306a36Sopenharmony_ci			return -ENOMEM;
50462306a36Sopenharmony_ci		}
50562306a36Sopenharmony_ci		down_write(&ctx->devs->rwsem);
50662306a36Sopenharmony_ci		ret = idr_alloc(&ctx->devs->tree, dif, 0, 0, GFP_KERNEL);
50762306a36Sopenharmony_ci		up_write(&ctx->devs->rwsem);
50862306a36Sopenharmony_ci		if (ret < 0) {
50962306a36Sopenharmony_ci			kfree(dif->path);
51062306a36Sopenharmony_ci			kfree(dif);
51162306a36Sopenharmony_ci			return ret;
51262306a36Sopenharmony_ci		}
51362306a36Sopenharmony_ci		++ctx->devs->extra_devices;
51462306a36Sopenharmony_ci		break;
51562306a36Sopenharmony_ci#ifdef CONFIG_EROFS_FS_ONDEMAND
51662306a36Sopenharmony_ci	case Opt_fsid:
51762306a36Sopenharmony_ci		kfree(ctx->fsid);
51862306a36Sopenharmony_ci		ctx->fsid = kstrdup(param->string, GFP_KERNEL);
51962306a36Sopenharmony_ci		if (!ctx->fsid)
52062306a36Sopenharmony_ci			return -ENOMEM;
52162306a36Sopenharmony_ci		break;
52262306a36Sopenharmony_ci	case Opt_domain_id:
52362306a36Sopenharmony_ci		kfree(ctx->domain_id);
52462306a36Sopenharmony_ci		ctx->domain_id = kstrdup(param->string, GFP_KERNEL);
52562306a36Sopenharmony_ci		if (!ctx->domain_id)
52662306a36Sopenharmony_ci			return -ENOMEM;
52762306a36Sopenharmony_ci		break;
52862306a36Sopenharmony_ci#else
52962306a36Sopenharmony_ci	case Opt_fsid:
53062306a36Sopenharmony_ci	case Opt_domain_id:
53162306a36Sopenharmony_ci		errorfc(fc, "%s option not supported", erofs_fs_parameters[opt].name);
53262306a36Sopenharmony_ci		break;
53362306a36Sopenharmony_ci#endif
53462306a36Sopenharmony_ci	default:
53562306a36Sopenharmony_ci		return -ENOPARAM;
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci	return 0;
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cistatic struct inode *erofs_nfs_get_inode(struct super_block *sb,
54162306a36Sopenharmony_ci					 u64 ino, u32 generation)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	return erofs_iget(sb, ino);
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic struct dentry *erofs_fh_to_dentry(struct super_block *sb,
54762306a36Sopenharmony_ci		struct fid *fid, int fh_len, int fh_type)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
55062306a36Sopenharmony_ci				    erofs_nfs_get_inode);
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic struct dentry *erofs_fh_to_parent(struct super_block *sb,
55462306a36Sopenharmony_ci		struct fid *fid, int fh_len, int fh_type)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci	return generic_fh_to_parent(sb, fid, fh_len, fh_type,
55762306a36Sopenharmony_ci				    erofs_nfs_get_inode);
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic struct dentry *erofs_get_parent(struct dentry *child)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	erofs_nid_t nid;
56362306a36Sopenharmony_ci	unsigned int d_type;
56462306a36Sopenharmony_ci	int err;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	err = erofs_namei(d_inode(child), &dotdot_name, &nid, &d_type);
56762306a36Sopenharmony_ci	if (err)
56862306a36Sopenharmony_ci		return ERR_PTR(err);
56962306a36Sopenharmony_ci	return d_obtain_alias(erofs_iget(child->d_sb, nid));
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic const struct export_operations erofs_export_ops = {
57362306a36Sopenharmony_ci	.fh_to_dentry = erofs_fh_to_dentry,
57462306a36Sopenharmony_ci	.fh_to_parent = erofs_fh_to_parent,
57562306a36Sopenharmony_ci	.get_parent = erofs_get_parent,
57662306a36Sopenharmony_ci};
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_cistatic int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	struct inode *inode;
58162306a36Sopenharmony_ci	struct erofs_sb_info *sbi;
58262306a36Sopenharmony_ci	struct erofs_fs_context *ctx = fc->fs_private;
58362306a36Sopenharmony_ci	int err;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	sb->s_magic = EROFS_SUPER_MAGIC;
58662306a36Sopenharmony_ci	sb->s_flags |= SB_RDONLY | SB_NOATIME;
58762306a36Sopenharmony_ci	sb->s_maxbytes = MAX_LFS_FILESIZE;
58862306a36Sopenharmony_ci	sb->s_op = &erofs_sops;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
59162306a36Sopenharmony_ci	if (!sbi)
59262306a36Sopenharmony_ci		return -ENOMEM;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	sb->s_fs_info = sbi;
59562306a36Sopenharmony_ci	sbi->opt = ctx->opt;
59662306a36Sopenharmony_ci	sbi->devs = ctx->devs;
59762306a36Sopenharmony_ci	ctx->devs = NULL;
59862306a36Sopenharmony_ci	sbi->fsid = ctx->fsid;
59962306a36Sopenharmony_ci	ctx->fsid = NULL;
60062306a36Sopenharmony_ci	sbi->domain_id = ctx->domain_id;
60162306a36Sopenharmony_ci	ctx->domain_id = NULL;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	sbi->blkszbits = PAGE_SHIFT;
60462306a36Sopenharmony_ci	if (erofs_is_fscache_mode(sb)) {
60562306a36Sopenharmony_ci		sb->s_blocksize = PAGE_SIZE;
60662306a36Sopenharmony_ci		sb->s_blocksize_bits = PAGE_SHIFT;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci		err = erofs_fscache_register_fs(sb);
60962306a36Sopenharmony_ci		if (err)
61062306a36Sopenharmony_ci			return err;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci		err = super_setup_bdi(sb);
61362306a36Sopenharmony_ci		if (err)
61462306a36Sopenharmony_ci			return err;
61562306a36Sopenharmony_ci	} else {
61662306a36Sopenharmony_ci		if (!sb_set_blocksize(sb, PAGE_SIZE)) {
61762306a36Sopenharmony_ci			errorfc(fc, "failed to set initial blksize");
61862306a36Sopenharmony_ci			return -EINVAL;
61962306a36Sopenharmony_ci		}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci		sbi->dax_dev = fs_dax_get_by_bdev(sb->s_bdev,
62262306a36Sopenharmony_ci						  &sbi->dax_part_off,
62362306a36Sopenharmony_ci						  NULL, NULL);
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	err = erofs_read_superblock(sb);
62762306a36Sopenharmony_ci	if (err)
62862306a36Sopenharmony_ci		return err;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	if (sb->s_blocksize_bits != sbi->blkszbits) {
63162306a36Sopenharmony_ci		if (erofs_is_fscache_mode(sb)) {
63262306a36Sopenharmony_ci			errorfc(fc, "unsupported blksize for fscache mode");
63362306a36Sopenharmony_ci			return -EINVAL;
63462306a36Sopenharmony_ci		}
63562306a36Sopenharmony_ci		if (!sb_set_blocksize(sb, 1 << sbi->blkszbits)) {
63662306a36Sopenharmony_ci			errorfc(fc, "failed to set erofs blksize");
63762306a36Sopenharmony_ci			return -EINVAL;
63862306a36Sopenharmony_ci		}
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	if (test_opt(&sbi->opt, DAX_ALWAYS)) {
64262306a36Sopenharmony_ci		if (!sbi->dax_dev) {
64362306a36Sopenharmony_ci			errorfc(fc, "DAX unsupported by block device. Turning off DAX.");
64462306a36Sopenharmony_ci			clear_opt(&sbi->opt, DAX_ALWAYS);
64562306a36Sopenharmony_ci		} else if (sbi->blkszbits != PAGE_SHIFT) {
64662306a36Sopenharmony_ci			errorfc(fc, "unsupported blocksize for DAX");
64762306a36Sopenharmony_ci			clear_opt(&sbi->opt, DAX_ALWAYS);
64862306a36Sopenharmony_ci		}
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	sb->s_time_gran = 1;
65262306a36Sopenharmony_ci	sb->s_xattr = erofs_xattr_handlers;
65362306a36Sopenharmony_ci	sb->s_export_op = &erofs_export_ops;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	if (test_opt(&sbi->opt, POSIX_ACL))
65662306a36Sopenharmony_ci		sb->s_flags |= SB_POSIXACL;
65762306a36Sopenharmony_ci	else
65862306a36Sopenharmony_ci		sb->s_flags &= ~SB_POSIXACL;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci#ifdef CONFIG_EROFS_FS_ZIP
66162306a36Sopenharmony_ci	xa_init(&sbi->managed_pslots);
66262306a36Sopenharmony_ci#endif
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	inode = erofs_iget(sb, ROOT_NID(sbi));
66562306a36Sopenharmony_ci	if (IS_ERR(inode))
66662306a36Sopenharmony_ci		return PTR_ERR(inode);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (!S_ISDIR(inode->i_mode)) {
66962306a36Sopenharmony_ci		erofs_err(sb, "rootino(nid %llu) is not a directory(i_mode %o)",
67062306a36Sopenharmony_ci			  ROOT_NID(sbi), inode->i_mode);
67162306a36Sopenharmony_ci		iput(inode);
67262306a36Sopenharmony_ci		return -EINVAL;
67362306a36Sopenharmony_ci	}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	sb->s_root = d_make_root(inode);
67662306a36Sopenharmony_ci	if (!sb->s_root)
67762306a36Sopenharmony_ci		return -ENOMEM;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	erofs_shrinker_register(sb);
68062306a36Sopenharmony_ci	if (erofs_sb_has_fragments(sbi) && sbi->packed_nid) {
68162306a36Sopenharmony_ci		sbi->packed_inode = erofs_iget(sb, sbi->packed_nid);
68262306a36Sopenharmony_ci		if (IS_ERR(sbi->packed_inode)) {
68362306a36Sopenharmony_ci			err = PTR_ERR(sbi->packed_inode);
68462306a36Sopenharmony_ci			sbi->packed_inode = NULL;
68562306a36Sopenharmony_ci			return err;
68662306a36Sopenharmony_ci		}
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci	err = erofs_init_managed_cache(sb);
68962306a36Sopenharmony_ci	if (err)
69062306a36Sopenharmony_ci		return err;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	err = erofs_xattr_prefixes_init(sb);
69362306a36Sopenharmony_ci	if (err)
69462306a36Sopenharmony_ci		return err;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	err = erofs_register_sysfs(sb);
69762306a36Sopenharmony_ci	if (err)
69862306a36Sopenharmony_ci		return err;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	erofs_info(sb, "mounted with root inode @ nid %llu.", ROOT_NID(sbi));
70162306a36Sopenharmony_ci	return 0;
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic int erofs_fc_get_tree(struct fs_context *fc)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	struct erofs_fs_context *ctx = fc->fs_private;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && ctx->fsid)
70962306a36Sopenharmony_ci		return get_tree_nodev(fc, erofs_fc_fill_super);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	return get_tree_bdev(fc, erofs_fc_fill_super);
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_cistatic int erofs_fc_reconfigure(struct fs_context *fc)
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci	struct super_block *sb = fc->root->d_sb;
71762306a36Sopenharmony_ci	struct erofs_sb_info *sbi = EROFS_SB(sb);
71862306a36Sopenharmony_ci	struct erofs_fs_context *ctx = fc->fs_private;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	DBG_BUGON(!sb_rdonly(sb));
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	if (ctx->fsid || ctx->domain_id)
72362306a36Sopenharmony_ci		erofs_info(sb, "ignoring reconfiguration for fsid|domain_id.");
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	if (test_opt(&ctx->opt, POSIX_ACL))
72662306a36Sopenharmony_ci		fc->sb_flags |= SB_POSIXACL;
72762306a36Sopenharmony_ci	else
72862306a36Sopenharmony_ci		fc->sb_flags &= ~SB_POSIXACL;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	sbi->opt = ctx->opt;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	fc->sb_flags |= SB_RDONLY;
73362306a36Sopenharmony_ci	return 0;
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_cistatic int erofs_release_device_info(int id, void *ptr, void *data)
73762306a36Sopenharmony_ci{
73862306a36Sopenharmony_ci	struct erofs_device_info *dif = ptr;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	fs_put_dax(dif->dax_dev, NULL);
74162306a36Sopenharmony_ci	if (dif->bdev_handle)
74262306a36Sopenharmony_ci		bdev_release(dif->bdev_handle);
74362306a36Sopenharmony_ci	erofs_fscache_unregister_cookie(dif->fscache);
74462306a36Sopenharmony_ci	dif->fscache = NULL;
74562306a36Sopenharmony_ci	kfree(dif->path);
74662306a36Sopenharmony_ci	kfree(dif);
74762306a36Sopenharmony_ci	return 0;
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_cistatic void erofs_free_dev_context(struct erofs_dev_context *devs)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	if (!devs)
75362306a36Sopenharmony_ci		return;
75462306a36Sopenharmony_ci	idr_for_each(&devs->tree, &erofs_release_device_info, NULL);
75562306a36Sopenharmony_ci	idr_destroy(&devs->tree);
75662306a36Sopenharmony_ci	kfree(devs);
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_cistatic void erofs_fc_free(struct fs_context *fc)
76062306a36Sopenharmony_ci{
76162306a36Sopenharmony_ci	struct erofs_fs_context *ctx = fc->fs_private;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	erofs_free_dev_context(ctx->devs);
76462306a36Sopenharmony_ci	kfree(ctx->fsid);
76562306a36Sopenharmony_ci	kfree(ctx->domain_id);
76662306a36Sopenharmony_ci	kfree(ctx);
76762306a36Sopenharmony_ci}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_cistatic const struct fs_context_operations erofs_context_ops = {
77062306a36Sopenharmony_ci	.parse_param	= erofs_fc_parse_param,
77162306a36Sopenharmony_ci	.get_tree       = erofs_fc_get_tree,
77262306a36Sopenharmony_ci	.reconfigure    = erofs_fc_reconfigure,
77362306a36Sopenharmony_ci	.free		= erofs_fc_free,
77462306a36Sopenharmony_ci};
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_cistatic int erofs_init_fs_context(struct fs_context *fc)
77762306a36Sopenharmony_ci{
77862306a36Sopenharmony_ci	struct erofs_fs_context *ctx;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
78162306a36Sopenharmony_ci	if (!ctx)
78262306a36Sopenharmony_ci		return -ENOMEM;
78362306a36Sopenharmony_ci	ctx->devs = kzalloc(sizeof(struct erofs_dev_context), GFP_KERNEL);
78462306a36Sopenharmony_ci	if (!ctx->devs) {
78562306a36Sopenharmony_ci		kfree(ctx);
78662306a36Sopenharmony_ci		return -ENOMEM;
78762306a36Sopenharmony_ci	}
78862306a36Sopenharmony_ci	fc->fs_private = ctx;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	idr_init(&ctx->devs->tree);
79162306a36Sopenharmony_ci	init_rwsem(&ctx->devs->rwsem);
79262306a36Sopenharmony_ci	erofs_default_options(ctx);
79362306a36Sopenharmony_ci	fc->ops = &erofs_context_ops;
79462306a36Sopenharmony_ci	return 0;
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_cistatic void erofs_kill_sb(struct super_block *sb)
79862306a36Sopenharmony_ci{
79962306a36Sopenharmony_ci	struct erofs_sb_info *sbi;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	if (erofs_is_fscache_mode(sb))
80262306a36Sopenharmony_ci		kill_anon_super(sb);
80362306a36Sopenharmony_ci	else
80462306a36Sopenharmony_ci		kill_block_super(sb);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	sbi = EROFS_SB(sb);
80762306a36Sopenharmony_ci	if (!sbi)
80862306a36Sopenharmony_ci		return;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	erofs_free_dev_context(sbi->devs);
81162306a36Sopenharmony_ci	fs_put_dax(sbi->dax_dev, NULL);
81262306a36Sopenharmony_ci	erofs_fscache_unregister_fs(sb);
81362306a36Sopenharmony_ci	kfree(sbi->fsid);
81462306a36Sopenharmony_ci	kfree(sbi->domain_id);
81562306a36Sopenharmony_ci	kfree(sbi);
81662306a36Sopenharmony_ci	sb->s_fs_info = NULL;
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_cistatic void erofs_put_super(struct super_block *sb)
82062306a36Sopenharmony_ci{
82162306a36Sopenharmony_ci	struct erofs_sb_info *const sbi = EROFS_SB(sb);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	DBG_BUGON(!sbi);
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	erofs_unregister_sysfs(sb);
82662306a36Sopenharmony_ci	erofs_shrinker_unregister(sb);
82762306a36Sopenharmony_ci	erofs_xattr_prefixes_cleanup(sb);
82862306a36Sopenharmony_ci#ifdef CONFIG_EROFS_FS_ZIP
82962306a36Sopenharmony_ci	iput(sbi->managed_cache);
83062306a36Sopenharmony_ci	sbi->managed_cache = NULL;
83162306a36Sopenharmony_ci#endif
83262306a36Sopenharmony_ci	iput(sbi->packed_inode);
83362306a36Sopenharmony_ci	sbi->packed_inode = NULL;
83462306a36Sopenharmony_ci	erofs_free_dev_context(sbi->devs);
83562306a36Sopenharmony_ci	sbi->devs = NULL;
83662306a36Sopenharmony_ci	erofs_fscache_unregister_fs(sb);
83762306a36Sopenharmony_ci}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_cistatic struct file_system_type erofs_fs_type = {
84062306a36Sopenharmony_ci	.owner          = THIS_MODULE,
84162306a36Sopenharmony_ci	.name           = "erofs",
84262306a36Sopenharmony_ci	.init_fs_context = erofs_init_fs_context,
84362306a36Sopenharmony_ci	.kill_sb        = erofs_kill_sb,
84462306a36Sopenharmony_ci	.fs_flags       = FS_REQUIRES_DEV | FS_ALLOW_IDMAP,
84562306a36Sopenharmony_ci};
84662306a36Sopenharmony_ciMODULE_ALIAS_FS("erofs");
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_cistatic int __init erofs_module_init(void)
84962306a36Sopenharmony_ci{
85062306a36Sopenharmony_ci	int err;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	erofs_check_ondisk_layout_definitions();
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	erofs_inode_cachep = kmem_cache_create("erofs_inode",
85562306a36Sopenharmony_ci			sizeof(struct erofs_inode), 0,
85662306a36Sopenharmony_ci			SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD | SLAB_ACCOUNT,
85762306a36Sopenharmony_ci			erofs_inode_init_once);
85862306a36Sopenharmony_ci	if (!erofs_inode_cachep)
85962306a36Sopenharmony_ci		return -ENOMEM;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	err = erofs_init_shrinker();
86262306a36Sopenharmony_ci	if (err)
86362306a36Sopenharmony_ci		goto shrinker_err;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	err = z_erofs_lzma_init();
86662306a36Sopenharmony_ci	if (err)
86762306a36Sopenharmony_ci		goto lzma_err;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	err = z_erofs_deflate_init();
87062306a36Sopenharmony_ci	if (err)
87162306a36Sopenharmony_ci		goto deflate_err;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	erofs_pcpubuf_init();
87462306a36Sopenharmony_ci	err = z_erofs_init_zip_subsystem();
87562306a36Sopenharmony_ci	if (err)
87662306a36Sopenharmony_ci		goto zip_err;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	err = erofs_init_sysfs();
87962306a36Sopenharmony_ci	if (err)
88062306a36Sopenharmony_ci		goto sysfs_err;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	err = register_filesystem(&erofs_fs_type);
88362306a36Sopenharmony_ci	if (err)
88462306a36Sopenharmony_ci		goto fs_err;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	return 0;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_cifs_err:
88962306a36Sopenharmony_ci	erofs_exit_sysfs();
89062306a36Sopenharmony_cisysfs_err:
89162306a36Sopenharmony_ci	z_erofs_exit_zip_subsystem();
89262306a36Sopenharmony_cizip_err:
89362306a36Sopenharmony_ci	z_erofs_deflate_exit();
89462306a36Sopenharmony_cideflate_err:
89562306a36Sopenharmony_ci	z_erofs_lzma_exit();
89662306a36Sopenharmony_cilzma_err:
89762306a36Sopenharmony_ci	erofs_exit_shrinker();
89862306a36Sopenharmony_cishrinker_err:
89962306a36Sopenharmony_ci	kmem_cache_destroy(erofs_inode_cachep);
90062306a36Sopenharmony_ci	return err;
90162306a36Sopenharmony_ci}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_cistatic void __exit erofs_module_exit(void)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	unregister_filesystem(&erofs_fs_type);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	/* Ensure all RCU free inodes / pclusters are safe to be destroyed. */
90862306a36Sopenharmony_ci	rcu_barrier();
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	erofs_exit_sysfs();
91162306a36Sopenharmony_ci	z_erofs_exit_zip_subsystem();
91262306a36Sopenharmony_ci	z_erofs_deflate_exit();
91362306a36Sopenharmony_ci	z_erofs_lzma_exit();
91462306a36Sopenharmony_ci	erofs_exit_shrinker();
91562306a36Sopenharmony_ci	kmem_cache_destroy(erofs_inode_cachep);
91662306a36Sopenharmony_ci	erofs_pcpubuf_exit();
91762306a36Sopenharmony_ci}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_cistatic int erofs_statfs(struct dentry *dentry, struct kstatfs *buf)
92062306a36Sopenharmony_ci{
92162306a36Sopenharmony_ci	struct super_block *sb = dentry->d_sb;
92262306a36Sopenharmony_ci	struct erofs_sb_info *sbi = EROFS_SB(sb);
92362306a36Sopenharmony_ci	u64 id = 0;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	if (!erofs_is_fscache_mode(sb))
92662306a36Sopenharmony_ci		id = huge_encode_dev(sb->s_bdev->bd_dev);
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	buf->f_type = sb->s_magic;
92962306a36Sopenharmony_ci	buf->f_bsize = sb->s_blocksize;
93062306a36Sopenharmony_ci	buf->f_blocks = sbi->total_blocks;
93162306a36Sopenharmony_ci	buf->f_bfree = buf->f_bavail = 0;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	buf->f_files = ULLONG_MAX;
93462306a36Sopenharmony_ci	buf->f_ffree = ULLONG_MAX - sbi->inos;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	buf->f_namelen = EROFS_NAME_LEN;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	buf->f_fsid    = u64_to_fsid(id);
93962306a36Sopenharmony_ci	return 0;
94062306a36Sopenharmony_ci}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_cistatic int erofs_show_options(struct seq_file *seq, struct dentry *root)
94362306a36Sopenharmony_ci{
94462306a36Sopenharmony_ci	struct erofs_sb_info *sbi = EROFS_SB(root->d_sb);
94562306a36Sopenharmony_ci	struct erofs_mount_opts *opt = &sbi->opt;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci#ifdef CONFIG_EROFS_FS_XATTR
94862306a36Sopenharmony_ci	if (test_opt(opt, XATTR_USER))
94962306a36Sopenharmony_ci		seq_puts(seq, ",user_xattr");
95062306a36Sopenharmony_ci	else
95162306a36Sopenharmony_ci		seq_puts(seq, ",nouser_xattr");
95262306a36Sopenharmony_ci#endif
95362306a36Sopenharmony_ci#ifdef CONFIG_EROFS_FS_POSIX_ACL
95462306a36Sopenharmony_ci	if (test_opt(opt, POSIX_ACL))
95562306a36Sopenharmony_ci		seq_puts(seq, ",acl");
95662306a36Sopenharmony_ci	else
95762306a36Sopenharmony_ci		seq_puts(seq, ",noacl");
95862306a36Sopenharmony_ci#endif
95962306a36Sopenharmony_ci#ifdef CONFIG_EROFS_FS_ZIP
96062306a36Sopenharmony_ci	if (opt->cache_strategy == EROFS_ZIP_CACHE_DISABLED)
96162306a36Sopenharmony_ci		seq_puts(seq, ",cache_strategy=disabled");
96262306a36Sopenharmony_ci	else if (opt->cache_strategy == EROFS_ZIP_CACHE_READAHEAD)
96362306a36Sopenharmony_ci		seq_puts(seq, ",cache_strategy=readahead");
96462306a36Sopenharmony_ci	else if (opt->cache_strategy == EROFS_ZIP_CACHE_READAROUND)
96562306a36Sopenharmony_ci		seq_puts(seq, ",cache_strategy=readaround");
96662306a36Sopenharmony_ci#endif
96762306a36Sopenharmony_ci	if (test_opt(opt, DAX_ALWAYS))
96862306a36Sopenharmony_ci		seq_puts(seq, ",dax=always");
96962306a36Sopenharmony_ci	if (test_opt(opt, DAX_NEVER))
97062306a36Sopenharmony_ci		seq_puts(seq, ",dax=never");
97162306a36Sopenharmony_ci#ifdef CONFIG_EROFS_FS_ONDEMAND
97262306a36Sopenharmony_ci	if (sbi->fsid)
97362306a36Sopenharmony_ci		seq_printf(seq, ",fsid=%s", sbi->fsid);
97462306a36Sopenharmony_ci	if (sbi->domain_id)
97562306a36Sopenharmony_ci		seq_printf(seq, ",domain_id=%s", sbi->domain_id);
97662306a36Sopenharmony_ci#endif
97762306a36Sopenharmony_ci	return 0;
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ciconst struct super_operations erofs_sops = {
98162306a36Sopenharmony_ci	.put_super = erofs_put_super,
98262306a36Sopenharmony_ci	.alloc_inode = erofs_alloc_inode,
98362306a36Sopenharmony_ci	.free_inode = erofs_free_inode,
98462306a36Sopenharmony_ci	.statfs = erofs_statfs,
98562306a36Sopenharmony_ci	.show_options = erofs_show_options,
98662306a36Sopenharmony_ci};
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_cimodule_init(erofs_module_init);
98962306a36Sopenharmony_cimodule_exit(erofs_module_exit);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ciMODULE_DESCRIPTION("Enhanced ROM File System");
99262306a36Sopenharmony_ciMODULE_AUTHOR("Gao Xiang, Chao Yu, Miao Xie, CONSUMER BG, HUAWEI Inc.");
99362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
994