diff --git a/VirtualFileSystem/Ext2FileSystem.cpp b/VirtualFileSystem/Ext2FileSystem.cpp index 0c5e7c08aed855483efc1f454b1f0f4bed0a4dd7..041590b005df8edfab0f3014b1174da9d2da70d9 100644 --- a/VirtualFileSystem/Ext2FileSystem.cpp +++ b/VirtualFileSystem/Ext2FileSystem.cpp @@ -256,51 +256,111 @@ Vector Ext2FileSystem::blockListForInode(const ext2_inode& e2inode) co return list; } -ByteBuffer Ext2FileSystem::readInode(InodeIdentifier inode) const +ssize_t Ext2FileSystem::readInodeBytes(InodeIdentifier inode, FileOffset offset, size_t count, byte* buffer) const { + ASSERT(offset >= 0); ASSERT(inode.fileSystemID() == id()); auto e2inode = lookupExt2Inode(inode.index()); if (!e2inode) { - printf("[ext2fs] readInode: metadata lookup for inode %u failed\n", inode.index()); - return nullptr; + printf("[ext2fs] readInodeBytes: metadata lookup for inode %u failed\n", inode.index()); + return -EIO; } +#if 0 + // FIXME: We can't fail here while the directory traversal depends on this function. :] + if (isDirectory(e2inode->i_mode)) + return -EISDIR; +#endif + if (e2inode->i_size == 0) - return ByteBuffer::createEmpty(); + return 0; // Symbolic links shorter than 60 characters are store inline inside the i_block array. // This avoids wasting an entire block on short links. (Most links are short.) static const unsigned maxInlineSymlinkLength = 60; if (isSymbolicLink(e2inode->i_mode) && e2inode->i_size < maxInlineSymlinkLength) { - auto inlineSymlink = ByteBuffer::createUninitialized(e2inode->i_size); - memcpy(inlineSymlink.pointer(), e2inode->i_block, e2inode->i_size); - return inlineSymlink; + ssize_t nread = min(e2inode->i_size - offset, static_cast(count)); + memcpy(buffer, e2inode->i_block + offset, nread); + return nread; } + // FIXME: It's grossly inefficient to fetch the blocklist on every call to readInodeBytes(). + // It needs to be cached! auto list = blockListForInode(*e2inode); if (list.isEmpty()) { - printf("[ext2fs] readInode: empty block list for inode %u\n", inode.index()); - return nullptr; + printf("[ext2fs] readInodeBytes: empty block list for inode %u\n", inode.index()); + return -EIO; } - auto contents = ByteBuffer::createUninitialized(list.size() * blockSize()); - auto* out = contents.pointer(); + dword firstBlockLogicalIndex = offset / blockSize(); + dword lastBlockLogicalIndex = (offset + count) / blockSize(); + if (lastBlockLogicalIndex >= list.size()) + lastBlockLogicalIndex = list.size() - 1; - for (unsigned i = 0; i < list.size(); ++i) { - auto block = readBlock(list[i]); + dword offsetIntoFirstBlock = offset % blockSize(); + + ssize_t nread = 0; + size_t remainingCount = min((FileOffset)count, e2inode->i_size - offset); + byte* out = buffer; + +#ifdef EXT2_DEBUG + printf("ok let's do it, read(%llu, %u) -> blocks %u thru %u, oifb: %u\n", offset, count, firstBlockLogicalIndex, lastBlockLogicalIndex, offsetIntoFirstBlock); +#endif + + for (dword bi = firstBlockLogicalIndex; bi <= lastBlockLogicalIndex; ++bi) { + auto block = readBlock(list[bi]); if (!block) { - printf("[ext2fs] readInode: readBlock(%u) failed\n", list[i]); - return nullptr; + printf("[ext2fs] readInodeBytes: readBlock(%u) failed (lbi: %u)\n", list[bi], bi); + return -EIO; } - memcpy(out, block.pointer(), block.size()); - out += blockSize(); + + dword offsetIntoBlock; + + if (bi == firstBlockLogicalIndex) + offsetIntoBlock = offsetIntoFirstBlock; + else + offsetIntoBlock = 0; + + dword numBytesToCopy = min(blockSize() - offsetIntoBlock, remainingCount); + memcpy(out, block.pointer() + offsetIntoBlock, numBytesToCopy); + remainingCount -= numBytesToCopy; + nread += numBytesToCopy; + out += numBytesToCopy; + } + + return nread; +} + +ByteBuffer Ext2FileSystem::readInode(InodeIdentifier inode) const +{ + ASSERT(inode.fileSystemID() == id()); + + auto e2inode = lookupExt2Inode(inode.index()); + if (!e2inode) { + printf("[ext2fs] readInode: metadata lookup for inode %u failed\n", inode.index()); + return nullptr; + } + + auto contents = ByteBuffer::createUninitialized(e2inode->i_size); + + ssize_t nread; + byte buffer[512]; + byte* out = contents.pointer(); + FileOffset offset = 0; + for (;;) { + nread = readInodeBytes(inode, offset, sizeof(buffer), buffer); + if (nread <= 0) + break; + memcpy(out, buffer, nread); + out += nread; + offset += nread; + } + if (nread < 0) { + printf("[ext2fs] readInode: ERROR: %d\n", nread); + return nullptr; } -#ifdef EXT2_DEBUG - printf("trim from %u to %u\n", contents.size(), e2inode->i_size); -#endif - contents.trim(e2inode->i_size); return contents; } diff --git a/VirtualFileSystem/Ext2FileSystem.h b/VirtualFileSystem/Ext2FileSystem.h index 97c7ae23ee37c1e17bed0f2f804cca52ce4e8b05..92aa3ddbe4f93eb9819dcb9c054c2c981a0ea3fa 100644 --- a/VirtualFileSystem/Ext2FileSystem.h +++ b/VirtualFileSystem/Ext2FileSystem.h @@ -40,6 +40,7 @@ private: virtual InodeMetadata inodeMetadata(InodeIdentifier) const override; virtual bool setModificationTime(InodeIdentifier, dword timestamp) override; virtual InodeIdentifier createInode(InodeIdentifier parentInode, const String& name, word mode) override; + virtual ssize_t readInodeBytes(InodeIdentifier, FileOffset offset, size_t count, byte* buffer) const override; bool isDirectoryInode(unsigned) const; unsigned allocateInode(unsigned preferredGroup, unsigned expectedSize); diff --git a/VirtualFileSystem/FileHandle.cpp b/VirtualFileSystem/FileHandle.cpp index 25f101f03925604016d521382ab5da668292f94c..3718bffbc06241c8788524112483fbe6d597455c 100644 --- a/VirtualFileSystem/FileHandle.cpp +++ b/VirtualFileSystem/FileHandle.cpp @@ -11,7 +11,61 @@ FileHandle::~FileHandle() { } -ByteBuffer FileHandle::read() +bool additionWouldOverflow(FileOffset a, FileOffset b) +{ + ASSERT(a > 0); + uint64_t ua = a; + return (ua + b) > maxFileOffset; +} + +FileOffset FileHandle::lseek(FileOffset offset, SeekType seekType) +{ + if (!m_vnode) + return -EBADF; + + // FIXME: The file type should be cached on the vnode. + // It's silly that we have to do a full metadata lookup here. + auto metadata = m_vnode->inode.metadata(); + if (metadata.isSocket() || metadata.isFIFO()) + return -ESPIPE; + + FileOffset newOffset; + + switch (seekType) { + case SeekType::Absolute: + newOffset = offset; + break; + case SeekType::RelativeToCurrent: + newOffset = m_currentOffset + offset; + if (additionWouldOverflow(m_currentOffset, offset)) + return -EOVERFLOW; + if (newOffset < 0) + return -EINVAL; + break; + case SeekType::RelativeToEnd: + // FIXME: Implement! + notImplemented(); + break; + default: + return -EINVAL; + } + + m_currentOffset = newOffset; + return m_currentOffset; +} + +ssize_t FileHandle::read(byte* buffer, size_t count) +{ + if (m_vnode->isCharacterDevice()) { + // FIXME: What should happen to m_currentOffset? + return m_vnode->characterDevice()->read(buffer, count); + } + ssize_t nread = m_vnode->fileSystem()->readInodeBytes(m_vnode->inode, m_currentOffset, count, buffer); + m_currentOffset += nread; + return nread; +} + +ByteBuffer FileHandle::readEntireFile() { if (m_vnode->isCharacterDevice()) { auto buffer = ByteBuffer::createUninitialized(1024); diff --git a/VirtualFileSystem/FileHandle.h b/VirtualFileSystem/FileHandle.h index 66aa88508c6b5a722c0d9ed2aa9cb70eb53d91b2..3a202acbfe38d4d78e4ac3bea689d6bb83148bd0 100644 --- a/VirtualFileSystem/FileHandle.h +++ b/VirtualFileSystem/FileHandle.h @@ -3,16 +3,27 @@ #include "VirtualFileSystem.h" #include +enum class SeekType { + Absolute, // SEEK_SET + RelativeToCurrent, // SEEK_CUR + RelativeToEnd, // SEEK_END +}; + class FileHandle { public: explicit FileHandle(RetainPtr&&); ~FileHandle(); - ByteBuffer read(); + FileOffset lseek(FileOffset, SeekType); + ssize_t read(byte* buffer, size_t count); + + ByteBuffer readEntireFile(); private: friend class VirtualFileSystem; RetainPtr m_vnode; + + FileOffset m_currentOffset { 0 }; }; diff --git a/VirtualFileSystem/FileSystem.h b/VirtualFileSystem/FileSystem.h index 9c9548d7882cd28073ce3bb4af669ad5b26294b6..99288b1eebc70eed19c95032fee1afc57232ee4a 100644 --- a/VirtualFileSystem/FileSystem.h +++ b/VirtualFileSystem/FileSystem.h @@ -3,6 +3,7 @@ #include "BlockDevice.h" #include "InodeIdentifier.h" #include "InodeMetadata.h" +#include "Limits.h" #include #include #include @@ -27,6 +28,8 @@ public: virtual bool writeInode(InodeIdentifier, const ByteBuffer&) = 0; virtual InodeMetadata inodeMetadata(InodeIdentifier) const = 0; + virtual ssize_t readInodeBytes(InodeIdentifier, FileOffset offset, size_t count, byte* buffer) const = 0; + struct DirectoryEntry { String name; InodeIdentifier inode; diff --git a/VirtualFileSystem/Limits.h b/VirtualFileSystem/Limits.h index ebe763e2fed4f2ee114bded782e1c2958897e6ea..a9bbf862b561468e929cca28d744eadb02017653 100644 --- a/VirtualFileSystem/Limits.h +++ b/VirtualFileSystem/Limits.h @@ -1,6 +1,12 @@ #pragma once +#include + typedef dword size_t; typedef signed_dword ssize_t; static const size_t GoodBufferSize = 4096; + +typedef int64_t FileOffset; +inline static const FileOffset maxFileOffset = std::numeric_limits::max(); + diff --git a/VirtualFileSystem/Makefile b/VirtualFileSystem/Makefile index 7b3df94bb46c43f23aa03836d9759f13d2f5a3e0..28994083b295914cc5a27c1e5b2f8246bca01558 100644 --- a/VirtualFileSystem/Makefile +++ b/VirtualFileSystem/Makefile @@ -26,7 +26,7 @@ VFS_OBJS = \ OBJS = $(AK_OBJS) $(VFS_OBJS) -CXXFLAGS = -std=c++17 -Os -W -Wall -Wextra -Wconversion -I. -I.. -ggdb3 -Wno-class-memaccess +CXXFLAGS = -std=c++17 -O0 -W -Wall -Wextra -Wconversion -I. -I.. -ggdb3 -Wno-class-memaccess #test.o: BlockDevice.h FileBackedBlockDevice.h FileSystem.h Ext2FileSystem.h VirtualFileSystem.h FileHandle.h diff --git a/VirtualFileSystem/SyntheticFileSystem.cpp b/VirtualFileSystem/SyntheticFileSystem.cpp index 2d12eae04b1c44739d3c4472c89eef01edc47c9e..723f58d96d3a7b4d353e090b8c0f06ec6fa7c2bb 100644 --- a/VirtualFileSystem/SyntheticFileSystem.cpp +++ b/VirtualFileSystem/SyntheticFileSystem.cpp @@ -121,3 +121,9 @@ bool SyntheticFileSystem::writeInode(InodeIdentifier, const ByteBuffer&) printf("FIXME: Implement SyntheticFileSystem::writeInode().\n"); return false; } + +ssize_t SyntheticFileSystem::readInodeBytes(InodeIdentifier, FileOffset offset, size_t count, byte* buffer) const +{ + printf("FIXME: Implement SyntheticFileSystem::readInodeBytes().\n"); + return 0; +} diff --git a/VirtualFileSystem/SyntheticFileSystem.h b/VirtualFileSystem/SyntheticFileSystem.h index 76f7075de06905aa20330e5cc677fec58509ab43..dda6c2633099333f93ba466e22d9b264d332395b 100644 --- a/VirtualFileSystem/SyntheticFileSystem.h +++ b/VirtualFileSystem/SyntheticFileSystem.h @@ -17,6 +17,7 @@ public: virtual InodeMetadata inodeMetadata(InodeIdentifier) const override; virtual bool setModificationTime(InodeIdentifier, dword timestamp) override; virtual InodeIdentifier createInode(InodeIdentifier parentInode, const String& name, word mode) override; + virtual ssize_t readInodeBytes(InodeIdentifier, FileOffset offset, size_t count, byte* buffer) const override; private: SyntheticFileSystem(); diff --git a/VirtualFileSystem/VirtualFileSystem.h b/VirtualFileSystem/VirtualFileSystem.h index 9993b91f52614c09cfadff219f3a99a43e350f48..2f968b7e38f27052f80bb6142fd285ca776f8962 100644 --- a/VirtualFileSystem/VirtualFileSystem.h +++ b/VirtualFileSystem/VirtualFileSystem.h @@ -6,6 +6,7 @@ #include #include #include "InodeIdentifier.h" +#include "Limits.h" class CharacterDevice; class FileHandle; diff --git a/VirtualFileSystem/test.cpp b/VirtualFileSystem/test.cpp index c7183611eef4efea06a0c2439558239d8a92b605..9bb4fa3338cf8cd6a4fff4005e30e96411d41ce1 100644 --- a/VirtualFileSystem/test.cpp +++ b/VirtualFileSystem/test.cpp @@ -44,7 +44,7 @@ int main(int c, char** v) printf("failed to open %s inside fs image\n", v[2]); return 1; } - auto contents = handle->read(); + auto contents = handle->readEntireFile(); FILE* fout = fopen(v[3], "w"); if (!fout) { @@ -71,7 +71,7 @@ int main(int c, char** v) printf("handle = %p\n", handle.ptr()); ASSERT(handle); - auto contents = handle->read(); + auto contents = handle->readEntireFile(); ASSERT(contents); printf("contents: '%s'\n", contents->pointer()); @@ -141,11 +141,33 @@ int main(int c, char** v) printf("failed to open %s\n", pathbuf); continue; } - auto contents = handle->read(); + auto contents = handle->readEntireFile(); fwrite(contents.pointer(), sizeof(char), contents.size(), stdout); continue; } + if (cmd == "kat" && parts.size() > 1) { + char pathbuf[1024]; + sprintf(pathbuf, "%s/%s", currentDirectory.characters(), parts[1].characters()); + auto handle = vfs.open(pathbuf); + if (!handle) { + printf("failed to open %s\n", pathbuf); + continue; + } + ssize_t nread; + byte buffer[512]; + for (;;) { + nread = handle->read(buffer, sizeof(buffer)); + printf("read() returned %d\n", nread); + if (nread <= 0) + break; + fwrite(buffer, 1, nread, stdout); + } + if (nread < 0) + printf("ERROR: %d\n", nread); + continue; + } + if (cmd == "ma") { SimpleMalloc::dump(); continue;