• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <fcntl.h>
24 #include <stdbool.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 #include <zlib.h>
30 
31 #ifndef USE_MINGW
32 #include <sys/mman.h>
33 #define O_BINARY 0
34 #endif
35 
36 
37 #if defined(__APPLE__) && defined(__MACH__)
38 #define lseek64 lseek
39 #define off64_t off_t
40 #endif
41 
42 #define SPARSE_HEADER_MAJOR_VER 1
43 #define SPARSE_HEADER_MINOR_VER 0
44 #define SPARSE_HEADER_LEN       (sizeof(sparse_header_t))
45 #define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
46 
47 struct output_file_ops {
48 	int (*seek)(struct output_file *, off64_t);
49 	int (*write)(struct output_file *, u8 *, int);
50 	void (*close)(struct output_file *);
51 };
52 
53 struct output_file {
54 	int fd;
55 	gzFile gz_fd;
56 	bool close_fd;
57 	int sparse;
58 	u64 cur_out_ptr;
59 	u32 chunk_cnt;
60 	u32 crc32;
61 	struct output_file_ops *ops;
62 	int use_crc;
63 };
64 
file_seek(struct output_file * out,off64_t off)65 static int file_seek(struct output_file *out, off64_t off)
66 {
67 	off64_t ret;
68 
69 	ret = lseek64(out->fd, off, SEEK_SET);
70 	if (ret < 0) {
71 		error_errno("lseek64");
72 		return -1;
73 	}
74 	return 0;
75 }
76 
file_write(struct output_file * out,u8 * data,int len)77 static int file_write(struct output_file *out, u8 *data, int len)
78 {
79 	int ret;
80 	ret = write(out->fd, data, len);
81 	if (ret < 0) {
82 		error_errno("write");
83 		return -1;
84 	} else if (ret < len) {
85 		error("incomplete write");
86 		return -1;
87 	}
88 
89 	return 0;
90 }
91 
file_close(struct output_file * out)92 static void file_close(struct output_file *out)
93 {
94 	if (out->close_fd) {
95 		close(out->fd);
96 	}
97 }
98 
99 
100 static struct output_file_ops file_ops = {
101 	.seek = file_seek,
102 	.write = file_write,
103 	.close = file_close,
104 };
105 
gz_file_seek(struct output_file * out,off64_t off)106 static int gz_file_seek(struct output_file *out, off64_t off)
107 {
108 	off64_t ret;
109 
110 	ret = gzseek(out->gz_fd, off, SEEK_SET);
111 	if (ret < 0) {
112 		error_errno("gzseek");
113 		return -1;
114 	}
115 	return 0;
116 }
117 
gz_file_write(struct output_file * out,u8 * data,int len)118 static int gz_file_write(struct output_file *out, u8 *data, int len)
119 {
120 	int ret;
121 	ret = gzwrite(out->gz_fd, data, len);
122 	if (ret < 0) {
123 		error_errno("gzwrite");
124 		return -1;
125 	} else if (ret < len) {
126 		error("incomplete gzwrite");
127 		return -1;
128 	}
129 
130 	return 0;
131 }
132 
gz_file_close(struct output_file * out)133 static void gz_file_close(struct output_file *out)
134 {
135 	gzclose(out->gz_fd);
136 }
137 
138 static struct output_file_ops gz_file_ops = {
139 	.seek = gz_file_seek,
140 	.write = gz_file_write,
141 	.close = gz_file_close,
142 };
143 
144 static sparse_header_t sparse_header = {
145 	.magic = SPARSE_HEADER_MAGIC,
146 	.major_version = SPARSE_HEADER_MAJOR_VER,
147 	.minor_version = SPARSE_HEADER_MINOR_VER,
148 	.file_hdr_sz = SPARSE_HEADER_LEN,
149 	.chunk_hdr_sz = CHUNK_HEADER_LEN,
150 	.blk_sz = 0,
151 	.total_blks = 0,
152 	.total_chunks = 0,
153 	.image_checksum = 0
154 };
155 
156 static u8 *zero_buf;
157 
emit_skip_chunk(struct output_file * out,u64 skip_len)158 static int emit_skip_chunk(struct output_file *out, u64 skip_len)
159 {
160 	chunk_header_t chunk_header;
161 	int ret, chunk;
162 
163 	//DBG printf("skip chunk: 0x%llx bytes\n", skip_len);
164 
165 	if (skip_len % info.block_size) {
166 		error("don't care size %llu is not a multiple of the block size %u",
167 				skip_len, info.block_size);
168 		return -1;
169 	}
170 
171 	/* We are skipping data, so emit a don't care chunk. */
172 	chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE;
173 	chunk_header.reserved1 = 0;
174 	chunk_header.chunk_sz = skip_len / info.block_size;
175 	chunk_header.total_sz = CHUNK_HEADER_LEN;
176 	ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header));
177 	if (ret < 0)
178 		return -1;
179 
180 	out->cur_out_ptr += skip_len;
181 	out->chunk_cnt++;
182 
183 	return 0;
184 }
185 
write_chunk_fill(struct output_file * out,u64 off,u32 fill_val,int len)186 static int write_chunk_fill(struct output_file *out, u64 off, u32 fill_val, int len)
187 {
188 	chunk_header_t chunk_header;
189 	int rnd_up_len, zero_len, count;
190 	int ret;
191 	unsigned int i;
192 	u32 fill_buf[4096/sizeof(u32)]; /* Maximum size of a block */
193 
194 	/* We can assume that all the chunks to be written are in
195 	 * ascending order, block-size aligned, and non-overlapping.
196 	 * So, if the offset is less than the current output pointer,
197 	 * throw an error, and if there is a gap, emit a "don't care"
198 	 * chunk.  The first write (of the super block) may not be
199 	 * blocksize aligned, so we need to deal with that too.
200 	 */
201 	//DBG printf("write chunk: offset 0x%llx, length 0x%x bytes\n", off, len);
202 
203 	if (off < out->cur_out_ptr) {
204 		error("offset %llu is less than the current output offset %llu",
205 				off, out->cur_out_ptr);
206 		return -1;
207 	}
208 
209 	if (off > out->cur_out_ptr) {
210 		emit_skip_chunk(out, off - out->cur_out_ptr);
211 	}
212 
213 	if (off % info.block_size) {
214 		error("write chunk offset %llu is not a multiple of the block size %u",
215 				off, info.block_size);
216 		return -1;
217 	}
218 
219 	if (off != out->cur_out_ptr) {
220 		error("internal error, offset accounting screwy in write_chunk_raw()");
221 		return -1;
222 	}
223 
224 	/* Round up the file length to a multiple of the block size */
225 	rnd_up_len = (len + (info.block_size - 1)) & (~(info.block_size -1));
226 
227 	/* Finally we can safely emit a chunk of data */
228 	chunk_header.chunk_type = CHUNK_TYPE_FILL;
229 	chunk_header.reserved1 = 0;
230 	chunk_header.chunk_sz = rnd_up_len / info.block_size;
231 	chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val);
232 	ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header));
233 
234 	if (ret < 0)
235 		return -1;
236 	ret = out->ops->write(out, (u8 *)&fill_val, sizeof(fill_val));
237 	if (ret < 0)
238 		return -1;
239 
240 	if (out->use_crc) {
241                 /* Initialize fill_buf with the fill_val */
242 		for (i = 0; i < (info.block_size / sizeof(u32)); i++) {
243 			fill_buf[i] = fill_val;
244 		}
245 
246 		count = chunk_header.chunk_sz;
247 		while (count) {
248 			out->crc32 = sparse_crc32(out->crc32, fill_buf, info.block_size);
249 			count--;
250 		}
251 	}
252 
253 	out->cur_out_ptr += rnd_up_len;
254 	out->chunk_cnt++;
255 
256 	return 0;
257 }
258 
write_chunk_raw(struct output_file * out,u64 off,u8 * data,int len)259 static int write_chunk_raw(struct output_file *out, u64 off, u8 *data, int len)
260 {
261 	chunk_header_t chunk_header;
262 	int rnd_up_len, zero_len;
263 	int ret;
264 
265 	/* We can assume that all the chunks to be written are in
266 	 * ascending order, block-size aligned, and non-overlapping.
267 	 * So, if the offset is less than the current output pointer,
268 	 * throw an error, and if there is a gap, emit a "don't care"
269 	 * chunk.  The first write (of the super block) may not be
270 	 * blocksize aligned, so we need to deal with that too.
271 	 */
272 	//DBG printf("write chunk: offset 0x%llx, length 0x%x bytes\n", off, len);
273 
274 	if (off < out->cur_out_ptr) {
275 		error("offset %llu is less than the current output offset %llu",
276 				off, out->cur_out_ptr);
277 		return -1;
278 	}
279 
280 	if (off > out->cur_out_ptr) {
281 		emit_skip_chunk(out, off - out->cur_out_ptr);
282 	}
283 
284 	if (off % info.block_size) {
285 		error("write chunk offset %llu is not a multiple of the block size %u",
286 				off, info.block_size);
287 		return -1;
288 	}
289 
290 	if (off != out->cur_out_ptr) {
291 		error("internal error, offset accounting screwy in write_chunk_raw()");
292 		return -1;
293 	}
294 
295 	/* Round up the file length to a multiple of the block size */
296 	rnd_up_len = (len + (info.block_size - 1)) & (~(info.block_size -1));
297 	zero_len = rnd_up_len - len;
298 
299 	/* Finally we can safely emit a chunk of data */
300 	chunk_header.chunk_type = CHUNK_TYPE_RAW;
301 	chunk_header.reserved1 = 0;
302 	chunk_header.chunk_sz = rnd_up_len / info.block_size;
303 	chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
304 	ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header));
305 
306 	if (ret < 0)
307 		return -1;
308 	ret = out->ops->write(out, data, len);
309 	if (ret < 0)
310 		return -1;
311 	if (zero_len) {
312 		ret = out->ops->write(out, zero_buf, zero_len);
313 		if (ret < 0)
314 			return -1;
315 	}
316 
317 	if (out->use_crc) {
318 		out->crc32 = sparse_crc32(out->crc32, data, len);
319 		if (zero_len)
320 			out->crc32 = sparse_crc32(out->crc32, zero_buf, zero_len);
321 	}
322 
323 	out->cur_out_ptr += rnd_up_len;
324 	out->chunk_cnt++;
325 
326 	return 0;
327 }
328 
close_output_file(struct output_file * out)329 void close_output_file(struct output_file *out)
330 {
331 	int ret;
332 	chunk_header_t chunk_header;
333 
334 	if (out->sparse) {
335 		if (out->use_crc) {
336 			chunk_header.chunk_type = CHUNK_TYPE_CRC32;
337 			chunk_header.reserved1 = 0;
338 			chunk_header.chunk_sz = 0;
339 			chunk_header.total_sz = CHUNK_HEADER_LEN + 4;
340 
341 			out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header));
342 			out->ops->write(out, (u8 *)&out->crc32, 4);
343 
344 			out->chunk_cnt++;
345 		}
346 
347 		if (out->chunk_cnt != sparse_header.total_chunks)
348 			error("sparse chunk count did not match: %d %d", out->chunk_cnt,
349 					sparse_header.total_chunks);
350 	}
351 	out->ops->close(out);
352 }
353 
open_output_fd(int fd,int gz,int sparse,int chunks,int crc,int wipe)354 struct output_file *open_output_fd(int fd, int gz, int sparse,
355         int chunks, int crc, int wipe)
356 {
357 	int ret;
358 	struct output_file *out = malloc(sizeof(struct output_file));
359 	if (!out) {
360 		error_errno("malloc struct out");
361 		return NULL;
362 	}
363 	zero_buf = malloc(info.block_size);
364 	if (!zero_buf) {
365 		error_errno("malloc zero_buf");
366 		free(out);
367 		return NULL;
368 	}
369 	memset(zero_buf, '\0', info.block_size);
370 
371 	if (gz) {
372 		out->ops = &gz_file_ops;
373 		out->gz_fd = gzdopen(fd, "wb9");
374 		if (!out->gz_fd) {
375 			error_errno("gzopen");
376 			free(out);
377 			return NULL;
378 		}
379 	} else {
380 		out->fd = fd;
381 		out->ops = &file_ops;
382 	}
383 	out->close_fd = false;
384 	out->sparse = sparse;
385 	out->cur_out_ptr = 0ll;
386 	out->chunk_cnt = 0;
387 
388 	/* Initialize the crc32 value */
389 	out->crc32 = 0;
390 	out->use_crc = crc;
391 
392 	if (wipe)
393 		wipe_block_device(out->fd, info.len);
394 
395 	if (out->sparse) {
396 		sparse_header.blk_sz = info.block_size,
397 		sparse_header.total_blks = info.len / info.block_size,
398 		sparse_header.total_chunks = chunks;
399 		if (out->use_crc)
400 			sparse_header.total_chunks++;
401 
402 		ret = out->ops->write(out, (u8 *)&sparse_header, sizeof(sparse_header));
403 		if (ret < 0)
404 			return NULL;
405 	}
406 
407 	return out;
408 }
409 
open_output_file(const char * filename,int gz,int sparse,int chunks,int crc,int wipe)410 struct output_file *open_output_file(const char *filename, int gz, int sparse,
411         int chunks, int crc, int wipe) {
412 
413 	int fd;
414 	struct output_file *file;
415 
416 	if (strcmp(filename, "-")) {
417 		fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
418 		if (fd < 0) {
419 			error_errno("open");
420 			return NULL;
421 		}
422 	} else {
423 		fd = STDOUT_FILENO;
424 	}
425 
426 	file = open_output_fd(fd, gz, sparse, chunks, crc, wipe);
427 	if (!file) {
428 		close(fd);
429 		return NULL;
430 	}
431 
432 	file->close_fd = true; // we opened descriptor thus we responsible for closing it
433 
434 	return file;
435 }
436 
pad_output_file(struct output_file * out,u64 len)437 void pad_output_file(struct output_file *out, u64 len)
438 {
439 	int ret;
440 
441 	if (len > (u64) info.len) {
442 		error("attempted to pad file %llu bytes past end of filesystem",
443 				len - info.len);
444 		return;
445 	}
446 	if (out->sparse) {
447 		/* We need to emit a DONT_CARE chunk to pad out the file if the
448 		 * cur_out_ptr is not already at the end of the filesystem.
449 		 */
450 		if (len < out->cur_out_ptr) {
451 			error("attempted to pad file %llu bytes less than the current output pointer",
452 					out->cur_out_ptr - len);
453 			return;
454 		}
455 		if (len > out->cur_out_ptr) {
456 			emit_skip_chunk(out, len - out->cur_out_ptr);
457 		}
458 	} else {
459 		//KEN TODO: Fixme.  If the filesystem image needs no padding,
460 		//          this will overwrite the last byte in the file with 0
461 		//          The answer is to do accounting like the sparse image
462 		//          code does and know if there is already data there.
463 		ret = out->ops->seek(out, len - 1);
464 		if (ret < 0)
465 			return;
466 
467 		ret = out->ops->write(out, (u8*)"", 1);
468 		if (ret < 0)
469 			return;
470 	}
471 }
472 
473 /* Write a contiguous region of data blocks from a memory buffer */
write_data_block(struct output_file * out,u64 off,u8 * data,int len)474 void write_data_block(struct output_file *out, u64 off, u8 *data, int len)
475 {
476 	int ret;
477 
478 	if (off + len > (u64) info.len) {
479 		error("attempted to write block %llu past end of filesystem",
480 				off + len - info.len);
481 		return;
482 	}
483 
484 	if (out->sparse) {
485 		write_chunk_raw(out, off, data, len);
486 	} else {
487 		ret = out->ops->seek(out, off);
488 		if (ret < 0)
489 			return;
490 
491 		ret = out->ops->write(out, data, len);
492 		if (ret < 0)
493 			return;
494 	}
495 }
496 
497 /* Write a contiguous region of data blocks with a fill value */
write_fill_block(struct output_file * out,u64 off,u32 fill_val,int len)498 void write_fill_block(struct output_file *out, u64 off, u32 fill_val, int len)
499 {
500 	int ret;
501 	unsigned int i;
502 	int write_len;
503 	u32 fill_buf[4096/sizeof(u32)]; /* Maximum size of a block */
504 
505 	if (off + len > (u64) info.len) {
506 		error("attempted to write block %llu past end of filesystem",
507 				off + len - info.len);
508 		return;
509 	}
510 
511 	if (out->sparse) {
512 		write_chunk_fill(out, off, fill_val, len);
513 	} else {
514 		/* Initialize fill_buf with the fill_val */
515 		for (i = 0; i < sizeof(fill_buf)/sizeof(u32); i++) {
516 			fill_buf[i] = fill_val;
517 		}
518 
519 		ret = out->ops->seek(out, off);
520 		if (ret < 0)
521 			return;
522 
523 		while (len) {
524 			write_len = (len > (int)sizeof(fill_buf) ? (int)sizeof(fill_buf) : len);
525 			ret = out->ops->write(out, (u8 *)fill_buf, write_len);
526 			if (ret < 0) {
527 				return;
528 			} else {
529 				len -= write_len;
530 			}
531 		}
532 	}
533 }
534 
535 /* Write a contiguous region of data blocks from a file */
write_data_file(struct output_file * out,u64 off,const char * file,off64_t offset,int len)536 void write_data_file(struct output_file *out, u64 off, const char *file,
537 		     off64_t offset, int len)
538 {
539 	int ret;
540 	off64_t aligned_offset;
541 	int aligned_diff;
542 	int buffer_size;
543 
544 	if (off + len >= (u64) info.len) {
545 		error("attempted to write block %llu past end of filesystem",
546 				off + len - info.len);
547 		return;
548 	}
549 
550 	int file_fd = open(file, O_RDONLY | O_BINARY);
551 	if (file_fd < 0) {
552 		error_errno("open");
553 		return;
554 	}
555 
556 	aligned_offset = offset & ~(4096 - 1);
557 	aligned_diff = offset - aligned_offset;
558 	buffer_size = len + aligned_diff;
559 
560 #ifndef USE_MINGW
561 	u8 *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, file_fd,
562 			aligned_offset);
563 	if (data == MAP_FAILED) {
564 		error_errno("mmap64");
565 		close(file_fd);
566 		return;
567 	}
568 #else
569 	u8 *data = malloc(buffer_size);
570 	if (!data) {
571 		error_errno("malloc");
572 		close(file_fd);
573 		return;
574 	}
575 	memset(data, 0, buffer_size);
576 #endif
577 
578 	if (out->sparse) {
579 		write_chunk_raw(out, off, data + aligned_diff, len);
580 	} else {
581 		ret = out->ops->seek(out, off);
582 		if (ret < 0)
583 			goto err;
584 
585 		ret = out->ops->write(out, data + aligned_diff, len);
586 		if (ret < 0)
587 			goto err;
588 	}
589 
590 err:
591 #ifndef USE_MINGW
592 	munmap(data, buffer_size);
593 #else
594 	write(file_fd, data, buffer_size);
595 	free(data);
596 #endif
597 	close(file_fd);
598 }
599