2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #define _LARGEFILE64_SOURCE
18 #include <sys/types.h>
20 #include <sys/types.h>
27 #include "ext4_utils.h"
28 #include "output_file.h"
29 #include "sparse_format.h"
30 #include "sparse_crc32.h"
32 #if defined(__APPLE__) && defined(__MACH__)
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))
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 *);
55 struct output_file_ops *ops;
58 static int file_seek(struct output_file *out, off64_t off)
62 ret = lseek64(out->fd, off, SEEK_SET);
64 error_errno("lseek64");
70 static int file_write(struct output_file *out, u8 *data, int len)
73 ret = write(out->fd, data, len);
77 } else if (ret < len) {
78 error("incomplete write");
85 static void file_close(struct output_file *out)
91 static struct output_file_ops file_ops = {
97 static int gz_file_seek(struct output_file *out, off64_t off)
101 ret = gzseek(out->gz_fd, off, SEEK_SET);
103 error_errno("gzseek");
109 static int gz_file_write(struct output_file *out, u8 *data, int len)
112 ret = gzwrite(out->gz_fd, data, len);
114 error_errno("gzwrite");
116 } else if (ret < len) {
117 error("incomplete gzwrite");
124 static void gz_file_close(struct output_file *out)
129 static struct output_file_ops gz_file_ops = {
130 .seek = gz_file_seek,
131 .write = gz_file_write,
132 .close = gz_file_close,
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,
149 static int emit_skip_chunk(struct output_file *out, u64 skip_len)
151 chunk_header_t chunk_header;
154 //DBG printf("skip chunk: 0x%llx bytes\n", skip_len);
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);
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));
171 out->cur_out_ptr += skip_len;
174 /* Compute the CRC for all those zeroes. Do it block_size bytes at a time. */
176 chunk = (skip_len > info.block_size) ? info.block_size : skip_len;
177 out->crc32 = sparse_crc32(out->crc32, zero_buf, chunk);
184 static int write_chunk_raw(struct output_file *out, u64 off, u8 *data, int len)
186 chunk_header_t chunk_header;
187 int rnd_up_len, zero_len;
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.
197 //DBG printf("write chunk: offset 0x%llx, length 0x%x bytes\n", off, len);
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);
205 if (off > out->cur_out_ptr) {
206 emit_skip_chunk(out, off - out->cur_out_ptr);
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);
215 if (off != out->cur_out_ptr) {
216 error("internal error, offset accounting screwy in write_chunk_raw()");
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;
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));
233 ret = out->ops->write(out, data, len);
237 ret = out->ops->write(out, zero_buf, zero_len);
242 out->crc32 = sparse_crc32(out->crc32, data, len);
244 out->crc32 = sparse_crc32(out->crc32, zero_buf, zero_len);
245 out->cur_out_ptr += rnd_up_len;
251 void close_output_file(struct output_file *out)
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;
260 ret = out->ops->seek(out, 0);
262 error("failure seeking to start of sparse file");
264 ret = out->ops->write(out, (u8 *)&sparse_header, sizeof(sparse_header));
266 error("failure updating sparse file header");
268 out->ops->close(out);
271 struct output_file *open_output_file(const char *filename, int gz, int sparse)
274 struct output_file *out = malloc(sizeof(struct output_file));
276 error_errno("malloc struct out");
279 zero_buf = malloc(info.block_size);
281 error_errno("malloc zero_buf");
284 memset(zero_buf, '\0', info.block_size);
287 out->ops = &gz_file_ops;
288 out->gz_fd = gzopen(filename, "wb9");
290 error_errno("gzopen");
295 out->ops = &file_ops;
296 out->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
303 out->sparse = sparse;
304 out->cur_out_ptr = 0ll;
307 /* Initialize the crc32 value */
311 /* Write out the file header. We'll update the unknown fields
312 * when we close the file.
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));
324 void pad_output_file(struct output_file *out, u64 len)
328 if (len > info.len) {
329 error("attempted to pad file %llu bytes past end of filesystem",
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.
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);
343 if (len > out->cur_out_ptr) {
344 emit_skip_chunk(out, len - out->cur_out_ptr);
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);
355 ret = out->ops->write(out, (u8*)"", 1);
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)
366 if (off + len > info.len) {
367 error("attempted to write block %llu past end of filesystem",
368 off + len - info.len);
373 write_chunk_raw(out, off, data, len);
375 ret = out->ops->seek(out, off);
379 ret = out->ops->write(out, data, len);
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)
391 if (off + len >= info.len) {
392 error("attempted to write block %llu past end of filesystem",
393 off + len - info.len);
397 int file_fd = open(file, O_RDONLY);
403 u8 *data = mmap(NULL, len, PROT_READ, MAP_SHARED, file_fd, offset);
404 if (data == MAP_FAILED) {
411 write_chunk_raw(out, off, data, len);
413 ret = out->ops->seek(out, off);
417 ret = out->ops->write(out, data, len);