2 * Copyright (C) 2012 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #define _FILE_OFFSET_BITS 64
19 #define _LARGEFILE64_SOURCE 1
30 #include <sparse/sparse.h>
33 #include "output_file.h"
34 #include "sparse_crc32.h"
35 #include "sparse_file.h"
36 #include "sparse_format.h"
38 #if defined(__APPLE__) && defined(__MACH__)
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))
47 #define COPY_BUF_SIZE (1024U*1024U)
51 ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
53 static void verbose_error(bool verbose
, int err
, const char *fmt
, ...)
62 size
= vsnprintf(NULL
, 0, fmt
, argp
);
69 at
= malloc(size
+ 1);
75 vsnprintf(at
, size
, fmt
, argp
);
81 if (err
== -EOVERFLOW
) {
82 sparse_print_verbose("EOF while reading file%s%s\n", s
, at
);
83 } else if (err
== -EINVAL
) {
84 sparse_print_verbose("Invalid sparse file format%s%s\n", s
, at
);
85 } else if (err
== -ENOMEM
) {
86 sparse_print_verbose("Failed allocation while reading file%s%s\n",
89 sparse_print_verbose("Unknown error %d%s%s\n", err
, s
, at
);
97 static int process_raw_chunk(struct sparse_file
*s
, unsigned int chunk_size
,
98 int fd
, int64_t offset
, unsigned int blocks
, unsigned int block
,
103 unsigned int len
= blocks
* s
->block_size
;
105 if (chunk_size
% s
->block_size
!= 0) {
109 if (chunk_size
/ s
->block_size
!= blocks
) {
113 ret
= sparse_file_add_fd(s
, fd
, offset
, len
, block
);
120 chunk
= min(len
, COPY_BUF_SIZE
);
121 ret
= read_all(fd
, copybuf
, chunk
);
125 *crc32
= sparse_crc32(*crc32
, copybuf
, chunk
);
129 lseek64(fd
, len
, SEEK_CUR
);
135 static int process_fill_chunk(struct sparse_file
*s
, unsigned int chunk_size
,
136 int fd
, unsigned int blocks
, unsigned int block
, uint32_t *crc32
)
140 int64_t len
= (int64_t)blocks
* s
->block_size
;
145 if (chunk_size
!= sizeof(fill_val
)) {
149 ret
= read_all(fd
, &fill_val
, sizeof(fill_val
));
154 ret
= sparse_file_add_fill(s
, fill_val
, len
, block
);
160 /* Fill copy_buf with the fill value */
161 fillbuf
= (uint32_t *)copybuf
;
162 for (i
= 0; i
< (COPY_BUF_SIZE
/ sizeof(fill_val
)); i
++) {
163 fillbuf
[i
] = fill_val
;
167 chunk
= min(len
, COPY_BUF_SIZE
);
168 *crc32
= sparse_crc32(*crc32
, copybuf
, chunk
);
176 static int process_skip_chunk(struct sparse_file
*s
, unsigned int chunk_size
,
177 int fd __unused
, unsigned int blocks
,
178 unsigned int block __unused
, uint32_t *crc32
)
180 if (chunk_size
!= 0) {
185 int64_t len
= (int64_t)blocks
* s
->block_size
;
186 memset(copybuf
, 0, COPY_BUF_SIZE
);
189 int chunk
= min(len
, COPY_BUF_SIZE
);
190 *crc32
= sparse_crc32(*crc32
, copybuf
, chunk
);
198 static int process_crc32_chunk(int fd
, unsigned int chunk_size
, uint32_t crc32
)
203 if (chunk_size
!= sizeof(file_crc32
)) {
207 ret
= read_all(fd
, &file_crc32
, sizeof(file_crc32
));
212 if (file_crc32
!= crc32
) {
219 static int process_chunk(struct sparse_file
*s
, int fd
, off64_t offset
,
220 unsigned int chunk_hdr_sz
, chunk_header_t
*chunk_header
,
221 unsigned int cur_block
, uint32_t *crc_ptr
)
224 unsigned int chunk_data_size
;
226 chunk_data_size
= chunk_header
->total_sz
- chunk_hdr_sz
;
228 switch (chunk_header
->chunk_type
) {
230 ret
= process_raw_chunk(s
, chunk_data_size
, fd
, offset
,
231 chunk_header
->chunk_sz
, cur_block
, crc_ptr
);
233 verbose_error(s
->verbose
, ret
, "data block at %lld", offset
);
236 return chunk_header
->chunk_sz
;
237 case CHUNK_TYPE_FILL
:
238 ret
= process_fill_chunk(s
, chunk_data_size
, fd
,
239 chunk_header
->chunk_sz
, cur_block
, crc_ptr
);
241 verbose_error(s
->verbose
, ret
, "fill block at %lld", offset
);
244 return chunk_header
->chunk_sz
;
245 case CHUNK_TYPE_DONT_CARE
:
246 ret
= process_skip_chunk(s
, chunk_data_size
, fd
,
247 chunk_header
->chunk_sz
, cur_block
, crc_ptr
);
248 if (chunk_data_size
!= 0) {
250 verbose_error(s
->verbose
, ret
, "skip block at %lld", offset
);
254 return chunk_header
->chunk_sz
;
255 case CHUNK_TYPE_CRC32
:
256 ret
= process_crc32_chunk(fd
, chunk_data_size
, *crc_ptr
);
258 verbose_error(s
->verbose
, -EINVAL
, "crc block at %lld",
264 verbose_error(s
->verbose
, -EINVAL
, "unknown block %04X at %lld",
265 chunk_header
->chunk_type
, offset
);
271 static int sparse_file_read_sparse(struct sparse_file
*s
, int fd
, bool crc
)
275 sparse_header_t sparse_header
;
276 chunk_header_t chunk_header
;
278 uint32_t *crc_ptr
= 0;
279 unsigned int cur_block
= 0;
283 copybuf
= malloc(COPY_BUF_SIZE
);
294 ret
= read_all(fd
, &sparse_header
, sizeof(sparse_header
));
299 if (sparse_header
.magic
!= SPARSE_HEADER_MAGIC
) {
303 if (sparse_header
.major_version
!= SPARSE_HEADER_MAJOR_VER
) {
307 if (sparse_header
.file_hdr_sz
< SPARSE_HEADER_LEN
) {
311 if (sparse_header
.chunk_hdr_sz
< sizeof(chunk_header
)) {
315 if (sparse_header
.file_hdr_sz
> SPARSE_HEADER_LEN
) {
316 /* Skip the remaining bytes in a header that is longer than
319 lseek64(fd
, sparse_header
.file_hdr_sz
- SPARSE_HEADER_LEN
, SEEK_CUR
);
322 for (i
= 0; i
< sparse_header
.total_chunks
; i
++) {
323 ret
= read_all(fd
, &chunk_header
, sizeof(chunk_header
));
328 if (sparse_header
.chunk_hdr_sz
> CHUNK_HEADER_LEN
) {
329 /* Skip the remaining bytes in a header that is longer than
332 lseek64(fd
, sparse_header
.chunk_hdr_sz
- CHUNK_HEADER_LEN
, SEEK_CUR
);
335 offset
= lseek64(fd
, 0, SEEK_CUR
);
337 ret
= process_chunk(s
, fd
, offset
, sparse_header
.chunk_hdr_sz
, &chunk_header
,
346 if (sparse_header
.total_blks
!= cur_block
) {
353 static int sparse_file_read_normal(struct sparse_file
*s
, int fd
)
356 uint32_t *buf
= malloc(s
->block_size
);
357 unsigned int block
= 0;
358 int64_t remain
= s
->len
;
360 unsigned int to_read
;
369 to_read
= min(remain
, s
->block_size
);
370 ret
= read_all(fd
, buf
, to_read
);
372 error("failed to read sparse file");
376 if (to_read
== s
->block_size
) {
378 for (i
= 1; i
< s
->block_size
/ sizeof(uint32_t); i
++) {
379 if (buf
[0] != buf
[i
]) {
380 sparse_block
= false;
385 sparse_block
= false;
389 /* TODO: add flag to use skip instead of fill for buf[0] == 0 */
390 sparse_file_add_fill(s
, buf
[0], to_read
, block
);
392 sparse_file_add_fd(s
, fd
, offset
, to_read
, block
);
403 int sparse_file_read(struct sparse_file
*s
, int fd
, bool sparse
, bool crc
)
405 if (crc
&& !sparse
) {
410 return sparse_file_read_sparse(s
, fd
, crc
);
412 return sparse_file_read_normal(s
, fd
);
416 struct sparse_file
*sparse_file_import(int fd
, bool verbose
, bool crc
)
419 sparse_header_t sparse_header
;
421 struct sparse_file
*s
;
423 ret
= read_all(fd
, &sparse_header
, sizeof(sparse_header
));
425 verbose_error(verbose
, ret
, "header");
429 if (sparse_header
.magic
!= SPARSE_HEADER_MAGIC
) {
430 verbose_error(verbose
, -EINVAL
, "header magic");
434 if (sparse_header
.major_version
!= SPARSE_HEADER_MAJOR_VER
) {
435 verbose_error(verbose
, -EINVAL
, "header major version");
439 if (sparse_header
.file_hdr_sz
< SPARSE_HEADER_LEN
) {
443 if (sparse_header
.chunk_hdr_sz
< sizeof(chunk_header_t
)) {
447 len
= (int64_t)sparse_header
.total_blks
* sparse_header
.blk_sz
;
448 s
= sparse_file_new(sparse_header
.blk_sz
, len
);
450 verbose_error(verbose
, -EINVAL
, NULL
);
454 ret
= lseek64(fd
, 0, SEEK_SET
);
456 verbose_error(verbose
, ret
, "seeking");
457 sparse_file_destroy(s
);
461 s
->verbose
= verbose
;
463 ret
= sparse_file_read(s
, fd
, true, crc
);
465 sparse_file_destroy(s
);
472 struct sparse_file
*sparse_file_import_auto(int fd
, bool crc
, bool verbose
)
474 struct sparse_file
*s
;
478 s
= sparse_file_import(fd
, verbose
, crc
);
483 len
= lseek64(fd
, 0, SEEK_END
);
488 lseek64(fd
, 0, SEEK_SET
);
490 s
= sparse_file_new(4096, len
);
495 ret
= sparse_file_read_normal(s
, fd
);
497 sparse_file_destroy(s
);