// vfs.cpp // Virtual file system subsystem for HOS // Author: Josh Holtrop // Date: 05/10/05 // Modified: 12/26/05 extern "C" { #include "hos_defines.h" #include "display/kout.h" #include "functions.h" #include "lang/lang.h" } #include "vfs.h" #include "fs/FileSystem.h" #include "fs/VFSMount.h" #include "fs/ext2/ext2.h" #include "lang/vector.h" #include "lang/string.h" #include "devices.h" /* Internal module function prototypes */ FileSystem *vfs_attempt_mount(device_t device, char *fsType); mount_func_t vfs_get_mount_func(const string & fsName); inode_num_t vfs_get_inode(const string & path); inode_num_t vfs_get_dir_inode(const string & path); inode_num_t vfs_get_inode_rel(const string & path, inode_num_t start_dir); inode_num_t vfs_get_dir_entry_inode(inode_num_t dir, const string & fname); int vfs_stat_inode(inode_num_t inum, vfs_stat_t *statptr); int vfs_link_deref_inode(inode_num_t inum, char *link); VFSMount *vfs_get_mount(u32_t mt_id); vector vfs_split_dirs(const string & path); OpenDirectory *vfs_open_directory_inode(inode_num_t inum, int mode); OpenFile *vfs_open_file_inode(inode_num_t inum, int mode); void vfs_close_directory_real(OpenDirectory *o); void vfs_close_file_real(OpenFile *o); inode_num_t vfs_get_real_inode(inode_num_t inum); /* Global module data members */ vector *mountPoints; vector *fses; u32_t mount_id = 0; /* initialize the VFS module */ int vfs_init() { mountPoints = new vector; fses = new vector; ext2_init(); return 0; } /* register a filesystem driver to the VFS module */ int vfs_register(char *fs, mount_func_t mount_func) { string fsName(fs); if (vfs_get_mount_func(fsName)) return -1; FSHandle fsh = {fsName, mount_func}; fses->add(fsh); return 0; } /* mount a device with a filesystem to a mount point */ int vfs_mount(device_t device, char *fsType, char *mountPoint) { string mountPt(mountPoint); string fsName(fsType); /* Bad filesystem type */ if (!vfs_get_mount_func(fsName)) { kprintf("vfs_mount: %s: unrecognized filesystem\n", fsType); return -501; } if (mountPt == "/") { if (mountPoints->size()) { kprintf("/ must be the first filesystem mounted!\n"); return -1; } FileSystem *fs = vfs_attempt_mount(device, fsType); /* Couldn't mount root */ if (fs == NULL) return -2; mountPoints->add(new VFSMount(device, fsName, fs, mountPt, 0, fs->getRootInodeNumber())); mount_id++; return 0; } inode_num_t mtInode = vfs_get_inode(mountPt); /* Invalid mount point */ if (mtInode == 0) return -3; FileSystem *fs = vfs_attempt_mount(device, fsType); /* Couldn't mount */ if (fs == NULL) return -2; inode_num_t thisInode = (((u64_t)(mount_id++)) << 32) | fs->getRootInodeNumber(); mountPoints->add(new VFSMount(device, fsName, fs, mountPt, mtInode, thisInode)); return 0; } /* get mount information about the mount point */ int vfs_get_mount_info(unsigned int mountNum, vfs_mount_info_t *infoptr) { if (mountNum >= mountPoints->size()) return -1; VFSMount *mt = (*mountPoints)[mountNum]; strcpy(infoptr->fs, mt->myFSType.data()); strcpy(infoptr->mountPoint, mt->myMountPoint.data()); infoptr->totalBlocks = mt->myFS->totalBlocks(); infoptr->freeBlocks = mt->myFS->freeBlocks(); infoptr->totalInodes = mt->myFS->totalInodes(); infoptr->freeInodes = mt->myFS->freeInodes(); return 0; } /* unmount a device */ int vfs_umount(device_t dev) { unsigned int max = mountPoints->size(); for (unsigned int i = 0; i < max; i++) { if ((*mountPoints)[i]->myDev == dev) { if ((*mountPoints)[i]->umount_safe()) return -502; delete (*mountPoints)[i]; mountPoints->remove(i); return 0; } } return -501; } /* stat a filename */ int vfs_stat(char *name, vfs_stat_t *buff) { string sname = string(name); inode_num_t inum; if ((inum = vfs_get_inode(sname)) == 0ULL) return -502; return vfs_stat_inode(inum, buff); } /* dereference a symbolic link */ int vfs_link_deref(char *name, char *buff) { string sname = string(name); inode_num_t inum; if ((inum = vfs_get_inode(sname)) == 0ULL) return -502; return vfs_link_deref_inode(inum, buff); } void *vfs_open_dir(char *name) { string sname(name); inode_num_t inum = vfs_get_inode(sname); return (void *) vfs_open_directory_inode(inum, VFS_MODE_READ); } void *vfs_open_file(char *name, int mode) { string sname(name); inode_num_t inum = vfs_get_inode(sname); return (void *) vfs_open_file_inode(inum, mode); } void vfs_close_dir(void *o) { vfs_close_directory_real((OpenDirectory *) o); } void vfs_close_file(void *o) { vfs_close_file_real((OpenFile *) o); } int vfs_read_dir(void *o, vfs_dir_entry_t *dirent) { OpenDirectory *odir = (OpenDirectory *) o; return odir->read(dirent); } int vfs_seek_dir(void *o, int pos, int mode) { OpenDirectory *odir = (OpenDirectory *) o; return odir->seek(pos, mode); } int vfs_read_file(void *o) { OpenFile *ofile = (OpenFile *) o; return ofile->read(); } int vfs_read_file_block(void *o, void *buf, u32_t num) { OpenFile *ofile = (OpenFile *) o; return ofile->read(buf, num); } int vfs_write_file(void *o, int chr) { OpenFile *ofile = (OpenFile *) o; return ofile->write(chr); } int vfs_write_file_block(void *o, void *buf, u32_t num) { OpenFile *ofile = (OpenFile *) o; return ofile->write(buf, num); } int vfs_seek_file(void *o, int pos, int mode) { OpenFile *ofile = (OpenFile *) o; return ofile->seek(pos, mode); } /**************************** Internal functions ****************************/ /* "attempt" a mount */ FileSystem *vfs_attempt_mount(device_t device, char *fsType) { mount_func_t mount_func; if (( mount_func = vfs_get_mount_func(string(fsType)) )) return mount_func(device); else return NULL; } /* Trace a file name to make an inode */ inode_num_t vfs_get_inode(const string & path) { return vfs_get_inode_rel(path, 0ULL); } /* Just get the inode of the directory containing the file */ inode_num_t vfs_get_dir_inode(const string & path) { vector paths = vfs_split_dirs(path); string dirPath(""); for (unsigned int i = 0; i < paths.size()-1; i++) { dirPath += "/"; dirPath += paths[i]; } return vfs_get_inode_rel(dirPath, 0ULL); } /* Trace file name to inode starting from this directory (recursive) */ inode_num_t vfs_get_inode_rel(const string & path, inode_num_t start_dir) { vector paths = vfs_split_dirs(path); unsigned int paths_size = paths.size(); inode_num_t currentInum = start_dir; for (unsigned int i = 0; i < paths_size; i++) { currentInum = vfs_get_dir_entry_inode(currentInum, paths[i]); if (currentInum == 0ULL) return 0ULL; currentInum = vfs_get_real_inode(currentInum); } return currentInum; } /* return the inode of a directory entry in the directory pointed to by dir */ inode_num_t vfs_get_dir_entry_inode(inode_num_t dir, const string & fname) { OpenDirectory *odir = vfs_open_directory_inode(dir, VFS_MODE_READ); if (odir == NULL) return 0ULL; vfs_dir_entry_t dir_ent; inode_num_t inum = 0ULL; while (vfs_read_dir(odir, &dir_ent) == 0) { if (strcmp(dir_ent.name, fname.data()) == 0) { inum = (dir & 0xFFFFFFFF00000000ULL) | dir_ent.inum; break; } } vfs_close_directory_real(odir); return inum; } /* Just split up a string into a vector of strings based on the path * delimeter character ('/'). */ vector vfs_split_dirs(const string & path) { char *sptr = new char[path.size() + 1]; strcpy(sptr, path.data()); char *bptr = sptr; char *cptr = sptr; vector parts; while (*cptr) { if (*cptr == '/') { *cptr = 0; string dir(bptr); if (dir.size() > 0) parts.add(dir); bptr = cptr + 1; } cptr++; } string dir(bptr); if (dir.size() > 0) parts.add(dir); delete sptr; return parts; } /* Return a pointer to the mount function for a certain file system type */ mount_func_t vfs_get_mount_func(const string & fsName) { unsigned int max = fses->size(); for (unsigned int i = 0; i < max; i++) { if (fsName == (*fses)[i].name) return (*fses)[i].mount_func; } return NULL; } /* Return a pointer to a VFSMount by the "mount id" (upper 32 bits of inode) */ VFSMount *vfs_get_mount(u32_t mt_id) { int max = mountPoints->size(); for (int i = 0; i < max; i++) { if (((*mountPoints)[i]->myThisInode >> 32) == mt_id) return (*mountPoints)[i]; } return NULL; } /* FS function: stat a inode, return error code */ int vfs_stat_inode(inode_num_t inum, vfs_stat_t *statptr) { inum = vfs_get_real_inode(inum); VFSMount *mt = vfs_get_mount(inum >> 32); if (mt == NULL) return -501; return mt->myFS->stat((u32_t)inum, statptr); } /* FS function: dereference a link by inode number, return error code */ int vfs_link_deref_inode(inode_num_t inum, char *link) { inum = vfs_get_real_inode(inum); VFSMount *mt = vfs_get_mount(inum >> 32); if (mt == NULL) return -501; return mt->myFS->link_deref((u32_t)inum, link); } /* FS function: open a directory based on the inode number */ OpenDirectory *vfs_open_directory_inode(inode_num_t inum, int mode) { inum = vfs_get_real_inode(inum); VFSMount *mt = vfs_get_mount(inum >> 32); if (mt == NULL) return NULL; return mt->myFS->openDirectory((u32_t)inum, mode); } /* FS function: open a file based on the inode number */ OpenFile *vfs_open_file_inode(inode_num_t inum, int mode) { inum = vfs_get_real_inode(inum); VFSMount *mt = vfs_get_mount(inum >> 32); if (mt == NULL) return NULL; return mt->myFS->openFile((u32_t)inum, mode); } /* FS function: close a directory */ void vfs_close_directory_real(OpenDirectory *o) { delete o; } /* FS function: close a file */ void vfs_close_file_real(OpenFile *o) { delete o; } /* return the "real" inode number based on the given one * This function enables inode numbers for directories that have something * mounted in them to be converted to the inode number for the root directory * of that mounted filesystem */ inode_num_t vfs_get_real_inode(inode_num_t inum) { unsigned int mounts = mountPoints->size(); for (unsigned int i = 0; i < mounts; i++) { if ((*mountPoints)[i]->myMountInode == inum) return (*mountPoints)[i]->myThisInode; } return inum; }