This was a bit silly. We were always creating Ext2FSInode objects when
Ext2FSInode::get_inode() was called. They'd then sit and fatten up the
inode cache forever, despite not representing allocated inodes.
This patch consults the inode bitmap and if get_inode() is called with
an unallocated inode index, we simply cache a nullptr to represent the
fact that this index is unused.
This could be a lot better optimized, it will currently hit the disk for
every new inode index encountered.
The inode cache was keeping these alive forever. Added a cute little magic
trick to Retainable that calls T::one_retain_left() when the retain count
is decremented to 1.
This synchronous approach to inodes is silly, obviously. I need to rework
it so that the in-memory CoreInode object is the canonical inode, and then
we just need a sync() that flushes pending changes to disk.
...by adding a new class called Ext2Inode that inherits CoreInode.
The idea is that a vnode will wrap a CoreInode rather than InodeIdentifier.
Each CoreInode subclass can keep whatever caches they like.
Right now, Ext2Inode caches the list of block indices since it can be very
expensive to retrieve.
This is pretty inefficient for ext2fs. We walk the entire block group
containing the inode, searching through every directory for an entry
referencing this inode.
It might be a good idea to cache this information somehow. I'm not sure
how often we'll be searching for it.
Obviously there are multiple caching layers missing in the file system.
I also added a generator cache to FileHandle. This way, multiple
reads to a generated file (i.e in a synthfs) can transparently
handle multiple calls to read() without the contents changing
between calls.
The cache is discarded at EOF (or when the FileHandle is destroyed.)
BlockDevice was the wrong name for this abstraction, since a block device
is a type of file in a unix system, and we should use that name for that
concept in the fs implementation.