#include "file_struct.h"
#include "file_system_instance.h"
#include "file_system_instance_struct.h"
#include "file_system_struct.h"
#include "ro_fs.h"
#include "pfs.h"
#include "pfs_structs.h"
#include "file_table.h"
#include <stat_struct.h>
#include <verify_area.h>
#include <mapping.h>
#include <kmalloc.h>
#include <panic.h>
#include <time.h>
#include <gdt.h>
#include <segment.h>
#include <asm.h>
#include <consts.h>
#include <enums.h>
#include <errno.h>
#include <assert.h>
#include <stddef.h>
#include <limits.h>
#define is_mount_point(dir) ((dir)->file_system_instance != (dir)->master_file_system_instance)
#define get_dir_local_id(dir) (is_mount_point(dir) ? 0 : (dir)->id)
#define get_local_id(node) ((node)->is_dir ? get_dir_local_id(node) : (node)->id)
static struct pfs_dir_struct common_dir_entries;
static const char dot_name[] = ".";
static struct pfs_dir_entry_struct dot = {
.info = {
.name = dot_name
}
};
static const char dotdot_name[] = "..";
static struct pfs_dir_entry_struct dotdot = {
.info = {
.name = dotdot_name
}
};
static const unsigned int common_dir_entries_size = sizeof dot_name + sizeof dotdot_name;
extern struct file_struct *const root;
static int check_for_addition(unsigned long pos, long offset)
{
if (offset < 0 && pos < (unsigned long)(1 - offset) - 1)
return -EINVAL;
if ((offset >= 0 && pos > (unsigned long)LONG_MAX - offset) || (offset < 0 && pos > (unsigned long)LONG_MAX + (-offset)))
return -EOVERFLOW;
return 0;
}
static int dir_read(struct file_struct *dir, unsigned long pos, char *fs_buf, unsigned int count)
{
if (pos < common_dir_entries_size)
return pfs_read_dir(&common_dir_entries, pos, fs_buf, count);
else
return dir->file_system_instance->file_system->read_dir(dir->file_system_instance->data, get_dir_local_id(dir), pos - common_dir_entries_size, fs_buf, count);
}
static int leaf_read(struct file_struct *leaf, unsigned long pos, char *fs_buf, unsigned int count)
{
return leaf->file_system_instance->file_system->read(leaf->file_system_instance->data, leaf->id, pos, fs_buf, count);
}
static int dir_write(struct file_struct *dir, unsigned long pos, const char *fs_buf, unsigned int count)
{
return -EISDIR;
}
static int leaf_write(struct file_struct *leaf, unsigned long pos, const char *fs_buf, unsigned int count)
{
return leaf->file_system_instance->file_system->write(leaf->file_system_instance->data, leaf->id, pos, fs_buf, count);
}
static long seek(const struct file_struct *node, unsigned long pos, long offset, int whence)
{
unsigned long size;
int rv;
switch (whence) {
case SEEK_SET:
if ((rv = check_for_addition(0, offset)) < 0)
return rv;
return offset;
case SEEK_CUR:
if ((rv = check_for_addition(pos, offset)) < 0)
return rv;
return pos + offset;
case SEEK_END:
if ((rv = node->file_system_instance->file_system->get_size(node->file_system_instance->data, get_local_id(node), &size)) < 0)
return rv;
if ((rv = check_for_addition(size, offset)) < 0)
return rv;
return size + offset;
default:
return -EINVAL;
}
}
static long dir_seek(const struct file_struct *dir, unsigned long pos, long offset, int whence)
{
if (whence == SEEK_END)
return -EINVAL;
return seek(dir, pos, offset, whence);
}
static long chr_seek(const struct file_struct *chr, unsigned long pos, long offset, int whence)
{
return 0;
}
static long blk_seek(const struct file_struct *blk, unsigned long pos, long offset, int whence)
{
if (whence == SEEK_END)
return -EINVAL;
return seek(blk, pos, offset, whence);
}
static long reg_seek(const struct file_struct *reg, unsigned long pos, long offset, int whence)
{
return seek(reg, pos, offset, whence);
}
static int node_stat(const struct file_struct *node, struct stat_struct *fs_buf)
{
return node->file_system_instance->file_system->stat(node->file_system_instance->data, get_local_id(node), fs_buf);
}
static int spec_ioctl(const struct file_struct *spec, int request, void *fs_arg)
{
return spec->file_system_instance->file_system->ioctl(spec->file_system_instance->data, spec->id, request, fs_arg);
}
static int not_spec_ioctl(const struct file_struct *node, int request, void *fs_arg)
{
return -ENOTTY;
}
static int spec_get_device(const struct file_struct *spec, struct device_struct **spec_device)
{
return spec->file_system_instance->file_system->get_device(spec->file_system_instance->data, spec->id, spec_device);
}
static int not_spec_get_device(const struct file_struct *node, struct device_struct **spec_device)
{
return -ENOTBLK;
}
static const struct file_operations_struct dir_operations = {
.read = dir_read,
.write = dir_write,
.seek = dir_seek,
.stat = node_stat,
.ioctl = not_spec_ioctl,
.get_device = not_spec_get_device
};
static const struct file_operations_struct chr_operations = {
.read = leaf_read,
.write = leaf_write,
.seek = chr_seek,
.stat = node_stat,
.ioctl = spec_ioctl,
.get_device = spec_get_device
};
static const struct file_operations_struct blk_operations = {
.read = leaf_read,
.write = leaf_write,
.seek = blk_seek,
.stat = node_stat,
.ioctl = spec_ioctl,
.get_device = spec_get_device
};
static const struct file_operations_struct reg_operations = {
.read = leaf_read,
.write = leaf_write,
.seek = reg_seek,
.stat = node_stat,
.ioctl = not_spec_ioctl,
.get_device = not_spec_get_device
};
static struct file_struct *get_previous(const struct file_struct *node)
{
struct file_struct *nd;
nd = node->parent->first_entry;
while (nd->next_entry != node)
nd = nd->next_entry;
return nd;
}
static void replace(const struct file_struct *node1, struct file_struct *node2)
{
if (node1->parent->first_entry == node1)
node1->parent->first_entry = node2;
else
get_previous(node1)->next_entry = node2;
}
static void init_node(struct file_struct *node, const struct file_operations_struct *file_operations, struct file_struct *parent, unsigned long id)
{
node->file_operations = file_operations;
node->count = 0;
node->write_access_count = 0;
mapping_init(&node->mapping);
node->parent = parent;
node->next_entry = parent->first_entry;
node->file_system_instance = parent->file_system_instance;
node->id = id;
}
static void init_dir(struct file_struct *dir, struct file_struct *parent, unsigned long id)
{
init_node(dir, &dir_operations, parent, id);
dir->is_dir = 1;
dir->first_entry = NULL;
dir->master_file_system_instance = parent->file_system_instance;
}
static void init_leaf(struct file_struct *leaf, const struct file_operations_struct *leaf_operations, struct file_struct *parent, unsigned long id)
{
init_node(leaf, leaf_operations, parent, id);
leaf->is_dir = 0;
leaf->to_remove = 0;
}
static int alloc_dir(struct file_struct *parent, unsigned long id, struct file_struct **dir)
{
if (!(*dir = alloc_file()))
return -ENOMEM;
init_dir(*dir, parent, id);
return 0;
}
static int alloc_chr(struct file_struct *parent, unsigned long id, struct file_struct **chr)
{
if (!(*chr = alloc_file()))
return -ENOMEM;
init_leaf(*chr, &chr_operations, parent, id);
return 0;
}
static int alloc_blk(struct file_struct *parent, unsigned long id, struct file_struct **blk)
{
if (!(*blk = alloc_file()))
return -ENOMEM;
init_leaf(*blk, &blk_operations, parent, id);
return 0;
}
static int alloc_reg(struct file_struct *parent, unsigned long id, struct file_struct **reg)
{
if (!(*reg = alloc_file()))
return -ENOMEM;
init_leaf(*reg, ®_operations, parent, id);
return 0;
}
static int alloc_node(struct file_struct *parent, unsigned long id, int type, struct file_struct **node)
{
switch (type) {
case FT_DIR:
return alloc_dir(parent, id, node);
case FT_CHR:
return alloc_chr(parent, id, node);
case FT_BLK:
return alloc_blk(parent, id, node);
case FT_REG:
default:
return alloc_reg(parent, id, node);
}
}
static int destroy_node(struct file_struct *node)
{
assert(node->count == 0);
mapping_destroy(&node->mapping);
if (!node->is_dir)
if (node->to_remove)
node->file_system_instance->file_system->remove(node->file_system_instance->data, node->id, get_dir_local_id(node->parent));
if (node->parent->first_entry == node) {
node->parent->first_entry = node->next_entry;
if (!node->parent->first_entry && node->parent != root)
return 1;
else
return 0;
}
else {
get_previous(node)->next_entry = node->next_entry;
return 0;
}
}
static int get_node(struct file_struct *parent, unsigned long id, int type, struct file_struct **node)
{
int rv;
for (*node = parent->first_entry; *node; *node = (*node)->next_entry)
if ((*node)->id == id) {
if (!(*node)->is_dir && (*node)->to_remove)
*node = NULL;
return 0;
}
if ((rv = alloc_node(parent, id, type, node)) < 0)
return rv;
parent->first_entry = *node;
return 0;
}
static int find_node_internal(struct file_struct *dir, const char *fs_basename, struct file_struct **node)
{
unsigned long id;
int type;
int rv;
if (pfs_name_match(fs_basename, dot_name))
*node = dir;
else if (pfs_name_match(fs_basename, dotdot_name))
*node = dir->parent;
else {
if ((rv = dir->file_system_instance->file_system->find(dir->file_system_instance->data, get_dir_local_id(dir), fs_basename, &id, &type)) < 0)
return rv;
if (!rv)
*node = NULL;
else
if ((rv = get_node(dir, id, type, node)) < 0)
return rv;
}
return 0;
}
static void free_node(struct file_struct *node)
{
struct file_struct *parent;
do {
parent = node->parent;
if (!destroy_node(node) || parent->count)
parent = NULL;
free_file(node);
}
while ((node = parent));
}
static int node_is_busy(const struct file_struct *node)
{
return node->count > 1;
}
static int is_parent_directory(const struct file_struct *dir, const struct file_struct *node)
{
for (; node != dir; node = node->parent)
if (node->parent == node)
return 0;
return 1;
}
static void copy_node(const struct file_struct *src, struct file_struct *dest)
{
if (dest == src)
return;
replace(dest, dest->next_entry);
dest->write_access_count = src->write_access_count;
mapping_copy(&src->mapping, &dest->mapping);
dest->parent = src->parent;
dest->next_entry = src->next_entry;
dest->file_system_instance = src->file_system_instance;
dest->id = src->id;
if (src->is_dir) {
dest->first_entry = src->first_entry;
dest->master_file_system_instance = src->master_file_system_instance;
}
else
dest->to_remove = src->to_remove;
replace(src, dest);
}
static int dummy_mount(const struct device_struct *device, void **data)
{
*data = NULL;
return 0;
}
static void dummy_umount(void *data) {}
static int dummy_find(void *data, unsigned long dir_id, const char *fs_basename, unsigned long *id, int *type)
{
return 0;
}
static int dummy_stat(void *data, unsigned long id, 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 = 0;
statbuf.type = FT_DIR;
statbuf.rdev = DEV_NONE;
statbuf.size = 0;
statbuf.mtime = startup_time;
memcpy_tofs(fs_buf, &statbuf, sizeof (struct stat_struct));
return 0;
}
static int dummy_read_dir(void *data, unsigned long dir_id, unsigned long pos, char *fs_buf, unsigned int count)
{
return 0;
}
static const struct file_system_struct dummy_file_system = {
.name = "dummy",
.type = FT_NONE,
.mount = dummy_mount,
.umount = dummy_umount,
.find = dummy_find,
.get_name = NULL,
.get_device = NULL,
.get_size = NULL,
.stat = dummy_stat,
.read_dir = dummy_read_dir,
.read = NULL,
.write = NULL,
.ioctl = NULL,
.create = no_create,
.mkdir = no_mkdir,
.can_remove = NULL,
.remove = NULL,
.rmdir = NULL,
.rename = NULL
};
static struct file_system_instance_struct dummy_file_system_instance = {
.file_system = &dummy_file_system,
.device = DEV_NONE,
.count = 1
};
static struct file_struct _root = {
.file_operations = &dir_operations,
{
{
.count = 0,
.write_access_count = 0,
.parent = &_root,
.next_entry = NULL,
.file_system_instance = &dummy_file_system_instance,
.id = 0,
.is_dir = 1,
.first_entry = NULL,
.master_file_system_instance = &dummy_file_system_instance
}
}
};
struct file_struct *const root = &_root;
void root_init()
{
pfs_insert_dir_entry(&dot, &common_dir_entries);
pfs_insert_dir_entry(&dotdot, &common_dir_entries);
}
int find_node(struct file_struct *dir, const char *fs_basename, struct file_struct **node)
{
return find_node_internal(dir, fs_basename, node);
}
void node_hold(struct file_struct *node)
{
node->count++;
file_system_instance_hold(node->file_system_instance);
}
void node_release(struct file_struct *node)
{
assert(node->count > 0);
node->count--;
file_system_instance_release(node->file_system_instance);
if (!node->count && (!node->is_dir || !node->first_entry)) {
if (node == root)
return;
free_node(node);
}
}
int node_get_write_access(struct file_struct *node)
{
if (node->write_access_count < 0)
return -ETXTBSY;
node->write_access_count++;
return 0;
}
int node_deny_write_access(struct file_struct *node)
{
if (node->write_access_count > 0)
return -ETXTBSY;
node->write_access_count--;
return 0;
}
void node_release_write_access(struct file_struct *node)
{
if (node->write_access_count < 0)
node->write_access_count++;
else if (node->write_access_count > 0)
node->write_access_count--;
}
struct mapping_struct *node_get_mapping(struct file_struct *node)
{
return &node->mapping;
}
int node_create(const struct file_struct *node, const char *fs_basename)
{
if (!node->is_dir)
return -ENOTDIR;
return node->file_system_instance->file_system->create(node->file_system_instance->data, get_dir_local_id(node), fs_basename);
}
int node_remove(struct file_struct *node)
{
int rv;
if (node->is_dir)
return -EISDIR;
if ((rv = node->file_system_instance->file_system->can_remove(node->file_system_instance->data, node->id)) < 0)
return rv;
node->to_remove = 1;
return 0;
}
int node_mkdir(const struct file_struct *node, const char *fs_basename)
{
if (!node->is_dir)
return -ENOTDIR;
return node->file_system_instance->file_system->mkdir(node->file_system_instance->data, get_dir_local_id(node), fs_basename);
}
int node_rmdir(const struct file_struct *node)
{
if (!node->is_dir)
return -ENOTDIR;
if (node_is_busy(node))
return -EBUSY;
return node->file_system_instance->file_system->rmdir(node->file_system_instance->data, node->id, get_dir_local_id(node->parent));
}
int node_rename(struct file_struct *node, struct file_struct *dir, const char *fs_basename)
{
struct file_struct *nd;
int rv;
if (node->is_dir && is_parent_directory(node, dir))
return -EINVAL;
if (node->is_dir && (node_is_busy(node)))
return -EBUSY;
if (node->file_system_instance != dir->file_system_instance)
return -EXDEV;
if ((rv = node->file_system_instance->file_system->rename(node->file_system_instance->data, node->id, get_dir_local_id(node->parent), get_dir_local_id(dir), fs_basename)) < 0)
return rv;
if ((rv = find_node_internal(dir, fs_basename, &nd)) < 0)
return rv;
copy_node(nd, node);
return 0;
}
int node_mount(struct file_struct *node, char *device_path, struct device_struct *device, const struct file_system_struct *file_system)
{
struct file_system_instance_struct *fsi;
int retval;
if (!node->is_dir)
return -ENOTDIR;
if ((node != root && (node_is_busy(node) || node->first_entry))
|| is_mount_point(node))
return -EBUSY;
if (!(fsi = kmalloc(sizeof (struct file_system_instance_struct))))
return -ENOMEM;
if ((retval = file_system_instance_init(fsi, file_system, device_path, device, node)) < 0)
goto error_free;
node_hold(node);
file_system_instance_hold(fsi);
node->file_system_instance = fsi;
return 0;
error_free:
kfree(fsi, sizeof (struct file_system_instance_struct));
return retval;
}
int node_umount(struct file_struct *node)
{
struct file_system_instance_struct *fsi;
if (!node->is_dir)
return -ENOTDIR;
if (!is_mount_point(node))
return -EINVAL;
fsi = node->file_system_instance;
if (file_system_instance_is_busy(fsi))
return -EBUSY;
node->file_system_instance = node->master_file_system_instance;
file_system_instance_release(fsi);
node_release(node);
file_system_instance_destroy(fsi);
kfree(fsi, sizeof (struct file_system_instance_struct));
return 0;
}
struct file_struct *node_get_parent(const struct file_struct *node)
{
return node->parent;
}
int node_is_dir(const struct file_struct *node)
{
return node->is_dir;
}
int node_get_name(const struct file_struct *node, char name[])
{
return node->parent->file_system_instance->file_system->get_name(node->parent->file_system_instance->data, node->id, name);
}
int node_get_device(const struct file_struct *node, struct device_struct **device)
{
return node->file_operations->get_device(node, device);
}
int stat_node(const struct file_struct *node, struct stat_struct *buf)
{
unsigned short fs;
int retval;
get_fs(fs);
load_fs(KERNEL_DATA_SEL);
retval = node_stat(node, buf);
load_fs(fs);
return retval;
}
int read_node(struct file_struct *node, unsigned long pos, void *buf, unsigned int count)
{
unsigned short fs;
int retval;
get_fs(fs);
load_fs(KERNEL_DATA_SEL);
retval = node->file_operations->read(node, pos, buf, count);
load_fs(fs);
return retval;
}