output_file.c revision 8642b7fba54727a38f751516bcdc452fb09ef610
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#define _LARGEFILE64_SOURCE 17 18#include <sys/types.h> 19#include <sys/stat.h> 20#include <sys/types.h> 21#include <sys/mman.h> 22#include <unistd.h> 23#include <fcntl.h> 24 25#include <zlib.h> 26 27#include "ext4_utils.h" 28#include "output_file.h" 29#include "sparse_format.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 int 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; 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 // KEN: TODO: CRC computation 170 out->cur_out_ptr += skip_len; 171 out->chunk_cnt++; 172 173 return 0; 174} 175 176static int write_chunk_raw(struct output_file *out, u64 off, u8 *data, int len) 177{ 178 chunk_header_t chunk_header; 179 int rnd_up_len, zero_len; 180 int ret; 181 182 /* We can assume that all the chunks to be written are in 183 * ascending order, block-size aligned, and non-overlapping. 184 * So, if the offset is less than the current output pointer, 185 * throw an error, and if there is a gap, emit a "don't care" 186 * chunk. The first write (of the super block) may not be 187 * blocksize aligned, so we need to deal with that too. 188 */ 189 //DBG printf("write chunk: offset 0x%llx, length 0x%x bytes\n", off, len); 190 191 if (off < out->cur_out_ptr) { 192 error("offset %llu is less than the current output offset %llu", 193 off, out->cur_out_ptr); 194 return -1; 195 } 196 197 if (off > out->cur_out_ptr) { 198 emit_skip_chunk(out, off - out->cur_out_ptr); 199 } 200 201 if (off % info.block_size) { 202 error("write chunk offset %llu is not a multiple of the block size %u", 203 off, info.block_size); 204 return -1; 205 } 206 207 if (off != out->cur_out_ptr) { 208 error("internal error, offset accounting screwy in write_chunk_raw()"); 209 return -1; 210 } 211 212 /* Round up the file length to a multiple of the block size */ 213 rnd_up_len = (len + (info.block_size - 1)) & (~(info.block_size -1)); 214 zero_len = rnd_up_len - len; 215 216 /* Finally we can safely emit a chunk of data */ 217 chunk_header.chunk_type = CHUNK_TYPE_RAW; 218 chunk_header.reserved1 = 0; 219 chunk_header.chunk_sz = rnd_up_len / info.block_size; 220 chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len; 221 ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header)); 222 223 if (ret < 0) 224 return -1; 225 ret = out->ops->write(out, data, len); 226 if (ret < 0) 227 return -1; 228 if (zero_len) { 229 ret = out->ops->write(out, zero_buf, zero_len); 230 if (ret < 0) 231 return -1; 232 } 233 234 // KEN: TODO: CRC computation of both the raw data and and zero buf data written */ 235 out->cur_out_ptr += rnd_up_len; 236 out->chunk_cnt++; 237 238 return 0; 239} 240 241void close_output_file(struct output_file *out) 242{ 243 int ret; 244 245 if (out->sparse) { 246 /* we need to seek back to the beginning and update the file header */ 247 sparse_header.total_chunks = out->chunk_cnt; 248 sparse_header.image_checksum = out->crc32; 249 250 ret = out->ops->seek(out, 0); 251 if (ret < 0) 252 error("failure seeking to start of sparse file"); 253 254 ret = out->ops->write(out, (u8 *)&sparse_header, sizeof(sparse_header)); 255 if (ret < 0) 256 error("failure updating sparse file header"); 257 } 258 out->ops->close(out); 259} 260 261struct output_file *open_output_file(const char *filename, int gz, int sparse) 262{ 263 int ret; 264 struct output_file *out = malloc(sizeof(struct output_file)); 265 if (!out) { 266 error_errno("malloc struct out"); 267 return NULL; 268 } 269 zero_buf = malloc(info.block_size); 270 if (!zero_buf) { 271 error_errno("malloc zero_buf"); 272 return NULL; 273 } 274 memset(zero_buf, '\0', info.block_size); 275 276 if (gz) { 277 out->ops = &gz_file_ops; 278 out->gz_fd = gzopen(filename, "wb9"); 279 if (!out->gz_fd) { 280 error_errno("gzopen"); 281 free(out); 282 return NULL; 283 } 284 } else { 285 out->ops = &file_ops; 286 out->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); 287 if (out->fd < 0) { 288 error_errno("open"); 289 free(out); 290 return NULL; 291 } 292 } 293 out->sparse = sparse; 294 out->cur_out_ptr = 0ll; 295 out->chunk_cnt = 0; 296 if (out->sparse) { 297 /* Write out the file header. We'll update the unknown fields 298 * when we close the file. 299 */ 300 sparse_header.blk_sz = info.block_size, 301 sparse_header.total_blks = info.len / info.block_size, 302 ret = out->ops->write(out, (u8 *)&sparse_header, sizeof(sparse_header)); 303 if (ret < 0) 304 return NULL; 305 } 306 307 return out; 308} 309 310void pad_output_file(struct output_file *out, u64 len) 311{ 312 int ret; 313 314 if (len > info.len) { 315 error("attempted to pad file %llu bytes past end of filesystem", 316 len - info.len); 317 return; 318 } 319 if (out->sparse) { 320 /* We need to emit a DONT_CARE chunk to pad out the file if the 321 * cur_out_ptr is not already at the end of the filesystem. 322 * We also need to compute the CRC for it. 323 */ 324 //KEN: TODO: CRC computation! 325 if (len < out->cur_out_ptr) { 326 error("attempted to pad file %llu bytes less than the current output pointer", 327 out->cur_out_ptr - len); 328 return; 329 } 330 if (len > out->cur_out_ptr) { 331 emit_skip_chunk(out, len - out->cur_out_ptr); 332 } 333 } else { 334 //KEN TODO: Fixme. If the filesystem image needs no padding, 335 // this will overwrite the last byte in the file with 0 336 // The answer is to do accounting like the sparse image 337 // code does and know if there is already data there. 338 ret = out->ops->seek(out, len - 1); 339 if (ret < 0) 340 return; 341 342 ret = out->ops->write(out, (u8*)"", 1); 343 if (ret < 0) 344 return; 345 } 346} 347 348/* Write a contiguous region of data blocks from a memory buffer */ 349void write_data_block(struct output_file *out, u64 off, u8 *data, int len) 350{ 351 int ret; 352 353 if (off + len > info.len) { 354 error("attempted to write block %llu past end of filesystem", 355 off + len - info.len); 356 return; 357 } 358 359 if (out->sparse) { 360 write_chunk_raw(out, off, data, len); 361 } else { 362 ret = out->ops->seek(out, off); 363 if (ret < 0) 364 return; 365 366 ret = out->ops->write(out, data, len); 367 if (ret < 0) 368 return; 369 } 370} 371 372/* Write a contiguous region of data blocks from a file */ 373void write_data_file(struct output_file *out, u64 off, const char *file, 374 off_t offset, int len) 375{ 376 int ret; 377 378 if (off + len >= info.len) { 379 error("attempted to write block %llu past end of filesystem", 380 off + len - info.len); 381 return; 382 } 383 384 int file_fd = open(file, O_RDONLY); 385 if (file_fd < 0) { 386 error_errno("open"); 387 return; 388 } 389 390 u8 *data = mmap(NULL, len, PROT_READ, MAP_SHARED, file_fd, offset); 391 if (data == MAP_FAILED) { 392 error_errno("mmap"); 393 close(file_fd); 394 return; 395 } 396 397 if (out->sparse) { 398 write_chunk_raw(out, off, data, len); 399 } else { 400 ret = out->ops->seek(out, off); 401 if (ret < 0) 402 goto err; 403 404 ret = out->ops->write(out, data, len); 405 if (ret < 0) 406 goto err; 407 } 408 409 munmap(data, len); 410 411 close(file_fd); 412 413err: 414 munmap(data, len); 415 close(file_fd); 416} 417 418