blob: 91d551c14615046aba23ac584fab2a56dd638dc9 [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>
24#include <stdbool.h>
25#include <stdint.h>
26#include <stdio.h>
27#include <stdlib.h>
Xiaolei Yuc39caaf2017-10-11 14:46:29 +080028#include <string.h>
Chris Friesa7eeb222017-04-17 21:53:16 -050029#include <string>
Colin Cross0c4c47f2012-04-25 19:02:58 -070030#include <unistd.h>
31
32#include <sparse/sparse.h>
33
Chris Friesa7eeb222017-04-17 21:53:16 -050034#include "android-base/stringprintf.h"
Mark Salyzyn031a7482014-02-27 16:56:15 -080035#include "defs.h"
36#include "output_file.h"
Colin Cross0c4c47f2012-04-25 19:02:58 -070037#include "sparse_crc32.h"
38#include "sparse_file.h"
39#include "sparse_format.h"
40
Chris Friesa7eeb222017-04-17 21:53:16 -050041
Colin Cross0c4c47f2012-04-25 19:02:58 -070042#if defined(__APPLE__) && defined(__MACH__)
43#define lseek64 lseek
44#define off64_t off_t
45#endif
46
47#define SPARSE_HEADER_MAJOR_VER 1
48#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
49#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
50
Chris Friesa7eeb222017-04-17 21:53:16 -050051static constexpr int64_t COPY_BUF_SIZE = 1024 * 1024;
Colin Cross0c4c47f2012-04-25 19:02:58 -070052static char *copybuf;
53
Chris Friesa7eeb222017-04-17 21:53:16 -050054static std::string ErrorString(int err)
55{
56 if (err == -EOVERFLOW) return "EOF while reading file";
57 if (err == -EINVAL) return "Invalid sparse file format";
58 if (err == -ENOMEM) return "Failed allocation while reading file";
59 return android::base::StringPrintf("Unknown error %d", err);
60}
Colin Cross0c4c47f2012-04-25 19:02:58 -070061
62static void verbose_error(bool verbose, int err, const char *fmt, ...)
63{
Chris Friesa7eeb222017-04-17 21:53:16 -050064 if (!verbose) return;
65
66 std::string msg = ErrorString(err);
Colin Cross0c4c47f2012-04-25 19:02:58 -070067 if (fmt) {
Chris Friesa7eeb222017-04-17 21:53:16 -050068 msg += " at ";
Colin Cross0c4c47f2012-04-25 19:02:58 -070069 va_list argp;
Colin Cross0c4c47f2012-04-25 19:02:58 -070070 va_start(argp, fmt);
Chris Friesa7eeb222017-04-17 21:53:16 -050071 android::base::StringAppendV(&msg, fmt, argp);
Colin Cross0c4c47f2012-04-25 19:02:58 -070072 va_end(argp);
Colin Cross0c4c47f2012-04-25 19:02:58 -070073 }
Chris Friesa7eeb222017-04-17 21:53:16 -050074 sparse_print_verbose("%s\n", msg.c_str());
Colin Cross0c4c47f2012-04-25 19:02:58 -070075}
76
77static int process_raw_chunk(struct sparse_file *s, unsigned int chunk_size,
78 int fd, int64_t offset, unsigned int blocks, unsigned int block,
79 uint32_t *crc32)
80{
81 int ret;
82 int chunk;
Chris Friesa7eeb222017-04-17 21:53:16 -050083 int64_t len = blocks * s->block_size;
Colin Cross0c4c47f2012-04-25 19:02:58 -070084
85 if (chunk_size % s->block_size != 0) {
86 return -EINVAL;
87 }
88
89 if (chunk_size / s->block_size != blocks) {
90 return -EINVAL;
91 }
92
93 ret = sparse_file_add_fd(s, fd, offset, len, block);
94 if (ret < 0) {
95 return ret;
96 }
97
98 if (crc32) {
99 while (len) {
Chris Friesa7eeb222017-04-17 21:53:16 -0500100 chunk = std::min(len, COPY_BUF_SIZE);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700101 ret = read_all(fd, copybuf, chunk);
102 if (ret < 0) {
103 return ret;
104 }
105 *crc32 = sparse_crc32(*crc32, copybuf, chunk);
106 len -= chunk;
107 }
108 } else {
109 lseek64(fd, len, SEEK_CUR);
110 }
111
112 return 0;
113}
114
115static int process_fill_chunk(struct sparse_file *s, unsigned int chunk_size,
116 int fd, unsigned int blocks, unsigned int block, uint32_t *crc32)
117{
118 int ret;
119 int chunk;
120 int64_t len = (int64_t)blocks * s->block_size;
121 uint32_t fill_val;
122 uint32_t *fillbuf;
123 unsigned int i;
124
125 if (chunk_size != sizeof(fill_val)) {
126 return -EINVAL;
127 }
128
129 ret = read_all(fd, &fill_val, sizeof(fill_val));
130 if (ret < 0) {
131 return ret;
132 }
133
134 ret = sparse_file_add_fill(s, fill_val, len, block);
135 if (ret < 0) {
136 return ret;
137 }
138
139 if (crc32) {
140 /* Fill copy_buf with the fill value */
141 fillbuf = (uint32_t *)copybuf;
142 for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) {
143 fillbuf[i] = fill_val;
144 }
145
146 while (len) {
Chris Friesa7eeb222017-04-17 21:53:16 -0500147 chunk = std::min(len, COPY_BUF_SIZE);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700148 *crc32 = sparse_crc32(*crc32, copybuf, chunk);
149 len -= chunk;
150 }
151 }
152
153 return 0;
154}
155
156static int process_skip_chunk(struct sparse_file *s, unsigned int chunk_size,
Mark Salyzyn031a7482014-02-27 16:56:15 -0800157 int fd __unused, unsigned int blocks,
158 unsigned int block __unused, uint32_t *crc32)
Colin Cross0c4c47f2012-04-25 19:02:58 -0700159{
Colin Cross0c4c47f2012-04-25 19:02:58 -0700160 if (chunk_size != 0) {
161 return -EINVAL;
162 }
163
164 if (crc32) {
Mark Salyzyn8116c8c2014-05-01 09:15:02 -0700165 int64_t len = (int64_t)blocks * s->block_size;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700166 memset(copybuf, 0, COPY_BUF_SIZE);
167
168 while (len) {
Chris Friesa7eeb222017-04-17 21:53:16 -0500169 int chunk = std::min(len, COPY_BUF_SIZE);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700170 *crc32 = sparse_crc32(*crc32, copybuf, chunk);
171 len -= chunk;
172 }
173 }
174
175 return 0;
176}
177
Colin Cross1eb743b2016-02-01 11:15:30 -0800178static int process_crc32_chunk(int fd, unsigned int chunk_size, uint32_t *crc32)
Colin Cross0c4c47f2012-04-25 19:02:58 -0700179{
180 uint32_t file_crc32;
181 int ret;
182
183 if (chunk_size != sizeof(file_crc32)) {
184 return -EINVAL;
185 }
186
187 ret = read_all(fd, &file_crc32, sizeof(file_crc32));
188 if (ret < 0) {
189 return ret;
190 }
191
Colin Cross1eb743b2016-02-01 11:15:30 -0800192 if (crc32 != NULL && file_crc32 != *crc32) {
Colin Cross0c4c47f2012-04-25 19:02:58 -0700193 return -EINVAL;
194 }
195
196 return 0;
197}
198
199static int process_chunk(struct sparse_file *s, int fd, off64_t offset,
200 unsigned int chunk_hdr_sz, chunk_header_t *chunk_header,
201 unsigned int cur_block, uint32_t *crc_ptr)
202{
203 int ret;
204 unsigned int chunk_data_size;
205
206 chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;
207
208 switch (chunk_header->chunk_type) {
209 case CHUNK_TYPE_RAW:
210 ret = process_raw_chunk(s, chunk_data_size, fd, offset,
211 chunk_header->chunk_sz, cur_block, crc_ptr);
212 if (ret < 0) {
Daniel Micayaf090a62015-10-13 16:18:45 -0400213 verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700214 return ret;
215 }
216 return chunk_header->chunk_sz;
217 case CHUNK_TYPE_FILL:
218 ret = process_fill_chunk(s, chunk_data_size, fd,
219 chunk_header->chunk_sz, cur_block, crc_ptr);
220 if (ret < 0) {
Daniel Micayaf090a62015-10-13 16:18:45 -0400221 verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700222 return ret;
223 }
224 return chunk_header->chunk_sz;
225 case CHUNK_TYPE_DONT_CARE:
226 ret = process_skip_chunk(s, chunk_data_size, fd,
227 chunk_header->chunk_sz, cur_block, crc_ptr);
228 if (chunk_data_size != 0) {
229 if (ret < 0) {
Daniel Micayaf090a62015-10-13 16:18:45 -0400230 verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700231 return ret;
232 }
233 }
234 return chunk_header->chunk_sz;
235 case CHUNK_TYPE_CRC32:
Colin Cross1eb743b2016-02-01 11:15:30 -0800236 ret = process_crc32_chunk(fd, chunk_data_size, crc_ptr);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700237 if (ret < 0) {
Daniel Micayaf090a62015-10-13 16:18:45 -0400238 verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64,
Colin Cross0c4c47f2012-04-25 19:02:58 -0700239 offset);
240 return ret;
241 }
242 return 0;
243 default:
Daniel Micayaf090a62015-10-13 16:18:45 -0400244 verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64,
Colin Cross0c4c47f2012-04-25 19:02:58 -0700245 chunk_header->chunk_type, offset);
246 }
247
248 return 0;
249}
250
251static int sparse_file_read_sparse(struct sparse_file *s, int fd, bool crc)
252{
253 int ret;
254 unsigned int i;
255 sparse_header_t sparse_header;
256 chunk_header_t chunk_header;
257 uint32_t crc32 = 0;
258 uint32_t *crc_ptr = 0;
259 unsigned int cur_block = 0;
260 off64_t offset;
261
262 if (!copybuf) {
Chris Friesa7eeb222017-04-17 21:53:16 -0500263 copybuf = (char *)malloc(COPY_BUF_SIZE);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700264 }
265
266 if (!copybuf) {
267 return -ENOMEM;
268 }
269
270 if (crc) {
271 crc_ptr = &crc32;
272 }
273
274 ret = read_all(fd, &sparse_header, sizeof(sparse_header));
275 if (ret < 0) {
276 return ret;
277 }
278
279 if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
280 return -EINVAL;
281 }
282
283 if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
284 return -EINVAL;
285 }
286
287 if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
288 return -EINVAL;
289 }
290
291 if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {
292 return -EINVAL;
293 }
294
295 if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
296 /* Skip the remaining bytes in a header that is longer than
297 * we expected.
298 */
299 lseek64(fd, sparse_header.file_hdr_sz - SPARSE_HEADER_LEN, SEEK_CUR);
300 }
301
302 for (i = 0; i < sparse_header.total_chunks; i++) {
303 ret = read_all(fd, &chunk_header, sizeof(chunk_header));
304 if (ret < 0) {
305 return ret;
306 }
307
308 if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
309 /* Skip the remaining bytes in a header that is longer than
310 * we expected.
311 */
312 lseek64(fd, sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN, SEEK_CUR);
313 }
314
315 offset = lseek64(fd, 0, SEEK_CUR);
316
317 ret = process_chunk(s, fd, offset, sparse_header.chunk_hdr_sz, &chunk_header,
318 cur_block, crc_ptr);
319 if (ret < 0) {
320 return ret;
321 }
322
323 cur_block += ret;
324 }
325
326 if (sparse_header.total_blks != cur_block) {
327 return -EINVAL;
328 }
329
330 return 0;
331}
332
333static int sparse_file_read_normal(struct sparse_file *s, int fd)
334{
335 int ret;
Chris Friesa7eeb222017-04-17 21:53:16 -0500336 uint32_t *buf = (uint32_t *)malloc(s->block_size);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700337 unsigned int block = 0;
338 int64_t remain = s->len;
339 int64_t offset = 0;
340 unsigned int to_read;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700341 unsigned int i;
342 bool sparse_block;
343
344 if (!buf) {
345 return -ENOMEM;
346 }
347
348 while (remain > 0) {
Chris Friesa7eeb222017-04-17 21:53:16 -0500349 to_read = std::min(remain, (int64_t)(s->block_size));
Colin Cross0c4c47f2012-04-25 19:02:58 -0700350 ret = read_all(fd, buf, to_read);
351 if (ret < 0) {
352 error("failed to read sparse file");
Colin Cross1eb743b2016-02-01 11:15:30 -0800353 free(buf);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700354 return ret;
355 }
356
357 if (to_read == s->block_size) {
358 sparse_block = true;
359 for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {
360 if (buf[0] != buf[i]) {
361 sparse_block = false;
362 break;
363 }
364 }
365 } else {
366 sparse_block = false;
367 }
368
369 if (sparse_block) {
370 /* TODO: add flag to use skip instead of fill for buf[0] == 0 */
371 sparse_file_add_fill(s, buf[0], to_read, block);
372 } else {
373 sparse_file_add_fd(s, fd, offset, to_read, block);
374 }
375
376 remain -= to_read;
377 offset += to_read;
378 block++;
379 }
380
Colin Cross1eb743b2016-02-01 11:15:30 -0800381 free(buf);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700382 return 0;
383}
384
385int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc)
386{
387 if (crc && !sparse) {
388 return -EINVAL;
389 }
390
391 if (sparse) {
392 return sparse_file_read_sparse(s, fd, crc);
393 } else {
394 return sparse_file_read_normal(s, fd);
395 }
396}
397
398struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc)
399{
400 int ret;
401 sparse_header_t sparse_header;
402 int64_t len;
403 struct sparse_file *s;
404
405 ret = read_all(fd, &sparse_header, sizeof(sparse_header));
406 if (ret < 0) {
407 verbose_error(verbose, ret, "header");
408 return NULL;
409 }
410
411 if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
412 verbose_error(verbose, -EINVAL, "header magic");
413 return NULL;
414 }
415
416 if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
417 verbose_error(verbose, -EINVAL, "header major version");
418 return NULL;
419 }
420
421 if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
422 return NULL;
423 }
424
425 if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
426 return NULL;
427 }
428
429 len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
430 s = sparse_file_new(sparse_header.blk_sz, len);
431 if (!s) {
432 verbose_error(verbose, -EINVAL, NULL);
433 return NULL;
434 }
435
436 ret = lseek64(fd, 0, SEEK_SET);
437 if (ret < 0) {
438 verbose_error(verbose, ret, "seeking");
439 sparse_file_destroy(s);
440 return NULL;
441 }
442
443 s->verbose = verbose;
444
445 ret = sparse_file_read(s, fd, true, crc);
446 if (ret < 0) {
447 sparse_file_destroy(s);
448 return NULL;
449 }
450
451 return s;
452}
453
Mohamad Ayyash80cc1f62015-03-31 12:09:29 -0700454struct sparse_file *sparse_file_import_auto(int fd, bool crc, bool verbose)
Colin Cross0c4c47f2012-04-25 19:02:58 -0700455{
456 struct sparse_file *s;
457 int64_t len;
458 int ret;
459
Mohamad Ayyash80cc1f62015-03-31 12:09:29 -0700460 s = sparse_file_import(fd, verbose, crc);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700461 if (s) {
462 return s;
463 }
464
465 len = lseek64(fd, 0, SEEK_END);
466 if (len < 0) {
467 return NULL;
468 }
469
470 lseek64(fd, 0, SEEK_SET);
471
472 s = sparse_file_new(4096, len);
473 if (!s) {
474 return NULL;
475 }
476
477 ret = sparse_file_read_normal(s, fd);
478 if (ret < 0) {
479 sparse_file_destroy(s);
480 return NULL;
481 }
482
483 return s;
484}