hos/kernel/fs/vfs.cpp

414 lines
9.9 KiB
C++

// 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<string> 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<VFSMount *> *mountPoints;
vector<FSHandle> *fses;
u32_t mount_id = 0;
/* initialize the VFS module */
int vfs_init()
{
mountPoints = new vector<VFSMount *>;
fses = new vector<FSHandle>;
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<string> 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<string> 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<string> vfs_split_dirs(const string & path)
{
char *sptr = new char[path.size() + 1];
strcpy(sptr, path.data());
char *bptr = sptr;
char *cptr = sptr;
vector<string> 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;
}