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 #include "sparse_crc32.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 int chunk_cnt; 54 u32 crc32; 55 struct output_file_ops *ops; 56 }; 57 58 static 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 70 static 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 85 static void file_close(struct output_file *out) 86 { 87 close(out->fd); 88 } 89 90 91 static struct output_file_ops file_ops = { 92 .seek = file_seek, 93 .write = file_write, 94 .close = file_close, 95 }; 96 97 static 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 109 static 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 124 static void gz_file_close(struct output_file *out) 125 { 126 gzclose(out->gz_fd); 127 } 128 129 static struct output_file_ops gz_file_ops = { 130 .seek = gz_file_seek, 131 .write = gz_file_write, 132 .close = gz_file_close, 133 }; 134 135 static 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 147 static u8 *zero_buf; 148 149 static 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 /* Compute the CRC for all those zeroes. Do it block_size bytes at a time. */ 175 while (skip_len) { 176 chunk = (skip_len > info.block_size) ? info.block_size : skip_len; 177 out->crc32 = sparse_crc32(out->crc32, zero_buf, chunk); 178 skip_len -= chunk; 179 } 180 181 return 0; 182 } 183 184 static int write_chunk_raw(struct output_file *out, u64 off, u8 *data, int len) 185 { 186 chunk_header_t chunk_header; 187 int rnd_up_len, zero_len; 188 int ret; 189 190 /* We can assume that all the chunks to be written are in 191 * ascending order, block-size aligned, and non-overlapping. 192 * So, if the offset is less than the current output pointer, 193 * throw an error, and if there is a gap, emit a "don't care" 194 * chunk. The first write (of the super block) may not be 195 * blocksize aligned, so we need to deal with that too. 196 */ 197 //DBG printf("write chunk: offset 0x%llx, length 0x%x bytes\n", off, len); 198 199 if (off < out->cur_out_ptr) { 200 error("offset %llu is less than the current output offset %llu", 201 off, out->cur_out_ptr); 202 return -1; 203 } 204 205 if (off > out->cur_out_ptr) { 206 emit_skip_chunk(out, off - out->cur_out_ptr); 207 } 208 209 if (off % info.block_size) { 210 error("write chunk offset %llu is not a multiple of the block size %u", 211 off, info.block_size); 212 return -1; 213 } 214 215 if (off != out->cur_out_ptr) { 216 error("internal error, offset accounting screwy in write_chunk_raw()"); 217 return -1; 218 } 219 220 /* Round up the file length to a multiple of the block size */ 221 rnd_up_len = (len + (info.block_size - 1)) & (~(info.block_size -1)); 222 zero_len = rnd_up_len - len; 223 224 /* Finally we can safely emit a chunk of data */ 225 chunk_header.chunk_type = CHUNK_TYPE_RAW; 226 chunk_header.reserved1 = 0; 227 chunk_header.chunk_sz = rnd_up_len / info.block_size; 228 chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len; 229 ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header)); 230 231 if (ret < 0) 232 return -1; 233 ret = out->ops->write(out, data, len); 234 if (ret < 0) 235 return -1; 236 if (zero_len) { 237 ret = out->ops->write(out, zero_buf, zero_len); 238 if (ret < 0) 239 return -1; 240 } 241 242 out->crc32 = sparse_crc32(out->crc32, data, len); 243 if (zero_len) 244 out->crc32 = sparse_crc32(out->crc32, zero_buf, zero_len); 245 out->cur_out_ptr += rnd_up_len; 246 out->chunk_cnt++; 247 248 return 0; 249 } 250 251 void close_output_file(struct output_file *out) 252 { 253 int ret; 254 255 if (out->sparse) { 256 /* we need to seek back to the beginning and update the file header */ 257 sparse_header.total_chunks = out->chunk_cnt; 258 sparse_header.image_checksum = out->crc32; 259 260 ret = out->ops->seek(out, 0); 261 if (ret < 0) 262 error("failure seeking to start of sparse file"); 263 264 ret = out->ops->write(out, (u8 *)&sparse_header, sizeof(sparse_header)); 265 if (ret < 0) 266 error("failure updating sparse file header"); 267 } 268 out->ops->close(out); 269 } 270 271 struct output_file *open_output_file(const char *filename, int gz, int sparse) 272 { 273 int ret; 274 struct output_file *out = malloc(sizeof(struct output_file)); 275 if (!out) { 276 error_errno("malloc struct out"); 277 return NULL; 278 } 279 zero_buf = malloc(info.block_size); 280 if (!zero_buf) { 281 error_errno("malloc zero_buf"); 282 return NULL; 283 } 284 memset(zero_buf, '\0', info.block_size); 285 286 if (gz) { 287 out->ops = &gz_file_ops; 288 out->gz_fd = gzopen(filename, "wb9"); 289 if (!out->gz_fd) { 290 error_errno("gzopen"); 291 free(out); 292 return NULL; 293 } 294 } else { 295 out->ops = &file_ops; 296 out->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); 297 if (out->fd < 0) { 298 error_errno("open"); 299 free(out); 300 return NULL; 301 } 302 } 303 out->sparse = sparse; 304 out->cur_out_ptr = 0ll; 305 out->chunk_cnt = 0; 306 307 /* Initialize the crc32 value */ 308 out->crc32 = 0; 309 310 if (out->sparse) { 311 /* Write out the file header. We'll update the unknown fields 312 * when we close the file. 313 */ 314 sparse_header.blk_sz = info.block_size, 315 sparse_header.total_blks = info.len / info.block_size, 316 ret = out->ops->write(out, (u8 *)&sparse_header, sizeof(sparse_header)); 317 if (ret < 0) 318 return NULL; 319 } 320 321 return out; 322 } 323 324 void pad_output_file(struct output_file *out, u64 len) 325 { 326 int ret; 327 328 if (len > info.len) { 329 error("attempted to pad file %llu bytes past end of filesystem", 330 len - info.len); 331 return; 332 } 333 if (out->sparse) { 334 /* We need to emit a DONT_CARE chunk to pad out the file if the 335 * cur_out_ptr is not already at the end of the filesystem. 336 * We also need to compute the CRC for it. 337 */ 338 if (len < out->cur_out_ptr) { 339 error("attempted to pad file %llu bytes less than the current output pointer", 340 out->cur_out_ptr - len); 341 return; 342 } 343 if (len > out->cur_out_ptr) { 344 emit_skip_chunk(out, len - out->cur_out_ptr); 345 } 346 } else { 347 //KEN TODO: Fixme. If the filesystem image needs no padding, 348 // this will overwrite the last byte in the file with 0 349 // The answer is to do accounting like the sparse image 350 // code does and know if there is already data there. 351 ret = out->ops->seek(out, len - 1); 352 if (ret < 0) 353 return; 354 355 ret = out->ops->write(out, (u8*)"", 1); 356 if (ret < 0) 357 return; 358 } 359 } 360 361 /* Write a contiguous region of data blocks from a memory buffer */ 362 void write_data_block(struct output_file *out, u64 off, u8 *data, int len) 363 { 364 int ret; 365 366 if (off + len > info.len) { 367 error("attempted to write block %llu past end of filesystem", 368 off + len - info.len); 369 return; 370 } 371 372 if (out->sparse) { 373 write_chunk_raw(out, off, data, len); 374 } else { 375 ret = out->ops->seek(out, off); 376 if (ret < 0) 377 return; 378 379 ret = out->ops->write(out, data, len); 380 if (ret < 0) 381 return; 382 } 383 } 384 385 /* Write a contiguous region of data blocks from a file */ 386 void write_data_file(struct output_file *out, u64 off, const char *file, 387 off_t offset, int len) 388 { 389 int ret; 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 u8 *data = mmap(NULL, len, PROT_READ, MAP_SHARED, file_fd, offset); 404 if (data == MAP_FAILED) { 405 error_errno("mmap"); 406 close(file_fd); 407 return; 408 } 409 410 if (out->sparse) { 411 write_chunk_raw(out, off, data, len); 412 } else { 413 ret = out->ops->seek(out, off); 414 if (ret < 0) 415 goto err; 416 417 ret = out->ops->write(out, data, len); 418 if (ret < 0) 419 goto err; 420 } 421 422 munmap(data, len); 423 424 close(file_fd); 425 426 err: 427 munmap(data, len); 428 close(file_fd); 429 } 430 431