273 lines
6.1 KiB
C
273 lines
6.1 KiB
C
// vfs.c
|
|
// Author: Josh Holtrop
|
|
// Date: 08/22/04
|
|
// Modified: 12/21/04
|
|
|
|
#include "hos_defines.h"
|
|
#include "kout.h"
|
|
#include "fs/vfs.h"
|
|
#include "fs/ext2.h"
|
|
#include "kernel.h"
|
|
#include "mm/vmm.h"
|
|
#include "lang/asmfuncs.h"
|
|
|
|
vfs_fs_t *fses[VFS_MAX_FS]; // a vfs_fs structure for every filesystem we support
|
|
vfs_node_t *vfsMountTree; // points to the root node ('/') of the VFS Mount Tree
|
|
|
|
// basic initialization routine, init all filesystem drivers
|
|
int vfs_init()
|
|
{
|
|
k_check(ext2_init(FS_EXT2), "ext2_init() failed!");
|
|
return 0;
|
|
}
|
|
|
|
// called by a filesystem driver to let us know that it is ready to handle fs requests
|
|
int vfs_register_fs(int fsn, vfs_fs_t *fs)
|
|
{
|
|
if (fsn < 0 || fsn >= VFS_MAX_FS || fses[fsn])
|
|
{
|
|
kprintf("Invalid filesystem register: %d\n", fsn);
|
|
return -1;
|
|
}
|
|
fses[fsn] = fs;
|
|
return 0;
|
|
}
|
|
|
|
// called to mount a block device with a certain filesystem to a part of our VFS Mount Tree
|
|
int vfs_mount(major_t maj, minor_t min, int fsType, char *mountPoint)
|
|
{
|
|
if (mountPoint[0] != '/')
|
|
return -1; // mount point must be absolute
|
|
int l = strlen(mountPoint);
|
|
if (l < 1)
|
|
return -7; // mount point must be non-null string
|
|
if (mountPoint[l-1] != '/')
|
|
return -8; // mount point must end with a '/' character
|
|
if (fsType < 0 || fsType >= VFS_MAX_FS || !fses[fsType])
|
|
return -2; // invalid filesystem type
|
|
if (vfs_find_mount(mountPoint))
|
|
return -5; // mount point already mounted
|
|
void *super = fses[fsType]->mount_super(maj, min);
|
|
if (!super)
|
|
return -6; // filesystem superblock not correctly identified
|
|
vfs_mount_t *mnt = New(vfs_mount_t);
|
|
if (!mnt)
|
|
return -3; // could not allocate memory
|
|
mnt->refs = 0;
|
|
mnt->fs = fsType;
|
|
mnt->major = maj;
|
|
mnt->minor = min;
|
|
mnt->super = super;
|
|
if (vfs_add_mount(mnt, mountPoint))
|
|
{
|
|
kfree(mnt);
|
|
return -4; // could not add mount
|
|
}
|
|
// DEBUG
|
|
ext2_dump_root(mnt);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// invoked by vfs_mount to do the "dirty work" of adding the nodes to the tree
|
|
int vfs_add_mount(vfs_mount_t *mnt, char *mountPoint)
|
|
{
|
|
vfs_node_t *node = vfsMountTree;
|
|
char *mp = mountPoint;
|
|
if (!vfsMountTree)
|
|
{
|
|
vfsMountTree = New(vfs_node_t);
|
|
node = vfsMountTree;
|
|
node->chr = *mp;
|
|
node->up = NULL;
|
|
node->next = NULL;
|
|
node->down = NULL;
|
|
node->mount = NULL;
|
|
}
|
|
for (;;)
|
|
{
|
|
if (node->chr == *mp) // got a match
|
|
{
|
|
mp++;
|
|
if (*mp == 0)
|
|
{
|
|
if (node->mount)
|
|
return -1; // mount point already taken
|
|
node->mount = mnt;
|
|
return 0;
|
|
}
|
|
if (node->down)
|
|
node = node->down;
|
|
else
|
|
{
|
|
node->down = New(vfs_node_t);
|
|
node->down->up = node;
|
|
node = node->down;
|
|
node->next = NULL;
|
|
node->down = NULL;
|
|
node->mount = NULL;
|
|
node->chr = *mp;
|
|
}
|
|
}
|
|
else if (node->next)
|
|
{
|
|
node = node->next;
|
|
}
|
|
else // add a node for this character
|
|
{
|
|
node->next = New(vfs_node_t);
|
|
node->next->up = node->up;
|
|
node = node->next;
|
|
node->next = NULL;
|
|
node->down = NULL;
|
|
node->mount = NULL;
|
|
node->chr = *mp;
|
|
}
|
|
}
|
|
}
|
|
|
|
// lookup a path and return the match structure for the deepest path matched
|
|
vfs_mount_match_t vfs_get_rel_path(char *path)
|
|
{
|
|
vfs_mount_match_t matched = {NULL, 0};
|
|
char *pp = path;
|
|
int length = 0;
|
|
vfs_node_t *node = vfsMountTree;
|
|
while (*pp)
|
|
{
|
|
if (!node)
|
|
break;
|
|
if (node->chr == *pp) // got a character match
|
|
{
|
|
length++;
|
|
if (node->mount) // and there is something mounted here
|
|
{
|
|
matched.mount = node->mount;
|
|
matched.length = length;
|
|
}
|
|
pp++;
|
|
node = node->down;
|
|
}
|
|
else
|
|
node = node->next;
|
|
}
|
|
return matched;
|
|
}
|
|
|
|
// is mountPoint a valid mount point?
|
|
// returns a pointer to the mount structure if so
|
|
vfs_mount_t *vfs_find_mount(char *mountPoint)
|
|
{
|
|
char *mp = mountPoint;
|
|
vfs_node_t *node = vfsMountTree;
|
|
while (node)
|
|
{
|
|
if (node->chr == *mp)
|
|
{
|
|
mp++;
|
|
if (*mp == 0) // we matched the whole mount point string
|
|
return node->mount;
|
|
node = node->down;
|
|
}
|
|
else
|
|
node = node->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// unmount the mountPoint, remove nodes from tree that aren't used anymore
|
|
int vfs_umount(char *mountPoint)
|
|
{
|
|
char *mp = mountPoint;
|
|
vfs_node_t *node = vfsMountTree;
|
|
while (node)
|
|
{
|
|
if (node->chr == *mp)
|
|
{
|
|
mp++;
|
|
if (*mp == 0) // we matched the whole mount point string
|
|
{
|
|
// is there something mounted underneath this directory?
|
|
if (node->down)
|
|
return -3; // yes, don't umount
|
|
if (!node->mount)
|
|
return -4; // no mount here
|
|
if (node->mount->refs)
|
|
return -5; // still refs open to mount
|
|
fses[node->mount->fs]->umount_super(node->mount);
|
|
kfree(node->mount);
|
|
node->mount = NULL;
|
|
return vfs_retire_node(node);
|
|
}
|
|
node = node->down;
|
|
}
|
|
else
|
|
node = node->next;
|
|
}
|
|
return -2; // mount point does not exist
|
|
}
|
|
|
|
// remove this node from the tree if it is not needed anymore
|
|
int vfs_retire_node(vfs_node_t *node)
|
|
{
|
|
if (node->down)
|
|
return 0; // node is part of another mount point, don't remove
|
|
if (node->mount)
|
|
return 0; // something is still mounted here
|
|
if (!node->up)
|
|
{
|
|
kfree(node);
|
|
vfsMountTree = NULL;
|
|
return 0; // we unmounted the root directory
|
|
}
|
|
// 3 cases left: node is first child (or only child), middle child, or last child
|
|
if (node->up->down == node) // first child
|
|
{
|
|
node->up->down = node->next; // re-point parent to next child
|
|
int r = vfs_retire_node(node->up); // retire parent if we were an only-child
|
|
kfree(node);
|
|
return r;
|
|
}
|
|
vfs_node_t *onode = node->up->down; // onode points to first child
|
|
while (onode)
|
|
{
|
|
if (onode->next == node) // onode is child before node
|
|
{
|
|
onode->next = node->next;
|
|
kfree(node);
|
|
return 0;
|
|
}
|
|
onode = onode->next;
|
|
}
|
|
return -1; // node wasn't found following onode
|
|
}
|
|
|
|
|
|
// debug routine to draw the VFS Mount Tree
|
|
void vfs_dump_tree()
|
|
{
|
|
if (!vfsMountTree)
|
|
kprintf("vfsMountTree == NULL!\n");
|
|
else
|
|
vfs_dump_node(vfsMountTree, 0);
|
|
}
|
|
|
|
void vfs_dump_node(vfs_node_t *node, int level)
|
|
{
|
|
int i;
|
|
for (i = 0; i < level; i++)
|
|
{
|
|
putc(' ');
|
|
}
|
|
kprintf("%c (0x%x) mount: 0x%x, up: 0x%x, next:0x%x, down:0x%x\n", node->chr, node, node->mount, node->up, node->next, node->down);
|
|
node = node->down;
|
|
if (!node)
|
|
return;
|
|
while (node)
|
|
{
|
|
vfs_dump_node(node, level + 1);
|
|
node = node->next;
|
|
}
|
|
}
|
|
|