Orion
Barry Importing existing Orion kernel d41a53c (2 years, 4 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); +}