Finished adding untested bare functionality vfs

Finished adding untested shm syscalls.
Finished adding untested l4 send/recv helpers

Everything compiles. Now going to fix lots of bugs ;-)
This commit is contained in:
Bahadir Balban
2008-02-03 17:42:38 +00:00
parent 05e9028e90
commit cab2e8bdd3
51 changed files with 1661 additions and 227 deletions

136
tasks/fs0/src/memfs/file.c Normal file
View File

@@ -0,0 +1,136 @@
/*
* Memfs file operations
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <fs.h>
#include <vfs.h>
#include <file.h>
#include <memfs/memfs.h>
#include <stdio.h>
#include <string.h>
#include <l4/macros.h>
#include <l4/api/errno.h>
#include INC_GLUE(memory.h)
#if 0
/*
* FIXME: read_write() could be more layered using these functions.
*/
void *memfs_read_block(struct vnode *v, int blknum)
{
void *buf = vfs_alloc_block();
if (!buf)
return PTR_ERR(-ENOMEM);
if(!v->block[blknum])
return PTR_ERR(-EEXIST);
memcpy(buf, &v->block[blknum], v->sb->blocksize);
return buf;
}
int memfs_write_block(struct vnode *v, int blknum, void *buf)
{
if(!v->block[blknum])
return -EEXIST;
memcpy(&v->block[blknum], buf, v->sb->blocksize);
return 0;
}
#endif
/*
* Handles both read and writes since most details are common.
*/
int memfs_file_read_write(struct vnode *v, unsigned long pfn,
unsigned long npages, void *buf, int wr)
{
struct memfs_inode *i;
struct memfs_superblock *memfs_sb;
u32 blocksize;
/* Don't support different block and page sizes for now */
BUG_ON(v->sb->blocksize != PAGE_SIZE);
/* Buffer must be page aligned */
BUG_ON(!is_page_aligned(buf));
/* Low-level fs refs must be valid */
BUG_ON(!(i = v->inode));
BUG_ON(!(memfs_sb = v->sb->fs_super));
blocksize = v->sb->blocksize;
/* Check filesystem per-file size limit */
if ((pfn + npages) > memfs_sb->fmaxblocks) {
printf("%s: fslimit: Trying to %s outside maximum file range: %x-%x\n",
__FUNCTION__, (wr) ? "write" : "read", pfn, pfn + npages);
return -EINVAL; /* Same error that posix llseek returns */
}
/* Read-specific operations */
if (!wr) {
/* Check if read is beyond EOF */
if ((pfn + npages) > __pfn(v->size)) {
printf("%s: Trying to read beyond end of file: %x-%x\n",
__FUNCTION__, pfn, pfn + npages);
return -EINVAL; /* Same error that posix llseek returns */
}
/* Copy the data from inode blocks into page buffer */
for (int x = pfn, bufpage = 0; x < pfn + npages; x++, bufpage++)
memcpy(((void *)buf) + (bufpage * blocksize),
&i->block[x], blocksize);
} else { /* Write-specific operations */
/* Is the write beyond current file size? */
if (i->size < ((pfn + npages) * (blocksize))) {
unsigned long diff = pfn + npages - __pfn(i->size);
unsigned long holes;
/*
* If write is not consecutively after the currently
* last file block, the gap must be filled in by holes.
*/
if (pfn > __pfn(i->size))
holes = pfn - __pfn(i->size);
/* Allocate new blocks */
for (int x = 0; x < diff; x++)
if (!(i->block[__pfn(i->size) + x] = memfs_alloc_block(memfs_sb)))
return -ENOSPC;
/* Zero out the holes. FIXME: How do we zero out non-page-aligned bytes?` */
for (int x = 0; x < holes; x++)
memset(i->block[__pfn(i->size) + x], 0, blocksize);
/* Update size and the vnode. FIXME: How do we handle non page-aligned size */
i->size = (pfn + npages) * blocksize;
v->sb->ops->read_vnode(v->sb, v);
}
/* Copy the data from page buffer into inode blocks */
for (int x = pfn, bufpage = 0; x < pfn + npages; x++, bufpage++)
memcpy(i->block[x], ((void *)buf) + (bufpage * blocksize), blocksize);
}
return npages * blocksize;
}
int memfs_file_write(struct vnode *v, unsigned long pfn, unsigned long npages, void *buf)
{
return memfs_file_read_write(v, pfn, npages, buf, 1);
}
int memfs_file_read(struct vnode *v, unsigned long pfn, unsigned long npages, void *buf)
{
return memfs_file_read_write(v, pfn, npages, buf, 0);
}
struct file_ops memfs_file_operations = {
.read = memfs_file_read,
.write = memfs_file_write,
};

