mirror of
https://github.com/drasko/codezero.git
synced 2026-01-12 02:43:15 +01:00
sys_readdir and memfs_readdir closer to targeted look and feel.
We now have a single dirbuf of size PAGE_SIZE kept on the vnode. This is to be used for directory contents only. The reason it's kept on FS0 is because the contents are modified by calls such as mkdir or create, and otherwise these would have been handled by mm0 on the page cache buffers, which wouldn't work.
This commit is contained in:
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user