sparse.c revision 1e17b313a6257b7b5081e178e81435c09d60378e
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 <assert.h>
18#include <stdlib.h>
19
20#include <sparse/sparse.h>
21
22#include "sparse_file.h"
23
24#include "output_file.h"
25#include "backed_block.h"
26#include "sparse_defs.h"
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(block_size);
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	return backed_block_add_data(s->backed_block_list, data, len, block);
57}
58
59int sparse_file_add_fill(struct sparse_file *s,
60		uint32_t fill_val, unsigned int len, unsigned int block)
61{
62	return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
63}
64
65int sparse_file_add_file(struct sparse_file *s,
66		const char *filename, int64_t file_offset, unsigned int len,
67		unsigned int block)
68{
69	return backed_block_add_file(s->backed_block_list, filename, file_offset,
70			len, block);
71}
72
73int sparse_file_add_fd(struct sparse_file *s,
74		int fd, int64_t file_offset, unsigned int len, unsigned int block)
75{
76	return backed_block_add_fd(s->backed_block_list, fd, file_offset,
77			len, block);
78}
79unsigned int sparse_count_chunks(struct sparse_file *s)
80{
81	struct backed_block *bb;
82	unsigned int last_block = 0;
83	unsigned int chunks = 0;
84
85	for (bb = backed_block_iter_new(s->backed_block_list); bb;
86			bb = backed_block_iter_next(bb)) {
87		if (backed_block_block(bb) > last_block) {
88			/* If there is a gap between chunks, add a skip chunk */
89			chunks++;
90		}
91		chunks++;
92		last_block = backed_block_block(bb) +
93				DIV_ROUND_UP(backed_block_len(bb), s->block_size);
94	}
95	if (last_block < DIV_ROUND_UP(s->len, s->block_size)) {
96		chunks++;
97	}
98
99	return chunks;
100}
101
102static void sparse_file_write_block(struct output_file *out,
103		struct backed_block *bb)
104{
105	switch (backed_block_type(bb)) {
106	case BACKED_BLOCK_DATA:
107		write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
108		break;
109	case BACKED_BLOCK_FILE:
110		write_file_chunk(out, backed_block_len(bb),
111				backed_block_filename(bb), backed_block_file_offset(bb));
112		break;
113	case BACKED_BLOCK_FD:
114		write_fd_chunk(out, backed_block_len(bb),
115				backed_block_fd(bb), backed_block_file_offset(bb));
116		break;
117	case BACKED_BLOCK_FILL:
118		write_fill_chunk(out, backed_block_len(bb),
119				backed_block_fill_val(bb));
120		break;
121	}
122}
123
124static int write_all_blocks(struct sparse_file *s, struct output_file *out)
125{
126	struct backed_block *bb;
127	unsigned int last_block = 0;
128	int64_t pad;
129
130	for (bb = backed_block_iter_new(s->backed_block_list); bb;
131			bb = backed_block_iter_next(bb)) {
132		if (backed_block_block(bb) > last_block) {
133			unsigned int blocks = backed_block_block(bb) - last_block;
134			write_skip_chunk(out, (int64_t)blocks * s->block_size);
135		}
136		sparse_file_write_block(out, bb);
137		last_block = backed_block_block(bb) +
138				DIV_ROUND_UP(backed_block_len(bb), s->block_size);
139	}
140
141	pad = s->len - last_block * s->block_size;
142	assert(pad >= 0);
143	if (pad > 0) {
144		write_skip_chunk(out, pad);
145	}
146
147	return 0;
148}
149
150int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,
151		bool crc)
152{
153	int ret;
154	int chunks;
155	struct output_file *out;
156
157	chunks = sparse_count_chunks(s);
158	out = open_output_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);
159
160	if (!out)
161		return -ENOMEM;
162
163	ret = write_all_blocks(s, out);
164
165	close_output_file(out);
166
167	return ret;
168}
169
170int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc,
171		int (*write)(void *priv, const void *data, int len), void *priv)
172{
173	int ret;
174	int chunks;
175	struct output_file *out;
176
177	chunks = sparse_count_chunks(s);
178	out = open_output_callback(write, priv, s->block_size, s->len, false,
179			sparse, chunks, crc);
180
181	if (!out)
182		return -ENOMEM;
183
184	ret = write_all_blocks(s, out);
185
186	close_output_file(out);
187
188	return ret;
189}
190
191void sparse_file_verbose(struct sparse_file *s)
192{
193	s->verbose = true;
194}
195