backed_block.c revision 411619e921904b896eddae81c086c1f687c8304d
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 <stdlib.h>
18#include <string.h>
19
20#include "backed_block.h"
21#include "sparse_defs.h"
22
23struct data_block {
24	u32 block;
25	u32 len;
26	void *data;
27	const char *filename;
28	int64_t offset;
29	struct data_block *next;
30	u32 fill_val;
31	u8 fill;
32	u8 pad1;
33	u16 pad2;
34};
35
36struct backed_block_list {
37	struct data_block *data_blocks;
38	struct data_block *last_used;
39};
40
41struct backed_block_list *backed_block_list_new(void)
42{
43	struct backed_block_list *b = calloc(sizeof(struct backed_block_list), 1);
44
45	return b;
46}
47
48void backed_block_list_destroy(struct backed_block_list *b)
49{
50	if (b->data_blocks) {
51		struct data_block *db = b->data_blocks;
52		while (db) {
53			struct data_block *next = db->next;
54			free((void*)db->filename);
55
56			free(db);
57			db = next;
58		}
59	}
60
61	free(b);
62}
63
64static void queue_db(struct backed_block_list *b, struct data_block *new_db)
65{
66	struct data_block *db;
67
68	if (b->data_blocks == NULL) {
69		b->data_blocks = new_db;
70		return;
71	}
72
73	if (b->data_blocks->block > new_db->block) {
74		new_db->next = b->data_blocks;
75		b->data_blocks = new_db;
76		return;
77	}
78
79	/* Optimization: blocks are mostly queued in sequence, so save the
80	   pointer to the last db that was added, and start searching from
81	   there if the next block number is higher */
82	if (b->last_used && new_db->block > b->last_used->block)
83		db = b->last_used;
84	else
85		db = b->data_blocks;
86	b->last_used = new_db;
87
88	for (; db->next && db->next->block < new_db->block; db = db->next)
89		;
90
91	if (db->next == NULL) {
92		db->next = new_db;
93	} else {
94		new_db->next = db->next;
95		db->next = new_db;
96	}
97}
98
99/* Queues a fill block of memory to be written to the specified data blocks */
100void queue_fill_block(struct backed_block_list *b, unsigned int fill_val,
101		unsigned int len, unsigned int block)
102{
103	struct data_block *db = calloc(1, sizeof(struct data_block));
104	if (db == NULL) {
105		error_errno("malloc");
106		return;
107	}
108
109	db->block = block;
110	db->len = len;
111	db->fill = 1;
112	db->fill_val = fill_val;
113	db->data = NULL;
114	db->filename = NULL;
115	db->next = NULL;
116
117	queue_db(b, db);
118}
119
120/* Queues a block of memory to be written to the specified data blocks */
121void queue_data_block(struct backed_block_list *b, void *data, unsigned int len,
122		unsigned int block)
123{
124	struct data_block *db = malloc(sizeof(struct data_block));
125	if (db == NULL) {
126		error_errno("malloc");
127		return;
128	}
129
130	db->block = block;
131	db->len = len;
132	db->data = data;
133	db->filename = NULL;
134	db->fill = 0;
135	db->next = NULL;
136
137	queue_db(b, db);
138}
139
140/* Queues a chunk of a file on disk to be written to the specified data blocks */
141void queue_data_file(struct backed_block_list *b, const char *filename,
142		int64_t offset, unsigned int len, unsigned int block)
143{
144	struct data_block *db = malloc(sizeof(struct data_block));
145	if (db == NULL) {
146		error_errno("malloc");
147		return;
148	}
149
150	db->block = block;
151	db->len = len;
152	db->filename = strdup(filename);
153	db->offset = offset;
154	db->data = NULL;
155	db->fill = 0;
156	db->next = NULL;
157
158	queue_db(b, db);
159}
160
161/* Iterates over the queued data blocks, calling data_func for each contiguous
162   data block, and file_func for each contiguous file block */
163void for_each_data_block(struct backed_block_list *b,
164	data_block_callback_t data_func,
165	data_block_file_callback_t file_func,
166	data_block_fill_callback_t fill_func,
167	void *priv, unsigned int block_size)
168{
169	struct data_block *db;
170	u32 last_block = 0;
171
172	for (db = b->data_blocks; db; db = db->next) {
173		if (db->block < last_block)
174			error("data blocks out of order: %u < %u", db->block, last_block);
175		last_block = db->block + DIV_ROUND_UP(db->len, block_size) - 1;
176
177		if (db->filename)
178			file_func(priv, (u64)db->block * block_size, db->filename, db->offset, db->len);
179		else if (db->fill)
180			fill_func(priv, (u64)db->block * block_size, db->fill_val, db->len);
181		else
182			data_func(priv, (u64)db->block * block_size, db->data, db->len);
183	}
184}
185