blob: 9e564dcd2496481c1379d8e43e5ea208b0587306 [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:
61 /* Seeks the source ahead by the given offset. */
62 virtual void Seek(int64_t offset) = 0;
Jerry Zhang50e60292018-06-05 11:44:52 -070063
Jerry Zhang7b444f02018-06-12 16:42:09 -070064 /* Return the current offset. */
65 virtual int64_t GetOffset() = 0;
Jerry Zhang50e60292018-06-05 11:44:52 -070066
Jerry Zhang7b444f02018-06-12 16:42:09 -070067 /* Set the current offset. Return 0 if successful. */
68 virtual int SetOffset(int64_t offset) = 0;
Jerry Zhang50e60292018-06-05 11:44:52 -070069
Jerry Zhang7b444f02018-06-12 16:42:09 -070070 /* 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;
Jerry Zhang50e60292018-06-05 11:44:52 -070073
Jerry Zhang7b444f02018-06-12 16:42:09 -070074 /* 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;
Jerry Zhang50e60292018-06-05 11:44:52 -070076
Jerry Zhang7b444f02018-06-12 16:42:09 -070077 /* 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;
Jerry Zhang50e60292018-06-05 11:44:52 -070079
Jerry Zhang7b444f02018-06-12 16:42:09 -070080 virtual ~SparseFileSource(){};
Jerry Zhang50e60292018-06-05 11:44:52 -070081};
82
83class SparseFileFdSource : public SparseFileSource {
Jerry Zhang7b444f02018-06-12 16:42:09 -070084 private:
85 int fd;
Jerry Zhang50e60292018-06-05 11:44:52 -070086
Jerry Zhang7b444f02018-06-12 16:42:09 -070087 public:
88 SparseFileFdSource(int fd) : fd(fd) {}
89 ~SparseFileFdSource() override {}
Jerry Zhang50e60292018-06-05 11:44:52 -070090
Jerry Zhang7b444f02018-06-12 16:42:09 -070091 void Seek(int64_t off) override { lseek64(fd, off, SEEK_CUR); }
Jerry Zhang50e60292018-06-05 11:44:52 -070092
Jerry Zhang7b444f02018-06-12 16:42:09 -070093 int64_t GetOffset() override { return lseek64(fd, 0, SEEK_CUR); }
Jerry Zhang50e60292018-06-05 11:44:52 -070094
Jerry Zhang7b444f02018-06-12 16:42:09 -070095 int SetOffset(int64_t offset) override {
96 return lseek64(fd, offset, SEEK_SET) == offset ? 0 : -errno;
97 }
Jerry Zhang50e60292018-06-05 11:44:52 -070098
Jerry Zhang7b444f02018-06-12 16:42:09 -070099 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 }
Jerry Zhang50e60292018-06-05 11:44:52 -0700102
Jerry Zhang7b444f02018-06-12 16:42:09 -0700103 int ReadValue(void* ptr, int len) override { return read_all(fd, ptr, len); }
104
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 }
Jerry Zhang50e60292018-06-05 11:44:52 -0700119};
120
121class SparseFileBufSource : public SparseFileSource {
Jerry Zhang7b444f02018-06-12 16:42:09 -0700122 private:
123 char* buf;
124 int64_t offset;
Jerry Zhang50e60292018-06-05 11:44:52 -0700125
Jerry Zhang7b444f02018-06-12 16:42:09 -0700126 public:
127 SparseFileBufSource(char* buf) : buf(buf), offset(0) {}
128 ~SparseFileBufSource() override {}
Jerry Zhang50e60292018-06-05 11:44:52 -0700129
Jerry Zhang7b444f02018-06-12 16:42:09 -0700130 void Seek(int64_t off) override {
131 buf += off;
132 offset += off;
133 }
Jerry Zhang50e60292018-06-05 11:44:52 -0700134
Jerry Zhang7b444f02018-06-12 16:42:09 -0700135 int64_t GetOffset() override { return offset; }
Jerry Zhang50e60292018-06-05 11:44:52 -0700136
Jerry Zhang7b444f02018-06-12 16:42:09 -0700137 int SetOffset(int64_t off) override {
138 buf += off - offset;
139 offset = off;
140 return 0;
141 }
Jerry Zhang50e60292018-06-05 11:44:52 -0700142
Jerry Zhang7b444f02018-06-12 16:42:09 -0700143 int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
144 return sparse_file_add_data(s, buf, len, block);
145 }
Jerry Zhang50e60292018-06-05 11:44:52 -0700146
Jerry Zhang7b444f02018-06-12 16:42:09 -0700147 int ReadValue(void* ptr, int len) override {
148 memcpy(ptr, buf, len);
149 Seek(len);
150 return 0;
151 }
152
153 int GetCrc32(uint32_t* crc32, int64_t len) override {
154 *crc32 = sparse_crc32(*crc32, buf, len);
155 Seek(len);
156 return 0;
157 }
Jerry Zhang50e60292018-06-05 11:44:52 -0700158};
159
Jerry Zhang7b444f02018-06-12 16:42:09 -0700160static void verbose_error(bool verbose, int err, const char* fmt, ...) {
161 if (!verbose) return;
Chris Friesa7eeb222017-04-17 21:53:16 -0500162
Jerry Zhang7b444f02018-06-12 16:42:09 -0700163 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());
Colin Cross0c4c47f2012-04-25 19:02:58 -0700172}
173
Jerry Zhang7b444f02018-06-12 16:42:09 -0700174static 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;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700179
Jerry Zhang7b444f02018-06-12 16:42:09 -0700180 if (chunk_size % s->block_size != 0) {
181 return -EINVAL;
182 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700183
Jerry Zhang7b444f02018-06-12 16:42:09 -0700184 if (chunk_size / s->block_size != blocks) {
185 return -EINVAL;
186 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700187
Jerry Zhang7b444f02018-06-12 16:42:09 -0700188 ret = source->AddToSparseFile(s, len, block);
189 if (ret < 0) {
190 return ret;
191 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700192
Jerry Zhang7b444f02018-06-12 16:42:09 -0700193 if (crc32) {
194 ret = source->GetCrc32(crc32, len);
195 if (ret < 0) {
196 return ret;
197 }
198 } else {
199 source->Seek(len);
200 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700201
Jerry Zhang7b444f02018-06-12 16:42:09 -0700202 return 0;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700203}
204
Jerry Zhang7b444f02018-06-12 16:42:09 -0700205static 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;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700214
Jerry Zhang7b444f02018-06-12 16:42:09 -0700215 if (chunk_size != sizeof(fill_val)) {
216 return -EINVAL;
217 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700218
Jerry Zhang7b444f02018-06-12 16:42:09 -0700219 ret = source->ReadValue(&fill_val, sizeof(fill_val));
220 if (ret < 0) {
221 return ret;
222 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700223
Jerry Zhang7b444f02018-06-12 16:42:09 -0700224 ret = sparse_file_add_fill(s, fill_val, len, block);
225 if (ret < 0) {
226 return ret;
227 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700228
Jerry Zhang7b444f02018-06-12 16:42:09 -0700229 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 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700235
Jerry Zhang7b444f02018-06-12 16:42:09 -0700236 while (len) {
237 chunk = std::min(len, COPY_BUF_SIZE);
238 *crc32 = sparse_crc32(*crc32, copybuf, chunk);
239 len -= chunk;
240 }
241 }
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_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 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700252
Jerry Zhang7b444f02018-06-12 16:42:09 -0700253 if (crc32) {
254 int64_t len = (int64_t)blocks * s->block_size;
255 memset(copybuf, 0, COPY_BUF_SIZE);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700256
Jerry Zhang7b444f02018-06-12 16:42:09 -0700257 while (len) {
258 int chunk = std::min(len, COPY_BUF_SIZE);
259 *crc32 = sparse_crc32(*crc32, copybuf, chunk);
260 len -= chunk;
261 }
262 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700263
Jerry Zhang7b444f02018-06-12 16:42:09 -0700264 return 0;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700265}
266
Jerry Zhang7b444f02018-06-12 16:42:09 -0700267static int process_crc32_chunk(SparseFileSource* source, unsigned int chunk_size, uint32_t* crc32) {
268 uint32_t file_crc32;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700269
Jerry Zhang7b444f02018-06-12 16:42:09 -0700270 if (chunk_size != sizeof(file_crc32)) {
271 return -EINVAL;
272 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700273
Jerry Zhang7b444f02018-06-12 16:42:09 -0700274 int ret = source->ReadValue(&file_crc32, sizeof(file_crc32));
275 if (ret < 0) {
276 return ret;
277 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700278
Yi Kong17ba95e2018-07-23 16:31:11 -0700279 if (crc32 != nullptr && file_crc32 != *crc32) {
Jerry Zhang7b444f02018-06-12 16:42:09 -0700280 return -EINVAL;
281 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700282
Jerry Zhang7b444f02018-06-12 16:42:09 -0700283 return 0;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700284}
285
Jerry Zhang7b444f02018-06-12 16:42:09 -0700286static 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();
Colin Cross0c4c47f2012-04-25 19:02:58 -0700291
Jerry Zhang7b444f02018-06-12 16:42:09 -0700292 chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700293
Jerry Zhang7b444f02018-06-12 16:42:09 -0700294 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 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700332
Jerry Zhang7b444f02018-06-12 16:42:09 -0700333 return 0;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700334}
335
Jerry Zhang7b444f02018-06-12 16:42:09 -0700336static 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;
Yi Kong17ba95e2018-07-23 16:31:11 -0700342 uint32_t* crc_ptr = nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700343 unsigned int cur_block = 0;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700344
Jerry Zhang7b444f02018-06-12 16:42:09 -0700345 if (!copybuf) {
346 copybuf = (char*)malloc(COPY_BUF_SIZE);
347 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700348
Jerry Zhang7b444f02018-06-12 16:42:09 -0700349 if (!copybuf) {
350 return -ENOMEM;
351 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700352
Jerry Zhang7b444f02018-06-12 16:42:09 -0700353 if (crc) {
354 crc_ptr = &crc32;
355 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700356
Jerry Zhang7b444f02018-06-12 16:42:09 -0700357 ret = source->ReadValue(&sparse_header, sizeof(sparse_header));
358 if (ret < 0) {
359 return ret;
360 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700361
Jerry Zhang7b444f02018-06-12 16:42:09 -0700362 if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
363 return -EINVAL;
364 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700365
Jerry Zhang7b444f02018-06-12 16:42:09 -0700366 if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
367 return -EINVAL;
368 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700369
Jerry Zhang7b444f02018-06-12 16:42:09 -0700370 if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
371 return -EINVAL;
372 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700373
Jerry Zhang7b444f02018-06-12 16:42:09 -0700374 if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {
375 return -EINVAL;
376 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700377
Jerry Zhang7b444f02018-06-12 16:42:09 -0700378 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 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700384
Jerry Zhang7b444f02018-06-12 16:42:09 -0700385 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 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700390
Jerry Zhang7b444f02018-06-12 16:42:09 -0700391 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 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700397
Jerry Zhang7b444f02018-06-12 16:42:09 -0700398 ret = process_chunk(s, source, sparse_header.chunk_hdr_sz, &chunk_header, cur_block, crc_ptr);
399 if (ret < 0) {
400 return ret;
401 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700402
Jerry Zhang7b444f02018-06-12 16:42:09 -0700403 cur_block += ret;
404 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700405
Jerry Zhang7b444f02018-06-12 16:42:09 -0700406 if (sparse_header.total_blks != cur_block) {
407 return -EINVAL;
408 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700409
Jerry Zhang7b444f02018-06-12 16:42:09 -0700410 return 0;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700411}
412
Sean Anderson95657f32021-12-30 15:16:08 -0500413static int do_sparse_file_read_normal(struct sparse_file* s, int fd, uint32_t* buf, int64_t offset,
414 int64_t remain) {
Jerry Zhang7b444f02018-06-12 16:42:09 -0700415 int ret;
Sean Anderson95657f32021-12-30 15:16:08 -0500416 unsigned int block = offset / s->block_size;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700417 unsigned int to_read;
418 unsigned int i;
419 bool sparse_block;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700420
Jerry Zhang7b444f02018-06-12 16:42:09 -0700421 if (!buf) {
422 return -ENOMEM;
423 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700424
Jerry Zhang7b444f02018-06-12 16:42:09 -0700425 while (remain > 0) {
426 to_read = std::min(remain, (int64_t)(s->block_size));
427 ret = read_all(fd, buf, to_read);
428 if (ret < 0) {
429 error("failed to read sparse file");
Jerry Zhang7b444f02018-06-12 16:42:09 -0700430 return ret;
431 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700432
Jerry Zhang7b444f02018-06-12 16:42:09 -0700433 if (to_read == s->block_size) {
434 sparse_block = true;
435 for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {
436 if (buf[0] != buf[i]) {
437 sparse_block = false;
438 break;
439 }
440 }
441 } else {
442 sparse_block = false;
443 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700444
Jerry Zhang7b444f02018-06-12 16:42:09 -0700445 if (sparse_block) {
446 /* TODO: add flag to use skip instead of fill for buf[0] == 0 */
447 sparse_file_add_fill(s, buf[0], to_read, block);
448 } else {
449 sparse_file_add_fd(s, fd, offset, to_read, block);
450 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700451
Jerry Zhang7b444f02018-06-12 16:42:09 -0700452 remain -= to_read;
453 offset += to_read;
454 block++;
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 sparse_file_read_normal(struct sparse_file* s, int fd) {
461 int ret;
462 uint32_t* buf = (uint32_t*)malloc(s->block_size);
463
464 if (!buf)
465 return -ENOMEM;
466
467 ret = do_sparse_file_read_normal(s, fd, buf, 0, s->len);
468 free(buf);
469 return ret;
470}
471
Sean Andersonf96466b2021-12-30 15:19:41 -0500472#ifdef __linux__
473static int sparse_file_read_hole(struct sparse_file* s, int fd) {
474 int ret;
475 uint32_t* buf = (uint32_t*)malloc(s->block_size);
476 int64_t end = 0;
477 int64_t start = 0;
478
479 if (!buf) {
480 return -ENOMEM;
481 }
482
483 do {
484 start = lseek(fd, end, SEEK_DATA);
485 if (start < 0) {
486 if (errno == ENXIO)
487 /* The rest of the file is a hole */
488 break;
489
490 error("could not seek to data");
491 free(buf);
492 return -errno;
493 } else if (start > s->len) {
494 break;
495 }
496
497 end = lseek(fd, start, SEEK_HOLE);
498 if (end < 0) {
499 error("could not seek to end");
500 free(buf);
501 return -errno;
502 }
503 end = std::min(end, s->len);
504
505 start = ALIGN_DOWN(start, s->block_size);
506 end = ALIGN(end, s->block_size);
507 if (lseek(fd, start, SEEK_SET) < 0) {
508 free(buf);
509 return -errno;
510 }
511
512 ret = do_sparse_file_read_normal(s, fd, buf, start, end - start);
513 if (ret) {
514 free(buf);
515 return ret;
516 }
517 } while (end < s->len);
518
519 free(buf);
520 return 0;
521}
522#else
523static int sparse_file_read_hole(struct sparse_file* s __unused, int fd __unused) {
524 return -ENOTSUP;
525}
526#endif
527
528int sparse_file_read(struct sparse_file* s, int fd, enum sparse_read_mode mode, bool crc) {
529 if (crc && mode != SPARSE_READ_MODE_SPARSE) {
Jerry Zhang7b444f02018-06-12 16:42:09 -0700530 return -EINVAL;
531 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700532
Sean Andersonf96466b2021-12-30 15:19:41 -0500533 switch (mode) {
534 case SPARSE_READ_MODE_SPARSE: {
535 SparseFileFdSource source(fd);
536 return sparse_file_read_sparse(s, &source, crc);
537 }
538 case SPARSE_READ_MODE_NORMAL:
539 return sparse_file_read_normal(s, fd);
540 case SPARSE_READ_MODE_HOLE:
541 return sparse_file_read_hole(s, fd);
542 default:
543 return -EINVAL;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700544 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700545}
546
Jerry Zhang7b444f02018-06-12 16:42:09 -0700547int sparse_file_read_buf(struct sparse_file* s, char* buf, bool crc) {
548 SparseFileBufSource source(buf);
549 return sparse_file_read_sparse(s, &source, crc);
Jerry Zhang50e60292018-06-05 11:44:52 -0700550}
551
Jerry Zhang7b444f02018-06-12 16:42:09 -0700552static struct sparse_file* sparse_file_import_source(SparseFileSource* source, bool verbose,
553 bool crc) {
554 int ret;
555 sparse_header_t sparse_header;
556 int64_t len;
557 struct sparse_file* s;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700558
Jerry Zhang7b444f02018-06-12 16:42:09 -0700559 ret = source->ReadValue(&sparse_header, sizeof(sparse_header));
560 if (ret < 0) {
561 verbose_error(verbose, ret, "header");
Yi Kong17ba95e2018-07-23 16:31:11 -0700562 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700563 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700564
Jerry Zhang7b444f02018-06-12 16:42:09 -0700565 if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
566 verbose_error(verbose, -EINVAL, "header magic");
Yi Kong17ba95e2018-07-23 16:31:11 -0700567 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700568 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700569
Jerry Zhang7b444f02018-06-12 16:42:09 -0700570 if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
571 verbose_error(verbose, -EINVAL, "header major version");
Yi Kong17ba95e2018-07-23 16:31:11 -0700572 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700573 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700574
Jerry Zhang7b444f02018-06-12 16:42:09 -0700575 if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
Yi Kong17ba95e2018-07-23 16:31:11 -0700576 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700577 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700578
Jerry Zhang7b444f02018-06-12 16:42:09 -0700579 if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
Yi Kong17ba95e2018-07-23 16:31:11 -0700580 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700581 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700582
Jerry Zhang7b444f02018-06-12 16:42:09 -0700583 len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
584 s = sparse_file_new(sparse_header.blk_sz, len);
585 if (!s) {
Yi Kong17ba95e2018-07-23 16:31:11 -0700586 verbose_error(verbose, -EINVAL, nullptr);
587 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700588 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700589
Jerry Zhang7b444f02018-06-12 16:42:09 -0700590 ret = source->SetOffset(0);
591 if (ret < 0) {
592 verbose_error(verbose, ret, "seeking");
593 sparse_file_destroy(s);
Yi Kong17ba95e2018-07-23 16:31:11 -0700594 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700595 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700596
Jerry Zhang7b444f02018-06-12 16:42:09 -0700597 s->verbose = verbose;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700598
Jerry Zhang7b444f02018-06-12 16:42:09 -0700599 ret = sparse_file_read_sparse(s, source, crc);
600 if (ret < 0) {
601 sparse_file_destroy(s);
Yi Kong17ba95e2018-07-23 16:31:11 -0700602 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700603 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700604
Jerry Zhang7b444f02018-06-12 16:42:09 -0700605 return s;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700606}
607
Jerry Zhang7b444f02018-06-12 16:42:09 -0700608struct sparse_file* sparse_file_import(int fd, bool verbose, bool crc) {
609 SparseFileFdSource source(fd);
610 return sparse_file_import_source(&source, verbose, crc);
Jerry Zhang50e60292018-06-05 11:44:52 -0700611}
612
Jerry Zhang7b444f02018-06-12 16:42:09 -0700613struct sparse_file* sparse_file_import_buf(char* buf, bool verbose, bool crc) {
614 SparseFileBufSource source(buf);
615 return sparse_file_import_source(&source, verbose, crc);
Jerry Zhang50e60292018-06-05 11:44:52 -0700616}
617
Jerry Zhang7b444f02018-06-12 16:42:09 -0700618struct sparse_file* sparse_file_import_auto(int fd, bool crc, bool verbose) {
619 struct sparse_file* s;
620 int64_t len;
621 int ret;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700622
Jerry Zhang7b444f02018-06-12 16:42:09 -0700623 s = sparse_file_import(fd, verbose, crc);
624 if (s) {
625 return s;
626 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700627
Jerry Zhang7b444f02018-06-12 16:42:09 -0700628 len = lseek64(fd, 0, SEEK_END);
629 if (len < 0) {
Yi Kong17ba95e2018-07-23 16:31:11 -0700630 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700631 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700632
Jerry Zhang7b444f02018-06-12 16:42:09 -0700633 lseek64(fd, 0, SEEK_SET);
Colin Cross0c4c47f2012-04-25 19:02:58 -0700634
Jerry Zhang7b444f02018-06-12 16:42:09 -0700635 s = sparse_file_new(4096, len);
636 if (!s) {
Yi Kong17ba95e2018-07-23 16:31:11 -0700637 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700638 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700639
Jerry Zhang7b444f02018-06-12 16:42:09 -0700640 ret = sparse_file_read_normal(s, fd);
641 if (ret < 0) {
642 sparse_file_destroy(s);
Yi Kong17ba95e2018-07-23 16:31:11 -0700643 return nullptr;
Jerry Zhang7b444f02018-06-12 16:42:09 -0700644 }
Colin Cross0c4c47f2012-04-25 19:02:58 -0700645
Jerry Zhang7b444f02018-06-12 16:42:09 -0700646 return s;
Colin Cross0c4c47f2012-04-25 19:02:58 -0700647}