blob: a014744a619bf66d236e452dd75f25e15e881475 [file] [log] [blame]
#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;
}