Home | History | Annotate | Download | only in ext4_utils
      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