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