• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define _FILE_OFFSET_BITS 64
18 #define _LARGEFILE64_SOURCE 1
19 
20 #include <algorithm>
21 #include <inttypes.h>
22 #include <fcntl.h>
23 #include <stdarg.h>
24 #include <stdbool.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string>
29 #include <unistd.h>
30 
31 #include <sparse/sparse.h>
32 
33 #include "android-base/stringprintf.h"
34 #include "defs.h"
35 #include "output_file.h"
36 #include "sparse_crc32.h"
37 #include "sparse_file.h"
38 #include "sparse_format.h"
39 
40 
41 #if defined(__APPLE__) && defined(__MACH__)
42 #define lseek64 lseek
43 #define off64_t off_t
44 #endif
45 
46 #define SPARSE_HEADER_MAJOR_VER 1
47 #define SPARSE_HEADER_LEN       (sizeof(sparse_header_t))
48 #define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
49 
50 static constexpr int64_t COPY_BUF_SIZE = 1024 * 1024;
51 static char *copybuf;
52 
ErrorString(int err)53 static std::string ErrorString(int err)
54 {
55 	if (err == -EOVERFLOW) return "EOF while reading file";
56 	if (err == -EINVAL) return "Invalid sparse file format";
57 	if (err == -ENOMEM) return "Failed allocation while reading file";
58 	return android::base::StringPrintf("Unknown error %d", err);
59 }
60 
verbose_error(bool verbose,int err,const char * fmt,...)61 static void verbose_error(bool verbose, int err, const char *fmt, ...)
62 {
63 	if (!verbose) return;
64 
65 	std::string msg = ErrorString(err);
66 	if (fmt) {
67 		msg += " at ";
68 		va_list argp;
69 		va_start(argp, fmt);
70 		android::base::StringAppendV(&msg, fmt, argp);
71 		va_end(argp);
72 	}
73 	sparse_print_verbose("%s\n", msg.c_str());
74 }
75 
process_raw_chunk(struct sparse_file * s,unsigned int chunk_size,int fd,int64_t offset,unsigned int blocks,unsigned int block,uint32_t * crc32)76 static int process_raw_chunk(struct sparse_file *s, unsigned int chunk_size,
77 		int fd, int64_t offset, unsigned int blocks, unsigned int block,
78 		uint32_t *crc32)
79 {
80 	int ret;
81 	int chunk;
82 	int64_t len = blocks * s->block_size;
83 
84 	if (chunk_size % s->block_size != 0) {
85 		return -EINVAL;
86 	}
87 
88 	if (chunk_size / s->block_size != blocks) {
89 		return -EINVAL;
90 	}
91 
92 	ret = sparse_file_add_fd(s, fd, offset, len, block);
93 	if (ret < 0) {
94 		return ret;
95 	}
96 
97 	if (crc32) {
98 		while (len) {
99 			chunk = std::min(len, COPY_BUF_SIZE);
100 			ret = read_all(fd, copybuf, chunk);
101 			if (ret < 0) {
102 				return ret;
103 			}
104 			*crc32 = sparse_crc32(*crc32, copybuf, chunk);
105 			len -= chunk;
106 		}
107 	} else {
108 		lseek64(fd, len, SEEK_CUR);
109 	}
110 
111 	return 0;
112 }
113 
process_fill_chunk(struct sparse_file * s,unsigned int chunk_size,int fd,unsigned int blocks,unsigned int block,uint32_t * crc32)114 static int process_fill_chunk(struct sparse_file *s, unsigned int chunk_size,
115 		int fd, unsigned int blocks, unsigned int block, uint32_t *crc32)
116 {
117 	int ret;
118 	int chunk;
119 	int64_t len = (int64_t)blocks * s->block_size;
120 	uint32_t fill_val;
121 	uint32_t *fillbuf;
122 	unsigned int i;
123 
124 	if (chunk_size != sizeof(fill_val)) {
125 		return -EINVAL;
126 	}
127 
128 	ret = read_all(fd, &fill_val, sizeof(fill_val));
129 	if (ret < 0) {
130 		return ret;
131 	}
132 
133 	ret = sparse_file_add_fill(s, fill_val, len, block);
134 	if (ret < 0) {
135 		return ret;
136 	}
137 
138 	if (crc32) {
139 		/* Fill copy_buf with the fill value */
140 		fillbuf = (uint32_t *)copybuf;
141 		for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) {
142 			fillbuf[i] = fill_val;
143 		}
144 
145 		while (len) {
146 			chunk = std::min(len, COPY_BUF_SIZE);
147 			*crc32 = sparse_crc32(*crc32, copybuf, chunk);
148 			len -= chunk;
149 		}
150 	}
151 
152 	return 0;
153 }
154 
process_skip_chunk(struct sparse_file * s,unsigned int chunk_size,int fd __unused,unsigned int blocks,unsigned int block __unused,uint32_t * crc32)155 static int process_skip_chunk(struct sparse_file *s, unsigned int chunk_size,
156 		int fd __unused, unsigned int blocks,
157 		unsigned int block __unused, uint32_t *crc32)
158 {
159 	if (chunk_size != 0) {
160 		return -EINVAL;
161 	}
162 
163 	if (crc32) {
164 	        int64_t len = (int64_t)blocks * s->block_size;
165 		memset(copybuf, 0, COPY_BUF_SIZE);
166 
167 		while (len) {
168 			int chunk = std::min(len, COPY_BUF_SIZE);
169 			*crc32 = sparse_crc32(*crc32, copybuf, chunk);
170 			len -= chunk;
171 		}
172 	}
173 
174 	return 0;
175 }
176 
process_crc32_chunk(int fd,unsigned int chunk_size,uint32_t * crc32)177 static int process_crc32_chunk(int fd, unsigned int chunk_size, uint32_t *crc32)
178 {
179 	uint32_t file_crc32;
180 	int ret;
181 
182 	if (chunk_size != sizeof(file_crc32)) {
183 		return -EINVAL;
184 	}
185 
186 	ret = read_all(fd, &file_crc32, sizeof(file_crc32));
187 	if (ret < 0) {
188 		return ret;
189 	}
190 
191 	if (crc32 != NULL && file_crc32 != *crc32) {
192 		return -EINVAL;
193 	}
194 
195 	return 0;
196 }
197 
process_chunk(struct sparse_file * s,int fd,off64_t offset,unsigned int chunk_hdr_sz,chunk_header_t * chunk_header,unsigned int cur_block,uint32_t * crc_ptr)198 static int process_chunk(struct sparse_file *s, int fd, off64_t offset,
199 		unsigned int chunk_hdr_sz, chunk_header_t *chunk_header,
200 		unsigned int cur_block, uint32_t *crc_ptr)
201 {
202 	int ret;
203 	unsigned int chunk_data_size;
204 
205 	chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;
206 
207 	switch (chunk_header->chunk_type) {
208 		case CHUNK_TYPE_RAW:
209 			ret = process_raw_chunk(s, chunk_data_size, fd, offset,
210 					chunk_header->chunk_sz, cur_block, crc_ptr);
211 			if (ret < 0) {
212 				verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
213 				return ret;
214 			}
215 			return chunk_header->chunk_sz;
216 		case CHUNK_TYPE_FILL:
217 			ret = process_fill_chunk(s, chunk_data_size, fd,
218 					chunk_header->chunk_sz, cur_block, crc_ptr);
219 			if (ret < 0) {
220 				verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
221 				return ret;
222 			}
223 			return chunk_header->chunk_sz;
224 		case CHUNK_TYPE_DONT_CARE:
225 			ret = process_skip_chunk(s, chunk_data_size, fd,
226 					chunk_header->chunk_sz, cur_block, crc_ptr);
227 			if (chunk_data_size != 0) {
228 				if (ret < 0) {
229 					verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
230 					return ret;
231 				}
232 			}
233 			return chunk_header->chunk_sz;
234 		case CHUNK_TYPE_CRC32:
235 			ret = process_crc32_chunk(fd, chunk_data_size, crc_ptr);
236 			if (ret < 0) {
237 				verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64,
238 						offset);
239 				return ret;
240 			}
241 			return 0;
242 		default:
243 			verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64,
244 					chunk_header->chunk_type, offset);
245 	}
246 
247 	return 0;
248 }
249 
sparse_file_read_sparse(struct sparse_file * s,int fd,bool crc)250 static int sparse_file_read_sparse(struct sparse_file *s, int fd, bool crc)
251 {
252 	int ret;
253 	unsigned int i;
254 	sparse_header_t sparse_header;
255 	chunk_header_t chunk_header;
256 	uint32_t crc32 = 0;
257 	uint32_t *crc_ptr = 0;
258 	unsigned int cur_block = 0;
259 	off64_t offset;
260 
261 	if (!copybuf) {
262 		copybuf = (char *)malloc(COPY_BUF_SIZE);
263 	}
264 
265 	if (!copybuf) {
266 		return -ENOMEM;
267 	}
268 
269 	if (crc) {
270 		crc_ptr = &crc32;
271 	}
272 
273 	ret = read_all(fd, &sparse_header, sizeof(sparse_header));
274 	if (ret < 0) {
275 		return ret;
276 	}
277 
278 	if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
279 		return -EINVAL;
280 	}
281 
282 	if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
283 		return -EINVAL;
284 	}
285 
286 	if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
287 		return -EINVAL;
288 	}
289 
290 	if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {
291 		return -EINVAL;
292 	}
293 
294 	if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
295 		/* Skip the remaining bytes in a header that is longer than
296 		 * we expected.
297 		 */
298 		lseek64(fd, sparse_header.file_hdr_sz - SPARSE_HEADER_LEN, SEEK_CUR);
299 	}
300 
301 	for (i = 0; i < sparse_header.total_chunks; i++) {
302 		ret = read_all(fd, &chunk_header, sizeof(chunk_header));
303 		if (ret < 0) {
304 			return ret;
305 		}
306 
307 		if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
308 			/* Skip the remaining bytes in a header that is longer than
309 			 * we expected.
310 			 */
311 			lseek64(fd, sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN, SEEK_CUR);
312 		}
313 
314 		offset = lseek64(fd, 0, SEEK_CUR);
315 
316 		ret = process_chunk(s, fd, offset, sparse_header.chunk_hdr_sz, &chunk_header,
317 				cur_block, crc_ptr);
318 		if (ret < 0) {
319 			return ret;
320 		}
321 
322 		cur_block += ret;
323 	}
324 
325 	if (sparse_header.total_blks != cur_block) {
326 		return -EINVAL;
327 	}
328 
329 	return 0;
330 }
331 
sparse_file_read_normal(struct sparse_file * s,int fd)332 static int sparse_file_read_normal(struct sparse_file *s, int fd)
333 {
334 	int ret;
335 	uint32_t *buf = (uint32_t *)malloc(s->block_size);
336 	unsigned int block = 0;
337 	int64_t remain = s->len;
338 	int64_t offset = 0;
339 	unsigned int to_read;
340 	unsigned int i;
341 	bool sparse_block;
342 
343 	if (!buf) {
344 		return -ENOMEM;
345 	}
346 
347 	while (remain > 0) {
348 		to_read = std::min(remain, (int64_t)(s->block_size));
349 		ret = read_all(fd, buf, to_read);
350 		if (ret < 0) {
351 			error("failed to read sparse file");
352 			free(buf);
353 			return ret;
354 		}
355 
356 		if (to_read == s->block_size) {
357 			sparse_block = true;
358 			for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {
359 				if (buf[0] != buf[i]) {
360 					sparse_block = false;
361 					break;
362 				}
363 			}
364 		} else {
365 			sparse_block = false;
366 		}
367 
368 		if (sparse_block) {
369 			/* TODO: add flag to use skip instead of fill for buf[0] == 0 */
370 			sparse_file_add_fill(s, buf[0], to_read, block);
371 		} else {
372 			sparse_file_add_fd(s, fd, offset, to_read, block);
373 		}
374 
375 		remain -= to_read;
376 		offset += to_read;
377 		block++;
378 	}
379 
380 	free(buf);
381 	return 0;
382 }
383 
sparse_file_read(struct sparse_file * s,int fd,bool sparse,bool crc)384 int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc)
385 {
386 	if (crc && !sparse) {
387 		return -EINVAL;
388 	}
389 
390 	if (sparse) {
391 		return sparse_file_read_sparse(s, fd, crc);
392 	} else {
393 		return sparse_file_read_normal(s, fd);
394 	}
395 }
396 
sparse_file_import(int fd,bool verbose,bool crc)397 struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc)
398 {
399 	int ret;
400 	sparse_header_t sparse_header;
401 	int64_t len;
402 	struct sparse_file *s;
403 
404 	ret = read_all(fd, &sparse_header, sizeof(sparse_header));
405 	if (ret < 0) {
406 		verbose_error(verbose, ret, "header");
407 		return NULL;
408 	}
409 
410 	if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
411 		verbose_error(verbose, -EINVAL, "header magic");
412 		return NULL;
413 	}
414 
415 	if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
416 		verbose_error(verbose, -EINVAL, "header major version");
417 		return NULL;
418 	}
419 
420 	if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
421 		return NULL;
422 	}
423 
424 	if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
425 		return NULL;
426 	}
427 
428 	len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
429 	s = sparse_file_new(sparse_header.blk_sz, len);
430 	if (!s) {
431 		verbose_error(verbose, -EINVAL, NULL);
432 		return NULL;
433 	}
434 
435 	ret = lseek64(fd, 0, SEEK_SET);
436 	if (ret < 0) {
437 		verbose_error(verbose, ret, "seeking");
438 		sparse_file_destroy(s);
439 		return NULL;
440 	}
441 
442 	s->verbose = verbose;
443 
444 	ret = sparse_file_read(s, fd, true, crc);
445 	if (ret < 0) {
446 		sparse_file_destroy(s);
447 		return NULL;
448 	}
449 
450 	return s;
451 }
452 
sparse_file_import_auto(int fd,bool crc,bool verbose)453 struct sparse_file *sparse_file_import_auto(int fd, bool crc, bool verbose)
454 {
455 	struct sparse_file *s;
456 	int64_t len;
457 	int ret;
458 
459 	s = sparse_file_import(fd, verbose, crc);
460 	if (s) {
461 		return s;
462 	}
463 
464 	len = lseek64(fd, 0, SEEK_END);
465 	if (len < 0) {
466 		return NULL;
467 	}
468 
469 	lseek64(fd, 0, SEEK_SET);
470 
471 	s = sparse_file_new(4096, len);
472 	if (!s) {
473 		return NULL;
474 	}
475 
476 	ret = sparse_file_read_normal(s, fd);
477 	if (ret < 0) {
478 		sparse_file_destroy(s);
479 		return NULL;
480 	}
481 
482 	return s;
483 }
484