blob: 43796352700c8860894d0baab5e720ecaf6e5557 [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
Colin Cross0c4c47f2012-04-25 19:02:58 -070017#define _FILE_OFFSET_BITS 64
18#define _LARGEFILE64_SOURCE 1
19
Chris Friesa7eeb222017-04-17 21:53:16 -050020#include <algorithm>
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>
Colin Cross0c4c47f2012-04-25 19:02:58 -070024#include <stdint.h>
25#include <stdio.h>
26#include <stdlib.h>
Xiaolei Yuc39caaf2017-10-11 14:46:29 +080027#include <string.h>
Chris Friesa7eeb222017-04-17 21:53:16 -050028#include <string>
Colin Cross0c4c47f2012-04-25 19:02:58 -070029#include <unistd.h>
30
31#include <sparse/sparse.h>
32
Chris Friesa7eeb222017-04-17 21:53:16 -050033#include "android-base/stringprintf.h"
Mark Salyzyn031a7482014-02-27 16:56:15 -080034#include "defs.h"
35#include "output_file.h"
Colin Cross0c4c47f2012-04-25 19:02:58 -070036#include "sparse_crc32.h"
37#include "sparse_file.h"
38#include "sparse_format.h"
39
Chris Friesa7eeb222017-04-17 21:53:16 -050040
Colin Cross0c4c47f2012-04-25 19:02:58 -070041#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
Chris Friesa7eeb222017-04-17 21:53:16 -050050static constexpr int64_t COPY_BUF_SIZE = 1024 * 1024;
Colin Cross0c4c47f2012-04-25 19:02:58 -070051static char *copybuf;
52
Chris Friesa7eeb222017-04-17 21:53:16 -050053static 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}
Colin Cross0c4c47f2012-04-25 19:02:58 -070060
61static void verbose_error(bool verbose, int err, const char *fmt, ...)
62{
Chris Friesa7eeb222017-04-17 21:53:16 -050063 if (!verbose) return;
64
65 std::string msg = ErrorString(err);
Colin Cross0c4c47f2012-04-25 19:02:58 -070066 if (fmt) {
Chris Friesa7eeb222017-04-17 21:53:16 -050067 msg += " at ";
Colin Cross0c4c47f2012-04-25 19:02:58 -070068 va_list argp;
Colin Cross0c4c47f2012-04-25 19:02:58 -070069 va_start(argp, fmt);
Chris Friesa7eeb222017-04-17 21:53:16 -050070 android::base::StringAppendV(&msg, fmt, argp);
Colin Cross0c4c47f2012-04-25 19:02:58 -070071 va_end(argp);
Colin Cross0c4c47f2012-04-25 19:02:58 -070072 }
Chris Friesa7eeb222017-04-17 21:53:16 -050073 sparse_print_verbose("%s\n", msg.c_str());
Colin Cross0c4c47f2012-04-25 19:02:58 -070074}
75
76static 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;
Chris Friesa7eeb222017-04-17 21:53:16 -050082 int64_t len = blocks * s->block_size;
Colin Cross0c4c47f2012-04-25 19:02:58 -070083
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) {
Chris Friesa7eeb222017-04-17 21:53:16 -050099 chunk = std::min(len, COPY_BUF_SIZE);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700100 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
114static 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) {
Chris Friesa7eeb222017-04-17 21:53:16 -0500146 chunk = std::min(len, COPY_BUF_SIZE);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700147 *crc32 = sparse_crc32(*crc32, copybuf, chunk);
148 len -= chunk;
149 }
150 }
151
152 return 0;
153}
154
155static int process_skip_chunk(struct sparse_file *s, unsigned int chunk_size,
Mark Salyzyn031a7482014-02-27 16:56:15 -0800156 int fd __unused, unsigned int blocks,
157 unsigned int block __unused, uint32_t *crc32)
Colin Cross0c4c47f2012-04-25 19:02:58 -0700158{
Colin Cross0c4c47f2012-04-25 19:02:58 -0700159 if (chunk_size != 0) {
160 return -EINVAL;
161 }
162
163 if (crc32) {
Mark Salyzyn8116c8c2014-05-01 09:15:02 -0700164 int64_t len = (int64_t)blocks * s->block_size;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700165 memset(copybuf, 0, COPY_BUF_SIZE);
166
167 while (len) {
Chris Friesa7eeb222017-04-17 21:53:16 -0500168 int chunk = std::min(len, COPY_BUF_SIZE);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700169 *crc32 = sparse_crc32(*crc32, copybuf, chunk);
170 len -= chunk;
171 }
172 }
173
174 return 0;
175}
176
Colin Cross1eb743b2016-02-01 11:15:30 -0800177static int process_crc32_chunk(int fd, unsigned int chunk_size, uint32_t *crc32)
Colin Cross0c4c47f2012-04-25 19:02:58 -0700178{
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
Colin Cross1eb743b2016-02-01 11:15:30 -0800191 if (crc32 != NULL && file_crc32 != *crc32) {
Colin Cross0c4c47f2012-04-25 19:02:58 -0700192 return -EINVAL;
193 }
194
195 return 0;
196}
197
198static 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) {
Daniel Micayaf090a62015-10-13 16:18:45 -0400212 verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700213 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) {
Daniel Micayaf090a62015-10-13 16:18:45 -0400220 verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700221 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) {
Daniel Micayaf090a62015-10-13 16:18:45 -0400229 verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700230 return ret;
231 }
232 }
233 return chunk_header->chunk_sz;
234 case CHUNK_TYPE_CRC32:
Colin Cross1eb743b2016-02-01 11:15:30 -0800235 ret = process_crc32_chunk(fd, chunk_data_size, crc_ptr);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700236 if (ret < 0) {
Daniel Micayaf090a62015-10-13 16:18:45 -0400237 verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64,
Colin Cross0c4c47f2012-04-25 19:02:58 -0700238 offset);
239 return ret;
240 }
241 return 0;
242 default:
Daniel Micayaf090a62015-10-13 16:18:45 -0400243 verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64,
Colin Cross0c4c47f2012-04-25 19:02:58 -0700244 chunk_header->chunk_type, offset);
245 }
246
247 return 0;
248}
249
250static 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) {
Chris Friesa7eeb222017-04-17 21:53:16 -0500262 copybuf = (char *)malloc(COPY_BUF_SIZE);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700263 }
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
332static int sparse_file_read_normal(struct sparse_file *s, int fd)
333{
334 int ret;
Chris Friesa7eeb222017-04-17 21:53:16 -0500335 uint32_t *buf = (uint32_t *)malloc(s->block_size);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700336 unsigned int block = 0;
337 int64_t remain = s->len;
338 int64_t offset = 0;
339 unsigned int to_read;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700340 unsigned int i;
341 bool sparse_block;
342
343 if (!buf) {
344 return -ENOMEM;
345 }
346
347 while (remain > 0) {
Chris Friesa7eeb222017-04-17 21:53:16 -0500348 to_read = std::min(remain, (int64_t)(s->block_size));
Colin Cross0c4c47f2012-04-25 19:02:58 -0700349 ret = read_all(fd, buf, to_read);
350 if (ret < 0) {
351 error("failed to read sparse file");
Colin Cross1eb743b2016-02-01 11:15:30 -0800352 free(buf);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700353 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
Colin Cross1eb743b2016-02-01 11:15:30 -0800380 free(buf);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700381 return 0;
382}
383
384int 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
397struct 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
Mohamad Ayyash80cc1f62015-03-31 12:09:29 -0700453struct sparse_file *sparse_file_import_auto(int fd, bool crc, bool verbose)
Colin Cross0c4c47f2012-04-25 19:02:58 -0700454{
455 struct sparse_file *s;
456 int64_t len;
457 int ret;
458
Mohamad Ayyash80cc1f62015-03-31 12:09:29 -0700459 s = sparse_file_import(fd, verbose, crc);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700460 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}