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