]>
Commit | Line | Data |
---|---|---|
830613f8 HJ |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | #include "internal.h" | |
65cb7305 | 3 | #include "decompress.h" |
830613f8 HJ |
4 | |
5 | static 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; | |
49 | err_out: | |
50 | return err; | |
51 | } | |
52 | ||
53 | int 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 | } | |
117 | out: | |
118 | map->m_llen = map->m_plen; | |
119 | return err; | |
120 | } | |
121 | ||
3a21e92f | 122 | int 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 |
150 | int 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 |
170 | static 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 |
219 | int 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 |
274 | static 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 |
341 | int 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 | } |