• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2020 Gao Xiang <hsiangkao@aol.com>
4  * Compression support by Huang Jianan <huangjianan@oppo.com>
5  */
6 #include <stdlib.h>
7 #include "erofs/print.h"
8 #include "erofs/internal.h"
9 #include "erofs/io.h"
10 #include "erofs/trace.h"
11 #include "erofs/decompress.h"
12 
erofs_map_blocks_flatmode(struct erofs_inode * inode,struct erofs_map_blocks * map,int flags)13 static int erofs_map_blocks_flatmode(struct erofs_inode *inode,
14 				     struct erofs_map_blocks *map,
15 				     int flags)
16 {
17 	int err = 0;
18 	erofs_blk_t nblocks, lastblk;
19 	u64 offset = map->m_la;
20 	struct erofs_inode *vi = inode;
21 	bool tailendpacking = (vi->datalayout == EROFS_INODE_FLAT_INLINE);
22 
23 	trace_erofs_map_blocks_flatmode_enter(inode, map, flags);
24 
25 	nblocks = DIV_ROUND_UP(inode->i_size, PAGE_SIZE);
26 	lastblk = nblocks - tailendpacking;
27 
28 	/* there is no hole in flatmode */
29 	map->m_flags = EROFS_MAP_MAPPED;
30 
31 	if (offset < blknr_to_addr(lastblk)) {
32 		map->m_pa = blknr_to_addr(vi->u.i_blkaddr) + map->m_la;
33 		map->m_plen = blknr_to_addr(lastblk) - offset;
34 	} else if (tailendpacking) {
35 		/* 2 - inode inline B: inode, [xattrs], inline last blk... */
36 		map->m_pa = iloc(vi->nid) + vi->inode_isize +
37 			vi->xattr_isize + erofs_blkoff(map->m_la);
38 		map->m_plen = inode->i_size - offset;
39 
40 		/* inline data should be located in one meta block */
41 		if (erofs_blkoff(map->m_pa) + map->m_plen > PAGE_SIZE) {
42 			erofs_err("inline data cross block boundary @ nid %" PRIu64,
43 				  vi->nid);
44 			DBG_BUGON(1);
45 			err = -EFSCORRUPTED;
46 			goto err_out;
47 		}
48 
49 		map->m_flags |= EROFS_MAP_META;
50 	} else {
51 		erofs_err("internal error @ nid: %" PRIu64 " (size %llu), m_la 0x%" PRIx64,
52 			  vi->nid, (unsigned long long)inode->i_size, map->m_la);
53 		DBG_BUGON(1);
54 		err = -EIO;
55 		goto err_out;
56 	}
57 
58 	map->m_llen = map->m_plen;
59 err_out:
60 	trace_erofs_map_blocks_flatmode_exit(inode, map, flags, 0);
61 	return err;
62 }
63 
erofs_map_blocks(struct erofs_inode * inode,struct erofs_map_blocks * map,int flags)64 int erofs_map_blocks(struct erofs_inode *inode,
65 		struct erofs_map_blocks *map, int flags)
66 {
67 	struct erofs_inode *vi = inode;
68 	struct erofs_inode_chunk_index *idx;
69 	u8 buf[EROFS_BLKSIZ];
70 	u64 chunknr;
71 	unsigned int unit;
72 	erofs_off_t pos;
73 	int err = 0;
74 
75 	map->m_deviceid = 0;
76 	if (map->m_la >= inode->i_size) {
77 		/* leave out-of-bound access unmapped */
78 		map->m_flags = 0;
79 		map->m_plen = 0;
80 		goto out;
81 	}
82 
83 	if (vi->datalayout != EROFS_INODE_CHUNK_BASED)
84 		return erofs_map_blocks_flatmode(inode, map, flags);
85 
86 	if (vi->u.chunkformat & EROFS_CHUNK_FORMAT_INDEXES)
87 		unit = sizeof(*idx);			/* chunk index */
88 	else
89 		unit = EROFS_BLOCK_MAP_ENTRY_SIZE;	/* block map */
90 
91 	chunknr = map->m_la >> vi->u.chunkbits;
92 	pos = roundup(iloc(vi->nid) + vi->inode_isize +
93 		      vi->xattr_isize, unit) + unit * chunknr;
94 
95 	err = blk_read(0, buf, erofs_blknr(pos), 1);
96 	if (err < 0)
97 		return -EIO;
98 
99 	map->m_la = chunknr << vi->u.chunkbits;
100 	map->m_plen = min_t(erofs_off_t, 1UL << vi->u.chunkbits,
101 			    roundup(inode->i_size - map->m_la, EROFS_BLKSIZ));
102 
103 	/* handle block map */
104 	if (!(vi->u.chunkformat & EROFS_CHUNK_FORMAT_INDEXES)) {
105 		__le32 *blkaddr = (void *)buf + erofs_blkoff(pos);
106 
107 		if (le32_to_cpu(*blkaddr) == EROFS_NULL_ADDR) {
108 			map->m_flags = 0;
109 		} else {
110 			map->m_pa = blknr_to_addr(le32_to_cpu(*blkaddr));
111 			map->m_flags = EROFS_MAP_MAPPED;
112 		}
113 		goto out;
114 	}
115 	/* parse chunk indexes */
116 	idx = (void *)buf + erofs_blkoff(pos);
117 	switch (le32_to_cpu(idx->blkaddr)) {
118 	case EROFS_NULL_ADDR:
119 		map->m_flags = 0;
120 		break;
121 	default:
122 		map->m_deviceid = le16_to_cpu(idx->device_id) &
123 			sbi.device_id_mask;
124 		map->m_pa = blknr_to_addr(le32_to_cpu(idx->blkaddr));
125 		map->m_flags = EROFS_MAP_MAPPED;
126 		break;
127 	}
128 out:
129 	map->m_llen = map->m_plen;
130 	return err;
131 }
132 
erofs_map_dev(struct erofs_sb_info * sbi,struct erofs_map_dev * map)133 int erofs_map_dev(struct erofs_sb_info *sbi, struct erofs_map_dev *map)
134 {
135 	struct erofs_device_info *dif;
136 	int id;
137 
138 	if (map->m_deviceid) {
139 		if (sbi->extra_devices < map->m_deviceid)
140 			return -ENODEV;
141 	} else if (sbi->extra_devices) {
142 		for (id = 0; id < sbi->extra_devices; ++id) {
143 			erofs_off_t startoff, length;
144 
145 			dif = sbi->devs + id;
146 			if (!dif->mapped_blkaddr)
147 				continue;
148 			startoff = blknr_to_addr(dif->mapped_blkaddr);
149 			length = blknr_to_addr(dif->blocks);
150 
151 			if (map->m_pa >= startoff &&
152 			    map->m_pa < startoff + length) {
153 				map->m_pa -= startoff;
154 				break;
155 			}
156 		}
157 	}
158 	return 0;
159 }
160 
erofs_read_raw_data(struct erofs_inode * inode,char * buffer,erofs_off_t size,erofs_off_t offset)161 static int erofs_read_raw_data(struct erofs_inode *inode, char *buffer,
162 			       erofs_off_t size, erofs_off_t offset)
163 {
164 	struct erofs_map_blocks map = {
165 		.index = UINT_MAX,
166 	};
167 	struct erofs_map_dev mdev;
168 	int ret;
169 	erofs_off_t ptr = offset;
170 
171 	while (ptr < offset + size) {
172 		char *const estart = buffer + ptr - offset;
173 		erofs_off_t eend;
174 
175 		map.m_la = ptr;
176 		ret = erofs_map_blocks(inode, &map, 0);
177 		if (ret)
178 			return ret;
179 
180 		DBG_BUGON(map.m_plen != map.m_llen);
181 
182 		mdev = (struct erofs_map_dev) {
183 			.m_deviceid = map.m_deviceid,
184 			.m_pa = map.m_pa,
185 		};
186 		ret = erofs_map_dev(&sbi, &mdev);
187 		if (ret)
188 			return ret;
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 			mdev.m_pa += ptr - map.m_la;
208 			map.m_la = ptr;
209 		}
210 
211 		ret = dev_read(mdev.m_deviceid, estart, mdev.m_pa,
212 			       eend - map.m_la);
213 		if (ret < 0)
214 			return -EIO;
215 		ptr = eend;
216 	}
217 	return 0;
218 }
219 
z_erofs_read_data(struct erofs_inode * inode,char * buffer,erofs_off_t size,erofs_off_t offset)220 static int z_erofs_read_data(struct erofs_inode *inode, char *buffer,
221 			     erofs_off_t size, erofs_off_t offset)
222 {
223 	erofs_off_t end, length, skip;
224 	struct erofs_map_blocks map = {
225 		.index = UINT_MAX,
226 	};
227 	struct erofs_map_dev mdev;
228 	bool partial;
229 	unsigned int bufsize = 0;
230 	char *raw = NULL;
231 	int ret = 0;
232 
233 	end = offset + size;
234 	while (end > offset) {
235 		map.m_la = end - 1;
236 
237 		ret = z_erofs_map_blocks_iter(inode, &map, 0);
238 		if (ret)
239 			break;
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(&sbi, &mdev);
246 		if (ret) {
247 			DBG_BUGON(1);
248 			break;
249 		}
250 
251 		/*
252 		 * trim to the needed size if the returned extent is quite
253 		 * larger than requested, and set up partial flag as well.
254 		 */
255 		if (end < map.m_la + map.m_llen) {
256 			length = end - map.m_la;
257 			partial = true;
258 		} else {
259 			DBG_BUGON(end != map.m_la + map.m_llen);
260 			length = map.m_llen;
261 			partial = !(map.m_flags & EROFS_MAP_FULL_MAPPED);
262 		}
263 
264 		if (map.m_la < offset) {
265 			skip = offset - map.m_la;
266 			end = offset;
267 		} else {
268 			skip = 0;
269 			end = map.m_la;
270 		}
271 
272 		if (!(map.m_flags & EROFS_MAP_MAPPED)) {
273 			memset(buffer + end - offset, 0, length);
274 			end = map.m_la;
275 			continue;
276 		}
277 
278 		if (map.m_plen > bufsize) {
279 			bufsize = map.m_plen;
280 			raw = realloc(raw, bufsize);
281 			if (!raw) {
282 				ret = -ENOMEM;
283 				break;
284 			}
285 		}
286 		ret = dev_read(mdev.m_deviceid, raw, mdev.m_pa, map.m_plen);
287 		if (ret < 0)
288 			break;
289 
290 		ret = z_erofs_decompress(&(struct z_erofs_decompress_req) {
291 					.in = raw,
292 					.out = buffer + end - offset,
293 					.decodedskip = skip,
294 					.inputsize = map.m_plen,
295 					.decodedlength = length,
296 					.alg = map.m_algorithmformat,
297 					.partial_decoding = partial
298 					 });
299 		if (ret < 0)
300 			break;
301 	}
302 	if (raw)
303 		free(raw);
304 	return ret < 0 ? ret : 0;
305 }
306 
erofs_pread(struct erofs_inode * inode,char * buf,erofs_off_t count,erofs_off_t offset)307 int erofs_pread(struct erofs_inode *inode, char *buf,
308 		erofs_off_t count, erofs_off_t offset)
309 {
310 	switch (inode->datalayout) {
311 	case EROFS_INODE_FLAT_PLAIN:
312 	case EROFS_INODE_FLAT_INLINE:
313 	case EROFS_INODE_CHUNK_BASED:
314 		return erofs_read_raw_data(inode, buf, count, offset);
315 	case EROFS_INODE_FLAT_COMPRESSION_LEGACY:
316 	case EROFS_INODE_FLAT_COMPRESSION:
317 		return z_erofs_read_data(inode, buf, count, offset);
318 	default:
319 		break;
320 	}
321 	return -EINVAL;
322 }
323