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