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