• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 <algorithm>
21 #include <fcntl.h>
22 #include <inttypes.h>
23 #include <limits.h>
24 #include <stdbool.h>
25 #include <stddef.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31 #include <zlib.h>
32 
33 #include "defs.h"
34 #include "output_file.h"
35 #include "sparse_crc32.h"
36 #include "sparse_format.h"
37 
38 #include <android-base/mapped_file.h>
39 
40 #ifndef _WIN32
41 #define O_BINARY 0
42 #else
43 #define ftruncate64 ftruncate
44 #endif
45 
46 #if defined(__APPLE__) && defined(__MACH__)
47 #define lseek64 lseek
48 #define ftruncate64 ftruncate
49 #define off64_t off_t
50 #endif
51 
52 #define SPARSE_HEADER_MAJOR_VER 1
53 #define SPARSE_HEADER_MINOR_VER 0
54 #define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
55 #define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
56 
57 #define FILL_ZERO_BUFSIZE (2 * 1024 * 1024)
58 
59 #define container_of(inner, outer_t, elem) ((outer_t*)((char*)(inner)-offsetof(outer_t, elem)))
60 
61 static constexpr size_t kMaxMmapSize = 256 * 1024 * 1024;
62 
63 struct output_file_ops {
64   int (*open)(struct output_file*, int fd);
65   int (*skip)(struct output_file*, int64_t);
66   int (*pad)(struct output_file*, int64_t);
67   int (*write)(struct output_file*, void*, size_t);
68   void (*close)(struct output_file*);
69 };
70 
71 struct sparse_file_ops {
72   int (*write_data_chunk)(struct output_file* out, uint64_t len, void* data);
73   int (*write_fill_chunk)(struct output_file* out, uint64_t len, uint32_t fill_val);
74   int (*write_skip_chunk)(struct output_file* out, uint64_t len);
75   int (*write_end_chunk)(struct output_file* out);
76   int (*write_fd_chunk)(struct output_file* out, uint64_t len, int fd, int64_t offset);
77 };
78 
79 struct output_file {
80   int64_t cur_out_ptr;
81   unsigned int chunk_cnt;
82   uint32_t crc32;
83   struct output_file_ops* ops;
84   struct sparse_file_ops* sparse_ops;
85   int use_crc;
86   unsigned int block_size;
87   int64_t len;
88   char* zero_buf;
89   uint32_t* fill_buf;
90   char* buf;
91 };
92 
93 struct output_file_gz {
94   struct output_file out;
95   gzFile gz_fd;
96 };
97 
98 #define to_output_file_gz(_o) container_of((_o), struct output_file_gz, out)
99 
100 struct output_file_normal {
101   struct output_file out;
102   int fd;
103 };
104 
105 #define to_output_file_normal(_o) container_of((_o), struct output_file_normal, out)
106 
107 struct output_file_callback {
108   struct output_file out;
109   void* priv;
110   int (*write)(void* priv, const void* buf, size_t len);
111 };
112 
113 #define to_output_file_callback(_o) container_of((_o), struct output_file_callback, out)
114 
file_open(struct output_file * out,int fd)115 static int file_open(struct output_file* out, int fd) {
116   struct output_file_normal* outn = to_output_file_normal(out);
117 
118   outn->fd = fd;
119   return 0;
120 }
121 
file_skip(struct output_file * out,int64_t cnt)122 static int file_skip(struct output_file* out, int64_t cnt) {
123   off64_t ret;
124   struct output_file_normal* outn = to_output_file_normal(out);
125 
126   ret = lseek64(outn->fd, cnt, SEEK_CUR);
127   if (ret < 0) {
128     error_errno("lseek64");
129     return -1;
130   }
131   return 0;
132 }
133 
file_pad(struct output_file * out,int64_t len)134 static int file_pad(struct output_file* out, int64_t len) {
135   int ret;
136   struct output_file_normal* outn = to_output_file_normal(out);
137 
138   ret = ftruncate64(outn->fd, len);
139   if (ret < 0) {
140     return -errno;
141   }
142 
143   return 0;
144 }
145 
file_write(struct output_file * out,void * data,size_t len)146 static int file_write(struct output_file* out, void* data, size_t len) {
147   ssize_t ret;
148   struct output_file_normal* outn = to_output_file_normal(out);
149 
150   while (len > 0) {
151     ret = write(outn->fd, data, len);
152     if (ret < 0) {
153       if (errno == EINTR) {
154         continue;
155       }
156       error_errno("write");
157       return -1;
158     }
159 
160     data = (char*)data + ret;
161     len -= ret;
162   }
163 
164   return 0;
165 }
166 
file_close(struct output_file * out)167 static void file_close(struct output_file* out) {
168   struct output_file_normal* outn = to_output_file_normal(out);
169 
170   free(outn);
171 }
172 
173 static struct output_file_ops file_ops = {
174     .open = file_open,
175     .skip = file_skip,
176     .pad = file_pad,
177     .write = file_write,
178     .close = file_close,
179 };
180 
gz_file_open(struct output_file * out,int fd)181 static int gz_file_open(struct output_file* out, int fd) {
182   struct output_file_gz* outgz = to_output_file_gz(out);
183 
184   outgz->gz_fd = gzdopen(fd, "wb9");
185   if (!outgz->gz_fd) {
186     error_errno("gzopen");
187     return -errno;
188   }
189 
190   return 0;
191 }
192 
gz_file_skip(struct output_file * out,int64_t cnt)193 static int gz_file_skip(struct output_file* out, int64_t cnt) {
194   off64_t ret;
195   struct output_file_gz* outgz = to_output_file_gz(out);
196 
197   ret = gzseek(outgz->gz_fd, cnt, SEEK_CUR);
198   if (ret < 0) {
199     error_errno("gzseek");
200     return -1;
201   }
202   return 0;
203 }
204 
gz_file_pad(struct output_file * out,int64_t len)205 static int gz_file_pad(struct output_file* out, int64_t len) {
206   off64_t ret;
207   struct output_file_gz* outgz = to_output_file_gz(out);
208 
209   ret = gztell(outgz->gz_fd);
210   if (ret < 0) {
211     return -1;
212   }
213 
214   if (ret >= len) {
215     return 0;
216   }
217 
218   ret = gzseek(outgz->gz_fd, len - 1, SEEK_SET);
219   if (ret < 0) {
220     return -1;
221   }
222 
223   gzwrite(outgz->gz_fd, "", 1);
224 
225   return 0;
226 }
227 
gz_file_write(struct output_file * out,void * data,size_t len)228 static int gz_file_write(struct output_file* out, void* data, size_t len) {
229   int ret;
230   struct output_file_gz* outgz = to_output_file_gz(out);
231 
232   while (len > 0) {
233     ret = gzwrite(outgz->gz_fd, data, std::min<unsigned int>(len, (unsigned int)INT_MAX));
234     if (ret == 0) {
235       error("gzwrite %s", gzerror(outgz->gz_fd, nullptr));
236       return -1;
237     }
238     len -= ret;
239     data = (char*)data + ret;
240   }
241 
242   return 0;
243 }
244 
gz_file_close(struct output_file * out)245 static void gz_file_close(struct output_file* out) {
246   struct output_file_gz* outgz = to_output_file_gz(out);
247 
248   gzclose(outgz->gz_fd);
249   free(outgz);
250 }
251 
252 static struct output_file_ops gz_file_ops = {
253     .open = gz_file_open,
254     .skip = gz_file_skip,
255     .pad = gz_file_pad,
256     .write = gz_file_write,
257     .close = gz_file_close,
258 };
259 
callback_file_open(struct output_file * out __unused,int fd __unused)260 static int callback_file_open(struct output_file* out __unused, int fd __unused) {
261   return 0;
262 }
263 
callback_file_skip(struct output_file * out,int64_t off)264 static int callback_file_skip(struct output_file* out, int64_t off) {
265   struct output_file_callback* outc = to_output_file_callback(out);
266   int to_write;
267   int ret;
268 
269   while (off > 0) {
270     to_write = std::min(off, (int64_t)INT_MAX);
271     ret = outc->write(outc->priv, nullptr, to_write);
272     if (ret < 0) {
273       return ret;
274     }
275     off -= to_write;
276   }
277 
278   return 0;
279 }
280 
callback_file_pad(struct output_file * out __unused,int64_t len __unused)281 static int callback_file_pad(struct output_file* out __unused, int64_t len __unused) {
282   return -1;
283 }
284 
callback_file_write(struct output_file * out,void * data,size_t len)285 static int callback_file_write(struct output_file* out, void* data, size_t len) {
286   struct output_file_callback* outc = to_output_file_callback(out);
287 
288   return outc->write(outc->priv, data, len);
289 }
290 
callback_file_close(struct output_file * out)291 static void callback_file_close(struct output_file* out) {
292   struct output_file_callback* outc = to_output_file_callback(out);
293 
294   free(outc);
295 }
296 
297 static struct output_file_ops callback_file_ops = {
298     .open = callback_file_open,
299     .skip = callback_file_skip,
300     .pad = callback_file_pad,
301     .write = callback_file_write,
302     .close = callback_file_close,
303 };
304 
read_all(int fd,void * buf,size_t len)305 int read_all(int fd, void* buf, size_t len) {
306   size_t total = 0;
307   int ret;
308   char* ptr = reinterpret_cast<char*>(buf);
309 
310   while (total < len) {
311     ret = read(fd, ptr, len - total);
312 
313     if (ret < 0) return -errno;
314 
315     if (ret == 0) return -EINVAL;
316 
317     ptr += ret;
318     total += ret;
319   }
320 
321   return 0;
322 }
323 
324 template <typename T>
write_fd_chunk_range(int fd,int64_t offset,uint64_t len,T callback)325 static bool write_fd_chunk_range(int fd, int64_t offset, uint64_t len, T callback) {
326   uint64_t bytes_written = 0;
327   int64_t current_offset = offset;
328   while (bytes_written < len) {
329     size_t mmap_size = std::min(static_cast<uint64_t>(kMaxMmapSize), len - bytes_written);
330     auto m = android::base::MappedFile::FromFd(fd, current_offset, mmap_size, PROT_READ);
331     if (!m) {
332       error("failed to mmap region of length %zu", mmap_size);
333       return false;
334     }
335     if (!callback(m->data(), mmap_size)) {
336       return false;
337     }
338     bytes_written += mmap_size;
339     current_offset += mmap_size;
340   }
341   return true;
342 }
343 
write_sparse_skip_chunk(struct output_file * out,uint64_t skip_len)344 static int write_sparse_skip_chunk(struct output_file* out, uint64_t skip_len) {
345   chunk_header_t chunk_header;
346   int ret;
347 
348   if (skip_len % out->block_size) {
349     error("don't care size %" PRIi64 " is not a multiple of the block size %u", skip_len,
350           out->block_size);
351     return -1;
352   }
353 
354   /* We are skipping data, so emit a don't care chunk. */
355   chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE;
356   chunk_header.reserved1 = 0;
357   chunk_header.chunk_sz = skip_len / out->block_size;
358   chunk_header.total_sz = CHUNK_HEADER_LEN;
359   ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
360   if (ret < 0) return -1;
361 
362   out->cur_out_ptr += skip_len;
363   out->chunk_cnt++;
364 
365   return 0;
366 }
367 
write_sparse_fill_chunk(struct output_file * out,uint64_t len,uint32_t fill_val)368 static int write_sparse_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val) {
369   chunk_header_t chunk_header;
370   uint64_t rnd_up_len;
371   int count;
372   int ret;
373 
374   /* Round up the fill length to a multiple of the block size */
375   rnd_up_len = ALIGN(len, out->block_size);
376 
377   /* Finally we can safely emit a chunk of data */
378   chunk_header.chunk_type = CHUNK_TYPE_FILL;
379   chunk_header.reserved1 = 0;
380   chunk_header.chunk_sz = rnd_up_len / out->block_size;
381   chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val);
382   ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
383 
384   if (ret < 0) return -1;
385   ret = out->ops->write(out, &fill_val, sizeof(fill_val));
386   if (ret < 0) return -1;
387 
388   if (out->use_crc) {
389     count = out->block_size / sizeof(uint32_t);
390     while (count--) out->crc32 = sparse_crc32(out->crc32, &fill_val, sizeof(uint32_t));
391   }
392 
393   out->cur_out_ptr += rnd_up_len;
394   out->chunk_cnt++;
395 
396   return 0;
397 }
398 
write_sparse_data_chunk(struct output_file * out,uint64_t len,void * data)399 static int write_sparse_data_chunk(struct output_file* out, uint64_t len, void* data) {
400   chunk_header_t chunk_header;
401   uint64_t rnd_up_len, zero_len;
402   int ret;
403 
404   /* Round up the data length to a multiple of the block size */
405   rnd_up_len = ALIGN(len, out->block_size);
406   zero_len = rnd_up_len - len;
407 
408   /* Finally we can safely emit a chunk of data */
409   chunk_header.chunk_type = CHUNK_TYPE_RAW;
410   chunk_header.reserved1 = 0;
411   chunk_header.chunk_sz = rnd_up_len / out->block_size;
412   chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
413   ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
414 
415   if (ret < 0) return -1;
416   ret = out->ops->write(out, data, len);
417   if (ret < 0) return -1;
418   if (zero_len) {
419     uint64_t len = zero_len;
420     uint64_t write_len;
421     while (len) {
422       write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE);
423       ret = out->ops->write(out, out->zero_buf, write_len);
424       if (ret < 0) {
425         return ret;
426       }
427       len -= write_len;
428     }
429   }
430 
431   if (out->use_crc) {
432     out->crc32 = sparse_crc32(out->crc32, data, len);
433     if (zero_len) {
434       uint64_t len = zero_len;
435       uint64_t write_len;
436       while (len) {
437         write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE);
438         out->crc32 = sparse_crc32(out->crc32, out->zero_buf, write_len);
439         len -= write_len;
440       }
441     }
442   }
443 
444   out->cur_out_ptr += rnd_up_len;
445   out->chunk_cnt++;
446 
447   return 0;
448 }
449 
write_sparse_fd_chunk(struct output_file * out,uint64_t len,int fd,int64_t offset)450 static int write_sparse_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset) {
451   chunk_header_t chunk_header;
452   uint64_t rnd_up_len, zero_len;
453   int ret;
454 
455   /* Round up the data length to a multiple of the block size */
456   rnd_up_len = ALIGN(len, out->block_size);
457   zero_len = rnd_up_len - len;
458 
459   /* Finally we can safely emit a chunk of data */
460   chunk_header.chunk_type = CHUNK_TYPE_RAW;
461   chunk_header.reserved1 = 0;
462   chunk_header.chunk_sz = rnd_up_len / out->block_size;
463   chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
464   ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
465 
466   if (ret < 0) return -1;
467   bool ok = write_fd_chunk_range(fd, offset, len, [&ret, out](char* data, size_t size) -> bool {
468     ret = out->ops->write(out, data, size);
469     if (ret < 0) return false;
470     if (out->use_crc) {
471       out->crc32 = sparse_crc32(out->crc32, data, size);
472     }
473     return true;
474   });
475   if (!ok) return -1;
476   if (zero_len) {
477     uint64_t len = zero_len;
478     uint64_t write_len;
479     while (len) {
480       write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE);
481       ret = out->ops->write(out, out->zero_buf, write_len);
482       if (ret < 0) {
483         return ret;
484       }
485       len -= write_len;
486     }
487 
488     if (out->use_crc) {
489       uint64_t len = zero_len;
490       uint64_t write_len;
491       while (len) {
492         write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE);
493         out->crc32 = sparse_crc32(out->crc32, out->zero_buf, write_len);
494         len -= write_len;
495       }
496     }
497   }
498 
499   out->cur_out_ptr += rnd_up_len;
500   out->chunk_cnt++;
501 
502   return 0;
503 }
504 
write_sparse_end_chunk(struct output_file * out)505 int write_sparse_end_chunk(struct output_file* out) {
506   chunk_header_t chunk_header;
507   int ret;
508 
509   if (out->use_crc) {
510     chunk_header.chunk_type = CHUNK_TYPE_CRC32;
511     chunk_header.reserved1 = 0;
512     chunk_header.chunk_sz = 0;
513     chunk_header.total_sz = CHUNK_HEADER_LEN + 4;
514 
515     ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
516     if (ret < 0) {
517       return ret;
518     }
519     out->ops->write(out, &out->crc32, 4);
520     if (ret < 0) {
521       return ret;
522     }
523 
524     out->chunk_cnt++;
525   }
526 
527   return 0;
528 }
529 
530 static struct sparse_file_ops sparse_file_ops = {
531     .write_data_chunk = write_sparse_data_chunk,
532     .write_fill_chunk = write_sparse_fill_chunk,
533     .write_skip_chunk = write_sparse_skip_chunk,
534     .write_end_chunk = write_sparse_end_chunk,
535     .write_fd_chunk = write_sparse_fd_chunk,
536 };
537 
write_normal_data_chunk(struct output_file * out,uint64_t len,void * data)538 static int write_normal_data_chunk(struct output_file* out, uint64_t len, void* data) {
539   int ret;
540   uint64_t rnd_up_len = ALIGN(len, out->block_size);
541 
542   ret = out->ops->write(out, data, len);
543   if (ret < 0) {
544     return ret;
545   }
546 
547   if (rnd_up_len > len) {
548     ret = out->ops->skip(out, rnd_up_len - len);
549   }
550 
551   return ret;
552 }
553 
write_normal_fill_chunk(struct output_file * out,uint64_t len,uint32_t fill_val)554 static int write_normal_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val) {
555   int ret;
556   unsigned int i;
557   uint64_t write_len;
558 
559   /* Initialize fill_buf with the fill_val */
560   for (i = 0; i < FILL_ZERO_BUFSIZE / sizeof(uint32_t); i++) {
561     out->fill_buf[i] = fill_val;
562   }
563 
564   while (len) {
565     write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE);
566     ret = out->ops->write(out, out->fill_buf, write_len);
567     if (ret < 0) {
568       return ret;
569     }
570 
571     len -= write_len;
572   }
573 
574   return 0;
575 }
576 
write_normal_fd_chunk(struct output_file * out,uint64_t len,int fd,int64_t offset)577 static int write_normal_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset) {
578   int ret;
579   uint64_t rnd_up_len = ALIGN(len, out->block_size);
580 
581   bool ok = write_fd_chunk_range(fd, offset, len, [&ret, out](char* data, size_t size) -> bool {
582     ret = out->ops->write(out, data, size);
583     return ret >= 0;
584   });
585   if (!ok) return ret;
586 
587   if (rnd_up_len > len) {
588     ret = out->ops->skip(out, rnd_up_len - len);
589   }
590 
591   return ret;
592 }
593 
write_normal_skip_chunk(struct output_file * out,uint64_t len)594 static int write_normal_skip_chunk(struct output_file* out, uint64_t len) {
595   return out->ops->skip(out, len);
596 }
597 
write_normal_end_chunk(struct output_file * out)598 int write_normal_end_chunk(struct output_file* out) {
599   return out->ops->pad(out, out->len);
600 }
601 
602 static struct sparse_file_ops normal_file_ops = {
603     .write_data_chunk = write_normal_data_chunk,
604     .write_fill_chunk = write_normal_fill_chunk,
605     .write_skip_chunk = write_normal_skip_chunk,
606     .write_end_chunk = write_normal_end_chunk,
607     .write_fd_chunk = write_normal_fd_chunk,
608 };
609 
output_file_close(struct output_file * out)610 void output_file_close(struct output_file* out) {
611   out->sparse_ops->write_end_chunk(out);
612   free(out->zero_buf);
613   free(out->fill_buf);
614   out->zero_buf = nullptr;
615   out->fill_buf = nullptr;
616   out->ops->close(out);
617 }
618 
output_file_init(struct output_file * out,int block_size,int64_t len,bool sparse,int chunks,bool crc)619 static int output_file_init(struct output_file* out, int block_size, int64_t len, bool sparse,
620                             int chunks, bool crc) {
621   int ret;
622 
623   out->len = len;
624   out->block_size = block_size;
625   out->cur_out_ptr = 0LL;
626   out->chunk_cnt = 0;
627   out->crc32 = 0;
628   out->use_crc = crc;
629 
630   // don't use sparse format block size as it can takes up to 32GB
631   out->zero_buf = reinterpret_cast<char*>(calloc(FILL_ZERO_BUFSIZE, 1));
632   if (!out->zero_buf) {
633     error_errno("malloc zero_buf");
634     return -ENOMEM;
635   }
636 
637   // don't use sparse format block size as it can takes up to 32GB
638   out->fill_buf = reinterpret_cast<uint32_t*>(calloc(FILL_ZERO_BUFSIZE, 1));
639   if (!out->fill_buf) {
640     error_errno("malloc fill_buf");
641     ret = -ENOMEM;
642     goto err_fill_buf;
643   }
644 
645   if (sparse) {
646     out->sparse_ops = &sparse_file_ops;
647   } else {
648     out->sparse_ops = &normal_file_ops;
649   }
650 
651   if (sparse) {
652     sparse_header_t sparse_header = {
653         .magic = SPARSE_HEADER_MAGIC,
654         .major_version = SPARSE_HEADER_MAJOR_VER,
655         .minor_version = SPARSE_HEADER_MINOR_VER,
656         .file_hdr_sz = SPARSE_HEADER_LEN,
657         .chunk_hdr_sz = CHUNK_HEADER_LEN,
658         .blk_sz = out->block_size,
659         .total_blks = static_cast<unsigned>(DIV_ROUND_UP(out->len, out->block_size)),
660         .total_chunks = static_cast<unsigned>(chunks),
661         .image_checksum = 0};
662 
663     if (out->use_crc) {
664       sparse_header.total_chunks++;
665     }
666 
667     ret = out->ops->write(out, &sparse_header, sizeof(sparse_header));
668     if (ret < 0) {
669       goto err_write;
670     }
671   }
672 
673   return 0;
674 
675 err_write:
676   free(out->fill_buf);
677 err_fill_buf:
678   free(out->zero_buf);
679   return ret;
680 }
681 
output_file_new_gz(void)682 static struct output_file* output_file_new_gz(void) {
683   struct output_file_gz* outgz =
684       reinterpret_cast<struct output_file_gz*>(calloc(1, sizeof(struct output_file_gz)));
685   if (!outgz) {
686     error_errno("malloc struct outgz");
687     return nullptr;
688   }
689 
690   outgz->out.ops = &gz_file_ops;
691 
692   return &outgz->out;
693 }
694 
output_file_new_normal(void)695 static struct output_file* output_file_new_normal(void) {
696   struct output_file_normal* outn =
697       reinterpret_cast<struct output_file_normal*>(calloc(1, sizeof(struct output_file_normal)));
698   if (!outn) {
699     error_errno("malloc struct outn");
700     return nullptr;
701   }
702 
703   outn->out.ops = &file_ops;
704 
705   return &outn->out;
706 }
707 
output_file_open_callback(int (* write)(void *,const void *,size_t),void * priv,unsigned int block_size,int64_t len,int gz __unused,int sparse,int chunks,int crc)708 struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,
709                                               unsigned int block_size, int64_t len, int gz __unused,
710                                               int sparse, int chunks, int crc) {
711   int ret;
712   struct output_file_callback* outc;
713 
714   outc =
715       reinterpret_cast<struct output_file_callback*>(calloc(1, sizeof(struct output_file_callback)));
716   if (!outc) {
717     error_errno("malloc struct outc");
718     return nullptr;
719   }
720 
721   outc->out.ops = &callback_file_ops;
722   outc->priv = priv;
723   outc->write = write;
724 
725   ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc);
726   if (ret < 0) {
727     free(outc);
728     return nullptr;
729   }
730 
731   return &outc->out;
732 }
733 
output_file_open_fd(int fd,unsigned int block_size,int64_t len,int gz,int sparse,int chunks,int crc)734 struct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t len, int gz,
735                                         int sparse, int chunks, int crc) {
736   int ret;
737   struct output_file* out;
738 
739   if (gz) {
740     out = output_file_new_gz();
741   } else {
742     out = output_file_new_normal();
743   }
744   if (!out) {
745     return nullptr;
746   }
747 
748   out->ops->open(out, fd);
749 
750   ret = output_file_init(out, block_size, len, sparse, chunks, crc);
751   if (ret < 0) {
752     free(out);
753     return nullptr;
754   }
755 
756   return out;
757 }
758 
759 /* Write a contiguous region of data blocks from a memory buffer */
write_data_chunk(struct output_file * out,uint64_t len,void * data)760 int write_data_chunk(struct output_file* out, uint64_t len, void* data) {
761   return out->sparse_ops->write_data_chunk(out, len, data);
762 }
763 
764 /* Write a contiguous region of data blocks with a fill value */
write_fill_chunk(struct output_file * out,uint64_t len,uint32_t fill_val)765 int write_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val) {
766   return out->sparse_ops->write_fill_chunk(out, len, fill_val);
767 }
768 
write_fd_chunk(struct output_file * out,uint64_t len,int fd,int64_t offset)769 int write_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset) {
770   return out->sparse_ops->write_fd_chunk(out, len, fd, offset);
771 }
772 
773 /* Write a contiguous region of data blocks from a file */
write_file_chunk(struct output_file * out,uint64_t len,const char * file,int64_t offset)774 int write_file_chunk(struct output_file* out, uint64_t len, const char* file, int64_t offset) {
775   int ret;
776 
777   int file_fd = open(file, O_RDONLY | O_BINARY);
778   if (file_fd < 0) {
779     return -errno;
780   }
781 
782   ret = write_fd_chunk(out, len, file_fd, offset);
783 
784   close(file_fd);
785 
786   return ret;
787 }
788 
write_skip_chunk(struct output_file * out,uint64_t len)789 int write_skip_chunk(struct output_file* out, uint64_t len) {
790   return out->sparse_ops->write_skip_chunk(out, len);
791 }
792