summaryrefslogtreecommitdiff
path: root/contrib/android/basefs_allocator.c
blob: a014744a619bf66d236e452dd75f25e15e881475 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include <sys/types.h>
#include <sys/stat.h>
#include "basefs_allocator.h"
#include "block_range.h"
#include "hashmap.h"
#include "base_fs.h"

struct base_fs_allocator {
	struct ext2fs_hashmap *entries;
	struct basefs_entry *cur_entry;
};

static errcode_t basefs_block_allocator(ext2_filsys, blk64_t, blk64_t *,
					struct blk_alloc_ctx *ctx);

static void fs_free_blocks_range(ext2_filsys fs, struct block_range *blocks)
{
	while (blocks) {
		ext2fs_unmark_block_bitmap_range2(fs->block_map, blocks->start,
			blocks->end - blocks->start + 1);
		blocks = blocks->next;
	}
}

static void fs_reserve_blocks_range(ext2_filsys fs, struct block_range *blocks)
{
	while (blocks) {
		ext2fs_mark_block_bitmap_range2(fs->block_map,
			blocks->start, blocks->end - blocks->start + 1);
		blocks = blocks->next;
	}
}

errcode_t base_fs_alloc_load(ext2_filsys fs, const char *file,
			     const char *mountpoint)
{
	errcode_t retval;
	struct basefs_entry *e;
	struct ext2fs_hashmap_entry *it = NULL;
	struct base_fs_allocator *allocator;
	struct ext2fs_hashmap *entries = basefs_parse(file, mountpoint);
	if (!entries)
		return -1;

	allocator = malloc(sizeof(*allocator));
	if (!allocator)
		goto err_alloc;

	retval = ext2fs_read_bitmaps(fs);
	if (retval)
		goto err_bitmap;
	while ((e = ext2fs_hashmap_iter_in_order(entries, &it)))
		fs_reserve_blocks_range(fs, e->head);

	allocator->cur_entry = NULL;
	allocator->entries = entries;

	/* Override the default allocator */
	fs->get_alloc_block2 = basefs_block_allocator;
	fs->priv_data = allocator;

	return 0;

err_bitmap:
	free(allocator);
err_alloc:
	ext2fs_hashmap_free(entries);
	return EXIT_FAILURE;
}

static errcode_t basefs_block_allocator(ext2_filsys fs, blk64_t goal,
					blk64_t *ret, struct blk_alloc_ctx *ctx)
{
	errcode_t retval;
	struct block_range *next_range;
	struct base_fs_allocator *allocator = fs->priv_data;
	struct basefs_entry *e = allocator->cur_entry;

	/* Try to get a block from the base_fs */
	if (e && e->head && ctx && (ctx->flags & BLOCK_ALLOC_DATA)) {
		*ret = e->head->start;
		e->head->start += 1;
		if (e->head->start > e->head->end) {
			next_range = e->head->next;
			free(e->head);
			e->head = next_range;
		}
	} else { /* Allocate a new block */
		retval = ext2fs_new_block2(fs, goal, fs->block_map, ret);
		if (retval)
			return retval;
		ext2fs_mark_block_bitmap2(fs->block_map, *ret);
	}
	return 0;
}

void base_fs_alloc_cleanup(ext2_filsys fs)
{
	struct basefs_entry *e;
	struct ext2fs_hashmap_entry *it = NULL;
	struct base_fs_allocator *allocator = fs->priv_data;

	while ((e = ext2fs_hashmap_iter_in_order(allocator->entries, &it))) {
		fs_free_blocks_range(fs, e->head);
		delete_block_ranges(e->head);
		e->head = e->tail = NULL;
	}

	fs->priv_data = NULL;
	fs->get_alloc_block2 = NULL;
	ext2fs_hashmap_free(allocator->entries);
	free(allocator);
}

errcode_t base_fs_alloc_set_target(ext2_filsys fs, const char *target_path,
	const char *name EXT2FS_ATTR((unused)),
	ext2_ino_t parent_ino EXT2FS_ATTR((unused)),
	ext2_ino_t root EXT2FS_ATTR((unused)), mode_t mode)
{
	struct base_fs_allocator *allocator = fs->priv_data;

	if (mode != S_IFREG)
		return 0;

	if (allocator)
		allocator->cur_entry = ext2fs_hashmap_lookup(allocator->entries,
						      target_path,
						      strlen(target_path));
	return 0;
}

errcode_t base_fs_alloc_unset_target(ext2_filsys fs,
        const char *target_path EXT2FS_ATTR((unused)),
	const char *name EXT2FS_ATTR((unused)),
	ext2_ino_t parent_ino EXT2FS_ATTR((unused)),
	ext2_ino_t root EXT2FS_ATTR((unused)), mode_t mode)
{
	struct base_fs_allocator *allocator = fs->priv_data;

	if (!allocator || !allocator->cur_entry || mode != S_IFREG)
		return 0;

	fs_free_blocks_range(fs, allocator->cur_entry->head);
	delete_block_ranges(allocator->cur_entry->head);
	allocator->cur_entry->head = allocator->cur_entry->tail = NULL;
	allocator->cur_entry = NULL;
	return 0;
}