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