1#include <sys/types.h>
2#include <sys/stat.h>
3#include "basefs_allocator.h"
4#include "block_range.h"
5#include "hashmap.h"
6#include "base_fs.h"
7
8struct base_fs_allocator {
9	struct ext2fs_hashmap *entries;
10	struct basefs_entry *cur_entry;
11};
12
13static errcode_t basefs_block_allocator(ext2_filsys, blk64_t, blk64_t *,
14					struct blk_alloc_ctx *ctx);
15
16static void fs_free_blocks_range(ext2_filsys fs, struct block_range *blocks)
17{
18	while (blocks) {
19		ext2fs_unmark_block_bitmap_range2(fs->block_map, blocks->start,
20			blocks->end - blocks->start + 1);
21		blocks = blocks->next;
22	}
23}
24
25static void fs_reserve_blocks_range(ext2_filsys fs, struct block_range *blocks)
26{
27	while (blocks) {
28		ext2fs_mark_block_bitmap_range2(fs->block_map,
29			blocks->start, blocks->end - blocks->start + 1);
30		blocks = blocks->next;
31	}
32}
33
34errcode_t base_fs_alloc_load(ext2_filsys fs, const char *file,
35			     const char *mountpoint)
36{
37	errcode_t retval;
38	struct basefs_entry *e;
39	struct ext2fs_hashmap_entry *it = NULL;
40	struct base_fs_allocator *allocator;
41	struct ext2fs_hashmap *entries = basefs_parse(file, mountpoint);
42	if (!entries)
43		return -1;
44
45	allocator = malloc(sizeof(*allocator));
46	if (!allocator)
47		goto err_alloc;
48
49	retval = ext2fs_read_bitmaps(fs);
50	if (retval)
51		goto err_bitmap;
52	while ((e = ext2fs_hashmap_iter_in_order(entries, &it)))
53		fs_reserve_blocks_range(fs, e->head);
54
55	allocator->cur_entry = NULL;
56	allocator->entries = entries;
57
58	/* Overhide the default allocator */
59	fs->get_alloc_block2 = basefs_block_allocator;
60	fs->priv_data = allocator;
61
62	return 0;
63
64err_bitmap:
65	free(allocator);
66err_alloc:
67	ext2fs_hashmap_free(entries);
68	return EXIT_FAILURE;
69}
70
71static errcode_t basefs_block_allocator(ext2_filsys fs, blk64_t goal,
72					blk64_t *ret, struct blk_alloc_ctx *ctx)
73{
74	errcode_t retval;
75	struct block_range *next_range;
76	struct base_fs_allocator *allocator = fs->priv_data;
77	struct basefs_entry *e = allocator->cur_entry;
78
79	/* Try to get a block from the base_fs */
80	if (e && e->head && ctx && (ctx->flags & BLOCK_ALLOC_DATA)) {
81		*ret = e->head->start;
82		e->head->start += 1;
83		if (e->head->start > e->head->end) {
84			next_range = e->head->next;
85			free(e->head);
86			e->head = next_range;
87		}
88	} else { /* Allocate a new block */
89		retval = ext2fs_new_block2(fs, goal, fs->block_map, ret);
90		if (retval)
91			return retval;
92		ext2fs_mark_block_bitmap2(fs->block_map, *ret);
93	}
94	return 0;
95}
96
97void base_fs_alloc_cleanup(ext2_filsys fs)
98{
99	struct basefs_entry *e;
100	struct ext2fs_hashmap_entry *it = NULL;
101	struct base_fs_allocator *allocator = fs->priv_data;
102
103	while ((e = ext2fs_hashmap_iter_in_order(allocator->entries, &it))) {
104		fs_free_blocks_range(fs, e->head);
105		delete_block_ranges(e->head);
106		e->head = e->tail = NULL;
107	}
108
109	fs->priv_data = NULL;
110	fs->get_alloc_block2 = NULL;
111	ext2fs_hashmap_free(allocator->entries);
112	free(allocator);
113}
114
115errcode_t base_fs_alloc_set_target(ext2_filsys fs, const char *target_path,
116	const char *name EXT2FS_ATTR((unused)),
117	ext2_ino_t parent_ino EXT2FS_ATTR((unused)),
118	ext2_ino_t root EXT2FS_ATTR((unused)), mode_t mode)
119{
120	struct base_fs_allocator *allocator = fs->priv_data;
121
122	if (mode != S_IFREG)
123		return 0;
124
125	if (allocator)
126		allocator->cur_entry = ext2fs_hashmap_lookup(allocator->entries,
127						      target_path,
128						      strlen(target_path));
129	return 0;
130}
131
132errcode_t base_fs_alloc_unset_target(ext2_filsys fs,
133        const char *target_path EXT2FS_ATTR((unused)),
134	const char *name EXT2FS_ATTR((unused)),
135	ext2_ino_t parent_ino EXT2FS_ATTR((unused)),
136	ext2_ino_t root EXT2FS_ATTR((unused)), mode_t mode)
137{
138	struct base_fs_allocator *allocator = fs->priv_data;
139
140	if (!allocator || !allocator->cur_entry || mode != S_IFREG)
141		return 0;
142
143	fs_free_blocks_range(fs, allocator->cur_entry->head);
144	delete_block_ranges(allocator->cur_entry->head);
145	allocator->cur_entry->head = allocator->cur_entry->tail = NULL;
146	allocator->cur_entry = NULL;
147	return 0;
148}
149