1 // SPDX-License-Identifier: GPL-2.0+ OR Apache-2.0
2 /*
3 * Created by Li Guifu <[email protected]>
4 */
5 #include <string.h>
6 #include <stdlib.h>
7 #include "erofs/print.h"
8 #include "erofs/xattr.h"
9 #include "erofs/cache.h"
10
check_layout_compatibility(struct erofs_sb_info * sbi,struct erofs_super_block * dsb)11 static bool check_layout_compatibility(struct erofs_sb_info *sbi,
12 struct erofs_super_block *dsb)
13 {
14 const unsigned int feature = le32_to_cpu(dsb->feature_incompat);
15
16 sbi->feature_incompat = feature;
17
18 /* check if current kernel meets all mandatory requirements */
19 if (feature & ~EROFS_ALL_FEATURE_INCOMPAT) {
20 erofs_err("unidentified incompatible feature %x, please upgrade kernel version",
21 feature & ~EROFS_ALL_FEATURE_INCOMPAT);
22 return false;
23 }
24 return true;
25 }
26
erofs_init_devices(struct erofs_sb_info * sbi,struct erofs_super_block * dsb)27 static int erofs_init_devices(struct erofs_sb_info *sbi,
28 struct erofs_super_block *dsb)
29 {
30 unsigned int ondisk_extradevs, i;
31 erofs_off_t pos;
32
33 sbi->total_blocks = sbi->primarydevice_blocks;
34
35 if (!erofs_sb_has_device_table(sbi))
36 ondisk_extradevs = 0;
37 else
38 ondisk_extradevs = le16_to_cpu(dsb->extra_devices);
39
40 if (sbi->extra_devices &&
41 ondisk_extradevs != sbi->extra_devices) {
42 erofs_err("extra devices don't match (ondisk %u, given %u)",
43 ondisk_extradevs, sbi->extra_devices);
44 return -EINVAL;
45 }
46 if (!ondisk_extradevs)
47 return 0;
48
49 sbi->extra_devices = ondisk_extradevs;
50 sbi->device_id_mask = roundup_pow_of_two(ondisk_extradevs + 1) - 1;
51 sbi->devs = calloc(ondisk_extradevs, sizeof(*sbi->devs));
52 if (!sbi->devs)
53 return -ENOMEM;
54 pos = le16_to_cpu(dsb->devt_slotoff) * EROFS_DEVT_SLOT_SIZE;
55 for (i = 0; i < ondisk_extradevs; ++i) {
56 struct erofs_deviceslot dis;
57 int ret;
58
59 ret = erofs_dev_read(sbi, 0, &dis, pos, sizeof(dis));
60 if (ret < 0) {
61 free(sbi->devs);
62 sbi->devs = NULL;
63 return ret;
64 }
65
66 sbi->devs[i].mapped_blkaddr = le32_to_cpu(dis.mapped_blkaddr);
67 sbi->devs[i].blocks = le32_to_cpu(dis.blocks);
68 memcpy(sbi->devs[i].tag, dis.tag, sizeof(dis.tag));
69 sbi->total_blocks += sbi->devs[i].blocks;
70 pos += EROFS_DEVT_SLOT_SIZE;
71 }
72 return 0;
73 }
74
erofs_read_superblock(struct erofs_sb_info * sbi)75 int erofs_read_superblock(struct erofs_sb_info *sbi)
76 {
77 u8 data[EROFS_MAX_BLOCK_SIZE];
78 struct erofs_super_block *dsb;
79 int ret;
80
81 sbi->blkszbits = ilog2(EROFS_MAX_BLOCK_SIZE);
82 ret = erofs_blk_read(sbi, 0, data, 0, erofs_blknr(sbi, sizeof(data)));
83 if (ret < 0) {
84 erofs_err("cannot read erofs superblock: %d", ret);
85 return -EIO;
86 }
87 dsb = (struct erofs_super_block *)(data + EROFS_SUPER_OFFSET);
88
89 ret = -EINVAL;
90 if (le32_to_cpu(dsb->magic) != EROFS_SUPER_MAGIC_V1) {
91 erofs_err("cannot find valid erofs superblock");
92 return ret;
93 }
94
95 sbi->feature_compat = le32_to_cpu(dsb->feature_compat);
96
97 sbi->blkszbits = dsb->blkszbits;
98 if (sbi->blkszbits < 9 ||
99 sbi->blkszbits > ilog2(EROFS_MAX_BLOCK_SIZE)) {
100 erofs_err("blksize %llu isn't supported on this platform",
101 erofs_blksiz(sbi) | 0ULL);
102 return ret;
103 } else if (!check_layout_compatibility(sbi, dsb)) {
104 return ret;
105 }
106
107 sbi->sb_size = 128 + dsb->sb_extslots * EROFS_SB_EXTSLOT_SIZE;
108 if (sbi->sb_size > (1 << sbi->blkszbits) - EROFS_SUPER_OFFSET) {
109 erofs_err("invalid sb_extslots %u (more than a fs block)",
110 dsb->sb_extslots);
111 return -EINVAL;
112 }
113 sbi->primarydevice_blocks = le32_to_cpu(dsb->blocks);
114 sbi->meta_blkaddr = le32_to_cpu(dsb->meta_blkaddr);
115 sbi->xattr_blkaddr = le32_to_cpu(dsb->xattr_blkaddr);
116 sbi->xattr_prefix_start = le32_to_cpu(dsb->xattr_prefix_start);
117 sbi->xattr_prefix_count = dsb->xattr_prefix_count;
118 sbi->islotbits = EROFS_ISLOTBITS;
119 sbi->root_nid = le16_to_cpu(dsb->root_nid);
120 sbi->packed_nid = le64_to_cpu(dsb->packed_nid);
121 sbi->inos = le64_to_cpu(dsb->inos);
122 sbi->checksum = le32_to_cpu(dsb->checksum);
123
124 sbi->build_time = le64_to_cpu(dsb->build_time);
125 sbi->build_time_nsec = le32_to_cpu(dsb->build_time_nsec);
126
127 memcpy(&sbi->uuid, dsb->uuid, sizeof(dsb->uuid));
128
129 ret = z_erofs_parse_cfgs(sbi, dsb);
130 if (ret)
131 return ret;
132
133 ret = erofs_init_devices(sbi, dsb);
134 if (ret)
135 return ret;
136
137 ret = erofs_xattr_prefixes_init(sbi);
138 if (ret && sbi->devs) {
139 free(sbi->devs);
140 sbi->devs = NULL;
141 }
142 return ret;
143 }
144
erofs_put_super(struct erofs_sb_info * sbi)145 void erofs_put_super(struct erofs_sb_info *sbi)
146 {
147 if (sbi->devs) {
148 free(sbi->devs);
149 sbi->devs = NULL;
150 }
151 erofs_xattr_prefixes_cleanup(sbi);
152 if (sbi->bmgr) {
153 erofs_buffer_exit(sbi->bmgr);
154 sbi->bmgr = NULL;
155 }
156 }
157
erofs_writesb(struct erofs_sb_info * sbi,struct erofs_buffer_head * sb_bh,erofs_blk_t * blocks)158 int erofs_writesb(struct erofs_sb_info *sbi, struct erofs_buffer_head *sb_bh,
159 erofs_blk_t *blocks)
160 {
161 struct erofs_super_block sb = {
162 .magic = cpu_to_le32(EROFS_SUPER_MAGIC_V1),
163 .blkszbits = sbi->blkszbits,
164 .root_nid = cpu_to_le16(sbi->root_nid),
165 .inos = cpu_to_le64(sbi->inos),
166 .build_time = cpu_to_le64(sbi->build_time),
167 .build_time_nsec = cpu_to_le32(sbi->build_time_nsec),
168 .meta_blkaddr = cpu_to_le32(sbi->meta_blkaddr),
169 .xattr_blkaddr = cpu_to_le32(sbi->xattr_blkaddr),
170 .xattr_prefix_count = sbi->xattr_prefix_count,
171 .xattr_prefix_start = cpu_to_le32(sbi->xattr_prefix_start),
172 .feature_incompat = cpu_to_le32(sbi->feature_incompat),
173 .feature_compat = cpu_to_le32(sbi->feature_compat &
174 ~EROFS_FEATURE_COMPAT_SB_CHKSUM),
175 .extra_devices = cpu_to_le16(sbi->extra_devices),
176 .devt_slotoff = cpu_to_le16(sbi->devt_slotoff),
177 .packed_nid = cpu_to_le64(sbi->packed_nid),
178 };
179 const u32 sb_blksize = round_up(EROFS_SUPER_END, erofs_blksiz(sbi));
180 char *buf;
181 int ret;
182
183 *blocks = erofs_mapbh(sbi->bmgr, NULL);
184 sb.blocks = cpu_to_le32(*blocks);
185 memcpy(sb.uuid, sbi->uuid, sizeof(sb.uuid));
186 memcpy(sb.volume_name, sbi->volume_name, sizeof(sb.volume_name));
187
188 if (erofs_sb_has_compr_cfgs(sbi))
189 sb.u1.available_compr_algs = cpu_to_le16(sbi->available_compr_algs);
190 else
191 sb.u1.lz4_max_distance = cpu_to_le16(sbi->lz4.max_distance);
192
193 buf = calloc(sb_blksize, 1);
194 if (!buf) {
195 erofs_err("failed to allocate memory for sb: %s",
196 erofs_strerror(-errno));
197 return -ENOMEM;
198 }
199 memcpy(buf + EROFS_SUPER_OFFSET, &sb, sizeof(sb));
200
201 ret = erofs_dev_write(sbi, buf, sb_bh ? erofs_btell(sb_bh, false) : 0,
202 EROFS_SUPER_END);
203 free(buf);
204 if (sb_bh)
205 erofs_bdrop(sb_bh, false);
206 return ret;
207 }
208
erofs_reserve_sb(struct erofs_bufmgr * bmgr)209 struct erofs_buffer_head *erofs_reserve_sb(struct erofs_bufmgr *bmgr)
210 {
211 struct erofs_buffer_head *bh;
212 int err;
213
214 bh = erofs_balloc(bmgr, META, 0, 0, 0);
215 if (IS_ERR(bh)) {
216 erofs_err("failed to allocate super: %s", PTR_ERR(bh));
217 return bh;
218 }
219 bh->op = &erofs_skip_write_bhops;
220 err = erofs_bh_balloon(bh, EROFS_SUPER_END);
221 if (err < 0) {
222 erofs_err("failed to balloon super: %s", erofs_strerror(err));
223 goto err_bdrop;
224 }
225
226 /* make sure that the super block should be the very first blocks */
227 (void)erofs_mapbh(NULL, bh->block);
228 if (erofs_btell(bh, false) != 0) {
229 erofs_err("failed to pin super block @ 0");
230 err = -EFAULT;
231 goto err_bdrop;
232 }
233 return bh;
234 err_bdrop:
235 erofs_bdrop(bh, true);
236 return ERR_PTR(err);
237 }
238
erofs_enable_sb_chksum(struct erofs_sb_info * sbi,u32 * crc)239 int erofs_enable_sb_chksum(struct erofs_sb_info *sbi, u32 *crc)
240 {
241 int ret;
242 u8 buf[EROFS_MAX_BLOCK_SIZE];
243 unsigned int len;
244 struct erofs_super_block *sb;
245
246 ret = erofs_blk_read(sbi, 0, buf, 0, erofs_blknr(sbi, EROFS_SUPER_END) + 1);
247 if (ret) {
248 erofs_err("failed to read superblock to set checksum: %s",
249 erofs_strerror(ret));
250 return ret;
251 }
252
253 /*
254 * skip the first 1024 bytes, to allow for the installation
255 * of x86 boot sectors and other oddities.
256 */
257 sb = (struct erofs_super_block *)(buf + EROFS_SUPER_OFFSET);
258
259 if (le32_to_cpu(sb->magic) != EROFS_SUPER_MAGIC_V1) {
260 erofs_err("internal error: not an erofs valid image");
261 return -EFAULT;
262 }
263
264 /* turn on checksum feature */
265 sb->feature_compat = cpu_to_le32(le32_to_cpu(sb->feature_compat) |
266 EROFS_FEATURE_COMPAT_SB_CHKSUM);
267 if (erofs_blksiz(sbi) > EROFS_SUPER_OFFSET)
268 len = erofs_blksiz(sbi) - EROFS_SUPER_OFFSET;
269 else
270 len = erofs_blksiz(sbi);
271 *crc = erofs_crc32c(~0, (u8 *)sb, len);
272
273 /* set up checksum field to erofs_super_block */
274 sb->checksum = cpu_to_le32(*crc);
275
276 ret = erofs_blk_write(sbi, buf, 0, 1);
277 if (ret) {
278 erofs_err("failed to write checksummed superblock: %s",
279 erofs_strerror(ret));
280 return ret;
281 }
282
283 return 0;
284 }
285