blob: 39a6007b3c381edfdd017444f6677530988380c2 [file] [log] [blame]
Colin Cross7a8bee72010-06-20 14:53:14 -07001/*
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 */
Colin Cross33f96c62010-12-22 18:40:14 -080016
17#include "ext4_utils.h"
18#include "output_file.h"
19#include "sparse_format.h"
20#include "sparse_crc32.h"
Colin Crossc2470652011-01-26 16:39:46 -080021#include "wipe.h"
Colin Cross7a8bee72010-06-20 14:53:14 -070022
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
Ken Sumrall75249ed2010-08-13 16:04:49 -070032#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))
Colin Cross7a8bee72010-06-20 14:53:14 -070041
42struct output_file_ops {
Ken Sumrall75249ed2010-08-13 16:04:49 -070043 int (*seek)(struct output_file *, off64_t);
Colin Cross7a8bee72010-06-20 14:53:14 -070044 int (*write)(struct output_file *, u8 *, int);
45 void (*close)(struct output_file *);
46};
47
48struct output_file {
49 int fd;
50 gzFile gz_fd;
Ken Sumrall75249ed2010-08-13 16:04:49 -070051 int sparse;
52 u64 cur_out_ptr;
Colin Crossb7813302010-12-22 18:41:13 -080053 u32 chunk_cnt;
Ken Sumrall75249ed2010-08-13 16:04:49 -070054 u32 crc32;
Colin Cross7a8bee72010-06-20 14:53:14 -070055 struct output_file_ops *ops;
Colin Cross757ace52010-12-29 13:57:01 -080056 int use_crc;
Colin Cross7a8bee72010-06-20 14:53:14 -070057};
58
Ken Sumrall75249ed2010-08-13 16:04:49 -070059static int file_seek(struct output_file *out, off64_t off)
Colin Cross7a8bee72010-06-20 14:53:14 -070060{
Ken Sumrall75249ed2010-08-13 16:04:49 -070061 off64_t ret;
Colin Cross7a8bee72010-06-20 14:53:14 -070062
Ken Sumrall75249ed2010-08-13 16:04:49 -070063 ret = lseek64(out->fd, off, SEEK_SET);
Colin Cross7a8bee72010-06-20 14:53:14 -070064 if (ret < 0) {
Ken Sumrall75249ed2010-08-13 16:04:49 -070065 error_errno("lseek64");
Colin Cross7a8bee72010-06-20 14:53:14 -070066 return -1;
67 }
68 return 0;
69}
70
71static 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
86static void file_close(struct output_file *out)
87{
88 close(out->fd);
89}
90
91
92static struct output_file_ops file_ops = {
93 .seek = file_seek,
94 .write = file_write,
95 .close = file_close,
96};
97
Ken Sumrall75249ed2010-08-13 16:04:49 -070098static int gz_file_seek(struct output_file *out, off64_t off)
Colin Cross7a8bee72010-06-20 14:53:14 -070099{
Ken Sumrall75249ed2010-08-13 16:04:49 -0700100 off64_t ret;
Colin Cross7a8bee72010-06-20 14:53:14 -0700101
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
110static 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
125static void gz_file_close(struct output_file *out)
126{
127 gzclose(out->gz_fd);
128}
129
130static struct output_file_ops gz_file_ops = {
131 .seek = gz_file_seek,
132 .write = gz_file_write,
133 .close = gz_file_close,
134};
135
Ken Sumrall75249ed2010-08-13 16:04:49 -0700136static 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
148static u8 *zero_buf;
149
150static int emit_skip_chunk(struct output_file *out, u64 skip_len)
151{
152 chunk_header_t chunk_header;
Ken Sumrall5a618172010-08-16 19:17:38 -0700153 int ret, chunk;
Ken Sumrall75249ed2010-08-13 16:04:49 -0700154
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;
Ken Sumrall5a618172010-08-16 19:17:38 -0700171
Ken Sumrall75249ed2010-08-13 16:04:49 -0700172 out->cur_out_ptr += skip_len;
173 out->chunk_cnt++;
174
175 return 0;
176}
177
Ken Sumrall107a9f12011-06-29 20:28:30 -0700178static 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
Ken Sumrall75249ed2010-08-13 16:04:49 -0700251static 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
Colin Cross757ace52010-12-29 13:57:01 -0800309 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
Ken Sumrall75249ed2010-08-13 16:04:49 -0700315 out->cur_out_ptr += rnd_up_len;
316 out->chunk_cnt++;
317
318 return 0;
319}
320
Colin Cross7a8bee72010-06-20 14:53:14 -0700321void close_output_file(struct output_file *out)
322{
Ken Sumrall75249ed2010-08-13 16:04:49 -0700323 int ret;
Colin Crossb7813302010-12-22 18:41:13 -0800324 chunk_header_t chunk_header;
Ken Sumrall75249ed2010-08-13 16:04:49 -0700325
326 if (out->sparse) {
Colin Cross757ace52010-12-29 13:57:01 -0800327 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
Colin Crossb7813302010-12-22 18:41:13 -0800339 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);
Ken Sumrall75249ed2010-08-13 16:04:49 -0700342 }
Colin Cross7a8bee72010-06-20 14:53:14 -0700343 out->ops->close(out);
344}
345
Colin Crossb7813302010-12-22 18:41:13 -0800346struct output_file *open_output_file(const char *filename, int gz, int sparse,
Colin Crossc2470652011-01-26 16:39:46 -0800347 int chunks, int crc, int wipe)
Colin Cross7a8bee72010-06-20 14:53:14 -0700348{
Ken Sumrall75249ed2010-08-13 16:04:49 -0700349 int ret;
Colin Cross7a8bee72010-06-20 14:53:14 -0700350 struct output_file *out = malloc(sizeof(struct output_file));
351 if (!out) {
Ken Sumrall75249ed2010-08-13 16:04:49 -0700352 error_errno("malloc struct out");
Colin Cross7a8bee72010-06-20 14:53:14 -0700353 return NULL;
354 }
Ken Sumrall75249ed2010-08-13 16:04:49 -0700355 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);
Colin Cross7a8bee72010-06-20 14:53:14 -0700361
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 {
Colin Crossb7813302010-12-22 18:41:13 -0800371 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;
Colin Cross7a8bee72010-06-20 14:53:14 -0700380 }
Colin Crossb7813302010-12-22 18:41:13 -0800381 out->ops = &file_ops;
Colin Cross7a8bee72010-06-20 14:53:14 -0700382 }
Ken Sumrall75249ed2010-08-13 16:04:49 -0700383 out->sparse = sparse;
384 out->cur_out_ptr = 0ll;
385 out->chunk_cnt = 0;
Ken Sumrall5a618172010-08-16 19:17:38 -0700386
387 /* Initialize the crc32 value */
388 out->crc32 = 0;
Colin Cross757ace52010-12-29 13:57:01 -0800389 out->use_crc = crc;
Ken Sumrall5a618172010-08-16 19:17:38 -0700390
Colin Crossc2470652011-01-26 16:39:46 -0800391 if (wipe)
392 wipe_block_device(out->fd, info.len);
393
Ken Sumrall75249ed2010-08-13 16:04:49 -0700394 if (out->sparse) {
Ken Sumrall75249ed2010-08-13 16:04:49 -0700395 sparse_header.blk_sz = info.block_size,
396 sparse_header.total_blks = info.len / info.block_size,
Colin Crossb7813302010-12-22 18:41:13 -0800397 sparse_header.total_chunks = chunks;
Colin Cross757ace52010-12-29 13:57:01 -0800398 if (out->use_crc)
399 sparse_header.total_chunks++;
400
Ken Sumrall75249ed2010-08-13 16:04:49 -0700401 ret = out->ops->write(out, (u8 *)&sparse_header, sizeof(sparse_header));
402 if (ret < 0)
403 return NULL;
404 }
405
Colin Cross7a8bee72010-06-20 14:53:14 -0700406 return out;
407}
408
Ken Sumrall75249ed2010-08-13 16:04:49 -0700409void pad_output_file(struct output_file *out, u64 len)
410{
411 int ret;
412
Ken Sumrall435a8b62011-01-14 18:33:06 -0800413 if (len > (u64) info.len) {
Ken Sumrall75249ed2010-08-13 16:04:49 -0700414 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.
Ken Sumrall75249ed2010-08-13 16:04:49 -0700421 */
Ken Sumrall75249ed2010-08-13 16:04:49 -0700422 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
Colin Cross7a8bee72010-06-20 14:53:14 -0700445/* Write a contiguous region of data blocks from a memory buffer */
446void write_data_block(struct output_file *out, u64 off, u8 *data, int len)
447{
448 int ret;
449
Ken Sumrall435a8b62011-01-14 18:33:06 -0800450 if (off + len > (u64) info.len) {
Colin Cross7a8bee72010-06-20 14:53:14 -0700451 error("attempted to write block %llu past end of filesystem",
452 off + len - info.len);
453 return;
454 }
455
Ken Sumrall75249ed2010-08-13 16:04:49 -0700456 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;
Colin Cross7a8bee72010-06-20 14:53:14 -0700462
Ken Sumrall75249ed2010-08-13 16:04:49 -0700463 ret = out->ops->write(out, data, len);
464 if (ret < 0)
465 return;
466 }
Colin Cross7a8bee72010-06-20 14:53:14 -0700467}
468
Ken Sumrall107a9f12011-06-29 20:28:30 -0700469/* Write a contiguous region of data blocks with a fill value */
470void 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
Colin Cross7a8bee72010-06-20 14:53:14 -0700507/* Write a contiguous region of data blocks from a file */
508void write_data_file(struct output_file *out, u64 off, const char *file,
Colin Cross33f96c62010-12-22 18:40:14 -0800509 off64_t offset, int len)
Colin Cross7a8bee72010-06-20 14:53:14 -0700510{
511 int ret;
Colin Cross33f96c62010-12-22 18:40:14 -0800512 off64_t aligned_offset;
513 int aligned_diff;
Colin Cross7a8bee72010-06-20 14:53:14 -0700514
Ken Sumrall435a8b62011-01-14 18:33:06 -0800515 if (off + len >= (u64) info.len) {
Colin Cross7a8bee72010-06-20 14:53:14 -0700516 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
Colin Cross33f96c62010-12-22 18:40:14 -0800527 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);
Colin Cross7a8bee72010-06-20 14:53:14 -0700532 if (data == MAP_FAILED) {
Colin Cross33f96c62010-12-22 18:40:14 -0800533 error_errno("mmap64");
Colin Cross7a8bee72010-06-20 14:53:14 -0700534 close(file_fd);
535 return;
536 }
537
Ken Sumrall75249ed2010-08-13 16:04:49 -0700538 if (out->sparse) {
Colin Cross33f96c62010-12-22 18:40:14 -0800539 write_chunk_raw(out, off, data + aligned_diff, len);
Ken Sumrall75249ed2010-08-13 16:04:49 -0700540 } else {
541 ret = out->ops->seek(out, off);
542 if (ret < 0)
543 goto err;
Colin Cross7a8bee72010-06-20 14:53:14 -0700544
Colin Cross33f96c62010-12-22 18:40:14 -0800545 ret = out->ops->write(out, data + aligned_diff, len);
Ken Sumrall75249ed2010-08-13 16:04:49 -0700546 if (ret < 0)
547 goto err;
548 }
Colin Cross7a8bee72010-06-20 14:53:14 -0700549
550 munmap(data, len);
551
552 close(file_fd);
553
554err:
555 munmap(data, len);
556 close(file_fd);
557}