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 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.