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