blob: f5fd4fa096aefeea200257b05878690eff846956 [file] [log] [blame]
Colin Crossec0a2e82010-06-11 14:21:37 -07001/*
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 *
Mohamad Ayyash95791982016-02-20 03:46:00 +00008 * http://www.apache.org/licenses/LICENSE-2.0
Colin Crossec0a2e82010-06-11 14:21:37 -07009 *
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
Colin Crossec0a2e82010-06-11 14:21:37 -070017#include "ext4_utils.h"
18#include "allocate.h"
Colin Crossec0a2e82010-06-11 14:21:37 -070019
Colin Crossdc5abee2012-04-23 23:20:48 -070020#include <sparse/sparse.h>
21
Colin Cross33f96c62010-12-22 18:40:14 -080022#include <stdio.h>
23#include <stdlib.h>
24
Nick Kralevich4df62f32013-02-07 14:21:34 -080025struct xattr_list_element {
26 struct ext4_inode *inode;
27 struct ext4_xattr_header *header;
28 struct xattr_list_element *next;
29};
30
Colin Crossec0a2e82010-06-11 14:21:37 -070031struct block_allocation *create_allocation()
32{
Colin Cross8aef66d2010-06-20 23:22:12 -070033 struct block_allocation *alloc = malloc(sizeof(struct block_allocation));
34 alloc->list.first = NULL;
35 alloc->list.last = NULL;
36 alloc->oob_list.first = NULL;
37 alloc->oob_list.last = NULL;
38 alloc->list.iter = NULL;
39 alloc->list.partial_iter = 0;
40 alloc->oob_list.iter = NULL;
41 alloc->oob_list.partial_iter = 0;
Doug Zongkerbec598e2014-08-12 11:35:37 -070042 alloc->filename = NULL;
43 alloc->next = NULL;
Colin Cross8aef66d2010-06-20 23:22:12 -070044 return alloc;
Colin Crossec0a2e82010-06-11 14:21:37 -070045}
46
Nick Kralevich4df62f32013-02-07 14:21:34 -080047static struct ext4_xattr_header *xattr_list_find(struct ext4_inode *inode)
48{
49 struct xattr_list_element *element;
50 for (element = aux_info.xattrs; element != NULL; element = element->next) {
51 if (element->inode == inode)
52 return element->header;
53 }
54 return NULL;
55}
56
57static void xattr_list_insert(struct ext4_inode *inode, struct ext4_xattr_header *header)
58{
59 struct xattr_list_element *element = malloc(sizeof(struct xattr_list_element));
60 element->inode = inode;
61 element->header = header;
62 element->next = aux_info.xattrs;
63 aux_info.xattrs = element;
64}
65
Colin Crossec0a2e82010-06-11 14:21:37 -070066static void region_list_remove(struct region_list *list, struct region *reg)
67{
68 if (reg->prev)
69 reg->prev->next = reg->next;
70
71 if (reg->next)
72 reg->next->prev = reg->prev;
73
74 if (list->first == reg)
75 list->first = reg->next;
76
77 if (list->last == reg)
78 list->last = reg->prev;
79
80 reg->next = NULL;
81 reg->prev = NULL;
82}
83
Mohamad Ayyash95791982016-02-20 03:46:00 +000084void region_list_append(struct region_list *list, struct region *reg)
Colin Crossec0a2e82010-06-11 14:21:37 -070085{
86 if (list->first == NULL) {
87 list->first = reg;
88 list->last = reg;
89 list->iter = reg;
90 list->partial_iter = 0;
91 reg->prev = NULL;
92 } else {
93 list->last->next = reg;
94 reg->prev = list->last;
95 list->last = reg;
96 }
97 reg->next = NULL;
98}
99
100#if 0
101static void dump_starting_from(struct region *reg)
102{
103 for (; reg; reg = reg->next) {
104 printf("%p: Blocks %d-%d (%d)\n", reg,
Doug Zongkerbec598e2014-08-12 11:35:37 -0700105 reg->block, reg->block + reg->len - 1, reg->len)
Colin Crossec0a2e82010-06-11 14:21:37 -0700106 }
107}
108
109static void dump_region_lists(struct block_allocation *alloc) {
110
111 printf("Main list:\n");
112 dump_starting_from(alloc->list.first);
113
114 printf("OOB list:\n");
115 dump_starting_from(alloc->oob_list.first);
116}
117#endif
118
Mohamad Ayyash95791982016-02-20 03:46:00 +0000119void print_blocks(FILE* f, struct block_allocation *alloc, char separator)
Doug Zongkerbec598e2014-08-12 11:35:37 -0700120{
121 struct region *reg;
Mohamad Ayyash95791982016-02-20 03:46:00 +0000122 fputc(' ', f);
Doug Zongkerbec598e2014-08-12 11:35:37 -0700123 for (reg = alloc->list.first; reg; reg = reg->next) {
124 if (reg->len == 1) {
Mohamad Ayyash95791982016-02-20 03:46:00 +0000125 fprintf(f, "%d", reg->block);
Doug Zongkerbec598e2014-08-12 11:35:37 -0700126 } else {
Mohamad Ayyash95791982016-02-20 03:46:00 +0000127 fprintf(f, "%d-%d", reg->block, reg->block + reg->len - 1);
Doug Zongkerbec598e2014-08-12 11:35:37 -0700128 }
Mohamad Ayyash95791982016-02-20 03:46:00 +0000129 fputc(separator, f);
Doug Zongkerbec598e2014-08-12 11:35:37 -0700130 }
131 fputc('\n', f);
132}
133
Colin Crossec0a2e82010-06-11 14:21:37 -0700134void append_region(struct block_allocation *alloc,
135 u32 block, u32 len, int bg_num)
136{
Colin Cross8aef66d2010-06-20 23:22:12 -0700137 struct region *reg;
138 reg = malloc(sizeof(struct region));
139 reg->block = block;
140 reg->len = len;
141 reg->bg = bg_num;
142 reg->next = NULL;
Colin Crossec0a2e82010-06-11 14:21:37 -0700143
Colin Cross8aef66d2010-06-20 23:22:12 -0700144 region_list_append(&alloc->list, reg);
Colin Crossec0a2e82010-06-11 14:21:37 -0700145}
146
147static void allocate_bg_inode_table(struct block_group_info *bg)
148{
149 if (bg->inode_table != NULL)
150 return;
151
152 u32 block = bg->first_block + 2;
153
154 if (bg->has_superblock)
Colin Cross22742ce2010-12-22 16:01:52 -0800155 block += aux_info.bg_desc_blocks + info.bg_desc_reserve_blocks + 1;
Colin Crossec0a2e82010-06-11 14:21:37 -0700156
157 bg->inode_table = calloc(aux_info.inode_table_blocks, info.block_size);
158 if (bg->inode_table == NULL)
159 critical_error_errno("calloc");
160
Colin Cross782879a2014-01-23 13:08:16 -0800161 sparse_file_add_data(ext4_sparse_file, bg->inode_table,
Colin Crossf0ee37f2012-04-24 17:48:43 -0700162 aux_info.inode_table_blocks * info.block_size, block);
Colin Crossec0a2e82010-06-11 14:21:37 -0700163
Colin Cross56497f22013-02-04 00:44:55 -0800164 bg->flags &= ~EXT4_BG_INODE_UNINIT;
Ken Sumrall107a9f12011-06-29 20:28:30 -0700165}
166
Colin Crossec0a2e82010-06-11 14:21:37 -0700167static int bitmap_set_bit(u8 *bitmap, u32 bit)
168{
169 if (bitmap[bit / 8] & 1 << (bit % 8))
170 return 1;
171
172 bitmap[bit / 8] |= 1 << (bit % 8);
173 return 0;
174}
175
176static int bitmap_set_8_bits(u8 *bitmap, u32 bit)
177{
178 int ret = bitmap[bit / 8];
179 bitmap[bit / 8] = 0xFF;
180 return ret;
181}
182
183/* Marks a the first num_blocks blocks in a block group as used, and accounts
184 for them in the block group free block info. */
Mohamad Ayyasha6866eb2016-03-02 21:10:45 -0800185static int reserve_blocks(struct block_group_info *bg, u32 bg_num, u32 start, u32 num)
Colin Crossec0a2e82010-06-11 14:21:37 -0700186{
187 unsigned int i = 0;
188
189 u32 block = start;
Colin Crossec0a2e82010-06-11 14:21:37 -0700190 for (i = 0; i < num && block % 8 != 0; i++, block++) {
191 if (bitmap_set_bit(bg->block_bitmap, block)) {
Mohamad Ayyasha6866eb2016-03-02 21:10:45 -0800192 error("attempted to reserve already reserved block %d in block group %d", block, bg_num);
Colin Crossec0a2e82010-06-11 14:21:37 -0700193 return -1;
194 }
195 }
196
197 for (; i + 8 <= (num & ~7); i += 8, block += 8) {
198 if (bitmap_set_8_bits(bg->block_bitmap, block)) {
Mohamad Ayyasha6866eb2016-03-02 21:10:45 -0800199 error("attempted to reserve already reserved block %d in block group %d", block, bg_num);
Colin Crossec0a2e82010-06-11 14:21:37 -0700200 return -1;
201 }
202 }
203
204 for (; i < num; i++, block++) {
205 if (bitmap_set_bit(bg->block_bitmap, block)) {
Mohamad Ayyasha6866eb2016-03-02 21:10:45 -0800206 error("attempted to reserve already reserved block %d in block group %d", block, bg_num);
Colin Crossec0a2e82010-06-11 14:21:37 -0700207 return -1;
208 }
209 }
210
211 bg->free_blocks -= num;
Colin Crossec0a2e82010-06-11 14:21:37 -0700212
213 return 0;
214}
215
Mohamad Ayyash95791982016-02-20 03:46:00 +0000216static void free_blocks(struct block_group_info *bg, u32 block, u32 num_blocks)
Colin Crossec0a2e82010-06-11 14:21:37 -0700217{
218 unsigned int i;
Colin Crossec0a2e82010-06-11 14:21:37 -0700219 for (i = 0; i < num_blocks; i++, block--)
220 bg->block_bitmap[block / 8] &= ~(1 << (block % 8));
221 bg->free_blocks += num_blocks;
Colin Crossec0a2e82010-06-11 14:21:37 -0700222}
223
224/* Reduces an existing allocation by len blocks by return the last blocks
225 to the free pool in their block group. Assumes that the blocks being
226 returned were the last ones allocated out of the block group */
227void reduce_allocation(struct block_allocation *alloc, u32 len)
228{
229 while (len) {
230 struct region *last_reg = alloc->list.last;
Mohamad Ayyash95791982016-02-20 03:46:00 +0000231 struct block_group_info *bg = &aux_info.bgs[last_reg->bg];
Colin Crossec0a2e82010-06-11 14:21:37 -0700232
233 if (last_reg->len > len) {
Mohamad Ayyash95791982016-02-20 03:46:00 +0000234 free_blocks(bg, last_reg->block + last_reg->len - bg->first_block - 1, len);
Colin Crossec0a2e82010-06-11 14:21:37 -0700235 last_reg->len -= len;
236 len = 0;
237 } else {
Colin Crossa1a175a2010-06-17 17:27:20 -0700238 struct region *reg = alloc->list.last->prev;
Mohamad Ayyash95791982016-02-20 03:46:00 +0000239 free_blocks(bg, last_reg->block + last_reg->len - bg->first_block - 1, last_reg->len);
Colin Crossec0a2e82010-06-11 14:21:37 -0700240 len -= last_reg->len;
Colin Crossa1a175a2010-06-17 17:27:20 -0700241 if (reg) {
Colin Crossec0a2e82010-06-11 14:21:37 -0700242 reg->next = NULL;
Colin Crossa1a175a2010-06-17 17:27:20 -0700243 } else {
244 alloc->list.first = NULL;
245 alloc->list.last = NULL;
246 alloc->list.iter = NULL;
247 alloc->list.partial_iter = 0;
248 }
Colin Crossec0a2e82010-06-11 14:21:37 -0700249 free(last_reg);
250 }
251 }
252}
253
254static void init_bg(struct block_group_info *bg, unsigned int i)
255{
256 int header_blocks = 2 + aux_info.inode_table_blocks;
257
258 bg->has_superblock = ext4_bg_has_super_block(i);
259
260 if (bg->has_superblock)
Colin Cross22742ce2010-12-22 16:01:52 -0800261 header_blocks += 1 + aux_info.bg_desc_blocks + info.bg_desc_reserve_blocks;
Colin Crossec0a2e82010-06-11 14:21:37 -0700262
263 bg->bitmaps = calloc(info.block_size, 2);
264 bg->block_bitmap = bg->bitmaps;
265 bg->inode_bitmap = bg->bitmaps + info.block_size;
266
267 bg->header_blocks = header_blocks;
268 bg->first_block = aux_info.first_data_block + i * info.blocks_per_group;
269
270 u32 block = bg->first_block;
271 if (bg->has_superblock)
Colin Cross22742ce2010-12-22 16:01:52 -0800272 block += 1 + aux_info.bg_desc_blocks + info.bg_desc_reserve_blocks;
Colin Cross782879a2014-01-23 13:08:16 -0800273 sparse_file_add_data(ext4_sparse_file, bg->bitmaps, 2 * info.block_size,
Colin Crossf0ee37f2012-04-24 17:48:43 -0700274 block);
Colin Crossec0a2e82010-06-11 14:21:37 -0700275
276 bg->data_blocks_used = 0;
277 bg->free_blocks = info.blocks_per_group;
Colin Crossec0a2e82010-06-11 14:21:37 -0700278 bg->free_inodes = info.inodes_per_group;
279 bg->first_free_inode = 1;
Mohamad Ayyashdedf8f92016-04-14 19:43:31 -0700280 bg->flags = EXT4_BG_INODE_UNINIT;
Colin Crossec0a2e82010-06-11 14:21:37 -0700281
Mohamad Ayyash95791982016-02-20 03:46:00 +0000282 bg->chunk_count = 0;
283 bg->max_chunk_count = 1;
284 bg->chunks = (struct region*) calloc(bg->max_chunk_count, sizeof(struct region));
285
Mohamad Ayyasha6866eb2016-03-02 21:10:45 -0800286 if (reserve_blocks(bg, i, 0, bg->header_blocks) < 0)
Colin Crossec0a2e82010-06-11 14:21:37 -0700287 error("failed to reserve %u blocks in block group %u\n", bg->header_blocks, i);
Mohamad Ayyash95791982016-02-20 03:46:00 +0000288 // Add empty starting delimiter chunk
289 reserve_bg_chunk(i, bg->header_blocks, 0);
Colin Crossec0a2e82010-06-11 14:21:37 -0700290
Ken Sumrall17de6542013-08-15 19:06:29 -0700291 if (bg->first_block + info.blocks_per_group > aux_info.len_blocks) {
292 u32 overrun = bg->first_block + info.blocks_per_group - aux_info.len_blocks;
Mohamad Ayyasha6866eb2016-03-02 21:10:45 -0800293 reserve_blocks(bg, i, info.blocks_per_group - overrun, overrun);
Mohamad Ayyash95791982016-02-20 03:46:00 +0000294 // Add empty ending delimiter chunk
295 reserve_bg_chunk(i, info.blocks_per_group - overrun, 0);
296 } else {
297 reserve_bg_chunk(i, info.blocks_per_group - 1, 0);
Ken Sumrall17de6542013-08-15 19:06:29 -0700298 }
Mohamad Ayyash95791982016-02-20 03:46:00 +0000299
Colin Crossec0a2e82010-06-11 14:21:37 -0700300}
301
302void block_allocator_init()
303{
304 unsigned int i;
305
306 aux_info.bgs = calloc(sizeof(struct block_group_info), aux_info.groups);
307 if (aux_info.bgs == NULL)
308 critical_error_errno("calloc");
309
310 for (i = 0; i < aux_info.groups; i++)
311 init_bg(&aux_info.bgs[i], i);
312}
313
314void block_allocator_free()
315{
316 unsigned int i;
317
318 for (i = 0; i < aux_info.groups; i++) {
319 free(aux_info.bgs[i].bitmaps);
320 free(aux_info.bgs[i].inode_table);
321 }
322 free(aux_info.bgs);
323}
324
Colin Crossec0a2e82010-06-11 14:21:37 -0700325/* Allocate a single block and return its block number */
326u32 allocate_block()
327{
Mohamad Ayyash95791982016-02-20 03:46:00 +0000328 u32 block;
329 struct block_allocation *blk_alloc = allocate_blocks(1);
330 if (!blk_alloc) {
331 return EXT4_ALLOCATE_FAILED;
Colin Crossec0a2e82010-06-11 14:21:37 -0700332 }
Mohamad Ayyash95791982016-02-20 03:46:00 +0000333 block = blk_alloc->list.first->block;
334 free_alloc(blk_alloc);
335 return block;
Colin Crossec0a2e82010-06-11 14:21:37 -0700336}
337
Colin Crosseb3915a2014-01-21 00:04:04 -0800338static struct region *ext4_allocate_best_fit_partial(u32 len)
Colin Crossec0a2e82010-06-11 14:21:37 -0700339{
Mohamad Ayyash95791982016-02-20 03:46:00 +0000340 unsigned int i, j;
341 unsigned int found_bg = 0, found_prev_chunk = 0, found_block = 0;
342 u32 found_allocate_len = 0;
343 bool minimize = false;
344 struct block_group_info *bgs = aux_info.bgs;
345 struct region *reg;
Mohamad Ayyash18785a82016-02-19 21:16:34 +0000346
Colin Crossec0a2e82010-06-11 14:21:37 -0700347 for (i = 0; i < aux_info.groups; i++) {
Mohamad Ayyash95791982016-02-20 03:46:00 +0000348 for (j = 1; j < bgs[i].chunk_count; j++) {
349 u32 hole_start, hole_size;
350 hole_start = bgs[i].chunks[j-1].block + bgs[i].chunks[j-1].len;
351 hole_size = bgs[i].chunks[j].block - hole_start;
352 if (hole_size == len) {
353 // Perfect fit i.e. right between 2 chunks no need to keep searching
354 found_bg = i;
355 found_prev_chunk = j - 1;
356 found_block = hole_start;
357 found_allocate_len = hole_size;
358 goto done;
359 } else if (hole_size > len && (found_allocate_len == 0 || (found_allocate_len > hole_size))) {
360 found_bg = i;
361 found_prev_chunk = j - 1;
362 found_block = hole_start;
363 found_allocate_len = hole_size;
364 minimize = true;
365 } else if (!minimize) {
366 if (found_allocate_len < hole_size) {
367 found_bg = i;
368 found_prev_chunk = j - 1;
369 found_block = hole_start;
370 found_allocate_len = hole_size;
371 }
372 }
Colin Crossec0a2e82010-06-11 14:21:37 -0700373 }
374 }
Colin Crosseb3915a2014-01-21 00:04:04 -0800375
Mohamad Ayyash95791982016-02-20 03:46:00 +0000376 if (found_allocate_len == 0) {
Colin Crosseb3915a2014-01-21 00:04:04 -0800377 error("failed to allocate %u blocks, out of space?", len);
Mohamad Ayyash95791982016-02-20 03:46:00 +0000378 return NULL;
Colin Crosseb3915a2014-01-21 00:04:04 -0800379 }
Mohamad Ayyash95791982016-02-20 03:46:00 +0000380 if (found_allocate_len > len) found_allocate_len = len;
381done:
382 // reclaim allocated space in chunk
383 bgs[found_bg].chunks[found_prev_chunk].len += found_allocate_len;
384 if (reserve_blocks(&bgs[found_bg],
Mohamad Ayyasha6866eb2016-03-02 21:10:45 -0800385 found_bg,
Mohamad Ayyash95791982016-02-20 03:46:00 +0000386 found_block,
387 found_allocate_len) < 0) {
388 error("failed to reserve %u blocks in block group %u\n", found_allocate_len, found_bg);
389 return NULL;
390 }
391 bgs[found_bg].data_blocks_used += found_allocate_len;
392 reg = malloc(sizeof(struct region));
393 reg->block = found_block + bgs[found_bg].first_block;
394 reg->len = found_allocate_len;
395 reg->next = NULL;
396 reg->prev = NULL;
397 reg->bg = found_bg;
398 return reg;
Colin Crossec0a2e82010-06-11 14:21:37 -0700399}
400
Colin Crosseb3915a2014-01-21 00:04:04 -0800401static struct region *ext4_allocate_best_fit(u32 len)
Colin Crossec0a2e82010-06-11 14:21:37 -0700402{
403 struct region *first_reg = NULL;
404 struct region *prev_reg = NULL;
405 struct region *reg;
406
407 while (len > 0) {
Colin Crosseb3915a2014-01-21 00:04:04 -0800408 reg = ext4_allocate_best_fit_partial(len);
Colin Crossec0a2e82010-06-11 14:21:37 -0700409 if (reg == NULL)
410 return NULL;
411
412 if (first_reg == NULL)
413 first_reg = reg;
414
415 if (prev_reg) {
416 prev_reg->next = reg;
417 reg->prev = prev_reg;
418 }
419
420 prev_reg = reg;
421 len -= reg->len;
422 }
423
424 return first_reg;
425}
426
Colin Crossec0a2e82010-06-11 14:21:37 -0700427/* Allocate len blocks. The blocks may be spread across multiple block groups,
428 and are returned in a linked list of the blocks in each block group. The
429 allocation algorithm is:
Mohamad Ayyash95791982016-02-20 03:46:00 +0000430 1. If the remaining allocation is larger than any available contiguous region,
431 allocate the largest contiguous region and loop
432 2. Otherwise, allocate the smallest contiguous region that it fits in
Colin Crossec0a2e82010-06-11 14:21:37 -0700433*/
434struct block_allocation *allocate_blocks(u32 len)
435{
Colin Crosseb3915a2014-01-21 00:04:04 -0800436 struct region *reg = ext4_allocate_best_fit(len);
Colin Crossec0a2e82010-06-11 14:21:37 -0700437
438 if (reg == NULL)
439 return NULL;
440
441 struct block_allocation *alloc = create_allocation();
442 alloc->list.first = reg;
Mohamad Ayyash95791982016-02-20 03:46:00 +0000443 while (reg->next != NULL)
444 reg = reg->next;
Colin Cross8aef66d2010-06-20 23:22:12 -0700445 alloc->list.last = reg;
Colin Crossec0a2e82010-06-11 14:21:37 -0700446 alloc->list.iter = alloc->list.first;
447 alloc->list.partial_iter = 0;
448 return alloc;
449}
450
451/* Returns the number of discontiguous regions used by an allocation */
452int block_allocation_num_regions(struct block_allocation *alloc)
453{
454 unsigned int i;
455 struct region *reg = alloc->list.first;
456
457 for (i = 0; reg != NULL; reg = reg->next)
458 i++;
459
460 return i;
461}
462
463int block_allocation_len(struct block_allocation *alloc)
464{
465 unsigned int i;
Colin Cross8aef66d2010-06-20 23:22:12 -0700466 struct region *reg = alloc->list.first;
Colin Crossec0a2e82010-06-11 14:21:37 -0700467
468 for (i = 0; reg != NULL; reg = reg->next)
469 i += reg->len;
470
471 return i;
472}
473
474/* Returns the block number of the block'th block in an allocation */
475u32 get_block(struct block_allocation *alloc, u32 block)
476{
Colin Cross8aef66d2010-06-20 23:22:12 -0700477 struct region *reg = alloc->list.iter;
478 block += alloc->list.partial_iter;
Colin Crossec0a2e82010-06-11 14:21:37 -0700479
Colin Cross8aef66d2010-06-20 23:22:12 -0700480 for (; reg; reg = reg->next) {
Colin Crossec0a2e82010-06-11 14:21:37 -0700481 if (block < reg->len)
482 return reg->block + block;
483 block -= reg->len;
484 }
485 return EXT4_ALLOCATE_FAILED;
486}
487
488u32 get_oob_block(struct block_allocation *alloc, u32 block)
489{
Colin Cross8aef66d2010-06-20 23:22:12 -0700490 struct region *reg = alloc->oob_list.iter;
491 block += alloc->oob_list.partial_iter;
Colin Crossec0a2e82010-06-11 14:21:37 -0700492
Colin Cross8aef66d2010-06-20 23:22:12 -0700493 for (; reg; reg = reg->next) {
Colin Crossec0a2e82010-06-11 14:21:37 -0700494 if (block < reg->len)
495 return reg->block + block;
496 block -= reg->len;
497 }
498 return EXT4_ALLOCATE_FAILED;
499}
500
501/* Gets the starting block and length in blocks of the first region
502 of an allocation */
503void get_region(struct block_allocation *alloc, u32 *block, u32 *len)
504{
505 *block = alloc->list.iter->block;
506 *len = alloc->list.iter->len - alloc->list.partial_iter;
507}
508
509/* Move to the next region in an allocation */
510void get_next_region(struct block_allocation *alloc)
511{
512 alloc->list.iter = alloc->list.iter->next;
513 alloc->list.partial_iter = 0;
514}
515
516/* Returns the number of free blocks in a block group */
517u32 get_free_blocks(u32 bg)
518{
519 return aux_info.bgs[bg].free_blocks;
520}
521
522int last_region(struct block_allocation *alloc)
523{
524 return (alloc->list.iter == NULL);
525}
526
527void rewind_alloc(struct block_allocation *alloc)
528{
529 alloc->list.iter = alloc->list.first;
530 alloc->list.partial_iter = 0;
531}
532
533static struct region *do_split_allocation(struct block_allocation *alloc, u32 len)
534{
Colin Cross8aef66d2010-06-20 23:22:12 -0700535 struct region *reg = alloc->list.iter;
536 struct region *new;
537 struct region *tmp;
Colin Crossec0a2e82010-06-11 14:21:37 -0700538
Colin Cross8aef66d2010-06-20 23:22:12 -0700539 while (reg && len >= reg->len) {
Colin Crossec0a2e82010-06-11 14:21:37 -0700540 len -= reg->len;
541 reg = reg->next;
542 }
543
544 if (reg == NULL && len > 0)
545 return NULL;
546
547 if (len > 0) {
548 new = malloc(sizeof(struct region));
549
550 new->bg = reg->bg;
551 new->block = reg->block + len;
552 new->len = reg->len - len;
553 new->next = reg->next;
554 new->prev = reg;
555
556 reg->next = new;
557 reg->len = len;
558
559 tmp = alloc->list.iter;
Colin Cross8aef66d2010-06-20 23:22:12 -0700560 alloc->list.iter = new;
Colin Crossec0a2e82010-06-11 14:21:37 -0700561 return tmp;
562 } else {
563 return reg;
564 }
565}
566
567/* Splits an allocation into two allocations. The returned allocation will
568 point to the first half, and the original allocation ptr will point to the
569 second half. */
570static struct region *split_allocation(struct block_allocation *alloc, u32 len)
571{
572 /* First make sure there is a split at the current ptr */
573 do_split_allocation(alloc, alloc->list.partial_iter);
574
575 /* Then split off len blocks */
576 struct region *middle = do_split_allocation(alloc, len);
577 alloc->list.partial_iter = 0;
578 return middle;
579}
580
581/* Reserve the next blocks for oob data (indirect or extent blocks) */
582int reserve_oob_blocks(struct block_allocation *alloc, int blocks)
583{
584 struct region *oob = split_allocation(alloc, blocks);
Colin Cross8aef66d2010-06-20 23:22:12 -0700585 struct region *next;
Colin Crossec0a2e82010-06-11 14:21:37 -0700586
587 if (oob == NULL)
588 return -1;
589
590 while (oob && oob != alloc->list.iter) {
591 next = oob->next;
592 region_list_remove(&alloc->list, oob);
593 region_list_append(&alloc->oob_list, oob);
594 oob = next;
595 }
596
597 return 0;
598}
599
600static int advance_list_ptr(struct region_list *list, int blocks)
601{
602 struct region *reg = list->iter;
603
604 while (reg != NULL && blocks > 0) {
605 if (reg->len > list->partial_iter + blocks) {
606 list->partial_iter += blocks;
607 return 0;
608 }
609
610 blocks -= (reg->len - list->partial_iter);
611 list->partial_iter = 0;
612 reg = reg->next;
613 }
614
615 if (blocks > 0)
616 return -1;
617
618 return 0;
619}
620
621/* Move the allocation pointer forward */
622int advance_blocks(struct block_allocation *alloc, int blocks)
623{
624 return advance_list_ptr(&alloc->list, blocks);
625}
626
627int advance_oob_blocks(struct block_allocation *alloc, int blocks)
628{
629 return advance_list_ptr(&alloc->oob_list, blocks);
630}
631
632int append_oob_allocation(struct block_allocation *alloc, u32 len)
633{
Colin Crosseb3915a2014-01-21 00:04:04 -0800634 struct region *reg = ext4_allocate_best_fit(len);
Colin Crossec0a2e82010-06-11 14:21:37 -0700635
636 if (reg == NULL) {
637 error("failed to allocate %d blocks", len);
638 return -1;
639 }
640
641 for (; reg; reg = reg->next)
642 region_list_append(&alloc->oob_list, reg);
643
644 return 0;
645}
646
647/* Returns an ext4_inode structure for an inode number */
648struct ext4_inode *get_inode(u32 inode)
649{
650 inode -= 1;
651 int bg = inode / info.inodes_per_group;
652 inode %= info.inodes_per_group;
653
654 allocate_bg_inode_table(&aux_info.bgs[bg]);
Colin Cross8aef66d2010-06-20 23:22:12 -0700655 return (struct ext4_inode *)(aux_info.bgs[bg].inode_table + inode *
656 info.inode_size);
Colin Crossec0a2e82010-06-11 14:21:37 -0700657}
658
Nick Kralevich4df62f32013-02-07 14:21:34 -0800659struct ext4_xattr_header *get_xattr_block_for_inode(struct ext4_inode *inode)
660{
661 struct ext4_xattr_header *block = xattr_list_find(inode);
662 if (block != NULL)
663 return block;
664
665 u32 block_num = allocate_block();
666 block = calloc(info.block_size, 1);
667 if (block == NULL) {
668 error("get_xattr: failed to allocate %d", info.block_size);
669 return NULL;
670 }
671
672 block->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC);
673 block->h_refcount = cpu_to_le32(1);
674 block->h_blocks = cpu_to_le32(1);
675 inode->i_blocks_lo = cpu_to_le32(le32_to_cpu(inode->i_blocks_lo) + (info.block_size / 512));
676 inode->i_file_acl_lo = cpu_to_le32(block_num);
677
Colin Cross782879a2014-01-23 13:08:16 -0800678 int result = sparse_file_add_data(ext4_sparse_file, block, info.block_size, block_num);
Nick Kralevich4df62f32013-02-07 14:21:34 -0800679 if (result != 0) {
680 error("get_xattr: sparse_file_add_data failure %d", result);
681 free(block);
682 return NULL;
683 }
684 xattr_list_insert(inode, block);
685 return block;
686}
687
Colin Crossec0a2e82010-06-11 14:21:37 -0700688/* Mark the first len inodes in a block group as used */
689u32 reserve_inodes(int bg, u32 num)
690{
691 unsigned int i;
692 u32 inode;
693
694 if (get_free_inodes(bg) < num)
695 return EXT4_ALLOCATE_FAILED;
696
697 for (i = 0; i < num; i++) {
698 inode = aux_info.bgs[bg].first_free_inode + i - 1;
699 aux_info.bgs[bg].inode_bitmap[inode / 8] |= 1 << (inode % 8);
700 }
701
702 inode = aux_info.bgs[bg].first_free_inode;
703
704 aux_info.bgs[bg].first_free_inode += num;
705 aux_info.bgs[bg].free_inodes -= num;
706
707 return inode;
708}
709
710/* Returns the first free inode number
711 TODO: Inodes should be allocated in the block group of the data? */
712u32 allocate_inode()
713{
714 unsigned int bg;
715 u32 inode;
716
717 for (bg = 0; bg < aux_info.groups; bg++) {
718 inode = reserve_inodes(bg, 1);
719 if (inode != EXT4_ALLOCATE_FAILED)
720 return bg * info.inodes_per_group + inode;
721 }
722
723 return EXT4_ALLOCATE_FAILED;
724}
725
726/* Returns the number of free inodes in a block group */
727u32 get_free_inodes(u32 bg)
728{
729 return aux_info.bgs[bg].free_inodes;
730}
731
732/* Increments the directory count of the block group that contains inode */
733void add_directory(u32 inode)
734{
735 int bg = (inode - 1) / info.inodes_per_group;
736 aux_info.bgs[bg].used_dirs += 1;
737}
738
739/* Returns the number of inodes in a block group that are directories */
740u16 get_directories(int bg)
741{
742 return aux_info.bgs[bg].used_dirs;
743}
744
Colin Cross56497f22013-02-04 00:44:55 -0800745/* Returns the flags for a block group */
746u16 get_bg_flags(int bg)
747{
748 return aux_info.bgs[bg].flags;
749}
750
Colin Crossec0a2e82010-06-11 14:21:37 -0700751/* Frees the memory used by a linked list of allocation regions */
752void free_alloc(struct block_allocation *alloc)
753{
754 struct region *reg;
755
756 reg = alloc->list.first;
757 while (reg) {
758 struct region *next = reg->next;
759 free(reg);
760 reg = next;
761 }
762
763 reg = alloc->oob_list.first;
764 while (reg) {
765 struct region *next = reg->next;
766 free(reg);
767 reg = next;
768 }
769
770 free(alloc);
771}
Mohamad Ayyash95791982016-02-20 03:46:00 +0000772
773void reserve_bg_chunk(int bg, u32 start_block, u32 size) {
774 struct block_group_info *bgs = aux_info.bgs;
775 int chunk_count;
776 if (bgs[bg].chunk_count == bgs[bg].max_chunk_count) {
777 bgs[bg].max_chunk_count *= 2;
778 bgs[bg].chunks = realloc(bgs[bg].chunks, bgs[bg].max_chunk_count * sizeof(struct region));
779 if (!bgs[bg].chunks)
780 critical_error("realloc failed");
781 }
782 chunk_count = bgs[bg].chunk_count;
783 bgs[bg].chunks[chunk_count].block = start_block;
784 bgs[bg].chunks[chunk_count].len = size;
785 bgs[bg].chunks[chunk_count].bg = bg;
786 bgs[bg].chunk_count++;
787}
788
789int reserve_blocks_for_allocation(struct block_allocation *alloc) {
790 struct region *reg;
791 struct block_group_info *bgs = aux_info.bgs;
792
793 if (!alloc) return 0;
794 reg = alloc->list.first;
795 while (reg != NULL) {
Mohamad Ayyasha6866eb2016-03-02 21:10:45 -0800796 if (reserve_blocks(&bgs[reg->bg], reg->bg, reg->block - bgs[reg->bg].first_block, reg->len) < 0) {
Mohamad Ayyash95791982016-02-20 03:46:00 +0000797 return -1;
Mohamad Ayyasha6866eb2016-03-02 21:10:45 -0800798 }
Mohamad Ayyash95791982016-02-20 03:46:00 +0000799 reg = reg->next;
800 }
801 return 0;
802}
803