The SpinLock was all backwards and didn't actually work. Fixing it exposed
how wrong most of the locking here is.
I need to come up with a better granularity here.
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.)
I added a dead-simple malloc that only allows allocations < 4096 bytes.
It just forwards the request to mmap() every time.
I also added simplified versions of opendir() and readdir().
This way subclasses only have to implement readBlock() and writeBlock().
read() and write() require that the offset and length are both divisible
by the blockSize().
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.