#include "file_system_struct.h"
#include "pfs.h"
#include "pfs_structs.h"
#include <time.h>
#include <stat_struct.h>
#include <kmalloc.h>
#include <verify_area.h>
#include <segment.h>
#include <consts.h>
#include <enums.h>
#include <errno.h>
#include <string.h>
#include <i386.h>
#include <util.h>
#include <limits.h>
#define RAMFS_BLOCK_SIZE PAGE_SIZE
#define MAX_SIZE LONG_MAX
#define BLOCK_TABLE_SIZE (RAMFS_BLOCK_SIZE / sizeof (void *))
struct ramfs_file_struct;
struct ramfs_file_operations_struct {
void (*destroy)(struct ramfs_file_struct *);
};
struct ramfs_file_struct {
struct pfs_dir_entry_struct dir_entry;
long mtime;
union {
struct pfs_dir_struct dir;
struct {
unsigned long size;
void *data;
};
};
const struct ramfs_file_operations_struct *operations;
};
static void *alloc_block()
{
void *block;
if (!(block = kmalloc(RAMFS_BLOCK_SIZE)))
return NULL;
memset(block, 0, RAMFS_BLOCK_SIZE);
return block;
}
static void free_block(void *block)
{
kfree(block, RAMFS_BLOCK_SIZE);
}
static void *create_block_tables(void *data, unsigned long old_size, unsigned long new_size)
{
unsigned long new_size_sav;
void **block_table;
old_size = (old_size - 1) / RAMFS_BLOCK_SIZE;
new_size = (new_size - 1) / RAMFS_BLOCK_SIZE;
while (old_size) {
old_size /= BLOCK_TABLE_SIZE;
new_size /= BLOCK_TABLE_SIZE;
}
new_size_sav = new_size;
while (new_size) {
if (!(block_table = alloc_block()))
goto error;
block_table[0] = data;
data = block_table;
new_size /= BLOCK_TABLE_SIZE;
}
return data;
error:
while (new_size < new_size_sav) {
block_table = data;
data = block_table[0];
free_block(block_table);
new_size *= BLOCK_TABLE_SIZE;
}
return NULL;
}
static int grow_reg(struct ramfs_file_struct *reg, unsigned long new_size)
{
void *data;
if (new_size > RAMFS_BLOCK_SIZE && reg->size > 0) {
if (!(data = create_block_tables(reg->data, reg->size, new_size)))
return 0;
reg->data = data;
}
reg->size = new_size;
return 1;
}
static inline unsigned long get_entry_size(unsigned long size)
{
unsigned long entry_size;
size = (size - 1) / RAMFS_BLOCK_SIZE;
entry_size = RAMFS_BLOCK_SIZE;
while (size /= BLOCK_TABLE_SIZE)
entry_size *= BLOCK_TABLE_SIZE;
return entry_size;
}
static const void *get_block(const struct ramfs_file_struct *reg, unsigned long pos)
{
const void *data;
unsigned long entry_size;
void *const *block_table;
void *const *ent;
if (reg->size == 0)
return NULL;
if (!reg->data)
return NULL;
if (reg->size <= RAMFS_BLOCK_SIZE)
return reg->data;
data = reg->data;
entry_size = get_entry_size(reg->size);
while (1) {
block_table = data;
ent = &block_table[pos / entry_size];
if (!*ent)
return NULL;
data = *ent;
pos = pos & (entry_size - 1);
if (entry_size <= RAMFS_BLOCK_SIZE)
break;
entry_size /= BLOCK_TABLE_SIZE;
}
return data;
}
static void *get_alloc_block(struct ramfs_file_struct *reg, unsigned long pos)
{
void *data;
unsigned long entry_size;
void **block_table;
void **ent;
if (reg->size == 0)
return NULL;
if (!reg->data)
if (!(reg->data = alloc_block()))
return NULL;
if (reg->size <= RAMFS_BLOCK_SIZE)
return reg->data;
data = reg->data;
entry_size = get_entry_size(reg->size);
while (1) {
block_table = data;
ent = &block_table[pos / entry_size];
if (!*ent)
if (!(*ent = alloc_block()))
return NULL;
data = *ent;
pos = pos & (entry_size - 1);
if (entry_size <= RAMFS_BLOCK_SIZE)
break;
entry_size /= BLOCK_TABLE_SIZE;
}
return data;
}
static void free_block_table(void **block_table, unsigned long entry_size)
{
void **ent;
for (ent = &block_table[0]; ent < &block_table[BLOCK_TABLE_SIZE]; ent++)
if (*ent) {
if (entry_size > RAMFS_BLOCK_SIZE)
free_block_table(*ent, entry_size / BLOCK_TABLE_SIZE);
else
free_block(*ent);
}
free_block(block_table);
}
static void free_blocks(struct ramfs_file_struct *reg)
{
if (reg->size == 0)
return;
if (!reg->data)
return;
if (reg->size <= RAMFS_BLOCK_SIZE) {
free_block(reg->data);
return;
}
free_block_table(reg->data, get_entry_size(reg->size));
}
static void init_file_common(struct ramfs_file_struct *file, unsigned long id, char *name, int type)
{
file->dir_entry.info.id = id;
pfs_set_dir_entry_info_name(&file->dir_entry.info, name);
file->dir_entry.info.type = type;
file->mtime = get_time();
}
static void destroy_file_common(struct ramfs_file_struct *file)
{
pfs_free_dir_entry_info_name(&file->dir_entry.info);
}
static void rename_file(struct ramfs_file_struct *file, char *name)
{
if (file->dir_entry.info.name)
pfs_free_dir_entry_info_name(&file->dir_entry.info);
return pfs_set_dir_entry_info_name(&file->dir_entry.info, name);
}
static void dir_destroy(struct ramfs_file_struct *dir)
{
destroy_file_common(dir);
}
static const struct ramfs_file_operations_struct dir_operations = {
.destroy = dir_destroy
};
static void init_dir(struct ramfs_file_struct *dir, unsigned long id, char *name)
{
init_file_common(dir, id, name, FT_DIR);
pfs_init_dir(&dir->dir);
dir->operations = &dir_operations;
}
static void reg_destroy(struct ramfs_file_struct *file)
{
free_blocks(file);
destroy_file_common(file);
}
static const struct ramfs_file_operations_struct reg_operations = {
.destroy = reg_destroy
};
static void init_reg(struct ramfs_file_struct *reg, unsigned long id, char *name)
{
init_file_common(reg, id, name, FT_REG);
reg->size = 0;
reg->data = NULL;
reg->operations = ®_operations;
}
static int find(const struct ramfs_file_struct *dir, const char *fs_basename, unsigned long *id, int *type)
{
return pfs_find(&dir->dir, fs_basename, id, type);
}
static int get_name(const struct ramfs_file_struct *file, char name[])
{
strcpy(name, file->dir_entry.info.name);
return 0;
}
static int get_size(const struct ramfs_file_struct *reg, unsigned long *size)
{
*size = reg->size;
return 0;
}
static int stat(const struct ramfs_file_struct *file, struct stat_struct *fs_buf)
{
struct stat_struct statbuf;
if (!verify_area(fs_buf, sizeof (struct stat_struct), PF_WRITE))
return -EFAULT;
statbuf.dev = DEV_NONE;
statbuf.id = file->dir_entry.info.id;
statbuf.type = file->dir_entry.info.type;
statbuf.rdev = DEV_NONE;
if (file->dir_entry.info.type == FT_REG)
get_size(file, &statbuf.size);
else
statbuf.size = 0;
statbuf.mtime = file->mtime;
memcpy_tofs(fs_buf, &statbuf, sizeof (struct stat_struct));
return 0;
}
static int read_dir(const struct ramfs_file_struct *dir, unsigned long pos, char *fs_buf, unsigned int count)
{
return pfs_read_dir(&dir->dir, pos, fs_buf, count);
}
static int read_reg(const struct ramfs_file_struct *reg, unsigned long pos, char *fs_buf, unsigned int count)
{
int n;
const void *block;
unsigned long off;
unsigned int k;
if (!count)
return 0;
if (pos >= reg->size)
return 0;
if (count > reg->size - pos)
count = reg->size - pos;
if (!verify_area(fs_buf, count, PF_WRITE))
return -EFAULT;
n = 0;
do {
block = get_block(reg, pos);
off = pos & (RAMFS_BLOCK_SIZE - 1);
k = min(RAMFS_BLOCK_SIZE - off, count);
if (block)
memcpy_tofs(fs_buf, block + off, k);
else
memset_tofs(fs_buf, 0, k);
n += k;
pos += k;
fs_buf += k;
count -= k;
}
while (count);
return n;
}
static int write_reg(struct ramfs_file_struct *reg, unsigned long pos, const char *fs_buf, unsigned int count)
{
unsigned long new_size;
int n;
void *block;
unsigned long off;
unsigned int k;
if (!count)
return 0;
if (pos >= MAX_SIZE)
return 0;
if (count > MAX_SIZE - pos)
count = MAX_SIZE - pos;
if (!verify_area(fs_buf, count, PF_READ))
return -EFAULT;
if ((new_size = pos + count) > reg->size)
if (!grow_reg(reg, new_size))
return 0;
n = 0;
do {
if (!(block = get_alloc_block(reg, pos)))
break;
off = pos & (RAMFS_BLOCK_SIZE - 1);
k = min(RAMFS_BLOCK_SIZE - off, count);
memcpy_fromfs(block + off, fs_buf, k);
n += k;
pos += k;
fs_buf += k;
count -= k;
}
while (count);
reg->mtime = get_time();
return n;
}
static int create_file_common(struct ramfs_file_struct *dir, const char *fs_basename, void (*init_file)(struct ramfs_file_struct *, unsigned long, char *))
{
struct ramfs_file_struct *new_file;
char *name;
int retval;
if (pfs_file_exists(&dir->dir, fs_basename))
return -EEXIST;
if (!(new_file = kmalloc(sizeof (struct ramfs_file_struct))))
return -ENOMEM;
if (!(name = pfs_create_name(fs_basename))) {
retval = -ENOMEM;
goto error_free_new_file;
}
init_file(new_file, (unsigned long)new_file, name);
pfs_insert_dir_entry(&new_file->dir_entry, &dir->dir);
dir->mtime = get_time();
return 0;
error_free_new_file:
kfree(new_file, sizeof (struct ramfs_file_struct));
return retval;
}
static int create_reg(struct ramfs_file_struct *dir, const char *fs_basename)
{
return create_file_common(dir, fs_basename, init_reg);
}
static int create_dir(struct ramfs_file_struct *dir, const char *fs_basename)
{
return create_file_common(dir, fs_basename, init_dir);
}
static int remove_file_common(struct ramfs_file_struct *file, struct ramfs_file_struct *parent_dir)
{
pfs_remove_dir_entry(&file->dir_entry, &parent_dir->dir);
file->operations->destroy(file);
kfree(file, sizeof (struct ramfs_file_struct));
return 0;
}
static int remove_reg(struct ramfs_file_struct *reg, struct ramfs_file_struct *parent_dir)
{
return remove_file_common(reg, parent_dir);
}
static int remove_dir(struct ramfs_file_struct *dir, struct ramfs_file_struct *parent_dir)
{
if (dir->dir.first_entry)
return -ENOTEMPTY;
return remove_file_common(dir, parent_dir);
}
static int rename(struct ramfs_file_struct *file, struct ramfs_file_struct *old_parent_dir, struct ramfs_file_struct *new_parent_dir, const char *fs_basename)
{
char *new_name;
if (new_parent_dir != old_parent_dir)
if (pfs_file_exists(&new_parent_dir->dir, fs_basename))
return -EEXIST;
if (!pfs_name_match(fs_basename, file->dir_entry.info.name)) {
if (!(new_name = pfs_create_name(fs_basename)))
return -ENOMEM;
}
else
new_name = NULL;
if (new_parent_dir != old_parent_dir) {
pfs_remove_dir_entry(&file->dir_entry, &old_parent_dir->dir);
old_parent_dir->mtime = get_time();
}
if (new_name)
rename_file(file, new_name);
if (new_parent_dir != old_parent_dir)
pfs_insert_dir_entry(&file->dir_entry, &new_parent_dir->dir);
if (new_parent_dir != old_parent_dir || new_name)
new_parent_dir->mtime = get_time();
return 0;
}
struct ramfs_instance_struct {
struct ramfs_file_struct root_dir;
};
static struct ramfs_file_struct *get_file(struct ramfs_instance_struct *instance, unsigned long id)
{
return id == 0 ? &instance->root_dir : (struct ramfs_file_struct *)id;
}
static int ramfs_instance_init(struct ramfs_instance_struct *ramfs_instance)
{
init_dir(&ramfs_instance->root_dir, 0, NULL);
return 0;
}
static void empty_dir(struct ramfs_file_struct *dir)
{
struct pfs_dir_entry_struct *dir_entry;
struct ramfs_file_struct *file;
for (dir_entry = dir->dir.first_entry; dir_entry; dir_entry = dir_entry->next) {
file = downcast(struct ramfs_file_struct, dir_entry, dir_entry);
if (file->dir_entry.info.type == FT_DIR)
empty_dir(file);
file->operations->destroy(file);
kfree(file, sizeof (struct ramfs_file_struct));
}
}
static void ramfs_instance_destroy(struct ramfs_instance_struct *ramfs_instance)
{
empty_dir(&ramfs_instance->root_dir);
ramfs_instance->root_dir.operations->destroy(&ramfs_instance->root_dir);
}
static int ramfs_mount(const struct device_struct *device, void **data)
{
struct ramfs_instance_struct *ramfs_instance;
int retval;
if (!(ramfs_instance = kmalloc(sizeof (struct ramfs_instance_struct))))
return -ENOMEM;
if ((retval = ramfs_instance_init(ramfs_instance)) < 0)
goto error_free;
*data = ramfs_instance;
return 0;
error_free:
kfree(ramfs_instance, sizeof (struct ramfs_instance_struct));
return retval;
}
static void ramfs_umount(void *data)
{
ramfs_instance_destroy(data);
kfree(data, sizeof (struct ramfs_instance_struct));
}
static int ramfs_find(void *data, unsigned long dir_id, const char *fs_basename, unsigned long *id, int *type)
{
return find(get_file(data, dir_id), fs_basename, id, type);
}
static int ramfs_get_name(void *data, unsigned long id, char name[])
{
return get_name(get_file(data, id), name);
}
static int ramfs_get_size(void *data, unsigned long reg_id, unsigned long *size)
{
return get_size(get_file(data, reg_id), size);
}
static int ramfs_stat(void *data, unsigned long id, struct stat_struct *fs_buf)
{
return stat(get_file(data, id), fs_buf);
}
static int ramfs_read_dir(void *data, unsigned long dir_id, unsigned long pos, char *fs_buf, unsigned int count)
{
return read_dir(get_file(data, dir_id), pos, fs_buf, count);
}
static int ramfs_read(void *data, unsigned long leaf_id, unsigned long pos, char *fs_buf, unsigned int count)
{
return read_reg(get_file(data, leaf_id), pos, fs_buf, count);
}
static int ramfs_write(void *data, unsigned long leaf_id, unsigned long pos, const char *fs_buf, unsigned int count)
{
return write_reg(get_file(data, leaf_id), pos, fs_buf, count);
}
static int ramfs_create(void *data, unsigned long dir_id, const char *fs_basename)
{
return create_reg(get_file(data, dir_id), fs_basename);
}
static int ramfs_mkdir(void *data, unsigned long dir_id, const char *fs_basename)
{
return create_dir(get_file(data, dir_id), fs_basename);
}
static int ramfs_can_remove(void *data, unsigned long leaf_id)
{
return 0;
}
static int ramfs_remove(void *data, unsigned long leaf_id, unsigned long parent_dir_id)
{
return remove_reg(get_file(data, leaf_id), get_file(data, parent_dir_id));
}
static int ramfs_rmdir(void *data, unsigned long dir_id, unsigned long parent_dir_id)
{
return remove_dir(get_file(data, dir_id), get_file(data, parent_dir_id));
}
static int ramfs_rename(void *data, unsigned long old_id, unsigned long old_parent_dir_id, unsigned long new_dir_id, const char *fs_basename)
{
return rename(get_file(data, old_id), get_file(data, old_parent_dir_id), get_file(data, new_dir_id), fs_basename);
}
const struct file_system_struct ramfs = {
.name = "ramfs",
.type = FT_NONE,
.mount = ramfs_mount,
.umount = ramfs_umount,
.find = ramfs_find,
.get_name = ramfs_get_name,
.get_device = NULL,
.get_size = ramfs_get_size,
.stat = ramfs_stat,
.read_dir = ramfs_read_dir,
.read = ramfs_read,
.write = ramfs_write,
.ioctl = NULL,
.create = ramfs_create,
.mkdir = ramfs_mkdir,
.can_remove = ramfs_can_remove,
.remove = ramfs_remove,
.rmdir = ramfs_rmdir,
.rename = ramfs_rename
};