• 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 <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 
file_seek(struct output_file * out,off64_t off)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 
file_write(struct output_file * out,u8 * data,int len)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 
file_close(struct output_file * out)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 
gz_file_seek(struct output_file * out,off64_t off)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 
gz_file_write(struct output_file * out,u8 * data,int len)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 
gz_file_close(struct output_file * out)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 
emit_skip_chunk(struct output_file * out,u64 skip_len)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 
write_chunk_fill(struct output_file * out,u64 off,u32 fill_val,int len)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 
write_chunk_raw(struct output_file * out,u64 off,u8 * data,int len)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 
close_output_file(struct output_file * out)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 
open_output_file(const char * filename,int gz,int sparse,int chunks,int crc,int wipe)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 
pad_output_file(struct output_file * out,u64 len)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 */
write_data_block(struct output_file * out,u64 off,u8 * data,int len)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 */
write_fill_block(struct output_file * out,u64 off,u32 fill_val,int len)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 */
write_data_file(struct output_file * out,u64 off,const char * file,off64_t offset,int len)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