1 /*
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 _FILE_OFFSET_BITS 64
18 #define _LARGEFILE64_SOURCE 1
19
20 #include <fcntl.h>
21 #include <inttypes.h>
22 #include <stdarg.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <algorithm>
29 #include <string>
30
31 #include <sparse/sparse.h>
32
33 #include "android-base/stringprintf.h"
34 #include "defs.h"
35 #include "output_file.h"
36 #include "sparse_crc32.h"
37 #include "sparse_file.h"
38 #include "sparse_format.h"
39
40 #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
49 static constexpr int64_t COPY_BUF_SIZE = 1024 * 1024;
50 static char* copybuf;
51
ErrorString(int err)52 static std::string ErrorString(int err) {
53 if (err == -EOVERFLOW) return "EOF while reading file";
54 if (err == -EINVAL) return "Invalid sparse file format";
55 if (err == -ENOMEM) return "Failed allocation while reading file";
56 return android::base::StringPrintf("Unknown error %d", err);
57 }
58
59 class SparseFileSource {
60 public:
61 /* Seeks the source ahead by the given offset. */
62 virtual void Seek(int64_t offset) = 0;
63
64 /* Return the current offset. */
65 virtual int64_t GetOffset() = 0;
66
67 /* Set the current offset. Return 0 if successful. */
68 virtual int SetOffset(int64_t offset) = 0;
69
70 /* Adds the given length from the current offset of the source to the file at the given block.
71 * Return 0 if successful. */
72 virtual int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) = 0;
73
74 /* Get data of fixed size from the current offset and seek len bytes. Return 0 if successful. */
75 virtual int ReadValue(void* ptr, int len) = 0;
76
77 /* Find the crc32 of the next len bytes and seek ahead len bytes. Return 0 if successful. */
78 virtual int GetCrc32(uint32_t* crc32, int64_t len) = 0;
79
~SparseFileSource()80 virtual ~SparseFileSource(){};
81 };
82
83 class SparseFileFdSource : public SparseFileSource {
84 private:
85 int fd;
86
87 public:
SparseFileFdSource(int fd)88 SparseFileFdSource(int fd) : fd(fd) {}
~SparseFileFdSource()89 ~SparseFileFdSource() override {}
90
Seek(int64_t off)91 void Seek(int64_t off) override { lseek64(fd, off, SEEK_CUR); }
92
GetOffset()93 int64_t GetOffset() override { return lseek64(fd, 0, SEEK_CUR); }
94
SetOffset(int64_t offset)95 int SetOffset(int64_t offset) override {
96 return lseek64(fd, offset, SEEK_SET) == offset ? 0 : -errno;
97 }
98
AddToSparseFile(struct sparse_file * s,int64_t len,unsigned int block)99 int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
100 return sparse_file_add_fd(s, fd, GetOffset(), len, block);
101 }
102
ReadValue(void * ptr,int len)103 int ReadValue(void* ptr, int len) override { return read_all(fd, ptr, len); }
104
GetCrc32(uint32_t * crc32,int64_t len)105 int GetCrc32(uint32_t* crc32, int64_t len) override {
106 int chunk;
107 int ret;
108 while (len) {
109 chunk = std::min(len, COPY_BUF_SIZE);
110 ret = read_all(fd, copybuf, chunk);
111 if (ret < 0) {
112 return ret;
113 }
114 *crc32 = sparse_crc32(*crc32, copybuf, chunk);
115 len -= chunk;
116 }
117 return 0;
118 }
119 };
120
121 class SparseFileBufSource : public SparseFileSource {
122 private:
123 char* buf;
124 int64_t offset;
125
126 public:
SparseFileBufSource(char * buf)127 SparseFileBufSource(char* buf) : buf(buf), offset(0) {}
~SparseFileBufSource()128 ~SparseFileBufSource() override {}
129
Seek(int64_t off)130 void Seek(int64_t off) override {
131 buf += off;
132 offset += off;
133 }
134
GetOffset()135 int64_t GetOffset() override { return offset; }
136
SetOffset(int64_t off)137 int SetOffset(int64_t off) override {
138 buf += off - offset;
139 offset = off;
140 return 0;
141 }
142
AddToSparseFile(struct sparse_file * s,int64_t len,unsigned int block)143 int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
144 return sparse_file_add_data(s, buf, len, block);
145 }
146
ReadValue(void * ptr,int len)147 int ReadValue(void* ptr, int len) override {
148 memcpy(ptr, buf, len);
149 Seek(len);
150 return 0;
151 }
152
GetCrc32(uint32_t * crc32,int64_t len)153 int GetCrc32(uint32_t* crc32, int64_t len) override {
154 *crc32 = sparse_crc32(*crc32, buf, len);
155 Seek(len);
156 return 0;
157 }
158 };
159
verbose_error(bool verbose,int err,const char * fmt,...)160 static void verbose_error(bool verbose, int err, const char* fmt, ...) {
161 if (!verbose) return;
162
163 std::string msg = ErrorString(err);
164 if (fmt) {
165 msg += " at ";
166 va_list argp;
167 va_start(argp, fmt);
168 android::base::StringAppendV(&msg, fmt, argp);
169 va_end(argp);
170 }
171 sparse_print_verbose("%s\n", msg.c_str());
172 }
173
process_raw_chunk(struct sparse_file * s,unsigned int chunk_size,SparseFileSource * source,unsigned int blocks,unsigned int block,uint32_t * crc32)174 static int process_raw_chunk(struct sparse_file* s, unsigned int chunk_size,
175 SparseFileSource* source, unsigned int blocks, unsigned int block,
176 uint32_t* crc32) {
177 int ret;
178 int64_t len = blocks * s->block_size;
179
180 if (chunk_size % s->block_size != 0) {
181 return -EINVAL;
182 }
183
184 if (chunk_size / s->block_size != blocks) {
185 return -EINVAL;
186 }
187
188 ret = source->AddToSparseFile(s, len, block);
189 if (ret < 0) {
190 return ret;
191 }
192
193 if (crc32) {
194 ret = source->GetCrc32(crc32, len);
195 if (ret < 0) {
196 return ret;
197 }
198 } else {
199 source->Seek(len);
200 }
201
202 return 0;
203 }
204
process_fill_chunk(struct sparse_file * s,unsigned int chunk_size,SparseFileSource * source,unsigned int blocks,unsigned int block,uint32_t * crc32)205 static int process_fill_chunk(struct sparse_file* s, unsigned int chunk_size,
206 SparseFileSource* source, unsigned int blocks, unsigned int block,
207 uint32_t* crc32) {
208 int ret;
209 int chunk;
210 int64_t len = (int64_t)blocks * s->block_size;
211 uint32_t fill_val;
212 uint32_t* fillbuf;
213 unsigned int i;
214
215 if (chunk_size != sizeof(fill_val)) {
216 return -EINVAL;
217 }
218
219 ret = source->ReadValue(&fill_val, sizeof(fill_val));
220 if (ret < 0) {
221 return ret;
222 }
223
224 ret = sparse_file_add_fill(s, fill_val, len, block);
225 if (ret < 0) {
226 return ret;
227 }
228
229 if (crc32) {
230 /* Fill copy_buf with the fill value */
231 fillbuf = (uint32_t*)copybuf;
232 for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) {
233 fillbuf[i] = fill_val;
234 }
235
236 while (len) {
237 chunk = std::min(len, COPY_BUF_SIZE);
238 *crc32 = sparse_crc32(*crc32, copybuf, chunk);
239 len -= chunk;
240 }
241 }
242
243 return 0;
244 }
245
process_skip_chunk(struct sparse_file * s,unsigned int chunk_size,SparseFileSource * source __unused,unsigned int blocks,unsigned int block __unused,uint32_t * crc32)246 static int process_skip_chunk(struct sparse_file* s, unsigned int chunk_size,
247 SparseFileSource* source __unused, unsigned int blocks,
248 unsigned int block __unused, uint32_t* crc32) {
249 if (chunk_size != 0) {
250 return -EINVAL;
251 }
252
253 if (crc32) {
254 int64_t len = (int64_t)blocks * s->block_size;
255 memset(copybuf, 0, COPY_BUF_SIZE);
256
257 while (len) {
258 int chunk = std::min(len, COPY_BUF_SIZE);
259 *crc32 = sparse_crc32(*crc32, copybuf, chunk);
260 len -= chunk;
261 }
262 }
263
264 return 0;
265 }
266
process_crc32_chunk(SparseFileSource * source,unsigned int chunk_size,uint32_t * crc32)267 static int process_crc32_chunk(SparseFileSource* source, unsigned int chunk_size, uint32_t* crc32) {
268 uint32_t file_crc32;
269
270 if (chunk_size != sizeof(file_crc32)) {
271 return -EINVAL;
272 }
273
274 int ret = source->ReadValue(&file_crc32, sizeof(file_crc32));
275 if (ret < 0) {
276 return ret;
277 }
278
279 if (crc32 != nullptr && file_crc32 != *crc32) {
280 return -EINVAL;
281 }
282
283 return 0;
284 }
285
process_chunk(struct sparse_file * s,SparseFileSource * source,unsigned int chunk_hdr_sz,chunk_header_t * chunk_header,unsigned int cur_block,uint32_t * crc_ptr)286 static int process_chunk(struct sparse_file* s, SparseFileSource* source, unsigned int chunk_hdr_sz,
287 chunk_header_t* chunk_header, unsigned int cur_block, uint32_t* crc_ptr) {
288 int ret;
289 unsigned int chunk_data_size;
290 int64_t offset = source->GetOffset();
291
292 chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;
293
294 switch (chunk_header->chunk_type) {
295 case CHUNK_TYPE_RAW:
296 ret =
297 process_raw_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block, crc_ptr);
298 if (ret < 0) {
299 verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
300 return ret;
301 }
302 return chunk_header->chunk_sz;
303 case CHUNK_TYPE_FILL:
304 ret = process_fill_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block,
305 crc_ptr);
306 if (ret < 0) {
307 verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
308 return ret;
309 }
310 return chunk_header->chunk_sz;
311 case CHUNK_TYPE_DONT_CARE:
312 ret = process_skip_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block,
313 crc_ptr);
314 if (chunk_data_size != 0) {
315 if (ret < 0) {
316 verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
317 return ret;
318 }
319 }
320 return chunk_header->chunk_sz;
321 case CHUNK_TYPE_CRC32:
322 ret = process_crc32_chunk(source, chunk_data_size, crc_ptr);
323 if (ret < 0) {
324 verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64, offset);
325 return ret;
326 }
327 return 0;
328 default:
329 verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64, chunk_header->chunk_type,
330 offset);
331 }
332
333 return 0;
334 }
335
sparse_file_read_sparse(struct sparse_file * s,SparseFileSource * source,bool crc)336 static int sparse_file_read_sparse(struct sparse_file* s, SparseFileSource* source, bool crc) {
337 int ret;
338 unsigned int i;
339 sparse_header_t sparse_header;
340 chunk_header_t chunk_header;
341 uint32_t crc32 = 0;
342 uint32_t* crc_ptr = nullptr;
343 unsigned int cur_block = 0;
344
345 if (!copybuf) {
346 copybuf = (char*)malloc(COPY_BUF_SIZE);
347 }
348
349 if (!copybuf) {
350 return -ENOMEM;
351 }
352
353 if (crc) {
354 crc_ptr = &crc32;
355 }
356
357 ret = source->ReadValue(&sparse_header, sizeof(sparse_header));
358 if (ret < 0) {
359 return ret;
360 }
361
362 if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
363 return -EINVAL;
364 }
365
366 if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
367 return -EINVAL;
368 }
369
370 if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
371 return -EINVAL;
372 }
373
374 if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {
375 return -EINVAL;
376 }
377
378 if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
379 /* Skip the remaining bytes in a header that is longer than
380 * we expected.
381 */
382 source->Seek(sparse_header.file_hdr_sz - SPARSE_HEADER_LEN);
383 }
384
385 for (i = 0; i < sparse_header.total_chunks; i++) {
386 ret = source->ReadValue(&chunk_header, sizeof(chunk_header));
387 if (ret < 0) {
388 return ret;
389 }
390
391 if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
392 /* Skip the remaining bytes in a header that is longer than
393 * we expected.
394 */
395 source->Seek(sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN);
396 }
397
398 ret = process_chunk(s, source, sparse_header.chunk_hdr_sz, &chunk_header, cur_block, crc_ptr);
399 if (ret < 0) {
400 return ret;
401 }
402
403 cur_block += ret;
404 }
405
406 if (sparse_header.total_blks != cur_block) {
407 return -EINVAL;
408 }
409
410 return 0;
411 }
412
sparse_file_read_normal(struct sparse_file * s,int fd)413 static int sparse_file_read_normal(struct sparse_file* s, int fd) {
414 int ret;
415 uint32_t* buf = (uint32_t*)malloc(s->block_size);
416 unsigned int block = 0;
417 int64_t remain = s->len;
418 int64_t offset = 0;
419 unsigned int to_read;
420 unsigned int i;
421 bool sparse_block;
422
423 if (!buf) {
424 return -ENOMEM;
425 }
426
427 while (remain > 0) {
428 to_read = std::min(remain, (int64_t)(s->block_size));
429 ret = read_all(fd, buf, to_read);
430 if (ret < 0) {
431 error("failed to read sparse file");
432 free(buf);
433 return ret;
434 }
435
436 if (to_read == s->block_size) {
437 sparse_block = true;
438 for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {
439 if (buf[0] != buf[i]) {
440 sparse_block = false;
441 break;
442 }
443 }
444 } else {
445 sparse_block = false;
446 }
447
448 if (sparse_block) {
449 /* TODO: add flag to use skip instead of fill for buf[0] == 0 */
450 sparse_file_add_fill(s, buf[0], to_read, block);
451 } else {
452 sparse_file_add_fd(s, fd, offset, to_read, block);
453 }
454
455 remain -= to_read;
456 offset += to_read;
457 block++;
458 }
459
460 free(buf);
461 return 0;
462 }
463
sparse_file_read(struct sparse_file * s,int fd,bool sparse,bool crc)464 int sparse_file_read(struct sparse_file* s, int fd, bool sparse, bool crc) {
465 if (crc && !sparse) {
466 return -EINVAL;
467 }
468
469 if (sparse) {
470 SparseFileFdSource source(fd);
471 return sparse_file_read_sparse(s, &source, crc);
472 } else {
473 return sparse_file_read_normal(s, fd);
474 }
475 }
476
sparse_file_read_buf(struct sparse_file * s,char * buf,bool crc)477 int sparse_file_read_buf(struct sparse_file* s, char* buf, bool crc) {
478 SparseFileBufSource source(buf);
479 return sparse_file_read_sparse(s, &source, crc);
480 }
481
sparse_file_import_source(SparseFileSource * source,bool verbose,bool crc)482 static struct sparse_file* sparse_file_import_source(SparseFileSource* source, bool verbose,
483 bool crc) {
484 int ret;
485 sparse_header_t sparse_header;
486 int64_t len;
487 struct sparse_file* s;
488
489 ret = source->ReadValue(&sparse_header, sizeof(sparse_header));
490 if (ret < 0) {
491 verbose_error(verbose, ret, "header");
492 return nullptr;
493 }
494
495 if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
496 verbose_error(verbose, -EINVAL, "header magic");
497 return nullptr;
498 }
499
500 if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
501 verbose_error(verbose, -EINVAL, "header major version");
502 return nullptr;
503 }
504
505 if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
506 return nullptr;
507 }
508
509 if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
510 return nullptr;
511 }
512
513 len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
514 s = sparse_file_new(sparse_header.blk_sz, len);
515 if (!s) {
516 verbose_error(verbose, -EINVAL, nullptr);
517 return nullptr;
518 }
519
520 ret = source->SetOffset(0);
521 if (ret < 0) {
522 verbose_error(verbose, ret, "seeking");
523 sparse_file_destroy(s);
524 return nullptr;
525 }
526
527 s->verbose = verbose;
528
529 ret = sparse_file_read_sparse(s, source, crc);
530 if (ret < 0) {
531 sparse_file_destroy(s);
532 return nullptr;
533 }
534
535 return s;
536 }
537
sparse_file_import(int fd,bool verbose,bool crc)538 struct sparse_file* sparse_file_import(int fd, bool verbose, bool crc) {
539 SparseFileFdSource source(fd);
540 return sparse_file_import_source(&source, verbose, crc);
541 }
542
sparse_file_import_buf(char * buf,bool verbose,bool crc)543 struct sparse_file* sparse_file_import_buf(char* buf, bool verbose, bool crc) {
544 SparseFileBufSource source(buf);
545 return sparse_file_import_source(&source, verbose, crc);
546 }
547
sparse_file_import_auto(int fd,bool crc,bool verbose)548 struct sparse_file* sparse_file_import_auto(int fd, bool crc, bool verbose) {
549 struct sparse_file* s;
550 int64_t len;
551 int ret;
552
553 s = sparse_file_import(fd, verbose, crc);
554 if (s) {
555 return s;
556 }
557
558 len = lseek64(fd, 0, SEEK_END);
559 if (len < 0) {
560 return nullptr;
561 }
562
563 lseek64(fd, 0, SEEK_SET);
564
565 s = sparse_file_new(4096, len);
566 if (!s) {
567 return nullptr;
568 }
569
570 ret = sparse_file_read_normal(s, fd);
571 if (ret < 0) {
572 sparse_file_destroy(s);
573 return nullptr;
574 }
575
576 return s;
577 }
578