backed_block.c revision be8ddcb35a459481c0bcf5bfe645c1fefe963f5c
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 <assert.h>
18#include <errno.h>
19#include <stdint.h>
20#include <stdlib.h>
21#include <string.h>
22
23#include "backed_block.h"
24#include "sparse_defs.h"
25
26struct backed_block {
27	unsigned int block;
28	unsigned int len;
29	enum backed_block_type type;
30	union {
31		struct {
32			void *data;
33		} data;
34		struct {
35			char *filename;
36			int64_t offset;
37		} file;
38		struct {
39			int fd;
40			int64_t offset;
41		} fd;
42		struct {
43			uint32_t val;
44		} fill;
45	};
46	struct backed_block *next;
47};
48
49struct backed_block_list {
50	struct backed_block *data_blocks;
51	struct backed_block *last_used;
52	unsigned int block_size;
53};
54
55struct backed_block *backed_block_iter_new(struct backed_block_list *bbl)
56{
57	return bbl->data_blocks;
58}
59
60struct backed_block *backed_block_iter_next(struct backed_block *bb)
61{
62	return bb->next;
63}
64
65unsigned int backed_block_len(struct backed_block *bb)
66{
67	return bb->len;
68}
69
70unsigned int backed_block_block(struct backed_block *bb)
71{
72	return bb->block;
73}
74
75void *backed_block_data(struct backed_block *bb)
76{
77	assert(bb->type == BACKED_BLOCK_DATA);
78	return bb->data.data;
79}
80
81const char *backed_block_filename(struct backed_block *bb)
82{
83	assert(bb->type == BACKED_BLOCK_FILE);
84	return bb->file.filename;
85}
86
87int backed_block_fd(struct backed_block *bb)
88{
89	assert(bb->type == BACKED_BLOCK_FD);
90	return bb->fd.fd;
91}
92
93int64_t backed_block_file_offset(struct backed_block *bb)
94{
95	assert(bb->type == BACKED_BLOCK_FILE || bb->type == BACKED_BLOCK_FD);
96	if (bb->type == BACKED_BLOCK_FILE) {
97		return bb->file.offset;
98	} else { /* bb->type == BACKED_BLOCK_FD */
99		return bb->fd.offset;
100	}
101}
102
103uint32_t backed_block_fill_val(struct backed_block *bb)
104{
105	assert(bb->type == BACKED_BLOCK_FILL);
106	return bb->fill.val;
107}
108
109enum backed_block_type backed_block_type(struct backed_block *bb)
110{
111	return bb->type;
112}
113
114void backed_block_destroy(struct backed_block *bb)
115{
116	if (bb->type == BACKED_BLOCK_FILE) {
117		free(bb->file.filename);
118	}
119
120	free(bb);
121}
122
123struct backed_block_list *backed_block_list_new(unsigned int block_size)
124{
125	struct backed_block_list *b = calloc(sizeof(struct backed_block_list), 1);
126	b->block_size = block_size;
127	return b;
128}
129
130void backed_block_list_destroy(struct backed_block_list *bbl)
131{
132	if (bbl->data_blocks) {
133		struct backed_block *bb = bbl->data_blocks;
134		while (bb) {
135			struct backed_block *next = bb->next;
136			backed_block_destroy(bb);
137			bb = next;
138		}
139	}
140
141	free(bbl);
142}
143
144/* may free b */
145static int merge_bb(struct backed_block_list *bbl,
146		struct backed_block *a, struct backed_block *b)
147{
148	unsigned int block_len;
149
150	/* Block doesn't exist (possible if one block is the last block) */
151	if (!a || !b) {
152		return -EINVAL;
153	}
154
155	assert(a->block < b->block);
156
157	/* Blocks are of different types */
158	if (a->type != b->type) {
159		return -EINVAL;
160	}
161
162	/* Blocks are not adjacent */
163	block_len = a->len / bbl->block_size; /* rounds down */
164	if (a->block + block_len != b->block) {
165		return -EINVAL;
166	}
167
168	switch (a->type) {
169	case BACKED_BLOCK_DATA:
170		/* Don't support merging data for now */
171		return -EINVAL;
172	case BACKED_BLOCK_FILL:
173		if (a->fill.val != b->fill.val) {
174			return -EINVAL;
175		}
176		break;
177	case BACKED_BLOCK_FILE:
178		if (a->file.filename != b->file.filename ||
179				a->file.offset + a->len != b->file.offset) {
180			return -EINVAL;
181		}
182		break;
183	case BACKED_BLOCK_FD:
184		if (a->fd.fd != b->fd.fd ||
185				a->fd.offset + a->len != b->fd.offset) {
186			return -EINVAL;
187		}
188		break;
189	}
190
191	/* Blocks are compatible and adjacent, with a before b.  Merge b into a,
192	 * and free b */
193	a->len += b->len;
194	a->next = b->next;
195
196	backed_block_destroy(b);
197
198	return 0;
199}
200
201static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb)
202{
203	struct backed_block *bb;
204
205	if (bbl->data_blocks == NULL) {
206		bbl->data_blocks = new_bb;
207		return 0;
208	}
209
210	if (bbl->data_blocks->block > new_bb->block) {
211		new_bb->next = bbl->data_blocks;
212		bbl->data_blocks = new_bb;
213		return 0;
214	}
215
216	/* Optimization: blocks are mostly queued in sequence, so save the
217	   pointer to the last bb that was added, and start searching from
218	   there if the next block number is higher */
219	if (bbl->last_used && new_bb->block > bbl->last_used->block)
220		bb = bbl->last_used;
221	else
222		bb = bbl->data_blocks;
223	bbl->last_used = new_bb;
224
225	for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
226		;
227
228	if (bb->next == NULL) {
229		bb->next = new_bb;
230	} else {
231		new_bb->next = bb->next;
232		bb->next = new_bb;
233	}
234
235	merge_bb(bbl, new_bb, new_bb->next);
236	merge_bb(bbl, bb, new_bb);
237
238	return 0;
239}
240
241/* Queues a fill block of memory to be written to the specified data blocks */
242int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
243		unsigned int len, unsigned int block)
244{
245	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
246	if (bb == NULL) {
247		return -ENOMEM;
248	}
249
250	bb->block = block;
251	bb->len = len;
252	bb->type = BACKED_BLOCK_FILL;
253	bb->fill.val = fill_val;
254	bb->next = NULL;
255
256	return queue_bb(bbl, bb);
257}
258
259/* Queues a block of memory to be written to the specified data blocks */
260int backed_block_add_data(struct backed_block_list *bbl, void *data,
261		unsigned int len, unsigned int block)
262{
263	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
264	if (bb == NULL) {
265		return -ENOMEM;
266	}
267
268	bb->block = block;
269	bb->len = len;
270	bb->type = BACKED_BLOCK_DATA;
271	bb->data.data = data;
272	bb->next = NULL;
273
274	return queue_bb(bbl, bb);
275}
276
277/* Queues a chunk of a file on disk to be written to the specified data blocks */
278int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
279		int64_t offset, unsigned int len, unsigned int block)
280{
281	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
282	if (bb == NULL) {
283		return -ENOMEM;
284	}
285
286	bb->block = block;
287	bb->len = len;
288	bb->type = BACKED_BLOCK_FILE;
289	bb->file.filename = strdup(filename);
290	bb->file.offset = offset;
291	bb->next = NULL;
292
293	return queue_bb(bbl, bb);
294}
295
296/* Queues a chunk of a fd to be written to the specified data blocks */
297int backed_block_add_fd(struct backed_block_list *bbl, int fd, int64_t offset,
298		unsigned int len, unsigned int block)
299{
300	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
301	if (bb == NULL) {
302		return -ENOMEM;
303	}
304
305	bb->block = block;
306	bb->len = len;
307	bb->type = BACKED_BLOCK_FD;
308	bb->fd.fd = fd;
309	bb->fd.offset = offset;
310	bb->next = NULL;
311
312	return queue_bb(bbl, bb);
313}
314