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