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 uint64_t 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 uint64_t 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,uint64_t len,unsigned int block)273 int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, uint64_t 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,uint64_t len,unsigned int block)290 int backed_block_add_data(struct backed_block_list* bbl, void* data, uint64_t 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,uint64_t len,unsigned int block)307 int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
308 uint64_t 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 if (!bb->file.filename) {
319 free(bb);
320 return -ENOMEM;
321 }
322 bb->file.offset = offset;
323 bb->next = nullptr;
324
325 return queue_bb(bbl, bb);
326 }
327
328 /* 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,uint64_t len,unsigned int block)329 int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, uint64_t len,
330 unsigned int block) {
331 struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
332 if (bb == nullptr) {
333 return -ENOMEM;
334 }
335
336 bb->block = block;
337 bb->len = len;
338 bb->type = BACKED_BLOCK_FD;
339 bb->fd.fd = fd;
340 bb->fd.offset = offset;
341 bb->next = nullptr;
342
343 return queue_bb(bbl, bb);
344 }
345
backed_block_split(struct backed_block_list * bbl,struct backed_block * bb,unsigned int max_len)346 int backed_block_split(struct backed_block_list* bbl, struct backed_block* bb,
347 unsigned int max_len) {
348 struct backed_block* new_bb;
349
350 max_len = ALIGN_DOWN(max_len, bbl->block_size);
351
352 if (bb->len <= max_len) {
353 return 0;
354 }
355
356 new_bb = reinterpret_cast<backed_block*>(malloc(sizeof(struct backed_block)));
357 if (new_bb == nullptr) {
358 return -ENOMEM;
359 }
360
361 *new_bb = *bb;
362
363 new_bb->len = bb->len - max_len;
364 new_bb->block = bb->block + max_len / bbl->block_size;
365 new_bb->next = bb->next;
366
367 switch (bb->type) {
368 case BACKED_BLOCK_DATA:
369 new_bb->data.data = (char*)bb->data.data + max_len;
370 break;
371 case BACKED_BLOCK_FILE:
372 new_bb->file.filename = strdup(bb->file.filename);
373 if (!new_bb->file.filename) {
374 free(new_bb);
375 return -ENOMEM;
376 }
377 new_bb->file.offset += max_len;
378 break;
379 case BACKED_BLOCK_FD:
380 new_bb->fd.offset += max_len;
381 break;
382 case BACKED_BLOCK_FILL:
383 break;
384 }
385
386 bb->next = new_bb;
387 bb->len = max_len;
388 return 0;
389 }
390