OpenCores
URL https://opencores.org/ocsvn/c0or1k/c0or1k/trunk

Subversion Repositories c0or1k

[/] [c0or1k/] [trunk/] [conts/] [posix/] [mm0/] [fs/] [memfs/] [vnode.c] - Rev 2

Compare with Previous | Blame | View Log

/*
 * 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 <malloc/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 | sb->fsidx;	/* Globalize by fsidx */
 
	/* 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 | sb->fsidx;
	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 & ~VFS_FSIDX_MASK;
	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_vnode_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,
};
 
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.