diff --git a/tasks/fs0/include/file.h b/tasks/fs0/include/file.h index 46a9b25..a553e4b 100644 --- a/tasks/fs0/include/file.h +++ b/tasks/fs0/include/file.h @@ -1,18 +1,6 @@ #ifndef __FS0_MM_H__ #define __FS0_MM_H__ -/* - * Describes the in-memory representation of a file. This is used to track - * file content, i.e. file pages by mm0, this is a temporary mock up until - * fs0 and mm0 are wired together. - */ -struct vm_file { - unsigned long vnum; - unsigned long length; - /* This is the cache of physical pages that this file has in memory. */ - struct list_head page_cache_list; - struct vm_pager *pager; -}; #endif /* __FS0_MM_H__ */ diff --git a/tasks/fs0/include/fs.h b/tasks/fs0/include/fs.h index 65454ed..d078fa6 100644 --- a/tasks/fs0/include/fs.h +++ b/tasks/fs0/include/fs.h @@ -48,6 +48,7 @@ struct vnode_ops { vnode_op_t create; struct vnode *(*lookup)(struct vnode *root, char *path); int (*readdir)(struct vnode *v); + int (*filldir)(struct vnode *v); vnode_op_t link; vnode_op_t unlink; int (*mkdir)(struct vnode *parent, char *name); @@ -91,8 +92,9 @@ struct dentry { }; /* - * Buffer to keep directory content. This is the only vnode content - * that fs0 maintains. All other file data is in mm0 page cache. + * Buffer that maintains directory content for a directory vnode. This is the + * only vnode content that fs0 maintains. All other file data is in mm0 page + * cache. */ struct dirbuf { unsigned long npages; @@ -100,6 +102,15 @@ struct dirbuf { u8 *buffer; }; +/* Posix-style dirent format used by userspace. Returned by sys_readdir() */ +#define DIRENT_NAME_MAX 32 +struct dirent { + u32 inum; /* Inode number */ + u32 offset; /* Dentry offset in its buffer */ + u32 rlength; /* Record length */ + u8 name[DIRENT_NAME_MAX]; /* Name string */ +}; + struct vnode { unsigned long vnum; /* Filesystem-wide unique vnode id */ int refcnt; /* Reference counter */ diff --git a/tasks/fs0/include/memfs/memfs.h b/tasks/fs0/include/memfs/memfs.h index eae95e6..79c3ed3 100644 --- a/tasks/fs0/include/memfs/memfs.h +++ b/tasks/fs0/include/memfs/memfs.h @@ -47,6 +47,7 @@ #define MEMFS_MAGIC 0xB #define MEMFS_NAME "memfs" #define MEMFS_NAME_SIZE 8 + struct memfs_inode { u32 inum; /* Inode number */ u32 mode; /* File permissions */ @@ -64,7 +65,7 @@ struct memfs_superblock { u32 blocksize; /* Filesystem block size */ u64 fmaxblocks; /* Maximum number of blocks per file */ u64 fssize; /* Total size of filesystem */ - struct memfs_inode *root; /* The root of this superblock */ + unsigned long root_vnum; /* The root vnum of this superblock */ struct list_head inode_cache_list; /* Chain of alloc caches */ struct list_head block_cache_list; /* Chain of alloc caches */ struct id_pool *ipool; /* Index pool for inodes */ diff --git a/tasks/fs0/src/file.c b/tasks/fs0/src/file.c index d9f3173..2bfd6fe 100644 --- a/tasks/fs0/src/file.c +++ b/tasks/fs0/src/file.c @@ -10,9 +10,6 @@ #include INC_GLUE(memory.h) #include -/* List of all in-memory files. */ -struct list_head vm_file_list; - /* * This reads contents of a file in pages, calling the fs-specific file read function to read-in * those pages' contents. diff --git a/tasks/fs0/src/init.c b/tasks/fs0/src/init.c index 307548f..bc0d9f9 100644 --- a/tasks/fs0/src/init.c +++ b/tasks/fs0/src/init.c @@ -49,8 +49,6 @@ void vfs_register_filesystems(void) memfs_register_fstype(&fs_type_list); } -extern struct list_head vm_file_list; - /* * Filesystem initialisation. */ diff --git a/tasks/fs0/src/memfs/memfs.c b/tasks/fs0/src/memfs/memfs.c index 56476a5..9f27a6b 100644 --- a/tasks/fs0/src/memfs/memfs.c +++ b/tasks/fs0/src/memfs/memfs.c @@ -66,12 +66,6 @@ int memfs_format_filesystem(void *buffer) INIT_LIST_HEAD(&sb->inode_cache_list); memfs_init_caches(sb); - /* We allocate and fix a root inode so the sb is ready for mount */ - sb->root = memfs_create_inode(sb); - - /* Some early-init code relies on root having inode number 0 */ - BUG_ON(sb->root->inum != 0); - return 0; } @@ -115,6 +109,50 @@ struct file_system_type memfs_fstype = { }, }; +/* + * 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; + int err; + + /* + * 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 = vfs_sb->ops->alloc_vnode(vfs_sb); + msb->root_vnum = vfs_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 it. NOTE: On root, parent is itself */ + strncpy(d->name, "/", VFS_DNAME_MAX); + d->ops = generic_dentry_operations; + d->parent = d; + d->vnode = v; + + /* Associate dentry with its vnode */ + list_add(&d->vref, &d->vnode->dentries); + + /* Associate dentry with its parent */ + list_add(&d->child, &parent->children); + + /* Add both vnode and dentry to their flat caches */ + list_add(&d->cache_list, &dentry_cache); + list_add(&v->cache_list, &vnode_cache); +} + /* Copies fs-specific superblock into generic vfs superblock */ struct superblock *memfs_fill_superblock(struct memfs_superblock *sb, struct superblock *vfs_sb) @@ -125,11 +163,8 @@ struct superblock *memfs_fill_superblock(struct memfs_superblock *sb, vfs_sb->fssize = sb->fssize; vfs_sb->blocksize = sb->blocksize; - /* - * Create the first vnode. Since this is memfs, root vnode is - * not read-in but dynamically created here. - */ - vfs_sb->ops->alloc_vnode(vfs_sb); + /* We initialise the root vnode as the root directory */ + memfs_init_rootdir(vfs_sb); return vfs_sb; } diff --git a/tasks/fs0/src/memfs/vnode.c b/tasks/fs0/src/memfs/vnode.c index 26ec12d..16d9628 100644 --- a/tasks/fs0/src/memfs/vnode.c +++ b/tasks/fs0/src/memfs/vnode.c @@ -204,6 +204,7 @@ int memfs_write_vnode(struct superblock *sb, struct vnode *v) return 0; } + /* * Creates ordinary files and directories at the moment. In the future, * other file types will be added. @@ -247,6 +248,8 @@ int memfs_vnode_mknod(struct vnode *v, char *dirname, unsigned int mode) /* Get the next directory entry available on the parent vnode */ if (v->dirbuf.npages * PAGE_SIZE <= v->size) return -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); @@ -254,8 +257,7 @@ int memfs_vnode_mknod(struct vnode *v, char *dirname, unsigned int mode) strncpy((char *)memfsd->name, dirname, MEMFS_DNAME_MAX); memfsd->name[MEMFS_DNAME_MAX - 1] = '\0'; - BUG(); /* FIXME: Fix this issue. */ - /* Write the updated directory buffer back to disk */ + /* Write the updated directory buffer back to disk block */ v->fops.write(v, 0, 1, v->dirbuf.buffer); /* Update parent vnode */ @@ -269,6 +271,7 @@ int memfs_vnode_mknod(struct vnode *v, char *dirname, unsigned int mode) newd->ops = generic_dentry_operations; newd->parent = parent; newd->vnode = newv; + strncpy(newd->name, dirname, VFS_DNAME_MAX); /* Associate dentry with its vnode */ list_add(&newd->vref, &newd->vnode->dentries); @@ -374,8 +377,33 @@ int memfs_vnode_readdir(struct vnode *v) } +int memfs_vnode_filldir(void *usrbuf, struct vnode *v, int count) +{ + int size; + + /* 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(buf, 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, }; diff --git a/tasks/fs0/src/syscalls.c b/tasks/fs0/src/syscalls.c index ca1cb79..d9f9532 100644 --- a/tasks/fs0/src/syscalls.c +++ b/tasks/fs0/src/syscalls.c @@ -226,17 +226,33 @@ int pager_sys_write(l4id_t sender, unsigned long vnum, unsigned long f_offset, * 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(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(l4id_t sender, int fd, void *buf, int count) { + struct dentry *d = list_entry(v->dentries.next, struct dentry, vref); + int dirent_size = sizeof(struct dirent); + int total = 0, nbytes = 0; unsigned long vnum; struct vnode *v; struct tcb *t; - unsigned long nbytes; int err; /* Get the task */ @@ -257,20 +273,30 @@ int sys_readdir(l4id_t sender, int fd, void *buf, int count) return 0; } - /* Read the whole content from fs, if haven't done so yet */ - if ((err = v->ops.readdir(v)) < 0) { - l4_ipc_return(err); + /* Write pseudo-entries . and .. to user buffer */ + if (count < dirent_size) + return 0; + + fill_dirent(buf, v->vnum, nbytes, "."); + nbytes += dirent_size; + buf += dirent_size; + count -= dirent_size; + + if (count < dirent_size) + return 0; + + fill_dirent(buf, d->parent->vnode->vnum, nbytes, ".."); + 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) { + l4_ipc_return(total); return 0; } - - /* 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); - l4_ipc_return(nbytes); - } + nbytes += total; + l4_ipc_return(nbytes); return 0; } diff --git a/tasks/fs0/src/vfs.c b/tasks/fs0/src/vfs.c index cc23597..3be6f6a 100644 --- a/tasks/fs0/src/vfs.c +++ b/tasks/fs0/src/vfs.c @@ -7,8 +7,8 @@ #include #include -struct list_head vnode_cache; -struct list_head dentry_cache; +LIST_HEAD(vnode_cache); +LIST_HEAD(dentry_cache); /* * / @@ -59,7 +59,7 @@ struct vnode *vfs_lookup_byvnum(struct superblock *sb, unsigned long vnum) */ struct vnode *vfs_lookup_bypath(struct tcb *task, char *path) { - struct vnode *vnstart; + struct vnode *vstart; /* Is it the root or current directory? If so, we already got it. */ if (!strcmp(path, "/")) @@ -76,13 +76,15 @@ struct vnode *vfs_lookup_bypath(struct tcb *task, char *path) /* * This does vfs cache + fs lookup. */ - return generic_vnode_lookup(vnstart, path); + return generic_vnode_lookup(vstart, path); } int vfs_mount_root(struct superblock *sb) { - /* Lookup the root vnode of this superblock. - * The root superblock has vnode number 0. */ + /* + * 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;