output_file.c revision b781330b1acae2e5706bbda8d81e5f7575f40e2a
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
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <sys/types.h>
25#include <sys/mman.h>
26#include <unistd.h>
27#include <fcntl.h>
28
29#include <zlib.h>
30
31#if defined(__APPLE__) && defined(__MACH__)
32#define lseek64 lseek
33#define off64_t off_t
34#endif
35
36#define SPARSE_HEADER_MAJOR_VER 1
37#define SPARSE_HEADER_MINOR_VER 0
38#define SPARSE_HEADER_LEN       (sizeof(sparse_header_t))
39#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
40
41struct output_file_ops {
42	int (*seek)(struct output_file *, off64_t);
43	int (*write)(struct output_file *, u8 *, int);
44	void (*close)(struct output_file *);
45};
46
47struct output_file {
48	int fd;
49	gzFile gz_fd;
50	int sparse;
51	u64 cur_out_ptr;
52	u32 chunk_cnt;
53	u32 crc32;
54	struct output_file_ops *ops;
55};
56
57static int file_seek(struct output_file *out, off64_t off)
58{
59	off64_t ret;
60
61	ret = lseek64(out->fd, off, SEEK_SET);
62	if (ret < 0) {
63		error_errno("lseek64");
64		return -1;
65	}
66	return 0;
67}
68
69static int file_write(struct output_file *out, u8 *data, int len)
70{
71	int ret;
72	ret = write(out->fd, data, len);
73	if (ret < 0) {
74		error_errno("write");
75		return -1;
76	} else if (ret < len) {
77		error("incomplete write");
78		return -1;
79	}
80
81	return 0;
82}
83
84static void file_close(struct output_file *out)
85{
86	close(out->fd);
87}
88
89
90static struct output_file_ops file_ops = {
91	.seek = file_seek,
92	.write = file_write,
93	.close = file_close,
94};
95
96static int gz_file_seek(struct output_file *out, off64_t off)
97{
98	off64_t ret;
99
100	ret = gzseek(out->gz_fd, off, SEEK_SET);
101	if (ret < 0) {
102		error_errno("gzseek");
103		return -1;
104	}
105	return 0;
106}
107
108static int gz_file_write(struct output_file *out, u8 *data, int len)
109{
110	int ret;
111	ret = gzwrite(out->gz_fd, data, len);
112	if (ret < 0) {
113		error_errno("gzwrite");
114		return -1;
115	} else if (ret < len) {
116		error("incomplete gzwrite");
117		return -1;
118	}
119
120	return 0;
121}
122
123static void gz_file_close(struct output_file *out)
124{
125	gzclose(out->gz_fd);
126}
127
128static struct output_file_ops gz_file_ops = {
129	.seek = gz_file_seek,
130	.write = gz_file_write,
131	.close = gz_file_close,
132};
133
134static sparse_header_t sparse_header = {
135	.magic = SPARSE_HEADER_MAGIC,
136	.major_version = SPARSE_HEADER_MAJOR_VER,
137	.minor_version = SPARSE_HEADER_MINOR_VER,
138	.file_hdr_sz = SPARSE_HEADER_LEN,
139	.chunk_hdr_sz = CHUNK_HEADER_LEN,
140	.blk_sz = 0,
141	.total_blks = 0,
142	.total_chunks = 0,
143	.image_checksum = 0
144};
145
146static u8 *zero_buf;
147
148static int emit_skip_chunk(struct output_file *out, u64 skip_len)
149{
150	chunk_header_t chunk_header;
151	int ret, chunk;
152
153	//DBG printf("skip chunk: 0x%llx bytes\n", skip_len);
154
155	if (skip_len % info.block_size) {
156		error("don't care size %llu is not a multiple of the block size %u",
157				skip_len, info.block_size);
158		return -1;
159	}
160
161	/* We are skipping data, so emit a don't care chunk. */
162	chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE;
163	chunk_header.reserved1 = 0;
164	chunk_header.chunk_sz = skip_len / info.block_size;
165	chunk_header.total_sz = CHUNK_HEADER_LEN;
166	ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header));
167	if (ret < 0)
168		return -1;
169
170	out->cur_out_ptr += skip_len;
171	out->chunk_cnt++;
172
173	/* Compute the CRC for all those zeroes.  Do it block_size bytes at a time. */
174	while (skip_len) {
175		chunk = (skip_len > info.block_size) ? info.block_size : skip_len;
176		out->crc32 = sparse_crc32(out->crc32, zero_buf, chunk);
177		skip_len -= chunk;
178	}
179
180	return 0;
181}
182
183static int write_chunk_raw(struct output_file *out, u64 off, u8 *data, int len)
184{
185	chunk_header_t chunk_header;
186	int rnd_up_len, zero_len;
187	int ret;
188
189	/* We can assume that all the chunks to be written are in
190	 * ascending order, block-size aligned, and non-overlapping.
191	 * So, if the offset is less than the current output pointer,
192	 * throw an error, and if there is a gap, emit a "don't care"
193	 * chunk.  The first write (of the super block) may not be
194	 * blocksize aligned, so we need to deal with that too.
195	 */
196	//DBG printf("write chunk: offset 0x%llx, length 0x%x bytes\n", off, len);
197
198	if (off < out->cur_out_ptr) {
199		error("offset %llu is less than the current output offset %llu",
200				off, out->cur_out_ptr);
201		return -1;
202	}
203
204	if (off > out->cur_out_ptr) {
205		emit_skip_chunk(out, off - out->cur_out_ptr);
206	}
207
208	if (off % info.block_size) {
209		error("write chunk offset %llu is not a multiple of the block size %u",
210				off, info.block_size);
211		return -1;
212	}
213
214	if (off != out->cur_out_ptr) {
215		error("internal error, offset accounting screwy in write_chunk_raw()");
216		return -1;
217	}
218
219	/* Round up the file length to a multiple of the block size */
220	rnd_up_len = (len + (info.block_size - 1)) & (~(info.block_size -1));
221	zero_len = rnd_up_len - len;
222
223	/* Finally we can safely emit a chunk of data */
224	chunk_header.chunk_type = CHUNK_TYPE_RAW;
225	chunk_header.reserved1 = 0;
226	chunk_header.chunk_sz = rnd_up_len / info.block_size;
227	chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
228	ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header));
229
230	if (ret < 0)
231		return -1;
232	ret = out->ops->write(out, data, len);
233	if (ret < 0)
234		return -1;
235	if (zero_len) {
236		ret = out->ops->write(out, zero_buf, zero_len);
237		if (ret < 0)
238			return -1;
239	}
240
241	out->crc32 = sparse_crc32(out->crc32, data, len);
242	if (zero_len)
243		out->crc32 = sparse_crc32(out->crc32, zero_buf, zero_len);
244	out->cur_out_ptr += rnd_up_len;
245	out->chunk_cnt++;
246
247	return 0;
248}
249
250void close_output_file(struct output_file *out)
251{
252	int ret;
253	chunk_header_t chunk_header;
254
255	if (out->sparse) {
256		if (out->chunk_cnt != sparse_header.total_chunks)
257			error("sparse chunk count did not match: %d %d", out->chunk_cnt,
258					sparse_header.total_chunks);
259	}
260	out->ops->close(out);
261}
262
263struct output_file *open_output_file(const char *filename, int gz, int sparse,
264        int chunks)
265{
266	int ret;
267	struct output_file *out = malloc(sizeof(struct output_file));
268	if (!out) {
269		error_errno("malloc struct out");
270		return NULL;
271	}
272	zero_buf = malloc(info.block_size);
273	if (!zero_buf) {
274		error_errno("malloc zero_buf");
275		return NULL;
276	}
277	memset(zero_buf, '\0', info.block_size);
278
279	if (gz) {
280		out->ops = &gz_file_ops;
281		out->gz_fd = gzopen(filename, "wb9");
282		if (!out->gz_fd) {
283			error_errno("gzopen");
284			free(out);
285			return NULL;
286		}
287	} else {
288		if (strcmp(filename, "-")) {
289			out->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
290			if (out->fd < 0) {
291				error_errno("open");
292				free(out);
293				return NULL;
294			}
295		} else {
296			out->fd = STDOUT_FILENO;
297		}
298		out->ops = &file_ops;
299	}
300	out->sparse = sparse;
301	out->cur_out_ptr = 0ll;
302	out->chunk_cnt = 0;
303
304	/* Initialize the crc32 value */
305	out->crc32 = 0;
306
307	if (out->sparse) {
308		/* Write out the file header.  We'll update the unknown fields
309		 * when we close the file.
310		 */
311		sparse_header.blk_sz = info.block_size,
312		sparse_header.total_blks = info.len / info.block_size,
313		sparse_header.total_chunks = chunks;
314		ret = out->ops->write(out, (u8 *)&sparse_header, sizeof(sparse_header));
315		if (ret < 0)
316			return NULL;
317	}
318
319	return out;
320}
321
322void pad_output_file(struct output_file *out, u64 len)
323{
324	int ret;
325
326	if (len > info.len) {
327		error("attempted to pad file %llu bytes past end of filesystem",
328				len - info.len);
329		return;
330	}
331	if (out->sparse) {
332		/* We need to emit a DONT_CARE chunk to pad out the file if the
333		 * cur_out_ptr is not already at the end of the filesystem.
334		 * We also need to compute the CRC for it.
335		 */
336		if (len < out->cur_out_ptr) {
337			error("attempted to pad file %llu bytes less than the current output pointer",
338					out->cur_out_ptr - len);
339			return;
340		}
341		if (len > out->cur_out_ptr) {
342			emit_skip_chunk(out, len - out->cur_out_ptr);
343		}
344	} else {
345		//KEN TODO: Fixme.  If the filesystem image needs no padding,
346		//          this will overwrite the last byte in the file with 0
347		//          The answer is to do accounting like the sparse image
348		//          code does and know if there is already data there.
349		ret = out->ops->seek(out, len - 1);
350		if (ret < 0)
351			return;
352
353		ret = out->ops->write(out, (u8*)"", 1);
354		if (ret < 0)
355			return;
356	}
357}
358
359/* Write a contiguous region of data blocks from a memory buffer */
360void write_data_block(struct output_file *out, u64 off, u8 *data, int len)
361{
362	int ret;
363
364	if (off + len > info.len) {
365		error("attempted to write block %llu past end of filesystem",
366				off + len - info.len);
367		return;
368	}
369
370	if (out->sparse) {
371		write_chunk_raw(out, off, data, len);
372	} else {
373		ret = out->ops->seek(out, off);
374		if (ret < 0)
375			return;
376
377		ret = out->ops->write(out, data, len);
378		if (ret < 0)
379			return;
380	}
381}
382
383/* Write a contiguous region of data blocks from a file */
384void write_data_file(struct output_file *out, u64 off, const char *file,
385		     off64_t offset, int len)
386{
387	int ret;
388	off64_t aligned_offset;
389	int aligned_diff;
390
391	if (off + len >= info.len) {
392		error("attempted to write block %llu past end of filesystem",
393				off + len - info.len);
394		return;
395	}
396
397	int file_fd = open(file, O_RDONLY);
398	if (file_fd < 0) {
399		error_errno("open");
400		return;
401	}
402
403	aligned_offset = offset & ~(4096 - 1);
404	aligned_diff = offset - aligned_offset;
405
406	u8 *data = mmap64(NULL, len + aligned_diff, PROT_READ, MAP_SHARED, file_fd,
407			aligned_offset);
408	if (data == MAP_FAILED) {
409		error_errno("mmap64");
410		close(file_fd);
411		return;
412	}
413
414	if (out->sparse) {
415		write_chunk_raw(out, off, data + aligned_diff, len);
416	} else {
417		ret = out->ops->seek(out, off);
418		if (ret < 0)
419			goto err;
420
421		ret = out->ops->write(out, data + aligned_diff, len);
422		if (ret < 0)
423			goto err;
424	}
425
426	munmap(data, len);
427
428	close(file_fd);
429
430err:
431	munmap(data, len);
432	close(file_fd);
433}
434
435