]> git.ipfire.org Git - thirdparty/u-boot.git/blame - fs/erofs/data.c
fs/erofs: Introduce new features including ztailpacking, fragments and dedupe
[thirdparty/u-boot.git] / fs / erofs / data.c
CommitLineData
830613f8
HJ
1// SPDX-License-Identifier: GPL-2.0+
2#include "internal.h"
65cb7305 3#include "decompress.h"
830613f8
HJ
4
5static int erofs_map_blocks_flatmode(struct erofs_inode *inode,
6 struct erofs_map_blocks *map,
7 int flags)
8{
9 int err = 0;
10 erofs_blk_t nblocks, lastblk;
11 u64 offset = map->m_la;
12 struct erofs_inode *vi = inode;
13 bool tailendpacking = (vi->datalayout == EROFS_INODE_FLAT_INLINE);
14
3a21e92f 15 nblocks = BLK_ROUND_UP(inode->i_size);
830613f8
HJ
16 lastblk = nblocks - tailendpacking;
17
18 /* there is no hole in flatmode */
19 map->m_flags = EROFS_MAP_MAPPED;
20
3a21e92f
YZ
21 if (offset < erofs_pos(lastblk)) {
22 map->m_pa = erofs_pos(vi->u.i_blkaddr) + map->m_la;
23 map->m_plen = erofs_pos(lastblk) - offset;
830613f8
HJ
24 } else if (tailendpacking) {
25 /* 2 - inode inline B: inode, [xattrs], inline last blk... */
26 map->m_pa = iloc(vi->nid) + vi->inode_isize +
27 vi->xattr_isize + erofs_blkoff(map->m_la);
28 map->m_plen = inode->i_size - offset;
29
3a21e92f
YZ
30 /* inline data should be located in the same meta block */
31 if (erofs_blkoff(map->m_pa) + map->m_plen > erofs_blksiz()) {
830613f8
HJ
32 erofs_err("inline data cross block boundary @ nid %" PRIu64,
33 vi->nid);
34 DBG_BUGON(1);
35 err = -EFSCORRUPTED;
36 goto err_out;
37 }
38
39 map->m_flags |= EROFS_MAP_META;
40 } else {
41 erofs_err("internal error @ nid: %" PRIu64 " (size %llu), m_la 0x%" PRIx64,
42 vi->nid, (unsigned long long)inode->i_size, map->m_la);
43 DBG_BUGON(1);
44 err = -EIO;
45 goto err_out;
46 }
47
48 map->m_llen = map->m_plen;
49err_out:
50 return err;
51}
52
53int erofs_map_blocks(struct erofs_inode *inode,
54 struct erofs_map_blocks *map, int flags)
55{
56 struct erofs_inode *vi = inode;
57 struct erofs_inode_chunk_index *idx;
3a21e92f 58 u8 buf[EROFS_MAX_BLOCK_SIZE];
830613f8
HJ
59 u64 chunknr;
60 unsigned int unit;
61 erofs_off_t pos;
62 int err = 0;
63
64 map->m_deviceid = 0;
65 if (map->m_la >= inode->i_size) {
66 /* leave out-of-bound access unmapped */
67 map->m_flags = 0;
68 map->m_plen = 0;
69 goto out;
70 }
71
72 if (vi->datalayout != EROFS_INODE_CHUNK_BASED)
73 return erofs_map_blocks_flatmode(inode, map, flags);
74
75 if (vi->u.chunkformat & EROFS_CHUNK_FORMAT_INDEXES)
76 unit = sizeof(*idx); /* chunk index */
77 else
78 unit = EROFS_BLOCK_MAP_ENTRY_SIZE; /* block map */
79
80 chunknr = map->m_la >> vi->u.chunkbits;
81 pos = roundup(iloc(vi->nid) + vi->inode_isize +
82 vi->xattr_isize, unit) + unit * chunknr;
83
84 err = erofs_blk_read(buf, erofs_blknr(pos), 1);
85 if (err < 0)
86 return -EIO;
87
88 map->m_la = chunknr << vi->u.chunkbits;
89 map->m_plen = min_t(erofs_off_t, 1UL << vi->u.chunkbits,
3a21e92f 90 roundup(inode->i_size - map->m_la, erofs_blksiz()));
830613f8
HJ
91
92 /* handle block map */
93 if (!(vi->u.chunkformat & EROFS_CHUNK_FORMAT_INDEXES)) {
94 __le32 *blkaddr = (void *)buf + erofs_blkoff(pos);
95
96 if (le32_to_cpu(*blkaddr) == EROFS_NULL_ADDR) {
97 map->m_flags = 0;
98 } else {
3a21e92f 99 map->m_pa = erofs_pos(le32_to_cpu(*blkaddr));
830613f8
HJ
100 map->m_flags = EROFS_MAP_MAPPED;
101 }
102 goto out;
103 }
104 /* parse chunk indexes */
105 idx = (void *)buf + erofs_blkoff(pos);
106 switch (le32_to_cpu(idx->blkaddr)) {
107 case EROFS_NULL_ADDR:
108 map->m_flags = 0;
109 break;
110 default:
111 map->m_deviceid = le16_to_cpu(idx->device_id) &
112 sbi.device_id_mask;
3a21e92f 113 map->m_pa = erofs_pos(le32_to_cpu(idx->blkaddr));
830613f8
HJ
114 map->m_flags = EROFS_MAP_MAPPED;
115 break;
116 }
117out:
118 map->m_llen = map->m_plen;
119 return err;
120}
121
3a21e92f 122int erofs_map_dev(struct erofs_map_dev *map)
830613f8
HJ
123{
124 struct erofs_device_info *dif;
125 int id;
126
127 if (map->m_deviceid) {
3a21e92f 128 if (sbi.extra_devices < map->m_deviceid)
830613f8 129 return -ENODEV;
3a21e92f
YZ
130 } else if (sbi.extra_devices) {
131 for (id = 0; id < sbi.extra_devices; ++id) {
830613f8
HJ
132 erofs_off_t startoff, length;
133
3a21e92f 134 dif = sbi.devs + id;
830613f8
HJ
135 if (!dif->mapped_blkaddr)
136 continue;
3a21e92f
YZ
137 startoff = erofs_pos(dif->mapped_blkaddr);
138 length = erofs_pos(dif->blocks);
830613f8
HJ
139
140 if (map->m_pa >= startoff &&
141 map->m_pa < startoff + length) {
142 map->m_pa -= startoff;
143 break;
144 }
145 }
146 }
147 return 0;
148}
149
3a21e92f
YZ
150int erofs_read_one_data(struct erofs_map_blocks *map, char *buffer, u64 offset,
151 size_t len)
152{
153 struct erofs_map_dev mdev;
154 int ret;
155
156 mdev = (struct erofs_map_dev) {
157 .m_deviceid = map->m_deviceid,
158 .m_pa = map->m_pa,
159 };
160 ret = erofs_map_dev(&mdev);
161 if (ret)
162 return ret;
163
164 ret = erofs_dev_read(mdev.m_deviceid, buffer, mdev.m_pa + offset, len);
165 if (ret < 0)
166 return -EIO;
167 return 0;
168}
169
830613f8
HJ
170static int erofs_read_raw_data(struct erofs_inode *inode, char *buffer,
171 erofs_off_t size, erofs_off_t offset)
172{
173 struct erofs_map_blocks map = {
174 .index = UINT_MAX,
175 };
830613f8
HJ
176 int ret;
177 erofs_off_t ptr = offset;
178
179 while (ptr < offset + size) {
180 char *const estart = buffer + ptr - offset;
3a21e92f 181 erofs_off_t eend, moff = 0;
830613f8
HJ
182
183 map.m_la = ptr;
184 ret = erofs_map_blocks(inode, &map, 0);
185 if (ret)
186 return ret;
187
188 DBG_BUGON(map.m_plen != map.m_llen);
189
830613f8
HJ
190 /* trim extent */
191 eend = min(offset + size, map.m_la + map.m_llen);
192 DBG_BUGON(ptr < map.m_la);
193
194 if (!(map.m_flags & EROFS_MAP_MAPPED)) {
195 if (!map.m_llen) {
196 /* reached EOF */
197 memset(estart, 0, offset + size - ptr);
198 ptr = offset + size;
199 continue;
200 }
201 memset(estart, 0, eend - ptr);
202 ptr = eend;
203 continue;
204 }
205
206 if (ptr > map.m_la) {
3a21e92f 207 moff = ptr - map.m_la;
830613f8
HJ
208 map.m_la = ptr;
209 }
210
3a21e92f
YZ
211 ret = erofs_read_one_data(&map, estart, moff, eend - map.m_la);
212 if (ret)
213 return ret;
830613f8
HJ
214 ptr = eend;
215 }
216 return 0;
217}
218
3a21e92f
YZ
219int z_erofs_read_one_data(struct erofs_inode *inode,
220 struct erofs_map_blocks *map, char *raw, char *buffer,
221 erofs_off_t skip, erofs_off_t length, bool trimmed)
222{
223 struct erofs_map_dev mdev;
224 int ret = 0;
225
226 if (map->m_flags & EROFS_MAP_FRAGMENT) {
227 struct erofs_inode packed_inode = {
228 .nid = sbi.packed_nid,
229 };
230
231 ret = erofs_read_inode_from_disk(&packed_inode);
232 if (ret) {
233 erofs_err("failed to read packed inode from disk");
234 return ret;
235 }
236
237 return erofs_pread(&packed_inode, buffer, length - skip,
238 inode->fragmentoff + skip);
239 }
240
241 /* no device id here, thus it will always succeed */
242 mdev = (struct erofs_map_dev) {
243 .m_pa = map->m_pa,
244 };
245 ret = erofs_map_dev(&mdev);
246 if (ret) {
247 DBG_BUGON(1);
248 return ret;
249 }
250
251 ret = erofs_dev_read(mdev.m_deviceid, raw, mdev.m_pa, map->m_plen);
252 if (ret < 0)
253 return ret;
254
255 ret = z_erofs_decompress(&(struct z_erofs_decompress_req) {
256 .in = raw,
257 .out = buffer,
258 .decodedskip = skip,
259 .interlaced_offset =
260 map->m_algorithmformat == Z_EROFS_COMPRESSION_INTERLACED ?
261 erofs_blkoff(map->m_la) : 0,
262 .inputsize = map->m_plen,
263 .decodedlength = length,
264 .alg = map->m_algorithmformat,
265 .partial_decoding = trimmed ? true :
266 !(map->m_flags & EROFS_MAP_FULL_MAPPED) ||
267 (map->m_flags & EROFS_MAP_PARTIAL_REF),
268 });
269 if (ret < 0)
270 return ret;
271 return 0;
272}
273
65cb7305
HJ
274static int z_erofs_read_data(struct erofs_inode *inode, char *buffer,
275 erofs_off_t size, erofs_off_t offset)
276{
277 erofs_off_t end, length, skip;
278 struct erofs_map_blocks map = {
279 .index = UINT_MAX,
280 };
3a21e92f 281 bool trimmed;
65cb7305
HJ
282 unsigned int bufsize = 0;
283 char *raw = NULL;
284 int ret = 0;
285
286 end = offset + size;
287 while (end > offset) {
288 map.m_la = end - 1;
289
290 ret = z_erofs_map_blocks_iter(inode, &map, 0);
291 if (ret)
292 break;
293
65cb7305
HJ
294 /*
295 * trim to the needed size if the returned extent is quite
296 * larger than requested, and set up partial flag as well.
297 */
298 if (end < map.m_la + map.m_llen) {
299 length = end - map.m_la;
3a21e92f 300 trimmed = true;
65cb7305
HJ
301 } else {
302 DBG_BUGON(end != map.m_la + map.m_llen);
303 length = map.m_llen;
3a21e92f 304 trimmed = false;
65cb7305
HJ
305 }
306
307 if (map.m_la < offset) {
308 skip = offset - map.m_la;
309 end = offset;
310 } else {
311 skip = 0;
312 end = map.m_la;
313 }
314
315 if (!(map.m_flags & EROFS_MAP_MAPPED)) {
316 memset(buffer + end - offset, 0, length);
317 end = map.m_la;
318 continue;
319 }
320
321 if (map.m_plen > bufsize) {
322 bufsize = map.m_plen;
323 raw = realloc(raw, bufsize);
324 if (!raw) {
325 ret = -ENOMEM;
326 break;
327 }
328 }
65cb7305 329
3a21e92f
YZ
330 ret = z_erofs_read_one_data(inode, &map, raw,
331 buffer + end - offset, skip, length,
332 trimmed);
65cb7305
HJ
333 if (ret < 0)
334 break;
335 }
336 if (raw)
337 free(raw);
338 return ret < 0 ? ret : 0;
339}
340
830613f8
HJ
341int erofs_pread(struct erofs_inode *inode, char *buf,
342 erofs_off_t count, erofs_off_t offset)
343{
344 switch (inode->datalayout) {
345 case EROFS_INODE_FLAT_PLAIN:
346 case EROFS_INODE_FLAT_INLINE:
347 case EROFS_INODE_CHUNK_BASED:
348 return erofs_read_raw_data(inode, buf, count, offset);
3a21e92f
YZ
349 case EROFS_INODE_COMPRESSED_FULL:
350 case EROFS_INODE_COMPRESSED_COMPACT:
65cb7305 351 return z_erofs_read_data(inode, buf, count, offset);
830613f8
HJ
352 default:
353 break;
354 }
355 return -EINVAL;
356}