// 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/lang.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 } 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 } // stat a file, fills a vfs_stat_t structure with stat information and returns standard status int vfs_stat(char *file, vfs_stat_t *stat) { vfs_mount_match_t match = vfs_get_rel_path(file); if (match.mount == NULL) return -256; if (fses[match.mount->fs]->stat) return fses[match.mount->fs]->stat(match.mount, file + match.length - 1, stat); return -257; } vfs_open_dir_t *vfs_open_dir(char *file) { vfs_mount_match_t match = vfs_get_rel_path(file); if (match.mount == NULL) return NULL; vfs_open_dir_t *open_dir = New(vfs_open_dir_t); if (fses[match.mount->fs]->open_dir && !(fses[match.mount->fs]->open_dir(match.mount, file + match.length - 1, open_dir))) { open_dir->mount = match.mount; return open_dir; } kfree(open_dir); return NULL; } int vfs_read_dir(vfs_open_dir_t *open_dir, vfs_dir_entry_t *dentry) { return !(fses[open_dir->mount->fs]->read_dir && !(fses[open_dir->mount->fs]->read_dir(open_dir->mount, open_dir, dentry))); } int vfs_close_dir(vfs_open_dir_t *open_dir) { int status = 0; if (fses[open_dir->mount->fs]->close_dir) status = fses[open_dir->mount->fs]->close_dir(open_dir->mount, open_dir); kfree(open_dir); return status; } // DEBUG routines 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; } } /******************************************/