Browse Source

Work on POSIX-like read() and lseek() support.

Andreas Kling 6 years ago
parent
commit
c94044a04a

+ 81 - 21
VirtualFileSystem/Ext2FileSystem.cpp

@@ -256,51 +256,111 @@ Vector<unsigned> Ext2FileSystem::blockListForInode(const ext2_inode& e2inode) co
     return list;
     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());
     ASSERT(inode.fileSystemID() == id());
 
 
     auto e2inode = lookupExt2Inode(inode.index());
     auto e2inode = lookupExt2Inode(inode.index());
     if (!e2inode) {
     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)
     if (e2inode->i_size == 0)
-        return ByteBuffer::createEmpty();
+        return 0;
 
 
     // Symbolic links shorter than 60 characters are store inline inside the i_block array.
     // 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.)
     // This avoids wasting an entire block on short links. (Most links are short.)
     static const unsigned maxInlineSymlinkLength = 60;
     static const unsigned maxInlineSymlinkLength = 60;
     if (isSymbolicLink(e2inode->i_mode) && e2inode->i_size < maxInlineSymlinkLength) {
     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<FileOffset>(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);
     auto list = blockListForInode(*e2inode);
     if (list.isEmpty()) {
     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) {
         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;
     return contents;
 }
 }
 
 

+ 1 - 0
VirtualFileSystem/Ext2FileSystem.h

@@ -40,6 +40,7 @@ private:
     virtual InodeMetadata inodeMetadata(InodeIdentifier) const override;
     virtual InodeMetadata inodeMetadata(InodeIdentifier) const override;
     virtual bool setModificationTime(InodeIdentifier, dword timestamp) override;
     virtual bool setModificationTime(InodeIdentifier, dword timestamp) override;
     virtual InodeIdentifier createInode(InodeIdentifier parentInode, const String& name, word mode) 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;
     bool isDirectoryInode(unsigned) const;
     unsigned allocateInode(unsigned preferredGroup, unsigned expectedSize);
     unsigned allocateInode(unsigned preferredGroup, unsigned expectedSize);

