output_file.c revision b55dceea986ab24f8b836b5116b389ed619c816e
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 <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 min(a, b) \ 55 ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; }) 56 57#define SPARSE_HEADER_MAJOR_VER 1 58#define SPARSE_HEADER_MINOR_VER 0 59#define SPARSE_HEADER_LEN (sizeof(sparse_header_t)) 60#define CHUNK_HEADER_LEN (sizeof(chunk_header_t)) 61 62struct output_file_ops { 63 int (*skip)(struct output_file *, int64_t); 64 int (*write)(struct output_file *, void *, int); 65 void (*close)(struct output_file *); 66}; 67 68struct sparse_file_ops { 69 int (*write_data_chunk)(struct output_file *out, unsigned int len, 70 void *data); 71 int (*write_fill_chunk)(struct output_file *out, unsigned int len, 72 uint32_t fill_val); 73 int (*write_skip_chunk)(struct output_file *out, int64_t len); 74 int (*write_end_chunk)(struct output_file *out); 75}; 76 77struct output_file { 78 int fd; 79 gzFile gz_fd; 80 bool close_fd; 81 int64_t cur_out_ptr; 82 unsigned int chunk_cnt; 83 uint32_t crc32; 84 struct output_file_ops *ops; 85 struct sparse_file_ops *sparse_ops; 86 int use_crc; 87 unsigned int block_size; 88 int64_t len; 89 char *zero_buf; 90 uint32_t *fill_buf; 91}; 92 93static int file_skip(struct output_file *out, int64_t cnt) 94{ 95 off64_t ret; 96 97 ret = lseek64(out->fd, cnt, SEEK_CUR); 98 if (ret < 0) { 99 error_errno("lseek64"); 100 return -1; 101 } 102 return 0; 103} 104 105static int file_write(struct output_file *out, void *data, int len) 106{ 107 int ret; 108 ret = write(out->fd, data, len); 109 if (ret < 0) { 110 error_errno("write"); 111 return -1; 112 } else if (ret < len) { 113 error("incomplete write"); 114 return -1; 115 } 116 117 return 0; 118} 119 120static void file_close(struct output_file *out) 121{ 122 if (out->close_fd) { 123 close(out->fd); 124 } 125} 126 127static struct output_file_ops file_ops = { 128 .skip = file_skip, 129 .write = file_write, 130 .close = file_close, 131}; 132 133static int gz_file_skip(struct output_file *out, int64_t cnt) 134{ 135 off64_t ret; 136 137 ret = gzseek(out->gz_fd, cnt, SEEK_CUR); 138 if (ret < 0) { 139 error_errno("gzseek"); 140 return -1; 141 } 142 return 0; 143} 144 145static int gz_file_write(struct output_file *out, void *data, int len) 146{ 147 int ret; 148 ret = gzwrite(out->gz_fd, data, len); 149 if (ret < 0) { 150 error_errno("gzwrite"); 151 return -1; 152 } else if (ret < len) { 153 error("incomplete gzwrite"); 154 return -1; 155 } 156 157 return 0; 158} 159 160static void gz_file_close(struct output_file *out) 161{ 162 gzclose(out->gz_fd); 163} 164 165static struct output_file_ops gz_file_ops = { 166 .skip = gz_file_skip, 167 .write = gz_file_write, 168 .close = gz_file_close, 169}; 170 171static int write_sparse_skip_chunk(struct output_file *out, int64_t skip_len) 172{ 173 chunk_header_t chunk_header; 174 int ret, chunk; 175 176 if (skip_len % out->block_size) { 177 error("don't care size %llu is not a multiple of the block size %u", 178 skip_len, out->block_size); 179 return -1; 180 } 181 182 /* We are skipping data, so emit a don't care chunk. */ 183 chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE; 184 chunk_header.reserved1 = 0; 185 chunk_header.chunk_sz = skip_len / out->block_size; 186 chunk_header.total_sz = CHUNK_HEADER_LEN; 187 ret = out->ops->write(out, &chunk_header, sizeof(chunk_header)); 188 if (ret < 0) 189 return -1; 190 191 out->cur_out_ptr += skip_len; 192 out->chunk_cnt++; 193 194 return 0; 195} 196 197static int write_sparse_fill_chunk(struct output_file *out, unsigned int len, 198 uint32_t fill_val) 199{ 200 chunk_header_t chunk_header; 201 int rnd_up_len, zero_len, count; 202 int ret; 203 unsigned int i; 204 205 /* Round up the fill length to a multiple of the block size */ 206 rnd_up_len = ALIGN(len, out->block_size); 207 208 /* Finally we can safely emit a chunk of data */ 209 chunk_header.chunk_type = CHUNK_TYPE_FILL; 210 chunk_header.reserved1 = 0; 211 chunk_header.chunk_sz = rnd_up_len / out->block_size; 212 chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val); 213 ret = out->ops->write(out, &chunk_header, sizeof(chunk_header)); 214 215 if (ret < 0) 216 return -1; 217 ret = out->ops->write(out, &fill_val, sizeof(fill_val)); 218 if (ret < 0) 219 return -1; 220 221 if (out->use_crc) { 222 count = out->block_size / sizeof(uint32_t); 223 while (count--) 224 out->crc32 = sparse_crc32(out->crc32, &fill_val, sizeof(uint32_t)); 225 } 226 227 out->cur_out_ptr += rnd_up_len; 228 out->chunk_cnt++; 229 230 return 0; 231} 232 233static int write_sparse_data_chunk(struct output_file *out, unsigned int len, 234 void *data) 235{ 236 chunk_header_t chunk_header; 237 int rnd_up_len, zero_len; 238 int ret; 239 240 /* Round up the data length to a multiple of the block size */ 241 rnd_up_len = ALIGN(len, out->block_size); 242 zero_len = rnd_up_len - len; 243 244 /* Finally we can safely emit a chunk of data */ 245 chunk_header.chunk_type = CHUNK_TYPE_RAW; 246 chunk_header.reserved1 = 0; 247 chunk_header.chunk_sz = rnd_up_len / out->block_size; 248 chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len; 249 ret = out->ops->write(out, &chunk_header, sizeof(chunk_header)); 250 251 if (ret < 0) 252 return -1; 253 ret = out->ops->write(out, data, len); 254 if (ret < 0) 255 return -1; 256 if (zero_len) { 257 ret = out->ops->write(out, out->zero_buf, zero_len); 258 if (ret < 0) 259 return -1; 260 } 261 262 if (out->use_crc) { 263 out->crc32 = sparse_crc32(out->crc32, data, len); 264 if (zero_len) 265 out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len); 266 } 267 268 out->cur_out_ptr += rnd_up_len; 269 out->chunk_cnt++; 270 271 return 0; 272} 273 274int write_sparse_end_chunk(struct output_file *out) 275{ 276 chunk_header_t chunk_header; 277 int ret; 278 279 if (out->use_crc) { 280 chunk_header.chunk_type = CHUNK_TYPE_CRC32; 281 chunk_header.reserved1 = 0; 282 chunk_header.chunk_sz = 0; 283 chunk_header.total_sz = CHUNK_HEADER_LEN + 4; 284 285 ret = out->ops->write(out, &chunk_header, sizeof(chunk_header)); 286 if (ret < 0) { 287 return ret; 288 } 289 out->ops->write(out, &out->crc32, 4); 290 if (ret < 0) { 291 return ret; 292 } 293 294 out->chunk_cnt++; 295 } 296 297 return 0; 298} 299 300static struct sparse_file_ops sparse_file_ops = { 301 .write_data_chunk = write_sparse_data_chunk, 302 .write_fill_chunk = write_sparse_fill_chunk, 303 .write_skip_chunk = write_sparse_skip_chunk, 304 .write_end_chunk = write_sparse_end_chunk, 305}; 306 307static int write_normal_data_chunk(struct output_file *out, unsigned int len, 308 void *data) 309{ 310 int ret; 311 unsigned int rnd_up_len = ALIGN(len, out->block_size); 312 313 ret = out->ops->write(out, data, len); 314 if (ret < 0) { 315 return ret; 316 } 317 318 if (rnd_up_len > len) { 319 ret = out->ops->skip(out, rnd_up_len - len); 320 } 321 322 return ret; 323} 324 325static int write_normal_fill_chunk(struct output_file *out, unsigned int len, 326 uint32_t fill_val) 327{ 328 int ret; 329 unsigned int i; 330 unsigned int write_len; 331 332 /* Initialize fill_buf with the fill_val */ 333 for (i = 0; i < out->block_size / sizeof(uint32_t); i++) { 334 out->fill_buf[i] = fill_val; 335 } 336 337 while (len) { 338 write_len = min(len, out->block_size); 339 ret = out->ops->write(out, out->fill_buf, write_len); 340 if (ret < 0) { 341 return ret; 342 } 343 344 len -= write_len; 345 } 346 347 return 0; 348} 349 350static int write_normal_skip_chunk(struct output_file *out, int64_t len) 351{ 352 int ret; 353 354 return out->ops->skip(out, len); 355} 356 357int write_normal_end_chunk(struct output_file *out) 358{ 359 int ret; 360 361 ret = ftruncate64(out->fd, out->len); 362 if (ret < 0) { 363 return -errno; 364 } 365 366 return 0; 367} 368 369static struct sparse_file_ops normal_file_ops = { 370 .write_data_chunk = write_normal_data_chunk, 371 .write_fill_chunk = write_normal_fill_chunk, 372 .write_skip_chunk = write_normal_skip_chunk, 373 .write_end_chunk = write_normal_end_chunk, 374}; 375 376void close_output_file(struct output_file *out) 377{ 378 int ret; 379 380 out->sparse_ops->write_end_chunk(out); 381 out->ops->close(out); 382 free(out); 383} 384 385struct output_file *open_output_fd(int fd, unsigned int block_size, int64_t len, 386 int gz, int sparse, int chunks, int crc) 387{ 388 int ret; 389 struct output_file *out = malloc(sizeof(struct output_file)); 390 if (!out) { 391 error_errno("malloc struct out"); 392 return NULL; 393 } 394 out->zero_buf = calloc(block_size, 1); 395 if (!out->zero_buf) { 396 error_errno("malloc zero_buf"); 397 goto err_zero_buf; 398 } 399 400 out->fill_buf = calloc(block_size, 1); 401 if (!out->fill_buf) { 402 error_errno("malloc fill_buf"); 403 goto err_fill_buf; 404 } 405 406 if (gz) { 407 out->ops = &gz_file_ops; 408 out->gz_fd = gzdopen(fd, "wb9"); 409 if (!out->gz_fd) { 410 error_errno("gzopen"); 411 goto err_gzopen; 412 } 413 } else { 414 out->fd = fd; 415 out->ops = &file_ops; 416 } 417 418 if (sparse) { 419 out->sparse_ops = &sparse_file_ops; 420 } else { 421 out->sparse_ops = &normal_file_ops; 422 } 423 424 out->close_fd = false; 425 out->cur_out_ptr = 0ll; 426 out->chunk_cnt = 0; 427 428 /* Initialize the crc32 value */ 429 out->crc32 = 0; 430 out->use_crc = crc; 431 432 out->len = len; 433 out->block_size = block_size; 434 435 if (sparse) { 436 sparse_header_t sparse_header = { 437 .magic = SPARSE_HEADER_MAGIC, 438 .major_version = SPARSE_HEADER_MAJOR_VER, 439 .minor_version = SPARSE_HEADER_MINOR_VER, 440 .file_hdr_sz = SPARSE_HEADER_LEN, 441 .chunk_hdr_sz = CHUNK_HEADER_LEN, 442 .blk_sz = out->block_size, 443 .total_blks = out->len / out->block_size, 444 .total_chunks = chunks, 445 .image_checksum = 0 446 }; 447 448 if (out->use_crc) { 449 sparse_header.total_chunks++; 450 } 451 452 ret = out->ops->write(out, &sparse_header, sizeof(sparse_header)); 453 if (ret < 0) { 454 goto err_write; 455 } 456 } 457 458 return out; 459 460err_write: 461 if (gz) { 462 gzclose(out->gz_fd); 463 } 464err_gzopen: 465 free(out->fill_buf); 466err_fill_buf: 467 free(out->zero_buf); 468err_zero_buf: 469 free(out); 470 return NULL; 471} 472 473struct output_file *open_output_file(const char *filename, 474 unsigned int block_size, int64_t len, 475 int gz, int sparse, int chunks, int crc) 476{ 477 int fd; 478 struct output_file *file; 479 480 if (strcmp(filename, "-")) { 481 fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); 482 if (fd < 0) { 483 error_errno("open"); 484 return NULL; 485 } 486 } else { 487 fd = STDOUT_FILENO; 488 } 489 490 file = open_output_fd(fd, block_size, len, gz, sparse, chunks, crc); 491 if (!file) { 492 close(fd); 493 return NULL; 494 } 495 496 file->close_fd = true; // we opened descriptor thus we responsible for closing it 497 498 return file; 499} 500 501/* Write a contiguous region of data blocks from a memory buffer */ 502int write_data_chunk(struct output_file *out, unsigned int len, void *data) 503{ 504 return out->sparse_ops->write_data_chunk(out, len, data); 505} 506 507/* Write a contiguous region of data blocks with a fill value */ 508int write_fill_chunk(struct output_file *out, unsigned int len, 509 uint32_t fill_val) 510{ 511 return out->sparse_ops->write_fill_chunk(out, len, fill_val); 512} 513 514/* Write a contiguous region of data blocks from a file */ 515int write_file_chunk(struct output_file *out, unsigned int len, 516 const char *file, int64_t offset) 517{ 518 int ret; 519 int64_t aligned_offset; 520 int aligned_diff; 521 int buffer_size; 522 523 int file_fd = open(file, O_RDONLY | O_BINARY); 524 if (file_fd < 0) { 525 return -errno; 526 } 527 528 aligned_offset = offset & ~(4096 - 1); 529 aligned_diff = offset - aligned_offset; 530 buffer_size = len + aligned_diff; 531 532#ifndef USE_MINGW 533 char *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, file_fd, 534 aligned_offset); 535 if (data == MAP_FAILED) { 536 ret = -errno; 537 close(file_fd); 538 return ret; 539 } 540#else 541 char *data = malloc(buffer_size); 542 if (!data) { 543 ret = -errno; 544 close(file_fd); 545 return ret; 546 } 547 memset(data, 0, buffer_size); 548#endif 549 550 ret = out->sparse_ops->write_data_chunk(out, len, data + aligned_diff); 551 552#ifndef USE_MINGW 553 munmap(data, buffer_size); 554#else 555 free(data); 556#endif 557 close(file_fd); 558 559 return ret; 560} 561 562int write_skip_chunk(struct output_file *out, int64_t len) 563{ 564 return out->sparse_ops->write_skip_chunk(out, len); 565} 566