// 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_mount_t *mounts; // 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 (fsType < 0 || fsType >= VFS_MAX_FS || !fses[fsType]) return -1; // invalid filesystem type if (!strcmp("/", mountPoint)) { if (mounts) return -2; // root already mounted void *super = fses[fsType]->mount_super(maj, min); if (!super) return -3; // didn't mount superblock vfs_mount_t *mnt = New(vfs_mount_t); mnt->refs = 0; mnt->fs = fsType; mnt->major = maj; mnt->minor = min; mnt->super = super; mnt->next = NULL; mnt->prev = NULL; mnt->mountPoint = kmalloc(2); strcpy(mnt->mountPoint, mountPoint); mnt->vfs_mount_inode = 0; // root not mounted on another fs mnt->vfs_root_inode = ((vfs_inode_t)maj << 40) | ((vfs_inode_t)min << 32) | fses[fsType]->get_root_inode(mnt); mnt->vfs_up_inode = mnt->vfs_root_inode; mounts = mnt; return 0; // successfully mounted root } if (mountPoint[0] != '/') return -3; // mount point must be absolute if (!mounts) return -4; // no root dir yet vfs_stat_t stat; if (vfs_stat(mountPoint, &stat)) return -5; // error statting if (stat.type != VFS_FT_DIR) return -6; // mountPoint not a directory vfs_inode_t vfs_inode = vfs_get_inode_number(mountPoint); vfs_inode_t updir = vfs_entry_lookup(vfs_inode, ".."); if (!updir || (updir & 0x8000000000000000ULL)) return -7; void *super = fses[fsType]->mount_super(maj, min); if (!super) return -3; // didn't mount superblock vfs_mount_t *mnt = New(vfs_mount_t); mnt->refs = 0; mnt->super = super; mnt->fs = fsType; mnt->major = maj; mnt->minor = min; mnt->mountPoint = kmalloc(strlen(mountPoint) + 1); strcpy(mnt->mountPoint, mountPoint); mnt->vfs_mount_inode = vfs_inode; mnt->vfs_up_inode = updir; mnt->vfs_root_inode = ((vfs_inode_t)maj << 40) | ((vfs_inode_t)min << 32) | fses[fsType]->get_root_inode(mnt); vfs_mount_t *mount = mounts; while (mount->next) mount = mount->next; mount->next = mnt; // add mnt to end of mounts list mnt->prev = mount; mnt->next = NULL; vfs_get_file_addr(updir).mount->refs++; return -256; } int vfs_umount(char *mountPoint) { vfs_inode_t vfs_inode = vfs_get_inode_number(mountPoint); if (!vfs_inode || (vfs_inode & 0x8000000000000000ULL)) return -1; // invalid vfs_mount_t *mnt = mounts; while (mnt) { if (mnt->vfs_root_inode == vfs_inode) // found mount point to unmount { if (mnt->refs) return -2; // open references to mount point; if (mnt->prev) mnt->prev->next = mnt->next; if (mnt->next) mnt->next->prev = mnt->prev; if (mnt == mounts) mounts = NULL; kfree(mnt->mountPoint); kfree(mnt); return 0; } } return -3; // invalid mount point } vfs_inode_t vfs_get_inode_number(char *path) { if (path[0] != '/') return 0; if (!mounts) return 0; return vfs_get_inode_number_rel(mounts->vfs_root_inode, path + 1); } vfs_inode_t vfs_get_inode_number_rel(vfs_inode_t dir_inode, char *path) { char *path_copy = kmalloc(strlen(path) + 1); strcpy(path_copy, path); int stanzas = str_split(path_copy, '/'); char *lookup = path_copy; vfs_inode_t vfs_inode = dir_inode; for (; stanzas > 0; stanzas--) { vfs_inode_t vfs_inode_this_dir = vfs_inode; vfs_inode = (strcmp("", lookup) ? vfs_entry_lookup(vfs_inode, lookup) : vfs_inode_this_dir); vfs_stat_t stat; if (vfs_stat_inode(vfs_inode, &stat)) return 0x8000000000000002ULL; if (stat.type == VFS_FT_SYMLINK) { char *link = kmalloc(4096); vfs_link_deref(vfs_inode, link); if (link[0] == '/') vfs_inode = vfs_get_inode_number(link); else vfs_inode = vfs_get_inode_number_rel(vfs_inode_this_dir, link); kfree(link); } if (!vfs_inode || (vfs_inode & 0x8000000000000000ULL)) { kfree(path_copy); return 0; } lookup = str_advance(lookup); } kfree(path_copy); return vfs_inode; } // look for entry in dir_inode, return the vfs_inode for the entry vfs_inode_t vfs_entry_lookup(vfs_inode_t dir_inode, char *entry) { vfs_open_file_t *open_dir = vfs_open_dir_inode(dir_inode); if (!open_dir) return 0x8000000000000001ULL; vfs_dir_entry_t dentry; while (!vfs_read_dir(open_dir, &dentry)) { if (!strcmp(dentry.name, entry)) { vfs_close_dir(open_dir); return vfs_real_inode((dir_inode & 0xFFFFFFFF00000000ULL) | dentry.inode_number); } } vfs_close_dir(open_dir); return 0x8000000000000002ULL; } // return the "real" vfs inode number (inode of the root of // a mounted filesystem instead of the inode of the folder // that the filesystem is mounted to) vfs_inode_t vfs_real_inode(vfs_inode_t vfs_inode) { vfs_mount_t *mnt = mounts; while (mnt) { if (mnt->vfs_mount_inode == vfs_inode) return mnt->vfs_root_inode; mnt = mnt->next; } return vfs_inode; } // translage a vfs inode number into a mount pointer and relative // inode for a mounted filesystem vfs_file_addr_t vfs_get_file_addr(vfs_inode_t vfs_inode) { vfs_inode = vfs_real_inode(vfs_inode); vfs_file_addr_t addr = {mounts, vfs_inode & 0xFFFFFFFF}; vfs_mount_t *mnt = mounts; while (mnt) { if ( (mnt->vfs_root_inode & 0xFFFFFFFF00000000ULL) == (vfs_inode & 0xFFFFFFFF00000000ULL) ) { addr.mount = mnt; return addr; } mnt = mnt->next; } return addr; } int vfs_mount_count() { vfs_mount_t *mnt = mounts; int count = 0; while (mnt) { count++; mnt = mnt->next; } return count; } vfs_mount_t *vfs_get_mount(int index) { vfs_mount_t *mnt = mounts; while (mnt && index) { mnt = mnt->next; index--; } if (!index) return mnt; return NULL; } int vfs_free_inodes(vfs_mount_t *mount) { if (fses[mount->fs]->free_inodes) return fses[mount->fs]->free_inodes(mount); return -1; } int vfs_total_inodes(vfs_mount_t *mount) { if (fses[mount->fs]->total_inodes) return fses[mount->fs]->total_inodes(mount); return -1; } int vfs_free_blocks(vfs_mount_t *mount) { if (fses[mount->fs]->free_blocks) return fses[mount->fs]->free_blocks(mount); return -1; } int vfs_total_blocks(vfs_mount_t *mount) { if (fses[mount->fs]->total_blocks) return fses[mount->fs]->total_blocks(mount); return -1; } vfs_inode_t vfs_alloc_inode(vfs_mount_t *mount) { if (fses[mount->fs]->alloc_inode) { u32_t fs_inode = fses[mount->fs]->alloc_inode(mount); if (!fs_inode) return 0; return (mount->vfs_root_inode & 0xFFFFFFFF00000000ULL) | fs_inode; } return 0; } int vfs_alloc_block(vfs_mount_t *mount) { if (fses[mount->fs]->alloc_block) return fses[mount->fs]->alloc_block(mount); return 0; } int vfs_link_deref(vfs_inode_t vfs_inode, char *link) { vfs_stat_t stat; if (vfs_stat_inode(vfs_inode, &stat)) return -1; if (stat.type != VFS_FT_SYMLINK) return -2; vfs_file_addr_t addr = vfs_get_file_addr(vfs_inode); if (fses[addr.mount->fs]->link_deref) return fses[addr.mount->fs]->link_deref(addr.mount, addr.inode, link); return -3; } // 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_inode_t vfs_inode = vfs_get_inode_number(file); if (!vfs_inode || (vfs_inode & 0x8000000000000000ULL)) return -256; return vfs_stat_inode(vfs_inode, stat); } int vfs_stat_inode(vfs_inode_t vfs_inode, vfs_stat_t *stat) { vfs_file_addr_t addr = vfs_get_file_addr(vfs_inode); if (fses[addr.mount->fs]->stat) return fses[addr.mount->fs]->stat(addr.mount, addr.inode, stat); return -257; } vfs_open_file_t *vfs_open_dir(char *path) { vfs_inode_t vfs_inode = vfs_get_inode_number(path); if (!vfs_inode || (vfs_inode & 0x8000000000000000ULL)) return NULL; return vfs_open_dir_inode(vfs_inode); } vfs_open_file_t *vfs_open_dir_inode(vfs_inode_t vfs_inode) { vfs_file_addr_t addr = vfs_get_file_addr(vfs_inode); vfs_open_file_t *open_dir = New(vfs_open_file_t); if (fses[addr.mount->fs]->open_dir && !(fses[addr.mount->fs]->open_dir(addr.mount, addr.inode, open_dir))) { open_dir->mount = addr.mount; return open_dir; } kfree(open_dir); return NULL; } int vfs_read_dir(vfs_open_file_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_file_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; } vfs_open_file_t *vfs_open_block_file(char *path) { vfs_inode_t vfs_inode = vfs_get_inode_number(path); if (!vfs_inode || (vfs_inode & 0x8000000000000000ULL)) return NULL; return vfs_open_block_file_inode(vfs_inode); } vfs_open_file_t *vfs_open_block_file_inode(vfs_inode_t vfs_inode) { vfs_file_addr_t addr = vfs_get_file_addr(vfs_inode); vfs_open_file_t *open_file = New(vfs_open_file_t); if (fses[addr.mount->fs]->open_block_file && !(fses[addr.mount->fs]->open_block_file(addr.mount, addr.inode, open_file))) { open_file->mount = addr.mount; return open_file; } kfree(open_file); return NULL; } int vfs_read_block_file(vfs_open_file_t *open_file, void *buffer) { if (fses[open_file->mount->fs]->read_block_file) return fses[open_file->mount->fs]->read_block_file(open_file->mount, open_file, buffer); return 0; } int vfs_block_file_seek(vfs_open_file_t *open_file, u32_t block_number) { if (fses[open_file->mount->fs]->block_file_seek) return fses[open_file->mount->fs]->block_file_seek(open_file->mount, open_file, block_number); return -1; } int vfs_close_block_file(vfs_open_file_t *open_file) { int status = 0; if (fses[open_file->mount->fs]->close_block_file) status = fses[open_file->mount->fs]->close_block_file(open_file->mount, open_file); kfree(open_file); return status; }