433 lines
11 KiB
C++
433 lines
11 KiB
C++
// vfs.cpp
|
|
// Author: Josh Holtrop
|
|
// Date: 08/22/04
|
|
// Modified: 12/21/04
|
|
|
|
#define _HOS_CPP_ _HOS_CPP_
|
|
|
|
extern "C"
|
|
{
|
|
#include "hos_defines.h"
|
|
#include "display/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!");
|
|
FileSystem fs;
|
|
FileSystem *ext2 = new Ext2();
|
|
FileSystem *jfs = new JoshsFS();
|
|
fs.out();
|
|
ext2->out();
|
|
jfs->out();
|
|
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 = (vfs_mount_t *) 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 = (char *) 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 = (vfs_mount_t *) New(vfs_mount_t);
|
|
mnt->refs = 0;
|
|
mnt->super = super;
|
|
mnt->fs = fsType;
|
|
mnt->major = maj;
|
|
mnt->minor = min;
|
|
mnt->mountPoint = (char *) 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 = (char *) 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 = (char *) 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_free_inode(vfs_mount_t *mount, u32_t inode_number)
|
|
{
|
|
if (fses[mount->fs]->free_inode)
|
|
return fses[mount->fs]->free_inode(mount, inode_number);
|
|
return -256;
|
|
}
|
|
|
|
u32_t vfs_alloc_block(vfs_mount_t *mount)
|
|
{
|
|
if (fses[mount->fs]->alloc_block)
|
|
return fses[mount->fs]->alloc_block(mount);
|
|
return 0;
|
|
}
|
|
|
|
int vfs_free_block(vfs_mount_t *mount, u32_t block_number)
|
|
{
|
|
if (fses[mount->fs]->free_block)
|
|
return fses[mount->fs]->free_block(mount, block_number);
|
|
return -256;
|
|
}
|
|
|
|
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 = (vfs_open_file_t *) 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 = (vfs_open_file_t *) 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;
|
|
}
|
|
|