diff options
Diffstat (limited to 'libsparse/output_file.c')
-rw-r--r-- | libsparse/output_file.c | 236 |
1 files changed, 158 insertions, 78 deletions
diff --git a/libsparse/output_file.c b/libsparse/output_file.c index 5e8a68c54..14b057a9e 100644 --- a/libsparse/output_file.c +++ b/libsparse/output_file.c | |||
@@ -19,6 +19,7 @@ | |||
19 | 19 | ||
20 | #include <fcntl.h> | 20 | #include <fcntl.h> |
21 | #include <stdbool.h> | 21 | #include <stdbool.h> |
22 | #include <stddef.h> | ||
22 | #include <stdlib.h> | 23 | #include <stdlib.h> |
23 | #include <string.h> | 24 | #include <string.h> |
24 | #include <sys/stat.h> | 25 | #include <sys/stat.h> |
@@ -33,6 +34,8 @@ | |||
33 | #ifndef USE_MINGW | 34 | #ifndef USE_MINGW |
34 | #include <sys/mman.h> | 35 | #include <sys/mman.h> |
35 | #define O_BINARY 0 | 36 | #define O_BINARY 0 |
37 | #else | ||
38 | #define ftruncate64 ftruncate | ||
36 | #endif | 39 | #endif |
37 | 40 | ||
38 | #if defined(__APPLE__) && defined(__MACH__) | 41 | #if defined(__APPLE__) && defined(__MACH__) |
@@ -59,8 +62,13 @@ static inline void *mmap64(void *addr, size_t length, int prot, int flags, | |||
59 | #define SPARSE_HEADER_LEN (sizeof(sparse_header_t)) | 62 | #define SPARSE_HEADER_LEN (sizeof(sparse_header_t)) |
60 | #define CHUNK_HEADER_LEN (sizeof(chunk_header_t)) | 63 | #define CHUNK_HEADER_LEN (sizeof(chunk_header_t)) |
61 | 64 | ||
65 | #define container_of(inner, outer_t, elem) \ | ||
66 | ((outer_t *)((char *)inner - offsetof(outer_t, elem))) | ||
67 | |||
62 | struct output_file_ops { | 68 | struct output_file_ops { |
69 | int (*open)(struct output_file *, int fd); | ||
63 | int (*skip)(struct output_file *, int64_t); | 70 | int (*skip)(struct output_file *, int64_t); |
71 | int (*pad)(struct output_file *, int64_t); | ||
64 | int (*write)(struct output_file *, void *, int); | 72 | int (*write)(struct output_file *, void *, int); |
65 | void (*close)(struct output_file *); | 73 | void (*close)(struct output_file *); |
66 | }; | 74 | }; |
@@ -75,9 +83,6 @@ struct sparse_file_ops { | |||
75 | }; | 83 | }; |
76 | 84 | ||
77 | struct output_file { | 85 | struct output_file { |
78 | int fd; | ||
79 | gzFile gz_fd; | ||
80 | bool close_fd; | ||
81 | int64_t cur_out_ptr; | 86 | int64_t cur_out_ptr; |
82 | unsigned int chunk_cnt; | 87 | unsigned int chunk_cnt; |
83 | uint32_t crc32; | 88 | uint32_t crc32; |
@@ -88,13 +93,39 @@ struct output_file { | |||
88 | int64_t len; | 93 | int64_t len; |
89 | char *zero_buf; | 94 | char *zero_buf; |
90 | uint32_t *fill_buf; | 95 | uint32_t *fill_buf; |
96 | char *buf; | ||
97 | }; | ||
98 | |||
99 | struct output_file_gz { | ||
100 | struct output_file out; | ||
101 | gzFile gz_fd; | ||
91 | }; | 102 | }; |
92 | 103 | ||
104 | #define to_output_file_gz(_o) \ | ||
105 | container_of((_o), struct output_file_gz, out) | ||
106 | |||
107 | struct output_file_normal { | ||
108 | struct output_file out; | ||
109 | int fd; | ||
110 | }; | ||
111 | |||
112 | #define to_output_file_normal(_o) \ | ||
113 | container_of((_o), struct output_file_normal, out) | ||
114 | |||
115 | static int file_open(struct output_file *out, int fd) | ||
116 | { | ||
117 | struct output_file_normal *outn = to_output_file_normal(out); | ||
118 | |||
119 | outn->fd = fd; | ||
120 | return 0; | ||
121 | } | ||
122 | |||
93 | static int file_skip(struct output_file *out, int64_t cnt) | 123 | static int file_skip(struct output_file *out, int64_t cnt) |
94 | { | 124 | { |
95 | off64_t ret; | 125 | off64_t ret; |
126 | struct output_file_normal *outn = to_output_file_normal(out); | ||
96 | 127 | ||
97 | ret = lseek64(out->fd, cnt, SEEK_CUR); | 128 | ret = lseek64(outn->fd, cnt, SEEK_CUR); |
98 | if (ret < 0) { | 129 | if (ret < 0) { |
99 | error_errno("lseek64"); | 130 | error_errno("lseek64"); |
100 | return -1; | 131 | return -1; |
@@ -102,10 +133,25 @@ static int file_skip(struct output_file *out, int64_t cnt) | |||
102 | return 0; | 133 | return 0; |
103 | } | 134 | } |
104 | 135 | ||
136 | static int file_pad(struct output_file *out, int64_t len) | ||
137 | { | ||
138 | int ret; | ||
139 | struct output_file_normal *outn = to_output_file_normal(out); | ||
140 | |||
141 | ret = ftruncate64(outn->fd, len); | ||
142 | if (ret < 0) { | ||
143 | return -errno; | ||
144 | } | ||
145 | |||
146 | return 0; | ||
147 | } | ||
148 | |||
105 | static int file_write(struct output_file *out, void *data, int len) | 149 | static int file_write(struct output_file *out, void *data, int len) |
106 | { | 150 | { |
107 | int ret; | 151 | int ret; |
108 | ret = write(out->fd, data, len); | 152 | struct output_file_normal *outn = to_output_file_normal(out); |
153 | |||
154 | ret = write(outn->fd, data, len); | ||
109 | if (ret < 0) { | 155 | if (ret < 0) { |
110 | error_errno("write"); | 156 | error_errno("write"); |
111 | return -1; | 157 | return -1; |
@@ -119,22 +165,39 @@ static int file_write(struct output_file *out, void *data, int len) | |||
119 | 165 | ||
120 | static void file_close(struct output_file *out) | 166 | static void file_close(struct output_file *out) |
121 | { | 167 | { |
122 | if (out->close_fd) { | 168 | struct output_file_normal *outn = to_output_file_normal(out); |
123 | close(out->fd); | 169 | |
124 | } | 170 | free(outn); |
125 | } | 171 | } |
126 | 172 | ||
127 | static struct output_file_ops file_ops = { | 173 | static struct output_file_ops file_ops = { |
174 | .open = file_open, | ||
128 | .skip = file_skip, | 175 | .skip = file_skip, |
176 | .pad = file_pad, | ||
129 | .write = file_write, | 177 | .write = file_write, |
130 | .close = file_close, | 178 | .close = file_close, |
131 | }; | 179 | }; |
132 | 180 | ||
181 | static int gz_file_open(struct output_file *out, int fd) | ||
182 | { | ||
183 | struct output_file_gz *outgz = to_output_file_gz(out); | ||
184 | |||
185 | outgz->gz_fd = gzdopen(fd, "wb9"); | ||
186 | if (!outgz->gz_fd) { | ||
187 | error_errno("gzopen"); | ||
188 | return -errno; | ||
189 | } | ||
190 | |||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | |||
133 | static int gz_file_skip(struct output_file *out, int64_t cnt) | 195 | static int gz_file_skip(struct output_file *out, int64_t cnt) |
134 | { | 196 | { |
135 | off64_t ret; | 197 | off64_t ret; |
198 | struct output_file_gz *outgz = to_output_file_gz(out); | ||
136 | 199 | ||
137 | ret = gzseek(out->gz_fd, cnt, SEEK_CUR); | 200 | ret = gzseek(outgz->gz_fd, cnt, SEEK_CUR); |
138 | if (ret < 0) { | 201 | if (ret < 0) { |
139 | error_errno("gzseek"); | 202 | error_errno("gzseek"); |
140 | return -1; | 203 | return -1; |
@@ -142,10 +205,36 @@ static int gz_file_skip(struct output_file *out, int64_t cnt) | |||
142 | return 0; | 205 | return 0; |
143 | } | 206 | } |
144 | 207 | ||
208 | static int gz_file_pad(struct output_file *out, int64_t len) | ||
209 | { | ||
210 | off64_t ret; | ||
211 | struct output_file_gz *outgz = to_output_file_gz(out); | ||
212 | |||
213 | ret = gztell(outgz->gz_fd); | ||
214 | if (ret < 0) { | ||
215 | return -1; | ||
216 | } | ||
217 | |||
218 | if (ret >= len) { | ||
219 | return 0; | ||
220 | } | ||
221 | |||
222 | ret = gzseek(outgz->gz_fd, len - 1, SEEK_SET); | ||
223 | if (ret < 0) { | ||
224 | return -1; | ||
225 | } | ||
226 | |||
227 | gzwrite(outgz->gz_fd, "", 1); | ||
228 | |||
229 | return 0; | ||
230 | } | ||
231 | |||
145 | static int gz_file_write(struct output_file *out, void *data, int len) | 232 | static int gz_file_write(struct output_file *out, void *data, int len) |
146 | { | 233 | { |
147 | int ret; | 234 | int ret; |
148 | ret = gzwrite(out->gz_fd, data, len); | 235 | struct output_file_gz *outgz = to_output_file_gz(out); |
236 | |||
237 | ret = gzwrite(outgz->gz_fd, data, len); | ||
149 | if (ret < 0) { | 238 | if (ret < 0) { |
150 | error_errno("gzwrite"); | 239 | error_errno("gzwrite"); |
151 | return -1; | 240 | return -1; |
@@ -159,11 +248,16 @@ static int gz_file_write(struct output_file *out, void *data, int len) | |||
159 | 248 | ||
160 | static void gz_file_close(struct output_file *out) | 249 | static void gz_file_close(struct output_file *out) |
161 | { | 250 | { |
162 | gzclose(out->gz_fd); | 251 | struct output_file_gz *outgz = to_output_file_gz(out); |
252 | |||
253 | gzclose(outgz->gz_fd); | ||
254 | free(outgz); | ||
163 | } | 255 | } |
164 | 256 | ||
165 | static struct output_file_ops gz_file_ops = { | 257 | static struct output_file_ops gz_file_ops = { |
258 | .open = gz_file_open, | ||
166 | .skip = gz_file_skip, | 259 | .skip = gz_file_skip, |
260 | .pad = gz_file_pad, | ||
167 | .write = gz_file_write, | 261 | .write = gz_file_write, |
168 | .close = gz_file_close, | 262 | .close = gz_file_close, |
169 | }; | 263 | }; |
@@ -371,21 +465,12 @@ static int write_normal_fill_chunk(struct output_file *out, unsigned int len, | |||
371 | 465 | ||
372 | static int write_normal_skip_chunk(struct output_file *out, int64_t len) | 466 | static int write_normal_skip_chunk(struct output_file *out, int64_t len) |
373 | { | 467 | { |
374 | int ret; | ||
375 | |||
376 | return out->ops->skip(out, len); | 468 | return out->ops->skip(out, len); |
377 | } | 469 | } |
378 | 470 | ||
379 | int write_normal_end_chunk(struct output_file *out) | 471 | int write_normal_end_chunk(struct output_file *out) |
380 | { | 472 | { |
381 | int ret; | 473 | return out->ops->pad(out, out->len); |
382 | |||
383 | ret = ftruncate64(out->fd, out->len); | ||
384 | if (ret < 0) { | ||
385 | return -errno; | ||
386 | } | ||
387 | |||
388 | return 0; | ||
389 | } | 474 | } |
390 | 475 | ||
391 | static struct sparse_file_ops normal_file_ops = { | 476 | static struct sparse_file_ops normal_file_ops = { |
@@ -401,59 +486,39 @@ void close_output_file(struct output_file *out) | |||
401 | 486 | ||
402 | out->sparse_ops->write_end_chunk(out); | 487 | out->sparse_ops->write_end_chunk(out); |
403 | out->ops->close(out); | 488 | out->ops->close(out); |
404 | free(out); | ||
405 | } | 489 | } |
406 | 490 | ||
407 | struct output_file *open_output_fd(int fd, unsigned int block_size, int64_t len, | 491 | static int output_file_init(struct output_file *out, int block_size, |
408 | int gz, int sparse, int chunks, int crc) | 492 | int64_t len, bool sparse, int chunks, bool crc) |
409 | { | 493 | { |
410 | int ret; | 494 | int ret; |
411 | struct output_file *out = malloc(sizeof(struct output_file)); | 495 | |
412 | if (!out) { | 496 | out->len = len; |
413 | error_errno("malloc struct out"); | 497 | out->block_size = block_size; |
414 | return NULL; | 498 | out->cur_out_ptr = 0ll; |
415 | } | 499 | out->chunk_cnt = 0; |
500 | out->crc32 = 0; | ||
501 | out->use_crc = crc; | ||
502 | |||
416 | out->zero_buf = calloc(block_size, 1); | 503 | out->zero_buf = calloc(block_size, 1); |
417 | if (!out->zero_buf) { | 504 | if (!out->zero_buf) { |
418 | error_errno("malloc zero_buf"); | 505 | error_errno("malloc zero_buf"); |
419 | goto err_zero_buf; | 506 | return -ENOMEM; |
420 | } | 507 | } |
421 | 508 | ||
422 | out->fill_buf = calloc(block_size, 1); | 509 | out->fill_buf = calloc(block_size, 1); |
423 | if (!out->fill_buf) { | 510 | if (!out->fill_buf) { |
424 | error_errno("malloc fill_buf"); | 511 | error_errno("malloc fill_buf"); |
512 | ret = -ENOMEM; | ||
425 | goto err_fill_buf; | 513 | goto err_fill_buf; |
426 | } | 514 | } |
427 | 515 | ||
428 | if (gz) { | ||
429 | out->ops = &gz_file_ops; | ||
430 | out->gz_fd = gzdopen(fd, "wb9"); | ||
431 | if (!out->gz_fd) { | ||
432 | error_errno("gzopen"); | ||
433 | goto err_gzopen; | ||
434 | } | ||
435 | } else { | ||
436 | out->fd = fd; | ||
437 | out->ops = &file_ops; | ||
438 | } | ||
439 | |||
440 | if (sparse) { | 516 | if (sparse) { |
441 | out->sparse_ops = &sparse_file_ops; | 517 | out->sparse_ops = &sparse_file_ops; |
442 | } else { | 518 | } else { |
443 | out->sparse_ops = &normal_file_ops; | 519 | out->sparse_ops = &normal_file_ops; |
444 | } | 520 | } |
445 | 521 | ||
446 | out->close_fd = false; | ||
447 | out->cur_out_ptr = 0ll; | ||
448 | out->chunk_cnt = 0; | ||
449 | |||
450 | /* Initialize the crc32 value */ | ||
451 | out->crc32 = 0; | ||
452 | out->use_crc = crc; | ||
453 | |||
454 | out->len = len; | ||
455 | out->block_size = block_size; | ||
456 | |||
457 | if (sparse) { | 522 | if (sparse) { |
458 | sparse_header_t sparse_header = { | 523 | sparse_header_t sparse_header = { |
459 | .magic = SPARSE_HEADER_MAGIC, | 524 | .magic = SPARSE_HEADER_MAGIC, |
@@ -477,47 +542,62 @@ struct output_file *open_output_fd(int fd, unsigned int block_size, int64_t len, | |||
477 | } | 542 | } |
478 | } | 543 | } |
479 | 544 | ||
480 | return out; | 545 | return 0; |
481 | 546 | ||
482 | err_write: | 547 | err_write: |
483 | if (gz) { | ||
484 | gzclose(out->gz_fd); | ||
485 | } | ||
486 | err_gzopen: | ||
487 | free(out->fill_buf); | 548 | free(out->fill_buf); |
488 | err_fill_buf: | 549 | err_fill_buf: |
489 | free(out->zero_buf); | 550 | free(out->zero_buf); |
490 | err_zero_buf: | 551 | return ret; |
491 | free(out); | 552 | } |
492 | return NULL; | 553 | |
554 | static struct output_file *output_file_new_gz(void) | ||
555 | { | ||
556 | struct output_file_gz *outgz = calloc(1, sizeof(struct output_file_gz)); | ||
557 | if (!outgz) { | ||
558 | error_errno("malloc struct outgz"); | ||
559 | return NULL; | ||
560 | } | ||
561 | |||
562 | outgz->out.ops = &gz_file_ops; | ||
563 | |||
564 | return &outgz->out; | ||
565 | } | ||
566 | |||
567 | static struct output_file *output_file_new_normal(void) | ||
568 | { | ||
569 | struct output_file_normal *outn = calloc(1, sizeof(struct output_file_normal)); | ||
570 | if (!outn) { | ||
571 | error_errno("malloc struct outn"); | ||
572 | return NULL; | ||
573 | } | ||
574 | |||
575 | outn->out.ops = &file_ops; | ||
576 | |||
577 | return &outn->out; | ||
493 | } | 578 | } |
494 | 579 | ||
495 | struct output_file *open_output_file(const char *filename, | 580 | struct output_file *open_output_fd(int fd, unsigned int block_size, int64_t len, |
496 | unsigned int block_size, int64_t len, | ||
497 | int gz, int sparse, int chunks, int crc) | 581 | int gz, int sparse, int chunks, int crc) |
498 | { | 582 | { |
499 | int fd; | 583 | int ret; |
500 | struct output_file *file; | 584 | struct output_file *out; |
501 | 585 | ||
502 | if (strcmp(filename, "-")) { | 586 | if (gz) { |
503 | fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); | 587 | out = output_file_new_gz(); |
504 | if (fd < 0) { | ||
505 | error_errno("open"); | ||
506 | return NULL; | ||
507 | } | ||
508 | } else { | 588 | } else { |
509 | fd = STDOUT_FILENO; | 589 | out = output_file_new_normal(); |
510 | } | 590 | } |
511 | 591 | ||
512 | file = open_output_fd(fd, block_size, len, gz, sparse, chunks, crc); | 592 | out->ops->open(out, fd); |
513 | if (!file) { | 593 | |
514 | close(fd); | 594 | ret = output_file_init(out, block_size, len, sparse, chunks, crc); |
595 | if (ret < 0) { | ||
596 | free(out); | ||
515 | return NULL; | 597 | return NULL; |
516 | } | 598 | } |
517 | 599 | ||
518 | file->close_fd = true; // we opened descriptor thus we responsible for closing it | 600 | return out; |
519 | |||
520 | return file; | ||
521 | } | 601 | } |
522 | 602 | ||
523 | /* Write a contiguous region of data blocks from a memory buffer */ | 603 | /* Write a contiguous region of data blocks from a memory buffer */ |