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
26 struct 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
49 struct backed_block_list {
50 struct backed_block *data_blocks;
51 struct backed_block *last_used;
52 unsigned int block_size;
53 };
54
backed_block_iter_new(struct backed_block_list * bbl)55 struct backed_block *backed_block_iter_new(struct backed_block_list *bbl)
56 {
57 return bbl->data_blocks;
58 }
59
backed_block_iter_next(struct backed_block * bb)60 struct backed_block *backed_block_iter_next(struct backed_block *bb)
61 {
62 return bb->next;
63 }
64
backed_block_len(struct backed_block * bb)65 unsigned int backed_block_len(struct backed_block *bb)
66 {
67 return bb->len;
68 }
69
backed_block_block(struct backed_block * bb)70 unsigned int backed_block_block(struct backed_block *bb)
71 {
72 return bb->block;
73 }
74
backed_block_data(struct backed_block * bb)75 void *backed_block_data(struct backed_block *bb)
76 {
77 assert(bb->type == BACKED_BLOCK_DATA);
78 return bb->data.data;
79 }
80
backed_block_filename(struct backed_block * bb)81 const char *backed_block_filename(struct backed_block *bb)
82 {
83 assert(bb->type == BACKED_BLOCK_FILE);
84 return bb->file.filename;
85 }
86
backed_block_fd(struct backed_block * bb)87 int backed_block_fd(struct backed_block *bb)
88 {
89 assert(bb->type == BACKED_BLOCK_FD);
90 return bb->fd.fd;
91 }
92
backed_block_file_offset(struct backed_block * bb)93 int64_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
backed_block_fill_val(struct backed_block * bb)103 uint32_t backed_block_fill_val(struct backed_block *bb)
104 {
105 assert(bb->type == BACKED_BLOCK_FILL);
106 return bb->fill.val;
107 }
108
backed_block_type(struct backed_block * bb)109 enum backed_block_type backed_block_type(struct backed_block *bb)
110 {
111 return bb->type;
112 }
113
backed_block_destroy(struct backed_block * bb)114 void 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
backed_block_list_new(unsigned int block_size)123 struct 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
backed_block_list_destroy(struct backed_block_list * bbl)130 void 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
backed_block_list_move(struct backed_block_list * from,struct backed_block_list * to,struct backed_block * start,struct backed_block * end)144 void backed_block_list_move(struct backed_block_list *from,
145 struct backed_block_list *to, struct backed_block *start,
146 struct backed_block *end)
147 {
148 struct backed_block *bb;
149
150 if (start == NULL) {
151 start = from->data_blocks;
152 }
153
154 if (!end) {
155 for (end = start; end && end->next; end = end->next)
156 ;
157 }
158
159 if (start == NULL || end == NULL) {
160 return;
161 }
162
163 from->last_used = NULL;
164 to->last_used = NULL;
165 if (from->data_blocks == start) {
166 from->data_blocks = end->next;
167 } else {
168 for (bb = from->data_blocks; bb; bb = bb->next) {
169 if (bb->next == start) {
170 bb->next = end->next;
171 break;
172 }
173 }
174 }
175
176 if (!to->data_blocks) {
177 to->data_blocks = start;
178 end->next = NULL;
179 } else {
180 for (bb = to->data_blocks; bb; bb = bb->next) {
181 if (!bb->next || bb->next->block > start->block) {
182 end->next = bb->next;
183 bb->next = start;
184 break;
185 }
186 }
187 }
188 }
189
190 /* may free b */
merge_bb(struct backed_block_list * bbl,struct backed_block * a,struct backed_block * b)191 static int merge_bb(struct backed_block_list *bbl,
192 struct backed_block *a, struct backed_block *b)
193 {
194 unsigned int block_len;
195
196 /* Block doesn't exist (possible if one block is the last block) */
197 if (!a || !b) {
198 return -EINVAL;
199 }
200
201 assert(a->block < b->block);
202
203 /* Blocks are of different types */
204 if (a->type != b->type) {
205 return -EINVAL;
206 }
207
208 /* Blocks are not adjacent */
209 block_len = a->len / bbl->block_size; /* rounds down */
210 if (a->block + block_len != b->block) {
211 return -EINVAL;
212 }
213
214 switch (a->type) {
215 case BACKED_BLOCK_DATA:
216 /* Don't support merging data for now */
217 return -EINVAL;
218 case BACKED_BLOCK_FILL:
219 if (a->fill.val != b->fill.val) {
220 return -EINVAL;
221 }
222 break;
223 case BACKED_BLOCK_FILE:
224 /* Already make sure b->type is BACKED_BLOCK_FILE */
225 if (strcmp(a->file.filename, b->file.filename) ||
226 a->file.offset + a->len != b->file.offset) {
227 return -EINVAL;
228 }
229 break;
230 case BACKED_BLOCK_FD:
231 if (a->fd.fd != b->fd.fd ||
232 a->fd.offset + a->len != b->fd.offset) {
233 return -EINVAL;
234 }
235 break;
236 }
237
238 /* Blocks are compatible and adjacent, with a before b. Merge b into a,
239 * and free b */
240 a->len += b->len;
241 a->next = b->next;
242
243 backed_block_destroy(b);
244
245 return 0;
246 }
247
queue_bb(struct backed_block_list * bbl,struct backed_block * new_bb)248 static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb)
249 {
250 struct backed_block *bb;
251
252 if (bbl->data_blocks == NULL) {
253 bbl->data_blocks = new_bb;
254 return 0;
255 }
256
257 if (bbl->data_blocks->block > new_bb->block) {
258 new_bb->next = bbl->data_blocks;
259 bbl->data_blocks = new_bb;
260 return 0;
261 }
262
263 /* Optimization: blocks are mostly queued in sequence, so save the
264 pointer to the last bb that was added, and start searching from
265 there if the next block number is higher */
266 if (bbl->last_used && new_bb->block > bbl->last_used->block)
267 bb = bbl->last_used;
268 else
269 bb = bbl->data_blocks;
270 bbl->last_used = new_bb;
271
272 for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
273 ;
274
275 if (bb->next == NULL) {
276 bb->next = new_bb;
277 } else {
278 new_bb->next = bb->next;
279 bb->next = new_bb;
280 }
281
282 merge_bb(bbl, new_bb, new_bb->next);
283 if (!merge_bb(bbl, bb, new_bb)) {
284 /* new_bb destroyed, point to retained as last_used */
285 bbl->last_used = bb;
286 }
287
288 return 0;
289 }
290
291 /* Queues a fill block of memory to be written to the specified data blocks */
backed_block_add_fill(struct backed_block_list * bbl,unsigned int fill_val,unsigned int len,unsigned int block)292 int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
293 unsigned int len, unsigned int block)
294 {
295 struct backed_block *bb = calloc(1, sizeof(struct backed_block));
296 if (bb == NULL) {
297 return -ENOMEM;
298 }
299
300 bb->block = block;
301 bb->len = len;
302 bb->type = BACKED_BLOCK_FILL;
303 bb->fill.val = fill_val;
304 bb->next = NULL;
305
306 return queue_bb(bbl, bb);
307 }
308
309 /* Queues a block of memory to be written to the specified data blocks */
backed_block_add_data(struct backed_block_list * bbl,void * data,unsigned int len,unsigned int block)310 int backed_block_add_data(struct backed_block_list *bbl, void *data,
311 unsigned int len, unsigned int block)
312 {
313 struct backed_block *bb = calloc(1, sizeof(struct backed_block));
314 if (bb == NULL) {
315 return -ENOMEM;
316 }
317
318 bb->block = block;
319 bb->len = len;
320 bb->type = BACKED_BLOCK_DATA;
321 bb->data.data = data;
322 bb->next = NULL;
323
324 return queue_bb(bbl, bb);
325 }
326
327 /* Queues a chunk of a file on disk to be written to the specified data blocks */
backed_block_add_file(struct backed_block_list * bbl,const char * filename,int64_t offset,unsigned int len,unsigned int block)328 int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
329 int64_t offset, unsigned int len, unsigned int block)
330 {
331 struct backed_block *bb = calloc(1, sizeof(struct backed_block));
332 if (bb == NULL) {
333 return -ENOMEM;
334 }
335
336 bb->block = block;
337 bb->len = len;
338 bb->type = BACKED_BLOCK_FILE;
339 bb->file.filename = strdup(filename);
340 bb->file.offset = offset;
341 bb->next = NULL;
342
343 return queue_bb(bbl, bb);
344 }
345
346 /* Queues a chunk of a fd to be written to the specified data blocks */
backed_block_add_fd(struct backed_block_list * bbl,int fd,int64_t offset,unsigned int len,unsigned int block)347 int backed_block_add_fd(struct backed_block_list *bbl, int fd, int64_t offset,
348 unsigned int len, unsigned int block)
349 {
350 struct backed_block *bb = calloc(1, sizeof(struct backed_block));
351 if (bb == NULL) {
352 return -ENOMEM;
353 }
354
355 bb->block = block;
356 bb->len = len;
357 bb->type = BACKED_BLOCK_FD;
358 bb->fd.fd = fd;
359 bb->fd.offset = offset;
360 bb->next = NULL;
361
362 return queue_bb(bbl, bb);
363 }
364
backed_block_split(struct backed_block_list * bbl,struct backed_block * bb,unsigned int max_len)365 int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb,
366 unsigned int max_len)
367 {
368 struct backed_block *new_bb;
369
370 max_len = ALIGN_DOWN(max_len, bbl->block_size);
371
372 if (bb->len <= max_len) {
373 return 0;
374 }
375
376 new_bb = malloc(sizeof(struct backed_block));
377 if (new_bb == NULL) {
378 return -ENOMEM;
379 }
380
381 *new_bb = *bb;
382
383 new_bb->len = bb->len - max_len;
384 new_bb->block = bb->block + max_len / bbl->block_size;
385 new_bb->next = bb->next;
386 bb->next = new_bb;
387 bb->len = max_len;
388
389 switch (bb->type) {
390 case BACKED_BLOCK_DATA:
391 new_bb->data.data = (char *)bb->data.data + max_len;
392 break;
393 case BACKED_BLOCK_FILE:
394 new_bb->file.offset += max_len;
395 break;
396 case BACKED_BLOCK_FD:
397 new_bb->fd.offset += max_len;
398 break;
399 case BACKED_BLOCK_FILL:
400 break;
401 }
402
403 return 0;
404 }
405