diff --git a/tasks/fs0/include/fs.h b/tasks/fs0/include/fs.h index c34faf2..69c3ce2 100644 --- a/tasks/fs0/include/fs.h +++ b/tasks/fs0/include/fs.h @@ -96,8 +96,8 @@ struct vnode { struct vnode_ops ops; /* Operations on this vnode */ struct file_ops fops; /* File-related operations on this vnode */ struct list_head dentries; /* Dirents that refer to this vnode */ - struct list_head state_list; /* List for vnode's dirty/clean state */ struct list_head cache_list; /* For adding the vnode to vnode cache */ + struct dirbuf dirbuf; /* Only directory buffers are kept */ u32 type; /* Vnode type, dev? socket? dir? ... */ u32 mode; /* Permissions */ u32 owner; /* Owner */ diff --git a/tasks/fs0/include/vfs.h b/tasks/fs0/include/vfs.h index 037f21f..37efd5c 100644 --- a/tasks/fs0/include/vfs.h +++ b/tasks/fs0/include/vfs.h @@ -10,10 +10,11 @@ * that fs0 maintains. All other file data is in mm0 page cache. */ struct dirbuf { - struct list_head list; - unsigned long bufsize; - u8 *buffer; + unsigned long npages; + int dirty; + u8 *buf; }; + extern struct list_head vnode_cache; extern struct list_head dentry_cache; @@ -54,6 +55,7 @@ static inline struct dentry *vfs_alloc_dentry(void) INIT_LIST_HEAD(&d->child); INIT_LIST_HEAD(&d->children); INIT_LIST_HEAD(&d->vref); + INIT_LIST_HEAD(&d->cache_list); return d; } @@ -68,9 +70,7 @@ static inline struct vnode *vfs_alloc_vnode(void) struct vnode *v = kzalloc(sizeof(struct vnode)); INIT_LIST_HEAD(&v->dentries); - INIT_LIST_HEAD(&v->state_list); INIT_LIST_HEAD(&v->cache_list); - list_add(&v->cache_list, &vnode_cache); return v; } diff --git a/tasks/fs0/src/memfs/vnode.c b/tasks/fs0/src/memfs/vnode.c index f3d75d2..109c9e8 100644 --- a/tasks/fs0/src/memfs/vnode.c +++ b/tasks/fs0/src/memfs/vnode.c @@ -211,21 +211,23 @@ int memfs_write_vnode(struct superblock *sb, struct vnode *v) * * TODO: Returns the list of posix-compliant dirent records in the buffer. */ -void *memfs_vnode_readdir(struct vnode *v, void *dirbuf) +int memfs_vnode_readdir(struct vnode *v) { int err; - struct memfs_dentry *memfsd = dirbuf; - struct dentry *parent = list_entry(v->dentries.next, struct dentry, - vref); + u8 *dirbuf; + struct memfs_dentry *memfsd; + struct dentry *parent = list_entry(v->dentries.next, + struct dentry, vref); /* Check directory type */ if (!vfs_isdir(v)) - return PTR_ERR(-ENOTDIR); + return -ENOTDIR; /* Allocate dirbuf if one is not provided by the upper layer */ - if (!dirbuf) { + if (!v->dirbuf->buffer) { /* This is as big as a page */ - memfsd = dirbuf = vfs_alloc_dirpage(); + v->dirbuf->buffer = vfs_alloc_dirbuf(); + memfsd = dirbuf = v->dirbuf->buffer; /* * Fail if vnode size is bigger than a page. Since this allocation @@ -234,18 +236,18 @@ void *memfs_vnode_readdir(struct vnode *v, void *dirbuf) BUG_ON(v->size > PAGE_SIZE); } - /* Read contents into the buffer */ + /* Read memfsd contents into the buffer */ if ((err = v->fops.read(v, 0, 1, dirbuf))) - return PTR_ERR(err); + return err; - /* For each fs-specific directory entry */ + /* 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 PTR_ERR(-ENOMEM); + return -ENOMEM; /* Initialise it */ newd->ops = generic_dentry_operations; @@ -264,8 +266,12 @@ void *memfs_vnode_readdir(struct vnode *v, void *dirbuf) /* Copy fields into generic dentry */ memcpy(newd->name, memfsd[i].name, MEMFS_DNAME_MAX); + + /* Add both vnode and dentry to their caches */ + list_add(&newd->cache_list, &dentry_cache); + list_add(&newv->cache_list, &vnode_cache); } - return dirbuf; + return 0; } struct vnode_ops memfs_vnode_operations = { diff --git a/tasks/fs0/src/syscalls.c b/tasks/fs0/src/syscalls.c index 19a50de..88cd27f 100644 --- a/tasks/fs0/src/syscalls.c +++ b/tasks/fs0/src/syscalls.c @@ -90,21 +90,39 @@ int sys_read(l4id_t sender, int fd, void *buf, int count) */ int sys_readdir(l4id_t sender, int fd, void *buf, int count) { + unsigned long vnum; struct vnode *v; struct tcb *t; + struct dirbuf *db; + unsigned long nbytes; + int err; /* Get the task */ BUG_ON(!(t = find_task(sender))); - /* Convert fd to vnode. */ - BUG_ON(!(v = t->fd[fd])); + /* Convert fd to vnum. */ + BUG_ON(!(vnum = t->fd[fd])); + + /* Lookup vnode */ + if (!(v = vfs_lookup_byvnum(vnum))) + return -EINVAL; /* No such vnode */ /* Ensure vnode is a directory */ if (!vfs_isdir(v)) return -ENOTDIR; - /* Simply read its contents */ - v->ops.readdir(v, buf, count); + /* Read the whole content from fs, if haven't done so yet */ + if ((err = v->ops.readdir(v)) < 0) + return err; + + /* Bytes to read, minimum of vnode size and count requested */ + nbytes = (v->size <= count) ? v->size : count; + + /* Do we have those bytes at hand? */ + if (v->dirbuf->buffer && (v->dirbuf->npages * PAGE_SIZE) >= nbytes) { + memcpy(buf, v->dirbuf->buffer, nbytes); /* Finish the job */ + return nbytes; + } return 0; }