From 09bd001e1af33490db19d015a877c7eb54ccf075 Mon Sep 17 00:00:00 2001 From: Bahadir Balban Date: Fri, 15 Feb 2008 17:32:41 +0000 Subject: [PATCH] Added sys_mkdir and memfs_vnode_mkdir() functions. Untested but all implemented. --- tasks/fs0/include/fs.h | 12 +++-- tasks/fs0/include/lib/pathstr.h | 9 ++++ tasks/fs0/src/lib/pathstr.c | 89 +++++++++++++++++++++++++++++++++ tasks/fs0/src/lookup.c | 34 +------------ tasks/fs0/src/memfs/vnode.c | 83 ++++++++++++++++++++++++++++-- tasks/fs0/src/syscalls.c | 50 ++++++++++++------ 6 files changed, 221 insertions(+), 56 deletions(-) create mode 100644 tasks/fs0/include/lib/pathstr.h create mode 100644 tasks/fs0/src/lib/pathstr.c diff --git a/tasks/fs0/include/fs.h b/tasks/fs0/include/fs.h index 8468ca1..fc0a884 100644 --- a/tasks/fs0/include/fs.h +++ b/tasks/fs0/include/fs.h @@ -50,7 +50,7 @@ struct vnode_ops { int (*readdir)(struct vnode *v); vnode_op_t link; vnode_op_t unlink; - vnode_op_t mkdir; + int (*mkdir)(struct vnode *parent, char *name); vnode_op_t rmdir; vnode_op_t rename; vnode_op_t getattr; @@ -109,8 +109,7 @@ struct vnode { struct list_head dentries; /* Dirents that refer to this vnode */ 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 mode; /* Permissions and vnode type */ u32 owner; /* Owner */ u64 atime; /* Last access time */ u64 mtime; /* Last content modification */ @@ -120,7 +119,12 @@ struct vnode { }; /* FS0 vfs specific macros */ -#define vfs_isdir(v) S_ISDIR((v)->type) + +/* Check if directory */ +#define vfs_isdir(v) S_ISDIR((v)->mode) + +/* Set vnode type */ +#define vfs_set_type(v, type) {v->mode &= ~S_IFMT; v->mode |= S_IFMT & type; } struct fstype_ops { struct superblock *(*get_superblock)(void *buf); diff --git a/tasks/fs0/include/lib/pathstr.h b/tasks/fs0/include/lib/pathstr.h new file mode 100644 index 0000000..724f870 --- /dev/null +++ b/tasks/fs0/include/lib/pathstr.h @@ -0,0 +1,9 @@ +#ifndef __LIB_PATHSTR_H__ +#define __LIB_PATHSTR_H__ + +char *strreverse(char *str); +char *splitpath_end(char **path, char sep); +char *splitpath(char **str, char sep); + + +#endif /* __LIB_PATHSTR_H__ */ diff --git a/tasks/fs0/src/lib/pathstr.c b/tasks/fs0/src/lib/pathstr.c new file mode 100644 index 0000000..aa7aaed --- /dev/null +++ b/tasks/fs0/src/lib/pathstr.c @@ -0,0 +1,89 @@ +/* + * Functions to manipulate path strings. + * + * Copyright (C) 2008 Bahadir Balban + */ +#include +#include + +/* Reverses a string by allocating on stack. Not super-efficient but easy. */ +char *strreverse(char *str) +{ + int length = strlen(str); + char *tmp = alloca(length); + + strcpy(tmp, str); + + for (int i = 0; i < length; i++) + str[i] = tmp[length - 1 - i]; + + return str; +} + +/* + * Splits the string str according to the given seperator, returns the + * first component, and modifies the str so that it points at the next + * available component (or a leading separator which can be filtered + * out on a subsequent call to this function). + */ +char *splitpath(char **str, char sep) +{ + char *cursor = *str; + char *end; + + /* Move forward until no seperator */ + while (*cursor == sep) { + *cursor = '\0'; + cursor++; /* Move to first char of component */ + } + + end = cursor; + while (*end != sep && *end != '\0') + end++; /* Move until end of component */ + + if (*end == sep) { /* if ended with separator */ + *end = '\0'; /* finish component by null */ + if (*(end + 1) != '\0') /* if more components after, */ + *str = end + 1; /* assign beginning to str */ + else + *str = end; /* else str is also depleted, give null */ + } else /* if end was null, that means the end for str, too. */ + *str = end; + + return cursor; +} + +/* Same as split path, but splits components from the end. Slow. */ +char *splitpath_end(char **path, char sep) +{ + char *component; + + /* Reverse the string */ + strreverse(*path); + + /* Pick one from the start */ + component = splitpath(path, sep); + + /* Reverse the rest back to original. */ + strreverse(*path); + + return component; +} + +/* Splitpath test program. Tests all 3 functions. +int main() +{ + char str1[256] = "/a/b/c/d/////e/f"; + char *str2 = malloc(strlen(str1) + 1); + char *comp; + + strcpy(str2, str1); + + comp = splitpath_end(&str2, '/'); + while (*comp) { + printf("%s and %s\n", comp, str2); + comp = splitpath_end(&str2, '/'); + } +} +*/ + diff --git a/tasks/fs0/src/lookup.c b/tasks/fs0/src/lookup.c index 1e3c1ac..661cab2 100644 --- a/tasks/fs0/src/lookup.c +++ b/tasks/fs0/src/lookup.c @@ -7,39 +7,7 @@ #include #include #include - -/* - * Splits the string str according to the given seperator, returns the - * first component, and modifies the str so that it points at the next - * available component (or a leading separator which can be filtered - * out on a subsequent call to this function). - */ -char *splitpath(char **str, char sep) -{ - char *cursor = *str; - char *end; - - /* Move forward until no seperator */ - while (*cursor == sep) { - *cursor = '\0'; - cursor++; /* Move to first char of component */ - } - - end = cursor; - while (*end != sep && *end != '\0') - end++; /* Move until end of component */ - - if (*end == sep) { /* if ended with separator */ - *end = '\0'; /* finish component by null */ - if (*(end + 1) != '\0') /* if more components after, */ - *str = end + 1; /* assign beginning to str */ - else - *str = end; /* else str is also depleted, give null */ - } else /* if end was null, that means the end for str, too. */ - *str = end; - - return cursor; -} +#include /* * Given a dentry that has been populated by readdir with children dentries diff --git a/tasks/fs0/src/memfs/vnode.c b/tasks/fs0/src/memfs/vnode.c index 06ef9bb..325ac86 100644 --- a/tasks/fs0/src/memfs/vnode.c +++ b/tasks/fs0/src/memfs/vnode.c @@ -204,6 +204,77 @@ int memfs_write_vnode(struct superblock *sb, struct vnode *v) return 0; } +int memfs_vnode_mkdir(struct vnode *v, char *dirname) +{ + struct dentry *d, *parent = list_entry(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 err; + + /* Check there's no existing child with same name */ + list_for_each_entry(d, &parent->children, child) { + /* Does the name exist as a child? */ + if(d->ops.compare(d, dirname)) + return -EEXIST; + } + + /* Allocate a new vnode for the new directory */ + if (IS_ERR(newv = v->sb->ops->alloc_vnode(v->sb))) + return (int)newv; + + /* Initialise the vnode */ + vfs_set_type(newv, S_IFDIR); + + /* Get the next directory entry available on the parent vnode */ + if (v->dirbuf.npages * PAGE_SIZE <= v->size) + return -ENOSPC; + 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'; + + /* Update parent vnode */ + v->size += sizeof(*memfsd); + + /* Allocate a new vfs dentry */ + if (!(newd = vfs_alloc_dentry())) + return -ENOMEM; + + /* Initialise it */ + newd->ops = generic_dentry_operations; + newd->parent = parent; + newd->vnode = newv; + + /* Associate dentry with its vnode */ + list_add(&newd->vref, &newd->vnode->dentries); + + /* Associate dentry with its parent */ + list_add(&newd->child, &parent->children); + + /* Add both vnode and dentry to their flat caches */ + list_add(&newd->cache_list, &dentry_cache); + list_add(&newv->cache_list, &vnode_cache); + + return 0; +} + /* * Reads the vnode directory contents into vnode's buffer in a posix-compliant * struct dirent format. @@ -220,9 +291,13 @@ int memfs_vnode_readdir(struct vnode *v) struct dentry *parent = list_entry(v->dentries.next, struct dentry, vref); - /* Check directory type */ - if (!vfs_isdir(v)) - return -ENOTDIR; + /* + * 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) @@ -279,8 +354,10 @@ int memfs_vnode_readdir(struct vnode *v) return 0; } + struct vnode_ops memfs_vnode_operations = { .readdir = memfs_vnode_readdir, + .mkdir = memfs_vnode_mkdir, }; struct superblock_ops memfs_superblock_operations = { diff --git a/tasks/fs0/src/syscalls.c b/tasks/fs0/src/syscalls.c index a8cda8f..bbc5bbc 100644 --- a/tasks/fs0/src/syscalls.c +++ b/tasks/fs0/src/syscalls.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -38,19 +39,22 @@ int send_pager_opendata(l4id_t sender, int fd, unsigned long vnum) return 0; } -int do_open(l4id_t sender, const char *pathname, int flags, unsigned int mode) +/* FIXME: + * - Is it already open? + * - Allocate a copy of path string since lookup destroys it + * - Check flags and mode. + */ +int sys_open(l4id_t sender, const char *pathname, int flags, unsigned int mode) { struct vnode *v; struct tcb *t; int fd; + char *copypath = kmalloc(strlen(pathname) + 1); - /* FIXME: Use strnlen */ - char *pathcopy = kmalloc(strlen(pathname)); - /* FIXME: Use strncpy */ - memcpy(pathcopy, pathname, strlen(pathname)); + strcpy(copypath, pathname); /* Get the vnode */ - if (IS_ERR(v = vfs_lookup_bypath(vfs_root.pivot->sb, pathcopy))) + if (IS_ERR(v = vfs_lookup_bypath(vfs_root.pivot->sb, copypath))) return (int)v; /* Get the task */ @@ -68,18 +72,32 @@ int do_open(l4id_t sender, const char *pathname, int flags, unsigned int mode) return 0; } -/* FIXME: - * - Is it already open? - * - Allocate a copy of path string since lookup destroys it - * - Check flags and mode. - */ -int sys_open(l4id_t sender, const char *pathname, int flags, unsigned int mode) -{ - return do_open(sender, pathname, flags, mode); -} - int sys_mkdir(l4id_t sender, const char *pathname, unsigned int mode) { + char *parentpath, *pathbuf = kmalloc(strlen(pathname) + 1); + struct vnode *vparent; + char *newdir_name; + int err; + + strcpy(pathbuf, pathname); + parentpath = pathbuf; + + /* The last component is to be created */ + newdir_name = splitpath_end(&parentpath, '/'); + + /* Check that the parentdir exists. */ + if (IS_ERR(vparent = vfs_lookup_bypath(vfs_root.pivot->sb, parentpath))) + return (int)vparent; + + /* The parent vnode must be a directory. */ + if (!vfs_isdir(vparent)) + return -ENOENT; + + /* Create new directory under the parent */ + if ((err = vparent->ops.mkdir(vparent, newdir_name)) < 0) + return err; + kfree(pathbuf); + return 0; }