blob: 1cf8d8dcf0adc414a4c39563b4f9ae576ad6f120 [file] [log] [blame]
Colin Cross28fa5bc2012-05-20 13:28:05 -07001/*
2 * Copyright (C) 2010 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>
Elliott Hughesccecf142014-01-16 10:53:11 -080021#include <inttypes.h>
Colin Cross1e17b312012-05-21 16:35:45 -070022#include <limits.h>
Colin Cross28fa5bc2012-05-20 13:28:05 -070023#include <stdbool.h>
Colin Crossb4cd2672012-05-18 14:49:50 -070024#include <stddef.h>
Colin Cross28fa5bc2012-05-20 13:28:05 -070025#include <stdlib.h>
26#include <string.h>
27#include <sys/stat.h>
28#include <sys/types.h>
29#include <unistd.h>
30#include <zlib.h>
31
Mark Salyzyn031a7482014-02-27 16:56:15 -080032#include "defs.h"
Colin Cross28fa5bc2012-05-20 13:28:05 -070033#include "output_file.h"
34#include "sparse_format.h"
35#include "sparse_crc32.h"
36
37#ifndef USE_MINGW
38#include <sys/mman.h>
39#define O_BINARY 0
Colin Crossb4cd2672012-05-18 14:49:50 -070040#else
41#define ftruncate64 ftruncate
Colin Cross28fa5bc2012-05-20 13:28:05 -070042#endif
43
44#if defined(__APPLE__) && defined(__MACH__)
45#define lseek64 lseek
46#define ftruncate64 ftruncate
47#define mmap64 mmap
48#define off64_t off_t
49#endif
50
Colin Crossb55dcee2012-04-24 23:07:49 -070051#define min(a, b) \
52 ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
53
Colin Cross28fa5bc2012-05-20 13:28:05 -070054#define SPARSE_HEADER_MAJOR_VER 1
55#define SPARSE_HEADER_MINOR_VER 0
56#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
57#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
58
Colin Crossb4cd2672012-05-18 14:49:50 -070059#define container_of(inner, outer_t, elem) \
60 ((outer_t *)((char *)inner - offsetof(outer_t, elem)))
61
Colin Cross28fa5bc2012-05-20 13:28:05 -070062struct output_file_ops {
Colin Crossb4cd2672012-05-18 14:49:50 -070063 int (*open)(struct output_file *, int fd);
Colin Crossb55dcee2012-04-24 23:07:49 -070064 int (*skip)(struct output_file *, int64_t);
Colin Crossb4cd2672012-05-18 14:49:50 -070065 int (*pad)(struct output_file *, int64_t);
Colin Crossb55dcee2012-04-24 23:07:49 -070066 int (*write)(struct output_file *, void *, int);
Colin Cross28fa5bc2012-05-20 13:28:05 -070067 void (*close)(struct output_file *);
68};
69
Colin Crossb55dcee2012-04-24 23:07:49 -070070struct sparse_file_ops {
71 int (*write_data_chunk)(struct output_file *out, unsigned int len,
72 void *data);
73 int (*write_fill_chunk)(struct output_file *out, unsigned int len,
74 uint32_t fill_val);
75 int (*write_skip_chunk)(struct output_file *out, int64_t len);
76 int (*write_end_chunk)(struct output_file *out);
77};
78
Colin Cross28fa5bc2012-05-20 13:28:05 -070079struct output_file {
Colin Cross28fa5bc2012-05-20 13:28:05 -070080 int64_t cur_out_ptr;
Colin Crossb55dcee2012-04-24 23:07:49 -070081 unsigned int chunk_cnt;
82 uint32_t crc32;
Colin Cross28fa5bc2012-05-20 13:28:05 -070083 struct output_file_ops *ops;
Colin Crossb55dcee2012-04-24 23:07:49 -070084 struct sparse_file_ops *sparse_ops;
Colin Cross28fa5bc2012-05-20 13:28:05 -070085 int use_crc;
86 unsigned int block_size;
87 int64_t len;
Colin Crossb55dcee2012-04-24 23:07:49 -070088 char *zero_buf;
89 uint32_t *fill_buf;
Colin Crossb4cd2672012-05-18 14:49:50 -070090 char *buf;
Colin Cross28fa5bc2012-05-20 13:28:05 -070091};
92
Colin Crossb4cd2672012-05-18 14:49:50 -070093struct output_file_gz {
94 struct output_file out;
95 gzFile gz_fd;
96};
97
98#define to_output_file_gz(_o) \
99 container_of((_o), struct output_file_gz, out)
100
101struct output_file_normal {
102 struct output_file out;
103 int fd;
104};
105
106#define to_output_file_normal(_o) \
107 container_of((_o), struct output_file_normal, out)
108
Colin Cross1e17b312012-05-21 16:35:45 -0700109struct output_file_callback {
110 struct output_file out;
111 void *priv;
112 int (*write)(void *priv, const void *buf, int len);
113};
114
115#define to_output_file_callback(_o) \
116 container_of((_o), struct output_file_callback, out)
117
Colin Crossb4cd2672012-05-18 14:49:50 -0700118static int file_open(struct output_file *out, int fd)
119{
120 struct output_file_normal *outn = to_output_file_normal(out);
121
122 outn->fd = fd;
123 return 0;
124}
125
Colin Crossb55dcee2012-04-24 23:07:49 -0700126static int file_skip(struct output_file *out, int64_t cnt)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700127{
128 off64_t ret;
Colin Crossb4cd2672012-05-18 14:49:50 -0700129 struct output_file_normal *outn = to_output_file_normal(out);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700130
Colin Crossb4cd2672012-05-18 14:49:50 -0700131 ret = lseek64(outn->fd, cnt, SEEK_CUR);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700132 if (ret < 0) {
133 error_errno("lseek64");
134 return -1;
135 }
136 return 0;
137}
138
Colin Crossb4cd2672012-05-18 14:49:50 -0700139static int file_pad(struct output_file *out, int64_t len)
140{
141 int ret;
142 struct output_file_normal *outn = to_output_file_normal(out);
143
144 ret = ftruncate64(outn->fd, len);
145 if (ret < 0) {
146 return -errno;
147 }
148
149 return 0;
150}
151
Colin Crossb55dcee2012-04-24 23:07:49 -0700152static int file_write(struct output_file *out, void *data, int len)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700153{
154 int ret;
Colin Crossb4cd2672012-05-18 14:49:50 -0700155 struct output_file_normal *outn = to_output_file_normal(out);
156
157 ret = write(outn->fd, data, len);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700158 if (ret < 0) {
159 error_errno("write");
160 return -1;
161 } else if (ret < len) {
162 error("incomplete write");
163 return -1;
164 }
165
166 return 0;
167}
168
169static void file_close(struct output_file *out)
170{
Colin Crossb4cd2672012-05-18 14:49:50 -0700171 struct output_file_normal *outn = to_output_file_normal(out);
172
173 free(outn);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700174}
175
Colin Cross28fa5bc2012-05-20 13:28:05 -0700176static struct output_file_ops file_ops = {
Colin Crossb4cd2672012-05-18 14:49:50 -0700177 .open = file_open,
Colin Crossb55dcee2012-04-24 23:07:49 -0700178 .skip = file_skip,
Colin Crossb4cd2672012-05-18 14:49:50 -0700179 .pad = file_pad,
Colin Cross28fa5bc2012-05-20 13:28:05 -0700180 .write = file_write,
181 .close = file_close,
182};
183
Colin Crossb4cd2672012-05-18 14:49:50 -0700184static int gz_file_open(struct output_file *out, int fd)
185{
186 struct output_file_gz *outgz = to_output_file_gz(out);
187
188 outgz->gz_fd = gzdopen(fd, "wb9");
189 if (!outgz->gz_fd) {
190 error_errno("gzopen");
191 return -errno;
192 }
193
194 return 0;
195}
196
197
Colin Crossb55dcee2012-04-24 23:07:49 -0700198static int gz_file_skip(struct output_file *out, int64_t cnt)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700199{
200 off64_t ret;
Colin Crossb4cd2672012-05-18 14:49:50 -0700201 struct output_file_gz *outgz = to_output_file_gz(out);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700202
Colin Crossb4cd2672012-05-18 14:49:50 -0700203 ret = gzseek(outgz->gz_fd, cnt, SEEK_CUR);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700204 if (ret < 0) {
205 error_errno("gzseek");
206 return -1;
207 }
208 return 0;
209}
210
Colin Crossb4cd2672012-05-18 14:49:50 -0700211static int gz_file_pad(struct output_file *out, int64_t len)
212{
213 off64_t ret;
214 struct output_file_gz *outgz = to_output_file_gz(out);
215
216 ret = gztell(outgz->gz_fd);
217 if (ret < 0) {
218 return -1;
219 }
220
221 if (ret >= len) {
222 return 0;
223 }
224
225 ret = gzseek(outgz->gz_fd, len - 1, SEEK_SET);
226 if (ret < 0) {
227 return -1;
228 }
229
230 gzwrite(outgz->gz_fd, "", 1);
231
232 return 0;
233}
234
Colin Crossb55dcee2012-04-24 23:07:49 -0700235static int gz_file_write(struct output_file *out, void *data, int len)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700236{
237 int ret;
Colin Crossb4cd2672012-05-18 14:49:50 -0700238 struct output_file_gz *outgz = to_output_file_gz(out);
239
240 ret = gzwrite(outgz->gz_fd, data, len);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700241 if (ret < 0) {
242 error_errno("gzwrite");
243 return -1;
244 } else if (ret < len) {
245 error("incomplete gzwrite");
246 return -1;
247 }
248
249 return 0;
250}
251
252static void gz_file_close(struct output_file *out)
253{
Colin Crossb4cd2672012-05-18 14:49:50 -0700254 struct output_file_gz *outgz = to_output_file_gz(out);
255
256 gzclose(outgz->gz_fd);
257 free(outgz);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700258}
259
260static struct output_file_ops gz_file_ops = {
Colin Crossb4cd2672012-05-18 14:49:50 -0700261 .open = gz_file_open,
Colin Crossb55dcee2012-04-24 23:07:49 -0700262 .skip = gz_file_skip,
Colin Crossb4cd2672012-05-18 14:49:50 -0700263 .pad = gz_file_pad,
Colin Cross28fa5bc2012-05-20 13:28:05 -0700264 .write = gz_file_write,
265 .close = gz_file_close,
266};
267
Mark Salyzyn031a7482014-02-27 16:56:15 -0800268static int callback_file_open(struct output_file *out __unused, int fd __unused)
Colin Cross1e17b312012-05-21 16:35:45 -0700269{
270 return 0;
271}
272
273static int callback_file_skip(struct output_file *out, int64_t off)
274{
275 struct output_file_callback *outc = to_output_file_callback(out);
276 int to_write;
277 int ret;
278
279 while (off > 0) {
280 to_write = min(off, (int64_t)INT_MAX);
281 ret = outc->write(outc->priv, NULL, to_write);
282 if (ret < 0) {
283 return ret;
284 }
285 off -= to_write;
286 }
287
288 return 0;
289}
290
Mark Salyzyn031a7482014-02-27 16:56:15 -0800291static int callback_file_pad(struct output_file *out __unused, int64_t len __unused)
Colin Cross1e17b312012-05-21 16:35:45 -0700292{
293 return -1;
294}
295
296static int callback_file_write(struct output_file *out, void *data, int len)
297{
298 int ret;
299 struct output_file_callback *outc = to_output_file_callback(out);
300
301 return outc->write(outc->priv, data, len);
302}
303
304static void callback_file_close(struct output_file *out)
305{
306 struct output_file_callback *outc = to_output_file_callback(out);
307
308 free(outc);
309}
310
311static struct output_file_ops callback_file_ops = {
312 .open = callback_file_open,
313 .skip = callback_file_skip,
314 .pad = callback_file_pad,
315 .write = callback_file_write,
316 .close = callback_file_close,
317};
318
Colin Cross13a56062012-06-19 16:45:48 -0700319int read_all(int fd, void *buf, size_t len)
320{
321 size_t total = 0;
322 int ret;
323 char *ptr = buf;
324
325 while (total < len) {
326 ret = read(fd, ptr, len - total);
327
328 if (ret < 0)
329 return -errno;
330
331 if (ret == 0)
332 return -EINVAL;
333
334 ptr += ret;
335 total += ret;
336 }
337
338 return 0;
339}
340
Colin Crossb55dcee2012-04-24 23:07:49 -0700341static int write_sparse_skip_chunk(struct output_file *out, int64_t skip_len)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700342{
343 chunk_header_t chunk_header;
344 int ret, chunk;
345
Colin Cross28fa5bc2012-05-20 13:28:05 -0700346 if (skip_len % out->block_size) {
Elliott Hughesccecf142014-01-16 10:53:11 -0800347 error("don't care size %"PRIi64" is not a multiple of the block size %u",
Colin Cross28fa5bc2012-05-20 13:28:05 -0700348 skip_len, out->block_size);
349 return -1;
350 }
351
352 /* We are skipping data, so emit a don't care chunk. */
353 chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE;
354 chunk_header.reserved1 = 0;
355 chunk_header.chunk_sz = skip_len / out->block_size;
356 chunk_header.total_sz = CHUNK_HEADER_LEN;
Colin Crossb55dcee2012-04-24 23:07:49 -0700357 ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
Colin Cross28fa5bc2012-05-20 13:28:05 -0700358 if (ret < 0)
359 return -1;
360
361 out->cur_out_ptr += skip_len;
362 out->chunk_cnt++;
363
364 return 0;
365}
366
Colin Crossb55dcee2012-04-24 23:07:49 -0700367static int write_sparse_fill_chunk(struct output_file *out, unsigned int len,
368 uint32_t fill_val)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700369{
370 chunk_header_t chunk_header;
371 int rnd_up_len, zero_len, count;
372 int ret;
373 unsigned int i;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700374
Colin Crossb55dcee2012-04-24 23:07:49 -0700375 /* Round up the fill length to a multiple of the block size */
376 rnd_up_len = ALIGN(len, out->block_size);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700377
378 /* Finally we can safely emit a chunk of data */
379 chunk_header.chunk_type = CHUNK_TYPE_FILL;
380 chunk_header.reserved1 = 0;
381 chunk_header.chunk_sz = rnd_up_len / out->block_size;
382 chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val);
Colin Crossb55dcee2012-04-24 23:07:49 -0700383 ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
Colin Cross28fa5bc2012-05-20 13:28:05 -0700384
385 if (ret < 0)
386 return -1;
Colin Crossb55dcee2012-04-24 23:07:49 -0700387 ret = out->ops->write(out, &fill_val, sizeof(fill_val));
Colin Cross28fa5bc2012-05-20 13:28:05 -0700388 if (ret < 0)
389 return -1;
390
391 if (out->use_crc) {
Colin Crossb55dcee2012-04-24 23:07:49 -0700392 count = out->block_size / sizeof(uint32_t);
393 while (count--)
394 out->crc32 = sparse_crc32(out->crc32, &fill_val, sizeof(uint32_t));
Colin Cross28fa5bc2012-05-20 13:28:05 -0700395 }
396
397 out->cur_out_ptr += rnd_up_len;
398 out->chunk_cnt++;
399
400 return 0;
401}
402
Colin Crossb55dcee2012-04-24 23:07:49 -0700403static int write_sparse_data_chunk(struct output_file *out, unsigned int len,
404 void *data)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700405{
406 chunk_header_t chunk_header;
407 int rnd_up_len, zero_len;
408 int ret;
409
Colin Crossb55dcee2012-04-24 23:07:49 -0700410 /* Round up the data length to a multiple of the block size */
411 rnd_up_len = ALIGN(len, out->block_size);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700412 zero_len = rnd_up_len - len;
413
414 /* Finally we can safely emit a chunk of data */
415 chunk_header.chunk_type = CHUNK_TYPE_RAW;
416 chunk_header.reserved1 = 0;
417 chunk_header.chunk_sz = rnd_up_len / out->block_size;
418 chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
Colin Crossb55dcee2012-04-24 23:07:49 -0700419 ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
Colin Cross28fa5bc2012-05-20 13:28:05 -0700420
421 if (ret < 0)
422 return -1;
423 ret = out->ops->write(out, data, len);
424 if (ret < 0)
425 return -1;
426 if (zero_len) {
Colin Crossb55dcee2012-04-24 23:07:49 -0700427 ret = out->ops->write(out, out->zero_buf, zero_len);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700428 if (ret < 0)
429 return -1;
430 }
431
432 if (out->use_crc) {
433 out->crc32 = sparse_crc32(out->crc32, data, len);
434 if (zero_len)
Colin Crossb55dcee2012-04-24 23:07:49 -0700435 out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700436 }
437
438 out->cur_out_ptr += rnd_up_len;
439 out->chunk_cnt++;
440
441 return 0;
442}
443
Colin Crossb55dcee2012-04-24 23:07:49 -0700444int write_sparse_end_chunk(struct output_file *out)
445{
446 chunk_header_t chunk_header;
447 int ret;
448
449 if (out->use_crc) {
450 chunk_header.chunk_type = CHUNK_TYPE_CRC32;
451 chunk_header.reserved1 = 0;
452 chunk_header.chunk_sz = 0;
453 chunk_header.total_sz = CHUNK_HEADER_LEN + 4;
454
455 ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
456 if (ret < 0) {
457 return ret;
458 }
459 out->ops->write(out, &out->crc32, 4);
460 if (ret < 0) {
461 return ret;
462 }
463
464 out->chunk_cnt++;
465 }
466
467 return 0;
468}
469
470static struct sparse_file_ops sparse_file_ops = {
471 .write_data_chunk = write_sparse_data_chunk,
472 .write_fill_chunk = write_sparse_fill_chunk,
473 .write_skip_chunk = write_sparse_skip_chunk,
474 .write_end_chunk = write_sparse_end_chunk,
475};
476
477static int write_normal_data_chunk(struct output_file *out, unsigned int len,
478 void *data)
479{
480 int ret;
481 unsigned int rnd_up_len = ALIGN(len, out->block_size);
482
483 ret = out->ops->write(out, data, len);
484 if (ret < 0) {
485 return ret;
486 }
487
488 if (rnd_up_len > len) {
489 ret = out->ops->skip(out, rnd_up_len - len);
490 }
491
492 return ret;
493}
494
495static int write_normal_fill_chunk(struct output_file *out, unsigned int len,
496 uint32_t fill_val)
497{
498 int ret;
499 unsigned int i;
500 unsigned int write_len;
501
502 /* Initialize fill_buf with the fill_val */
503 for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
504 out->fill_buf[i] = fill_val;
505 }
506
507 while (len) {
508 write_len = min(len, out->block_size);
509 ret = out->ops->write(out, out->fill_buf, write_len);
510 if (ret < 0) {
511 return ret;
512 }
513
514 len -= write_len;
515 }
516
517 return 0;
518}
519
520static int write_normal_skip_chunk(struct output_file *out, int64_t len)
521{
Colin Crossb55dcee2012-04-24 23:07:49 -0700522 return out->ops->skip(out, len);
523}
524
525int write_normal_end_chunk(struct output_file *out)
526{
Colin Crossb4cd2672012-05-18 14:49:50 -0700527 return out->ops->pad(out, out->len);
Colin Crossb55dcee2012-04-24 23:07:49 -0700528}
529
530static struct sparse_file_ops normal_file_ops = {
531 .write_data_chunk = write_normal_data_chunk,
532 .write_fill_chunk = write_normal_fill_chunk,
533 .write_skip_chunk = write_normal_skip_chunk,
534 .write_end_chunk = write_normal_end_chunk,
535};
536
Colin Crossb43828b2012-06-08 16:55:35 -0700537void output_file_close(struct output_file *out)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700538{
539 int ret;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700540
Colin Crossb55dcee2012-04-24 23:07:49 -0700541 out->sparse_ops->write_end_chunk(out);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700542 out->ops->close(out);
543}
544
Colin Crossb4cd2672012-05-18 14:49:50 -0700545static int output_file_init(struct output_file *out, int block_size,
546 int64_t len, bool sparse, int chunks, bool crc)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700547{
548 int ret;
Colin Crossb4cd2672012-05-18 14:49:50 -0700549
550 out->len = len;
551 out->block_size = block_size;
552 out->cur_out_ptr = 0ll;
553 out->chunk_cnt = 0;
554 out->crc32 = 0;
555 out->use_crc = crc;
556
Colin Crossb55dcee2012-04-24 23:07:49 -0700557 out->zero_buf = calloc(block_size, 1);
558 if (!out->zero_buf) {
Colin Cross28fa5bc2012-05-20 13:28:05 -0700559 error_errno("malloc zero_buf");
Colin Crossb4cd2672012-05-18 14:49:50 -0700560 return -ENOMEM;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700561 }
Colin Crossb55dcee2012-04-24 23:07:49 -0700562
563 out->fill_buf = calloc(block_size, 1);
564 if (!out->fill_buf) {
565 error_errno("malloc fill_buf");
Colin Crossb4cd2672012-05-18 14:49:50 -0700566 ret = -ENOMEM;
Colin Crossb55dcee2012-04-24 23:07:49 -0700567 goto err_fill_buf;
568 }
Colin Cross28fa5bc2012-05-20 13:28:05 -0700569
Colin Crossb55dcee2012-04-24 23:07:49 -0700570 if (sparse) {
571 out->sparse_ops = &sparse_file_ops;
572 } else {
573 out->sparse_ops = &normal_file_ops;
574 }
575
Colin Crossb55dcee2012-04-24 23:07:49 -0700576 if (sparse) {
577 sparse_header_t sparse_header = {
578 .magic = SPARSE_HEADER_MAGIC,
579 .major_version = SPARSE_HEADER_MAJOR_VER,
580 .minor_version = SPARSE_HEADER_MINOR_VER,
581 .file_hdr_sz = SPARSE_HEADER_LEN,
582 .chunk_hdr_sz = CHUNK_HEADER_LEN,
583 .blk_sz = out->block_size,
584 .total_blks = out->len / out->block_size,
585 .total_chunks = chunks,
586 .image_checksum = 0
587 };
Colin Cross28fa5bc2012-05-20 13:28:05 -0700588
Colin Crossb55dcee2012-04-24 23:07:49 -0700589 if (out->use_crc) {
590 sparse_header.total_chunks++;
591 }
592
593 ret = out->ops->write(out, &sparse_header, sizeof(sparse_header));
594 if (ret < 0) {
595 goto err_write;
596 }
Colin Cross28fa5bc2012-05-20 13:28:05 -0700597 }
598
Colin Crossb4cd2672012-05-18 14:49:50 -0700599 return 0;
Colin Crossb55dcee2012-04-24 23:07:49 -0700600
601err_write:
Colin Crossb55dcee2012-04-24 23:07:49 -0700602 free(out->fill_buf);
603err_fill_buf:
604 free(out->zero_buf);
Colin Crossb4cd2672012-05-18 14:49:50 -0700605 return ret;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700606}
607
Colin Crossb4cd2672012-05-18 14:49:50 -0700608static struct output_file *output_file_new_gz(void)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700609{
Colin Crossb4cd2672012-05-18 14:49:50 -0700610 struct output_file_gz *outgz = calloc(1, sizeof(struct output_file_gz));
611 if (!outgz) {
612 error_errno("malloc struct outgz");
Colin Cross28fa5bc2012-05-20 13:28:05 -0700613 return NULL;
614 }
615
Colin Crossb4cd2672012-05-18 14:49:50 -0700616 outgz->out.ops = &gz_file_ops;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700617
Colin Crossb4cd2672012-05-18 14:49:50 -0700618 return &outgz->out;
619}
620
621static struct output_file *output_file_new_normal(void)
622{
623 struct output_file_normal *outn = calloc(1, sizeof(struct output_file_normal));
624 if (!outn) {
625 error_errno("malloc struct outn");
626 return NULL;
627 }
628
629 outn->out.ops = &file_ops;
630
631 return &outn->out;
632}
633
Colin Crossb43828b2012-06-08 16:55:35 -0700634struct output_file *output_file_open_callback(int (*write)(void *, const void *, int),
Mark Salyzyn031a7482014-02-27 16:56:15 -0800635 void *priv, unsigned int block_size, int64_t len,
636 int gz __unused, int sparse, int chunks, int crc)
Colin Cross1e17b312012-05-21 16:35:45 -0700637{
638 int ret;
639 struct output_file_callback *outc;
640
641 outc = calloc(1, sizeof(struct output_file_callback));
642 if (!outc) {
643 error_errno("malloc struct outc");
644 return NULL;
645 }
646
647 outc->out.ops = &callback_file_ops;
648 outc->priv = priv;
649 outc->write = write;
650
651 ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc);
652 if (ret < 0) {
653 free(outc);
654 return NULL;
655 }
656
657 return &outc->out;
658}
659
Colin Crossb43828b2012-06-08 16:55:35 -0700660struct output_file *output_file_open_fd(int fd, unsigned int block_size, int64_t len,
Colin Crossb4cd2672012-05-18 14:49:50 -0700661 int gz, int sparse, int chunks, int crc)
662{
663 int ret;
664 struct output_file *out;
665
666 if (gz) {
667 out = output_file_new_gz();
668 } else {
669 out = output_file_new_normal();
670 }
Hong-Mei Li83a6d362013-04-01 11:22:50 +0800671 if (!out) {
672 return NULL;
673 }
Colin Crossb4cd2672012-05-18 14:49:50 -0700674
675 out->ops->open(out, fd);
676
677 ret = output_file_init(out, block_size, len, sparse, chunks, crc);
678 if (ret < 0) {
679 free(out);
680 return NULL;
681 }
682
683 return out;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700684}
685
Colin Cross28fa5bc2012-05-20 13:28:05 -0700686/* Write a contiguous region of data blocks from a memory buffer */
Colin Crossb55dcee2012-04-24 23:07:49 -0700687int write_data_chunk(struct output_file *out, unsigned int len, void *data)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700688{
Colin Crossb55dcee2012-04-24 23:07:49 -0700689 return out->sparse_ops->write_data_chunk(out, len, data);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700690}
691
692/* Write a contiguous region of data blocks with a fill value */
Colin Crossb55dcee2012-04-24 23:07:49 -0700693int write_fill_chunk(struct output_file *out, unsigned int len,
694 uint32_t fill_val)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700695{
Colin Crossb55dcee2012-04-24 23:07:49 -0700696 return out->sparse_ops->write_fill_chunk(out, len, fill_val);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700697}
698
Colin Cross9e1f17e2012-04-25 18:31:39 -0700699int write_fd_chunk(struct output_file *out, unsigned int len,
700 int fd, int64_t offset)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700701{
702 int ret;
703 int64_t aligned_offset;
704 int aligned_diff;
705 int buffer_size;
Colin Cross13a56062012-06-19 16:45:48 -0700706 char *ptr;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700707
Colin Cross28fa5bc2012-05-20 13:28:05 -0700708 aligned_offset = offset & ~(4096 - 1);
709 aligned_diff = offset - aligned_offset;
710 buffer_size = len + aligned_diff;
711
712#ifndef USE_MINGW
Colin Cross9e1f17e2012-04-25 18:31:39 -0700713 char *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, fd,
Colin Cross28fa5bc2012-05-20 13:28:05 -0700714 aligned_offset);
715 if (data == MAP_FAILED) {
Colin Cross9e1f17e2012-04-25 18:31:39 -0700716 return -errno;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700717 }
Colin Cross13a56062012-06-19 16:45:48 -0700718 ptr = data + aligned_diff;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700719#else
Colin Cross13a56062012-06-19 16:45:48 -0700720 off64_t pos;
721 char *data = malloc(len);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700722 if (!data) {
Colin Cross9e1f17e2012-04-25 18:31:39 -0700723 return -errno;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700724 }
Colin Cross13a56062012-06-19 16:45:48 -0700725 pos = lseek64(fd, offset, SEEK_SET);
726 if (pos < 0) {
Elliott Hughes14e28d32013-10-29 14:12:46 -0700727 free(data);
Colin Cross13a56062012-06-19 16:45:48 -0700728 return -errno;
729 }
730 ret = read_all(fd, data, len);
731 if (ret < 0) {
Elliott Hughes14e28d32013-10-29 14:12:46 -0700732 free(data);
Colin Cross13a56062012-06-19 16:45:48 -0700733 return ret;
734 }
735 ptr = data;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700736#endif
737
Colin Cross13a56062012-06-19 16:45:48 -0700738 ret = out->sparse_ops->write_data_chunk(out, len, ptr);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700739
Colin Cross28fa5bc2012-05-20 13:28:05 -0700740#ifndef USE_MINGW
741 munmap(data, buffer_size);
742#else
Colin Cross28fa5bc2012-05-20 13:28:05 -0700743 free(data);
744#endif
Colin Cross9e1f17e2012-04-25 18:31:39 -0700745
746 return ret;
747}
748
749/* Write a contiguous region of data blocks from a file */
750int write_file_chunk(struct output_file *out, unsigned int len,
751 const char *file, int64_t offset)
752{
753 int ret;
754
755 int file_fd = open(file, O_RDONLY | O_BINARY);
756 if (file_fd < 0) {
757 return -errno;
758 }
759
760 ret = write_fd_chunk(out, len, file_fd, offset);
761
Colin Cross28fa5bc2012-05-20 13:28:05 -0700762 close(file_fd);
Colin Crossb55dcee2012-04-24 23:07:49 -0700763
764 return ret;
765}
766
767int write_skip_chunk(struct output_file *out, int64_t len)
768{
769 return out->sparse_ops->write_skip_chunk(out, len);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700770}