+ 55 - 1
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()) {
     if (m_vnode->isCharacterDevice()) {
         auto buffer = ByteBuffer::createUninitialized(1024);
         auto buffer = ByteBuffer::createUninitialized(1024);

+ 12 - 1
VirtualFileSystem/FileHandle.h

@@ -3,16 +3,27 @@
 #include "VirtualFileSystem.h"
 #include "VirtualFileSystem.h"
 #include <AK/ByteBuffer.h>
 #include <AK/ByteBuffer.h>
 
 
+enum class SeekType {
+    Absolute,           // SEEK_SET
+    RelativeToCurrent,  // SEEK_CUR
+    RelativeToEnd,      // SEEK_END
+};
+
 class FileHandle {
 class FileHandle {
 public:
 public:
     explicit FileHandle(RetainPtr<VirtualFileSystem::Node>&&);
     explicit FileHandle(RetainPtr<VirtualFileSystem::Node>&&);
     ~FileHandle();
     ~FileHandle();
 
 
-    ByteBuffer read();
+    FileOffset lseek(FileOffset, SeekType);
+    ssize_t read(byte* buffer, size_t count);
+
+    ByteBuffer readEntireFile();
 
 
 private:
 private:
     friend class VirtualFileSystem;
     friend class VirtualFileSystem;
 
 
     RetainPtr<VirtualFileSystem::Node> m_vnode;
     RetainPtr<VirtualFileSystem::Node> m_vnode;
+
+    FileOffset m_currentOffset { 0 };
 };
 };
 
 

+ 3 - 0
VirtualFileSystem/FileSystem.h

@@ -3,6 +3,7 @@
 #include "BlockDevice.h"
 #include "BlockDevice.h"
 #include "InodeIdentifier.h"
 #include "InodeIdentifier.h"
 #include "InodeMetadata.h"
 #include "InodeMetadata.h"
+#include "Limits.h"
 #include <AK/ByteBuffer.h>
 #include <AK/ByteBuffer.h>
 #include <AK/HashMap.h>
 #include <AK/HashMap.h>
 #include <AK/OwnPtr.h>
 #include <AK/OwnPtr.h>
@@ -27,6 +28,8 @@ public:
     virtual bool writeInode(InodeIdentifier, const ByteBuffer&) = 0;
     virtual bool writeInode(InodeIdentifier, const ByteBuffer&) = 0;
     virtual InodeMetadata inodeMetadata(InodeIdentifier) const = 0;
     virtual InodeMetadata inodeMetadata(InodeIdentifier) const = 0;
 
 
+    virtual ssize_t readInodeBytes(InodeIdentifier, FileOffset offset, size_t count, byte* buffer) const = 0;
+
     struct DirectoryEntry {
     struct DirectoryEntry {
         String name;
         String name;
         InodeIdentifier inode;
         InodeIdentifier inode;

+ 6 - 0
VirtualFileSystem/Limits.h

@@ -1,6 +1,12 @@
 #pragma once
 #pragma once
 
 
+#include <limits>
+
 typedef dword size_t;
 typedef dword size_t;
 typedef signed_dword ssize_t;
 typedef signed_dword ssize_t;
 
 
 static const size_t GoodBufferSize = 4096;
 static const size_t GoodBufferSize = 4096;
+
+typedef int64_t FileOffset;
+inline static const FileOffset maxFileOffset = std::numeric_limits<FileOffset>::max();
+

+ 1 - 1
VirtualFileSystem/Makefile

@@ -26,7 +26,7 @@ VFS_OBJS = \
 
 
 OBJS = $(AK_OBJS) $(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
 #test.o: BlockDevice.h FileBackedBlockDevice.h FileSystem.h Ext2FileSystem.h VirtualFileSystem.h FileHandle.h
 
 

+ 6 - 0
VirtualFileSystem/SyntheticFileSystem.cpp

@@ -121,3 +121,9 @@ bool SyntheticFileSystem::writeInode(InodeIdentifier, const ByteBuffer&)
     printf("FIXME: Implement SyntheticFileSystem::writeInode().\n");
     printf("FIXME: Implement SyntheticFileSystem::writeInode().\n");
     return false;
     return false;
 }
 }
+
+ssize_t SyntheticFileSystem::readInodeBytes(InodeIdentifier, FileOffset offset, size_t count, byte* buffer) const
+{
+    printf("FIXME: Implement SyntheticFileSystem::readInodeBytes().\n");
+    return 0;
+}

+ 1 - 0
VirtualFileSystem/SyntheticFileSystem.h

@@ -17,6 +17,7 @@ public:
     virtual InodeMetadata inodeMetadata(InodeIdentifier) const override;
     virtual InodeMetadata inodeMetadata(InodeIdentifier) const override;
     virtual bool setModificationTime(InodeIdentifier, dword timestamp) override;
     virtual bool setModificationTime(InodeIdentifier, dword timestamp) override;
     virtual InodeIdentifier createInode(InodeIdentifier parentInode, const String& name, word mode) 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:
 private:
     SyntheticFileSystem();
     SyntheticFileSystem();

+ 1 - 0
VirtualFileSystem/VirtualFileSystem.h

@@ -6,6 +6,7 @@
 #include <AK/String.h>
 #include <AK/String.h>
 #include <AK/Vector.h>
 #include <AK/Vector.h>
 #include "InodeIdentifier.h"
 #include "InodeIdentifier.h"
+#include "Limits.h"
 
 
 class CharacterDevice;
 class CharacterDevice;
 class FileHandle;
 class FileHandle;

+ 25 - 3
VirtualFileSystem/test.cpp

@@ -44,7 +44,7 @@ int main(int c, char** v)
             printf("failed to open %s inside fs image\n", v[2]);
             printf("failed to open %s inside fs image\n", v[2]);
             return 1;
             return 1;
         }
         }
-        auto contents = handle->read();
+        auto contents = handle->readEntireFile();
 
 
         FILE* fout = fopen(v[3], "w");
         FILE* fout = fopen(v[3], "w");
         if (!fout) {
         if (!fout) {
@@ -71,7 +71,7 @@ int main(int c, char** v)
     printf("handle = %p\n", handle.ptr());
     printf("handle = %p\n", handle.ptr());
     ASSERT(handle);
     ASSERT(handle);
 
 
-    auto contents = handle->read();
+    auto contents = handle->readEntireFile();
     ASSERT(contents);
     ASSERT(contents);
 
 
     printf("contents: '%s'\n", contents->pointer());
     printf("contents: '%s'\n", contents->pointer());
@@ -141,11 +141,33 @@ int main(int c, char** v)
                 printf("failed to open %s\n", pathbuf);
                 printf("failed to open %s\n", pathbuf);
                 continue;
                 continue;
             }
             }
-            auto contents = handle->read();
+            auto contents = handle->readEntireFile();
             fwrite(contents.pointer(), sizeof(char), contents.size(), stdout);
             fwrite(contents.pointer(), sizeof(char), contents.size(), stdout);
             continue;
             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") {
         if (cmd == "ma") {
             SimpleMalloc::dump();
             SimpleMalloc::dump();
             continue;
             continue;