blob: 873c87c7a88fbf3029a1623373e5ba57454e6986 [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
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
Mark Salyzyn031a7482014-02-27 16:56:15 -080032#include "defs.h"
33#include "output_file.h"
Colin Cross0c4c47f2012-04-25 19:02:58 -070034#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)
48static char *copybuf;
49
50#define min(a, b) \
51 ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
52
53static 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
100static 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
138static 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
179static int process_skip_chunk(struct sparse_file *s, unsigned int chunk_size,
Mark Salyzyn031a7482014-02-27 16:56:15 -0800180 int fd __unused, unsigned int blocks,
181 unsigned int block __unused, uint32_t *crc32)
Colin Cross0c4c47f2012-04-25 19:02:58 -0700182{
183 int ret;
184 int chunk;
185 int64_t len = (int64_t)blocks * s->block_size;
186 uint32_t fill_val;
187 uint32_t *fillbuf;
188 unsigned int i;
189
190 if (chunk_size != 0) {
191 return -EINVAL;
192 }
193
194 if (crc32) {
195 memset(copybuf, 0, COPY_BUF_SIZE);
196
197 while (len) {
198 chunk = min(len, COPY_BUF_SIZE);
199 *crc32 = sparse_crc32(*crc32, copybuf, chunk);
200 len -= chunk;
201 }
202 }
203
204 return 0;
205}
206
207static int process_crc32_chunk(int fd, unsigned int chunk_size, uint32_t crc32)
208{
209 uint32_t file_crc32;
210 int ret;
211
212 if (chunk_size != sizeof(file_crc32)) {
213 return -EINVAL;
214 }
215
216 ret = read_all(fd, &file_crc32, sizeof(file_crc32));
217 if (ret < 0) {
218 return ret;
219 }
220
221 if (file_crc32 != crc32) {
222 return -EINVAL;
223 }
224
225 return 0;
226}
227
228static int process_chunk(struct sparse_file *s, int fd, off64_t offset,
229 unsigned int chunk_hdr_sz, chunk_header_t *chunk_header,
230 unsigned int cur_block, uint32_t *crc_ptr)
231{
232 int ret;
233 unsigned int chunk_data_size;
234
235 chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;
236
237 switch (chunk_header->chunk_type) {
238 case CHUNK_TYPE_RAW:
239 ret = process_raw_chunk(s, chunk_data_size, fd, offset,
240 chunk_header->chunk_sz, cur_block, crc_ptr);
241 if (ret < 0) {
242 verbose_error(s->verbose, ret, "data block at %lld", offset);
243 return ret;
244 }
245 return chunk_header->chunk_sz;
246 case CHUNK_TYPE_FILL:
247 ret = process_fill_chunk(s, chunk_data_size, fd,
248 chunk_header->chunk_sz, cur_block, crc_ptr);
249 if (ret < 0) {
250 verbose_error(s->verbose, ret, "fill block at %lld", offset);
251 return ret;
252 }
253 return chunk_header->chunk_sz;
254 case CHUNK_TYPE_DONT_CARE:
255 ret = process_skip_chunk(s, chunk_data_size, fd,
256 chunk_header->chunk_sz, cur_block, crc_ptr);
257 if (chunk_data_size != 0) {
258 if (ret < 0) {
259 verbose_error(s->verbose, ret, "skip block at %lld", offset);
260 return ret;
261 }
262 }
263 return chunk_header->chunk_sz;
264 case CHUNK_TYPE_CRC32:
265 ret = process_crc32_chunk(fd, chunk_data_size, *crc_ptr);
266 if (ret < 0) {
267 verbose_error(s->verbose, -EINVAL, "crc block at %lld",
268 offset);
269 return ret;
270 }
271 return 0;
272 default:
273 verbose_error(s->verbose, -EINVAL, "unknown block %04X at %lld",
274 chunk_header->chunk_type, offset);
275 }
276
277 return 0;
278}
279
280static int sparse_file_read_sparse(struct sparse_file *s, int fd, bool crc)
281{
282 int ret;
283 unsigned int i;
284 sparse_header_t sparse_header;
285 chunk_header_t chunk_header;
286 uint32_t crc32 = 0;
287 uint32_t *crc_ptr = 0;
288 unsigned int cur_block = 0;
289 off64_t offset;
290
291 if (!copybuf) {
292 copybuf = malloc(COPY_BUF_SIZE);
293 }
294
295 if (!copybuf) {
296 return -ENOMEM;
297 }
298
299 if (crc) {
300 crc_ptr = &crc32;
301 }
302
303 ret = read_all(fd, &sparse_header, sizeof(sparse_header));
304 if (ret < 0) {
305 return ret;
306 }
307
308 if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
309 return -EINVAL;
310 }
311
312 if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
313 return -EINVAL;
314 }
315
316 if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
317 return -EINVAL;
318 }
319
320 if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {
321 return -EINVAL;
322 }
323
324 if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
325 /* Skip the remaining bytes in a header that is longer than
326 * we expected.
327 */
328 lseek64(fd, sparse_header.file_hdr_sz - SPARSE_HEADER_LEN, SEEK_CUR);
329 }
330
331 for (i = 0; i < sparse_header.total_chunks; i++) {
332 ret = read_all(fd, &chunk_header, sizeof(chunk_header));
333 if (ret < 0) {
334 return ret;
335 }
336
337 if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
338 /* Skip the remaining bytes in a header that is longer than
339 * we expected.
340 */
341 lseek64(fd, sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN, SEEK_CUR);
342 }
343
344 offset = lseek64(fd, 0, SEEK_CUR);
345
346 ret = process_chunk(s, fd, offset, sparse_header.chunk_hdr_sz, &chunk_header,
347 cur_block, crc_ptr);
348 if (ret < 0) {
349 return ret;
350 }
351
352 cur_block += ret;
353 }
354
355 if (sparse_header.total_blks != cur_block) {
356 return -EINVAL;
357 }
358
359 return 0;
360}
361
362static int sparse_file_read_normal(struct sparse_file *s, int fd)
363{
364 int ret;
365 uint32_t *buf = malloc(s->block_size);
366 unsigned int block = 0;
367 int64_t remain = s->len;
368 int64_t offset = 0;
369 unsigned int to_read;
370 char *ptr;
371 unsigned int i;
372 bool sparse_block;
373
374 if (!buf) {
375 return -ENOMEM;
376 }
377
378 while (remain > 0) {
379 to_read = min(remain, s->block_size);
380 ret = read_all(fd, buf, to_read);
381 if (ret < 0) {
382 error("failed to read sparse file");
383 return ret;
384 }
385
386 if (to_read == s->block_size) {
387 sparse_block = true;
388 for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {
389 if (buf[0] != buf[i]) {
390 sparse_block = false;
391 break;
392 }
393 }
394 } else {
395 sparse_block = false;
396 }
397
398 if (sparse_block) {
399 /* TODO: add flag to use skip instead of fill for buf[0] == 0 */
400 sparse_file_add_fill(s, buf[0], to_read, block);
401 } else {
402 sparse_file_add_fd(s, fd, offset, to_read, block);
403 }
404
405 remain -= to_read;
406 offset += to_read;
407 block++;
408 }
409
410 return 0;
411}
412
413int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc)
414{
415 if (crc && !sparse) {
416 return -EINVAL;
417 }
418
419 if (sparse) {
420 return sparse_file_read_sparse(s, fd, crc);
421 } else {
422 return sparse_file_read_normal(s, fd);
423 }
424}
425
426struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc)
427{
428 int ret;
429 sparse_header_t sparse_header;
430 int64_t len;
431 struct sparse_file *s;
432
433 ret = read_all(fd, &sparse_header, sizeof(sparse_header));
434 if (ret < 0) {
435 verbose_error(verbose, ret, "header");
436 return NULL;
437 }
438
439 if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
440 verbose_error(verbose, -EINVAL, "header magic");
441 return NULL;
442 }
443
444 if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
445 verbose_error(verbose, -EINVAL, "header major version");
446 return NULL;
447 }
448
449 if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
450 return NULL;
451 }
452
453 if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
454 return NULL;
455 }
456
457 len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
458 s = sparse_file_new(sparse_header.blk_sz, len);
459 if (!s) {
460 verbose_error(verbose, -EINVAL, NULL);
461 return NULL;
462 }
463
464 ret = lseek64(fd, 0, SEEK_SET);
465 if (ret < 0) {
466 verbose_error(verbose, ret, "seeking");
467 sparse_file_destroy(s);
468 return NULL;
469 }
470
471 s->verbose = verbose;
472
473 ret = sparse_file_read(s, fd, true, crc);
474 if (ret < 0) {
475 sparse_file_destroy(s);
476 return NULL;
477 }
478
479 return s;
480}
481
482struct sparse_file *sparse_file_import_auto(int fd, bool crc)
483{
484 struct sparse_file *s;
485 int64_t len;
486 int ret;
487
488 s = sparse_file_import(fd, true, crc);
489 if (s) {
490 return s;
491 }
492
493 len = lseek64(fd, 0, SEEK_END);
494 if (len < 0) {
495 return NULL;
496 }
497
498 lseek64(fd, 0, SEEK_SET);
499
500 s = sparse_file_new(4096, len);
501 if (!s) {
502 return NULL;
503 }
504
505 ret = sparse_file_read_normal(s, fd);
506 if (ret < 0) {
507 sparse_file_destroy(s);
508 return NULL;
509 }
510
511 return s;
512}