mirror of
https://github.com/drasko/codezero.git
synced 2026-01-24 16:53:14 +01:00
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:
14
tasks/fs0/src/bdev.c
Normal file
14
tasks/fs0/src/bdev.c
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* This is just to allocate some memory as a block device.
|
||||
*/
|
||||
#include <l4/macros.h>
|
||||
|
||||
extern char _start_bdev[];
|
||||
extern char _end_bdev[];
|
||||
|
||||
__attribute__((section(".data.memfs"))) char blockdevice[SZ_16MB];
|
||||
|
||||
void *vfs_rootdev_open(void)
|
||||
{
|
||||
return (void *)_start_bdev;
|
||||
}
|
||||
29
tasks/fs0/src/c0fs/c0fs.c
Normal file
29
tasks/fs0/src/c0fs/c0fs.c
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* A basic unix-like read/writeable filesystem for Codezero.
|
||||
*
|
||||
* Copyright (C) 2007, 2008 Bahadir Balban
|
||||
*/
|
||||
#include <init.h>
|
||||
#include <fs.h>
|
||||
|
||||
void sfs_read_sb(struct superblock *sb)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void simplefs_alloc_vnode(struct vnode *)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
struct file_system_type sfs_type = {
|
||||
.name = "c0fs",
|
||||
.magic = 1,
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
/* Registers sfs as an available filesystem type */
|
||||
void sfs_register_fstype(struct list_head *fslist)
|
||||
{
|
||||
list_add(&sfs_type.list, fslist);
|
||||
}
|
||||
@@ -4,10 +4,11 @@
|
||||
* Copyright (C) 2007, 2008 Bahadir Balban
|
||||
*/
|
||||
|
||||
#ifndef __SFS_LAYOUT_H__
|
||||
#define __SFS_LAYOUT_H__
|
||||
#ifndef __C0FS_LAYOUT_H__
|
||||
#define __C0FS_LAYOUT_H__
|
||||
|
||||
#include <l4lib/types.h>
|
||||
#include <l4/lib/list.h>
|
||||
#include <l4/macros.h>
|
||||
#include <l4/config.h>
|
||||
#include INC_GLUE(memory.h)
|
||||
@@ -54,13 +55,20 @@
|
||||
|
||||
struct sfs_superblock {
|
||||
u32 magic; /* Filesystem magic number */
|
||||
u32 fssize; /* Total size of filesystem */
|
||||
u32 szidx; /* Bitmap index size */
|
||||
u64 fssize; /* Total size of filesystem */
|
||||
u32 total; /* To */
|
||||
u32 groupmap[]; /* Bitmap of all fs groups */
|
||||
};
|
||||
|
||||
struct sfs_group_table {
|
||||
u32 total;
|
||||
u32 free;
|
||||
u32 groupmap[];
|
||||
};
|
||||
|
||||
struct sfs_inode_table {
|
||||
u32 szidx;
|
||||
u32 total;
|
||||
u32 free;
|
||||
u32 inodemap[INODE_BITMAP_SIZE];
|
||||
struct sfs_inode inode[INODE_TABLE_SIZE];
|
||||
};
|
||||
@@ -74,7 +82,7 @@ struct sfs_inode_table {
|
||||
*/
|
||||
#define INODE_DIRECT_BLOCKS 5
|
||||
struct sfs_inode_blocks {
|
||||
u32 szidx; /* Direct array index size */
|
||||
int szidx; /* Direct array index size */
|
||||
unsigned long indirect;
|
||||
unsigned long indirect2;
|
||||
unsigned long indirect3;
|
||||
@@ -86,10 +94,10 @@ struct sfs_inode {
|
||||
u32 inum; /* Inode number */
|
||||
u32 mode; /* File permissions */
|
||||
u32 owner; /* File owner */
|
||||
u32 atime; /* Last access time */
|
||||
u32 mtime; /* Last content modification */
|
||||
u32 ctime; /* Last inode modification */
|
||||
u32 size; /* Size of contents */
|
||||
u64 atime; /* Last access time */
|
||||
u64 mtime; /* Last content modification */
|
||||
u64 ctime; /* Last inode modification */
|
||||
u64 size; /* Size of contents */
|
||||
struct sfs_inode_blocks blocks;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
@@ -99,4 +107,7 @@ struct sfs_dentry {
|
||||
u8 name[]; /* Name string */
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
#endif /* __SFS_LAYOUT_H__ */
|
||||
|
||||
void sfs_register_type(struct list_head *);
|
||||
|
||||
#endif /* __C0FS_LAYOUT_H__ */
|
||||
28
tasks/fs0/src/file.c
Normal file
28
tasks/fs0/src/file.c
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* File content tracking.
|
||||
*
|
||||
* Copyright (C) 2008 Bahadir Balban
|
||||
*/
|
||||
#include <fs.h>
|
||||
#include <file.h>
|
||||
#include <l4/lib/list.h>
|
||||
#include <l4/macros.h>
|
||||
#include INC_GLUE(memory.h)
|
||||
#include <stdio.h>
|
||||
|
||||
/* List of all in-memory files. */
|
||||
struct list_head vm_file_list;
|
||||
|
||||
/*
|
||||
* This reads contents of a file in pages, calling the fs-specific file read function to read-in
|
||||
* those pages' contents.
|
||||
*
|
||||
* Normally this is ought to be called by mm0 when a file's pages cannot be found in the page
|
||||
* cache.
|
||||
*/
|
||||
int generic_file_read(struct vnode *v, unsigned long pfn, unsigned long npages, void *page_buf)
|
||||
{
|
||||
BUG_ON(!is_page_aligned(page_buf));
|
||||
|
||||
return v->fops.read(v, pfn, npages, page_buf);
|
||||
}
|
||||
@@ -3,19 +3,92 @@
|
||||
*
|
||||
* Copyright (C) 2007 Bahadir Balban
|
||||
*/
|
||||
#include <kdata.h>
|
||||
#include <fs.h>
|
||||
#include <string.h>
|
||||
#include <vfs.h>
|
||||
#include <bdev.h>
|
||||
#include <task.h>
|
||||
#include <kdata.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <l4/lib/list.h>
|
||||
#include <init.h>
|
||||
#include <blkdev/blkdev.h>
|
||||
#include <l4/api/errno.h>
|
||||
#include <memfs/memfs.h>
|
||||
|
||||
void initialise(void)
|
||||
struct list_head fs_type_list;
|
||||
|
||||
struct superblock *vfs_probe_filesystems(void *block)
|
||||
{
|
||||
struct block_device *bdev;
|
||||
struct file_system_type *fstype;
|
||||
struct superblock *sb;
|
||||
|
||||
request_initdata(&initdata);
|
||||
vfs_probe_fs(bdev);
|
||||
list_for_each_entry(fstype, &fs_type_list, list) {
|
||||
/* Does the superblock match for this fs type? */
|
||||
if ((sb = fstype->ops.get_superblock(block))) {
|
||||
/*
|
||||
* Add this to the list of superblocks this
|
||||
* fs already has.
|
||||
*/
|
||||
list_add(&sb->list, &fstype->sblist);
|
||||
return sb;
|
||||
}
|
||||
}
|
||||
|
||||
return PTR_ERR(-ENODEV);
|
||||
}
|
||||
|
||||
/*
|
||||
* Registers each available filesystem so that these can be
|
||||
* used when probing superblocks on block devices.
|
||||
*/
|
||||
void vfs_register_filesystems(void)
|
||||
{
|
||||
/* Initialise fstype list */
|
||||
INIT_LIST_HEAD(&fs_type_list);
|
||||
|
||||
/* Call per-fs registration functions */
|
||||
memfs_register_fstype(&fs_type_list);
|
||||
}
|
||||
|
||||
extern struct list_head vm_file_list;
|
||||
|
||||
/*
|
||||
* Filesystem initialisation.
|
||||
*/
|
||||
int initialise(void)
|
||||
{
|
||||
void *rootdev_blocks;
|
||||
struct superblock *root_sb;
|
||||
|
||||
/* Get standard init data from microkernel */
|
||||
request_initdata(&initdata);
|
||||
|
||||
/* Register compiled-in filesystems with vfs core. */
|
||||
vfs_register_filesystems();
|
||||
|
||||
/* Get a pointer to first block of root block device */
|
||||
rootdev_blocks = vfs_rootdev_open();
|
||||
|
||||
/*
|
||||
* Since the *only* filesystem we have is a temporary memory
|
||||
* filesystem, we create it on the root device first.
|
||||
*/
|
||||
memfs_format_filesystem(rootdev_blocks);
|
||||
|
||||
INIT_LIST_HEAD(&vm_file_list);
|
||||
|
||||
/* Search for a filesystem on the root device */
|
||||
BUG_ON(IS_ERR(root_sb = vfs_probe_filesystems(rootdev_blocks)));
|
||||
|
||||
/* Mount the filesystem on the root device */
|
||||
vfs_mount_root(root_sb);
|
||||
|
||||
/* Learn about what tasks are running */
|
||||
init_task_data();
|
||||
|
||||
/*
|
||||
* Initialisation is done. From here on, we can start
|
||||
* serving filesystem requests.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
* Copyright (C) 2007 Bahadir Balban
|
||||
*/
|
||||
#include <lib/idpool.h>
|
||||
#include <kmalloc/kmalloc.h>
|
||||
// #include <kmalloc/kmalloc.h> --> This requires page allocation to grow/shrink.
|
||||
#include <lib/malloc.h> // --> This is a local library that statically allocates its heap.
|
||||
#include <l4/macros.h>
|
||||
#include INC_GLUE(memory.h)
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ typedef struct _malloc /* Turbo C DJGPP */
|
||||
static char *g_heap_bot, *g_kbrk, *g_heap_top;
|
||||
/*****************************************************************************
|
||||
*****************************************************************************/
|
||||
static void dump_heap(void)
|
||||
void dump_heap(void)
|
||||
{
|
||||
unsigned blks_used = 0, blks_free = 0;
|
||||
size_t bytes_used = 0, bytes_free = 0;
|
||||
@@ -204,6 +204,15 @@ create a new, free block */
|
||||
}
|
||||
return (char *)n + sizeof(malloc_t);
|
||||
}
|
||||
|
||||
static inline void *kzalloc(size_t size)
|
||||
{
|
||||
void *buf = kmalloc(size);
|
||||
|
||||
memset(buf, 0, size);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*****************************************************************************/
|
||||
void kfree(void *blk)
|
||||
111
tasks/fs0/src/lookup.c
Normal file
111
tasks/fs0/src/lookup.c
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Inode lookup.
|
||||
*
|
||||
* Copyright (C) 2007, 2008 Bahadir Balban
|
||||
*/
|
||||
#include <fs.h>
|
||||
#include <vfs.h>
|
||||
#include <stat.h>
|
||||
#include <l4/api/errno.h>
|
||||
|
||||
/*
|
||||
* Splits the string str according to the given seperator, returns the
|
||||
* first component, and modifies the str so that it points at the next
|
||||
* available component (or a leading separator which can be filtered
|
||||
* out on a subsequent call to this function).
|
||||
*/
|
||||
char *splitpath(char **str, char sep)
|
||||
{
|
||||
char *cursor = *str;
|
||||
char *end;
|
||||
|
||||
/* Move forward until no seperator */
|
||||
while (*cursor == sep) {
|
||||
*cursor = '\0';
|
||||
cursor++; /* Move to first char of component */
|
||||
}
|
||||
|
||||
end = cursor;
|
||||
while (*end != sep && *end != '\0')
|
||||
end++; /* Move until end of component */
|
||||
|
||||
if (*end == sep) { /* if ended with separator */
|
||||
*end = '\0'; /* finish component by null */
|
||||
if (*(end + 1) != '\0') /* if more components after, */
|
||||
*str = end + 1; /* assign beginning to str */
|
||||
else
|
||||
*str = end; /* else str is also depleted, give null */
|
||||
} else /* if end was null, that means the end for str, too. */
|
||||
*str = end;
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a dentry that has been populated by readdir with children dentries
|
||||
* and their vnodes, this itself checks all those children on that level for
|
||||
* a match, but it also calls lookup, which recursively checks lower levels.
|
||||
*/
|
||||
struct vnode *lookup_dentry_children(struct dentry *parentdir, char *path)
|
||||
{
|
||||
struct dentry *childdir;
|
||||
struct vnode *v;
|
||||
|
||||
list_for_each_entry(childdir, &parentdir->children, child)
|
||||
/* Non-zero result means either found it or error occured */
|
||||
if ((v = childdir->vnode->ops.lookup(childdir->vnode, path)))
|
||||
return v;
|
||||
|
||||
/* Out of all children dentries, neither was a match, so we return 0 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Lookup, recursive, assuming single-mountpoint */
|
||||
struct vnode *generic_vnode_lookup(struct vnode *thisnode, char *path)
|
||||
{
|
||||
char *component = splitpath(&path, VFS_STR_SEP);
|
||||
struct dentry *d;
|
||||
struct vnode *found;
|
||||
int err;
|
||||
|
||||
/* Does this path component match with any of parent vnode's names? */
|
||||
list_for_each_entry(d, &thisnode->dentries, vref) {
|
||||
if (d->ops.compare(d, component)) {
|
||||
/* Is this a directory? */
|
||||
if (vfs_isdir(thisnode)) {
|
||||
/* Are there any more path components? */
|
||||
if (path)
|
||||
/* Read directory contents */
|
||||
if (IS_ERR(err = (int)d->vnode->ops.readdir(d->vnode, 0)))
|
||||
return PTR_ERR(err);
|
||||
/* Search all children one level below. */
|
||||
if ((found = lookup_dentry_children(d, path)))
|
||||
/* Either found, or non-zero error */
|
||||
return found;
|
||||
else
|
||||
return thisnode;
|
||||
} else { /* Its a file */
|
||||
if (path) /* There's still path, but not directory */
|
||||
return PTR_ERR(-ENOTDIR);
|
||||
else /* No path left, found it, so return file */
|
||||
return thisnode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Not found, return nothing */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int generic_dentry_compare(struct dentry *d, char *name)
|
||||
{
|
||||
if (!strcmp(d->name, name))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dentry_ops generic_dentry_operations = {
|
||||
.compare = generic_dentry_compare,
|
||||
};
|
||||
|
||||
136
tasks/fs0/src/memfs/file.c
Normal file
136
tasks/fs0/src/memfs/file.c
Normal 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
159
tasks/fs0/src/memfs/memfs.c
Normal 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
278
tasks/fs0/src/memfs/vnode.c
Normal 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,
|
||||
};
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* A basic unix-like read/writeable filesystem.
|
||||
*
|
||||
* Copyright (C) 2007, 2008 Bahadir Balban
|
||||
*/
|
||||
|
||||
void sfs_read_sb(struct superblock *sb)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void simplefs_alloc_vnode(struct vnode *)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
95
tasks/fs0/src/syscalls.c
Normal file
95
tasks/fs0/src/syscalls.c
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Some syscall stubs
|
||||
*
|
||||
* Copyright (C) 2008 Bahadir Balban
|
||||
*/
|
||||
#include <l4/api/errno.h>
|
||||
#include <l4lib/types.h>
|
||||
#include <l4lib/ipcdefs.h>
|
||||
#include <l4lib/arch/syscalls.h>
|
||||
#include <l4lib/arch/syslib.h>
|
||||
#include <vfs.h>
|
||||
#include <lib/malloc.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <task.h>
|
||||
|
||||
/*
|
||||
* This notifies mm0 that this is the fd that refers to this vnode number
|
||||
* from now on.
|
||||
*
|
||||
* MM0 *also* keeps track of fd's because mm0 is a better candidate
|
||||
* for handling syscalls that access file content (i.e. read/write) since
|
||||
* it maintains the page cache.
|
||||
*/
|
||||
int send_pager_opendata(l4id_t sender, int fd, unsigned long vnum)
|
||||
{
|
||||
int err;
|
||||
|
||||
write_mr(L4SYS_ARG0, sender);
|
||||
write_mr(L4SYS_ARG1, fd);
|
||||
write_mr(L4SYS_ARG2, vnum);
|
||||
|
||||
if ((err = l4_send(PAGER_TID, L4_IPC_TAG_OPENDATA)) < 0) {
|
||||
printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int do_open(l4id_t sender, const char *pathname, int flags, unsigned int mode)
|
||||
{
|
||||
struct vnode *v;
|
||||
struct tcb *t;
|
||||
int fd;
|
||||
|
||||
/* FIXME: Use strnlen */
|
||||
char *pathcopy = kmalloc(strlen(pathname));
|
||||
/* FIXME: Use strncpy */
|
||||
memcpy(pathcopy, pathname, strlen(pathname));
|
||||
|
||||
/* Get the vnode */
|
||||
if (IS_ERR(v = vfs_lookup(vfs_root.pivot->sb, pathcopy)))
|
||||
return (int)v;
|
||||
|
||||
/* Get the task */
|
||||
BUG_ON(!(t = find_task(sender)));
|
||||
|
||||
/* Get a new fd */
|
||||
BUG_ON(!(fd = id_new(t->fdpool)));
|
||||
|
||||
/* Assign the new fd with the vnode's number */
|
||||
t->fd[fd] = v->vnum;
|
||||
|
||||
/* Tell mm0 about opened vnode information */
|
||||
BUG_ON(send_pager_opendata(sender, fd, v->vnum) < 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME:
|
||||
* - Is it already open?
|
||||
* - Allocate a copy of path string since lookup destroys it
|
||||
* - Check flags and mode.
|
||||
*/
|
||||
int sys_open(l4id_t sender, const char *pathname, int flags, unsigned int mode)
|
||||
{
|
||||
return do_open(sender, pathname, flags, mode);
|
||||
}
|
||||
|
||||
int sys_read(l4id_t sender, int fd, void *buf, int count)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sys_write(l4id_t sender, int fd, const void *buf, int count)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sys_lseek(l4id_t sender, int fd, int offset, int whence)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
107
tasks/fs0/src/task.c
Normal file
107
tasks/fs0/src/task.c
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* FS0 task data initialisation.
|
||||
*
|
||||
* Copyright (C) 2008 Bahadir Balban
|
||||
*/
|
||||
#include <l4/macros.h>
|
||||
#include <l4/lib/list.h>
|
||||
#include <l4/api/errno.h>
|
||||
#include <l4lib/arch/syscalls.h>
|
||||
#include <l4lib/arch/syslib.h>
|
||||
#include <l4lib/ipcdefs.h>
|
||||
#include <lib/malloc.h>
|
||||
#include <task.h>
|
||||
|
||||
struct tcb_head {
|
||||
struct list_head list;
|
||||
int total; /* Total threads */
|
||||
} tcb_head;
|
||||
|
||||
struct tcb *find_task(int tid)
|
||||
{
|
||||
struct tcb *t;
|
||||
|
||||
list_for_each_entry(t, &tcb_head.list, list)
|
||||
if (t->tid == tid)
|
||||
return t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Asks pager to send information about currently running tasks. Since this is
|
||||
* called during initialisation, there can't be that many, so we assume message
|
||||
* registers are sufficient. First argument tells how many there are, the rest
|
||||
* tells the tids.
|
||||
*/
|
||||
int receive_pager_taskdata(l4id_t *tdata)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Make the actual ipc call */
|
||||
if ((err = l4_sendrecv(PAGER_TID, PAGER_TID,
|
||||
L4_IPC_TAG_TASKDATA)) < 0) {
|
||||
printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Check if call itself was successful */
|
||||
if ((err = l4_get_retval()) < 0) {
|
||||
printf("%s: Error: %d.\n", __FUNCTION__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Read total number of tasks. Note already used one mr. */
|
||||
if ((tdata[0] = (l4id_t)read_mr(L4SYS_ARG0)) >= MR_UNUSED_TOTAL) {
|
||||
printf("%s: Error: Too many tasks to read. Won't fit in mrs.\n",
|
||||
__FUNCTION__);
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* Now read task ids. */
|
||||
for (int i = 0; i < (int)tdata[0]; i++)
|
||||
tdata[1 + i] = (l4id_t)read_mr(L4SYS_ARG1 + i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allocate a task struct and initialise it */
|
||||
struct tcb *create_tcb(l4id_t tid)
|
||||
{
|
||||
struct tcb *t;
|
||||
|
||||
if (!(t = kmalloc(sizeof(*t))))
|
||||
return PTR_ERR(-ENOMEM);
|
||||
|
||||
t->tid = tid;
|
||||
INIT_LIST_HEAD(&t->list);
|
||||
list_add_tail(&t->list, &tcb_head.list);
|
||||
tcb_head.total++;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
int init_task_structs(l4id_t *tdata)
|
||||
{
|
||||
struct tcb *t;
|
||||
int total = tdata[0];
|
||||
|
||||
for (int i = 0; i < total; i++)
|
||||
if (IS_ERR(t = create_tcb(tdata[1 + i])))
|
||||
return (int)t;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void init_task_data(void)
|
||||
{
|
||||
l4id_t tdata[MR_UNUSED_TOTAL];
|
||||
|
||||
INIT_LIST_HEAD(&tcb_head.list);
|
||||
|
||||
/* Read how many tasks and tids of each */
|
||||
BUG_ON(receive_pager_taskdata(tdata) < 0);
|
||||
|
||||
/* Initialise local task structs using this info */
|
||||
init_task_structs(tdata);
|
||||
}
|
||||
|
||||
36
tasks/fs0/src/vfs.c
Normal file
36
tasks/fs0/src/vfs.c
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* High-level vfs implementation.
|
||||
*
|
||||
* Copyright (C) 2008 Bahadir Balban
|
||||
*/
|
||||
#include <fs.h>
|
||||
#include <vfs.h>
|
||||
|
||||
struct vnode *vfs_lookup(struct superblock *sb, char *path)
|
||||
{
|
||||
/* If it's just / we already got it. */
|
||||
if (!strcmp(path, "/"))
|
||||
return sb->root;
|
||||
|
||||
return sb->root->ops.lookup(sb->root, path);
|
||||
}
|
||||
|
||||
/*
|
||||
* /
|
||||
* /boot
|
||||
* /boot/images/mm0.axf
|
||||
* /boot/images/fs0.axf
|
||||
* /boot/images/test0.axf
|
||||
* /file.txt
|
||||
*/
|
||||
struct vfs_mountpoint vfs_root;
|
||||
|
||||
int vfs_mount_root(struct superblock *sb)
|
||||
{
|
||||
/* Lookup the root vnode of this superblock */
|
||||
vfs_root.pivot = vfs_lookup(sb, "/");
|
||||
vfs_root.sb = sb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user