Files
codezero/tasks/fs0/src/syscalls.c

532 lines
12 KiB
C

/*
* 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 reply.
*/
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;
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
/* 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 reply.
*/
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_for_each_entry(d, &v->dentries, vref) {
printf("%s\n", d->name);
printf("Children dentries:\n");
list_for_each_entry(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 = list_entry(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;
}