123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- #include "VirtualFileSystem.h"
- #include "FileHandle.h"
- #include "FileSystem.h"
- #include <AK/kmalloc.h>
- #include <AK/kstdio.h>
- #include <AK/ktime.h>
- //#define VFS_DEBUG
- static dword encodedDevice(unsigned major, unsigned minor)
- {
- return (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12);
- }
- static VirtualFileSystem* s_the;
- VirtualFileSystem& VirtualFileSystem::the()
- {
- ASSERT(s_the);
- return *s_the;
- }
- static SpinLock* s_vfsLock;
- SpinLock& VirtualFileSystem::lock()
- {
- ASSERT(s_vfsLock);
- return *s_vfsLock;
- }
- void VirtualFileSystem::initializeGlobals()
- {
- s_the = nullptr;
- s_vfsLock = new SpinLock;
- FileSystem::initializeGlobals();
- }
- VirtualFileSystem::VirtualFileSystem()
- {
- #ifdef VFS_DEBUG
- kprintf("[VFS] Constructing VFS\n");
- #endif
- s_the = this;
- m_maxNodeCount = 16;
- m_nodes = reinterpret_cast<Node*>(kmalloc(sizeof(Node) * maxNodeCount()));
- memset(m_nodes, 0, sizeof(Node) * maxNodeCount());
- for (unsigned i = 0; i < m_maxNodeCount; ++i)
- m_nodeFreeList.append(&m_nodes[i]);
- }
- VirtualFileSystem::~VirtualFileSystem()
- {
- kprintf("[VFS] ~VirtualFileSystem with %u nodes allocated\n", allocatedNodeCount());
- }
- auto VirtualFileSystem::makeNode(InodeIdentifier inode) -> RetainPtr<Node>
- {
- auto metadata = inode.metadata();
- if (!metadata.isValid())
- return nullptr;
- CharacterDevice* characterDevice = nullptr;
- if (metadata.isCharacterDevice()) {
- auto it = m_characterDevices.find(encodedDevice(metadata.majorDevice, metadata.minorDevice));
- if (it != m_characterDevices.end()) {
- characterDevice = (*it).value;
- } else {
- kprintf("[VFS] makeNode() no such character device %u,%u\n", metadata.majorDevice, metadata.minorDevice);
- return nullptr;
- }
- }
- auto vnode = allocateNode();
- ASSERT(vnode);
- FileSystem* fileSystem = inode.fileSystem();
- fileSystem->retain();
- vnode->inode = inode;
- vnode->m_cachedMetadata = { };
- #ifdef VFS_DEBUG
- kprintf("makeNode: inode=%u, size=%u, mode=%o, uid=%u, gid=%u\n", inode.index(), metadata.size, metadata.mode, metadata.uid, metadata.gid);
- #endif
- m_inode2vnode.set(inode, vnode.ptr());
- vnode->m_characterDevice = characterDevice;
- return vnode;
- }
- auto VirtualFileSystem::getOrCreateNode(InodeIdentifier inode) -> RetainPtr<Node>
- {
- auto it = m_inode2vnode.find(inode);
- if (it != m_inode2vnode.end())
- return (*it).value;
- return makeNode(inode);
- }
- bool VirtualFileSystem::mount(RetainPtr<FileSystem>&& fileSystem, const String& path)
- {
- ASSERT(fileSystem);
- auto inode = resolvePath(path);
- if (!inode.isValid()) {
- kprintf("[VFS] mount can't resolve mount point '%s'\n", path.characters());
- return false;
- }
- kprintf("mounting %s{%p} at %s (inode: %u)\n", fileSystem->className(), fileSystem.ptr(), path.characters(), inode.index());
- // FIXME: check that this is not already a mount point
- auto mount = make<Mount>(inode, move(fileSystem));
- m_mounts.append(move(mount));
- return true;
- }
- bool VirtualFileSystem::mountRoot(RetainPtr<FileSystem>&& fileSystem)
- {
- if (m_rootNode) {
- kprintf("[VFS] mountRoot can't mount another root\n");
- return false;
- }
- auto mount = make<Mount>(InodeIdentifier(), move(fileSystem));
- auto node = makeNode(mount->guest());
- if (!node->inUse()) {
- kprintf("[VFS] root inode for / is not in use :(\n");
- return false;
- }
- if (!node->inode.metadata().isDirectory()) {
- kprintf("[VFS] root inode for / is not in use :(\n");
- return false;
- }
- m_rootNode = move(node);
- kprintf("[VFS] mountRoot mounted %s{%p}\n",
- m_rootNode->fileSystem()->className(),
- m_rootNode->fileSystem());
- m_mounts.append(move(mount));
- return true;
- }
- auto VirtualFileSystem::allocateNode() -> RetainPtr<Node>
- {
- if (m_nodeFreeList.isEmpty()) {
- kprintf("[VFS] allocateNode has no nodes left\n");
- return nullptr;
- }
- auto* node = m_nodeFreeList.takeLast();
- ASSERT(node->retainCount == 0);
- node->retainCount = 1;
- node->m_vfs = this;
- return adopt(*node);
- }
- void VirtualFileSystem::freeNode(Node* node)
- {
- ASSERT(node);
- ASSERT(node->inUse());
- m_inode2vnode.remove(node->inode);
- node->inode.fileSystem()->release();
- node->inode = InodeIdentifier();
- node->m_characterDevice = nullptr;
- m_nodeFreeList.append(move(node));
- }
- bool VirtualFileSystem::isDirectory(const String& path)
- {
- auto inode = resolvePath(path);
- if (!inode.isValid())
- return false;
- return inode.metadata().isDirectory();
- }
- auto VirtualFileSystem::findMountForHost(InodeIdentifier inode) -> Mount*
- {
- for (auto& mount : m_mounts) {
- if (mount->host() == inode)
- return mount.ptr();
- }
- return nullptr;
- }
- auto VirtualFileSystem::findMountForGuest(InodeIdentifier inode) -> Mount*
- {
- for (auto& mount : m_mounts) {
- if (mount->guest() == inode)
- return mount.ptr();
- }
- return nullptr;
- }
- bool VirtualFileSystem::isRoot(InodeIdentifier inode) const
- {
- return inode == m_rootNode->inode;
- }
- void VirtualFileSystem::enumerateDirectoryInode(InodeIdentifier directoryInode, Function<bool(const FileSystem::DirectoryEntry&)> callback)
- {
- if (!directoryInode.isValid())
- return;
- directoryInode.fileSystem()->enumerateDirectoryInode(directoryInode, [&] (const FileSystem::DirectoryEntry& entry) {
- InodeIdentifier resolvedInode;
- if (auto mount = findMountForHost(entry.inode))
- resolvedInode = mount->guest();
- else
- resolvedInode = entry.inode;
- if (directoryInode.isRootInode() && !isRoot(directoryInode) && entry.name == "..") {
- auto mount = findMountForGuest(entry.inode);
- ASSERT(mount);
- resolvedInode = mount->host();
- }
- callback({ entry.name, resolvedInode });
- return true;
- });
- }
- void VirtualFileSystem::listDirectory(const String& path)
- {
- auto directoryInode = resolvePath(path);
- if (!directoryInode.isValid())
- return;
- kprintf("[VFS] ls %s -> %s %02u:%08u\n", path.characters(), directoryInode.fileSystem()->className(), directoryInode.fileSystemID(), directoryInode.index());
- enumerateDirectoryInode(directoryInode, [&] (const FileSystem::DirectoryEntry& entry) {
- const char* nameColorBegin = "";
- const char* nameColorEnd = "";
- auto metadata = entry.inode.metadata();
- ASSERT(metadata.isValid());
- if (metadata.isDirectory()) {
- nameColorBegin = "\033[34;1m";
- nameColorEnd = "\033[0m";
- } else if (metadata.isSymbolicLink()) {
- nameColorBegin = "\033[36;1m";
- nameColorEnd = "\033[0m";
- }
- if (metadata.isSticky()) {
- nameColorBegin = "\033[42;30m";
- nameColorEnd = "\033[0m";
- }
- if (metadata.isCharacterDevice() || metadata.isBlockDevice()) {
- nameColorBegin = "\033[33;1m";
- nameColorEnd = "\033[0m";
- }
- kprintf("%02u:%08u ",
- metadata.inode.fileSystemID(),
- metadata.inode.index());
- if (metadata.isDirectory())
- kprintf("d");
- else if (metadata.isSymbolicLink())
- kprintf("l");
- else if (metadata.isBlockDevice())
- kprintf("b");
- else if (metadata.isCharacterDevice())
- kprintf("c");
- else if (metadata.isSocket())
- kprintf("s");
- else if (metadata.isFIFO())
- kprintf("f");
- else if (metadata.isRegularFile())
- kprintf("-");
- else
- kprintf("?");
- kprintf("%c%c%c%c%c%c%c%c",
- metadata.mode & 00400 ? 'r' : '-',
- metadata.mode & 00200 ? 'w' : '-',
- metadata.mode & 00100 ? 'x' : '-',
- metadata.mode & 00040 ? 'r' : '-',
- metadata.mode & 00020 ? 'w' : '-',
- metadata.mode & 00010 ? 'x' : '-',
- metadata.mode & 00004 ? 'r' : '-',
- metadata.mode & 00002 ? 'w' : '-'
- );
- if (metadata.isSticky())
- kprintf("t");
- else
- kprintf("%c", metadata.mode & 00001 ? 'x' : '-');
- if (metadata.isCharacterDevice() || metadata.isBlockDevice()) {
- char buf[16];
- ksprintf(buf, "%u, %u", metadata.majorDevice, metadata.minorDevice);
- kprintf("%12s ", buf);
- } else {
- kprintf("%12lld ", metadata.size);
- }
- kprintf("\033[30;1m");
- time_t mtime = metadata.mtime;
- auto tm = *klocaltime(&mtime);
- kprintf("%04u-%02u-%02u %02u:%02u:%02u ",
- tm.tm_year + 1900,
- tm.tm_mon + 1,
- tm.tm_mday,
- tm.tm_hour,
- tm.tm_min,
- tm.tm_sec);
- kprintf("\033[0m");
- kprintf("%s%s%s",
- nameColorBegin,
- entry.name.characters(),
- nameColorEnd);
- if (metadata.isDirectory()) {
- kprintf("/");
- } else if (metadata.isSymbolicLink()) {
- auto symlinkContents = directoryInode.fileSystem()->readEntireInode(metadata.inode);
- kprintf(" -> %s", String((const char*)symlinkContents.pointer(), symlinkContents.size()).characters());
- }
- kprintf("\n");
- return true;
- });
- }
- void VirtualFileSystem::listDirectoryRecursively(const String& path)
- {
- auto directory = resolvePath(path);
- if (!directory.isValid())
- return;
- kprintf("%s\n", path.characters());
- enumerateDirectoryInode(directory, [&] (const FileSystem::DirectoryEntry& entry) {
- auto metadata = entry.inode.metadata();
- if (metadata.isDirectory()) {
- if (entry.name != "." && entry.name != "..") {
- char buf[4096];
- ksprintf(buf, "%s/%s", path.characters(), entry.name.characters());
- listDirectoryRecursively(buf);
- }
- } else {
- kprintf("%s/%s\n", path.characters(), entry.name.characters());
- }
- return true;
- });
- }
- bool VirtualFileSystem::touch(const String& path)
- {
- Locker locker(VirtualFileSystem::lock());
- auto inode = resolvePath(path);
- if (!inode.isValid())
- return false;
- return inode.fileSystem()->setModificationTime(inode, ktime(nullptr));
- }
- OwnPtr<FileHandle> VirtualFileSystem::open(const String& path)
- {
- Locker locker(VirtualFileSystem::lock());
- auto inode = resolvePath(path);
- if (!inode.isValid())
- return nullptr;
- auto vnode = getOrCreateNode(inode);
- if (!vnode)
- return nullptr;
- return make<FileHandle>(move(vnode));
- }
- OwnPtr<FileHandle> VirtualFileSystem::create(const String& path)
- {
- Locker locker(VirtualFileSystem::lock());
- // FIXME: Do the real thing, not just this fake thing!
- (void) path;
- m_rootNode->fileSystem()->createInode(m_rootNode->fileSystem()->rootInode(), "empty", 0100644, 0);
- return nullptr;
- }
- OwnPtr<FileHandle> VirtualFileSystem::mkdir(const String& path)
- {
- Locker locker(VirtualFileSystem::lock());
- // FIXME: Do the real thing, not just this fake thing!
- (void) path;
- m_rootNode->fileSystem()->makeDirectory(m_rootNode->fileSystem()->rootInode(), "mydir", 0400755);
- return nullptr;
- }
- InodeIdentifier VirtualFileSystem::resolveSymbolicLink(const String& basePath, InodeIdentifier symlinkInode)
- {
- auto symlinkContents = symlinkInode.readEntireFile();
- if (!symlinkContents)
- return { };
- char buf[4096];
- ksprintf(buf, "/%s/%s", basePath.characters(), String((const char*)symlinkContents.pointer(), symlinkContents.size()).characters());
- return resolvePath(buf);
- }
- InodeIdentifier VirtualFileSystem::resolvePath(const String& path)
- {
- auto parts = path.split('/');
- InodeIdentifier inode = m_rootNode->inode;
- for (unsigned i = 0; i < parts.size(); ++i) {
- auto& part = parts[i];
- auto metadata = inode.metadata();
- if (!metadata.isValid()) {
- #ifdef VFS_DEBUG
- kprintf("invalid metadata\n");
- #endif
- return InodeIdentifier();
- }
- if (!metadata.isDirectory()) {
- #ifdef VFS_DEBUG
- kprintf("not directory\n");
- #endif
- return InodeIdentifier();
- }
- inode = inode.fileSystem()->childOfDirectoryInodeWithName(inode, part);
- if (!inode.isValid()) {
- #ifdef VFS_DEBUG
- kprintf("bad child\n");
- #endif
- return InodeIdentifier();
- }
- #ifdef VFS_DEBUG
- kprintf("<%s> %02u:%08u\n", part.characters(), inode.fileSystemID(), inode.index());
- #endif
- if (auto mount = findMountForHost(inode)) {
- #ifdef VFS_DEBUG
- kprintf(" -- is host\n");
- #endif
- inode = mount->guest();
- }
- if (inode.isRootInode() && !isRoot(inode) && part == "..") {
- #ifdef VFS_DEBUG
- kprintf(" -- is guest\n");
- #endif
- auto mount = findMountForGuest(inode);
- inode = mount->host();
- inode = inode.fileSystem()->childOfDirectoryInodeWithName(inode, "..");
- }
- metadata = inode.metadata();
- if (metadata.isSymbolicLink()) {
- char buf[4096] = "";
- char* p = buf;
- for (unsigned j = 0; j < i; ++j) {
- p += ksprintf(p, "/%s", parts[j].characters());
- }
- inode = resolveSymbolicLink(buf, inode);
- if (!inode.isValid()) {
- kprintf("Symbolic link resolution failed :(\n");
- return { };
- }
- }
- }
- return inode;
- }
- void VirtualFileSystem::Node::retain()
- {
- ++retainCount;
- }
- void VirtualFileSystem::Node::release()
- {
- ASSERT(retainCount);
- if (--retainCount == 0) {
- m_vfs->freeNode(this);
- }
- }
- const InodeMetadata& VirtualFileSystem::Node::metadata() const
- {
- if (!m_cachedMetadata.isValid())
- m_cachedMetadata = inode.metadata();
- return m_cachedMetadata;
- }
- VirtualFileSystem::Mount::Mount(InodeIdentifier host, RetainPtr<FileSystem>&& guestFileSystem)
- : m_host(host)
- , m_guest(guestFileSystem->rootInode())
- , m_fileSystem(move(guestFileSystem))
- {
- }
- void VirtualFileSystem::registerCharacterDevice(unsigned major, unsigned minor, CharacterDevice& device)
- {
- m_characterDevices.set(encodedDevice(major, minor), &device);
- }
|