387 lines
9.1 KiB
C++
387 lines
9.1 KiB
C++
// vfs.cpp
|
|
// Virtual file system subsystem for HOS
|
|
// Author: Josh Holtrop
|
|
// Date: 05/10/05
|
|
// Modified: 12/26/05
|
|
|
|
#define _HOS_CPP_ _HOS_CPP_
|
|
|
|
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_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);
|
|
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);
|
|
OpenFile *vfs_open_file_inode(inode_num_t inum);
|
|
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)) == 0)
|
|
return -502;
|
|
return vfs_stat_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);
|
|
}
|
|
|
|
void *vfs_open_file(char *name)
|
|
{
|
|
string sname(name);
|
|
inode_num_t inum = vfs_get_inode(sname);
|
|
return (void *) vfs_open_file_inode(inum);
|
|
}
|
|
|
|
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_write_dir(void *o, vfs_dir_entry_t *dirent)
|
|
{
|
|
OpenDirectory *odir = (OpenDirectory *) o;
|
|
return odir->write(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);
|
|
}
|
|
|
|
/* 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);
|
|
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: open a directory based on the inode number */
|
|
OpenDirectory *vfs_open_directory_inode(inode_num_t inum)
|
|
{
|
|
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);
|
|
}
|
|
|
|
/* FS function: open a file based on the inode number */
|
|
OpenFile *vfs_open_file_inode(inode_num_t inum)
|
|
{
|
|
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);
|
|
}
|
|
|
|
/* 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;
|
|
}
|