blob: bfb70c7959eee6dda1ab55f50a7deb59a9ddffda [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>
Chris Friesa7eeb222017-04-17 21:53:16 -050027#include <string>
Colin Cross0c4c47f2012-04-25 19:02:58 -070028#include <unistd.h>
29
30#include <sparse/sparse.h>
31
Chris Friesa7eeb222017-04-17 21:53:16 -050032#include "android-base/stringprintf.h"
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
Chris Friesa7eeb222017-04-17 21:53:16 -050039
Colin Cross0c4c47f2012-04-25 19:02:58 -070040#if defined(__APPLE__) && defined(__MACH__)
41#define lseek64 lseek
42#define off64_t off_t
43#endif
44
45#define SPARSE_HEADER_MAJOR_VER 1
46#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
47#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
48
Chris Friesa7eeb222017-04-17 21:53:16 -050049static constexpr int64_t COPY_BUF_SIZE = 1024 * 1024;
Colin Cross0c4c47f2012-04-25 19:02:58 -070050static char *copybuf;
51
Chris Friesa7eeb222017-04-17 21:53:16 -050052static std::string ErrorString(int err)
53{
54 if (err == -EOVERFLOW) return "EOF while reading file";
55 if (err == -EINVAL) return "Invalid sparse file format";
56 if (err == -ENOMEM) return "Failed allocation while reading file";
57 return android::base::StringPrintf("Unknown error %d", err);
58}
Colin Cross0c4c47f2012-04-25 19:02:58 -070059
60static void verbose_error(bool verbose, int err, const char *fmt, ...)
61{
Chris Friesa7eeb222017-04-17 21:53:16 -050062 if (!verbose) return;
63
64 std::string msg = ErrorString(err);
Colin Cross0c4c47f2012-04-25 19:02:58 -070065 if (fmt) {
Chris Friesa7eeb222017-04-17 21:53:16 -050066 msg += " at ";
Colin Cross0c4c47f2012-04-25 19:02:58 -070067 va_list argp;
Colin Cross0c4c47f2012-04-25 19:02:58 -070068 va_start(argp, fmt);
Chris Friesa7eeb222017-04-17 21:53:16 -050069 android::base::StringAppendV(&msg, fmt, argp);
Colin Cross0c4c47f2012-04-25 19:02:58 -070070 va_end(argp);
Colin Cross0c4c47f2012-04-25 19:02:58 -070071 }
Chris Friesa7eeb222017-04-17 21:53:16 -050072 sparse_print_verbose("%s\n", msg.c_str());
Colin Cross0c4c47f2012-04-25 19:02:58 -070073}
74
75static int process_raw_chunk(struct sparse_file *s, unsigned int chunk_size,
76 int fd, int64_t offset, unsigned int blocks, unsigned int block,
77 uint32_t *crc32)
78{
79 int ret;
80 int chunk;
Chris Friesa7eeb222017-04-17 21:53:16 -050081 int64_t len = blocks * s->block_size;
Colin Cross0c4c47f2012-04-25 19:02:58 -070082
83 if (chunk_size % s->block_size != 0) {
84 return -EINVAL;
85 }
86
87 if (chunk_size / s->block_size != blocks) {
88 return -EINVAL;
89 }
90
91 ret = sparse_file_add_fd(s, fd, offset, len, block);
92 if (ret < 0) {
93 return ret;
94 }
95
96 if (crc32) {
97 while (len) {
Chris Friesa7eeb222017-04-17 21:53:16 -050098 chunk = std::min(len, COPY_BUF_SIZE);
Colin Cross0c4c47f2012-04-25 19:02:58 -070099 ret = read_all(fd, copybuf, chunk);
100 if (ret < 0) {
101 return ret;
102 }
103 *crc32 = sparse_crc32(*crc32, copybuf, chunk);
104 len -= chunk;
105 }
106 } else {
107 lseek64(fd, len, SEEK_CUR);
108 }
109
110 return 0;
111}
112
113static int process_fill_chunk(struct sparse_file *s, unsigned int chunk_size,
114 int fd, unsigned int blocks, unsigned int block, uint32_t *crc32)
115{
116 int ret;
117 int chunk;
118 int64_t len = (int64_t)blocks * s->block_size;
119 uint32_t fill_val;
120 uint32_t *fillbuf;
121 unsigned int i;
122
123 if (chunk_size != sizeof(fill_val)) {
124 return -EINVAL;
125 }
126
127 ret = read_all(fd, &fill_val, sizeof(fill_val));
128 if (ret < 0) {
129 return ret;
130 }
131
132 ret = sparse_file_add_fill(s, fill_val, len, block);
133 if (ret < 0) {
134 return ret;
135 }
136
137 if (crc32) {
138 /* Fill copy_buf with the fill value */
139 fillbuf = (uint32_t *)copybuf;
140 for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) {
141 fillbuf[i] = fill_val;
142 }
143
144 while (len) {
Chris Friesa7eeb222017-04-17 21:53:16 -0500145 chunk = std::min(len, COPY_BUF_SIZE);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700146 *crc32 = sparse_crc32(*crc32, copybuf, chunk);
147 len -= chunk;
148 }
149 }
150
151 return 0;
152}
153
154static int process_skip_chunk(struct sparse_file *s, unsigned int chunk_size,
Mark Salyzyn031a7482014-02-27 16:56:15 -0800155 int fd __unused, unsigned int blocks,
156 unsigned int block __unused, uint32_t *crc32)
Colin Cross0c4c47f2012-04-25 19:02:58 -0700157{
Colin Cross0c4c47f2012-04-25 19:02:58 -0700158 if (chunk_size != 0) {
159 return -EINVAL;
160 }
161
162 if (crc32) {
Mark Salyzyn8116c8c2014-05-01 09:15:02 -0700163 int64_t len = (int64_t)blocks * s->block_size;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700164 memset(copybuf, 0, COPY_BUF_SIZE);
165
166 while (len) {
Chris Friesa7eeb222017-04-17 21:53:16 -0500167 int chunk = std::min(len, COPY_BUF_SIZE);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700168 *crc32 = sparse_crc32(*crc32, copybuf, chunk);
169 len -= chunk;
170 }
171 }
172
173 return 0;
174}
175
Colin Cross1eb743b2016-02-01 11:15:30 -0800176static int process_crc32_chunk(int fd, unsigned int chunk_size, uint32_t *crc32)
Colin Cross0c4c47f2012-04-25 19:02:58 -0700177{
178 uint32_t file_crc32;
179 int ret;
180
181 if (chunk_size != sizeof(file_crc32)) {
182 return -EINVAL;
183 }
184
185 ret = read_all(fd, &file_crc32, sizeof(file_crc32));
186 if (ret < 0) {
187 return ret;
188 }
189
Colin Cross1eb743b2016-02-01 11:15:30 -0800190 if (crc32 != NULL && file_crc32 != *crc32) {
Colin Cross0c4c47f2012-04-25 19:02:58 -0700191 return -EINVAL;
192 }
193
194 return 0;
195}
196
197static int process_chunk(struct sparse_file *s, int fd, off64_t offset,
198 unsigned int chunk_hdr_sz, chunk_header_t *chunk_header,
199 unsigned int cur_block, uint32_t *crc_ptr)
200{
201 int ret;
202 unsigned int chunk_data_size;
203
204 chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;
205
206 switch (chunk_header->chunk_type) {
207 case CHUNK_TYPE_RAW:
208 ret = process_raw_chunk(s, chunk_data_size, fd, offset,
209 chunk_header->chunk_sz, cur_block, crc_ptr);
210 if (ret < 0) {
Daniel Micayaf090a62015-10-13 16:18:45 -0400211 verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700212 return ret;
213 }
214 return chunk_header->chunk_sz;
215 case CHUNK_TYPE_FILL:
216 ret = process_fill_chunk(s, chunk_data_size, fd,
217 chunk_header->chunk_sz, cur_block, crc_ptr);
218 if (ret < 0) {
Daniel Micayaf090a62015-10-13 16:18:45 -0400219 verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700220 return ret;
221 }
222 return chunk_header->chunk_sz;
223 case CHUNK_TYPE_DONT_CARE:
224 ret = process_skip_chunk(s, chunk_data_size, fd,
225 chunk_header->chunk_sz, cur_block, crc_ptr);
226 if (chunk_data_size != 0) {
227 if (ret < 0) {
Daniel Micayaf090a62015-10-13 16:18:45 -0400228 verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700229 return ret;
230 }
231 }
232 return chunk_header->chunk_sz;
233 case CHUNK_TYPE_CRC32:
Colin Cross1eb743b2016-02-01 11:15:30 -0800234 ret = process_crc32_chunk(fd, chunk_data_size, crc_ptr);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700235 if (ret < 0) {
Daniel Micayaf090a62015-10-13 16:18:45 -0400236 verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64,
Colin Cross0c4c47f2012-04-25 19:02:58 -0700237 offset);
238 return ret;
239 }
240 return 0;
241 default:
Daniel Micayaf090a62015-10-13 16:18:45 -0400242 verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64,
Colin Cross0c4c47f2012-04-25 19:02:58 -0700243 chunk_header->chunk_type, offset);
244 }
245
246 return 0;
247}
248
249static int sparse_file_read_sparse(struct sparse_file *s, int fd, bool crc)
250{
251 int ret;
252 unsigned int i;
253 sparse_header_t sparse_header;
254 chunk_header_t chunk_header;
255 uint32_t crc32 = 0;
256 uint32_t *crc_ptr = 0;
257 unsigned int cur_block = 0;
258 off64_t offset;
259
260 if (!copybuf) {
Chris Friesa7eeb222017-04-17 21:53:16 -0500261 copybuf = (char *)malloc(COPY_BUF_SIZE);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700262 }
263
264 if (!copybuf) {
265 return -ENOMEM;
266 }
267
268 if (crc) {
269 crc_ptr = &crc32;
270 }
271
272 ret = read_all(fd, &sparse_header, sizeof(sparse_header));
273 if (ret < 0) {
274 return ret;
275 }
276
277 if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
278 return -EINVAL;
279 }
280
281 if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
282 return -EINVAL;
283 }
284
285 if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
286 return -EINVAL;
287 }
288
289 if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {
290 return -EINVAL;
291 }
292
293 if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
294 /* Skip the remaining bytes in a header that is longer than
295 * we expected.
296 */
297 lseek64(fd, sparse_header.file_hdr_sz - SPARSE_HEADER_LEN, SEEK_CUR);
298 }
299
300 for (i = 0; i < sparse_header.total_chunks; i++) {
301 ret = read_all(fd, &chunk_header, sizeof(chunk_header));
302 if (ret < 0) {
303 return ret;
304 }
305
306 if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
307 /* Skip the remaining bytes in a header that is longer than
308 * we expected.
309 */
310 lseek64(fd, sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN, SEEK_CUR);
311 }
312
313 offset = lseek64(fd, 0, SEEK_CUR);
314
315 ret = process_chunk(s, fd, offset, sparse_header.chunk_hdr_sz, &chunk_header,
316 cur_block, crc_ptr);
317 if (ret < 0) {
318 return ret;
319 }
320
321 cur_block += ret;
322 }
323
324 if (sparse_header.total_blks != cur_block) {
325 return -EINVAL;
326 }
327
328 return 0;
329}
330
331static int sparse_file_read_normal(struct sparse_file *s, int fd)
332{
333 int ret;
Chris Friesa7eeb222017-04-17 21:53:16 -0500334 uint32_t *buf = (uint32_t *)malloc(s->block_size);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700335 unsigned int block = 0;
336 int64_t remain = s->len;
337 int64_t offset = 0;
338 unsigned int to_read;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700339 unsigned int i;
340 bool sparse_block;
341
342 if (!buf) {
343 return -ENOMEM;
344 }
345
346 while (remain > 0) {
Chris Friesa7eeb222017-04-17 21:53:16 -0500347 to_read = std::min(remain, (int64_t)(s->block_size));
Colin Cross0c4c47f2012-04-25 19:02:58 -0700348 ret = read_all(fd, buf, to_read);
349 if (ret < 0) {
350 error("failed to read sparse file");
Colin Cross1eb743b2016-02-01 11:15:30 -0800351 free(buf);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700352 return ret;
353 }
354
355 if (to_read == s->block_size) {
356 sparse_block = true;
357 for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {
358 if (buf[0] != buf[i]) {
359 sparse_block = false;
360 break;
361 }
362 }
363 } else {
364 sparse_block = false;
365 }
366
367 if (sparse_block) {
368 /* TODO: add flag to use skip instead of fill for buf[0] == 0 */
369 sparse_file_add_fill(s, buf[0], to_read, block);
370 } else {
371 sparse_file_add_fd(s, fd, offset, to_read, block);
372 }
373
374 remain -= to_read;
375 offset += to_read;
376 block++;
377 }
378
Colin Cross1eb743b2016-02-01 11:15:30 -0800379 free(buf);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700380 return 0;
381}
382
383int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc)
384{
385 if (crc && !sparse) {
386 return -EINVAL;
387 }
388
389 if (sparse) {
390 return sparse_file_read_sparse(s, fd, crc);
391 } else {
392 return sparse_file_read_normal(s, fd);
393 }
394}
395
396struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc)
397{
398 int ret;
399 sparse_header_t sparse_header;
400 int64_t len;
401 struct sparse_file *s;
402
403 ret = read_all(fd, &sparse_header, sizeof(sparse_header));
404 if (ret < 0) {
405 verbose_error(verbose, ret, "header");
406 return NULL;
407 }
408
409 if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
410 verbose_error(verbose, -EINVAL, "header magic");
411 return NULL;
412 }
413
414 if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
415 verbose_error(verbose, -EINVAL, "header major version");
416 return NULL;
417 }
418
419 if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
420 return NULL;
421 }
422
423 if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
424 return NULL;
425 }
426
427 len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
428 s = sparse_file_new(sparse_header.blk_sz, len);
429 if (!s) {
430 verbose_error(verbose, -EINVAL, NULL);
431 return NULL;
432 }
433
434 ret = lseek64(fd, 0, SEEK_SET);
435 if (ret < 0) {
436 verbose_error(verbose, ret, "seeking");
437 sparse_file_destroy(s);
438 return NULL;
439 }
440
441 s->verbose = verbose;
442
443 ret = sparse_file_read(s, fd, true, crc);
444 if (ret < 0) {
445 sparse_file_destroy(s);
446 return NULL;
447 }
448
449 return s;
450}
451
Mohamad Ayyash80cc1f62015-03-31 12:09:29 -0700452struct sparse_file *sparse_file_import_auto(int fd, bool crc, bool verbose)
Colin Cross0c4c47f2012-04-25 19:02:58 -0700453{
454 struct sparse_file *s;
455 int64_t len;
456 int ret;
457
Mohamad Ayyash80cc1f62015-03-31 12:09:29 -0700458 s = sparse_file_import(fd, verbose, crc);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700459 if (s) {
460 return s;
461 }
462
463 len = lseek64(fd, 0, SEEK_END);
464 if (len < 0) {
465 return NULL;
466 }
467
468 lseek64(fd, 0, SEEK_SET);
469
470 s = sparse_file_new(4096, len);
471 if (!s) {
472 return NULL;
473 }
474
475 ret = sparse_file_read_normal(s, fd);
476 if (ret < 0) {
477 sparse_file_destroy(s);
478 return NULL;
479 }
480
481 return s;
482}