Orion
Barry Importing existing Orion kernel d41a53c (3 years, 2 months ago)
diff --git a/vfs/file.c b/vfs/file.c
new file mode 100644
index 0000000..d3dfdc6
--- /dev/null
+++ b/vfs/file.c
@@ -0,0 +1,181 @@
+/*
+ * This file handles the file operations. Most of the functions here are just
+ * wrapper around the file operations. They generally check if the operation
+ * exists and use it if it does. If it doesn't they may implement a generic
+ * action, or just simply return. They also perform the necessary validation to
+ * ensure the operation can be called on the particular file.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include "vfs.h"
+#include "inode.h"
+#include "cache.h"
+#include "../mem/heap.h"
+#include "../mem/frame.h"
+#include "../screen.h"
+
+/* Get a File */
+File *
+file_get(File *file)
+{
+ file->usage++;
+ return file;
+}
+
+/* Put a File */
+void
+file_put(File *file)
+{
+ ASSERT(file->usage);
+ if (--file->usage)
+ return;
+ if (file->inode)
+ inode_put(file->inode);
+ if (file->path)
+ destroy_custody_chain(file->path);
+ kfree(file);
+}
+
+/* Read a file */
+size_t
+file_read(File *file, char *buf, size_t size)
+{
+ size_t count = 0;
+ if (!S_ISREG(file->inode->mode)) {
+ if (!file->ops)
+ return count;
+ if (!file->ops->read)
+ return count;
+ count = file->ops->read(file, buf, size, file->pos);
+ file->pos += count;
+ return count;
+ }
+ if (file->pos > file->inode->size)
+ return count;
+ if (size + file->pos > file->inode->size)
+ size = file->inode->size - file->pos;
+
+ uint16_t min, offset = file->pos & 0xFFF;
+ Page *page;
+ page_t oldPage;
+ acquire(&file->lock);
+ while (size) {
+ min = (size > 0x1000) ? 0x1000 : size;
+ page = page_find(file->inode, file->pos + (count & ~0xFFF));
+ if (!page) {
+ if (!file->ops)
+ goto end;
+ if (!file->ops->read)
+ goto end;
+ file->ops->read(file, buf + count, min,
+ file->pos + count);
+ } else {
+ acquire(&quickPageLock);
+ oldPage = quick_page(page->frame);
+ memcpy(buf + count, QUICK_PAGE + offset, min);
+ quick_page(oldPage);
+ release(&quickPageLock);
+ }
+ offset = 0;
+end:
+ size -= min;
+ count += min;
+ }
+ file->pos += count;
+ release(&file->lock);
+ return count;
+}
+
+/* Write a file */
+size_t
+file_write(File *file, char *buf, size_t size)
+{
+ size_t count = 0;
+ if (!S_ISREG(file->inode->mode)) {
+ if (!file->ops)
+ return count;
+ if (!file->ops->write)
+ return count;
+ count = file->ops->write(file, buf, size, file->pos);
+ file->pos += count;
+ return count;
+ }
+ if (size + file->pos > file->inode->size)
+ file->inode->size = size + file->pos;
+
+ uint16_t min, offset = file->pos & 0xFFF;
+ Page *page;
+ page_t oldPage;
+ acquire(&file->lock);
+ while (size) {
+ min = (size > 0x1000) ? 0x1000 : size;
+ page = page_find(file->inode, file->pos + (count & ~0xFFF));
+ if (!page) {
+ if (!file->ops)
+ goto end;
+ if (!file->ops->write)
+ goto end;
+ file->ops->write(file, buf + count, min,
+ file->pos + count);
+ } else {
+ acquire(&quickPageLock);
+ oldPage = quick_page(page->frame);
+ memcpy(QUICK_PAGE + offset, buf + count, min);
+ quick_page(oldPage);
+ release(&quickPageLock);
+ }
+ offset = 0;
+end:
+ size -= min;
+ count += min;
+ }
+ file->pos += count;
+ release(&file->lock);
+ return count;
+}
+
+/* I/O Control */
+int
+file_ioctl(File *file, unsigned long request, uintptr_t argp)
+{
+ if (!file->ops)
+ return -EINVAL;
+ if (!file->ops->ioctl)
+ return -ENOTTY;
+ return file->ops->ioctl(file, request, argp);
+}
+
+/* Read a directory entry (DirEnt) from a directory */
+int
+file_readdir(File *file, DirEnt *dent, off_t index)
+{
+ if (!file->ops)
+ return -EINVAL;
+ if (!file->ops->readdir)
+ return -EINVAL;
+ return file->ops->readdir(file, dent, index);
+}
+
+/* Open a file */
+int
+file_open(File *file)
+{
+ if (!file->ops)
+ return -EINVAL;
+ if (!file->ops->open)
+ return -EINVAL;
+ return file->ops->open(file);
+}
+
+/* Map a file into memory */
+void
+file_mmap(File *file, void *addr, size_t len, off_t offset)
+{
+ if (!file->ops)
+ return;
+ if (file->ops->mmap)
+ return file->ops->mmap(file, addr, len, offset);
+ if (S_ISREG(file->mode))
+ file->ops->read(file, addr, len, offset);
+}