Orion
Barry Importing existing Orion kernel d41a53c (2 years, 4 months ago)diff --git a/vfs/cache.c b/vfs/cache.c new file mode 100644 index 0000000..54a7495 --- /dev/null +++ b/vfs/cache.c @@ -0,0 +1,307 @@ +/* + * This file handles the VFS cache. This includes the VFS tree cache and all + * the functions for managing the associated structures. One such structure + * is the Inode's list of Directory Entries. Every DirEntry has a name, but + * also has a hash. This is for fast comparisons - when iterating the list to + * search for a name, first check if the hash matches. This saves a lot of time + * rather than actually comparing the names. Entries with hash collisions are + * placed adjacent in the list. + */ + +#include <stdint.h> +#include <string.h> +#include "vfs.h" +#include "inode.h" +#include "cache.h" +#include "../mem/heap.h" +#include "../mem/paging.h" +#include "../mem/frame.h" +#include "../task/task.h" + +/* Hash a file name */ +static uint32_t +name_hash(const char *name) +{ + uint32_t digest = 5381; + int c; + while (c = *name++) + digest = ((digest << 5) + digest) ^ c; /* digest*33 ^ c */ + return digest; +} + +/* Reap any unused branches of the cache tree */ +void +cache_reaper(void) +{ + /* + * The Cache Reaper attempts to release all the directory entries + * belonging to the root of the current task's file system context. + * This will in theory release all the associated inodes. If those + * inodes are only held in the cache (not in use, and therefore not + * referenced by anything else), they'll be freed and will in turn have + * their directory entries released. This recursive process will prune + * most of the VFS cache, leaving only in-use inodes. + */ + if (!current) + return; + if (!current->fs) + return; + if (!current->fs->root) + return; + entry_clean(current->fs->root); + current->fs->root->dirEntries = NULL; +} + +/* Get a DirEntry */ +DirEntry * +entry_get(DirEntry *entry) +{ + entry->usage++; + return entry; +} + +/* Put a DirEntry */ +void +entry_put(DirEntry *entry) +{ + if (--entry->usage) + return; + if (entry->inode) + inode_put(entry->inode); + kfree(entry); +} + +/* Search for a Directory Entry in a list by name */ +DirEntry * +entry_find(Inode *inode, const char *name) +{ + if (!inode) + return NULL; + acquire(&inode->lock); + uint32_t search = name_hash(name); + DirEntry *item = inode->dirEntries; + /* Look for matching hash */ + while (item) { + if (item->hash == search) + break; + item = item->next; + } + if (!item) { + release(&inode->lock); + return NULL; + } + /* Check all collisions */ + while (item && item->hash == search) { + if (!strcmp(item->name, name)) + break; + item = item->next; + } + release(&inode->lock); + if (!item || item->hash != search) + return NULL; + return item; +} + +/* Add a Directory Entry to a list */ +void +entry_add(Inode *inode, DirEntry *insert) +{ + if (!inode) + return; + acquire(&inode->lock); + insert->hash = name_hash(insert->name); + if (!inode->dirEntries) { + inode->dirEntries = entry_get(insert); + release(&inode->lock); + return; + } + + /* Try to find a collision */ + DirEntry *collision = inode->dirEntries; + while (collision->next) { + if (collision->hash == insert->hash) + break; + collision = collision->next; + } + /* Add into list */ + insert->next = collision->next; + collision->next = entry_get(insert); + release(&inode->lock); +} + +/* Remove a Directory Entry from a list */ +void +entry_remove(Inode *inode, const char *name) +{ + if (!inode) + return; + acquire(&inode->lock); + uint32_t search = name_hash(name); + DirEntry *item = inode->dirEntries, *prev = NULL; + /* Look for matching hash */ + while (item) { + if (item->hash == search) + break; + prev = item; + item = item->next; + } + if (!item) { + release(&inode->lock); + return; + } + /* Check all collisions */ + while (item && item->hash == search) { + if (!strcmp(item->name, name)) + break; + prev = item; + item = item->next; + } + if (!item || item->hash != search) { + release(&inode->lock); + return; + } + + /* Link over item */ + if (prev) + prev->next = item->next; + else + inode->dirEntries = item; + entry_put(item); + release(&inode->lock); +} + +/* Clean a list of Directory Entries */ +void +entry_clean(Inode *inode) +{ + if (!inode) + return; + if (!inode->dirEntries) + return; + acquire(&inode->lock); + DirEntry *de, *den; + for (de = inode->dirEntries; de; de = den) { + den = de->next; + entry_put(de); + } + release(&inode->lock); +} + +/* Get a Page */ +Page * +page_get(Page *page) +{ + page->usage++; + return page; +} + +/* Put a Page */ +void +page_put(Page *page) +{ + if (--page->usage) + return; + free_frame(PG_ADDR(page->frame)); + kfree(page); +} + +/* Find a Page by offset */ +Page * +page_find(Inode *inode, off_t offset) +{ + if (!inode) + return NULL; + acquire(&inode->lock); + PageCache *cache; + for (cache = inode->pages; cache; cache = cache->next) + if (cache->page->offset == PG_ADDR(offset)) + break; + release(&inode->lock); + if (!cache) + return NULL; + return cache->page; +} + +/* Add a page to a list */ +void +page_add(Inode *inode, Page *page) +{ + if (!inode) + return; + acquire(&inode->lock); + PageCache *cache; + if (!inode->pages) { + inode->pages = kmalloc(sizeof(PageCache)); + cache = inode->pages; + } else { + for (cache = inode->pages; cache->next; cache = cache->next); + cache->next = kmalloc(sizeof(PageCache)); + cache = cache->next; + } + cache->page = page_get(page); + release(&inode->lock); +} + +/* Create and add a Page to a list */ +Page * +page_create(Inode *inode, page_t frame, off_t offset) +{ + if (!inode) + return NULL; + acquire(&inode->lock); + Page *prev, *page = kmalloc(sizeof(Page)); + page->frame = PG_ADDR(frame); + page->offset = PG_ADDR(offset); + page_add(inode, page); + release(&inode->lock); + return page; +} + +/* Remove a Page from a list */ +void +page_remove(Inode *inode, Page *page) +{ + if (!inode) + return; + if (!inode->pages) + return; + acquire(&inode->lock); + + PageCache *cache, *item; + if (inode->pages->page == page) { + item = inode->pages; + inode->pages = item->next; + } else { + for (cache = inode->pages; + cache->next; cache = cache->next) + if (cache->next->page == page) + break; + if (!cache->next) { + release(&inode->lock); + return; + } + item = cache->next; + cache->next = item->next; + } + page_put(page); + kfree(item); + release(&inode->lock); +} + +/* Clean a list of Pages */ +void +page_clean(Inode *inode) +{ + if (!inode) + return; + if (!inode->pages) + return; + acquire(&inode->lock); + PageCache *c, *cn; + for (c = inode->pages; c; c = cn) { + page_put(c->page); + cn = c->next; + kfree(c); + } + release(&inode->lock); +} diff --git a/vfs/cache.h b/vfs/cache.h new file mode 100644 index 0000000..158d2ab --- /dev/null +++ b/vfs/cache.h @@ -0,0 +1,22 @@ +#ifndef KERNEL_VFS_CACHE_H +#define KERNEL_VFS_CACHE_H + +#include "vfs.h" + +void cache_reaper(void); + +DirEntry *entry_get(DirEntry *entry); +void entry_put(DirEntry *entry); +DirEntry *entry_find(Inode *inode, const char *name); +void entry_add(Inode *inode, DirEntry *insert); +void entry_remove(Inode *inode, const char *name); +void entry_clean(Inode *inode); + +Page *page_get(Page *page); +Page *page_find(Inode *inode, off_t offset); +void page_add(Inode *inode, Page *page); +Page *page_create(Inode *inode, page_t frame, off_t offset); +void page_remove(Inode *inode, Page *page); +void page_clean(Inode *inode); + +#endif diff --git a/vfs/custody.c b/vfs/custody.c new file mode 100644 index 0000000..1200f59 --- /dev/null +++ b/vfs/custody.c @@ -0,0 +1,117 @@ +/* + * This file handles chains of custody for files. It implements a generic set + * of operations for acting on CustodyChain objects. A CustodyChain holds a + * linked list of Custody objects, each of which refers to a directory entry. + * With this chain, the VFS is able to find what path was used to access a + * resources and go back up the chain (for implementing ..) and avoid going + * beyond the root. + */ + +#include <string.h> +#include "vfs.h" +#include "cache.h" +#include "../mem/heap.h" + +/* Initialise a custody chain */ +void +init_custody_chain(CustodyChain *chain) +{ + Custody *root = kmalloc(sizeof(Custody)); + chain->start = chain->end = root; + chain->size = 0; + init_lock(&chain->lock); + root->prev = root->next = NULL; + root->chain = chain; + root->entry = NULL; +} + +/* Create a new custody chain */ +CustodyChain * +create_custody_chain(void) +{ + CustodyChain *chain = kmalloc(sizeof(CustodyChain)); + init_custody_chain(chain); + return chain; +} + +/* Clean a custody chain */ +void +clean_custody_chain(CustodyChain *chain) +{ + acquire(&chain->lock); + while (chain->size) + remove_custody(chain); + kfree(chain->start); + release(&chain->lock); +} + +/* Destroy a custody chain */ +void +destroy_custody_chain(CustodyChain *chain) +{ + clean_custody_chain(chain); + kfree(chain); +} + +/* Copy custody chain */ +void +copy_custody_chain(CustodyChain *chain, CustodyChain *newchain) +{ + acquire(&chain->lock); + acquire(&newchain->lock); + Custody *c; + for (c = chain->start->next; c; c = c->next) + add_custody(newchain, c->entry); + release(&newchain->lock); + release(&chain->lock); +} + +/* Add entry */ +void +add_custody(CustodyChain *chain, DirEntry *entry) +{ + acquire(&chain->lock); + chain->end->next = kmalloc(sizeof(Custody)); + chain->end->next->prev= chain->end; + chain->end = chain->end->next; + chain->end->chain = chain; + chain->end->entry = entry_get(entry); + chain->size++; + release(&chain->lock); +} + +/* Remove an entry */ +DirEntry * +remove_custody(CustodyChain *chain) +{ + if (!chain->size) + return NULL; + acquire(&chain->lock); + entry_put(chain->end->entry); + chain->end = chain->end->prev; + kfree(chain->end->next); + chain->end->next = NULL; + chain->size--; + release(&chain->lock); + return chain->end->entry; +} + +/* Convert a chain to a path string */ +char * +custody_path(CustodyChain *chain, char *buf, size_t size) +{ + char *ptr = buf; + Custody *curr; + if (!chain->start->next) { + *ptr++ = '/'; + *ptr = '\0'; + return buf; + } + for (curr = chain->start->next; curr; curr = curr->next) { + *ptr++ = '/'; + strcpy(ptr, curr->entry->name); + ptr += strlen(curr->entry->name); + } + *ptr = '\0'; + return buf; +} diff --git a/vfs/devfs/file.c b/vfs/devfs/file.c new file mode 100644 index 0000000..d2da4e4 --- /dev/null +++ b/vfs/devfs/file.c @@ -0,0 +1,84 @@ +/* + * This file controls access to DevFS Files. It contains the functions called + * by the VFS for any operation on a File struct belonging to DevFS. + */ + +#include <stddef.h> +#include <string.h> +#include <errno.h> +#include <dirent.h> +#include "fs.h" +#include "../vfs.h" +#include "../cache.h" +#include "../../mem/paging.h" +#include "../../mem/frame.h" +#include "../../drivers/drivers.h" + +int devfs_open(File *file); +int devfs_readdir(File *file, DirEnt *dent, off_t index); + +FileOps devfsFileOps = { + .open = devfs_open, + .readdir = devfs_readdir, +}; + +/* Open a device node */ +int +devfs_open(File *file) +{ + if (S_ISREG(file->inode->mode) || S_ISDIR(file->inode->mode)) + return 0; + + /* Use major number to find relevant driver */ + Driver *driver; + for (driver = drivers; driver; driver = driver->next) + if (driver->major == MAJOR(file->inode->dev)) + break; + if (!driver) + return -ENXIO; + + file->ops = driver->ops; + if (!file->ops) + return 0; + if (!file->ops->open) + return 0; + return file->ops->open(file); + /* For checking the minor internally */ +} + +/* Read a directory entry */ +int +devfs_readdir(File *file, DirEnt *dent, off_t index) +{ + DirEntry *de; + + if (!index--) { + dent->ino = file->inode->ino; + dent->type = DT_DIR; + dent->namelen = 2; + strncpy(dent->name, ".", dent->namelen); + return 0; + } + + for (de = file->inode->dirEntries; de && index; de = de->next, index--); + if (!de) + return -ENOENT; + dent->ino = de->inode->ino; + if (S_ISBLK(de->inode->mode)) + dent->type = DT_BLK; + if (S_ISCHR(de->inode->mode)) + dent->type = DT_CHR; + if (S_ISDIR(de->inode->mode)) + dent->type = DT_DIR; + if (S_ISFIFO(de->inode->mode)) + dent->type = DT_FIFO; + if (S_ISLNK(de->inode->mode)) + dent->type = DT_LNK; + if (S_ISREG(de->inode->mode)) + dent->type = DT_REG; + if (S_ISSOCK(de->inode->mode)) + dent->type = DT_SOCK; + dent->namelen = strnlen(de->name, NAME_MAX) + 1; + strncpy(dent->name, de->name, NAME_MAX); + return 0; +} diff --git a/vfs/devfs/fs.h b/vfs/devfs/fs.h new file mode 100644 index 0000000..f972b67 --- /dev/null +++ b/vfs/devfs/fs.h @@ -0,0 +1,13 @@ +#ifndef KERNEL_VFS_DEVFS_H +#define KERNEL_VFS_DEVFS_H + +#include "../vfs.h" + +/* Operations */ +extern SuperOps devfsSuperOps; +extern InodeOps devfsInodeOps; +extern FileOps devfsFileOps; + +extern FileSystemType devfsType; + +#endif diff --git a/vfs/devfs/inode.c b/vfs/devfs/inode.c new file mode 100644 index 0000000..2208263 --- /dev/null +++ b/vfs/devfs/inode.c @@ -0,0 +1,69 @@ +/* + * This file contains the functions dealing with DevFS inodes. The VFS will + * call these when it performs operations on DevFS Inodes, or is dealing with + * the DevFS hierarchy. + */ + +#include <string.h> +#include "fs.h" +#include "../vfs.h" +#include "../../mem/heap.h" + +int devfs_create(Inode *inode, DirEntry *entry, mode_t mode); +Inode *devfs_lookup(Inode *inode, const char *name); +int devfs_mkdir(Inode *inode, DirEntry *entry, mode_t mode); +int devfs_rmdir(Inode *inode, DirEntry *entry); +int devfs_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev); +int devfs_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde); + +InodeOps devfsInodeOps = { + .create = devfs_create, + .lookup = devfs_lookup, + .mkdir = devfs_mkdir, + .rmdir = devfs_rmdir, + .mknod = devfs_mknod, + .rename = devfs_rename, +}; + +/* Create a file */ +int +devfs_create(Inode *inode, DirEntry *entry, mode_t mode) +{ + return 0; +} + + +/* Look up a file */ +Inode * +devfs_lookup(Inode *inode, const char *name) +{ + return NULL; +} + +/* Make a directory */ +int +devfs_mkdir(Inode *inode, DirEntry *entry, mode_t mode) +{ + return 0; +} + +/* Remove a directory */ +int +devfs_rmdir(Inode *inode, DirEntry *entry) +{ + return 0; +} + +/* Make a node */ +int +devfs_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev) +{ + return 0; +} + +/* Rename/mode a directory entry */ +int +devfs_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde) +{ + return 0; +} diff --git a/vfs/devfs/super.c b/vfs/devfs/super.c new file mode 100644 index 0000000..e57d60b --- /dev/null +++ b/vfs/devfs/super.c @@ -0,0 +1,85 @@ +/* + * This file controls the superblock for DevFS. It supports mounting new DevFS + * filesystems. The VFS will use the calls here when dealing directly with the + * filesystem structure. + */ + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include "fs.h" +#include "../vfs.h" +#include "../super.h" +#include "../inode.h" +#include "../../mem/heap.h" + +Inode *devfs_mount(FileSystemType *type, int flags, + const char *dev, void *data); +Inode *devfs_alloc_inode(SuperBlock *sb); + +FileSystemType devfsType = { + .name = "DevFS", + .mount = devfs_mount, +// .kill_sb = devfs_kill_sb, +}; + +SuperOps devfsSuperOps = { + .alloc_inode = devfs_alloc_inode, +// .free_inode = devfs_free_inode, +// .write_inode = devfs_write_inode, +// .delete_inode = devfs_delete_inode, +}; + +Inode devfsRoot = { + .mode = S_IFDIR | 0755, + .ops = &devfsInodeOps, + .fileOps = &devfsFileOps, + .size = 4096, +}; + +/* Mount a DevFS instance */ +Inode * +devfs_mount(FileSystemType *type, int flags, const char *dev, void *data) +{ + if (type != &devfsType) + return NULL; + + SuperBlock *super = kmalloc(sizeof(SuperBlock)); + + super->type = type; + super->ops = &devfsSuperOps; + init_lock(&super->lock); + + /* + * DevFS is special - it has a globally unique root. This means that no + * matter where a DevFS instance is mounted, it will be mounted with + * this inode, meaning the contents are the same. It basically causes + * the mountpoint to act as a hard-link. This means you can mount a new + * DevFS instance in a chroot() environment, and not have to remake all + * the device nodes. + */ + Inode *inode = &devfsRoot; + inode->nlink++; + inode->super = super; + /* FIXME: need an inode per super, or only one super */ + super->root = inode; + /* Never free */ + if (!inode->usage) + inode_get(inode); + + return inode; +} + +/* Allocate an inode */ +Inode * +devfs_alloc_inode(SuperBlock *sb) +{ + Inode *inode = kmalloc(sizeof(Inode)); + init_lock(&inode->lock); + inode->ops = &devfsInodeOps; + inode->fileOps = &devfsFileOps; + inode->super = sb; + inode->nlink = 1; + return inode_get(inode); /* This ensures that the inode is never free */ +} diff --git a/vfs/ext2fs/block.c b/vfs/ext2fs/block.c new file mode 100644 index 0000000..28cf480 --- /dev/null +++ b/vfs/ext2fs/block.c @@ -0,0 +1,32 @@ +/* + * This file contains the Ext2 block implementation. It reads a backing file + * from the super block in the intervals specified by the superblock. It also + * contains functions for reading blocks from files. + */ + +#include <stdint.h> +#include "fs.h" + +/* Read a block of data */ +void +ext2_read_block(SuperBlock *super, uint32_t index, char *buf) +{ + Ext2Super *rsuper = super->data; + + super->back->pos = index * (1024 << rsuper->blockSize); + file_read(super->back, buf, 1024 << rsuper->blockSize); +} + +/* Get the data block address from inode block index */ +uint32_t +ext2_get_data_addr(SuperBlock *super, Ext2Inode *node, uint32_t index) +{ + uint32_t tmp; + char block[4096]; + + /* Main blocks */ + if (index < 12) + return node->directBlock[index]; + index -= 12; + return 0; +} diff --git a/vfs/ext2fs/file.c b/vfs/ext2fs/file.c new file mode 100644 index 0000000..d0caa35 --- /dev/null +++ b/vfs/ext2fs/file.c @@ -0,0 +1,86 @@ +/* + * This file controls access to Ext2FS Files. It contains the functions called + * by the VFS for any operation on a Ext2FS File struct. + */ + +#include <stddef.h> +#include <string.h> +#include <dirent.h> +#include <errno.h> +#include "fs.h" +#include "../vfs.h" + +size_t ext2fs_read(File *file, char *buf, size_t size, off_t offset); +int ext2fs_readdir(File *file, DirEnt *dent, off_t index); +int ext2fs_open(File *file); + +FileOps ext2fsFileOps = { + .read = ext2fs_read, + .readdir = ext2fs_readdir, + .open = ext2fs_open, +}; + +/* Read a file */ +size_t +ext2fs_read(File *file, char *buf, size_t size, off_t offset) +{ + Ext2Inode inode; + uint16_t min; + size_t count = 0, i = offset / 4096; + uint32_t blk; + char ebuf[4096]; + ext2_read_inode(file->inode->super, file->inode->ino, &inode); + if (offset > inode.lsize) + return 0; + if (size + offset > inode.lsize) + size = inode.lsize - offset; + while (size) { + min = (size > 0x1000) ? 0x1000 : size; + blk = ext2_get_data_addr(file->inode->super, &inode, i); + ext2_read_block(file->inode->super, blk, ebuf); + memcpy(buf + count, ebuf + (offset % 4096), min); + size -= min; + count += min; + i++; + } + if (count >= 0x1000) + return count; +} + +/* Read a directory entry */ +int +ext2fs_readdir(File *file, DirEnt *dent, off_t index) +{ + char buf[4096]; + uint32_t block, blk = 0; + Ext2DirEntry *de; + Ext2Inode inode; + ext2_read_inode(file->inode->super, file->inode->ino, &inode); + for (blk = 0; blk < 0xFFFF; blk++) { + block = ext2_get_data_addr(file->inode->super, &inode, blk); + if (!block) + return -ENOENT; + ext2_read_block(file->inode->super, block, buf); + for (de = (Ext2DirEntry *) buf; + index && de < (Ext2DirEntry *) (buf + 4096); + de = (void *) ((char *) de + de->size), index--); + if (de >= (Ext2DirEntry *) (buf + 4096)) + return -ENOENT; + if (!index) + break; + } + if (!de->ino) + return -ENOENT; + dent->ino = de->ino; + dent->type = de->type; + dent->namelen = de->nameLen + 1; + strncpy(dent->name, de->name, de->size); + return 0; +} + +/* Open a file */ +int +ext2fs_open(File *file) +{ + return 0; +} diff --git a/vfs/ext2fs/fs.h b/vfs/ext2fs/fs.h new file mode 100644 index 0000000..0e5cadb --- /dev/null +++ b/vfs/ext2fs/fs.h @@ -0,0 +1,116 @@ +#ifndef KERNEL_VFS_EXT2FS_H +#define KERNEL_VFS_EXT2FS_H + +#include "../vfs.h" + +typedef struct Ext2Super Ext2Super; +typedef struct Ext2BlockGroupDesc Ext2BlockGroupDesc; +typedef struct Ext2Inode Ext2Inode; +typedef struct Ext2DirEntry Ext2DirEntry; + +/* Structure of the Ext2 SuperBlock */ +struct Ext2Super { + uint32_t numInodes; + uint32_t numBlocks; + uint32_t reservedBlocks; + uint32_t unallocBlocks; + uint32_t unallocInodes; + uint32_t superBlock; + uint32_t blockSize; + uint32_t fragSize; + uint32_t blocksPerGroup; + uint32_t fragsPerGroup; + uint32_t inodesPerGroup; + uint32_t lastMountTime; + uint32_t lastWriteTime; + uint16_t lastCheck; + uint16_t mustCheck; + uint16_t signature; + uint16_t state; + uint16_t error; + uint16_t verMinor; + uint32_t lastCheckTime; + uint32_t checkInterval; + uint32_t creator; + uint32_t verMajor; + uint16_t uid, gid; + /* Extended fields */ + uint32_t firstAvailInode; + uint16_t inodeSize; + uint16_t blockGroup; + uint32_t optionalFeatures; + uint32_t requiredFeatures; + uint32_t writableFeatures; + char fsId[16]; + char volumeName[16]; + char lastPath[64]; + uint32_t compression; + uint8_t preallocFileBlocks; + uint8_t preallocDirBlocks; + uint16_t unused; + char journalId[16]; + uint32_t journalInode; + uint32_t journalDev; + uint32_t orphanHead; +} __attribute__((packed)); + +/* Structure of the Ext2 Block Group Descriptor */ +struct Ext2BlockGroupDesc { + uint32_t blockUsage; + uint32_t inodeUsage; + uint32_t inodeTable; + uint16_t unallocBlocks; + uint16_t unallocInodes; + uint16_t numDirs; +} __attribute__((packed)); + +/* Structure of the Ext2 Inode */ +struct Ext2Inode { + uint16_t type; + uint16_t uid; + uint32_t lsize; + uint32_t lastAccessTime; + uint32_t creationTime; + uint32_t lastWriteTime; + uint32_t deletionTime; + uint16_t gid; + uint16_t numHardLinks; + uint32_t numSectors; + uint32_t flags; + uint32_t osA; + uint32_t directBlock[12]; + uint32_t singleBlock; + uint32_t doubleBlock; + uint32_t tripleBlock; + uint32_t gen; + uint32_t attr; + union { + uint32_t usize; + uint32_t dirACL; + }; + uint32_t fragment; + uint32_t osB[3]; +} __attribute__((packed)); + +/* Structure of the Ext2 Directory Entry */ +struct Ext2DirEntry { + uint32_t ino; + uint16_t size; + uint8_t nameLen; + uint8_t type; + char name[]; +} __attribute__((packed)); + +/* Operations */ +extern SuperOps ext2fsSuperOps; +extern InodeOps ext2fsInodeOps; +extern FileOps ext2fsFileOps; + +extern FileSystemType ext2fsType; + +void ext2_read_vnode(SuperBlock *super, uint32_t index, Inode *res); +void ext2_read_inode(SuperBlock *super, uint32_t index, Ext2Inode *res); +void ext2_read_block(SuperBlock *super, uint32_t index, char *buf); +uint32_t ext2_get_data_addr(SuperBlock *super, Ext2Inode *node, uint32_t index); + +#endif diff --git a/vfs/ext2fs/inode.c b/vfs/ext2fs/inode.c new file mode 100644 index 0000000..3437220 --- /dev/null +++ b/vfs/ext2fs/inode.c @@ -0,0 +1,94 @@ +/* + * This file contains the Ext2 inode implementation. It contains the functions + * for the inode operations, as well as several internal operations relating to + * inodes required for the full Ext2 implementation. + */ + +#include <stdint.h> +#include <string.h> +#include <fcntl.h> +#include "fs.h" +#include "../vfs.h" +#include "../super.h" +#include "../../mem/heap.h" + +Inode *ext2fs_lookup(Inode *inode, const char *name); + +InodeOps ext2fsInodeOps = { + .lookup = ext2fs_lookup, +}; + +/* Read an Ext2 inode as a VFS inode */ +void +ext2_read_vnode(SuperBlock *super, uint32_t index, Inode *res) +{ + Ext2Inode inode; + ext2_read_inode(super, index, &inode); + + res->ino = index; + res->usage = 0; + res->uid = inode.uid; + res->gid = inode.gid; + res->mode = inode.type; + res->nlink = inode.numHardLinks; + res->size = inode.lsize; + res->ops = &ext2fsInodeOps; + res->fileOps = &ext2fsFileOps; + res->super = super; +} + +/* Read an Ext2 inode */ +void +ext2_read_inode(SuperBlock *super, uint32_t index, Ext2Inode *res) +{ + Ext2Super *rsuper = super->data; + + /* Find the Block Group Descriptor */ + Ext2BlockGroupDesc bgd; + uint32_t groups = rsuper->numBlocks / rsuper->blocksPerGroup; + uint32_t group = (index - 1) / rsuper->inodesPerGroup; + index = (index - 1) % rsuper->inodesPerGroup; + if (rsuper->numBlocks % rsuper->blocksPerGroup) + groups++; + super->back->pos = (rsuper->blockSize ? 1 : 2) + * (1024 << rsuper->blockSize); + super->back->pos += sizeof(Ext2BlockGroupDesc) * group; + file_read(super->back, (char *) &bgd, sizeof(Ext2BlockGroupDesc)); + /* Read Inode */ + super->back->pos = bgd.inodeTable * (1024 << rsuper->blockSize); + super->back->pos += rsuper->inodeSize * index; + file_read(super->back, (char *) res, sizeof(Ext2Inode)); +} + +/* Look up a file */ +Inode * +ext2fs_lookup(Inode *vnode, const char *name) +{ + char buf[4096]; + uint32_t block, blk = 0; + Ext2DirEntry *de; + Ext2Inode inode; + ext2_read_inode(vnode->super, vnode->ino, &inode); + for (blk = 0; blk < 0xFFFF; blk++) { + block = ext2_get_data_addr(vnode->super, &inode, blk); + if (!block) + return NULL; + ext2_read_block(vnode->super, block, buf); + for (de = (Ext2DirEntry *) buf; + strncmp(de->name, name, de->nameLen) + && de < (Ext2DirEntry *) (buf + 4096); + de = (void *) ((char *) de + de->size)); + if (de >= (Ext2DirEntry *) (buf + 4096)) + return NULL; + if (!strncmp(de->name, name, de->nameLen)) + break; + } + if (!de->ino) + return NULL; + Inode *res = super_find_inode(vnode->super, de->ino); + if (res) + return res; + res = kmalloc(sizeof(Inode)); + ext2_read_vnode(vnode->super, de->ino, res); + return res; +} diff --git a/vfs/ext2fs/super.c b/vfs/ext2fs/super.c new file mode 100644 index 0000000..98ca800 --- /dev/null +++ b/vfs/ext2fs/super.c @@ -0,0 +1,89 @@ +/* + * This file controls the superblock for Ext2FS. It supports mounting new + * Ext2FS filesystems. The VFS will use the calls here when dealing directly + * with the filesystem structure. + */ + +#include <stdint.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/mount.h> +#include "fs.h" +#include "../vfs.h" +#include "../inode.h" +#include "../super.h" +#include "../../mem/heap.h" +#include "../../task/task.h" +#include "../../screen.h" + +Inode *ext2fs_mount(FileSystemType *type, int flags, + const char *dev, void *data); +Inode *ext2fs_alloc_inode(SuperBlock *sb); + +FileSystemType ext2fsType = { + .name = "Ext2FS", + .mount = ext2fs_mount, +}; + +SuperOps ext2fsSuperOps = { + .alloc_inode = ext2fs_alloc_inode, +}; + +/* Mount a Ext2FS instance */ +Inode * +ext2fs_mount(FileSystemType *type, int flags, const char *dev, void *data) +{ + if (type != &ext2fsType) + return NULL; + + int fd = open(dev, O_RDONLY); + if (fd < 0) + return (void *) fd; + File *back = file_get(current->files->fd[fd]); + close(fd); + if (!S_ISBLK(back->mode)) { + file_put(back); + return (void *) -ENOTBLK; + } + + Ext2Super *rsuper = kmalloc(sizeof(Ext2Super)); + back->pos = 1024; + file_read(back, (char *) rsuper, sizeof(Ext2Super)); + if (rsuper->signature != 0xEF53) { + kprintf("Error while mounting %s: Not an Ext2FS drive", dev); + file_put(back); + kfree(rsuper); + return (void *) -EINVAL; + } + if (rsuper->requiredFeatures & 0x01) { + kprintf("Cannot mount %s: Compression required", dev); + file_put(back); + kfree(rsuper); + return (void *) -EINVAL; + } + if ((rsuper->writableFeatures & 0x02) && !(flags & MS_RDONLY)) { + kprintf("Cannot mount %s: 64-bit File System required", dev); + file_put(back); + kfree(rsuper); + return (void *) -EINVAL; + } + + SuperBlock *super = kmalloc(sizeof(SuperBlock)); + super->type = type; + super->ops = &ext2fsSuperOps; + init_lock(&super->lock); + super->back = back; + super->data = rsuper; + super->root = kmalloc(sizeof(Inode)); + ext2_read_vnode(super, 2, super->root); + + return super->root; +} + +/* Allocate an inode */ +Inode * +ext2fs_alloc_inode(SuperBlock *sb) +{ + return NULL; +} 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); +} diff --git a/vfs/inode.c b/vfs/inode.c new file mode 100644 index 0000000..34b5c65 --- /dev/null +++ b/vfs/inode.c @@ -0,0 +1,204 @@ +/* + * This file deals with inode operations. Most of the functions here are just + * wrappers for the inode 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 inode. + */ + +#include <string.h> +#include <errno.h> +#include "vfs.h" +#include "cache.h" +#include "inode.h" +#include "super.h" +#include "../mem/heap.h" +#include "../screen.h" + +/* Get an Inode */ +Inode * +inode_get(Inode *inode) +{ + /* Add the inode to the Inode List */ + if (!inode->usage && inode->super) { + if (!inode->super->inodes) { + inode->super->inodes = inode; + } else { + inode->lnext = inode->super->inodes; + inode->super->inodes = inode; + } + inode->super->cachedInodes++; + } + + inode->usage++; + return inode; +} + +/* Put an Inode */ +void +inode_put(Inode *inode) +{ + ASSERT(inode->usage); + if (--inode->usage) + return; + + /* Remove the inode from the Inode List */ + Inode *search, *prev = NULL; + if (inode->super) { + for (search = inode->super->inodes; search; + prev = search, search = search->lnext) + if (search == inode) + break; + if (search) { + if (prev) + prev->lnext = search->lnext; + else + inode->super->inodes = search->lnext; + } + } + + /* Clean up */ + if (S_ISDIR(inode->mode)) + entry_clean(inode); + else + page_clean(inode); + + kfree(inode); +} + +/* Create an inode */ +int +inode_create(Inode *inode, DirEntry *entry, mode_t mode) +{ + if (!inode->ops) + return -EINVAL; + if (!inode->ops->create) + return -EINVAL; + acquire(&inode->lock); + int err = inode->ops->create(inode, entry, mode); + if (!err) { + entry_add(inode, entry); + entry->inode = super_alloc_inode(inode->super); + entry->inode->mode = mode | S_IFREG; + } + release(&inode->lock); + return err; +} + +/* Find a child inode in a directory inode */ +DirEntry * +inode_lookup(Inode *inode, const char *name) +{ + if (!S_ISDIR(inode->mode)) + return NULL; + + acquire(&inode->lock); + + /* Check cache first */ + DirEntry *de = entry_find(inode, name); + if (de) { + entry_get(de); + if (de->inode) { + release(&inode->lock); + return de; + } + } + /* Try file system lookup */ + if (!inode->ops) { + release(&inode->lock); + return NULL; + } + if (!inode->ops->lookup) { + release(&inode->lock); + return NULL; + } + Inode *child = inode->ops->lookup(inode, name); + + /* The file doesn't exist */ + if (!child) { + if (de) + entry_remove(inode, name); + release(&inode->lock); + return NULL; + } + + /* Fill in DirEntry */ + if (!de) { + de = kmalloc(sizeof(DirEntry)); + strncpy(de->name, name, NAME_MAX); + de->super = inode->super; + entry_add(inode, de); + } + de->inode = inode_get(child); + de->mnt = NULL; + release(&inode->lock); + + return entry_get(de); +} + +/* Make a directory */ +int +inode_mkdir(Inode *inode, DirEntry *entry, mode_t mode) +{ + if (!inode->ops) + return -EINVAL; + if (!inode->ops->mkdir) + return -EINVAL; + acquire(&inode->lock); + int err = inode->ops->mkdir(inode, entry, mode); + if (!err) { + entry_add(inode, entry); + entry->inode = super_alloc_inode(inode->super); + entry->inode->mode = mode | S_IFDIR; + } + release(&inode->lock); + return err; +} + +/* Remove a directory */ +int +inode_rmdir(Inode *inode, DirEntry *entry) +{ + if (!inode->ops) + return -EINVAL; + if (!inode->ops->rmdir) + return -EINVAL; + acquire(&inode->lock); + int err = inode->ops->rmdir(inode, entry); + if (!err) + entry_remove(inode, entry->name); + release(&inode->lock); + return err; +} + +/* Make a node */ +int +inode_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev) +{ + if (!inode->ops) + return -EINVAL; + if (!inode->ops->mknod) + return -EINVAL; + acquire(&inode->lock); + + int err = inode->ops->mknod(inode, entry, mode, dev); + if (!err) { + entry_add(inode, entry); + entry->inode = super_alloc_inode(inode->super); + entry->inode->mode = mode; /* FIXME */ + entry->inode->dev = dev; + } + release(&inode->lock); + return err; +} + +/* Rename/mode a directory entry */ +int +inode_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde) +{ + if (!si->ops) + return -EINVAL; + if (!si->ops->rename) + return -EINVAL; + return 0; +} diff --git a/vfs/inode.h b/vfs/inode.h new file mode 100644 index 0000000..adf5b3b --- /dev/null +++ b/vfs/inode.h @@ -0,0 +1,15 @@ +#ifndef KERNEL_VFS_INODE_H +#define KERNEL_VFS_INODE_H + +#include "vfs.h" + +Inode *inode_get(Inode *inode); +void inode_put(Inode *inode); +int inode_create(Inode *inode, DirEntry *entry, mode_t mode); +DirEntry *inode_lookup(Inode *inode, const char *name); +int inode_mkdir(Inode *inode, DirEntry *entry, mode_t mode); +int inode_rmdir(Inode *inode, DirEntry *entry); +int inode_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev); +int inode_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde); + +#endif diff --git a/vfs/procfs/file.c b/vfs/procfs/file.c new file mode 100644 index 0000000..b292cf4 --- /dev/null +++ b/vfs/procfs/file.c @@ -0,0 +1,126 @@ +/* + * This file controls access to ProcFS Files. It contains the functions called + * by the VFS for any operation on a File struct belonging to ProcFS. + */ + +#include <stddef.h> +#include <string.h> +#include <errno.h> +#include "fs.h" +#include "../vfs.h" +#include "../cache.h" +#include "../../mem/paging.h" +#include "../../mem/frame.h" + +size_t procfs_read(File *file, char *buf, size_t size, off_t offset); +size_t procfs_write(File *file, char *buf, size_t size, off_t offset); +int procfs_readdir(File *file, DirEnt *dent, off_t index); +int procfs_open(File *file); + +FileOps procfsFileOps = { + .read = procfs_read, + .write = procfs_write, + .readdir = procfs_readdir, + .open = procfs_open, +}; + +/* Read a file */ +size_t +procfs_read(File *file, char *buf, size_t size, off_t offset) +{ + if (offset > file->inode->size) + return 0; + if (size + offset > file->inode->size) + size = file->inode->size - offset; + + uint16_t min; + size_t count = 0; + Page *page; + page_t oldPage; + while (size) { + min = (size > 0x1000) ? 0x1000 : size; + page = page_find(file->inode, offset); + if (!page) + break; + acquire(&quickPageLock); + oldPage = quick_page(page->frame); + memcpy(buf + count, QUICK_PAGE, min); + quick_page(oldPage); + release(&quickPageLock); + size -= min; + count += min; + } + return count; +} + +/* Write a file */ +size_t +procfs_write(File *file, char *buf, size_t size, off_t offset) +{ + if (size + offset > file->inode->size) + file->inode->size = size + offset; + + uint16_t min; + size_t count = 0; + Page *page; + page_t oldPage; + while (size) { + min = (size > 0x1000) ? 0x1000 : size; + page = page_find(file->inode, offset); + if (!page) + page = page_create(file->inode, alloc_frames(1), + offset); + acquire(&quickPageLock); + oldPage = quick_page(page->frame); + memcpy(QUICK_PAGE, buf + count, min); + quick_page(oldPage); + release(&quickPageLock); + size -= min; + count += min; + } + return count; +} + +/* Read a directory entry */ +int +procfs_readdir(File *file, DirEnt *dent, off_t index) +{ + DirEntry *de; + + if (!index--) { + dent->ino = file->inode->ino; + dent->type = DT_DIR; + dent->namelen = 2; + strncpy(dent->name, ".", dent->namelen); + return 0; + } + + for (de = file->inode->dirEntries; de && index; de = de->next, index--); + if (!de) + return -ENOENT; + dent->ino = de->inode->ino; + if (S_ISBLK(de->inode->mode)) + dent->type = DT_BLK; + if (S_ISCHR(de->inode->mode)) + dent->type = DT_CHR; + if (S_ISDIR(de->inode->mode)) + dent->type = DT_DIR; + if (S_ISFIFO(de->inode->mode)) + dent->type = DT_FIFO; + if (S_ISLNK(de->inode->mode)) + dent->type = DT_LNK; + if (S_ISREG(de->inode->mode)) + dent->type = DT_REG; + if (S_ISSOCK(de->inode->mode)) + dent->type = DT_SOCK; + dent->namelen = strnlen(de->name, NAME_MAX) + 1; + strncpy(dent->name, de->name, NAME_MAX); + return 0; +} + +/* Open a file */ +int +procfs_open(File *file) +{ + return 0; +} diff --git a/vfs/procfs/fs.h b/vfs/procfs/fs.h new file mode 100644 index 0000000..346c1b3 --- /dev/null +++ b/vfs/procfs/fs.h @@ -0,0 +1,13 @@ +#ifndef KERNEL_VFS_PROCFS_H +#define KERNEL_VFS_PROCFS_H + +#include "../vfs.h" + +/* Operations */ +extern SuperOps procfsSuperOps; +extern InodeOps procfsInodeOps; +extern FileOps procfsFileOps; + +extern FileSystemType procfsType; + +#endif diff --git a/vfs/procfs/inode.c b/vfs/procfs/inode.c new file mode 100644 index 0000000..a81c3fc --- /dev/null +++ b/vfs/procfs/inode.c @@ -0,0 +1,69 @@ +/* + * This file contains the functions dealing with ProcFS inodes. The VFS will + * call these when it performs operations on ProcFS Inodes, or is dealing with + * the ProcFS hierarchy. + */ + +#include <string.h> +#include "fs.h" +#include "../vfs.h" +#include "../../mem/heap.h" + +int procfs_create(Inode *inode, DirEntry *entry, mode_t mode); +Inode *procfs_lookup(Inode *inode, const char *name); +int procfs_mkdir(Inode *inode, DirEntry *entry, mode_t mode); +int procfs_rmdir(Inode *inode, DirEntry *entry); +int procfs_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev); +int procfs_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde); + +InodeOps procfsInodeOps = { + .create = procfs_create, + .lookup = procfs_lookup, + .mkdir = procfs_mkdir, + .rmdir = procfs_rmdir, + .mknod = procfs_mknod, + .rename = procfs_rename, +}; + +/* Create a file */ +int +procfs_create(Inode *inode, DirEntry *entry, mode_t mode) +{ + return 0; +} + + +/* Look up a file */ +Inode * +procfs_lookup(Inode *inode, const char *name) +{ + return NULL; +} + +/* Make a directory */ +int +procfs_mkdir(Inode *inode, DirEntry *entry, mode_t mode) +{ + return 0; +} + +/* Remove a directory */ +int +procfs_rmdir(Inode *inode, DirEntry *entry) +{ + return 0; +} + +/* Make a node */ +int +procfs_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev) +{ + return 0; +} + +/* Rename/mode a directory entry */ +int +procfs_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde) +{ + return 0; +} diff --git a/vfs/procfs/super.c b/vfs/procfs/super.c new file mode 100644 index 0000000..dcfa871 --- /dev/null +++ b/vfs/procfs/super.c @@ -0,0 +1,77 @@ +/* + * This file controls the superblock for ProcFS. It supports mounting new ProcFS + * filesystems. The VFS will use the calls here when dealing directly with the + * filesystem structure. + */ + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include "fs.h" +#include "../vfs.h" +#include "../super.h" +#include "../inode.h" +#include "../../mem/heap.h" + +Inode *procfs_mount(FileSystemType *type, int flags, + const char *dev, void *data); +Inode *procfs_alloc_inode(SuperBlock *sb); + +FileSystemType procfsType = { + .name = "ProcFS", + .mount = procfs_mount, +// .kill_sb = procfs_kill_sb, +}; + +SuperOps procfsSuperOps = { + .alloc_inode = procfs_alloc_inode, +// .free_inode = procfs_free_inode, +// .write_inode = procfs_write_inode, +// .delete_inode = procfs_delete_inode, +}; + +Inode procfsRoot = { + .mode = S_IFDIR | 0755, + .ops = &procfsInodeOps, + .fileOps = &procfsFileOps, + .size = 4096, +}; + +/* Mount a ProcFS instance */ +Inode * +procfs_mount(FileSystemType *type, int flags, const char *dev, void *data) +{ + if (type != &procfsType) + return NULL; + + SuperBlock *super = kmalloc(sizeof(SuperBlock)); + + super->type = type; + super->ops = &procfsSuperOps; + init_lock(&super->lock); + + Inode *inode = &procfsRoot; + inode->nlink++; + inode->super = super; + /* FIXME: need an inode per super, or only one super */ + super->root = inode; + /* Never free */ + if (!inode->usage) + inode_get(inode); + + return inode; +} + +/* Allocate an inode */ +Inode * +procfs_alloc_inode(SuperBlock *sb) +{ + Inode *inode = kmalloc(sizeof(Inode)); + init_lock(&inode->lock); + inode->ops = &procfsInodeOps; + inode->fileOps = &procfsFileOps; + inode->super = sb; + inode->nlink = 1; + return inode_get(inode); /* This ensures that the inode is never free */ +} diff --git a/vfs/super.c b/vfs/super.c new file mode 100644 index 0000000..5201110 --- /dev/null +++ b/vfs/super.c @@ -0,0 +1,42 @@ +/* + * This file contains the wrapper functions for the Super Block Operations. It + * just calls the relevant internal functions for the file system, since most of + * them need to know driver specific values, like where to find the file system + * operations. There usually isn't a generic way to handle the functions. + */ + +#include <stdint.h> +#include "vfs.h" + +/* Allocate an inode */ +Inode * +super_alloc_inode(SuperBlock *sb) +{ + if (!sb->ops) + return NULL; + if (!sb->ops->alloc_inode) + return NULL; + return sb->ops->alloc_inode(sb); +} + +/* Search for an Inode in a Super Block's Inode list */ +Inode * +super_find_inode(SuperBlock *sb, ino_t ino) +{ + Inode *inode; + for (inode = sb->inodes; inode; inode = inode->lnext) + if (inode->ino == ino) + break; + return inode; +} + +/* Write an inode to disk */ +int +super_write_inode(SuperBlock *sb, Inode *inode) +{ + if (!sb->ops) + return 0; + if (!sb->ops->write_inode) + return 0; + return sb->ops->write_inode(inode); +} diff --git a/vfs/super.h b/vfs/super.h new file mode 100644 index 0000000..917b7c1 --- /dev/null +++ b/vfs/super.h @@ -0,0 +1,10 @@ +#ifndef KERNEL_VFS_SUPER_H +#define KERNEL_VFS_SUPER_H + +#include "vfs.h" + +Inode *super_alloc_inode(SuperBlock *sb); +Inode *super_find_inode(SuperBlock *sb, ino_t ino); +int super_write_inode(SuperBlock *sb, Inode *inode); + +#endif diff --git a/vfs/tmpfs/file.c b/vfs/tmpfs/file.c new file mode 100644 index 0000000..252c88f --- /dev/null +++ b/vfs/tmpfs/file.c @@ -0,0 +1,126 @@ +/* + * This file controls access to TmpFS Files. It contains the functions called + * by the VFS for any operation on a File struct belonging to TmpFS. + */ + +#include <stddef.h> +#include <string.h> +#include <errno.h> +#include "fs.h" +#include "../vfs.h" +#include "../cache.h" +#include "../../mem/paging.h" +#include "../../mem/frame.h" + +size_t tmpfs_read(File *file, char *buf, size_t size, off_t offset); +size_t tmpfs_write(File *file, char *buf, size_t size, off_t offset); +int tmpfs_readdir(File *file, DirEnt *dent, off_t index); +int tmpfs_open(File *file); + +FileOps tmpfsFileOps = { + .read = tmpfs_read, + .write = tmpfs_write, + .readdir = tmpfs_readdir, + .open = tmpfs_open, +}; + +/* Read a file */ +size_t +tmpfs_read(File *file, char *buf, size_t size, off_t offset) +{ + if (offset > file->inode->size) + return 0; + if (size + offset > file->inode->size) + size = file->inode->size - offset; + + uint16_t min; + size_t count = 0; + Page *page; + page_t oldPage; + while (size) { + min = (size > 0x1000) ? 0x1000 : size; + page = page_find(file->inode, offset); + if (!page) + break; + acquire(&quickPageLock); + oldPage = quick_page(page->frame); + memcpy(buf + count, QUICK_PAGE, min); + quick_page(oldPage); + release(&quickPageLock); + size -= min; + count += min; + } + return count; +} + +/* Write a file */ +size_t +tmpfs_write(File *file, char *buf, size_t size, off_t offset) +{ + if (size + offset > file->inode->size) + file->inode->size = size + offset; + + uint16_t min; + size_t count = 0; + Page *page; + page_t oldPage; + while (size) { + min = (size > 0x1000) ? 0x1000 : size; + page = page_find(file->inode, offset); + if (!page) + page = page_create(file->inode, alloc_frames(1), + offset); + acquire(&quickPageLock); + oldPage = quick_page(page->frame); + memcpy(QUICK_PAGE, buf + count, min); + quick_page(oldPage); + release(&quickPageLock); + size -= min; + count += min; + } + return count; +} + +/* Read a directory entry */ +int +tmpfs_readdir(File *file, DirEnt *dent, off_t index) +{ + DirEntry *de; + + if (!index--) { + dent->ino = file->inode->ino; + dent->type = DT_DIR; + dent->namelen = 2; + strncpy(dent->name, ".", dent->namelen); + return 0; + } + + for (de = file->inode->dirEntries; de && index; de = de->next, index--); + if (!de) + return -ENOENT; + dent->ino = de->inode->ino; + if (S_ISBLK(de->inode->mode)) + dent->type = DT_BLK; + if (S_ISCHR(de->inode->mode)) + dent->type = DT_CHR; + if (S_ISDIR(de->inode->mode)) + dent->type = DT_DIR; + if (S_ISFIFO(de->inode->mode)) + dent->type = DT_FIFO; + if (S_ISLNK(de->inode->mode)) + dent->type = DT_LNK; + if (S_ISREG(de->inode->mode)) + dent->type = DT_REG; + if (S_ISSOCK(de->inode->mode)) + dent->type = DT_SOCK; + dent->namelen = strnlen(de->name, NAME_MAX) + 1; + strncpy(dent->name, de->name, NAME_MAX); + return 0; +} + +/* Open a file */ +int +tmpfs_open(File *file) +{ + return 0; +} diff --git a/vfs/tmpfs/fs.h b/vfs/tmpfs/fs.h new file mode 100644 index 0000000..104f64d --- /dev/null +++ b/vfs/tmpfs/fs.h @@ -0,0 +1,13 @@ +#ifndef KERNEL_VFS_TMPFS_H +#define KERNEL_VFS_TMPFS_H + +#include "../vfs.h" + +/* Operations */ +extern SuperOps tmpfsSuperOps; +extern InodeOps tmpfsInodeOps; +extern FileOps tmpfsFileOps; + +extern FileSystemType tmpfsType; + +#endif diff --git a/vfs/tmpfs/inode.c b/vfs/tmpfs/inode.c new file mode 100644 index 0000000..5da5408 --- /dev/null +++ b/vfs/tmpfs/inode.c @@ -0,0 +1,69 @@ +/* + * This file contains the functions dealing with TmpFS inodes. The VFS will + * call these when it performs operations on TmpFS Inodes, or is dealing with + * the TmpFS hierarchy. + */ + +#include <string.h> +#include "fs.h" +#include "../vfs.h" +#include "../../mem/heap.h" + +int tmpfs_create(Inode *inode, DirEntry *entry, mode_t mode); +Inode *tmpfs_lookup(Inode *inode, const char *name); +int tmpfs_mkdir(Inode *inode, DirEntry *entry, mode_t mode); +int tmpfs_rmdir(Inode *inode, DirEntry *entry); +int tmpfs_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev); +int tmpfs_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde); + +InodeOps tmpfsInodeOps = { + .create = tmpfs_create, + .lookup = tmpfs_lookup, + .mkdir = tmpfs_mkdir, + .rmdir = tmpfs_rmdir, + .mknod = tmpfs_mknod, + .rename = tmpfs_rename, +}; + +/* Create a file */ +int +tmpfs_create(Inode *inode, DirEntry *entry, mode_t mode) +{ + return 0; +} + + +/* Look up a file */ +Inode * +tmpfs_lookup(Inode *inode, const char *name) +{ + return NULL; +} + +/* Make a directory */ +int +tmpfs_mkdir(Inode *inode, DirEntry *entry, mode_t mode) +{ + return 0; +} + +/* Remove a directory */ +int +tmpfs_rmdir(Inode *inode, DirEntry *entry) +{ + return 0; +} + +/* Make a node */ +int +tmpfs_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev) +{ + return 0; +} + +/* Rename/mode a directory entry */ +int +tmpfs_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde) +{ + return 0; +} diff --git a/vfs/tmpfs/super.c b/vfs/tmpfs/super.c new file mode 100644 index 0000000..ede1961 --- /dev/null +++ b/vfs/tmpfs/super.c @@ -0,0 +1,65 @@ +/* + * This file controls the superblock for TmpFS. It supports mounting new TmpFS + * filesystems. The VFS will use the calls here when dealing directly with the + * filesystem structure. + */ + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include "fs.h" +#include "../vfs.h" +#include "../super.h" +#include "../inode.h" +#include "../../mem/heap.h" + +Inode *tmpfs_mount(FileSystemType *type, int flags, + const char *dev, void *data); +Inode *tmpfs_alloc_inode(SuperBlock *sb); + +FileSystemType tmpfsType = { + .name = "TmpFS", + .mount = tmpfs_mount, +// .kill_sb = tmpfs_kill_sb, +}; + +SuperOps tmpfsSuperOps = { + .alloc_inode = tmpfs_alloc_inode, +// .free_inode = tmpfs_free_inode, +// .write_inode = tmpfs_write_inode, +// .delete_inode = tmpfs_delete_inode, +}; + +/* Mount a TmpFS instance */ +Inode * +tmpfs_mount(FileSystemType *type, int flags, const char *dev, void *data) +{ + if (type != &tmpfsType) + return NULL; + + SuperBlock *super = kmalloc(sizeof(SuperBlock)); + + super->type = type; + super->ops = &tmpfsSuperOps; + init_lock(&super->lock); + + Inode *inode = super_alloc_inode(super); + inode->mode = S_IFDIR | 0755; + super->root = inode; + + return inode; +} + +/* Allocate an inode */ +Inode * +tmpfs_alloc_inode(SuperBlock *sb) +{ + Inode *inode = kmalloc(sizeof(Inode)); + init_lock(&inode->lock); + inode->ops = &tmpfsInodeOps; + inode->fileOps = &tmpfsFileOps; + inode->super = sb; + inode->nlink = 1; + return inode_get(inode); /* This ensures that the inode is never free */ +} diff --git a/vfs/vfs.c b/vfs/vfs.c new file mode 100644 index 0000000..68f4b34 --- /dev/null +++ b/vfs/vfs.c @@ -0,0 +1,680 @@ +/* + * This file controls the Virtual File System for the Kernel. It implements a + * generic File System, which can hook other File Systems. This forms a key + * part of the Kernel, and is used for loading programs and some higher-level + * IPC. This system will dispatch messages to the actual File System tasks in + * user-space. + */ + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include "tmpfs/fs.h" +#include "devfs/fs.h" +#include "procfs/fs.h" +#include "ext2fs/fs.h" +#include "vfs.h" +#include "cache.h" +#include "inode.h" +#include "../mem/heap.h" +#include "../task/task.h" + +FileSystemType *fsTypes; + +/* Register a File System Type */ +void +register_fstype(FileSystemType *fstype) +{ + if (!fsTypes) { + fsTypes = fstype; + return; + } + FileSystemType *prev; + for (prev = fsTypes; prev->next; prev = prev->next); + prev->next = fstype; +} + +/* Initialise the Virtual File System */ +void +init_vfs(void) +{ + register_fstype(&tmpfsType); + register_fstype(&devfsType); + register_fstype(&procfsType); + register_fstype(&ext2fsType); + + mount("tmpfs", NULL, "TmpFS", 0, NULL); + mkdir("dev", 0); + mount("devfs", "/dev", "DevFS", 0, NULL); + mkdir("proc", 0); + mount("procfs", "/proc", "ProcFS", 0, NULL); +} + +/* Check if a process has permission for a file */ +int +permission(Inode *inode, int mask) +{ + if (!inode) + return 0; + + int mode = inode->mode; + + if (current->euid == inode->uid) + mode >>= 6; + else if (current->egid == inode->gid) + mode >>= 3; + + if (((mode & mask & 0007) == mask) || super_user()) + return 1; + return 0; +} + +/* Lookup a file path */ +Inode * +lookup(const char *path, CustodyChain *newchain) +{ + if (!current->fs->root) + return NULL; + + char buf[PATH_MAX], *p = buf; + strncpy(buf, path, PATH_MAX); + Inode *ino; + DirEntry *de = NULL; + CustodyChain *chain = create_custody_chain(); + + /* Resolve to absolute/relative root */ + if (*p == '/') { + ino = current->fs->root; + while (*++p == '/'); + } else { + ino = current->fs->cwd; + copy_custody_chain(¤t->fs->cwdCustody, chain); + } + + /* Drop through directories */ + char *curr, *prev = p; + for (curr = p; *curr; curr++) { + if (*curr != '/') + continue; + *curr = '\0'; + if (!strcmp(prev, ".")) { + de = NULL; + } else if (!strcmp(prev, "..")) { + de = NULL; + if (ino != current->fs->root) { + de = remove_custody(chain); + if (!de) + ino = current->fs->root; + } + } else { + de = inode_lookup(ino, prev); + if (!de) { + ino = NULL; + goto end; + } + add_custody(chain, de); + entry_put(de); + } + if (de) { + ino = de->inode; + if (de->mnt) + ino = de->mnt; + } + while (*++curr == '/'); + prev = curr; + } + /* Basename */ + if (!strcmp(prev, ".") || !strcmp(prev, "")) { + if (!S_ISDIR(ino->mode)) { + ino = NULL; + de = NULL; + } + } else if (!strcmp(prev, "..")) { + if (ino != current->fs->root) { + de = remove_custody(chain); + if (!de) + ino = current->fs->root; + } + } else { + de = inode_lookup(ino, prev); + if (!de) { + de = kmalloc(sizeof(DirEntry)); + init_lock(&de->lock); + strncpy(de->name, prev, NAME_MAX); + de->super = ino->super; + de->usage = 1; + } + add_custody(chain, de); + entry_put(de); + } + if (de) { + ino = de->inode; + if (de->mnt) + ino = de->mnt; + if (ino) + inode_get(ino); + } + + /* Copy chain */ + if (newchain) + copy_custody_chain(chain, newchain); +end: + destroy_custody_chain(chain); + return ino; +} + +/* Mount a file system */ +int +mount(const char *src, const char *target, const char *type, + unsigned long flags, void *data) +{ + if (!verify_access(src, strnlen(src, PATH_MAX), PROT_READ)) + return -EFAULT; + if (!verify_access(target, strnlen(target, PATH_MAX), PROT_READ)) + return -EFAULT; + if (!verify_access(type, strnlen(type, 16), PROT_READ)) + return -EFAULT; + + FileSystemType *fsType; + for (fsType = fsTypes; fsType; fsType = fsType->next) + if (!strcmp(fsType->name, type)) + break; + if (!fsType) + return -ENODEV; + + Inode *mnt = fsType->mount(fsType, flags, src, data); + if ((int) mnt < 0) + return (int) mnt; + if (!target && !current->fs->root) { + current->fs->root = inode_get(mnt); + current->fs->cwd = inode_get(mnt); + return 0; + } + /* Find target's DirEntry */ + CustodyChain tmp; + init_custody_chain(&tmp); + Inode *root = lookup(target, &tmp); + if (!root) { + clean_custody_chain(&tmp); + return -ENOENT; + } + if (!S_ISDIR(root->mode)) { + inode_put(root); + clean_custody_chain(&tmp); + return -ENOTDIR; + } + DirEntry *de = entry_get(tmp.end->entry); + de->mnt = inode_get(mnt); + inode_put(root); + clean_custody_chain(&tmp); + return 0; +} + +/* Open a file */ +int +open(const char *name, int flags, ...) +{ + if (!verify_access(name, strnlen(name, PATH_MAX), PROT_READ)) + return -EFAULT; + + /* Allocate file descriptor */ + int fd; + for (fd = 0; fd < NFILES; fd++) + if (!current->files->fd[fd]) + break; + if (fd == NFILES) + return -EMFILE; + + /* Find inode */ + CustodyChain chain; + init_custody_chain(&chain); + Inode *ino = lookup(name, &chain); + va_list args; + va_start(args, flags); + if (!ino) { + if (!(flags & O_CREATE)) { + clean_custody_chain(&chain); + return -ENOENT; + } + /* Create file */ + if (!chain.size) { + clean_custody_chain(&chain); + return -ENOENT; + } + if (chain.end->prev->entry) { + ino = chain.end->prev->entry->inode; + if (chain.end->prev->entry->mnt) + ino = chain.end->prev->entry->mnt; + } else { + ino = current->fs->root; + } + if (!permission(ino, MAY_WRITE)) { + clean_custody_chain(&chain); + return -EACCES; + } + inode_create(ino, chain.end->entry, va_arg(args, mode_t)); + ino = lookup(name, NULL); + } + va_end(args); + + /* Check permissions */ + int perm = 0; + if ((flags & O_ACCMODE) == O_RDONLY) + perm = MAY_READ; + else if ((flags & O_ACCMODE) == O_WRONLY) + perm = MAY_WRITE; + else if ((flags & O_ACCMODE) == O_RDWR) + perm = MAY_READ | MAY_WRITE; + if (!permission(ino, perm)) { + inode_put(ino); + clean_custody_chain(&chain); + return -EACCES; + } + + /* Open file */ + File *file = kmalloc(sizeof(File)); + file->inode = inode_get(ino); + file->uid = ino->uid; + file->gid = ino->gid; + file->flags = flags; + file->mode = ino->mode; + file->pos = 0; + init_lock(&file->lock); + file->ops = ino->fileOps; + file->path = create_custody_chain(); + file->usage = 1; + + copy_custody_chain(&chain, file->path); + clean_custody_chain(&chain); + + int err = file_open(file); + if (err) { + file_put(file); + inode_put(ino); + return err; + } + + current->files->fd[fd] = file; + inode_put(ino); + return fd; +} + +/* Close a file */ +int +close(int fd) +{ + if (fd < 0 || fd >= NFILES) + return -EBADF; + File *file = current->files->fd[fd]; + current->files->fd[fd] = NULL; + if (!file) + return -EBADF; + + file_put(file); + return 0; +} + +/* Read a file */ +size_t +read(int fd, void *buf, size_t count) +{ + if (!verify_access(buf, count, PROT_WRITE)) + return -EFAULT; + if (fd < 0 || fd >= NFILES) + return -EBADF; + File *file = current->files->fd[fd]; + if (!file) + return -EBADF; + + if (S_ISDIR(file->mode)) + return -EISDIR; + + return file_read(file, buf, count); +} + +/* Write a file */ +size_t +write(int fd, void *buf, size_t count) +{ + if (!verify_access(buf, count, PROT_READ)) + return -EFAULT; + if (fd < 0 || fd >= NFILES) + return -EBADF; + File *file = current->files->fd[fd]; + if (!file) + return -EBADF; + + if (S_ISDIR(file->mode)) + return -EISDIR; + + return file_write(file, buf, count); +} + +/* I/O Control */ +int +ioctl(int fd, unsigned long request, ...) +{ + if (fd < 0 || fd >= NFILES) + return -EBADF; + File *file = current->files->fd[fd]; + if (!file) + return -EBADF; + va_list args; + va_start(args, request); + uintptr_t argp = va_arg(args, uintptr_t); + va_end(args); + return file_ioctl(file, request, argp); +} + +/* Move a file's position */ +off_t +lseek(int fd, off_t offset, int whence) +{ + if (fd < 0 || fd >= NFILES) + return -EBADF; + File *file = current->files->fd[fd]; + if (!file) + return -EBADF; + acquire(&file->lock); + + switch (whence) { + case SEEK_SET: + file->pos = offset; + break; + case SEEK_CUR: + file->pos += offset; + break; + case SEEK_END: + file->pos = file->inode->size + offset; + break; + default: + release(&file->lock); + return -EINVAL; + } + release(&file->lock); + return file->pos; +} + +/* Get file status */ +int +stat(const char *pathname, struct stat *statbuf) +{ + if (!verify_access(pathname, strnlen(pathname, PATH_MAX), PROT_READ)) + return -EFAULT; + if (!verify_access(statbuf, sizeof(struct stat), PROT_WRITE)) + return -EFAULT; + Inode *inode = lookup(pathname, NULL); + if (!inode) + return -ENOENT; + if (statbuf) { + statbuf->inode = inode->ino; + statbuf->mode = inode->mode; + statbuf->nlink = inode->nlink; + statbuf->uid = inode->uid; + statbuf->gid = inode->gid; + statbuf->size = inode->size; + } + inode_put(inode); + return 0; +} + +/* Get file status by file descriptor */ +int +fstat(int fd, struct stat *statbuf) +{ + if (!verify_access(statbuf, sizeof(struct stat), PROT_WRITE)) + return -EFAULT; + if (fd < 0 || fd >= NFILES) + return -EBADF; + File *file = current->files->fd[fd]; + if (!file) + return -EBADF; + Inode *inode = file->inode; + statbuf->inode = inode->ino; + statbuf->mode = inode->mode; + statbuf->size = inode->size; + return 0; +} + +/* Get directory entries */ +size_t +getdents(int fd, void *buf, size_t count) +{ + if (!verify_access(buf, count, PROT_WRITE)) + return -EFAULT; + if (count < sizeof(DirEnt)) + return -EINVAL; + + int err; + size_t i, size = 0; + DirEnt *dent = buf; + File *file = current->files->fd[fd]; + if (!file) + return -EBADF; + if (!S_ISDIR(file->inode->mode)) + return -ENOTDIR; + + /* Read as many entries as possible */ + for (i = 0; i < count / sizeof(DirEnt); i++) { + err = file_readdir(file, dent, i); + if (err < 0) + goto out; + size += sizeof(DirEnt) + dent->namelen; + *((char *) buf + size - 1) = '\0'; + dent = (void *) ((char *) buf + size); + } +out: + if (!i) + return err; + return size; +} + +/* Make a directory */ +int +mkdir(const char *pathname, mode_t mode) +{ + if (!verify_access(pathname, strnlen(pathname, PATH_MAX), PROT_READ)) + return -EFAULT; + int err; + CustodyChain chain; + init_custody_chain(&chain); + Inode *inode = lookup(pathname, &chain); + if (inode) { + err = -EEXIST; + goto end; + } + if (!chain.size) { + err = -ENOENT; + goto end; + } + /* Check write permission */ + if (chain.end->prev->entry) { + inode = chain.end->prev->entry->inode; + if (chain.end->prev->entry->mnt) + inode = chain.end->prev->entry->mnt; + } else { + inode = current->fs->root; + } + if (!permission(inode, MAY_WRITE)) { + err = -EACCES; + goto end; + } + + /* Create directory */ + err = inode_mkdir(inode, entry_get(chain.end->entry), mode); +end: + clean_custody_chain(&chain); +// if (inode) +// inode_put(inode); + return err; +} + +/* Remove a directory */ +int +rmdir(const char *pathname) +{ + return 0; +} + +/* Make a special node */ +int +mknod(const char *pathname, mode_t mode, dev_t dev) +{ + if (!verify_access(pathname, strnlen(pathname, PATH_MAX), PROT_READ)) + return -EFAULT; + int err; + CustodyChain chain; + init_custody_chain(&chain); + Inode *inode = lookup(pathname, &chain); + if (inode) { + err = -EEXIST; + goto end; + } + if (!chain.size) { + err = -ENOENT; + goto end; + } + /* Check write permission */ + if (chain.end->prev->entry) { + inode = chain.end->prev->entry->inode; + if (chain.end->prev->entry->mnt) + inode = chain.end->prev->entry->mnt; + } else { + inode = current->fs->root; + } + if (!permission(inode, MAY_WRITE)) { + err = -EACCES; + goto end; + } + + /* Create node */ + err = inode_mknod(inode, entry_get(chain.end->entry), mode, dev); +end: + clean_custody_chain(&chain); +// if (inode) +// inode_put(inode); + return err; +} + +/* Rename/move a file */ +int +rename(const char *oldpath, const char *newpath) +{ + return 0; +} + +/* Duplicate a file descriptor */ +int +dup(int oldfd) +{ + /* Check file descriptor */ + if (oldfd < 0 || oldfd >= NFILES) + return -EBADF; + if (!current->files->fd[oldfd]) + return -EBADF; + + /* Allocate file descriptor */ + int fd; + for (fd = 0; fd < NFILES; fd++) + if (!current->files->fd[fd]) + break; + if (fd == NFILES) + return -EMFILE; + dup2(oldfd, fd); +} + +/* Duplicate a file descriptor to a new file descriptor */ +int +dup2(int oldfd, int newfd) +{ + /* Check file descriptors */ + if (oldfd < 0 || oldfd >= NFILES) + return -EBADF; + if (!current->files->fd[oldfd]) + return -EBADF; + if (newfd < 0 || newfd >= NFILES) + return -EBADF; + + if (newfd == oldfd) + return newfd; + + /* Close old file in newfd */ + if (current->files->fd[newfd]) + close(newfd); + current->files->fd[newfd] = file_get(current->files->fd[oldfd]); + return newfd; +} + +/* Check if a file descriptor refers to a terminal */ +int +isatty(int fd) +{ + if (fd < 0 || fd >= NFILES) + return -EBADF; + File *file = current->files->fd[fd]; + if (!file) + return -EBADF; + if (S_ISCHR(file->mode)) + return 1; + return 0; +} + +/* Change the current working directory */ +int +chdir(const char *path) +{ + if (!verify_access(path, strnlen(path, PATH_MAX), PROT_READ)) + return -EFAULT; + CustodyChain tmp; + init_custody_chain(&tmp); + Inode *node = lookup(path, &tmp); + if (!node) + return -ENOENT; + if (!S_ISDIR(node->mode)) + return -ENOTDIR; + if (!permission(node, MAY_READ)) + return -EACCES; + inode_put(current->fs->cwd); + current->fs->cwd = inode_get(node); + while (current->fs->cwdCustody.size) + remove_custody(¤t->fs->cwdCustody); + copy_custody_chain(&tmp, ¤t->fs->cwdCustody); + clean_custody_chain(&tmp); +// inode_put(node); + return 0; +} + +/* Change the current root */ +int +chroot(const char *path) +{ + if (!verify_access(path, strnlen(path, PATH_MAX), PROT_READ)) + return -EFAULT; + Inode *node = lookup(path, NULL); + if (!node) + return -ENOENT; + if (!S_ISDIR(node->mode)) + return -ENOTDIR; + if (!permission(node, MAY_READ)) + return -EACCES; + inode_put(current->fs->root); + current->fs->root = inode_get(node); +// inode_put(node); + return 0; +} + +/* Get the current working directory's path */ +char * +getcwd(char *buf, size_t size) +{ + if (!verify_access(buf, size, PROT_WRITE)) + return (char *) -EFAULT; + return custody_path(¤t->fs->cwdCustody, buf, size); +} diff --git a/vfs/vfs.h b/vfs/vfs.h new file mode 100644 index 0000000..ece5679 --- /dev/null +++ b/vfs/vfs.h @@ -0,0 +1,202 @@ +#ifndef KERNEL_VFS_H +#define KERNEL_VFS_H + +#include <stdint.h> +#include <stddef.h> +#include <dirent.h> +#include <sys/stat.h> +#include "../spinlock.h" +#include "../mem/paging.h" + +#define NFILES 32 +#define NAME_MAX 255 +#define PATH_MAX 1024 + +#define MAY_READ 4 +#define MAY_WRITE 2 +#define MAY_EXECUTE 1 + +typedef struct FileSystemType FileSystemType; +typedef struct SuperBlock SuperBlock; +typedef struct SuperOps SuperOps; +typedef struct Inode Inode; +typedef struct InodeOps InodeOps; +typedef struct File File; +typedef struct FileOps FileOps; +typedef struct DirEntry DirEntry; +typedef struct Page Page; +typedef struct PageCache PageCache; +typedef struct Custody Custody; +typedef struct CustodyChain CustodyChain; + +/* Structure for a File System Type */ +struct FileSystemType { + const char *name; + Inode *(*mount)(FileSystemType *, int, const char *, void *); + void (*kill_sb)(SuperBlock *); + pid_t owner; + FileSystemType *next; +}; + +/* Structure for a Super Block */ +struct SuperBlock { + FileSystemType *type; + SuperOps *ops; + Spinlock lock; + Inode *root; + Inode *inodes; /* List of cached inodes */ + size_t cachedInodes; + File *back; + void *data; +}; +struct SuperOps { + Inode *(*alloc_inode)(SuperBlock *); + int (*write_inode)(Inode *); + void (*delete_inode)(Inode *); +}; + +/* Structure for an Inode */ +struct Inode { + ino_t ino; + refcount_t usage; + unsigned short uid, gid; + mode_t mode; + nlink_t nlink; + uint32_t flags; + size_t size; + dev_t dev; + Spinlock lock; + InodeOps *ops; + FileOps *fileOps; + SuperBlock *super; + union { + DirEntry *dirEntries; /* List of Directory Entries */ + PageCache *pages; /* List of Pages */ + }; + Inode *lnext; /* Next inode in super list */ +}; +struct InodeOps { + int (*create)(Inode *, DirEntry *, mode_t); + Inode *(*lookup)(Inode *, const char *); + int (*mkdir)(Inode *, DirEntry *, mode_t); + int (*rmdir)(Inode *, DirEntry *); + int (*mknod)(Inode *, DirEntry *, mode_t, dev_t); + int (*rename) (Inode *, DirEntry *, Inode *, DirEntry *); +}; + +/* Structure for an open File */ +struct File { + Inode *inode; + unsigned short uid, gid; + int flags; + mode_t mode; + off_t pos; + Spinlock lock; + FileOps *ops; + CustodyChain *path; + refcount_t usage; +}; +struct FileOps { + off_t (*lseek)(File *, off_t, int); + size_t (*read)(File *, char *, size_t, off_t); + size_t (*write)(File *, char *, size_t, off_t); + int (*ioctl)(File *, unsigned long, uintptr_t); + int (*readdir)(File *, DirEnt *, off_t); + int (*open)(File *); + int (*flush)(File *); + int (*release)(File *); + void (*mmap)(File *, void *, size_t, off_t); +}; + +/* Structure for a Directory Entry */ +struct DirEntry { + DirEntry *next; + Inode *inode, *mnt; + uint32_t hash; + char name[NAME_MAX]; + Spinlock lock; + SuperBlock *super; + refcount_t usage; + DirEntry *lnext; /* Next directory entry in global list */ +}; + +/* Structure for a Page in a block cache */ +struct Page { + page_t frame; + off_t offset; + refcount_t usage; +}; +struct PageCache { + Page *page; + PageCache *next; +}; + +/* Custody Chains */ +struct Custody { + Custody *prev, *next; + CustodyChain *chain; + DirEntry *entry; +}; +struct CustodyChain { + Custody *start, *end; + size_t size; + Spinlock lock; +}; + +/* File System Namespace */ +typedef struct FileSystem { + Inode *cwd; + CustodyChain cwdCustody; + Inode *root; + refcount_t usage; +} FileSystem; + +/* Files Namespace */ +typedef struct Files { + File *fd[NFILES]; + refcount_t usage; +} Files; + +void init_vfs(void); +void register_fstype(FileSystemType *fstype); +Inode *lookup(const char *name, CustodyChain *chain); + +int open(const char *name, int flags, ...); /* mode_t mode */ +int close(int fd); +size_t read(int fd, void *buf, size_t count); +size_t write(int fd, void *buf, size_t count); +int ioctl(int fd, unsigned long request, ...); +off_t lseek(int fd, off_t offset, int whence); +int stat(const char *pathname, struct stat *statbuf); +size_t getdents(int fd, void *buf, size_t count); +int mkdir(const char *pathname, mode_t mode); +int rmdir(const char *pathname); +int mknod(const char *pathname, mode_t mode, dev_t dev); +int rename(const char *oldpath, const char *newpath); +int dup(int oldfd); +int dup2(int oldfd, int newfd); +int mount(const char *src, const char *target, const char *type, + unsigned long flags, void *data); +int chdir(const char *path); +int chroot(const char *path); +char *getcwd(char *buf, size_t size); + +void init_custody_chain(CustodyChain *chain); +CustodyChain *create_custody_chain(void); +void clean_custody_chain(CustodyChain *chain); +void destroy_custody_chain(CustodyChain *chain); +void copy_custody_chain(CustodyChain *chain, CustodyChain *newchain); +void add_custody(CustodyChain *chain, DirEntry *entry); +DirEntry *remove_custody(CustodyChain *chain); +char *custody_path(CustodyChain *chain, char *buf, size_t size); + +File *file_get(File *file); +void file_put(File *file); +size_t file_read(File *file, char *buf, size_t size); +size_t file_write(File *file, char *buf, size_t size); +int file_ioctl(File *file, unsigned long reqeust, uintptr_t argp); +int file_readdir(File *file, DirEnt *dent, off_t index); +int file_open(File *file); +void file_mmap(File *file, void *buf, size_t len, off_t offset); + +#endif