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