1 // SPDX-License-Identifier: GPL-2.0+
2 #include "internal.h"
3 #include "decompress.h"
4 
erofs_map_blocks_flatmode(struct erofs_inode * inode,struct erofs_map_blocks * map,int flags)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 
15 	nblocks = BLK_ROUND_UP(inode->i_size);
16 	lastblk = nblocks - tailendpacking;
17 
18 	/* there is no hole in flatmode */
19 	map->m_flags = EROFS_MAP_MAPPED;
20 
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;
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 
30 		/* inline data should be located in the same meta block */
31 		if (erofs_blkoff(map->m_pa) + map->m_plen > erofs_blksiz()) {
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 
erofs_map_blocks(struct erofs_inode * inode,struct erofs_map_blocks * map,int flags)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;
58 	u8 buf[EROFS_MAX_BLOCK_SIZE];
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,
90 			    roundup(inode->i_size - map->m_la, erofs_blksiz()));
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 {
99 			map->m_pa = erofs_pos(le32_to_cpu(*blkaddr));
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;
113 		map->m_pa = erofs_pos(le32_to_cpu(idx->blkaddr));
114 		map->m_flags = EROFS_MAP_MAPPED;
115 		break;
116 	}
117 out:
118 	map->m_llen = map->m_plen;
119 	return err;
120 }
121 
erofs_map_dev(struct erofs_map_dev * map)122 int erofs_map_dev(struct erofs_map_dev *map)
123 {
124 	struct erofs_device_info *dif;
125 	int id;
126 
127 	if (map->m_deviceid) {
128 		if (sbi.extra_devices < map->m_deviceid)
129 			return -ENODEV;
130 	} else if (sbi.extra_devices) {
131 		for (id = 0; id < sbi.extra_devices; ++id) {
132 			erofs_off_t startoff, length;
133 
134 			dif = sbi.devs + id;
135 			if (!dif->mapped_blkaddr)
136 				continue;
137 			startoff = erofs_pos(dif->mapped_blkaddr);
138 			length = erofs_pos(dif->blocks);
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 
erofs_read_one_data(struct erofs_map_blocks * map,char * buffer,u64 offset,size_t len)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 
erofs_read_raw_data(struct erofs_inode * inode,char * buffer,erofs_off_t size,erofs_off_t offset)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 	};
176 	int ret;
177 	erofs_off_t ptr = offset;
178 
179 	while (ptr < offset + size) {
180 		char *const estart = buffer + ptr - offset;
181 		erofs_off_t eend, moff = 0;
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 
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) {
207 			moff = ptr - map.m_la;
208 			map.m_la = ptr;
209 		}
210 
211 		ret = erofs_read_one_data(&map, estart, moff, eend - map.m_la);
212 		if (ret)
213 			return ret;
214 		ptr = eend;
215 	}
216 	return 0;
217 }
218 
z_erofs_read_one_data(struct erofs_inode * inode,struct erofs_map_blocks * map,char * raw,char * buffer,erofs_off_t skip,erofs_off_t length,bool trimmed)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 
z_erofs_read_data(struct erofs_inode * inode,char * buffer,erofs_off_t size,erofs_off_t offset)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 	};
281 	bool trimmed;
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 
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;
300 			trimmed = true;
301 		} else {
302 			DBG_BUGON(end != map.m_la + map.m_llen);
303 			length = map.m_llen;
304 			trimmed = false;
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 - skip);
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 		}
329 
330 		ret = z_erofs_read_one_data(inode, &map, raw,
331 					    buffer + end - offset, skip, length,
332 					    trimmed);
333 		if (ret < 0)
334 			break;
335 	}
336 	if (raw)
337 		free(raw);
338 	return ret < 0 ? ret : 0;
339 }
340 
erofs_pread(struct erofs_inode * inode,char * buf,erofs_off_t count,erofs_off_t offset)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);
349 	case EROFS_INODE_COMPRESSED_FULL:
350 	case EROFS_INODE_COMPRESSED_COMPACT:
351 		return z_erofs_read_data(inode, buf, count, offset);
352 	default:
353 		break;
354 	}
355 	return -EINVAL;
356 }
357