blob: 028b6bed1b0b9d59a173463b07d11d1a91f5c768 [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
20#include <fcntl.h>
Jerry Zhang7b444f02018-06-12 16:42:09 -070021#include <inttypes.h>
Colin Cross0c4c47f2012-04-25 19:02:58 -070022#include <stdarg.h>
Colin Cross0c4c47f2012-04-25 19:02:58 -070023#include <stdint.h>
24#include <stdio.h>
25#include <stdlib.h>
Xiaolei Yuc39caaf2017-10-11 14:46:29 +080026#include <string.h>
Colin Cross0c4c47f2012-04-25 19:02:58 -070027#include <unistd.h>
Jerry Zhang7b444f02018-06-12 16:42:09 -070028#include <algorithm>
29#include <string>
Colin Cross0c4c47f2012-04-25 19:02:58 -070030
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
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
Jerry Zhang7b444f02018-06-12 16:42:09 -070046#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
Colin Cross0c4c47f2012-04-25 19:02:58 -070047#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;
Jerry Zhang7b444f02018-06-12 16:42:09 -070050static char* copybuf;
Colin Cross0c4c47f2012-04-25 19:02:58 -070051
Jerry Zhang7b444f02018-06-12 16:42:09 -070052static 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);
Chris Friesa7eeb222017-04-17 21:53:16 -050057}
Colin Cross0c4c47f2012-04-25 19:02:58 -070058
Jerry Zhang50e60292018-06-05 11:44:52 -070059class SparseFileSource {
Jerry Zhang7b444f02018-06-12 16:42:09 -070060 public:
Keith Moka3b72062021-12-31 05:09:32 +000061 /* Seeks the source ahead by the given offset.
62 * Return 0 if successful. */
63 virtual int Seek(int64_t offset) = 0;
Jerry Zhang50e60292018-06-05 11:44:52 -070064
Jerry Zhang7b444f02018-06-12 16:42:09 -070065 /* Return the current offset. */
66 virtual int64_t GetOffset() = 0;
Jerry Zhang50e60292018-06-05 11:44:52 -070067
Keith Moka3b72062021-12-31 05:09:32 +000068 /* Rewind to beginning. Return 0 if successful. */
69 virtual int Rewind() = 0;
Jerry Zhang50e60292018-06-05 11:44:52 -070070
Jerry Zhang7b444f02018-06-12 16:42:09 -070071 /* Adds the given length from the current offset of the source to the file at the given block.
72 * Return 0 if successful. */
73 virtual int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) = 0;
Jerry Zhang50e60292018-06-05 11:44:52 -070074
Jerry Zhang7b444f02018-06-12 16:42:09 -070075 /* Get data of fixed size from the current offset and seek len bytes. Return 0 if successful. */
76 virtual int ReadValue(void* ptr, int len) = 0;
Jerry Zhang50e60292018-06-05 11:44:52 -070077
Jerry Zhang7b444f02018-06-12 16:42:09 -070078 /* Find the crc32 of the next len bytes and seek ahead len bytes. Return 0 if successful. */
79 virtual int GetCrc32(uint32_t* crc32, int64_t len) = 0;
Jerry Zhang50e60292018-06-05 11:44:52 -070080
Jerry Zhang7b444f02018-06-12 16:42:09 -070081 virtual ~SparseFileSource(){};
Jerry Zhang50e60292018-06-05 11:44:52 -070082};
83
84class SparseFileFdSource : public SparseFileSource {
Jerry Zhang7b444f02018-06-12 16:42:09 -070085 private:
86 int fd;
Jerry Zhang50e60292018-06-05 11:44:52 -070087
Jerry Zhang7b444f02018-06-12 16:42:09 -070088 public:
89 SparseFileFdSource(int fd) : fd(fd) {}
90 ~SparseFileFdSource() override {}
Jerry Zhang50e60292018-06-05 11:44:52 -070091
Keith Moka3b72062021-12-31 05:09:32 +000092 int Seek(int64_t off) override {
93 return lseek64(fd, off, SEEK_CUR) != -1 ? 0 : -errno;
94 }
Jerry Zhang50e60292018-06-05 11:44:52 -070095
Jerry Zhang7b444f02018-06-12 16:42:09 -070096 int64_t GetOffset() override { return lseek64(fd, 0, SEEK_CUR); }
Jerry Zhang50e60292018-06-05 11:44:52 -070097
Keith Moka3b72062021-12-31 05:09:32 +000098 int Rewind() override {
99 return lseek64(fd, 0, SEEK_SET) == 0 ? 0 : -errno;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700100 }
Jerry Zhang50e60292018-06-05 11:44:52 -0700101
Jerry Zhang7b444f02018-06-12 16:42:09 -0700102 int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
103 return sparse_file_add_fd(s, fd, GetOffset(), len, block);
104 }
Jerry Zhang50e60292018-06-05 11:44:52 -0700105
Jerry Zhang7b444f02018-06-12 16:42:09 -0700106 int ReadValue(void* ptr, int len) override { return read_all(fd, ptr, len); }
107
108 int GetCrc32(uint32_t* crc32, int64_t len) override {
109 int chunk;
110 int ret;
111 while (len) {
112 chunk = std::min(len, COPY_BUF_SIZE);
113 ret = read_all(fd, copybuf, chunk);
114 if (ret < 0) {
115 return ret;
116 }
117 *crc32 = sparse_crc32(*crc32, copybuf, chunk);
118 len -= chunk;
119 }
120 return 0;
121 }
Jerry Zhang50e60292018-06-05 11:44:52 -0700122};
123
124class SparseFileBufSource : public SparseFileSource {
Jerry Zhang7b444f02018-06-12 16:42:09 -0700125 private:
Keith Moka3b72062021-12-31 05:09:32 +0000126 char* buf_start;
127 char* buf_end;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700128 char* buf;
129 int64_t offset;
Jerry Zhang50e60292018-06-05 11:44:52 -0700130
Keith Moka3b72062021-12-31 05:09:32 +0000131 int AccessOkay(int64_t len) {
132 if (len <= 0) return -EINVAL;
133 if (buf < buf_start) return -EOVERFLOW;
134 if (buf >= buf_end) return -EOVERFLOW;
135 if (len > buf_end - buf) return -EOVERFLOW;
136
137 return 0;
138 }
139
Jerry Zhang7b444f02018-06-12 16:42:09 -0700140 public:
Keith Moka3b72062021-12-31 05:09:32 +0000141 SparseFileBufSource(char* buf, uint64_t len) {
142 this->buf = buf;
143 this->offset = 0;
144 this->buf_start = buf;
145 this->buf_end = buf + len;
146 }
Jerry Zhang7b444f02018-06-12 16:42:09 -0700147 ~SparseFileBufSource() override {}
Jerry Zhang50e60292018-06-05 11:44:52 -0700148
Keith Moka3b72062021-12-31 05:09:32 +0000149 int Seek(int64_t off) override {
150 int ret = AccessOkay(off);
151 if (ret < 0) {
152 return ret;
153 }
Jerry Zhang7b444f02018-06-12 16:42:09 -0700154 buf += off;
155 offset += off;
Keith Moka3b72062021-12-31 05:09:32 +0000156 return 0;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700157 }
Jerry Zhang50e60292018-06-05 11:44:52 -0700158
Jerry Zhang7b444f02018-06-12 16:42:09 -0700159 int64_t GetOffset() override { return offset; }
Jerry Zhang50e60292018-06-05 11:44:52 -0700160
Keith Moka3b72062021-12-31 05:09:32 +0000161 int Rewind() override {
162 buf = buf_start;
163 offset = 0;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700164 return 0;
165 }
Jerry Zhang50e60292018-06-05 11:44:52 -0700166
Jerry Zhang7b444f02018-06-12 16:42:09 -0700167 int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
Keith Moka3b72062021-12-31 05:09:32 +0000168 int ret = AccessOkay(len);
169 if (ret < 0) {
170 return ret;
171 }
Jerry Zhang7b444f02018-06-12 16:42:09 -0700172 return sparse_file_add_data(s, buf, len, block);
173 }
Jerry Zhang50e60292018-06-05 11:44:52 -0700174
Jerry Zhang7b444f02018-06-12 16:42:09 -0700175 int ReadValue(void* ptr, int len) override {
Keith Moka3b72062021-12-31 05:09:32 +0000176 int ret = AccessOkay(len);
177 if (ret < 0) {
178 return ret;
179 }
Jerry Zhang7b444f02018-06-12 16:42:09 -0700180 memcpy(ptr, buf, len);
Keith Moka3b72062021-12-31 05:09:32 +0000181 buf += len;
182 offset += len;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700183 return 0;
184 }
185
186 int GetCrc32(uint32_t* crc32, int64_t len) override {
Keith Moka3b72062021-12-31 05:09:32 +0000187 int ret = AccessOkay(len);
188 if (ret < 0) {
189 return ret;
190 }
Jerry Zhang7b444f02018-06-12 16:42:09 -0700191 *crc32 = sparse_crc32(*crc32, buf, len);
Keith Moka3b72062021-12-31 05:09:32 +0000192 buf += len;
193 offset += len;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700194 return 0;
195 }
Jerry Zhang50e60292018-06-05 11:44:52 -0700196};
197
Jerry Zhang7b444f02018-06-12 16:42:09 -0700198static void verbose_error(bool verbose, int err, const char* fmt, ...) {
199 if (!verbose) return;
Chris Friesa7eeb222017-04-17 21:53:16 -0500200
Jerry Zhang7b444f02018-06-12 16:42:09 -0700201 std::string msg = ErrorString(err);
202 if (fmt) {
203 msg += " at ";
204 va_list argp;
205 va_start(argp, fmt);
206 android::base::StringAppendV(&msg, fmt, argp);
207 va_end(argp);
208 }
209 sparse_print_verbose("%s\n", msg.c_str());
Colin Cross0c4c47f2012-04-25 19:02:58 -0700210}
211
Jerry Zhang7b444f02018-06-12 16:42:09 -0700212static int process_raw_chunk(struct sparse_file* s, unsigned int chunk_size,
213 SparseFileSource* source, unsigned int blocks, unsigned int block,
214 uint32_t* crc32) {
215 int ret;
Keith Moka3b72062021-12-31 05:09:32 +0000216 int64_t len = (int64_t)blocks * s->block_size;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700217
Jerry Zhang7b444f02018-06-12 16:42:09 -0700218 if (chunk_size % s->block_size != 0) {
219 return -EINVAL;
220 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700221
Jerry Zhang7b444f02018-06-12 16:42:09 -0700222 if (chunk_size / s->block_size != blocks) {
223 return -EINVAL;
224 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700225
Jerry Zhang7b444f02018-06-12 16:42:09 -0700226 ret = source->AddToSparseFile(s, len, block);
227 if (ret < 0) {
228 return ret;
229 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700230
Jerry Zhang7b444f02018-06-12 16:42:09 -0700231 if (crc32) {
232 ret = source->GetCrc32(crc32, len);
233 if (ret < 0) {
234 return ret;
235 }
236 } else {
Keith Moka3b72062021-12-31 05:09:32 +0000237 ret = source->Seek(len);
238 if (ret < 0) {
239 return ret;
240 }
Jerry Zhang7b444f02018-06-12 16:42:09 -0700241 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700242
Jerry Zhang7b444f02018-06-12 16:42:09 -0700243 return 0;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700244}
245
Jerry Zhang7b444f02018-06-12 16:42:09 -0700246static int process_fill_chunk(struct sparse_file* s, unsigned int chunk_size,
247 SparseFileSource* source, unsigned int blocks, unsigned int block,
248 uint32_t* crc32) {
249 int ret;
250 int chunk;
251 int64_t len = (int64_t)blocks * s->block_size;
252 uint32_t fill_val;
253 uint32_t* fillbuf;
254 unsigned int i;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700255
Jerry Zhang7b444f02018-06-12 16:42:09 -0700256 if (chunk_size != sizeof(fill_val)) {
257 return -EINVAL;
258 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700259
Jerry Zhang7b444f02018-06-12 16:42:09 -0700260 ret = source->ReadValue(&fill_val, sizeof(fill_val));
261 if (ret < 0) {
262 return ret;
263 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700264
Jerry Zhang7b444f02018-06-12 16:42:09 -0700265 ret = sparse_file_add_fill(s, fill_val, len, block);
266 if (ret < 0) {
267 return ret;
268 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700269
Jerry Zhang7b444f02018-06-12 16:42:09 -0700270 if (crc32) {
271 /* Fill copy_buf with the fill value */
272 fillbuf = (uint32_t*)copybuf;
273 for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) {
274 fillbuf[i] = fill_val;
275 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700276
Jerry Zhang7b444f02018-06-12 16:42:09 -0700277 while (len) {
278 chunk = std::min(len, COPY_BUF_SIZE);
279 *crc32 = sparse_crc32(*crc32, copybuf, chunk);
280 len -= chunk;
281 }
282 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700283
Jerry Zhang7b444f02018-06-12 16:42:09 -0700284 return 0;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700285}
286
Jerry Zhang7b444f02018-06-12 16:42:09 -0700287static int process_skip_chunk(struct sparse_file* s, unsigned int chunk_size,
288 SparseFileSource* source __unused, unsigned int blocks,
289 unsigned int block __unused, uint32_t* crc32) {
290 if (chunk_size != 0) {
291 return -EINVAL;
292 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700293
Jerry Zhang7b444f02018-06-12 16:42:09 -0700294 if (crc32) {
295 int64_t len = (int64_t)blocks * s->block_size;
296 memset(copybuf, 0, COPY_BUF_SIZE);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700297
Jerry Zhang7b444f02018-06-12 16:42:09 -0700298 while (len) {
299 int chunk = std::min(len, COPY_BUF_SIZE);
300 *crc32 = sparse_crc32(*crc32, copybuf, chunk);
301 len -= chunk;
302 }
303 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700304
Jerry Zhang7b444f02018-06-12 16:42:09 -0700305 return 0;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700306}
307
Jerry Zhang7b444f02018-06-12 16:42:09 -0700308static int process_crc32_chunk(SparseFileSource* source, unsigned int chunk_size, uint32_t* crc32) {
309 uint32_t file_crc32;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700310
Jerry Zhang7b444f02018-06-12 16:42:09 -0700311 if (chunk_size != sizeof(file_crc32)) {
312 return -EINVAL;
313 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700314
Jerry Zhang7b444f02018-06-12 16:42:09 -0700315 int ret = source->ReadValue(&file_crc32, sizeof(file_crc32));
316 if (ret < 0) {
317 return ret;
318 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700319
Yi Kong17ba95e2018-07-23 16:31:11 -0700320 if (crc32 != nullptr && file_crc32 != *crc32) {
Jerry Zhang7b444f02018-06-12 16:42:09 -0700321 return -EINVAL;
322 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700323
Jerry Zhang7b444f02018-06-12 16:42:09 -0700324 return 0;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700325}
326
Jerry Zhang7b444f02018-06-12 16:42:09 -0700327static int process_chunk(struct sparse_file* s, SparseFileSource* source, unsigned int chunk_hdr_sz,
328 chunk_header_t* chunk_header, unsigned int cur_block, uint32_t* crc_ptr) {
329 int ret;
330 unsigned int chunk_data_size;
331 int64_t offset = source->GetOffset();
Colin Cross0c4c47f2012-04-25 19:02:58 -0700332
Jerry Zhang7b444f02018-06-12 16:42:09 -0700333 chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700334
Jerry Zhang7b444f02018-06-12 16:42:09 -0700335 switch (chunk_header->chunk_type) {
336 case CHUNK_TYPE_RAW:
337 ret =
338 process_raw_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block, crc_ptr);
339 if (ret < 0) {
340 verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
341 return ret;
342 }
343 return chunk_header->chunk_sz;
344 case CHUNK_TYPE_FILL:
345 ret = process_fill_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block,
346 crc_ptr);
347 if (ret < 0) {
348 verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
349 return ret;
350 }
351 return chunk_header->chunk_sz;
352 case CHUNK_TYPE_DONT_CARE:
353 ret = process_skip_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block,
354 crc_ptr);
355 if (chunk_data_size != 0) {
356 if (ret < 0) {
357 verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
358 return ret;
359 }
360 }
361 return chunk_header->chunk_sz;
362 case CHUNK_TYPE_CRC32:
363 ret = process_crc32_chunk(source, chunk_data_size, crc_ptr);
364 if (ret < 0) {
365 verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64, offset);
366 return ret;
367 }
368 return 0;
369 default:
370 verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64, chunk_header->chunk_type,
371 offset);
372 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700373
Jerry Zhang7b444f02018-06-12 16:42:09 -0700374 return 0;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700375}
376
Jerry Zhang7b444f02018-06-12 16:42:09 -0700377static int sparse_file_read_sparse(struct sparse_file* s, SparseFileSource* source, bool crc) {
378 int ret;
379 unsigned int i;
380 sparse_header_t sparse_header;
381 chunk_header_t chunk_header;
382 uint32_t crc32 = 0;
Yi Kong17ba95e2018-07-23 16:31:11 -0700383 uint32_t* crc_ptr = nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700384 unsigned int cur_block = 0;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700385
Jerry Zhang7b444f02018-06-12 16:42:09 -0700386 if (!copybuf) {
387 copybuf = (char*)malloc(COPY_BUF_SIZE);
388 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700389
Jerry Zhang7b444f02018-06-12 16:42:09 -0700390 if (!copybuf) {
391 return -ENOMEM;
392 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700393
Jerry Zhang7b444f02018-06-12 16:42:09 -0700394 if (crc) {
395 crc_ptr = &crc32;
396 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700397
Jerry Zhang7b444f02018-06-12 16:42:09 -0700398 ret = source->ReadValue(&sparse_header, sizeof(sparse_header));
399 if (ret < 0) {
400 return ret;
401 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700402
Jerry Zhang7b444f02018-06-12 16:42:09 -0700403 if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
404 return -EINVAL;
405 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700406
Jerry Zhang7b444f02018-06-12 16:42:09 -0700407 if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
408 return -EINVAL;
409 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700410
Jerry Zhang7b444f02018-06-12 16:42:09 -0700411 if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
412 return -EINVAL;
413 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700414
Jerry Zhang7b444f02018-06-12 16:42:09 -0700415 if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {
416 return -EINVAL;
417 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700418
Jerry Zhang7b444f02018-06-12 16:42:09 -0700419 if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
420 /* Skip the remaining bytes in a header that is longer than
421 * we expected.
422 */
Keith Moka3b72062021-12-31 05:09:32 +0000423 ret = source->Seek(sparse_header.file_hdr_sz - SPARSE_HEADER_LEN);
424 if (ret < 0) {
425 return ret;
426 }
Jerry Zhang7b444f02018-06-12 16:42:09 -0700427 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700428
Jerry Zhang7b444f02018-06-12 16:42:09 -0700429 for (i = 0; i < sparse_header.total_chunks; i++) {
430 ret = source->ReadValue(&chunk_header, sizeof(chunk_header));
431 if (ret < 0) {
432 return ret;
433 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700434
Jerry Zhang7b444f02018-06-12 16:42:09 -0700435 if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
436 /* Skip the remaining bytes in a header that is longer than
437 * we expected.
438 */
Keith Moka3b72062021-12-31 05:09:32 +0000439 ret = source->Seek(sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN);
440 if (ret < 0) {
441 return ret;
442 }
Jerry Zhang7b444f02018-06-12 16:42:09 -0700443 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700444
Jerry Zhang7b444f02018-06-12 16:42:09 -0700445 ret = process_chunk(s, source, sparse_header.chunk_hdr_sz, &chunk_header, cur_block, crc_ptr);
446 if (ret < 0) {
447 return ret;
448 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700449
Jerry Zhang7b444f02018-06-12 16:42:09 -0700450 cur_block += ret;
451 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700452
Jerry Zhang7b444f02018-06-12 16:42:09 -0700453 if (sparse_header.total_blks != cur_block) {
454 return -EINVAL;
455 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700456
Jerry Zhang7b444f02018-06-12 16:42:09 -0700457 return 0;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700458}
459
Sean Anderson95657f32021-12-30 15:16:08 -0500460static int do_sparse_file_read_normal(struct sparse_file* s, int fd, uint32_t* buf, int64_t offset,
461 int64_t remain) {
Jerry Zhang7b444f02018-06-12 16:42:09 -0700462 int ret;
Sean Anderson95657f32021-12-30 15:16:08 -0500463 unsigned int block = offset / s->block_size;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700464 unsigned int to_read;
465 unsigned int i;
466 bool sparse_block;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700467
Jerry Zhang7b444f02018-06-12 16:42:09 -0700468 if (!buf) {
469 return -ENOMEM;
470 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700471
Jerry Zhang7b444f02018-06-12 16:42:09 -0700472 while (remain > 0) {
473 to_read = std::min(remain, (int64_t)(s->block_size));
474 ret = read_all(fd, buf, to_read);
475 if (ret < 0) {
476 error("failed to read sparse file");
Jerry Zhang7b444f02018-06-12 16:42:09 -0700477 return ret;
478 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700479
Jerry Zhang7b444f02018-06-12 16:42:09 -0700480 if (to_read == s->block_size) {
481 sparse_block = true;
482 for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {
483 if (buf[0] != buf[i]) {
484 sparse_block = false;
485 break;
486 }
487 }
488 } else {
489 sparse_block = false;
490 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700491
Jerry Zhang7b444f02018-06-12 16:42:09 -0700492 if (sparse_block) {
493 /* TODO: add flag to use skip instead of fill for buf[0] == 0 */
494 sparse_file_add_fill(s, buf[0], to_read, block);
495 } else {
496 sparse_file_add_fd(s, fd, offset, to_read, block);
497 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700498
Jerry Zhang7b444f02018-06-12 16:42:09 -0700499 remain -= to_read;
500 offset += to_read;
501 block++;
502 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700503
Jerry Zhang7b444f02018-06-12 16:42:09 -0700504 return 0;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700505}
506
Sean Anderson95657f32021-12-30 15:16:08 -0500507static int sparse_file_read_normal(struct sparse_file* s, int fd) {
508 int ret;
509 uint32_t* buf = (uint32_t*)malloc(s->block_size);
510
511 if (!buf)
512 return -ENOMEM;
513
514 ret = do_sparse_file_read_normal(s, fd, buf, 0, s->len);
515 free(buf);
516 return ret;
517}
518
Sean Andersonf96466b2021-12-30 15:19:41 -0500519#ifdef __linux__
520static int sparse_file_read_hole(struct sparse_file* s, int fd) {
521 int ret;
522 uint32_t* buf = (uint32_t*)malloc(s->block_size);
523 int64_t end = 0;
524 int64_t start = 0;
525
526 if (!buf) {
527 return -ENOMEM;
528 }
529
530 do {
531 start = lseek(fd, end, SEEK_DATA);
532 if (start < 0) {
533 if (errno == ENXIO)
534 /* The rest of the file is a hole */
535 break;
536
537 error("could not seek to data");
538 free(buf);
539 return -errno;
540 } else if (start > s->len) {
541 break;
542 }
543
544 end = lseek(fd, start, SEEK_HOLE);
545 if (end < 0) {
546 error("could not seek to end");
547 free(buf);
548 return -errno;
549 }
550 end = std::min(end, s->len);
551
552 start = ALIGN_DOWN(start, s->block_size);
553 end = ALIGN(end, s->block_size);
554 if (lseek(fd, start, SEEK_SET) < 0) {
555 free(buf);
556 return -errno;
557 }
558
559 ret = do_sparse_file_read_normal(s, fd, buf, start, end - start);
560 if (ret) {
561 free(buf);
562 return ret;
563 }
564 } while (end < s->len);
565
566 free(buf);
567 return 0;
568}
569#else
570static int sparse_file_read_hole(struct sparse_file* s __unused, int fd __unused) {
571 return -ENOTSUP;
572}
573#endif
574
575int sparse_file_read(struct sparse_file* s, int fd, enum sparse_read_mode mode, bool crc) {
576 if (crc && mode != SPARSE_READ_MODE_SPARSE) {
Jerry Zhang7b444f02018-06-12 16:42:09 -0700577 return -EINVAL;
578 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700579
Sean Andersonf96466b2021-12-30 15:19:41 -0500580 switch (mode) {
581 case SPARSE_READ_MODE_SPARSE: {
582 SparseFileFdSource source(fd);
583 return sparse_file_read_sparse(s, &source, crc);
584 }
585 case SPARSE_READ_MODE_NORMAL:
586 return sparse_file_read_normal(s, fd);
587 case SPARSE_READ_MODE_HOLE:
588 return sparse_file_read_hole(s, fd);
589 default:
590 return -EINVAL;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700591 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700592}
593
Jerry Zhang7b444f02018-06-12 16:42:09 -0700594static struct sparse_file* sparse_file_import_source(SparseFileSource* source, bool verbose,
595 bool crc) {
596 int ret;
597 sparse_header_t sparse_header;
598 int64_t len;
599 struct sparse_file* s;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700600
Jerry Zhang7b444f02018-06-12 16:42:09 -0700601 ret = source->ReadValue(&sparse_header, sizeof(sparse_header));
602 if (ret < 0) {
603 verbose_error(verbose, ret, "header");
Yi Kong17ba95e2018-07-23 16:31:11 -0700604 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700605 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700606
Jerry Zhang7b444f02018-06-12 16:42:09 -0700607 if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
608 verbose_error(verbose, -EINVAL, "header magic");
Yi Kong17ba95e2018-07-23 16:31:11 -0700609 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700610 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700611
Jerry Zhang7b444f02018-06-12 16:42:09 -0700612 if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
613 verbose_error(verbose, -EINVAL, "header major version");
Yi Kong17ba95e2018-07-23 16:31:11 -0700614 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700615 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700616
Jerry Zhang7b444f02018-06-12 16:42:09 -0700617 if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
Yi Kong17ba95e2018-07-23 16:31:11 -0700618 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700619 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700620
Jerry Zhang7b444f02018-06-12 16:42:09 -0700621 if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
Yi Kong17ba95e2018-07-23 16:31:11 -0700622 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700623 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700624
Keith Moka3b72062021-12-31 05:09:32 +0000625 if (!sparse_header.blk_sz || (sparse_header.blk_sz % 4)) {
626 return nullptr;
627 }
628
629 if (!sparse_header.total_blks) {
630 return nullptr;
631 }
632
Jerry Zhang7b444f02018-06-12 16:42:09 -0700633 len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
634 s = sparse_file_new(sparse_header.blk_sz, len);
635 if (!s) {
Yi Kong17ba95e2018-07-23 16:31:11 -0700636 verbose_error(verbose, -EINVAL, nullptr);
637 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700638 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700639
Keith Moka3b72062021-12-31 05:09:32 +0000640 ret = source->Rewind();
Jerry Zhang7b444f02018-06-12 16:42:09 -0700641 if (ret < 0) {
642 verbose_error(verbose, ret, "seeking");
643 sparse_file_destroy(s);
Yi Kong17ba95e2018-07-23 16:31:11 -0700644 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700645 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700646
Jerry Zhang7b444f02018-06-12 16:42:09 -0700647 s->verbose = verbose;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700648
Jerry Zhang7b444f02018-06-12 16:42:09 -0700649 ret = sparse_file_read_sparse(s, source, crc);
650 if (ret < 0) {
651 sparse_file_destroy(s);
Yi Kong17ba95e2018-07-23 16:31:11 -0700652 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700653 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700654
Jerry Zhang7b444f02018-06-12 16:42:09 -0700655 return s;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700656}
657
Jerry Zhang7b444f02018-06-12 16:42:09 -0700658struct sparse_file* sparse_file_import(int fd, bool verbose, bool crc) {
659 SparseFileFdSource source(fd);
660 return sparse_file_import_source(&source, verbose, crc);
Jerry Zhang50e60292018-06-05 11:44:52 -0700661}
662
Keith Moka3b72062021-12-31 05:09:32 +0000663struct sparse_file* sparse_file_import_buf(char* buf, size_t len, bool verbose, bool crc) {
664 SparseFileBufSource source(buf, len);
Jerry Zhang7b444f02018-06-12 16:42:09 -0700665 return sparse_file_import_source(&source, verbose, crc);
Jerry Zhang50e60292018-06-05 11:44:52 -0700666}
667
Jerry Zhang7b444f02018-06-12 16:42:09 -0700668struct sparse_file* sparse_file_import_auto(int fd, bool crc, bool verbose) {
669 struct sparse_file* s;
670 int64_t len;
671 int ret;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700672
Jerry Zhang7b444f02018-06-12 16:42:09 -0700673 s = sparse_file_import(fd, verbose, crc);
674 if (s) {
675 return s;
676 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700677
Jerry Zhang7b444f02018-06-12 16:42:09 -0700678 len = lseek64(fd, 0, SEEK_END);
679 if (len < 0) {
Yi Kong17ba95e2018-07-23 16:31:11 -0700680 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700681 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700682
Jerry Zhang7b444f02018-06-12 16:42:09 -0700683 lseek64(fd, 0, SEEK_SET);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700684
Jerry Zhang7b444f02018-06-12 16:42:09 -0700685 s = sparse_file_new(4096, len);
686 if (!s) {
Yi Kong17ba95e2018-07-23 16:31:11 -0700687 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700688 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700689
Jerry Zhang7b444f02018-06-12 16:42:09 -0700690 ret = sparse_file_read_normal(s, fd);
691 if (ret < 0) {
692 sparse_file_destroy(s);
Yi Kong17ba95e2018-07-23 16:31:11 -0700693 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700694 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700695
Jerry Zhang7b444f02018-06-12 16:42:09 -0700696 return s;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700697}