mirror of
https://github.com/drasko/codezero.git
synced 2026-03-11 23:03:15 +01:00
Added posix code
This commit is contained in:
15
conts/posix/fs0/src/bdev.c
Normal file
15
conts/posix/fs0/src/bdev.c
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* This is just to allocate some memory as a block device.
|
||||
*/
|
||||
#include <l4/macros.h>
|
||||
#include <memfs/memfs.h>
|
||||
|
||||
extern char _start_bdev[];
|
||||
extern char _end_bdev[];
|
||||
|
||||
__attribute__((section(".data.memfs"))) char blockdevice[MEMFS_TOTAL_SIZE];
|
||||
|
||||
void *vfs_rootdev_open(void)
|
||||
{
|
||||
return (void *)_start_bdev;
|
||||
}
|
||||
147
conts/posix/fs0/src/bootfs/bootfs.c
Normal file
147
conts/posix/fs0/src/bootfs/bootfs.c
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* A pseudo-filesystem for reading the in-memory
|
||||
* server tasks loaded from the initial elf executable.
|
||||
*
|
||||
* Copyright (C) 2007, 2008 Bahadir Balban
|
||||
*/
|
||||
#include <fs.h>
|
||||
#include <l4/lib/list.h>
|
||||
#include <malloc.h>
|
||||
|
||||
struct dentry *bootfs_dentry_lookup(struct dentry *d, char *dname)
|
||||
{
|
||||
struct dentry *this;
|
||||
|
||||
list_foreach_struct(this, child, &d->children) {
|
||||
if (this->compare(this, dname))
|
||||
return this;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dentry *path_lookup(struct superblock *sb, char *pathstr)
|
||||
{
|
||||
char *dname;
|
||||
char *splitpath;
|
||||
struct dentry *this, *next;
|
||||
|
||||
/* First dentry is root */
|
||||
this = sb->root;
|
||||
|
||||
/* Get next path component from path string */
|
||||
dname = path_next_dentry_name(pathstr);
|
||||
|
||||
if (!this->compare(dname))
|
||||
return;
|
||||
|
||||
while(!(dname = path_next_dentry_name(pathstr))) {
|
||||
if ((d = this->lookup(this, dname)))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This creates a pseudo-filesystem tree from the loaded
|
||||
* server elf images whose information is available from
|
||||
* initdata.
|
||||
*/
|
||||
void bootfs_populate(struct initdata *initdata, struct superblock *sb)
|
||||
{
|
||||
struct bootdesc *bd = initdata->bootdesc;
|
||||
struct svc_image *img;
|
||||
struct dentry *d;
|
||||
struct vnode *v;
|
||||
struct file *f;
|
||||
|
||||
for (int i = 0; i < bd->total_images; i++) {
|
||||
img = &bd->images[i];
|
||||
|
||||
d = malloc(sizeof(struct dentry));
|
||||
v = malloc(sizeof(struct vnode));
|
||||
f = malloc(sizeof(struct file));
|
||||
|
||||
/* Initialise dentry for image */
|
||||
d->refcnt = 0;
|
||||
d->vnode = v;
|
||||
d->parent = sb->root;
|
||||
strncpy(d->name, img->name, VFS_DENTRY_NAME_MAX);
|
||||
link_init(&d->child);
|
||||
link_init(&d->children);
|
||||
list_insert(&d->child, &sb->root->children);
|
||||
|
||||
/* Initialise vnode for image */
|
||||
v->refcnt = 0;
|
||||
v->id = img->phys_start;
|
||||
v->size = img->phys_end - img->phys_start;
|
||||
link_init(&v->dirents);
|
||||
list_insert(&d->v_ref, &v->dirents);
|
||||
|
||||
/* Initialise file struct for image */
|
||||
f->refcnt = 0;
|
||||
f->dentry = d;
|
||||
|
||||
img_d++;
|
||||
img_vn++;
|
||||
img_f++;
|
||||
}
|
||||
}
|
||||
|
||||
void bootfs_init_root(struct dentry *r)
|
||||
{
|
||||
struct vnode *v = r->vnode;
|
||||
|
||||
/* Initialise dentry for rootdir */
|
||||
r->refcnt = 0;
|
||||
strcpy(r->name, "");
|
||||
link_init(&r->child);
|
||||
link_init(&r->children);
|
||||
link_init(&r->vref);
|
||||
r->parent = r;
|
||||
|
||||
/* Initialise vnode for rootdir */
|
||||
v->id = 0;
|
||||
v->refcnt = 0;
|
||||
link_init(&v->dirents);
|
||||
link_init(&v->state_list);
|
||||
list_insert(&r->vref, &v->dirents);
|
||||
v->size = 0;
|
||||
}
|
||||
|
||||
struct superblock *bootfs_init_sb(struct superblock *sb)
|
||||
{
|
||||
sb->root = malloc(sizeof(struct dentry));
|
||||
sb->root->vnode = malloc(sizeof(struct vnode));
|
||||
|
||||
bootfs_init_root(&sb->root);
|
||||
|
||||
return sb;
|
||||
}
|
||||
|
||||
struct superblock bootfs_sb = {
|
||||
.fs = bootfs_type,
|
||||
.ops = {
|
||||
.read_sb = bootfs_read_sb,
|
||||
.write_sb = bootfs_write_sb,
|
||||
.read_vnode = bootfs_read_vnode,
|
||||
.write_vnode = bootfs_write_vnode,
|
||||
},
|
||||
};
|
||||
|
||||
struct superblock *bootfs_get_sb(void)
|
||||
{
|
||||
bootfs_init_sb(&bootfs_sb);
|
||||
return &bootfs_sb;
|
||||
}
|
||||
|
||||
struct file_system_type bootfs_type = {
|
||||
.name = "bootfs",
|
||||
.magic = 0,
|
||||
.get_sb = bootfs_get_sb,
|
||||
};
|
||||
|
||||
void init_bootfs()
|
||||
{
|
||||
bootfs_init_sb(&bootfs_sb);
|
||||
|
||||
bootfs_populate(&bootfs_sb);
|
||||
}
|
||||
29
conts/posix/fs0/src/c0fs/c0fs.c
Normal file
29
conts/posix/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 link *fslist)
|
||||
{
|
||||
list_insert(&sfs_type.list, fslist);
|
||||
}
|
||||
113
conts/posix/fs0/src/c0fs/c0fs.h
Normal file
113
conts/posix/fs0/src/c0fs/c0fs.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* The disk layout of our simple unix-like filesystem.
|
||||
*
|
||||
* Copyright (C) 2007, 2008 Bahadir Balban
|
||||
*/
|
||||
|
||||
#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)
|
||||
|
||||
/*
|
||||
*
|
||||
* Filesystem layout:
|
||||
*
|
||||
* |---------------|
|
||||
* | Group 0 |
|
||||
* |---------------|
|
||||
* | Group 1 |
|
||||
* |---------------|
|
||||
* | ... |
|
||||
* |---------------|
|
||||
* | Group n |
|
||||
* |---------------|
|
||||
*
|
||||
*
|
||||
* Group layout:
|
||||
*
|
||||
* |---------------|
|
||||
* | Superblock |
|
||||
* |---------------|
|
||||
* | Inode table |
|
||||
* |---------------|
|
||||
* | Data blocks |
|
||||
* |---------------|
|
||||
*
|
||||
* or
|
||||
*
|
||||
* |---------------|
|
||||
* | Data blocks |
|
||||
* |---------------|
|
||||
*
|
||||
*/
|
||||
|
||||
#define BLOCK_SIZE PAGE_SIZE
|
||||
#define BLOCK_BITS PAGE_BITS
|
||||
#define GROUP_SIZE SZ_8MB
|
||||
#define INODE_TABLE_SIZE ((GROUP_SIZE / BLOCK_SIZE) / 2)
|
||||
#define INODE_BITMAP_SIZE (INODE_TABLE_SIZE >> 5)
|
||||
|
||||
|
||||
struct sfs_superblock {
|
||||
u32 magic; /* Filesystem magic number */
|
||||
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 total;
|
||||
u32 free;
|
||||
u32 inodemap[INODE_BITMAP_SIZE];
|
||||
struct sfs_inode inode[INODE_TABLE_SIZE];
|
||||
};
|
||||
|
||||
/*
|
||||
* The purpose of an inode:
|
||||
*
|
||||
* 1) Uniquely identify a file or a directory.
|
||||
* 2) Keep file/directory metadata.
|
||||
* 3) Provide access means to file blocks/directory contents.
|
||||
*/
|
||||
#define INODE_DIRECT_BLOCKS 5
|
||||
struct sfs_inode_blocks {
|
||||
int szidx; /* Direct array index size */
|
||||
unsigned long indirect;
|
||||
unsigned long indirect2;
|
||||
unsigned long indirect3;
|
||||
unsigned long direct[];
|
||||
};
|
||||
|
||||
struct sfs_inode {
|
||||
u32 unum; /* Unit number this inode is in */
|
||||
u32 inum; /* Inode number */
|
||||
u32 mode; /* File permissions */
|
||||
u32 owner; /* File owner */
|
||||
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__));
|
||||
|
||||
struct sfs_dentry {
|
||||
u32 inum; /* Inode number */
|
||||
u32 nlength; /* Name length */
|
||||
u8 name[]; /* Name string */
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
|
||||
void sfs_register_type(struct link *);
|
||||
|
||||
#endif /* __C0FS_LAYOUT_H__ */
|
||||
94
conts/posix/fs0/src/crt0.S
Normal file
94
conts/posix/fs0/src/crt0.S
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Australian Public Licence B (OZPLB)
|
||||
*
|
||||
* Version 1-0
|
||||
*
|
||||
* Copyright (c) 2004 National ICT Australia
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by: Embedded, Real-time and Operating Systems Program (ERTOS)
|
||||
* National ICT Australia
|
||||
* http://www.ertos.nicta.com.au
|
||||
*
|
||||
* Permission is granted by National ICT Australia, free of charge, to
|
||||
* any person obtaining a copy of this software and any associated
|
||||
* documentation files (the "Software") to deal with the Software without
|
||||
* restriction, including (without limitation) the rights to use, copy,
|
||||
* modify, adapt, merge, publish, distribute, communicate to the public,
|
||||
* sublicense, and/or sell, lend or rent out copies of the Software, and
|
||||
* to permit persons to whom the Software is furnished to do so, subject
|
||||
* to the following conditions:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimers.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimers in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
*
|
||||
* * Neither the name of National ICT Australia, nor the names of its
|
||||
* contributors, may be used to endorse or promote products derived
|
||||
* from this Software without specific prior written permission.
|
||||
*
|
||||
* EXCEPT AS EXPRESSLY STATED IN THIS LICENCE AND TO THE FULL EXTENT
|
||||
* PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS-IS", AND
|
||||
* NATIONAL ICT AUSTRALIA AND ITS CONTRIBUTORS MAKE NO REPRESENTATIONS,
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO ANY REPRESENTATIONS, WARRANTIES OR CONDITIONS
|
||||
* REGARDING THE CONTENTS OR ACCURACY OF THE SOFTWARE, OR OF TITLE,
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT,
|
||||
* THE ABSENCE OF LATENT OR OTHER DEFECTS, OR THE PRESENCE OR ABSENCE OF
|
||||
* ERRORS, WHETHER OR NOT DISCOVERABLE.
|
||||
*
|
||||
* TO THE FULL EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL
|
||||
* NATIONAL ICT AUSTRALIA OR ITS CONTRIBUTORS BE LIABLE ON ANY LEGAL
|
||||
* THEORY (INCLUDING, WITHOUT LIMITATION, IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHERWISE) FOR ANY CLAIM, LOSS, DAMAGES OR OTHER
|
||||
* LIABILITY, INCLUDING (WITHOUT LIMITATION) LOSS OF PRODUCTION OR
|
||||
* OPERATION TIME, LOSS, DAMAGE OR CORRUPTION OF DATA OR RECORDS; OR LOSS
|
||||
* OF ANTICIPATED SAVINGS, OPPORTUNITY, REVENUE, PROFIT OR GOODWILL, OR
|
||||
* OTHER ECONOMIC LOSS; OR ANY SPECIAL, INCIDENTAL, INDIRECT,
|
||||
* CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THIS LICENCE, THE SOFTWARE OR THE USE OF OR OTHER
|
||||
* DEALINGS WITH THE SOFTWARE, EVEN IF NATIONAL ICT AUSTRALIA OR ITS
|
||||
* CONTRIBUTORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH CLAIM, LOSS,
|
||||
* DAMAGES OR OTHER LIABILITY.
|
||||
*
|
||||
* If applicable legislation implies representations, warranties, or
|
||||
* conditions, or imposes obligations or liability on National ICT
|
||||
* Australia or one of its contributors in respect of the Software that
|
||||
* cannot be wholly or partly excluded, restricted or modified, the
|
||||
* liability of National ICT Australia or the contributor is limited, to
|
||||
* the full extent permitted by the applicable legislation, at its
|
||||
* option, to:
|
||||
* a. in the case of goods, any one or more of the following:
|
||||
* i. the replacement of the goods or the supply of equivalent goods;
|
||||
* ii. the repair of the goods;
|
||||
* iii. the payment of the cost of replacing the goods or of acquiring
|
||||
* equivalent goods;
|
||||
* iv. the payment of the cost of having the goods repaired; or
|
||||
* b. in the case of services:
|
||||
* i. the supplying of the services again; or
|
||||
* ii. the payment of the cost of having the services supplied again.
|
||||
*
|
||||
* The construction, validity and performance of this licence is governed
|
||||
* by the laws in force in New South Wales, Australia.
|
||||
*/
|
||||
|
||||
#ifdef __thumb__
|
||||
#define bl blx
|
||||
#endif
|
||||
|
||||
.section .text.head
|
||||
.code 32
|
||||
.global _start;
|
||||
.align;
|
||||
_start:
|
||||
ldr sp, =__stack
|
||||
bl platform_init
|
||||
bl __container_init
|
||||
1:
|
||||
b 1b
|
||||
|
||||
25
conts/posix/fs0/src/file.c
Normal file
25
conts/posix/fs0/src/file.c
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
91
conts/posix/fs0/src/init.c
Normal file
91
conts/posix/fs0/src/init.c
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* FS0 Initialisation.
|
||||
*
|
||||
* Copyright (C) 2007 Bahadir Balban
|
||||
*/
|
||||
#include <fs.h>
|
||||
#include <vfs.h>
|
||||
#include <bdev.h>
|
||||
#include <task.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <l4/lib/list.h>
|
||||
#include <l4/api/errno.h>
|
||||
#include <memfs/memfs.h>
|
||||
|
||||
struct link fs_type_list;
|
||||
|
||||
struct superblock *vfs_probe_filesystems(void *block)
|
||||
{
|
||||
struct file_system_type *fstype;
|
||||
struct superblock *sb;
|
||||
|
||||
list_foreach_struct(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_insert(&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 */
|
||||
link_init(&fs_type_list);
|
||||
|
||||
/* Call per-fs registration functions */
|
||||
memfs_register_fstype(&fs_type_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);
|
||||
|
||||
/* 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);
|
||||
|
||||
printf("%s: Mounted memfs root filesystem.\n", __TASKNAME__);
|
||||
|
||||
/* Learn about what tasks are running */
|
||||
init_task_data();
|
||||
|
||||
/*
|
||||
* Initialisation is done. From here on, we can start
|
||||
* serving filesystem requests.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
111
conts/posix/fs0/src/lib/bit.c
Normal file
111
conts/posix/fs0/src/lib/bit.c
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Bit manipulation functions.
|
||||
*
|
||||
* Copyright (C) 2007 Bahadir Balban
|
||||
*/
|
||||
#include <lib/bit.h>
|
||||
#include <l4/macros.h>
|
||||
#include <l4/config.h>
|
||||
#include <stdio.h>
|
||||
#include INC_GLUE(memory.h)
|
||||
|
||||
/* Emulation of ARM's CLZ (count leading zeroes) instruction */
|
||||
unsigned int __clz(unsigned int bitvector)
|
||||
{
|
||||
unsigned int x = 0;
|
||||
while((!(bitvector & ((unsigned)1 << 31))) && (x < 32)) {
|
||||
bitvector <<= 1;
|
||||
x++;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
int find_and_set_first_free_bit(u32 *word, unsigned int limit)
|
||||
{
|
||||
int success = 0;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < limit; i++) {
|
||||
/* Find first unset bit */
|
||||
if (!(word[BITWISE_GETWORD(i)] & BITWISE_GETBIT(i))) {
|
||||
/* Set it */
|
||||
word[BITWISE_GETWORD(i)] |= BITWISE_GETBIT(i);
|
||||
success = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Return bit just set */
|
||||
if (success)
|
||||
return i;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int find_and_set_first_free_contig_bits(u32 *word, unsigned int limit,
|
||||
int nbits)
|
||||
{
|
||||
int i = 0, first = 0, last = 0, found = 0;
|
||||
|
||||
/* Can't allocate more than the limit */
|
||||
if (nbits > limit)
|
||||
return -1;
|
||||
|
||||
/* This is a state machine that checks n contiguous free bits. */
|
||||
while (i + nbits < limit) {
|
||||
first = i;
|
||||
last = i;
|
||||
while (!(word[BITWISE_GETWORD(last)] & BITWISE_GETBIT(last))) {
|
||||
last++;
|
||||
i++;
|
||||
if (last == first + nbits) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* If found, set the bits */
|
||||
if (found) {
|
||||
for (int x = first; x < first + nbits; x++)
|
||||
word[BITWISE_GETWORD(x)] |= BITWISE_GETBIT(x);
|
||||
return first;
|
||||
} else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int check_and_clear_bit(u32 *word, int bit)
|
||||
{
|
||||
/* Check that bit was set */
|
||||
if (word[BITWISE_GETWORD(bit)] & BITWISE_GETBIT(bit)) {
|
||||
word[BITWISE_GETWORD(bit)] &= ~BITWISE_GETBIT(bit);
|
||||
return 0;
|
||||
} else {
|
||||
printf("Trying to clear already clear bit\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int check_and_clear_contig_bits(u32 *word, int first, int nbits)
|
||||
{
|
||||
for (int i = first; i < first + nbits; i++)
|
||||
if (check_and_clear_bit(word, i) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_and_set_bit(u32 *word, int bit)
|
||||
{
|
||||
/* Check that bit was clear */
|
||||
if (!(word[BITWISE_GETWORD(bit)] & BITWISE_GETBIT(bit))) {
|
||||
word[BITWISE_GETWORD(bit)] |= BITWISE_GETBIT(bit);
|
||||
return 0;
|
||||
} else {
|
||||
//printf("Trying to set already set bit\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
81
conts/posix/fs0/src/lib/idpool.c
Normal file
81
conts/posix/fs0/src/lib/idpool.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Used for thread and space ids.
|
||||
*
|
||||
* Copyright (C) 2007 Bahadir Balban
|
||||
*/
|
||||
#include <lib/idpool.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>
|
||||
#include <l4/api/errno.h>
|
||||
|
||||
struct id_pool *id_pool_new_init(int totalbits)
|
||||
{
|
||||
int nwords = BITWISE_GETWORD(totalbits);
|
||||
struct id_pool *new = kzalloc((nwords * SZ_WORD)
|
||||
+ sizeof(struct id_pool));
|
||||
if (!new)
|
||||
return PTR_ERR(-ENOMEM);
|
||||
new->nwords = nwords;
|
||||
return new;
|
||||
}
|
||||
|
||||
int id_new(struct id_pool *pool)
|
||||
{
|
||||
int id = find_and_set_first_free_bit(pool->bitmap,
|
||||
pool->nwords * WORD_BITS);
|
||||
if (id < 0)
|
||||
printf("%s: Warning! New id alloc failed\n", __FUNCTION__);
|
||||
return id;
|
||||
}
|
||||
|
||||
/* This finds n contiguous free ids, allocates and returns the first one */
|
||||
int ids_new_contiguous(struct id_pool *pool, int numids)
|
||||
{
|
||||
int id = find_and_set_first_free_contig_bits(pool->bitmap,
|
||||
pool->nwords *WORD_BITS,
|
||||
numids);
|
||||
if (id < 0)
|
||||
printf("%s: Warning! New id alloc failed\n", __FUNCTION__);
|
||||
return id;
|
||||
}
|
||||
|
||||
/* This deletes a list of contiguous ids given the first one and number of ids */
|
||||
int ids_del_contiguous(struct id_pool *pool, int first, int numids)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (pool->nwords * WORD_BITS < first + numids)
|
||||
return -1;
|
||||
if ((ret = check_and_clear_contig_bits(pool->bitmap, first, numids)))
|
||||
printf("%s: Error: Invalid argument range.\n", __FUNCTION__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int id_del(struct id_pool *pool, int id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (pool->nwords * WORD_BITS < id)
|
||||
return -1;
|
||||
|
||||
if ((ret = check_and_clear_bit(pool->bitmap, id) < 0))
|
||||
printf("%s: Error: Could not delete id.\n", __FUNCTION__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Return a specific id, if available */
|
||||
int id_get(struct id_pool *pool, int id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = check_and_set_bit(pool->bitmap, id);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else
|
||||
return id;
|
||||
}
|
||||
|
||||
417
conts/posix/fs0/src/lib/malloc.c
Normal file
417
conts/posix/fs0/src/lib/malloc.c
Normal file
@@ -0,0 +1,417 @@
|
||||
/*****************************************************************************
|
||||
Simple malloc
|
||||
Chris Giese <geezer@execpc.com> http://www.execpc.com/~geezer
|
||||
Release date: Oct 30, 2002
|
||||
This code is public domain (no copyright).
|
||||
You can do whatever you want with it.
|
||||
|
||||
Features:
|
||||
- First-fit
|
||||
- free() coalesces adjacent free blocks
|
||||
- Uses variable-sized heap, enlarged with kbrk()/sbrk() function
|
||||
- Does not use mmap()
|
||||
- Can be easily modified to use fixed-size heap
|
||||
- Works with 16- or 32-bit compilers
|
||||
|
||||
Build this program with either of the two main() functions, then run it.
|
||||
Messages that indicate a software error will contain three asterisks (***).
|
||||
*****************************************************************************/
|
||||
#include <string.h> /* memcpy(), memset() */
|
||||
#include <stdio.h> /* printf() */
|
||||
|
||||
#define _32BIT 1
|
||||
|
||||
/* use small (32K) heap for 16-bit compilers,
|
||||
large (500K) heap for 32-bit compilers */
|
||||
#if defined(_32BIT)
|
||||
#define HEAP_SIZE 500000uL
|
||||
#else
|
||||
#define HEAP_SIZE 32768u
|
||||
#endif
|
||||
|
||||
#define MALLOC_MAGIC 0x6D92 /* must be < 0x8000 */
|
||||
|
||||
typedef struct _malloc /* Turbo C DJGPP */
|
||||
{
|
||||
size_t size; /* 2 bytes 4 bytes */
|
||||
struct _malloc *next; /* 2 bytes 4 bytes */
|
||||
unsigned magic : 15; /* 2 bytes total 4 bytes total */
|
||||
unsigned used : 1;
|
||||
} malloc_t; /* total 6 bytes 12 bytes */
|
||||
|
||||
static char *g_heap_bot, *g_kbrk, *g_heap_top;
|
||||
/*****************************************************************************
|
||||
*****************************************************************************/
|
||||
void dump_heap(void)
|
||||
{
|
||||
unsigned blks_used = 0, blks_free = 0;
|
||||
size_t bytes_used = 0, bytes_free = 0;
|
||||
malloc_t *m;
|
||||
int total;
|
||||
|
||||
printf("===============================================\n");
|
||||
for(m = (malloc_t *)g_heap_bot; m != NULL; m = m->next)
|
||||
{
|
||||
printf("blk %5p: %6u bytes %s\n", m,
|
||||
m->size, m->used ? "used" : "free");
|
||||
if(m->used)
|
||||
{
|
||||
blks_used++;
|
||||
bytes_used += m->size;
|
||||
}
|
||||
else
|
||||
{
|
||||
blks_free++;
|
||||
bytes_free += m->size;
|
||||
}
|
||||
}
|
||||
printf("blks: %6u used, %6u free, %6u total\n", blks_used,
|
||||
blks_free, blks_used + blks_free);
|
||||
printf("bytes: %6u used, %6u free, %6u total\n", bytes_used,
|
||||
bytes_free, bytes_used + bytes_free);
|
||||
printf("g_heap_bot=0x%p, g_kbrk=0x%p, g_heap_top=0x%p\n",
|
||||
g_heap_bot, g_kbrk, g_heap_top);
|
||||
total = (bytes_used + bytes_free) +
|
||||
(blks_used + blks_free) * sizeof(malloc_t);
|
||||
if(total != g_kbrk - g_heap_bot)
|
||||
printf("*** some heap memory is not accounted for\n");
|
||||
printf("===============================================\n");
|
||||
}
|
||||
/*****************************************************************************
|
||||
POSIX sbrk() looks like this
|
||||
void *sbrk(int incr);
|
||||
Mine is a bit different so I can signal the calling function
|
||||
if more memory than desired was allocated (e.g. in a system with paging)
|
||||
If your kbrk()/sbrk() always allocates the amount of memory you ask for,
|
||||
this code can be easily changed.
|
||||
|
||||
int brk( void *sbrk( void *kbrk(
|
||||
function void *adr); int delta); int *delta);
|
||||
---------------------- ------------ ------------ -------------
|
||||
POSIX? yes yes NO
|
||||
return value if error -1 -1 NULL
|
||||
get break value . sbrk(0) int x=0; kbrk(&x);
|
||||
set break value to X brk(X) sbrk(X - sbrk(0)) int x=X, y=0; kbrk(&x) - kbrk(&y);
|
||||
enlarge heap by N bytes . sbrk(+N) int x=N; kbrk(&x);
|
||||
shrink heap by N bytes . sbrk(-N) int x=-N; kbrk(&x);
|
||||
can you tell if you're
|
||||
given more memory
|
||||
than you wanted? no no yes
|
||||
*****************************************************************************/
|
||||
static void *kbrk(int *delta)
|
||||
{
|
||||
static char heap[HEAP_SIZE];
|
||||
/**/
|
||||
char *new_brk, *old_brk;
|
||||
|
||||
/* heap doesn't exist yet */
|
||||
if(g_heap_bot == NULL)
|
||||
{
|
||||
g_heap_bot = g_kbrk = heap;
|
||||
g_heap_top = g_heap_bot + HEAP_SIZE;
|
||||
}
|
||||
new_brk = g_kbrk + (*delta);
|
||||
/* too low: return NULL */
|
||||
if(new_brk < g_heap_bot)
|
||||
return NULL;
|
||||
/* too high: return NULL */
|
||||
if(new_brk >= g_heap_top)
|
||||
return NULL;
|
||||
/* success: adjust brk value... */
|
||||
old_brk = g_kbrk;
|
||||
g_kbrk = new_brk;
|
||||
/* ...return actual delta... (for this sbrk(), they are the same)
|
||||
(*delta) = (*delta); */
|
||||
/* ...return old brk value */
|
||||
return old_brk;
|
||||
}
|
||||
/*****************************************************************************
|
||||
kmalloc() and kfree() use g_heap_bot, but not g_kbrk nor g_heap_top
|
||||
*****************************************************************************/
|
||||
void *kmalloc(size_t size)
|
||||
{
|
||||
unsigned total_size;
|
||||
malloc_t *m, *n;
|
||||
int delta;
|
||||
|
||||
if(size == 0)
|
||||
return NULL;
|
||||
total_size = size + sizeof(malloc_t);
|
||||
/* search heap for free block (FIRST FIT) */
|
||||
m = (malloc_t *)g_heap_bot;
|
||||
/* g_heap_bot == 0 == NULL if heap does not yet exist */
|
||||
if(m != NULL)
|
||||
{
|
||||
if(m->magic != MALLOC_MAGIC)
|
||||
// panic("kernel heap is corrupt in kmalloc()");
|
||||
{
|
||||
printf("*** kernel heap is corrupt in kmalloc()\n");
|
||||
return NULL;
|
||||
}
|
||||
for(; m->next != NULL; m = m->next)
|
||||
{
|
||||
if(m->used)
|
||||
continue;
|
||||
/* size == m->size is a perfect fit */
|
||||
if(size == m->size)
|
||||
m->used = 1;
|
||||
else
|
||||
{
|
||||
/* otherwise, we need an extra sizeof(malloc_t) bytes for the header
|
||||
of a second, free block */
|
||||
if(total_size > m->size)
|
||||
continue;
|
||||
/* create a new, smaller free block after this one */
|
||||
n = (malloc_t *)((char *)m + total_size);
|
||||
n->size = m->size - total_size;
|
||||
n->next = m->next;
|
||||
n->magic = MALLOC_MAGIC;
|
||||
n->used = 0;
|
||||
/* reduce the size of this block and mark it used */
|
||||
m->size = size;
|
||||
m->next = n;
|
||||
m->used = 1;
|
||||
}
|
||||
return (char *)m + sizeof(malloc_t);
|
||||
}
|
||||
}
|
||||
/* use kbrk() to enlarge (or create!) heap */
|
||||
delta = total_size;
|
||||
n = kbrk(&delta);
|
||||
/* uh-oh */
|
||||
if(n == NULL)
|
||||
return NULL;
|
||||
if(m != NULL)
|
||||
m->next = n;
|
||||
n->size = size;
|
||||
n->magic = MALLOC_MAGIC;
|
||||
n->used = 1;
|
||||
/* did kbrk() return the exact amount of memory we wanted?
|
||||
cast to make "gcc -Wall -W ..." shut the hell up */
|
||||
if((int)total_size == delta)
|
||||
n->next = NULL;
|
||||
else
|
||||
{
|
||||
/* it returned more than we wanted (it will never return less):
|
||||
create a new, free block */
|
||||
m = (malloc_t *)((char *)n + total_size);
|
||||
m->size = delta - total_size - sizeof(malloc_t);
|
||||
m->next = NULL;
|
||||
m->magic = MALLOC_MAGIC;
|
||||
m->used = 0;
|
||||
|
||||
n->next = m;
|
||||
}
|
||||
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)
|
||||
{
|
||||
malloc_t *m, *n;
|
||||
|
||||
/* get address of header */
|
||||
m = (malloc_t *)((char *)blk - sizeof(malloc_t));
|
||||
if(m->magic != MALLOC_MAGIC)
|
||||
// panic("attempt to kfree() block at 0x%p "
|
||||
// "with bad magic value", blk);
|
||||
{
|
||||
printf("*** attempt to kfree() block at 0x%p "
|
||||
"with bad magic value\n", blk);
|
||||
return;
|
||||
}
|
||||
/* find this block in the heap */
|
||||
n = (malloc_t *)g_heap_bot;
|
||||
if(n->magic != MALLOC_MAGIC)
|
||||
// panic("kernel heap is corrupt in kfree()");
|
||||
{
|
||||
printf("*** kernel heap is corrupt in kfree()\n");
|
||||
return;
|
||||
}
|
||||
for(; n != NULL; n = n->next)
|
||||
{
|
||||
if(n == m)
|
||||
break;
|
||||
}
|
||||
/* not found? bad pointer or no heap or something else? */
|
||||
if(n == NULL)
|
||||
// panic("attempt to kfree() block at 0x%p "
|
||||
// "that is not in the heap", blk);
|
||||
{
|
||||
printf("*** attempt to kfree() block at 0x%p "
|
||||
"that is not in the heap\n", blk);
|
||||
return;
|
||||
}
|
||||
/* free the block */
|
||||
m->used = 0;
|
||||
/* coalesce adjacent free blocks
|
||||
Hard to spell, hard to do */
|
||||
for(m = (malloc_t *)g_heap_bot; m != NULL; m = m->next)
|
||||
{
|
||||
while(!m->used && m->next != NULL && !m->next->used)
|
||||
{
|
||||
/* resize this block */
|
||||
m->size += sizeof(malloc_t) + m->next->size;
|
||||
/* merge with next block */
|
||||
m->next = m->next->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*****************************************************************************
|
||||
*****************************************************************************/
|
||||
void *krealloc(void *blk, size_t size)
|
||||
{
|
||||
void *new_blk;
|
||||
malloc_t *m;
|
||||
|
||||
/* size == 0: free block */
|
||||
if(size == 0)
|
||||
{
|
||||
if(blk != NULL)
|
||||
kfree(blk);
|
||||
new_blk = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* allocate new block */
|
||||
new_blk = kmalloc(size);
|
||||
/* if allocation OK, and if old block exists, copy old block to new */
|
||||
if(new_blk != NULL && blk != NULL)
|
||||
{
|
||||
m = (malloc_t *)((char *)blk - sizeof(malloc_t));
|
||||
if(m->magic != MALLOC_MAGIC)
|
||||
// panic("attempt to krealloc() block at "
|
||||
// "0x%p with bad magic value", blk);
|
||||
{
|
||||
printf("*** attempt to krealloc() block at "
|
||||
"0x%p with bad magic value\n", blk);
|
||||
return NULL;
|
||||
}
|
||||
/* copy minimum of old and new block sizes */
|
||||
if(size > m->size)
|
||||
size = m->size;
|
||||
memcpy(new_blk, blk, size);
|
||||
/* free the old block */
|
||||
kfree(blk);
|
||||
}
|
||||
}
|
||||
return new_blk;
|
||||
}
|
||||
/*****************************************************************************
|
||||
*****************************************************************************/
|
||||
|
||||
#if 0
|
||||
|
||||
#include <stdlib.h> /* rand() */
|
||||
|
||||
|
||||
#define SLOTS 17
|
||||
|
||||
int main(void)
|
||||
{
|
||||
unsigned lifetime[SLOTS];
|
||||
void *blk[SLOTS];
|
||||
int i, j, k;
|
||||
|
||||
dump_heap();
|
||||
memset(lifetime, 0, sizeof(lifetime));
|
||||
memset(blk, 0, sizeof(blk));
|
||||
for(i = 0; i < 1000; i++)
|
||||
{
|
||||
printf("Pass %6u\n", i);
|
||||
for(j = 0; j < SLOTS; j++)
|
||||
{
|
||||
/* age the block */
|
||||
if(lifetime[j] != 0)
|
||||
{
|
||||
(lifetime[j])--;
|
||||
continue;
|
||||
}
|
||||
/* too old; free it */
|
||||
if(blk[j] != NULL)
|
||||
{
|
||||
kfree(blk[j]);
|
||||
blk[j] = NULL;
|
||||
}
|
||||
/* alloc new block of random size
|
||||
Note that size_t==unsigned, but kmalloc() uses integer math,
|
||||
so block size must be positive integer */
|
||||
#if defined(_32BIT)
|
||||
k = rand() % 40960 + 1;
|
||||
#else
|
||||
k = rand() % 4096 + 1;
|
||||
#endif
|
||||
blk[j] = kmalloc(k);
|
||||
if(blk[j] == NULL)
|
||||
printf("failed to alloc %u bytes\n", k);
|
||||
else
|
||||
/* give it a random lifetime 0-20 */
|
||||
lifetime[j] = rand() % 21;
|
||||
}
|
||||
}
|
||||
/* let's see what we've wrought */
|
||||
printf("\n\n");
|
||||
dump_heap();
|
||||
/* free everything */
|
||||
for(j = 0; j < SLOTS; j++)
|
||||
{
|
||||
if(blk[j] != NULL)
|
||||
{
|
||||
kfree(blk[j]);
|
||||
blk[j] = NULL;
|
||||
}
|
||||
(lifetime[j]) = 0;
|
||||
}
|
||||
/* after all that, we should have a single, unused block */
|
||||
dump_heap();
|
||||
return 0;
|
||||
}
|
||||
/*****************************************************************************
|
||||
*****************************************************************************/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
void *b1, *b2, *b3;
|
||||
|
||||
dump_heap();
|
||||
|
||||
b1 = kmalloc(42);
|
||||
dump_heap();
|
||||
|
||||
b2 = kmalloc(23);
|
||||
dump_heap();
|
||||
|
||||
b3 = kmalloc(7);
|
||||
dump_heap();
|
||||
|
||||
b2 = krealloc(b2, 24);
|
||||
dump_heap();
|
||||
|
||||
kfree(b1);
|
||||
dump_heap();
|
||||
|
||||
b1 = kmalloc(5);
|
||||
dump_heap();
|
||||
|
||||
kfree(b2);
|
||||
dump_heap();
|
||||
|
||||
kfree(b3);
|
||||
dump_heap();
|
||||
|
||||
kfree(b1);
|
||||
dump_heap();
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
92
conts/posix/fs0/src/lib/pathstr.c
Normal file
92
conts/posix/fs0/src/lib/pathstr.c
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Functions to manipulate path strings.
|
||||
*
|
||||
* Copyright (C) 2008 Bahadir Balban
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <alloca.h>
|
||||
|
||||
/* Reverses a string by allocating on stack. Not super-efficient but easy. */
|
||||
char *strreverse(char *str)
|
||||
{
|
||||
int length = strlen(str);
|
||||
char *tmp = alloca(length);
|
||||
|
||||
strcpy(tmp, str);
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
str[i] = tmp[length - 1 - i];
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/* Same as split path, but splits components from the end. Slow. */
|
||||
char *splitpath_end(char **path, char sep)
|
||||
{
|
||||
char *component;
|
||||
|
||||
/* Reverse the string */
|
||||
strreverse(*path);
|
||||
|
||||
/* Pick one from the start */
|
||||
component = splitpath(path, sep);
|
||||
|
||||
/* Reverse the rest back to original. */
|
||||
strreverse(*path);
|
||||
|
||||
/* Reverse component back to original */
|
||||
strreverse(component);
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
/* Splitpath test program. Tests all 3 functions.
|
||||
int main()
|
||||
{
|
||||
char str1[256] = "/a/b/c/d/////e/f";
|
||||
char *str2 = malloc(strlen(str1) + 1);
|
||||
char *comp;
|
||||
|
||||
strcpy(str2, str1);
|
||||
|
||||
comp = splitpath_end(&str2, '/');
|
||||
while (*comp) {
|
||||
printf("%s and %s\n", comp, str2);
|
||||
comp = splitpath_end(&str2, '/');
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
39
conts/posix/fs0/src/lib/vaddr.c
Normal file
39
conts/posix/fs0/src/lib/vaddr.c
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This module allocates an unused virtual address range for shm segments.
|
||||
*
|
||||
* Copyright (C) 2007 Bahadir Balban
|
||||
*/
|
||||
#include <lib/bit.h>
|
||||
#include <l4/macros.h>
|
||||
#include <l4/types.h>
|
||||
#include INC_GLUE(memory.h)
|
||||
#include <lib/vaddr.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void vaddr_pool_init(struct id_pool *pool, unsigned long start, unsigned long end)
|
||||
{
|
||||
pool = id_pool_new_init(__pfn(end - start));
|
||||
}
|
||||
|
||||
void *vaddr_new(struct id_pool *pool, int npages)
|
||||
{
|
||||
unsigned int shm_vpfn;
|
||||
|
||||
if ((int)(shm_vpfn = ids_new_contiguous(pool, npages)) < 0)
|
||||
return 0;
|
||||
|
||||
return (void *)__pfn_to_addr(shm_vpfn + SHM_AREA_START);
|
||||
}
|
||||
|
||||
int vaddr_del(struct id_pool *pool, void *vaddr, int npages)
|
||||
{
|
||||
unsigned long idpfn = __pfn(page_align(vaddr) - SHM_AREA_START);
|
||||
|
||||
if (ids_del_contiguous(pool, idpfn, npages) < 0) {
|
||||
printf("%s: Invalid address range returned to "
|
||||
"virtual address pool.\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
90
conts/posix/fs0/src/lookup.c
Normal file
90
conts/posix/fs0/src/lookup.c
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Inode lookup.
|
||||
*
|
||||
* Copyright (C) 2007, 2008 Bahadir Balban
|
||||
*/
|
||||
#include <fs.h>
|
||||
#include <vfs.h>
|
||||
#include <stat.h>
|
||||
#include <l4/api/errno.h>
|
||||
#include <lib/pathstr.h>
|
||||
#include <path.h>
|
||||
|
||||
/*
|
||||
* 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,
|
||||
struct pathdata *pdata)
|
||||
{
|
||||
struct dentry *childdir;
|
||||
struct vnode *v;
|
||||
const char *component = pathdata_next_component(pdata);
|
||||
|
||||
list_foreach_struct(childdir, &parentdir->children, child)
|
||||
if (IS_ERR(v = childdir->vnode->ops.lookup(childdir->vnode,
|
||||
pdata, component)))
|
||||
/* Means not found, continue search */
|
||||
if ((int)v == -ENOENT)
|
||||
continue;
|
||||
else /* A real error */
|
||||
return v;
|
||||
else /* No error, so found it */
|
||||
return v;
|
||||
|
||||
/* Out of all children dentries, neither was a match */
|
||||
return PTR_ERR(-ENOENT);
|
||||
}
|
||||
|
||||
/* Lookup, recursive, assuming single-mountpoint */
|
||||
struct vnode *generic_vnode_lookup(struct vnode *thisnode,
|
||||
struct pathdata *pdata,
|
||||
const char *component)
|
||||
{
|
||||
struct dentry *d;
|
||||
struct vnode *found;
|
||||
int err;
|
||||
|
||||
/* Does this path component match with any of this vnode's dentries? */
|
||||
list_foreach_struct(d, &thisnode->dentries, vref) {
|
||||
if (d->ops.compare(d, component)) {
|
||||
/* Is this a directory? */
|
||||
if (vfs_isdir(thisnode)) {
|
||||
/* Are there more path components? */
|
||||
if (!list_empty(&pdata->list)) {
|
||||
/* Read directory contents */
|
||||
if ((err = d->vnode->ops.readdir(d->vnode)) < 0)
|
||||
return PTR_ERR(err);
|
||||
|
||||
/* Search all children one level below. */
|
||||
if ((found = lookup_dentry_children(d, pdata)))
|
||||
/* Either found, or non-zero error */
|
||||
return found;
|
||||
} else
|
||||
return thisnode;
|
||||
} else { /* Its a file */
|
||||
if (!list_empty(&pdata->list)) /* 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 PTR_ERR(-ENOENT);
|
||||
}
|
||||
|
||||
int generic_dentry_compare(struct dentry *d, const char *name)
|
||||
{
|
||||
if (!strcmp(d->name, name) || !strcmp(name, VFS_STR_CURDIR))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dentry_ops generic_dentry_operations = {
|
||||
.compare = generic_dentry_compare,
|
||||
};
|
||||
|
||||
142
conts/posix/fs0/src/memfs/file.c
Normal file
142
conts/posix/fs0/src/memfs/file.c
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* TODO: Think about whether to use inode or the vnode's fields (e.g. size)
|
||||
* and when updated, which one is to be updated first. Normally if you use and
|
||||
* update inode, then you sync vnode via read_vnode. but this is not really meant for
|
||||
* this use, its meant for retrieving an unknown inode under the vnode with valid vnum.
|
||||
* here we already have the inode.
|
||||
*/
|
||||
int memfs_file_read_write(struct vnode *v, unsigned int pfn,
|
||||
unsigned int npages, void *buf, int wr)
|
||||
{
|
||||
struct memfs_inode *i;
|
||||
struct memfs_superblock *memfs_sb;
|
||||
unsigned int start, end, count;
|
||||
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) {
|
||||
/* Find read boundaries from expected range and file's current range */
|
||||
start = pfn < __pfn(v->size) ? pfn : __pfn(v->size);
|
||||
end = pfn + npages < __pfn(page_align_up(v->size))
|
||||
? pfn + npages : __pfn(page_align_up(v->size));
|
||||
count = end - start;
|
||||
|
||||
/* Copy the data from inode blocks into page buffer */
|
||||
for (int x = start, bufpage = 0; x < end; x++, bufpage++)
|
||||
memcpy(((void *)buf) + (bufpage * blocksize),
|
||||
i->block[x], blocksize);
|
||||
return (int)(count * blocksize);
|
||||
} else { /* Write-specific operations */
|
||||
/* Is the write beyond current file size? */
|
||||
if (v->size < ((pfn + npages) * (blocksize))) {
|
||||
unsigned long pagediff = pfn + npages - __pfn(v->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(v->size))
|
||||
holes = pfn - __pfn(v->size);
|
||||
else
|
||||
holes = 0;
|
||||
|
||||
/* Allocate new blocks */
|
||||
for (int x = 0; x < pagediff; x++)
|
||||
if (!(i->block[__pfn(v->size) + x] =
|
||||
memfs_alloc_block(v->sb->fs_super)))
|
||||
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(v->size) + x], 0, blocksize);
|
||||
}
|
||||
|
||||
/* 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 (int)(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,
|
||||
};
|
||||
|
||||
215
conts/posix/fs0/src/memfs/memfs.c
Normal file
215
conts/posix/fs0/src/memfs/memfs.c
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* A simple read/writeable memory-only filesystem.
|
||||
*
|
||||
* Copyright (C) 2007, 2008 Bahadir Balban
|
||||
*/
|
||||
#include <init.h>
|
||||
#include <fs.h>
|
||||
#include <vfs.h>
|
||||
#include <task.h>
|
||||
#include <stdio.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_insert(&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_insert(&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 */
|
||||
link_init(&sb->block_cache_list);
|
||||
link_init(&sb->inode_cache_list);
|
||||
memfs_init_caches(sb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allocates a block of unused buffer */
|
||||
void *memfs_alloc_block(struct memfs_superblock *sb)
|
||||
{
|
||||
struct mem_cache *cache;
|
||||
|
||||
list_foreach_struct(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_foreach_removable_struct(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,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialise root inode as a directory, as in the mknod() call
|
||||
* but differently since root is parentless and is the parent of itself.
|
||||
*/
|
||||
int memfs_init_rootdir(struct superblock *sb)
|
||||
{
|
||||
struct memfs_superblock *msb = sb->fs_super;
|
||||
struct dentry *d;
|
||||
struct vnode *v;
|
||||
|
||||
/*
|
||||
* Create the root vnode. Since this is memfs, root vnode is
|
||||
* not read-in but dynamically created here. We expect this
|
||||
* first vnode to have vnum = 0.
|
||||
*/
|
||||
v = sb->root = sb->ops->alloc_vnode(sb);
|
||||
msb->root_vnum = sb->root->vnum;
|
||||
BUG_ON(msb->root_vnum != 0);
|
||||
|
||||
/* Initialise fields */
|
||||
vfs_set_type(v, S_IFDIR);
|
||||
|
||||
/* Allocate a new vfs dentry */
|
||||
if (!(d = vfs_alloc_dentry()))
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Initialise root dentry.
|
||||
*
|
||||
* NOTE: Root's parent is itself.
|
||||
* Here's how it looks like in structures:
|
||||
* root's parent is root. But root's child is not root.
|
||||
*
|
||||
* NOTE: Root has no name. This helps since splitpath
|
||||
* cuts out the '/' and "" is left for root name search.
|
||||
*/
|
||||
strncpy(d->name, VFS_STR_ROOTDIR, VFS_DNAME_MAX);
|
||||
d->ops = generic_dentry_operations;
|
||||
d->parent = d;
|
||||
d->vnode = v;
|
||||
|
||||
/* Associate dentry with its vnode */
|
||||
list_insert(&d->vref, &d->vnode->dentries);
|
||||
|
||||
/* Add both vnode and dentry to their flat caches */
|
||||
list_insert(&d->cache_list, &dentry_cache);
|
||||
list_insert(&v->cache_list, &vnode_cache);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
/* We initialise the root vnode as the root directory */
|
||||
memfs_init_rootdir(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;
|
||||
|
||||
// printf("%s: %s: Reading superblock.\n", __TASKNAME__, __FUNCTION__);
|
||||
/* We don't do sanity checks here, just confirm id. */
|
||||
if (strcmp(sb->name, "memfs")) {
|
||||
printf("%s: Name does not match: %s\n", __FUNCTION__, sb->name);
|
||||
return 0;
|
||||
}
|
||||
if (sb->magic != MEMFS_MAGIC) {
|
||||
printf("%s: Magic number not match: %u\n", __FUNCTION__, sb->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 link *fslist)
|
||||
{
|
||||
/* Initialise superblock list for this fstype */
|
||||
link_init(&memfs_fstype.sblist);
|
||||
|
||||
/* Add this fstype to list of available fstypes. */
|
||||
list_insert(&memfs_fstype.list, fslist);
|
||||
}
|
||||
|
||||
427
conts/posix/fs0/src/memfs/vnode.c
Normal file
427
conts/posix/fs0/src/memfs/vnode.c
Normal file
@@ -0,0 +1,427 @@
|
||||
/*
|
||||
* 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_foreach_struct(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_insert(&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_foreach_removable_struct(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_remove(&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;
|
||||
}
|
||||
|
||||
/* Allocates both an inode and a vnode and associates the two together */
|
||||
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;
|
||||
v->sb = sb;
|
||||
|
||||
/* Return the vnode */
|
||||
return v;
|
||||
}
|
||||
|
||||
/* Frees the inode and the corresponding vnode */
|
||||
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;
|
||||
}
|
||||
|
||||
/* Writes a valid vnode's fields back to its fs-specific inode */
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Creates ordinary files and directories at the moment. In the future,
|
||||
* other file types will be added. Returns the created node.
|
||||
*/
|
||||
struct vnode *memfs_vnode_mknod(struct vnode *v, const char *dirname,
|
||||
unsigned int mode)
|
||||
{
|
||||
struct dentry *d, *parent = link_to_struct(v->dentries.next,
|
||||
struct dentry, vref);
|
||||
struct memfs_dentry *memfsd;
|
||||
struct dentry *newd;
|
||||
struct vnode *newv;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Precautions to prove that parent is the *only* dentry,
|
||||
* since directories can't have multiple dentries associated
|
||||
* with them.
|
||||
*/
|
||||
BUG_ON(list_empty(&v->dentries));
|
||||
BUG_ON(parent->vref.next != &v->dentries);
|
||||
BUG_ON(!vfs_isdir(v));
|
||||
|
||||
/* Populate the children */
|
||||
if ((err = v->ops.readdir(v)) < 0)
|
||||
return PTR_ERR(err);
|
||||
|
||||
/* Check there's no existing child with same name */
|
||||
list_foreach_struct(d, &parent->children, child) {
|
||||
/* Does the name exist as a child? */
|
||||
if(d->ops.compare(d, dirname))
|
||||
return PTR_ERR(-EEXIST);
|
||||
}
|
||||
|
||||
/* Allocate a new vnode for the new directory */
|
||||
if (IS_ERR(newv = v->sb->ops->alloc_vnode(v->sb)))
|
||||
return newv;
|
||||
|
||||
/* Initialise the vnode */
|
||||
vfs_set_type(newv, mode);
|
||||
|
||||
/* Get the next directory entry available on the parent vnode */
|
||||
if (v->dirbuf.npages * PAGE_SIZE <= v->size)
|
||||
return PTR_ERR(-ENOSPC);
|
||||
|
||||
/* Fill in the new entry to parent directory entry */
|
||||
memfsd = (struct memfs_dentry *)&v->dirbuf.buffer[v->size];
|
||||
memfsd->offset = v->size;
|
||||
memfsd->rlength = sizeof(*memfsd);
|
||||
memfsd->inum = ((struct memfs_inode *)newv->inode)->inum;
|
||||
strncpy((char *)memfsd->name, dirname, MEMFS_DNAME_MAX);
|
||||
memfsd->name[MEMFS_DNAME_MAX - 1] = '\0';
|
||||
|
||||
/* Write the updated directory buffer back to disk block */
|
||||
if ((err = v->fops.write(v, 0, 1, v->dirbuf.buffer)) < 0)
|
||||
return PTR_ERR(err); /* FIXME: free all you allocated so far */
|
||||
|
||||
/* Update parent vnode size */
|
||||
v->size += sizeof(*memfsd);
|
||||
v->sb->ops->write_vnode(v->sb, v);
|
||||
|
||||
/* Allocate a new vfs dentry */
|
||||
if (!(newd = vfs_alloc_dentry()))
|
||||
return PTR_ERR(-ENOMEM);
|
||||
|
||||
/* Initialise it */
|
||||
newd->ops = generic_dentry_operations;
|
||||
newd->parent = parent;
|
||||
newd->vnode = newv;
|
||||
strncpy(newd->name, dirname, VFS_DNAME_MAX);
|
||||
|
||||
/* Associate dentry with its vnode */
|
||||
list_insert(&newd->vref, &newd->vnode->dentries);
|
||||
|
||||
/* Associate dentry with its parent */
|
||||
list_insert(&newd->child, &parent->children);
|
||||
|
||||
/* Add both vnode and dentry to their flat caches */
|
||||
list_insert(&newd->cache_list, &dentry_cache);
|
||||
list_insert(&newv->cache_list, &vnode_cache);
|
||||
|
||||
return newv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads the vnode directory contents into vnode's buffer in a posix-compliant
|
||||
* struct dirent format.
|
||||
*
|
||||
* Reading the buffer, 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.
|
||||
*/
|
||||
int memfs_vnode_readdir(struct vnode *v)
|
||||
{
|
||||
int err;
|
||||
struct memfs_dentry *memfsd;
|
||||
struct dentry *parent = link_to_struct(v->dentries.next,
|
||||
struct dentry, vref);
|
||||
|
||||
/*
|
||||
* Precautions to prove that parent is the *only* dentry,
|
||||
* since directories can't have multiple dentries associated
|
||||
* with them.
|
||||
*/
|
||||
BUG_ON(parent->vref.next != &v->dentries);
|
||||
BUG_ON(!vfs_isdir(v));
|
||||
|
||||
/* If a buffer is there, it means the directory is already read */
|
||||
if (v->dirbuf.buffer)
|
||||
return 0;
|
||||
|
||||
/* This is as big as a page */
|
||||
if (IS_ERR(v->dirbuf.buffer = vfs_alloc_dirpage(v))) {
|
||||
printf("%s: Could not allocate dirbuf.\n", __FUNCTION__);
|
||||
return (int)v->dirbuf.buffer;
|
||||
}
|
||||
v->dirbuf.npages = 1;
|
||||
|
||||
/*
|
||||
* Fail if vnode size is bigger than a page. Since this allocation
|
||||
* method is to be origaced, we can live with this limitation for now.
|
||||
*/
|
||||
BUG_ON(v->size > PAGE_SIZE);
|
||||
|
||||
/* Read memfsd contents into the buffer */
|
||||
if ((err = v->fops.read(v, 0, 1, v->dirbuf.buffer)))
|
||||
return err;
|
||||
|
||||
memfsd = (struct memfs_dentry *)v->dirbuf.buffer;
|
||||
|
||||
/* Read fs-specific directory entry into vnode and dentry caches. */
|
||||
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 -ENOMEM;
|
||||
|
||||
/* Initialise it */
|
||||
newd->ops = generic_dentry_operations;
|
||||
newd->parent = parent;
|
||||
list_insert(&newd->child, &parent->children);
|
||||
|
||||
/*
|
||||
* Lookup the vnode for dentry by its vnode number. We call
|
||||
* vnode_lookup_byvnum instead of directly reading it because
|
||||
* this dentry might just be a link to a vnode that's already
|
||||
* in the vnode cache. If it's not there, the lookup function
|
||||
* allocates and reads it for us as well.
|
||||
*/
|
||||
newv = newd->vnode = vfs_lookup_byvnum(v->sb, memfsd[i].inum);
|
||||
if (!newv) {
|
||||
printf("Filesystem seems to be broken. Directory has"
|
||||
"inode number: %d, but no such inode found.\n",
|
||||
memfsd[i].inum);
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* Assing this dentry as a name of its vnode */
|
||||
list_insert(&newd->vref, &newd->vnode->dentries);
|
||||
|
||||
/* Increase link count */
|
||||
newv->links++;
|
||||
|
||||
/* Copy fields into generic dentry */
|
||||
memcpy(newd->name, memfsd[i].name, MEMFS_DNAME_MAX);
|
||||
|
||||
/* Add both vnode and dentry to their caches */
|
||||
list_insert(&newd->cache_list, &dentry_cache);
|
||||
list_insert(&newv->cache_list, &vnode_cache);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copies fs-specific dirent data into user buffer in
|
||||
* generic struct dirent format.
|
||||
*/
|
||||
int memfs_vnode_filldir(void *userbuf, struct vnode *v, int count)
|
||||
{
|
||||
int nbytes;
|
||||
int err;
|
||||
|
||||
/* Bytes to read, minimum of vnode size and count requested */
|
||||
nbytes = (v->size <= count) ? v->size : count;
|
||||
|
||||
/* Read the dir content from fs, if haven't done so yet */
|
||||
if ((err = v->ops.readdir(v)) < 0)
|
||||
return err;
|
||||
|
||||
/* Do we have those bytes at hand? */
|
||||
if (v->dirbuf.buffer && (v->dirbuf.npages * PAGE_SIZE) >= nbytes) {
|
||||
/*
|
||||
* Memfs does a direct copy since memfs dirent format
|
||||
* is the same as generic dirent format.
|
||||
*/
|
||||
memcpy(userbuf, v->dirbuf.buffer, nbytes);
|
||||
return nbytes;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct vnode_ops memfs_vnode_operations = {
|
||||
.readdir = memfs_vnode_readdir,
|
||||
.filldir = memfs_vnode_filldir,
|
||||
.mknod = memfs_vnode_mknod,
|
||||
.lookup = generic_vnode_lookup,
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
145
conts/posix/fs0/src/path.c
Normal file
145
conts/posix/fs0/src/path.c
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Path manipulation functions.
|
||||
*
|
||||
* Copyright (C) 2008 Bahadir Balban
|
||||
*/
|
||||
#include <l4/macros.h>
|
||||
#include <l4/lib/list.h>
|
||||
#include <l4/api/errno.h>
|
||||
#include <lib/pathstr.h>
|
||||
#include <lib/malloc.h>
|
||||
#include <path.h>
|
||||
#include <stdio.h>
|
||||
#include <fs.h>
|
||||
#include <task.h>
|
||||
#include <vfs.h>
|
||||
|
||||
const char *pathdata_next_component(struct pathdata *pdata)
|
||||
{
|
||||
struct pathcomp *p, *n;
|
||||
const char *pathstr;
|
||||
|
||||
list_foreach_removable_struct(p, n, &pdata->list, list) {
|
||||
list_remove(&p->list);
|
||||
pathstr = p->str;
|
||||
kfree(p);
|
||||
return pathstr;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Check there's at least one element, unlink and return the last element */
|
||||
const char *pathdata_last_component(struct pathdata *pdata)
|
||||
{
|
||||
struct pathcomp *p;
|
||||
const char *pathstr;
|
||||
|
||||
if (!list_empty(&pdata->list)) {
|
||||
p = link_to_struct(pdata->list.prev, struct pathcomp, list);
|
||||
list_remove(&p->list);
|
||||
pathstr = p->str;
|
||||
kfree(p);
|
||||
return pathstr;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Unlink and free all path components in pathdata, and then free pathdata */
|
||||
void pathdata_destroy(struct pathdata *p)
|
||||
{
|
||||
struct pathcomp *c, *n;
|
||||
|
||||
list_foreach_removable_struct(c, n, &p->list, list) {
|
||||
list_remove(&c->list);
|
||||
kfree(c);
|
||||
}
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
void pathdata_print(struct pathdata *p)
|
||||
{
|
||||
struct pathcomp *comp;
|
||||
|
||||
printf("Extracted path is:\n");
|
||||
list_foreach_struct(comp, &p->list, list)
|
||||
printf("%s\n", comp->str);
|
||||
}
|
||||
|
||||
/* Extracts all path components from pathname into more presentable form */
|
||||
struct pathdata *pathdata_parse(const char *pathname,
|
||||
char *pathbuf, struct tcb *task)
|
||||
{
|
||||
struct pathdata *pdata = kzalloc(sizeof(*pdata));
|
||||
struct pathcomp *comp;
|
||||
char *str;
|
||||
|
||||
if (!pdata)
|
||||
return PTR_ERR(-ENOMEM);
|
||||
|
||||
/* Initialise pathdata */
|
||||
link_init(&pdata->list);
|
||||
strcpy(pathbuf, pathname);
|
||||
|
||||
/* First component is root if there's a root */
|
||||
if (pathname[0] == VFS_CHAR_SEP) {
|
||||
if (!(comp = kzalloc(sizeof(*comp)))) {
|
||||
kfree(pdata);
|
||||
return PTR_ERR(-ENOMEM);
|
||||
}
|
||||
link_init(&comp->list);
|
||||
comp->str = VFS_STR_ROOTDIR;
|
||||
list_insert_tail(&comp->list, &pdata->list);
|
||||
|
||||
if (task)
|
||||
/* Lookup start vnode is root vnode */
|
||||
pdata->vstart = task->fs_data->rootdir;
|
||||
else /* If no task, we use the root mountpoint pivot vnode */
|
||||
pdata->vstart = vfs_root.pivot;
|
||||
|
||||
/* Otherwise start from current directory */
|
||||
} else {
|
||||
struct dentry *curdir;
|
||||
|
||||
if (!(comp = kzalloc(sizeof(*comp)))) {
|
||||
kfree(pdata);
|
||||
return PTR_ERR(-ENOMEM);
|
||||
}
|
||||
link_init(&comp->list);
|
||||
|
||||
/* Get current dentry for this task */
|
||||
curdir = link_to_struct(task->fs_data->curdir->dentries.next,
|
||||
struct dentry, vref);
|
||||
|
||||
/* Use its name in path component */
|
||||
comp->str = curdir->name;
|
||||
list_insert_tail(&comp->list, &pdata->list);
|
||||
|
||||
/* Lookup start vnode is current dir vnode */
|
||||
pdata->vstart = task->fs_data->curdir;
|
||||
}
|
||||
|
||||
/* Add every other path component */
|
||||
str = splitpath(&pathbuf, VFS_CHAR_SEP);
|
||||
while(*str) {
|
||||
/* Any curdir components in path are ignored. */
|
||||
if (!strcmp(str, VFS_STR_CURDIR)) {
|
||||
;
|
||||
} else {
|
||||
if (!(comp = kzalloc(sizeof(*comp)))) {
|
||||
pathdata_destroy(pdata);
|
||||
return PTR_ERR(-ENOMEM);
|
||||
}
|
||||
link_init(&comp->list);
|
||||
comp->str = str;
|
||||
list_insert_tail(&comp->list, &pdata->list);
|
||||
}
|
||||
|
||||
/* Next component */
|
||||
str = splitpath(&pathbuf, VFS_CHAR_SEP);
|
||||
}
|
||||
// pathdata_print(pdata);
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
||||
86
conts/posix/fs0/src/romfs/romfs.c
Normal file
86
conts/posix/fs0/src/romfs/romfs.c
Normal file
@@ -0,0 +1,86 @@
|
||||
#include <stdio.h>
|
||||
#include <block.h>
|
||||
#include <l4/lib/math.h>
|
||||
#include <l4/macros.h>
|
||||
#include <l4/types.h>
|
||||
#include INC_GLUE(memory.h)
|
||||
|
||||
/*
|
||||
* Romfs superblock descriptor:
|
||||
*
|
||||
* All words are Big-Endian.
|
||||
*
|
||||
* Word 0: | - | r | o | m |
|
||||
* Word 1: | 1 | f | s | - |
|
||||
* Word 2: | Size | The number of bytes in this fs.
|
||||
* Word 3: | Checksum | The checksum of first 512 bytes.
|
||||
* Word 4: | Volume Name | The name of volume, padded to 16-byte boundary.
|
||||
* Rest: | File Headers | The rest of the data.
|
||||
*/
|
||||
struct romfs_superblock {
|
||||
u32 word0;
|
||||
u32 word1;
|
||||
u32 size;
|
||||
u32 checksum;
|
||||
char name[0];
|
||||
};
|
||||
|
||||
struct romfs_inode {
|
||||
unsigned long mdata_size; /* Size of metadata */
|
||||
unsigned long data_offset; /* Offset of data from start of fs */
|
||||
};
|
||||
|
||||
static u32
|
||||
romfs_checksum(void *data)
|
||||
{
|
||||
u32 sum = 0;
|
||||
u32 *ptr = data;
|
||||
|
||||
size >>= 2;
|
||||
while (size > 0) {
|
||||
sum += be32_to_cpu(*ptr++);
|
||||
size--;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
int romfs_fill_super(struct superblock *sb)
|
||||
{
|
||||
char buf[PAGE_SIZE];
|
||||
struct romfs_superblock *romfs_sb = (struct romfs_superblock *)buf;
|
||||
unsigned long vroot_offset;
|
||||
struct vnode *vroot;
|
||||
|
||||
/* Read first page from block device */
|
||||
bdev_readpage(0, buf);
|
||||
|
||||
/* Check superblock sanity */
|
||||
if (strcmp(be32_to_cpu(romfs_sb->word0), ROMFS_SB_WORD0)) {
|
||||
printf("Bad magic word 0\n");
|
||||
}
|
||||
if (strcmp(be32_to_cpu(romfs_sb->word1), ROMFS_SB_WORD1)) {
|
||||
printf("Bad magic word 1\n");
|
||||
}
|
||||
if (romfs_checksum(romfs_sb, min(romfs_sb->size, PAGE_SIZE))) {
|
||||
printf("Bad checksum.\n");
|
||||
}
|
||||
|
||||
/* Copy some params to generic superblock */
|
||||
sb->size = be32_to_cpu(romfs_sb->size);
|
||||
sb->magic = ROMFS_MAGIC;
|
||||
sb->ops = romfs_ops;
|
||||
|
||||
/* Offset of first vnode, which is the root vnode */
|
||||
vroot_offset = align_up(strnlen(romfs_sb->name, ROMFS_MAXNAME) + 1, 16);
|
||||
if (!(vroot = romfs_read_vnode(s, vroot_offset))) {
|
||||
printf("Error, could not get root inode.\n");
|
||||
}
|
||||
|
||||
/* Get the dirent for this vnode */
|
||||
if (!(sb->root = new_dentry(vroot))) {
|
||||
printf("Error: Could not get new dentry for root vnode.\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
43
conts/posix/fs0/src/romfs/romfs.h
Normal file
43
conts/posix/fs0/src/romfs/romfs.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef __ROMFS_H__
|
||||
#define __ROMFS_H__
|
||||
|
||||
#define ROMFS_MAGIC 0x7275
|
||||
|
||||
#define ROMFS_FTYPE_MSK 0xF /* File mask */
|
||||
#define ROMFS_FTYPE_HRD 0 /* Hard link */
|
||||
#define ROMFS_FTYPE_DIR 1 /* Directory */
|
||||
#define ROMFS_FTYPE_REG 2 /* Regular file */
|
||||
#define ROMFS_FTYPE_SYM 3 /* Symbolic link */
|
||||
#define ROMFS_FTYPE_BLK 4 /* Block device */
|
||||
#define ROMFS_FTYPE_CHR 5 /* Char device */
|
||||
#define ROMFS_FTYPE_SCK 6 /* Socket */
|
||||
#define ROMFS_FTYPE_FIF 7 /* FIFO */
|
||||
#define ROMFS_FTYPE_EXE 8 /* Executable */
|
||||
|
||||
#define ROMFS_NAME_ALIGN 16 /* Alignment size of names */
|
||||
|
||||
#define ROMFS_SB_WORD0 "-rom"
|
||||
#define ROMFS_SB_WORD1 "1fs-"
|
||||
|
||||
/*
|
||||
* Romfs superblock descriptor:
|
||||
*
|
||||
* All words are Big-Endian.
|
||||
*
|
||||
* Word 0: | - | r | o | m |
|
||||
* Word 1: | 1 | f | s | - |
|
||||
* Word 2: | Size | The number of bytes in this fs.
|
||||
* Word 3: | Checksum | The checksum of first 512 bytes.
|
||||
* Word 4: | Volume Name | The name of volume, padded to 16-byte boundary.
|
||||
* Rest: | File Headers | The rest of the data.
|
||||
*/
|
||||
struct romfs_superblock {
|
||||
u32 word0;
|
||||
u32 word1;
|
||||
u32 size;
|
||||
u32 checksum;
|
||||
char name[0];
|
||||
};
|
||||
|
||||
|
||||
#endif /* __ROMFS_H__ */
|
||||
61
conts/posix/fs0/src/romfs/romfs_fs.h
Normal file
61
conts/posix/fs0/src/romfs/romfs_fs.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef __LINUX_ROMFS_FS_H
|
||||
#define __LINUX_ROMFS_FS_H
|
||||
|
||||
/* The basic structures of the romfs filesystem */
|
||||
|
||||
#define ROMBSIZE BLOCK_SIZE
|
||||
#define ROMBSBITS BLOCK_SIZE_BITS
|
||||
#define ROMBMASK (ROMBSIZE-1)
|
||||
#define ROMFS_MAGIC 0x7275
|
||||
|
||||
#define ROMFS_MAXFN 128
|
||||
|
||||
#define __mkw(h,l) (((h)&0x00ff)<< 8|((l)&0x00ff))
|
||||
#define __mkl(h,l) (((h)&0xffff)<<16|((l)&0xffff))
|
||||
#define __mk4(a,b,c,d) cpu_to_be32(__mkl(__mkw(a,b),__mkw(c,d)))
|
||||
#define ROMSB_WORD0 __mk4('-','r','o','m')
|
||||
#define ROMSB_WORD1 __mk4('1','f','s','-')
|
||||
|
||||
/* On-disk "super block" */
|
||||
|
||||
struct romfs_super_block {
|
||||
__be32 word0;
|
||||
__be32 word1;
|
||||
__be32 size;
|
||||
__be32 checksum;
|
||||
char name[0]; /* volume name */
|
||||
};
|
||||
|
||||
/* On disk inode */
|
||||
|
||||
struct romfs_inode {
|
||||
__be32 next; /* low 4 bits see ROMFH_ */
|
||||
__be32 spec;
|
||||
__be32 size;
|
||||
__be32 checksum;
|
||||
char name[0];
|
||||
};
|
||||
|
||||
#define ROMFH_TYPE 7
|
||||
#define ROMFH_HRD 0
|
||||
#define ROMFH_DIR 1
|
||||
#define ROMFH_REG 2
|
||||
#define ROMFH_SYM 3
|
||||
#define ROMFH_BLK 4
|
||||
#define ROMFH_CHR 5
|
||||
#define ROMFH_SCK 6
|
||||
#define ROMFH_FIF 7
|
||||
#define ROMFH_EXEC 8
|
||||
|
||||
/* Alignment */
|
||||
|
||||
#define ROMFH_SIZE 16
|
||||
#define ROMFH_PAD (ROMFH_SIZE-1)
|
||||
#define ROMFH_MASK (~ROMFH_PAD)
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
/* Not much now */
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif
|
||||
528
conts/posix/fs0/src/syscalls.c
Normal file
528
conts/posix/fs0/src/syscalls.c
Normal file
@@ -0,0 +1,528 @@
|
||||
/*
|
||||
* 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 <lib/pathstr.h>
|
||||
#include <lib/malloc.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <task.h>
|
||||
#include <stat.h>
|
||||
#include <vfs.h>
|
||||
#include <alloca.h>
|
||||
#include <path.h>
|
||||
#include <syscalls.h>
|
||||
|
||||
#define NILFD -1
|
||||
|
||||
/*
|
||||
* This informs mm0 about an opened file descriptors.
|
||||
*
|
||||
* 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. MM0 is not notified about opened files
|
||||
* but is rather informed when it asks to be. This avoids deadlocks by
|
||||
* keeping the request flow in one way.
|
||||
*/
|
||||
|
||||
int pager_sys_open(struct tcb *pager, l4id_t opener, int fd)
|
||||
{
|
||||
struct tcb *task;
|
||||
struct vnode *v;
|
||||
|
||||
//printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
|
||||
if (pager->tid != PAGER_TID)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check if such task exists */
|
||||
if (!(task = find_task(opener)))
|
||||
return -ESRCH;
|
||||
|
||||
/* Check if that fd has been opened */
|
||||
if (task->files->fd[fd] == NILFD)
|
||||
return -EBADF;
|
||||
|
||||
/* Search the vnode by that vnum */
|
||||
if (IS_ERR(v = vfs_lookup_byvnum(vfs_root.pivot->sb,
|
||||
task->files->fd[fd])))
|
||||
return (int)v;
|
||||
|
||||
/*
|
||||
* Write file information, they will
|
||||
* be sent via the return origy.
|
||||
*/
|
||||
write_mr(L4SYS_ARG0, v->vnum);
|
||||
write_mr(L4SYS_ARG1, v->size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is called when the pager needs to open a vfs file via its path */
|
||||
int pager_open_bypath(struct tcb *pager, char *pathname)
|
||||
{
|
||||
struct pathdata *pdata;
|
||||
struct tcb *task;
|
||||
struct vnode *v;
|
||||
int retval;
|
||||
|
||||
//printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
|
||||
if (pager->tid != PAGER_TID)
|
||||
return -EINVAL;
|
||||
|
||||
/* Parse path data */
|
||||
if (IS_ERR(pdata = pathdata_parse(pathname,
|
||||
alloca(strlen(pathname) + 1),
|
||||
task)))
|
||||
return (int)pdata;
|
||||
|
||||
/* Search the vnode by that path */
|
||||
if (IS_ERR(v = vfs_lookup_bypath(pdata))) {
|
||||
retval = (int)v;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write file information, they will
|
||||
* be sent via the return origy.
|
||||
*/
|
||||
write_mr(L4SYS_ARG0, v->vnum);
|
||||
write_mr(L4SYS_ARG1, v->size);
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
pathdata_destroy(pdata);
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Directories only for now */
|
||||
void print_vnode(struct vnode *v)
|
||||
{
|
||||
struct dentry *d, *c;
|
||||
|
||||
printf("Vnode names:\n");
|
||||
list_foreach_struct(d, &v->dentries, vref) {
|
||||
printf("%s\n", d->name);
|
||||
printf("Children dentries:\n");
|
||||
list_foreach_struct(c, &d->children, child)
|
||||
printf("%s\n", c->name);
|
||||
}
|
||||
}
|
||||
|
||||
/* Creates a node under a directory, e.g. a file, directory. */
|
||||
struct vnode *vfs_create(struct tcb *task, struct pathdata *pdata,
|
||||
unsigned int mode)
|
||||
{
|
||||
struct vnode *vparent, *newnode;
|
||||
const char *nodename;
|
||||
|
||||
/* The last component is to be created */
|
||||
nodename = pathdata_last_component(pdata);
|
||||
|
||||
/* Check that the parent directory exists. */
|
||||
if (IS_ERR(vparent = vfs_lookup_bypath(pdata)))
|
||||
return vparent;
|
||||
|
||||
/* The parent vnode must be a directory. */
|
||||
if (!vfs_isdir(vparent))
|
||||
return PTR_ERR(-ENOENT);
|
||||
|
||||
/* Create new directory under the parent */
|
||||
if (IS_ERR(newnode = vparent->ops.mknod(vparent, nodename, mode)))
|
||||
return newnode;
|
||||
|
||||
// print_vnode(vparent);
|
||||
return newnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pager notifies vfs about a closed file descriptor.
|
||||
*
|
||||
* FIXME: fsync + close could be done under a single "close" ipc
|
||||
* from pager. Currently there are 2 ipcs: 1 fsync + 1 fd close.
|
||||
*/
|
||||
int pager_sys_close(struct tcb *sender, l4id_t closer, int fd)
|
||||
{
|
||||
struct tcb *task;
|
||||
int err;
|
||||
|
||||
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
|
||||
|
||||
BUG_ON(!(task = find_task(closer)));
|
||||
|
||||
if ((err = id_del(task->files->fdpool, fd)) < 0) {
|
||||
printf("%s: Error releasing fd identifier.\n",
|
||||
__FUNCTION__);
|
||||
return err;
|
||||
}
|
||||
task->files->fd[fd] = NILFD;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME:
|
||||
* - Is it already open?
|
||||
* - Allocate a copy of path string since lookup destroys it
|
||||
* - Check flags and mode.
|
||||
*/
|
||||
int sys_open(struct tcb *task, const char *pathname, int flags, unsigned int mode)
|
||||
{
|
||||
struct pathdata *pdata;
|
||||
struct vnode *v;
|
||||
int fd;
|
||||
int retval;
|
||||
|
||||
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
|
||||
|
||||
/* Parse path data */
|
||||
if (IS_ERR(pdata = pathdata_parse(pathname,
|
||||
alloca(strlen(pathname) + 1),
|
||||
task)))
|
||||
return (int)pdata;
|
||||
|
||||
/* Creating new file */
|
||||
if (flags & O_CREAT) {
|
||||
/* Make sure mode identifies a file */
|
||||
mode |= S_IFREG;
|
||||
if (IS_ERR(v = vfs_create(task, pdata, mode))) {
|
||||
retval = (int)v;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
/* Not creating, just opening, get the vnode */
|
||||
if (IS_ERR(v = vfs_lookup_bypath(pdata))) {
|
||||
retval = (int)v;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get a new fd */
|
||||
BUG_ON((fd = id_new(task->files->fdpool)) < 0);
|
||||
retval = fd;
|
||||
|
||||
/* Assign the new fd with the vnode's number */
|
||||
task->files->fd[fd] = v->vnum;
|
||||
|
||||
out:
|
||||
pathdata_destroy(pdata);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int sys_mkdir(struct tcb *task, const char *pathname, unsigned int mode)
|
||||
{
|
||||
struct pathdata *pdata;
|
||||
struct vnode *v;
|
||||
int ret = 0;
|
||||
|
||||
/* Parse path data */
|
||||
if (IS_ERR(pdata = pathdata_parse(pathname,
|
||||
alloca(strlen(pathname) + 1),
|
||||
task)))
|
||||
return (int)pdata;
|
||||
|
||||
/* Make sure we create a directory */
|
||||
mode |= S_IFDIR;
|
||||
|
||||
/* Create the directory or fail */
|
||||
if (IS_ERR(v = vfs_create(task, pdata, mode)))
|
||||
ret = (int)v;
|
||||
|
||||
/* Destroy extracted path data */
|
||||
pathdata_destroy(pdata);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sys_chdir(struct tcb *task, const char *pathname)
|
||||
{
|
||||
struct vnode *v;
|
||||
struct pathdata *pdata;
|
||||
int ret = 0;
|
||||
|
||||
/* Parse path data */
|
||||
if (IS_ERR(pdata = pathdata_parse(pathname,
|
||||
alloca(strlen(pathname) + 1),
|
||||
task)))
|
||||
return (int)pdata;
|
||||
|
||||
/* Get the vnode */
|
||||
if (IS_ERR(v = vfs_lookup_bypath(pdata))) {
|
||||
ret = (int)v;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Ensure it's a directory */
|
||||
if (!vfs_isdir(v)) {
|
||||
ret = -ENOTDIR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Assign the current directory pointer */
|
||||
task->fs_data->curdir = v;
|
||||
|
||||
out:
|
||||
/* Destroy extracted path data */
|
||||
pathdata_destroy(pdata);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void fill_kstat(struct vnode *v, struct kstat *ks)
|
||||
{
|
||||
ks->vnum = (u64)v->vnum;
|
||||
ks->mode = v->mode;
|
||||
ks->links = v->links;
|
||||
ks->uid = v->owner & 0xFFFF;
|
||||
ks->gid = (v->owner >> 16) & 0xFFFF;
|
||||
ks->size = v->size;
|
||||
ks->blksize = v->sb->blocksize;
|
||||
ks->atime = v->atime;
|
||||
ks->mtime = v->mtime;
|
||||
ks->ctime = v->ctime;
|
||||
}
|
||||
|
||||
int sys_fstat(struct tcb *task, int fd, void *statbuf)
|
||||
{
|
||||
struct vnode *v;
|
||||
unsigned long vnum;
|
||||
|
||||
/* Get the vnum */
|
||||
if (fd < 0 || fd > TASK_FILES_MAX || task->files->fd[fd] == NILFD)
|
||||
return -EBADF;
|
||||
|
||||
vnum = task->files->fd[fd];
|
||||
|
||||
/* Lookup vnode */
|
||||
if (!(v = vfs_lookup_byvnum(vfs_root.pivot->sb, vnum)))
|
||||
return -EINVAL;
|
||||
|
||||
/* Fill in the c0-style stat structure */
|
||||
fill_kstat(v, statbuf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns codezero-style stat structure which in turn is
|
||||
* converted to posix style stat structure via the libposix
|
||||
* library in userspace.
|
||||
*/
|
||||
int sys_stat(struct tcb *task, const char *pathname, void *statbuf)
|
||||
{
|
||||
struct vnode *v;
|
||||
struct pathdata *pdata;
|
||||
int ret = 0;
|
||||
|
||||
/* Parse path data */
|
||||
if (IS_ERR(pdata = pathdata_parse(pathname,
|
||||
alloca(strlen(pathname) + 1),
|
||||
task)))
|
||||
return (int)pdata;
|
||||
|
||||
/* Get the vnode */
|
||||
if (IS_ERR(v = vfs_lookup_bypath(pdata))) {
|
||||
ret = (int)v;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Fill in the c0-style stat structure */
|
||||
fill_kstat(v, statbuf);
|
||||
|
||||
out:
|
||||
/* Destroy extracted path data */
|
||||
pathdata_destroy(pdata);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note this can be solely called by the pager and is not the posix read call.
|
||||
* That call is in the pager. This merely supplies the pages the pager needs
|
||||
* if they're not in the page cache.
|
||||
*/
|
||||
int pager_sys_read(struct tcb *pager, unsigned long vnum, unsigned long f_offset,
|
||||
unsigned long npages, void *pagebuf)
|
||||
{
|
||||
struct vnode *v;
|
||||
|
||||
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
|
||||
|
||||
if (pager->tid != PAGER_TID)
|
||||
return -EINVAL;
|
||||
|
||||
/* Lookup vnode */
|
||||
if (!(v = vfs_lookup_byvnum(vfs_root.pivot->sb, vnum)))
|
||||
return -EINVAL;
|
||||
|
||||
/* Ensure vnode is not a directory */
|
||||
if (vfs_isdir(v))
|
||||
return -EISDIR;
|
||||
|
||||
return v->fops.read(v, f_offset, npages, pagebuf);
|
||||
}
|
||||
|
||||
int pager_update_stats(struct tcb *pager, unsigned long vnum,
|
||||
unsigned long newsize)
|
||||
{
|
||||
struct vnode *v;
|
||||
|
||||
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
|
||||
|
||||
if (pager->tid != PAGER_TID)
|
||||
return -EINVAL;
|
||||
|
||||
/* Lookup vnode */
|
||||
if (!(v = vfs_lookup_byvnum(vfs_root.pivot->sb, vnum)))
|
||||
return -EINVAL;
|
||||
|
||||
v->size = newsize;
|
||||
v->sb->ops->write_vnode(v->sb, v);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This can be solely called by the pager and is not the posix write call.
|
||||
* That call is in the pager. This writes the dirty pages of a file
|
||||
* back to disk via vfs.
|
||||
*
|
||||
* The buffer must be contiguous by page, if npages > 1.
|
||||
*/
|
||||
int pager_sys_write(struct tcb *pager, unsigned long vnum, unsigned long f_offset,
|
||||
unsigned long npages, void *pagebuf)
|
||||
{
|
||||
struct vnode *v;
|
||||
int ret;
|
||||
int fwrite_end;
|
||||
|
||||
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
|
||||
|
||||
if (pager->tid != PAGER_TID)
|
||||
return -EINVAL;
|
||||
|
||||
/* Lookup vnode */
|
||||
if (!(v = vfs_lookup_byvnum(vfs_root.pivot->sb, vnum)))
|
||||
return -EINVAL;
|
||||
|
||||
/* Ensure vnode is not a directory */
|
||||
if (vfs_isdir(v))
|
||||
return -EISDIR;
|
||||
|
||||
//printf("%s/%s: Writing to vnode %lu, at pgoff 0x%x, %d pages, buf at 0x%x\n",
|
||||
// __TASKNAME__, __FUNCTION__, vnum, f_offset, npages, pagebuf);
|
||||
|
||||
if ((ret = v->fops.write(v, f_offset, npages, pagebuf)) < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* If the file is extended, write silently extends it.
|
||||
* We update the extended size here. Otherwise subsequent write's
|
||||
* may fail by relying on wrong file size.
|
||||
*/
|
||||
fwrite_end = __pfn_to_addr(f_offset) + ret;
|
||||
if (v->size < fwrite_end) {
|
||||
v->size = fwrite_end;
|
||||
v->sb->ops->write_vnode(v->sb, v);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: Here's how this should have been:
|
||||
* v->ops.readdir() -> Reads fs-specific directory contents. i.e. reads
|
||||
* the directory buffer, doesn't care however contained vnode details are
|
||||
* stored.
|
||||
*
|
||||
* After reading, it converts the fs-spceific contents into generic vfs
|
||||
* dentries and populates the dentries of those vnodes.
|
||||
*
|
||||
* If vfs_readdir() is issued, those generic dentries are converted into
|
||||
* the posix-defined directory record structure. During this on-the-fly
|
||||
* generation, pseudo-entries such as . and .. are also added.
|
||||
*
|
||||
* If this layering is not done, i.e. the low-level dentry buffer already
|
||||
* keeps this record structure and we try to return that, then we wont
|
||||
* have a chance to add the pseudo-entries . and .. These record entries
|
||||
* are essentially created from parent vnode and current vnode but using
|
||||
* the names . and ..
|
||||
*/
|
||||
|
||||
int fill_dirent(void *buf, unsigned long vnum, int offset, char *name)
|
||||
{
|
||||
struct dirent *d = buf;
|
||||
|
||||
d->inum = (unsigned int)vnum;
|
||||
d->offset = offset;
|
||||
d->rlength = sizeof(struct dirent);
|
||||
strncpy((char *)d->name, name, DIRENT_NAME_MAX);
|
||||
|
||||
return d->rlength;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads @count bytes of posix struct dirents into @buf. This implements
|
||||
* the raw dirent read syscall upon which readdir() etc. posix calls
|
||||
* can be built in userspace.
|
||||
*
|
||||
* FIXME: Ensure buf is in shared utcb, and count does not exceed it.
|
||||
*/
|
||||
int sys_readdir(struct tcb *t, int fd, void *buf, int count)
|
||||
{
|
||||
int dirent_size = sizeof(struct dirent);
|
||||
int total = 0, nbytes = 0;
|
||||
unsigned long vnum;
|
||||
struct vnode *v;
|
||||
struct dentry *d;
|
||||
|
||||
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
|
||||
|
||||
/* Check address is in task's utcb */
|
||||
if ((unsigned long)buf < t->shpage_address ||
|
||||
(unsigned long)buf > t->shpage_address + PAGE_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if (fd < 0 || fd > TASK_FILES_MAX || t->files->fd[fd] == NILFD)
|
||||
return -EBADF;
|
||||
|
||||
vnum = t->files->fd[fd];
|
||||
|
||||
/* Lookup vnode */
|
||||
if (!(v = vfs_lookup_byvnum(vfs_root.pivot->sb, vnum)))
|
||||
return -EINVAL;
|
||||
|
||||
d = link_to_struct(v->dentries.next, struct dentry, vref);
|
||||
|
||||
/* Ensure vnode is a directory */
|
||||
if (!vfs_isdir(v))
|
||||
return -ENOTDIR;
|
||||
|
||||
/* Write pseudo-entries . and .. to user buffer */
|
||||
if (count < dirent_size)
|
||||
return 0;
|
||||
|
||||
fill_dirent(buf, v->vnum, nbytes, VFS_STR_CURDIR);
|
||||
nbytes += dirent_size;
|
||||
buf += dirent_size;
|
||||
count -= dirent_size;
|
||||
|
||||
if (count < dirent_size)
|
||||
return 0;
|
||||
|
||||
fill_dirent(buf, d->parent->vnode->vnum, nbytes, VFS_STR_PARDIR);
|
||||
nbytes += dirent_size;
|
||||
buf += dirent_size;
|
||||
count -= dirent_size;
|
||||
|
||||
/* Copy fs-specific dir to buf in struct dirent format */
|
||||
if ((total = v->ops.filldir(buf, v, count)) < 0)
|
||||
return total;
|
||||
|
||||
return nbytes + total;
|
||||
}
|
||||
|
||||
315
conts/posix/fs0/src/task.c
Normal file
315
conts/posix/fs0/src/task.c
Normal file
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* 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/arch/utcb.h>
|
||||
#include <l4lib/ipcdefs.h>
|
||||
#include <lib/malloc.h>
|
||||
#include <lib/idpool.h>
|
||||
#include <task.h>
|
||||
#include <vfs.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <syscalls.h>
|
||||
#include <globals.h>
|
||||
|
||||
|
||||
extern void *shared_page;
|
||||
|
||||
struct global_list global_tasks = {
|
||||
.list = { &global_tasks.list, &global_tasks.list },
|
||||
.total = 0,
|
||||
};
|
||||
|
||||
void global_add_task(struct tcb *task)
|
||||
{
|
||||
BUG_ON(!list_empty(&task->list));
|
||||
list_insert_tail(&task->list, &global_tasks.list);
|
||||
global_tasks.total++;
|
||||
}
|
||||
|
||||
void global_remove_task(struct tcb *task)
|
||||
{
|
||||
BUG_ON(list_empty(&task->list));
|
||||
list_remove_init(&task->list);
|
||||
BUG_ON(--global_tasks.total < 0);
|
||||
}
|
||||
|
||||
struct tcb *find_task(int tid)
|
||||
{
|
||||
struct tcb *t;
|
||||
|
||||
list_foreach_struct(t, &global_tasks.list, list)
|
||||
if (t->tid == tid)
|
||||
return t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allocate a vfs task structure according to given flags */
|
||||
struct tcb *tcb_alloc_init(unsigned int flags)
|
||||
{
|
||||
struct tcb *task;
|
||||
|
||||
if (!(task = kzalloc(sizeof(struct tcb))))
|
||||
return PTR_ERR(-ENOMEM);
|
||||
|
||||
/* Allocate new fs data struct if its not shared */
|
||||
if (!(flags & TCB_SHARED_FS)) {
|
||||
if (!(task->fs_data =
|
||||
kzalloc(sizeof(*task->fs_data)))) {
|
||||
kfree(task);
|
||||
return PTR_ERR(-ENOMEM);
|
||||
}
|
||||
task->fs_data->tcb_refs = 1;
|
||||
}
|
||||
|
||||
/* Allocate file structures if not shared */
|
||||
if (!(flags & TCB_SHARED_FILES)) {
|
||||
if (!(task->files =
|
||||
kzalloc(sizeof(*task->files)))) {
|
||||
kfree(task->fs_data);
|
||||
kfree(task);
|
||||
return PTR_ERR(-ENOMEM);
|
||||
}
|
||||
if (IS_ERR(task->files->fdpool =
|
||||
id_pool_new_init(TASK_FILES_MAX))) {
|
||||
void *err = task->files->fdpool;
|
||||
|
||||
kfree(task->files);
|
||||
kfree(task->fs_data);
|
||||
kfree(task);
|
||||
return err;
|
||||
}
|
||||
task->files->tcb_refs = 1;
|
||||
}
|
||||
|
||||
/* Ids will be set up later */
|
||||
task->tid = TASK_ID_INVALID;
|
||||
|
||||
/* Initialise list structure */
|
||||
link_init(&task->list);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
void copy_tcb(struct tcb *to, struct tcb *from, unsigned int share_flags)
|
||||
{
|
||||
if (share_flags & TCB_SHARED_FILES) {
|
||||
to->files = from->files;
|
||||
to->files->tcb_refs++;
|
||||
} else {
|
||||
/* Copy all file descriptors */
|
||||
memcpy(to->files->fd, from->files->fd,
|
||||
TASK_FILES_MAX * sizeof(to->files->fd[0]));
|
||||
|
||||
/* Copy the idpool */
|
||||
id_pool_copy(to->files->fdpool, from->files->fdpool, TASK_FILES_MAX);
|
||||
}
|
||||
|
||||
if (share_flags & TCB_SHARED_FS) {
|
||||
to->fs_data = from->fs_data;
|
||||
to->fs_data->tcb_refs++;
|
||||
} else
|
||||
memcpy(to->fs_data, from->fs_data, sizeof(*to->fs_data));
|
||||
}
|
||||
|
||||
/* Allocate a task struct and initialise it */
|
||||
struct tcb *tcb_create(struct tcb *orig, l4id_t tid, unsigned long shpage,
|
||||
unsigned int share_flags)
|
||||
{
|
||||
struct tcb *task;
|
||||
|
||||
/* Can't have some share flags with no original task */
|
||||
BUG_ON(!orig && share_flags);
|
||||
|
||||
/* Create a task and use given space and thread ids. */
|
||||
if (IS_ERR(task = tcb_alloc_init(share_flags)))
|
||||
return task;
|
||||
|
||||
task->tid = tid;
|
||||
task->shpage_address = shpage;
|
||||
|
||||
/*
|
||||
* If there's an original task that means this will be a full
|
||||
* or partial copy of it. We copy depending on share_flags.
|
||||
*/
|
||||
if (orig)
|
||||
copy_tcb(task, orig, share_flags);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
/* Free shared structures if no references left */
|
||||
void tcb_free_resources(struct tcb *task)
|
||||
{
|
||||
|
||||
if (--task->fs_data->tcb_refs == 0)
|
||||
kfree(task->fs_data);
|
||||
|
||||
if (--task->files->tcb_refs == 0) {
|
||||
kfree(task->files->fdpool);
|
||||
kfree(task->files);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void tcb_destroy(struct tcb *task)
|
||||
{
|
||||
global_remove_task(task);
|
||||
|
||||
tcb_free_resources(task);
|
||||
|
||||
kfree(task);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attaches to task's utcb. TODO: Add SHM_RDONLY and test it.
|
||||
* FIXME: This calls the pager and is a potential for deadlock
|
||||
* it only doesn't lock because it is called during initialisation.
|
||||
*/
|
||||
int task_utcb_attach(struct tcb *t)
|
||||
{
|
||||
int shmid;
|
||||
void *shmaddr;
|
||||
|
||||
/* Use it as a key to create a shared memory region */
|
||||
if ((shmid = shmget((key_t)t->shpage_address, PAGE_SIZE, 0)) == -1)
|
||||
goto out_err;
|
||||
|
||||
/* Attach to the region */
|
||||
if ((int)(shmaddr = shmat(shmid, (void *)t->shpage_address, 0)) == -1)
|
||||
goto out_err;
|
||||
|
||||
/* Ensure address is right */
|
||||
if ((unsigned long)shmaddr != t->shpage_address)
|
||||
return -EINVAL;
|
||||
|
||||
// printf("%s: Mapped shared page of task %d @ 0x%x\n",
|
||||
// __TASKNAME__, t->tid, shmaddr);
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
printf("%s: Mapping utcb of task %d failed with err: %d.\n",
|
||||
__TASKNAME__, t->tid, errno);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Receives ipc from pager about a new fork event and
|
||||
* the information on the resulting child task.
|
||||
*/
|
||||
int pager_notify_fork(struct tcb *sender, l4id_t parentid,
|
||||
l4id_t childid, unsigned long shpage_address,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct tcb *child, *parent;
|
||||
|
||||
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
|
||||
BUG_ON(!(parent = find_task(parentid)));
|
||||
|
||||
/* Create a child vfs tcb using given parent and copy flags */
|
||||
if (IS_ERR(child = tcb_create(parent, childid, shpage_address, flags)))
|
||||
return (int)child;
|
||||
|
||||
global_add_task(child);
|
||||
|
||||
// printf("%s/%s: Exiting...\n", __TASKNAME__, __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Pager tells us that a task is exiting by this call.
|
||||
*/
|
||||
int pager_notify_exit(struct tcb *sender, l4id_t tid)
|
||||
{
|
||||
struct tcb *task;
|
||||
|
||||
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
|
||||
BUG_ON(!(task = find_task(tid)));
|
||||
|
||||
tcb_destroy(task);
|
||||
|
||||
// printf("%s/%s: Exiting...\n", __TASKNAME__, __FUNCTION__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Read task information into the utcb page, since it won't fit into mrs. */
|
||||
struct task_data_head *receive_pager_taskdata(void)
|
||||
{
|
||||
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 PTR_ERR(err);
|
||||
}
|
||||
|
||||
/* Check if call itself was successful */
|
||||
if ((err = l4_get_retval()) < 0) {
|
||||
printf("%s: Error: %d.\n", __FUNCTION__, err);
|
||||
return PTR_ERR(err);
|
||||
}
|
||||
|
||||
/* Data is expected in the utcb page */
|
||||
// printf("%s: %d Total tasks.\n", __FUNCTION__,
|
||||
// ((struct task_data_head *)shared_page)->total);
|
||||
|
||||
return (struct task_data_head *)shared_page;
|
||||
}
|
||||
|
||||
|
||||
int init_task_structs(struct task_data_head *tdata_head)
|
||||
{
|
||||
struct tcb *t;
|
||||
|
||||
for (int i = 0; i < tdata_head->total; i++) {
|
||||
/* New tcb with fields sent by pager */
|
||||
if (IS_ERR(t = tcb_create(0, tdata_head->tdata[i].tid,
|
||||
tdata_head->tdata[i].shpage_address,
|
||||
0)))
|
||||
return (int)t;
|
||||
|
||||
/* Initialise vfs specific fields. */
|
||||
t->fs_data->rootdir = vfs_root.pivot;
|
||||
t->fs_data->curdir = vfs_root.pivot;
|
||||
|
||||
/* Print task information */
|
||||
//printf("%s: Task info received from mm0:\n", __TASKNAME__);
|
||||
//printf("%s: task id: %d, utcb address: 0x%x\n",
|
||||
// __TASKNAME__, t->tid, t->utcb_address);
|
||||
|
||||
/* shm attach to the utcbs for all these tasks except own */
|
||||
if (t->tid != self_tid())
|
||||
task_utcb_attach(t);
|
||||
global_add_task(t);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init_task_data(void)
|
||||
{
|
||||
struct task_data_head *tdata_head;
|
||||
|
||||
/* Read how many tasks and tids of each */
|
||||
BUG_ON((tdata_head = receive_pager_taskdata()) < 0);
|
||||
|
||||
/* Initialise local task structs using this info */
|
||||
BUG_ON(init_task_structs(tdata_head) < 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
84
conts/posix/fs0/src/vfs.c
Normal file
84
conts/posix/fs0/src/vfs.c
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* High-level vfs implementation.
|
||||
*
|
||||
* Copyright (C) 2008 Bahadir Balban
|
||||
*/
|
||||
#include <fs.h>
|
||||
#include <vfs.h>
|
||||
#include <task.h>
|
||||
#include <path.h>
|
||||
|
||||
LINK_DECLARE(vnode_cache);
|
||||
LINK_DECLARE(dentry_cache);
|
||||
|
||||
/*
|
||||
* /
|
||||
* /boot
|
||||
* /boot/images/mm0.axf
|
||||
* /boot/images/fs0.axf
|
||||
* /boot/images/test0.axf
|
||||
* /file.txt
|
||||
*/
|
||||
struct vfs_mountpoint vfs_root;
|
||||
|
||||
/*
|
||||
* Vnodes in the vnode cache have 2 keys. One is their dentry names, the other
|
||||
* is their vnum. This one checks the vnode cache by the given vnum first.
|
||||
* If nothing is found, it reads the vnode from disk into cache. This is called
|
||||
* by system calls since tasks keep an fd-to-vnum table.
|
||||
*/
|
||||
struct vnode *vfs_lookup_byvnum(struct superblock *sb, unsigned long vnum)
|
||||
{
|
||||
struct vnode *v;
|
||||
int err;
|
||||
|
||||
/* Check the vnode flat list by vnum */
|
||||
list_foreach_struct(v, &vnode_cache, cache_list)
|
||||
if (v->vnum == vnum)
|
||||
return v;
|
||||
|
||||
/* Check the actual filesystem for the vnode */
|
||||
v = vfs_alloc_vnode();
|
||||
v->vnum = vnum;
|
||||
|
||||
/* Note this only checks given superblock */
|
||||
if ((err = sb->ops->read_vnode(sb, v)) < 0) {
|
||||
vfs_free_vnode(v);
|
||||
return PTR_ERR(err);
|
||||
}
|
||||
|
||||
/* Add the vnode back to vnode flat list */
|
||||
list_insert(&v->cache_list, &vnode_cache);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/*
|
||||
* Vnodes in the vnode cache have 2 keys. One is the set of dentry names they
|
||||
* have, the other is their vnum. This one checks the vnode cache by the path
|
||||
* first. If nothing is found, it reads the vnode from disk into the cache.
|
||||
*/
|
||||
struct vnode *vfs_lookup_bypath(struct pathdata *pdata)
|
||||
{
|
||||
const char *firstcomp;
|
||||
|
||||
/*
|
||||
* This does vfs cache + fs lookup.
|
||||
*/
|
||||
BUG_ON(list_empty(&pdata->list));
|
||||
firstcomp = pathdata_next_component(pdata);
|
||||
return pdata->vstart->ops.lookup(pdata->vstart, pdata, firstcomp);
|
||||
}
|
||||
|
||||
int vfs_mount_root(struct superblock *sb)
|
||||
{
|
||||
/*
|
||||
* Lookup the root vnode of this superblock.
|
||||
* The root superblock has vnode number 0.
|
||||
*/
|
||||
vfs_root.pivot = vfs_lookup_byvnum(sb, 0);
|
||||
vfs_root.sb = sb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user