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