159
tasks/fs0/src/memfs/memfs.c Normal file
View File

@@ -0,0 +1,159 @@
/*
* A simple read/writeable memory-only filesystem.
*
* Copyright (C) 2007, 2008 Bahadir Balban
*/
#include <init.h>
#include <fs.h>
#include <vfs.h>
#include <memfs/memfs.h>
#include <memfs/vnode.h>
#include <lib/idpool.h>
#include <l4/macros.h>
#include <l4/types.h>
#include <l4/api/errno.h>
#include INC_GLUE(memory.h)
struct memfs_superblock *memfs_superblock;
/* Initialise allocation caches as part of superblock initialisation */
int memfs_init_caches(struct memfs_superblock *sb)
{
void *free_block;
struct mem_cache *block_cache;
struct mem_cache *inode_cache;
/* Use the whole filesystem space to initialise block cache */
free_block = (void *)sb + sizeof(*sb);
block_cache = mem_cache_init(free_block, sb->fssize - sizeof(*sb),
sb->blocksize, 1);
list_add(&block_cache->list, &sb->block_cache_list);
/* Allocate a block and initialise it as first inode cache */
free_block = mem_cache_alloc(block_cache);
inode_cache = mem_cache_init(free_block, sb->blocksize,
sizeof(struct memfs_inode), 0);
list_add(&inode_cache->list, &sb->inode_cache_list);
return 0;
}
/*
* Given an empty block buffer, initialises a filesystem there.
*/
int memfs_format_filesystem(void *buffer)
{
struct memfs_superblock *sb = buffer; /* Buffer is the first block */
/* Zero initialise the superblock area */
memset(sb, 0, sizeof(*sb));
/* Initialise filesystem parameters */
sb->magic = MEMFS_MAGIC;
memcpy(sb->name, MEMFS_NAME, MEMFS_NAME_SIZE);
sb->blocksize = MEMFS_BLOCK_SIZE;
sb->fmaxblocks = MEMFS_FMAX_BLOCKS;
sb->fssize = MEMFS_TOTAL_SIZE;
/* Initialise block and inode index pools */
sb->ipool = id_pool_new_init(MEMFS_TOTAL_INODES);
sb->bpool = id_pool_new_init(MEMFS_TOTAL_BLOCKS);
/* Initialise bitmap allocation lists for blocks and inodes */
INIT_LIST_HEAD(&sb->block_cache_list);
INIT_LIST_HEAD(&sb->inode_cache_list);
memfs_init_caches(sb);
/* We allocate and fix a root inode so the sb is ready for mount */
sb->root = memfs_create_inode(sb);
return 0;
}
/* Allocates a block of unused buffer */
void *memfs_alloc_block(struct memfs_superblock *sb)
{
struct mem_cache *cache;
list_for_each_entry(cache, &sb->block_cache_list, list) {
if (cache->free)
return mem_cache_zalloc(cache);
else
continue;
}
return PTR_ERR(-ENOSPC);
}
/*
* Even though on a list, block allocation is currently from a single cache.
* This frees a block back to the free buffer cache.
*/
int memfs_free_block(struct memfs_superblock *sb, void *block)
{
struct mem_cache *c, *tmp;
list_for_each_entry_safe(c, tmp, &sb->block_cache_list, list)
if (!mem_cache_free(c, block))
return 0;
else
return -EINVAL;
return -EINVAL;
}
struct superblock *memfs_get_superblock(void *block);
struct file_system_type memfs_fstype = {
.name = "memfs",
.magic = MEMFS_MAGIC,
.ops = {
.get_superblock = memfs_get_superblock,
},
};
/* Copies fs-specific superblock into generic vfs superblock */
struct superblock *memfs_fill_superblock(struct memfs_superblock *sb,
struct superblock *vfs_sb)
{
vfs_sb->fs = &memfs_fstype;
vfs_sb->ops = &memfs_superblock_operations;
vfs_sb->fs_super = sb;
vfs_sb->fssize = sb->fssize;
vfs_sb->blocksize = sb->blocksize;
/*
* Create the first vnode. Since this is memfs, root vnode is
* not read-in but dynamically created here.
*/
vfs_sb->ops->alloc_vnode(vfs_sb);
return vfs_sb;
}
/*
* Probes block buffer for a valid memfs superblock, if found,
* allocates and copies data to a vfs superblock, and returns it.
*/
struct superblock *memfs_get_superblock(void *block)
{
struct memfs_superblock *sb = block;
struct superblock *vfs_sb;
/* We don't do sanity checks here, just confirm id. */
if (!strcmp(sb->name, "memfs"))
return 0;
if (sb->magic != MEMFS_MAGIC)
return 0;
/* Allocate a vfs superblock. */
vfs_sb = vfs_alloc_superblock();
/* Fill generic sb from fs-specific sb */
return memfs_fill_superblock(sb, vfs_sb);
}
/* Registers sfs as an available filesystem type */
void memfs_register_fstype(struct list_head *fslist)
{
list_add(&memfs_fstype.list, fslist);
}

