1 // SPDX-License-Identifier: GPL-2.0+
2 #include "internal.h"
3 
check_layout_compatibility(struct erofs_sb_info * sbi,struct erofs_super_block * dsb)4 static bool check_layout_compatibility(struct erofs_sb_info *sbi,
5 				       struct erofs_super_block *dsb)
6 {
7 	const unsigned int feature = le32_to_cpu(dsb->feature_incompat);
8 
9 	sbi->feature_incompat = feature;
10 
11 	/* check if current kernel meets all mandatory requirements */
12 	if (feature & ~EROFS_ALL_FEATURE_INCOMPAT) {
13 		erofs_err("unidentified incompatible feature %x, please upgrade kernel version",
14 			  feature & ~EROFS_ALL_FEATURE_INCOMPAT);
15 		return false;
16 	}
17 	return true;
18 }
19 
erofs_init_devices(struct erofs_sb_info * sbi,struct erofs_super_block * dsb)20 static int erofs_init_devices(struct erofs_sb_info *sbi,
21 			      struct erofs_super_block *dsb)
22 {
23 	unsigned int ondisk_extradevs, i;
24 	erofs_off_t pos;
25 
26 	sbi->total_blocks = sbi->primarydevice_blocks;
27 
28 	if (!erofs_sb_has_device_table())
29 		ondisk_extradevs = 0;
30 	else
31 		ondisk_extradevs = le16_to_cpu(dsb->extra_devices);
32 
33 	if (ondisk_extradevs != sbi->extra_devices) {
34 		erofs_err("extra devices don't match (ondisk %u, given %u)",
35 			  ondisk_extradevs, sbi->extra_devices);
36 		return -EINVAL;
37 	}
38 	if (!ondisk_extradevs)
39 		return 0;
40 
41 	sbi->device_id_mask = roundup_pow_of_two(ondisk_extradevs + 1) - 1;
42 	sbi->devs = calloc(ondisk_extradevs, sizeof(*sbi->devs));
43 	if (!sbi->devs)
44 		return -ENOMEM;
45 	pos = le16_to_cpu(dsb->devt_slotoff) * EROFS_DEVT_SLOT_SIZE;
46 	for (i = 0; i < ondisk_extradevs; ++i) {
47 		struct erofs_deviceslot dis;
48 		int ret;
49 
50 		ret = erofs_dev_read(0, &dis, pos, sizeof(dis));
51 		if (ret < 0) {
52 			free(sbi->devs);
53 			return ret;
54 		}
55 
56 		sbi->devs[i].mapped_blkaddr = dis.mapped_blkaddr;
57 		sbi->total_blocks += dis.blocks;
58 		pos += EROFS_DEVT_SLOT_SIZE;
59 	}
60 	return 0;
61 }
62 
erofs_read_superblock(void)63 int erofs_read_superblock(void)
64 {
65 	u8 data[EROFS_MAX_BLOCK_SIZE];
66 	struct erofs_super_block *dsb;
67 	int ret;
68 
69 	ret = erofs_blk_read(data, 0, erofs_blknr(sizeof(data)));
70 	if (ret < 0) {
71 		erofs_dbg("cannot read erofs superblock: %d", ret);
72 		return -EIO;
73 	}
74 	dsb = (struct erofs_super_block *)(data + EROFS_SUPER_OFFSET);
75 
76 	ret = -EINVAL;
77 	if (le32_to_cpu(dsb->magic) != EROFS_SUPER_MAGIC_V1) {
78 		erofs_dbg("cannot find valid erofs superblock");
79 		return ret;
80 	}
81 
82 	sbi.feature_compat = le32_to_cpu(dsb->feature_compat);
83 
84 	sbi.blkszbits = dsb->blkszbits;
85 	if (sbi.blkszbits < 9 ||
86 	    sbi.blkszbits > ilog2(EROFS_MAX_BLOCK_SIZE)) {
87 		erofs_err("blksize %llu isn't supported on this platform",
88 			  erofs_blksiz() | 0ULL);
89 		return ret;
90 	} else if (!check_layout_compatibility(&sbi, dsb)) {
91 		return ret;
92 	}
93 
94 	sbi.primarydevice_blocks = le32_to_cpu(dsb->blocks);
95 	sbi.meta_blkaddr = le32_to_cpu(dsb->meta_blkaddr);
96 	sbi.xattr_blkaddr = le32_to_cpu(dsb->xattr_blkaddr);
97 	sbi.islotbits = EROFS_ISLOTBITS;
98 	sbi.root_nid = le16_to_cpu(dsb->root_nid);
99 	sbi.packed_nid = le64_to_cpu(dsb->packed_nid);
100 	sbi.inos = le64_to_cpu(dsb->inos);
101 	sbi.checksum = le32_to_cpu(dsb->checksum);
102 
103 	sbi.build_time = le64_to_cpu(dsb->build_time);
104 	sbi.build_time_nsec = le32_to_cpu(dsb->build_time_nsec);
105 
106 	memcpy(&sbi.uuid, dsb->uuid, sizeof(dsb->uuid));
107 	return erofs_init_devices(&sbi, dsb);
108 }
109