sparse.c revision 411619e921904b896eddae81c086c1f687c8304d
1/*
2 * Copyright (C) 2012 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 <stdlib.h>
18
19#include <sparse/sparse.h>
20
21#include "sparse_file.h"
22
23#include "output_file.h"
24#include "backed_block.h"
25#include "sparse_defs.h"
26
27
28struct sparse_file *sparse_file_new(unsigned int block_size, int64_t len)
29{
30	struct sparse_file *s = calloc(sizeof(struct sparse_file), 1);
31	if (!s) {
32		return NULL;
33	}
34
35	s->backed_block_list = backed_block_list_new();
36	if (!s->backed_block_list) {
37		free(s);
38		return NULL;
39	}
40
41	s->block_size = block_size;
42	s->len = len;
43
44	return s;
45}
46
47void sparse_file_destroy(struct sparse_file *s)
48{
49	backed_block_list_destroy(s->backed_block_list);
50	free(s);
51}
52
53int sparse_file_add_data(struct sparse_file *s,
54		void *data, unsigned int len, unsigned int block)
55{
56	queue_data_block(s->backed_block_list, data, len, block);
57
58	return 0;
59}
60
61int sparse_file_add_fill(struct sparse_file *s,
62		uint32_t fill_val, unsigned int len, unsigned int block)
63{
64	queue_fill_block(s->backed_block_list, fill_val, len, block);
65
66	return 0;
67}
68
69int sparse_file_add_file(struct sparse_file *s,
70		const char *filename, int64_t file_offset, unsigned int len,
71		unsigned int block)
72{
73	queue_data_file(s->backed_block_list, filename, file_offset, len, block);
74
75	return 0;
76}
77
78struct count_chunks {
79	unsigned int chunks;
80	int64_t cur_ptr;
81	unsigned int block_size;
82};
83
84static void count_data_block(void *priv, int64_t off, void *data, int len)
85{
86	struct count_chunks *count_chunks = priv;
87	if (off > count_chunks->cur_ptr)
88		count_chunks->chunks++;
89	count_chunks->cur_ptr = off + ALIGN(len, count_chunks->block_size);
90	count_chunks->chunks++;
91}
92
93static void count_fill_block(void *priv, int64_t off, unsigned int fill_val, int len)
94{
95	struct count_chunks *count_chunks = priv;
96	if (off > count_chunks->cur_ptr)
97		count_chunks->chunks++;
98	count_chunks->cur_ptr = off + ALIGN(len, count_chunks->block_size);
99	count_chunks->chunks++;
100}
101
102static void count_file_block(void *priv, int64_t off, const char *file,
103		int64_t offset, int len)
104{
105	struct count_chunks *count_chunks = priv;
106	if (off > count_chunks->cur_ptr)
107		count_chunks->chunks++;
108	count_chunks->cur_ptr = off + ALIGN(len, count_chunks->block_size);
109	count_chunks->chunks++;
110}
111
112static int count_sparse_chunks(struct backed_block_list *b,
113		unsigned int block_size, int64_t len)
114{
115	struct count_chunks count_chunks = {0, 0, block_size};
116
117	for_each_data_block(b, count_data_block, count_file_block,
118			count_fill_block, &count_chunks, block_size);
119
120	if (count_chunks.cur_ptr != len)
121		count_chunks.chunks++;
122
123	return count_chunks.chunks;
124}
125
126static void ext4_write_data_block(void *priv, int64_t off, void *data, int len)
127{
128	write_data_block(priv, off, data, len);
129}
130
131static void ext4_write_fill_block(void *priv, int64_t off, unsigned int fill_val, int len)
132{
133	write_fill_block(priv, off, fill_val, len);
134}
135
136static void ext4_write_data_file(void *priv, int64_t off, const char *file,
137		int64_t offset, int len)
138{
139	write_data_file(priv, off, file, offset, len);
140}
141
142int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,
143		bool crc)
144{
145	int chunks = count_sparse_chunks(s->backed_block_list, s->block_size,
146			s->len);
147	struct output_file *out = open_output_fd(fd, s->block_size, s->len,
148			gz, sparse, chunks, crc);
149
150	if (!out)
151		return -ENOMEM;
152
153	for_each_data_block(s->backed_block_list, ext4_write_data_block,
154			ext4_write_data_file, ext4_write_fill_block, out, s->block_size);
155
156	if (s->len)
157		pad_output_file(out, s->len);
158
159	close_output_file(out);
160
161	return 0;
162}
163