diff --git a/kernel-src/fs/vfs.c b/kernel-src/fs/vfs.c index 30325d1a..fe349bed 100644 --- a/kernel-src/fs/vfs.c +++ b/kernel-src/fs/vfs.c @@ -3,25 +3,356 @@ #include #include #include +#include +#include + +#define PATHNAME_MAX 512 +#define MAXLINKDEPTH 64 static hashtable_t fstable; vnode_t *vfsroot; +static spinlock_t listlock; +static vfs_t *vfslist; + +static cred_t kernelcred = { + .gid = 0, + .uid = 0 +}; + +static cred_t *getcred() { + if (_cpu()->thread == NULL || _cpu()->thread->proc == NULL) + return &kernelcred; + else + return &_cpu()->thread->proc->cred; +} + void vfs_init() { __assert(hashtable_init(&fstable, 20) == 0); vfsroot = alloc(sizeof(vnode_t)); __assert(vfsroot); + SPINLOCK_INIT(listlock); + vfsroot->type = V_TYPE_DIR; + vfsroot->refcount = 1; } int vfs_register(vfsops_t *ops, char *name) { return hashtable_set(&fstable, ops, name, strlen(name), true); } -int vfs_mount(vnode_t *mounton, char *name, void *data) { +int vfs_mount(vnode_t *backing, vnode_t *pathref, char *path, char *name, void *data) { vfsops_t *ops; - int e = hashtable_get(&fstable, (void **)(&ops), name, strlen(name)); - if (e) - return e; + int err = hashtable_get(&fstable, (void **)(&ops), name, strlen(name)); + if (err) + return err; + + vnode_t *mounton; + err = vfs_lookup(&mounton, pathref, path, NULL, 0); + + if (err) + return err; + + if (mounton->type != V_TYPE_DIR) { + VOP_RELEASE(mounton); + return ENOTDIR; + } + + vfs_t *vfs; + + err = ops->mount(&vfs, mounton, backing, data); + + if (err) { + VOP_RELEASE(mounton); + return err; + } + + spinlock_acquire(&listlock); + + vfs->next = vfslist; + vfslist = vfs; + + spinlock_release(&listlock); + + mounton->vfsmounted = vfs; + vfs->nodecovered = mounton; return 0; } + +int vfs_open(vnode_t *ref, char *path, int flags, vnode_t **res) { + int err = vfs_lookup(res, ref, path, NULL, 0); + if (err) + return err; + + VOP_LOCK(*res); + err = VOP_OPEN(res, flags, getcred()); + VOP_UNLOCK(*res); + if (err) + VOP_RELEASE(*res); + + return err; +} + +int vfs_create(vnode_t *ref, char *path, vattr_t *attr, int type, vnode_t **node) { + vnode_t *parent; + char *component = alloc(strlen(path) + 1); + int err = vfs_lookup(&parent, ref, path, component, VFS_LOOKUP_PARENT); + if (err) + goto cleanup; + + VOP_LOCK(parent); + err = VOP_CREATE(parent, component, attr, type, node, getcred()); + VOP_UNLOCK(parent); + VOP_RELEASE(parent); + + cleanup: + free(component); + return err; +} + +int vfs_write(vnode_t *node, void *buffer, size_t size, uintmax_t offset, size_t *written, int flags) { + VOP_LOCK(node); + int err = VOP_WRITE(node, buffer, size, offset, flags, written, getcred()); + VOP_UNLOCK(node); + return err; +} + +int vfs_read(vnode_t *node, void *buffer, size_t size, uintmax_t offset, size_t *bytesread, int flags) { + VOP_LOCK(node); + int err = VOP_READ(node, buffer, size, offset, flags, bytesread, getcred()); + VOP_UNLOCK(node); + return err; +} + +// if type is V_TYPE_LINK, a symlink is made +// in that case, destref is ignored, destpath is the link value and attr points to the attributes of the symlink +// if type is V_TYPE_REGULAR, a hardlink is made. +// in that case, destref and destpath point to the the node that will be linked and attr is ignored +// in both cases, linkref and linkpath describe the location of where to create the new link +int vfs_link(vnode_t *destref, char *destpath, vnode_t *linkref, char *linkpath, int type, vattr_t *attr) { + __assert(type == V_TYPE_LINK || type == V_TYPE_REGULAR); + char *component = alloc(strlen(linkpath) + 1); + vnode_t *parent = NULL; + int err = vfs_lookup(&parent, linkref, linkpath, component, VFS_LOOKUP_PARENT); + if (err) + goto cleanup; + + if (type == V_TYPE_REGULAR) { + vnode_t *targetnode; + err = vfs_lookup(&targetnode, destref, destpath, NULL, 0); + if (err) { + VOP_RELEASE(parent); + goto cleanup; + } + VOP_LOCK(parent); + err = VOP_LINK(targetnode, parent, component, getcred()); + VOP_UNLOCK(parent); + VOP_RELEASE(targetnode); + } else { + VOP_LOCK(parent); + err = VOP_SYMLINK(parent, component, attr, linkpath, getcred()); + VOP_UNLOCK(parent); + } + + VOP_RELEASE(parent); + + cleanup: + free(component); + return err; +} + +int vfs_unlink(vnode_t *ref, char *path) { + char *component = alloc(strlen(path) + 1); + vnode_t *parent = NULL; + int err = vfs_lookup(&parent, ref, path, component, VFS_LOOKUP_PARENT); + if (err) + goto cleanup; + + VOP_LOCK(parent); + VOP_UNLINK(parent, component, getcred()); + VOP_UNLOCK(parent); + VOP_RELEASE(parent); + + cleanup: + free(component); + return err; +} + +// returns the highest node in a mount point +static int highestnodeinmp(vnode_t *node, vnode_t **ret) { + int e = 0; + while (node->vfsmounted && e == 0) + e = VFS_ROOT(node->vfsmounted, &node); + + *ret = node; + + return e; +} + +// returns the lowest node in a mount point +static vnode_t *lowestnodeinmp(vnode_t *node) { + while (node->flags & V_FLAGS_ROOT) + node = node->vfs->nodecovered; + + return node; +} + +// looks up a pathname +// if flags & VFS_LOOKUP_PARENT, the parent of the resulting node will be put in +// the result pointer and lastcomp will contain the last component name +// if flags & VFS_LOOKUP_NOLINK, the last component will not be dereferenced if its a symbolic link +// if flags & VFS_LOOKUP_INTERNAL, the first byte of flags will be the current count of symlinks transversed +// this is not an external flag and is meant to be called from inside the function itself +int vfs_lookup(vnode_t **result, vnode_t *start, char *path, char *lastcomp, int flags) { + if ((flags & VFS_LOOKUP_INTERNAL) && (flags & 0xff) > MAXLINKDEPTH) + return ELOOP; + + size_t pathlen = strlen(path); + + if (pathlen == 0) { + __assert((flags & VFS_LOOKUP_PARENT) == 0); + vnode_t *r = start; + int e = highestnodeinmp(start, &r); + if (e) + return e; + VOP_HOLD(r); + *result = r; + return 0; + } + + if (pathlen > PATHNAME_MAX) + return ENAMETOOLONG; + + vnode_t *current = start; + int error = highestnodeinmp(start, ¤t); + if (error) + return error; + + char *compbuffer = alloc(pathlen + 1); + if (compbuffer == NULL) + return ENOMEM; + + strcpy(compbuffer, path); + + for (int i = 0; i < pathlen; ++i) { + if (compbuffer[i] == '/') + compbuffer[i] = '\0'; + } + + vnode_t *next; + error = 0; + VOP_HOLD(current); + + for (int i = 0; i < pathlen; ++i) { + if (compbuffer[i] == '\0') + continue; + + if (current->type != V_TYPE_DIR) { + error = ENOTDIR; + break; + } + + char *component = &compbuffer[i]; + size_t complen = strlen(component); + bool islast = i + complen == pathlen; + + if (islast && (flags & VFS_LOOKUP_PARENT)) { + strcpy(lastcomp, component); + break; + } + + if (strcmp(component, "..") == 0) { + // if the tree root, skip to next component + vnode_t *root = NULL; + __assert(highestnodeinmp(vfsroot, &root) == 0); + if (root == current) { + i += complen; + continue; + } + // if the root of a mounted fs, go to + if (current->flags & V_FLAGS_ROOT) { + vnode_t *n = lowestnodeinmp(current); + if (n != current) { + VOP_HOLD(n); + VOP_RELEASE(current); + current = n; + } + } + } + + VOP_LOCK(current); + error = VOP_LOOKUP(current, component, &next, getcred()); + VOP_UNLOCK(current); + if (error) + break; + + vnode_t *r = next; + error = highestnodeinmp(next, &r); + if (error) { + VOP_RELEASE(next); + break; + } + + if (r != next) { + VOP_HOLD(r); + VOP_RELEASE(next); + next = r; + } + + // dereference symlink + if (next->type == V_TYPE_LINK) { + // get path + char *linkderef; + VOP_LOCK(next); + error = VOP_READLINK(next, &linkderef, getcred()); + VOP_UNLOCK(next); + if (error) { + VOP_RELEASE(next); + break; + } + + // lookup new path with recursion + int pass = flags & VFS_LOOKUP_INTERNAL ? flags + 1 : VFS_LOOKUP_INTERNAL; + vnode_t *derefresult = NULL; + + // get the start node of the dereference + vnode_t *derefstart = next; + + if (*linkderef == '/' && _cpu()->thread && _cpu()->thread->proc) { + spinlock_acquire(&_cpu()->thread->proc->lock); + derefstart = _cpu()->thread->proc->root; + VOP_HOLD(derefstart); + spinlock_release(&_cpu()->thread->proc->lock); + } else if (*linkderef == '/') { + derefstart = vfsroot; + VOP_HOLD(derefstart); + } + + error = vfs_lookup(&derefresult, derefstart, linkderef, NULL, pass); + + if (*linkderef == '/') + VOP_RELEASE(derefstart); + + free(linkderef); + + VOP_RELEASE(next); + if (error) + break; + + next = derefresult; + } + + VOP_RELEASE(current); + current = next; + i += complen; + } + + if (error) { + VOP_RELEASE(current); + } else { + *result = current; + } + + free(compbuffer); + return error; +} diff --git a/kernel-src/include/kernel/scheduler.h b/kernel-src/include/kernel/scheduler.h index 12c8a3a2..e3413c9e 100644 --- a/kernel-src/include/kernel/scheduler.h +++ b/kernel-src/include/kernel/scheduler.h @@ -3,16 +3,12 @@ #include #include +#include +#include #define SCHED_THREAD_FLAGS_QUEUED 1 #define SCHED_THREAD_FLAGS_RUNNING 2 -typedef int pid_t; -typedef int tid_t; -typedef int gid_t; -typedef int uid_t; -typedef unsigned int mode_t; - struct proc_t; typedef struct thread_t { @@ -37,13 +33,14 @@ typedef struct proc_t { struct proc_t *parent; struct proc_t *child; pid_t pid; - gid_t gid; - uid_t uid; + cred_t cred; thread_t **threads; size_t threadtablesize; size_t runningthreadcount; mode_t umask; int flags; + vnode_t *cwd; + vnode_t *root; } proc_t; #include diff --git a/kernel-src/include/kernel/vfs.h b/kernel-src/include/kernel/vfs.h index 918ccf75..54e3419b 100644 --- a/kernel-src/include/kernel/vfs.h +++ b/kernel-src/include/kernel/vfs.h @@ -1,24 +1,43 @@ #ifndef _VFS_H -#define _VFS_h +#define _VFS_H #include #include #include +#include +#include -// TODO move typedefs to an abi header and use them here typedef struct { - int uid; - int gid; + uid_t uid; + gid_t gid; } cred_t; +typedef struct { + int type; + mode_t mode; + uid_t uid; + gid_t gid; + int fsid; + ino_t inode; + int nlinks; + size_t size; + size_t fsblocksize; + timespec_t atime; + timespec_t mtime; + timespec_t ctime; + int rdev; + size_t blocksused; +} vattr_t; + typedef struct vfs_t { struct vfs_t *next; struct vfsops_t *ops; - struct vnode_t *nodemountedon; + struct vnode_t *nodecovered; + struct vnode_t *root; int flags; } vfs_t; -#define V_FLAG_ROOT 1 +#define V_FLAGS_ROOT 1 #define V_TYPE_REGULAR 0 #define V_TYPE_DIR 1 @@ -28,8 +47,9 @@ typedef struct vfs_t { #define V_TYPE_LINK 5 #define V_TYPE_SOCKET 6 -#define V_FFLAG_READ 1 -#define V_FFLAG_WRITE 2 +#define V_FFLAGS_READ 1 +#define V_FFLAGS_WRITE 2 +#define V_FFLAGS_NONBLOCKING 4 typedef struct vnode_t { struct vops_t *ops; @@ -51,10 +71,19 @@ typedef struct vfsops_t { typedef struct vops_t { int (*open)(vnode_t **node, int flags, cred_t *cred); int (*close)(vnode_t *node, int flags, cred_t *cred); - int (*read)(vnode_t *node, void *buffer, size_t size, uintmax_t offset, int flags, cred_t *cred); - int (*write)(vnode_t *node, void *buffer, size_t size, uintmax_t offset, int flags, cred_t *cred); + int (*read)(vnode_t *node, void *buffer, size_t size, uintmax_t offset, int flags, size_t *readc, cred_t *cred); + int (*write)(vnode_t *node, void *buffer, size_t size, uintmax_t offset, int flags, size_t *writec, cred_t *cred); int (*lookup)(vnode_t *node, char *name, vnode_t **result, cred_t *cred); - // TODO get/set/poll + int (*create)(vnode_t *parent, char *name, vattr_t *attr, int type, vnode_t **result, cred_t *cred); + int (*getattr)(vnode_t *node, vattr_t *attr, cred_t *cred); + int (*setattr)(vnode_t *node, vattr_t *attr, cred_t *cred); + int (*poll)(vnode_t *node, int events); + int (*access)(vnode_t *node, mode_t mode, cred_t *cred); + int (*unlink)(vnode_t *node, char *name, cred_t *cred); + int (*link)(vnode_t *node, vnode_t *dir, char *name, cred_t *cred); + int (*symlink)(vnode_t *parent, char *name, vattr_t *attr, char *path, cred_t *cred); + int (*readlink)(vnode_t *parent, char **link, cred_t *cred); + int (*inactive)(vnode_t *node); } vops_t; #define VFS_MOUNT(vfs, mp, b, d) (vfs)->ops->mount(vfs, mp, b, d) @@ -65,24 +94,46 @@ typedef struct vops_t { #define VOP_LOCK(v) spinlock_acquire(&(v)->lock) #define VOP_UNLOCK(v) spinlock_release(&(v)->lock) -// these are expected to be called with the vnode locked -#define VOP_OPEN(v, f, c) (v)->ops->open(node, f, c) +// these are expected to be called with the vnode(s) locked +#define VOP_OPEN(v, f, c) (*v)->ops->open(v, f, c) #define VOP_CLOSE(v, f, c) (v)->ops->close(v, f, c) -#define VOP_READ(v, b, s, o, f, c) (v)->ops->read(v, b, s, o, f, c) -#define VOP_WRITE(v, b, s, o, f, c) (v)->ops->write(v, b, s, o, f, c) +#define VOP_READ(v, b, s, o, f, r, c) (v)->ops->read(v, b, s, o, f, r, c) +#define VOP_WRITE(v, b, s, o, f, r, c) (v)->ops->write(v, b, s, o, f, r, c) #define VOP_LOOKUP(v, n, r, c) (v)->ops->lookup(v, n, r, c) -#define VOP_GETATTR(v, a, c) asdfmsadijsadsaddsadsadsadsadsad -#define VOP_SETATTR(v, a, c) sdfdskafiasdfoidsjfdsjofdsaopdosp +#define VOP_CREATE(v, n, a, t, r, c) (v)->ops->create(v, n, a, t, r, c) +#define VOP_GETATTR(v, a, c) (v)->ops->getattr(v, a, c) +#define VOP_SETATTR(v, a, c) (v)->ops->setattr(v, a, c) +#define VOP_ACCESS(v, m, c) (v)->ops->access(v, m, c) +#define VOP_UNLINK(v, n, c) (v)->ops->unlink(v, n, c) +#define VOP_LINK(v, d, n, c) (v)->ops->link(v, d, n, c) +#define VOP_SYMLINK(v, n, a, p, c) (v)->ops->symlink(v, n, a, p, c) +#define VOP_READLINK(v, l, c) (v)->ops->readlink(v, l, c) // lock not needed unless atomicity is desired -#define VOP_POLL(v, p, c) sdfmdsaofidasfdsafdsafdsaofodsajfodis -#define VOP_HOLD(v) __atomic_add_fetch(&v->refcount, 1, __ATOMIC_SEQ_CST) -#define VOP_RELEASE(v) __attomic_sub_fetch(&v->refcount, 1, __ATOMIC_SEQ_CST); +#define VOP_POLL(v, p) (v)->ops->poll(v, p); +#define VOP_HOLD(v) __atomic_add_fetch(&(v)->refcount, 1, __ATOMIC_SEQ_CST) +#define VOP_RELEASE(v) {\ + if (__atomic_sub_fetch(&(v)->refcount, 1, __ATOMIC_SEQ_CST) == 0) {\ + (v)->ops->inactive(v); \ + v = NULL; \ + } \ + } extern vnode_t *vfsroot; void vfs_init(); -int vfs_mount(vnode_t *mounton, char *name, void *data); +int vfs_mount(vnode_t *backing, vnode_t *pathref, char *path, char *name, void *data); int vfs_register(vfsops_t *vfsops, char *name); +int vfs_open(vnode_t *ref, char *path, int flags, vnode_t **result); +int vfs_close(vnode_t *node, int flags); +int vfs_write(vnode_t *node, void *buffer, size_t size, uintmax_t offset, size_t *written, int flags); +int vfs_read(vnode_t *node, void *buffer, size_t size, uintmax_t offset, size_t *bytesread, int flags); +int vfs_create(vnode_t *ref, char *path, vattr_t *attr, int type, vnode_t **node); + +#define VFS_LOOKUP_PARENT 1 +#define VFS_LOOKUP_NOLINK 2 +#define VFS_LOOKUP_INTERNAL 0x80000000 +int vfs_lookup(vnode_t **result, vnode_t *start, char *path, char *lastcomp, int flags); + #endif