blob: 14203050072e0195d56b6d122f700a251152eef9 [file] [log] [blame]
#include "base_fs.h"
#include <stdio.h>
#define BASE_FS_VERSION "Base EXT4 version 1.0"
struct base_fs {
FILE *file;
const char *mountpoint;
struct basefs_entry entry;
};
static FILE *basefs_open(const char *file)
{
char *line = NULL;
size_t len;
FILE *f = fopen(file, "r");
if (!f)
return NULL;
if (getline(&line, &len, f) == -1 || !line)
goto err_getline;
if (strncmp(line, BASE_FS_VERSION, strlen(BASE_FS_VERSION)))
goto err_header;
free(line);
return f;
err_header:
free(line);
err_getline:
fclose(f);
return NULL;
}
static struct basefs_entry *basefs_readline(FILE *f, const char *mountpoint,
int *err)
{
char *line = NULL, *saveptr1, *saveptr2, *block_range, *block;
int offset;
size_t len;
struct basefs_entry *entry = NULL;
blk64_t range_start, range_end;
if (getline(&line, &len, f) == -1) {
if (feof(f))
goto end;
goto err_getline;
}
entry = calloc(1, sizeof(*entry));
if (!entry)
goto err_alloc;
/*
* With BASEFS version 1.0, a typical line looks like this:
* /bin/mke2fs 5000-5004,8000,9000-9990
*/
if (sscanf(line, "%ms%n", &entry->path, &offset) != 1)
goto err_sscanf;
len = strlen(mountpoint);
memmove(entry->path, entry->path + len, strlen(entry->path) - len + 1);
while (line[offset] == ' ')
++offset;
block_range = strtok_r(line + offset, ",\n", &saveptr1);
while (block_range) {
block = strtok_r(block_range, "-", &saveptr2);
if (!block)
break;
range_start = atoll(block);
block = strtok_r(NULL, "-", &saveptr2);
range_end = block ? atoll(block) : range_start;
add_blocks_to_range(&entry->head, &entry->tail, range_start,
range_end);
block_range = strtok_r(NULL, ",\n", &saveptr1);
}
end:
*err = 0;
free(line);
return entry;
err_sscanf:
free(entry);
err_alloc:
free(line);
err_getline:
*err = 1;
return NULL;
}
static void free_base_fs_entry(void *e)
{
struct basefs_entry *entry = e;
if (entry) {
free(entry->path);
free(entry);
}
}
struct ext2fs_hashmap *basefs_parse(const char *file, const char *mountpoint)
{
int err;
struct ext2fs_hashmap *entries = NULL;
struct basefs_entry *entry;
FILE *f = basefs_open(file);
if (!f)
return NULL;
entries = ext2fs_hashmap_create(ext2fs_djb2_hash, free_base_fs_entry, 1024);
if (!entries)
goto end;
while ((entry = basefs_readline(f, mountpoint, &err)))
ext2fs_hashmap_add(entries, entry, entry->path,
strlen(entry->path));
if (err) {
fclose(f);
ext2fs_hashmap_free(entries);
return NULL;
}
end:
fclose(f);
return entries;
}
static void *init(const char *file, const char *mountpoint)
{
struct base_fs *params = malloc(sizeof(*params));
if (!params)
return NULL;
params->mountpoint = mountpoint;
params->file = fopen(file, "w+");
if (!params->file) {
free(params);
return NULL;
}
if (fwrite(BASE_FS_VERSION"\n", 1, strlen(BASE_FS_VERSION"\n"),
params->file) != strlen(BASE_FS_VERSION"\n")) {
fclose(params->file);
free(params);
return NULL;
}
return params;
}
static int start_new_file(char *path, ext2_ino_t ino EXT2FS_ATTR((unused)),
struct ext2_inode *inode, void *data)
{
struct base_fs *params = data;
params->entry.head = params->entry.tail = NULL;
params->entry.path = LINUX_S_ISREG(inode->i_mode) ? path : NULL;
return 0;
}
static int add_block(ext2_filsys fs EXT2FS_ATTR((unused)), blk64_t blocknr,
int metadata, void *data)
{
struct base_fs *params = data;
if (params->entry.path && !metadata)
add_blocks_to_range(&params->entry.head, &params->entry.tail,
blocknr, blocknr);
return 0;
}
static int inline_data(void *inline_data EXT2FS_ATTR((unused)),
void *data EXT2FS_ATTR((unused)))
{
return 0;
}
static int end_new_file(void *data)
{
struct base_fs *params = data;
if (!params->entry.path)
return 0;
if (fprintf(params->file, "%s%s ", params->mountpoint,
params->entry.path) < 0
|| write_block_ranges(params->file, params->entry.head, ",")
|| fwrite("\n", 1, 1, params->file) != 1)
return -1;
delete_block_ranges(params->entry.head);
return 0;
}
static int cleanup(void *data)
{
struct base_fs *params = data;
fclose(params->file);
free(params);
return 0;
}
struct fsmap_format base_fs_format = {
.init = init,
.start_new_file = start_new_file,
.add_block = add_block,
.inline_data = inline_data,
.end_new_file = end_new_file,
.cleanup = cleanup,
};