blob: 2c4b5573eb610a289261d4367cf2d44610f6377d [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>
21#include <stdbool.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/stat.h>
25#include <sys/types.h>
26#include <unistd.h>
27#include <zlib.h>
28
29#include "output_file.h"
30#include "sparse_format.h"
31#include "sparse_crc32.h"
32
33#ifndef USE_MINGW
34#include <sys/mman.h>
35#define O_BINARY 0
36#endif
37
38#if defined(__APPLE__) && defined(__MACH__)
39#define lseek64 lseek
40#define ftruncate64 ftruncate
41#define mmap64 mmap
42#define off64_t off_t
43#endif
44
45#ifdef __BIONIC__
46extern void* __mmap2(void *, size_t, int, int, int, off_t);
47static inline void *mmap64(void *addr, size_t length, int prot, int flags,
48 int fd, off64_t offset)
49{
50 return __mmap2(addr, length, prot, flags, fd, offset >> 12);
51}
52#endif
53
54#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
59struct output_file_ops {
60 int (*seek)(struct output_file *, int64_t);
61 int (*write)(struct output_file *, u8 *, int);
62 void (*close)(struct output_file *);
63};
64
65struct output_file {
66 int fd;
67 gzFile gz_fd;
68 bool close_fd;
69 int sparse;
70 int64_t cur_out_ptr;
71 u32 chunk_cnt;
72 u32 crc32;
73 struct output_file_ops *ops;
74 int use_crc;
75 unsigned int block_size;
76 int64_t len;
77};
78
79static int file_seek(struct output_file *out, int64_t off)
80{
81 off64_t ret;
82
83 ret = lseek64(out->fd, off, SEEK_SET);
84 if (ret < 0) {
85 error_errno("lseek64");
86 return -1;
87 }
88 return 0;
89}
90
91static int file_write(struct output_file *out, u8 *data, int len)
92{
93 int ret;
94 ret = write(out->fd, data, len);
95 if (ret < 0) {
96 error_errno("write");
97 return -1;
98 } else if (ret < len) {
99 error("incomplete write");
100 return -1;
101 }
102
103 return 0;
104}
105
106static void file_close(struct output_file *out)
107{
108 if (out->close_fd) {
109 close(out->fd);
110 }
111}
112
113
114static struct output_file_ops file_ops = {
115 .seek = file_seek,
116 .write = file_write,
117 .close = file_close,
118};
119
120static int gz_file_seek(struct output_file *out, int64_t off)
121{
122 off64_t ret;
123
124 ret = gzseek(out->gz_fd, off, SEEK_SET);
125 if (ret < 0) {
126 error_errno("gzseek");
127 return -1;
128 }
129 return 0;
130}
131
132static int gz_file_write(struct output_file *out, u8 *data, int len)
133{
134 int ret;
135 ret = gzwrite(out->gz_fd, data, len);
136 if (ret < 0) {
137 error_errno("gzwrite");
138 return -1;
139 } else if (ret < len) {
140 error("incomplete gzwrite");
141 return -1;
142 }
143
144 return 0;
145}
146
147static void gz_file_close(struct output_file *out)
148{
149 gzclose(out->gz_fd);
150}
151
152static struct output_file_ops gz_file_ops = {
153 .seek = gz_file_seek,
154 .write = gz_file_write,
155 .close = gz_file_close,
156};
157
158static sparse_header_t sparse_header = {
159 .magic = SPARSE_HEADER_MAGIC,
160 .major_version = SPARSE_HEADER_MAJOR_VER,
161 .minor_version = SPARSE_HEADER_MINOR_VER,
162 .file_hdr_sz = SPARSE_HEADER_LEN,
163 .chunk_hdr_sz = CHUNK_HEADER_LEN,
164 .blk_sz = 0,
165 .total_blks = 0,
166 .total_chunks = 0,
167 .image_checksum = 0
168};
169
170static u8 *zero_buf;
171
172static int emit_skip_chunk(struct output_file *out, u64 skip_len)
173{
174 chunk_header_t chunk_header;
175 int ret, chunk;
176
177 //DBG printf("skip chunk: 0x%llx bytes\n", skip_len);
178
179 if (skip_len % out->block_size) {
180 error("don't care size %llu is not a multiple of the block size %u",
181 skip_len, out->block_size);
182 return -1;
183 }
184
185 /* We are skipping data, so emit a don't care chunk. */
186 chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE;
187 chunk_header.reserved1 = 0;
188 chunk_header.chunk_sz = skip_len / out->block_size;
189 chunk_header.total_sz = CHUNK_HEADER_LEN;
190 ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header));
191 if (ret < 0)
192 return -1;
193
194 out->cur_out_ptr += skip_len;
195 out->chunk_cnt++;
196
197 return 0;
198}
199
200static int write_chunk_fill(struct output_file *out, int64_t off, u32 fill_val, int len)
201{
202 chunk_header_t chunk_header;
203 int rnd_up_len, zero_len, count;
204 int ret;
205 unsigned int i;
206 u32 fill_buf[4096/sizeof(u32)]; /* Maximum size of a block */
207
208 /* We can assume that all the chunks to be written are in
209 * ascending order, block-size aligned, and non-overlapping.
210 * So, if the offset is less than the current output pointer,
211 * throw an error, and if there is a gap, emit a "don't care"
212 * chunk. The first write (of the super block) may not be
213 * blocksize aligned, so we need to deal with that too.
214 */
215 //DBG printf("write chunk: offset 0x%llx, length 0x%x bytes\n", off, len);
216
217 if (off < out->cur_out_ptr) {
218 error("offset %llu is less than the current output offset %llu",
219 off, out->cur_out_ptr);
220 return -1;
221 }
222
223 if (off > out->cur_out_ptr) {
224 emit_skip_chunk(out, off - out->cur_out_ptr);
225 }
226
227 if (off % out->block_size) {
228 error("write chunk offset %llu is not a multiple of the block size %u",
229 off, out->block_size);
230 return -1;
231 }
232
233 if (off != out->cur_out_ptr) {
234 error("internal error, offset accounting screwy in write_chunk_raw()");
235 return -1;
236 }
237
238 /* Round up the file length to a multiple of the block size */
239 rnd_up_len = (len + (out->block_size - 1)) & (~(out->block_size -1));
240
241 /* Finally we can safely emit a chunk of data */
242 chunk_header.chunk_type = CHUNK_TYPE_FILL;
243 chunk_header.reserved1 = 0;
244 chunk_header.chunk_sz = rnd_up_len / out->block_size;
245 chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val);
246 ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header));
247
248 if (ret < 0)
249 return -1;
250 ret = out->ops->write(out, (u8 *)&fill_val, sizeof(fill_val));
251 if (ret < 0)
252 return -1;
253
254 if (out->use_crc) {
255 /* Initialize fill_buf with the fill_val */
256 for (i = 0; i < (out->block_size / sizeof(u32)); i++) {
257 fill_buf[i] = fill_val;
258 }
259
260 count = chunk_header.chunk_sz;
261 while (count) {
262 out->crc32 = sparse_crc32(out->crc32, fill_buf, out->block_size);
263 count--;
264 }
265 }
266
267 out->cur_out_ptr += rnd_up_len;
268 out->chunk_cnt++;
269
270 return 0;
271}
272
273static int write_chunk_raw(struct output_file *out, int64_t off, u8 *data, int len)
274{
275 chunk_header_t chunk_header;
276 int rnd_up_len, zero_len;
277 int ret;
278
279 /* We can assume that all the chunks to be written are in
280 * ascending order, block-size aligned, and non-overlapping.
281 * So, if the offset is less than the current output pointer,
282 * throw an error, and if there is a gap, emit a "don't care"
283 * chunk. The first write (of the super block) may not be
284 * blocksize aligned, so we need to deal with that too.
285 */
286 //DBG printf("write chunk: offset 0x%llx, length 0x%x bytes\n", off, len);
287
288 if (off < out->cur_out_ptr) {
289 error("offset %llu is less than the current output offset %llu",
290 off, out->cur_out_ptr);
291 return -1;
292 }
293
294 if (off > out->cur_out_ptr) {
295 emit_skip_chunk(out, off - out->cur_out_ptr);
296 }
297
298 if (off % out->block_size) {
299 error("write chunk offset %llu is not a multiple of the block size %u",
300 off, out->block_size);
301 return -1;
302 }
303
304 if (off != out->cur_out_ptr) {
305 error("internal error, offset accounting screwy in write_chunk_raw()");
306 return -1;
307 }
308
309 /* Round up the file length to a multiple of the block size */
310 rnd_up_len = (len + (out->block_size - 1)) & (~(out->block_size -1));
311 zero_len = rnd_up_len - len;
312
313 /* Finally we can safely emit a chunk of data */
314 chunk_header.chunk_type = CHUNK_TYPE_RAW;
315 chunk_header.reserved1 = 0;
316 chunk_header.chunk_sz = rnd_up_len / out->block_size;
317 chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
318 ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header));
319
320 if (ret < 0)
321 return -1;
322 ret = out->ops->write(out, data, len);
323 if (ret < 0)
324 return -1;
325 if (zero_len) {
326 ret = out->ops->write(out, zero_buf, zero_len);
327 if (ret < 0)
328 return -1;
329 }
330
331 if (out->use_crc) {
332 out->crc32 = sparse_crc32(out->crc32, data, len);
333 if (zero_len)
334 out->crc32 = sparse_crc32(out->crc32, zero_buf, zero_len);
335 }
336
337 out->cur_out_ptr += rnd_up_len;
338 out->chunk_cnt++;
339
340 return 0;
341}
342
343void close_output_file(struct output_file *out)
344{
345 int ret;
346 chunk_header_t chunk_header;
347
348 if (out->sparse) {
349 if (out->use_crc) {
350 chunk_header.chunk_type = CHUNK_TYPE_CRC32;
351 chunk_header.reserved1 = 0;
352 chunk_header.chunk_sz = 0;
353 chunk_header.total_sz = CHUNK_HEADER_LEN + 4;
354
355 out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header));
356 out->ops->write(out, (u8 *)&out->crc32, 4);
357
358 out->chunk_cnt++;
359 }
360
361 if (out->chunk_cnt != sparse_header.total_chunks)
362 error("sparse chunk count did not match: %d %d", out->chunk_cnt,
363 sparse_header.total_chunks);
364 }
365 out->ops->close(out);
366}
367
368struct output_file *open_output_fd(int fd, unsigned int block_size, int64_t len,
369 int gz, int sparse, int chunks, int crc)
370{
371 int ret;
372 struct output_file *out = malloc(sizeof(struct output_file));
373 if (!out) {
374 error_errno("malloc struct out");
375 return NULL;
376 }
377 zero_buf = malloc(out->block_size);
378 if (!zero_buf) {
379 error_errno("malloc zero_buf");
380 free(out);
381 return NULL;
382 }
383 memset(zero_buf, '\0', out->block_size);
384
385 if (gz) {
386 out->ops = &gz_file_ops;
387 out->gz_fd = gzdopen(fd, "wb9");
388 if (!out->gz_fd) {
389 error_errno("gzopen");
390 free(out);
391 return NULL;
392 }
393 } else {
394 out->fd = fd;
395 out->ops = &file_ops;
396 }
397 out->close_fd = false;
398 out->sparse = sparse;
399 out->cur_out_ptr = 0ll;
400 out->chunk_cnt = 0;
401
402 /* Initialize the crc32 value */
403 out->crc32 = 0;
404 out->use_crc = crc;
405
406 out->len = len;
407 out->block_size = block_size;
408
409 if (out->sparse) {
410 sparse_header.blk_sz = out->block_size,
411 sparse_header.total_blks = out->len / out->block_size,
412 sparse_header.total_chunks = chunks;
413 if (out->use_crc)
414 sparse_header.total_chunks++;
415
416 ret = out->ops->write(out, (u8 *)&sparse_header, sizeof(sparse_header));
417 if (ret < 0)
418 return NULL;
419 }
420
421 return out;
422}
423
424struct output_file *open_output_file(const char *filename,
425 unsigned int block_size, int64_t len,
426 int gz, int sparse, int chunks, int crc)
427{
428 int fd;
429 struct output_file *file;
430
431 if (strcmp(filename, "-")) {
432 fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
433 if (fd < 0) {
434 error_errno("open");
435 return NULL;
436 }
437 } else {
438 fd = STDOUT_FILENO;
439 }
440
441 file = open_output_fd(fd, block_size, len, gz, sparse, chunks, crc);
442 if (!file) {
443 close(fd);
444 return NULL;
445 }
446
447 file->close_fd = true; // we opened descriptor thus we responsible for closing it
448
449 return file;
450}
451
452void pad_output_file(struct output_file *out, int64_t len)
453{
454 int ret;
455
456 if (len > out->len) {
457 error("attempted to pad file %llu bytes past end of filesystem",
458 len - out->len);
459 return;
460 }
461 if (out->sparse) {
462 /* We need to emit a DONT_CARE chunk to pad out the file if the
463 * cur_out_ptr is not already at the end of the filesystem.
464 */
465 if (len < out->cur_out_ptr) {
466 error("attempted to pad file %llu bytes less than the current output pointer",
467 out->cur_out_ptr - len);
468 return;
469 }
470 if (len > out->cur_out_ptr) {
471 emit_skip_chunk(out, len - out->cur_out_ptr);
472 }
473 } else {
474 //KEN TODO: Fixme. If the filesystem image needs no padding,
475 // this will overwrite the last byte in the file with 0
476 // The answer is to do accounting like the sparse image
477 // code does and know if there is already data there.
478 ret = out->ops->seek(out, len - 1);
479 if (ret < 0)
480 return;
481
482 ret = out->ops->write(out, (u8*)"", 1);
483 if (ret < 0)
484 return;
485 }
486}
487
488/* Write a contiguous region of data blocks from a memory buffer */
489void write_data_block(struct output_file *out, int64_t off, void *data, int len)
490{
491 int ret;
492
493 if (off + len > out->len) {
494 error("attempted to write block %llu past end of filesystem",
495 off + len - out->len);
496 return;
497 }
498
499 if (out->sparse) {
500 write_chunk_raw(out, off, data, len);
501 } else {
502 ret = out->ops->seek(out, off);
503 if (ret < 0)
504 return;
505
506 ret = out->ops->write(out, data, len);
507 if (ret < 0)
508 return;
509 }
510}
511
512/* Write a contiguous region of data blocks with a fill value */
513void write_fill_block(struct output_file *out, int64_t off, unsigned int fill_val, int len)
514{
515 int ret;
516 unsigned int i;
517 int write_len;
518 u32 fill_buf[4096/sizeof(u32)]; /* Maximum size of a block */
519
520 if (off + len > out->len) {
521 error("attempted to write block %llu past end of filesystem",
522 off + len - out->len);
523 return;
524 }
525
526 if (out->sparse) {
527 write_chunk_fill(out, off, fill_val, len);
528 } else {
529 /* Initialize fill_buf with the fill_val */
530 for (i = 0; i < sizeof(fill_buf)/sizeof(u32); i++) {
531 fill_buf[i] = fill_val;
532 }
533
534 ret = out->ops->seek(out, off);
535 if (ret < 0)
536 return;
537
538 while (len) {
539 write_len = (len > (int)sizeof(fill_buf) ? (int)sizeof(fill_buf) : len);
540 ret = out->ops->write(out, (u8 *)fill_buf, write_len);
541 if (ret < 0) {
542 return;
543 } else {
544 len -= write_len;
545 }
546 }
547 }
548}
549
550/* Write a contiguous region of data blocks from a file */
551void write_data_file(struct output_file *out, int64_t off, const char *file,
552 int64_t offset, int len)
553{
554 int ret;
555 int64_t aligned_offset;
556 int aligned_diff;
557 int buffer_size;
558
559 if (off + len >= out->len) {
560 error("attempted to write block %llu past end of filesystem",
561 off + len - out->len);
562 return;
563 }
564
565 int file_fd = open(file, O_RDONLY | O_BINARY);
566 if (file_fd < 0) {
567 error_errno("open");
568 return;
569 }
570
571 aligned_offset = offset & ~(4096 - 1);
572 aligned_diff = offset - aligned_offset;
573 buffer_size = len + aligned_diff;
574
575#ifndef USE_MINGW
576 u8 *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, file_fd,
577 aligned_offset);
578 if (data == MAP_FAILED) {
579 error_errno("mmap64");
580 close(file_fd);
581 return;
582 }
583#else
584 u8 *data = malloc(buffer_size);
585 if (!data) {
586 error_errno("malloc");
587 close(file_fd);
588 return;
589 }
590 memset(data, 0, buffer_size);
591#endif
592
593 if (out->sparse) {
594 write_chunk_raw(out, off, data + aligned_diff, len);
595 } else {
596 ret = out->ops->seek(out, off);
597 if (ret < 0)
598 goto err;
599
600 ret = out->ops->write(out, data + aligned_diff, len);
601 if (ret < 0)
602 goto err;
603 }
604
605err:
606#ifndef USE_MINGW
607 munmap(data, buffer_size);
608#else
609 write(file_fd, data, buffer_size);
610 free(data);
611#endif
612 close(file_fd);
613}