278
tasks/fs0/src/memfs/vnode.c Normal file
View File

@@ -0,0 +1,278 @@
/*
* Inode and vnode implementation.
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <fs.h>
#include <vfs.h>
#include <memfs/memfs.h>
#include <memfs/file.h>
#include <l4/lib/list.h>
#include <l4/api/errno.h>
#include <l4/macros.h>
#include <lib/malloc.h>
#include <stdio.h>
struct memfs_inode *memfs_alloc_inode(struct memfs_superblock *sb)
{
struct mem_cache *cache;
struct memfs_inode *i;
void *free_block;
/* Ask existing inode caches for a new inode */
list_for_each_entry(cache, &sb->inode_cache_list, list) {
if (cache->free)
if (!(i = mem_cache_zalloc(cache)))
return PTR_ERR(-ENOSPC);
else
return i;
else
continue;
}
/* Ask existing block caches for a new block */
if (IS_ERR(free_block = memfs_alloc_block(sb)))
return PTR_ERR(free_block);
/* Initialise it as an inode cache */
cache = mem_cache_init(free_block, sb->blocksize,
sizeof(struct memfs_inode), 0);
list_add(&cache->list, &sb->inode_cache_list);
if (!(i = mem_cache_zalloc(cache)))
return PTR_ERR(-ENOSPC);
else
return i;
}
/*
* O(n^2) complexity but its simple, yet it would only reveal on high numbers.
*/
int memfs_free_inode(struct memfs_superblock *sb, struct memfs_inode *i)
{
struct mem_cache *c, *tmp;
list_for_each_entry_safe(c, tmp, &sb->inode_cache_list, list) {
/* Free it, if success */
if (!mem_cache_free(c, i)) {
/* If cache completely emtpy */
if (mem_cache_is_empty(c)) {
/* Free the block, too. */
list_del(&c->list);
memfs_free_block(sb, c);
}
return 0;
}
}
return -EINVAL;
}
/* Allocates *and* initialises the inode */
struct memfs_inode *memfs_create_inode(struct memfs_superblock *sb)
{
struct memfs_inode *i;
/* Allocate the inode */
if (PTR_ERR(i = memfs_alloc_inode(sb)) < 0)
return i;
/* Allocate a new inode number */
if ((i->inum = id_new(sb->ipool)) < 0)
return i;
/* Put a reference to this inode in the inode table at this index */
sb->inode[i->inum] = i;
return i;
}
/* Deallocate the inode and any other closely relevant structure */
int memfs_destroy_inode(struct memfs_superblock *sb, struct memfs_inode *i)
{
int inum = i->inum;
/* Deallocate the inode */
if (memfs_free_inode(sb, i) < 0)
return -EINVAL;
/* Deallocate the inode number */
if (id_del(sb->ipool, inum) < 0)
return -EINVAL;
/* Clear the ref in inode table */
sb->inode[inum] = 0;
return 0;
}
struct vnode *memfs_alloc_vnode(struct superblock *sb)
{
struct memfs_inode *i;
struct vnode *v;
/* Get a (pseudo-disk) memfs inode */
if (IS_ERR(i = memfs_create_inode(sb->fs_super)))
return PTR_ERR(i);
/* Get a vnode */
if (!(v = vfs_alloc_vnode()))
return PTR_ERR(-ENOMEM);
/* Associate the two together */
v->inode = i;
v->vnum = i->inum;
/* Associate memfs-specific fields with vnode */
v->ops = memfs_vnode_operations;
v->fops = memfs_file_operations;
/* Return the vnode */
return v;
}
int memfs_free_vnode(struct superblock *sb, struct vnode *v)
{
struct memfs_inode *i = v->inode;
BUG_ON(!i); /* Vnodes that come here must have valid inodes */
/* Destroy on-disk inode */
memfs_destroy_inode(sb->fs_super, i);
/* Free vnode */
vfs_free_vnode(v);
return 0;
}
/*
* Given a vnode with a valid vnum, this retrieves the corresponding
* inode from the filesystem.
*/
struct memfs_inode *memfs_read_inode(struct superblock *sb, struct vnode *v)
{
struct memfs_superblock *fssb = sb->fs_super;
BUG_ON(!fssb->inode[v->vnum]);
return fssb->inode[v->vnum];
}
/*
* Given a preallocated vnode with a valid vnum, this reads the corresponding
* inode from the filesystem and fills in the vnode's fields.
*/
int memfs_read_vnode(struct superblock *sb, struct vnode *v)
{
struct memfs_inode *i = memfs_read_inode(sb, v);
if (!i)
return -EEXIST;
/* Simply copy common fields */
v->vnum = i->inum;
v->size = i->size;
v->mode = i->mode;
v->owner = i->owner;
v->atime = i->atime;
v->mtime = i->mtime;
v->ctime = i->ctime;
return 0;
}
int memfs_write_vnode(struct superblock *sb, struct vnode *v)
{
struct memfs_inode *i = v->inode;
/* Vnodes that come here must have valid inodes */
BUG_ON(!i);
/* Simply copy common fields */
i->inum = v->vnum;
i->size = v->size;
i->mode = v->mode;
i->owner = v->owner;
i->atime = v->atime;
i->mtime = v->mtime;
i->ctime = v->ctime;
return 0;
}
/*
* Allocates and populates all dentries and their corresponding vnodes that are
* the direct children of vnode v. This means that by each call to readdir, the vfs
* layer increases its cache of filesystem tree by one level beneath that directory.
*/
void *memfs_vnode_readdir(struct vnode *v, void *dirbuf)
{
int err;
struct memfs_dentry *memfsd = dirbuf;
struct dentry *parent = list_entry(v->dentries.next, struct dentry,
vref);
/* Check directory type */
if (!vfs_isdir(v))
return PTR_ERR(-ENOTDIR);
/* Allocate dirbuf if one is not provided by the upper layer */
if (!dirbuf) {
/* This is as big as a page */
memfsd = dirbuf = vfs_alloc_dirpage();
/*
* Fail if vnode size is bigger than a page. Since this allocation
* method is to be replaced, we can live with this limitation for now.
*/
BUG_ON(v->size > PAGE_SIZE);
}
/* Read contents into the buffer */
if ((err = v->fops.read(v, 0, 1, dirbuf)))
return PTR_ERR(err);
/* For each fs-specific directory entry */
for (int i = 0; i < (v->size / sizeof(struct memfs_dentry)); i++) {
struct dentry *newd;
struct vnode *newv;
/* Allocate a vfs dentry */
if (!(newd = vfs_alloc_dentry()))
return PTR_ERR(-ENOMEM);
/* Initialise it */
newd->ops = generic_dentry_operations;
newd->parent = parent;
list_add(&newd->child, &parent->children);
/*
* Read the vnode for dentry by its vnode number.
*/
newv = newd->vnode = vfs_alloc_vnode();
newv->vnum = memfsd[i].inum;
BUG_ON(newv->sb->ops->read_vnode(newv->sb, newv) < 0);
/* Assing this dentry as a name of its vnode */
list_add(&newd->vref, &newd->vnode->dentries);
/* Copy fields into generic dentry */
memcpy(newd->name, memfsd[i].name, MEMFS_DNAME_MAX);
}
return dirbuf;
}
struct vnode_ops memfs_vnode_operations = {
.lookup = generic_vnode_lookup,
.readdir = memfs_vnode_readdir,
};
struct superblock_ops memfs_superblock_operations = {
.read_vnode = memfs_read_vnode,
.write_vnode = memfs_write_vnode,
.alloc_vnode = memfs_alloc_vnode,
.free_vnode = memfs_free_vnode,
};