aboutsummaryrefslogtreecommitdiff
path: root/kernel/fs/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/fs/file.c')
-rw-r--r--kernel/fs/file.c115
1 files changed, 115 insertions, 0 deletions
diff --git a/kernel/fs/file.c b/kernel/fs/file.c
new file mode 100644
index 0000000..4e79a3d
--- /dev/null
+++ b/kernel/fs/file.c
@@ -0,0 +1,115 @@
+#include "fs/file.h"
+#include "fs/vfs.h"
+#include "fs/vnode.h"
+#include "kernel.h"
+#include "mm/slab.h"
+#include "util/debug.h"
+#include "util/string.h"
+
+static slab_allocator_t *file_allocator;
+
+void file_init(void)
+{
+ file_allocator = slab_allocator_create("file", sizeof(file_t));
+}
+
+void fref(file_t *f)
+{
+ KASSERT(f->f_mode <= FMODE_MAX_VALUE && f->f_vnode);
+
+ f->f_refcount++;
+
+ if (f->f_vnode)
+ {
+ dbg(DBG_FREF, "fref: 0x%p, 0x%p ino %u, up to %lu\n", f,
+ f->f_vnode->vn_fs, f->f_vnode->vn_vno, f->f_refcount);
+ }
+ else
+ {
+ dbg(DBG_FREF, "fref: 0x%p up to %lu\n", f, f->f_refcount);
+ }
+}
+
+/*
+ * Create a file, initialize its members, vref the vnode, call acquire() on the
+ * vnode if the function pointer is non-NULL, and set the file descriptor in
+ * curproc->p_files.
+ *
+ * On successful return, the vnode's refcount should be incremented by one,
+ * the file's refcount should be 1, and curproc->p_files[fd] should point to
+ * the file being returned.
+ */
+file_t *fcreate(int fd, vnode_t *vnode, unsigned int mode)
+{
+ KASSERT(!curproc->p_files[fd]);
+ file_t *file = slab_obj_alloc(file_allocator);
+ if (!file)
+ return NULL;
+ memset(file, 0, sizeof(file_t));
+ file->f_mode = mode;
+
+ vref(file->f_vnode = vnode);
+ if (vnode->vn_ops->acquire)
+ vnode->vn_ops->acquire(vnode, file);
+
+ curproc->p_files[fd] = file;
+ fref(file);
+ return file;
+}
+
+/*
+ * Perform bounds checking on the fd, use curproc->p_files to get the file,
+ * fref it if it exists, and return.
+ */
+file_t *fget(int fd)
+{
+ if (fd < 0 || fd >= NFILES)
+ return NULL;
+ file_t *file = curproc->p_files[fd];
+ if (file)
+ fref(file);
+ return file;
+}
+
+/*
+ * Decrement the refcount, and set *filep to NULL.
+ *
+ * If the refcount drops to 0, call release on the vnode if the function pointer
+ * is non-null, vput() file's vnode, and free the file memory.
+ *
+ * Regardless of the ending refcount, *filep == NULL on return.
+ */
+void fput(file_t **filep)
+{
+ file_t *file = *filep;
+ *filep = NULL;
+
+ KASSERT(file && file->f_mode <= FMODE_MAX_VALUE);
+ KASSERT(file->f_refcount > 0);
+ if (file->f_refcount != 1)
+ KASSERT(file->f_vnode);
+
+ file->f_refcount--;
+
+ if (file->f_vnode)
+ {
+ dbg(DBG_FREF, "fput: 0x%p, 0x%p ino %u, down to %lu\n", file,
+ file->f_vnode->vn_fs, file->f_vnode->vn_vno, file->f_refcount);
+ }
+ else
+ {
+ dbg(DBG_FREF, "fput: 0x%p down to %lu\n", file, file->f_refcount);
+ }
+
+ if (!file->f_refcount)
+ {
+ if (file->f_vnode)
+ {
+ vlock(file->f_vnode);
+ if (file->f_vnode->vn_ops->release)
+ file->f_vnode->vn_ops->release(file->f_vnode, file);
+ vput_locked(&file->f_vnode);
+ }
+ slab_obj_free(file_allocator, file);
+ }
+}