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