summaryrefslogtreecommitdiff
path: root/Source/Kernel/VFS
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Kernel/VFS')
-rw-r--r--Source/Kernel/VFS/BlockCache.class.cpp89
-rw-r--r--Source/Kernel/VFS/BlockCache.class.h32
-rw-r--r--Source/Kernel/VFS/DirectoryNode.class.cpp45
-rw-r--r--Source/Kernel/VFS/DirectoryNode.class.h15
-rw-r--r--Source/Kernel/VFS/FSNode.proto.h4
-rw-r--r--Source/Kernel/VFS/File.class.cpp3
-rw-r--r--Source/Kernel/VFS/FileSystem.proto.h15
-rw-r--r--Source/Kernel/VFS/Part.ns.cpp32
-rw-r--r--Source/Kernel/VFS/Part.ns.h5
-rw-r--r--Source/Kernel/VFS/Partition.class.cpp10
-rw-r--r--Source/Kernel/VFS/Partition.class.h4
-rw-r--r--Source/Kernel/VFS/VFS.ns.cpp107
-rw-r--r--Source/Kernel/VFS/VFS.ns.h16
13 files changed, 349 insertions, 28 deletions
diff --git a/Source/Kernel/VFS/BlockCache.class.cpp b/Source/Kernel/VFS/BlockCache.class.cpp
new file mode 100644
index 0000000..d505a99
--- /dev/null
+++ b/Source/Kernel/VFS/BlockCache.class.cpp
@@ -0,0 +1,89 @@
+#include <DeviceManager/Time.ns.h>
+
+template <typename T>
+BlockCache<T>::BlockCache(T* dev) {
+ m_dev = dev;
+ m_count = 0;
+}
+
+template <typename T>
+BlockCache<T>::~BlockCache() {
+ sync();
+ m_count = 0;
+ delete m_cache;
+ delete m_cacheInfo;
+}
+
+template <typename T>
+void BlockCache<T>::sync() {
+ for (u32int i = 0; i < m_count; i++) {
+ if (m_cacheInfo[i].dirty) m_dev->writeBlocks(m_cacheInfo[i].id, 1, m_cache + (i * m_dev->blockSize()));
+ }
+}
+
+template <typename T>
+void BlockCache<T>::init(u32int count) {
+ m_count = count;
+ m_cacheInfo = new cached_block_t[count];
+ for (u32int i = 0; i < m_count; i++) {
+ m_cacheInfo[i].id = 0;
+ m_cacheInfo[i].lastuse = 0;
+ m_cacheInfo[i].dirty = false;
+ }
+ m_cache = (u8int*)Mem::alloc(m_count * m_dev->blockSize());
+}
+
+template <typename T>
+bool BlockCache<T>::setCache(u64int block, u8int* data, bool dirty) {
+ u32int best = 0;
+ for (u32int i = 0; i < m_count; i++) {
+ if (m_cacheInfo[i].id == block) {
+ best = i;
+ break;
+ }
+ if (m_cacheInfo[i].lastuse < m_cacheInfo[best].lastuse) best = i;
+ }
+ if (best >= m_count) return false;
+ if (m_cacheInfo[best].dirty && (m_cacheInfo[best].id != block or !dirty))
+ m_dev->writeBlocks(m_cacheInfo[best].id, 1, m_cache + (best * m_dev->blockSize()));
+ m_cacheInfo[best].id = block;
+ m_cacheInfo[best].lastuse = Time::uptime();
+ m_cacheInfo[best].dirty = dirty;
+ memcpy(m_cache + (best * m_dev->blockSize()), data, m_dev->blockSize());
+ return true;
+}
+
+template <typename T>
+bool BlockCache<T>::getCache(u64int block, u8int* data) {
+ for (u32int i = 0; i < m_count; i++) {
+ if (m_cacheInfo[i].id == block && m_cacheInfo[i].lastuse != 0) {
+ m_cacheInfo[i].lastuse = Time::uptime();
+ memcpy(data, m_cache + (i * m_dev->blockSize()), m_dev->blockSize());
+ return true;
+ }
+ }
+ return false;
+}
+
+template <typename T>
+bool BlockCache<T>::readBlocks(u64int startblock, u32int count, u8int* data) {
+ if (count == 1) {
+ if (getCache(startblock, data)) return true;
+ if (!m_dev->readBlocks(startblock, count, data)) return false;
+ setCache(startblock, data);
+ return true;
+ } else {
+ return m_dev->readBlocks(startblock, count, data);
+ }
+}
+
+template <typename T>
+bool BlockCache<T>::writeBlocks(u64int startblock, u32int count, u8int* data) {
+ if (m_dev->readOnly()) return false;
+ if (count == 1) {
+ if (!setCache(startblock, data, true)) return m_dev->writeBlocks(startblock, count, data);
+ return true;
+ } else {
+ return m_dev->writeBlocks(startblock, count, data);
+ }
+}
diff --git a/Source/Kernel/VFS/BlockCache.class.h b/Source/Kernel/VFS/BlockCache.class.h
new file mode 100644
index 0000000..0b26180
--- /dev/null
+++ b/Source/Kernel/VFS/BlockCache.class.h
@@ -0,0 +1,32 @@
+#ifndef DEF_BLOCKCACHE_CLASS_H
+#define DEF_BLOCKCACHE_CLASS_H
+
+template <typename T>
+class BlockCache {
+ private:
+ T* m_dev;
+ u32int m_count;
+ struct cached_block_t {
+ u64int id;
+ u32int lastuse;
+ bool dirty;
+ } *m_cacheInfo;
+ u8int* m_cache;
+
+ void sync();
+ bool setCache(u64int block, u8int* data, bool dirty = false);
+ bool getCache(u64int block, u8int* data);
+
+ public:
+ BlockCache(T* dev);
+ ~BlockCache();
+ void init(u32int count);
+
+ bool readBlocks(u64int startblock, u32int count, u8int* data);
+ bool writeBlocks(u64int startblock, u32int count, u8int* data);
+
+};
+
+#include "BlockCache.class.cpp"
+
+#endif
diff --git a/Source/Kernel/VFS/DirectoryNode.class.cpp b/Source/Kernel/VFS/DirectoryNode.class.cpp
index 74c1ff8..55365f0 100644
--- a/Source/Kernel/VFS/DirectoryNode.class.cpp
+++ b/Source/Kernel/VFS/DirectoryNode.class.cpp
@@ -6,6 +6,15 @@ call_t DirectoryNode::m_callTable[] = {
CALL0(0, 0)
};
+DirectoryNode::~DirectoryNode() {
+ if (m_contentLoaded) {
+ for (u32int i = 0; i < m_children.size(); i++) {
+ delete m_children[i];
+ }
+ }
+ if (m_name == "/" && m_parent != NULL) ((DirectoryNode*)(m_parent))->unmount();
+}
+
u32int DirectoryNode::getIdxChildSC(u32int idx) {
if (!runnable()) return (u32int) - 1;
FSNode* n = getChild(idx);
@@ -21,15 +30,29 @@ u32int DirectoryNode::getNameChildSC(u32int name) {
return (u32int) - 1;
}
+u64int DirectoryNode::getLength() {
+ if (m_mounts != 0) return m_mounts->getLength();
+ if (!m_contentLoaded)
+ if (!loadContent())
+ return 0;
+ return m_length;
+}
+
+FSNode* DirectoryNode::getParent() {
+ //if (m_name == "/" and m_parent != 0) return m_parent->getParent();
+ return m_parent;
+}
+
bool DirectoryNode::removable() {
if (!m_contentLoaded)
if (!loadContent())
return false;
- return m_children.empty();
+ return m_children.empty() && (m_mounts == 0);
}
bool DirectoryNode::unmountable() {
if (!m_contentLoaded) return true;
+ if (m_mounts != 0) return false;
for (u32int i = 0; i < m_children.size(); i++) {
if (m_children[i]->type() == NT_DIRECTORY) {
if (!((DirectoryNode*)m_children[i])->unmountable()) return false;
@@ -40,7 +63,22 @@ bool DirectoryNode::unmountable() {
return true;
}
+bool DirectoryNode::mountpointable() {
+ if (!m_contentLoaded)
+ if (!loadContent()) return false;
+ return m_children.empty();
+}
+
+void DirectoryNode::mount(DirectoryNode* childRoot) {
+ m_mounts = childRoot;
+}
+
+void DirectoryNode::unmount() {
+ m_mounts = 0;
+}
+
bool DirectoryNode::loadContent() {
+ if (m_mounts != 0) return m_mounts->loadContent();
if (m_contentLoaded) return true;
bool b = m_fs->loadContents(this);
if (!b) return false;
@@ -50,6 +88,7 @@ bool DirectoryNode::loadContent() {
}
FSNode* DirectoryNode::getChild(u32int index) {
+ if (m_mounts != 0) return m_mounts->getChild(index);
if (!m_contentLoaded)
if (!loadContent())
return NULL;
@@ -58,6 +97,7 @@ FSNode* DirectoryNode::getChild(u32int index) {
}
FSNode* DirectoryNode::getChild(const String& name) {
+ if (m_mounts != 0) return m_mounts->getChild(name);
if (!m_contentLoaded)
if (!loadContent())
return NULL;
@@ -69,18 +109,21 @@ FSNode* DirectoryNode::getChild(const String& name) {
}
FileNode* DirectoryNode::createFile(const String& name) {
+ if (m_mounts != 0) return m_mounts->createFile(name);
FileNode* n = m_fs->createFile(this, name);
m_length = m_children.size();
return n;
}
DirectoryNode* DirectoryNode::createDirectory(const String& name) {
+ if (m_mounts != 0) return m_mounts->createDirectory(name);
DirectoryNode* n = m_fs->createDirectory(this, name);
m_length = m_children.size();
return n;
}
bool DirectoryNode::remove(FSNode* child) {
+ if (m_mounts != 0) return m_mounts->remove(child);
//Check node is indeed one of our childs
if (!m_contentLoaded)
if (!loadContent())
diff --git a/Source/Kernel/VFS/DirectoryNode.class.h b/Source/Kernel/VFS/DirectoryNode.class.h
index 4d9b211..1fc4c52 100644
--- a/Source/Kernel/VFS/DirectoryNode.class.h
+++ b/Source/Kernel/VFS/DirectoryNode.class.h
@@ -8,6 +8,7 @@ class DirectoryNode : public FSNode {
protected:
Vector<FSNode*> m_children;
bool m_contentLoaded;
+ DirectoryNode* m_mounts; //Root node of the filesystem mounted here, null if none
//Syscalls
static call_t m_callTable[];
@@ -19,20 +20,20 @@ class DirectoryNode : public FSNode {
u32int uid = 0, u32int gid = 0) :
FSNode(name, fs, parent, 0, permissions, uid, gid), m_children(), m_contentLoaded(false) {
addCallTable(m_callTable);
+ m_mounts = 0;
}
- virtual ~DirectoryNode() {
- if (m_contentLoaded) {
- for (u32int i = 0; i < m_children.size(); i++) {
- delete m_children[i];
- }
- }
- }
+ virtual ~DirectoryNode();
Vector<FSNode*> &getChildren() { return m_children; } //MUST BE USED ONLY BY FILESYSTEMS
u8int type() { return NT_DIRECTORY; }
+ u64int getLength();
+ FSNode* getParent();
bool removable();
bool unmountable();
+ bool mountpointable();
+ void mount(DirectoryNode* childRoot);
+ void unmount();
bool loadContent();
FSNode* getChild(u32int index);
diff --git a/Source/Kernel/VFS/FSNode.proto.h b/Source/Kernel/VFS/FSNode.proto.h
index 0aafc8a..a46c79d 100644
--- a/Source/Kernel/VFS/FSNode.proto.h
+++ b/Source/Kernel/VFS/FSNode.proto.h
@@ -44,12 +44,12 @@ class FSNode : public Ressource {
virtual bool used() { return false; } //True if file is read/written from/to
const String& getName() { return m_name; }
- u64int getLength() { return m_length; }
+ virtual u64int getLength() { return m_length; }
u32int getPermissions() { return m_permissions; }
u32int getUid() { return m_uid; }
u32int getGid() { return m_gid; }
FileSystem *getFS() { return m_fs; }
- FSNode* getParent() { return m_parent; }
+ virtual FSNode* getParent() { return m_parent; }
//Helper functions
bool readable(User* user = 0);
diff --git a/Source/Kernel/VFS/File.class.cpp b/Source/Kernel/VFS/File.class.cpp
index c5ddcd6..84561a5 100644
--- a/Source/Kernel/VFS/File.class.cpp
+++ b/Source/Kernel/VFS/File.class.cpp
@@ -33,7 +33,7 @@ bool File::open(String filename, u8int mode, FSNode* start, bool vrfyperm) {
if (node == NULL){
if (mode == FM_READ) return false;
node = VFS::createFile(filename, start, vrfyperm);
- if (node == 0) return false;
+ if (node == NULL) return false;
}
if (node->type() != NT_FILE) return false;
@@ -137,6 +137,7 @@ bool File::seek(u64int count, u8int mode) {
}
bool File::eof() {
+ if (!m_valid) return false;
return m_position == m_file->getLength();
}
diff --git a/Source/Kernel/VFS/FileSystem.proto.h b/Source/Kernel/VFS/FileSystem.proto.h
index a614c13..9216219 100644
--- a/Source/Kernel/VFS/FileSystem.proto.h
+++ b/Source/Kernel/VFS/FileSystem.proto.h
@@ -5,20 +5,29 @@
class FSNode;
class FileNode;
class DirectoryNode;
+class FileSystem;
+
+namespace VFS {
+ bool unmount(FileSystem*);
+}
//This abstract class describes a filesystem
class FileSystem {
+ friend bool VFS::unmount(FileSystem*);
+
protected:
- virtual ~FileSystem() {}
+ virtual ~FileSystem();
bool m_isWritable; //false = read only
DirectoryNode* m_rootNode;
- public:
- bool unmount();
+ virtual bool unmount() = 0; //Sync data with the disk
+ public:
bool isWritable() { return m_isWritable; }
DirectoryNode* getRootNode() { return m_rootNode; }
+ virtual String getDevDescription() = 0;
+
//Must be implemented by the filesystem
virtual bool setName(FSNode* node, String name) = 0;
virtual bool setPermissions(FSNode* node, u32int permissions) = 0;
diff --git a/Source/Kernel/VFS/Part.ns.cpp b/Source/Kernel/VFS/Part.ns.cpp
index 6408dbd..9ead6b5 100644
--- a/Source/Kernel/VFS/Part.ns.cpp
+++ b/Source/Kernel/VFS/Part.ns.cpp
@@ -56,4 +56,36 @@ u32int getDeviceID(BlockDevice* dev) {
return (u32int) - 1;
}
+BlockDevice* dev(String _class, u32int idx) {
+ if (_class.empty()) return devices[idx];
+ for (u32int i = 0; i < devices.size(); i++) {
+ String devclass = devices[i]->getClass();
+ if (devclass == _class or (devclass.size() > _class.size() and devclass.substr(0, _class.size()) == _class)) {
+ if (idx == 0) {
+ return devices[i];
+ } else {
+ idx--;
+ }
+ }
+ }
+ return NULL;
+}
+
+Partition* part(BlockDevice* dev, u32int idx) {
+ for (u32int i = 0; i < partitions.size(); i++) {
+ if (partitions[i]->getDevice() == dev) {
+ if (idx == 0) {
+ return partitions[i];
+ } else {
+ idx--;
+ }
+ }
+ }
+ return NULL;
+}
+
+String partIdentifier(Partition* p) {
+ return String("d") += String::number(getDeviceID(p->getDevice())) += String("p") += String::number(p->getPartNumber());
+}
+
}
diff --git a/Source/Kernel/VFS/Part.ns.h b/Source/Kernel/VFS/Part.ns.h
index 40a0fb2..992f6f3 100644
--- a/Source/Kernel/VFS/Part.ns.h
+++ b/Source/Kernel/VFS/Part.ns.h
@@ -13,6 +13,11 @@ namespace Part {
void unregisterDevice(BlockDevice* dev);
u32int getDeviceID(BlockDevice* dev);
+
+ BlockDevice* dev(String _class, u32int idx);
+ Partition* part(BlockDevice* dev, u32int idx);
+
+ String partIdentifier(Partition* p); //Simply to help recognize the partition
}
#endif
diff --git a/Source/Kernel/VFS/Partition.class.cpp b/Source/Kernel/VFS/Partition.class.cpp
index b62f33c..8d7de9b 100644
--- a/Source/Kernel/VFS/Partition.class.cpp
+++ b/Source/Kernel/VFS/Partition.class.cpp
@@ -2,21 +2,23 @@
using namespace CMem; //For memcpy
-Partition::Partition(BlockDevice* dev, u8int partnumber, u64int startblock, u64int blockcount) {
+Partition::Partition(BlockDevice* dev, u8int partnumber, u64int startblock, u64int blockcount)
+: m_cache(dev) {
m_device = dev;
m_partnumber = partnumber;
m_startblock = startblock;
m_blockcount = blockcount;
+ m_cache.init(10 + (m_device->blocks() / 1000 > 100 ? 100 : m_device->blocks() / 1000));
}
bool Partition::readBlocks(u64int startblock, u32int count, u8int *data) {
if (startblock + count > m_startblock + m_blockcount) return false;
- return m_device->readBlocks(startblock - m_startblock, count, data);
+ return m_cache.readBlocks(startblock - m_startblock, count, data);
}
bool Partition::writeBlocks(u64int startblock, u32int count, u8int *data) {
if (startblock + count > m_startblock + m_blockcount) return false;
- return m_device->writeBlocks(startblock - m_startblock, count, data);
+ return m_cache.writeBlocks(startblock - m_startblock, count, data);
}
bool Partition::read(u64int start, u32int length, u8int *data) {
@@ -65,4 +67,4 @@ BlockDevice* Partition::getDevice() { return m_device; }
u64int Partition::getStartBlock() { return m_startblock; }
u64int Partition::getBlockCount() { return m_blockcount; }
u8int Partition::getPartNumber() { return m_partnumber; }
-u32int Partition::getBlockSize() { return m_device->blockSize(); }
+u32int Partition::blockSize() { return m_device->blockSize(); }
diff --git a/Source/Kernel/VFS/Partition.class.h b/Source/Kernel/VFS/Partition.class.h
index 8df1c4f..eb0aafd 100644
--- a/Source/Kernel/VFS/Partition.class.h
+++ b/Source/Kernel/VFS/Partition.class.h
@@ -2,10 +2,12 @@
#define DEF_PARTITION_CLASS_H
#include <Devices/BlockDevice.proto.h>
+#include <VFS/BlockCache.class.h>
class Partition {
private:
BlockDevice* m_device;
+ BlockCache<BlockDevice> m_cache;
u64int m_startblock, m_blockcount;
u8int m_partnumber; //Partition number in partition table of device
@@ -24,7 +26,7 @@ class Partition {
u64int getStartBlock();
u64int getBlockCount();
u8int getPartNumber();
- u32int getBlockSize();
+ u32int blockSize();
inline u64int blocks() { return getBlockCount(); }
};
diff --git a/Source/Kernel/VFS/VFS.ns.cpp b/Source/Kernel/VFS/VFS.ns.cpp
index e95b911..3b59c5a 100644
--- a/Source/Kernel/VFS/VFS.ns.cpp
+++ b/Source/Kernel/VFS/VFS.ns.cpp
@@ -1,19 +1,114 @@
#include "VFS.ns.h"
#include <VFS/FileNode.class.h>
+#include <Vector.class.h>
+#include <VTManager/VirtualTerminal.proto.h>
+
+#include <VFS/Part.ns.h>
+#include <FileSystems/RamFS/RamFS.class.h>
+#include <FileSystems/FAT/FATFS.class.h>
+
+struct local_fs_t {
+ const char* name;
+ mount_callback_t cb;
+} fileSystems[] = {
+ {"fat", FATFS::mount},
+ {0, 0}
+};
+
+FileSystem::~FileSystem() { delete m_rootNode; }
namespace VFS {
-DirectoryNode *rootNode;
+DirectoryNode *rootNode = 0;
+Vector<FileSystem*> filesystems;
+
+DirectoryNode* getRootNode() {
+ return rootNode;
+}
+
+void registerFilesystem(FileSystem* fs) {
+ unregisterFilesystem(fs);
+ filesystems.push(fs);
+ if (rootNode == 0) rootNode = fs->getRootNode();
+}
-//TODO : mount stuff
+void unregisterFilesystem(FileSystem* fs) {
+ for (u32int i = 0; i < filesystems.size(); i++) {
+ if (filesystems[i] == fs) {
+ filesystems[i] = filesystems.back();
+ filesystems.pop();
+ break;
+ }
+ }
+}
-bool setRootNode(DirectoryNode* node) {
- rootNode = node;
+bool unmount(FileSystem* fs) {
+ if (!fs->getRootNode()->unmountable()) return false;
+ if (fs->getRootNode() == rootNode) return false;
+ if (!fs->unmount()) return false;
+ delete fs; //Will automatically delete the root node (destructor is in this file);
return true;
}
-DirectoryNode* getRootNode() {
- return rootNode;
+bool mount(String str, VirtualTerminal* vt, multiboot_info_t *mbd) {
+ Vector<String> fs = str.split(":");
+ DirectoryNode* root;
+ if (fs[0] == "/") {
+ root = NULL;
+ } else {
+ FSNode* n = VFS::find(fs[0]);
+ if (n == NULL) {
+ *vt << "Mountpoint does not exist : " << fs[0] << "\n";
+ return false;
+ }
+ if (n->type() != NT_DIRECTORY) {
+ *vt << "Mountpoint is not a directory : " << fs[0] << "\n";
+ return false;
+ }
+ root = (DirectoryNode*)n;
+ }
+ if (fs[1] == "ramfs") {
+ if (fs.size() > 2) {
+ if (mbd != 0) {
+ module_t *mods = (module_t*)mbd->mods_addr;
+ if (fs[2].toInt() >= mbd->mods_count) {
+ *vt << "Invalid module number for filesystem to mount on " << fs[0] << "\n";
+ return false;
+ }
+ RamFS::mount((u8int*)mods[fs[2].toInt()].mod_start, 1024 * 1024, root);
+ return true;
+ } else {
+ *vt << "Cannot mount kernel modules outside of kernel command line.\n";
+ return false;
+ }
+ } else {
+ RamFS::mount(1024 * 1024, root);
+ return true;
+ }
+ } else {
+ if (fs.size() < 4) {
+ *vt << "Syntax: <mountpoint>:[<dev_class>]:<dev_id>:<part_id>[:<fs_type>[:[ro|rw]]]\n";
+ return false;
+ }
+ if (fs.size() < 5) fs.push("");
+ if (fs.size() < 6) fs.push("ro"); //By default, mount file systems read-only
+ BlockDevice* d = Part::dev(fs[1], fs[2].toInt());
+ Partition* p = Part::part(d, fs[3].toInt());
+ for (u32int i = 0; fileSystems[i].cb != 0; i++) {
+ if (fs[4] == fileSystems[i].name or fs[4] == "") {
+ if (fileSystems[i].cb(p, root, (fs[5] == "rw")) != NULL) {
+ return true;
+ } else if (fs[4] != "") {
+ *vt << "Could not mount filesystem on " << fs[0] << "\n";
+ if (root == NULL) PANIC("Error while mounting root filesystem.");
+ return false;
+ }
+ }
+ }
+ *vt << "Unknown filesystem type for filesystem to mount on " << fs[0] << "\n";
+ if (root == NULL) PANIC("Unknown filesystem type for root file system.");
+ return false;
+ }
}
FSNode* find(const String& path, FSNode* start) {
diff --git a/Source/Kernel/VFS/VFS.ns.h b/Source/Kernel/VFS/VFS.ns.h
index f1d628f..eede728 100644
--- a/Source/Kernel/VFS/VFS.ns.h
+++ b/Source/Kernel/VFS/VFS.ns.h
@@ -1,17 +1,27 @@
#ifndef DEF_VFS_NS_H
#define DEF_VFS_NS_H
+#include <Core/multiboot.wtf.h>
+
#include <VFS/DirectoryNode.class.h>
#include <VFS/FileSystem.proto.h>
+#include <Vector.class.h>
-typedef FileSystem* (* mountcallback)(Partition* partition);
+typedef FileSystem* (* mount_callback_t)(Partition* partition, DirectoryNode* mountpoint, bool readwrite);
namespace VFS {
- void registerMountCallback(mountcallback mcb);
+ extern Vector<FileSystem*> filesystems;
+
+ void registerMountCallback(mount_callback_t mcb);
bool mount(Partition* partition, DirectoryNode *mountpoint);
- bool setRootNode(DirectoryNode* root);
DirectoryNode* getRootNode();
+ void registerFilesystem(FileSystem* fs);
+ void unregisterFilesystem(FileSystem* fs);
+ bool unmount(FileSystem* fs);
+
+ bool mount(String str, VirtualTerminal* logvt, multiboot_info_t* mbd = 0);
+
FSNode* find(const String& path, FSNode* start = 0);
FSNode* createFile(const String& path, FSNode* start = 0, bool vrfyperm = false);
FSNode* createDirectory(const String& path, FSNode* start = 0, bool vrfyperm = false);