The designers of the Fast File System (FFS) organized it in two different layers aiming for flexibility and code reuse; this happened in 4.4BSD (or maybe earlier, I don't know for sure). The upper layer communicates with the VFS and vnode code while the lower layer accesses the underlying devices and only receives requests from the upper layer. It goes like this:
VFS/vnode calls <-> FFS upper layer <-> FFS lower layer <-> backing device
The upper layer handles all logical details of a Unix file-system, abstracting, among many other things, inode representation and management, tree manipulations and verification of credentials. It also lays out data in different blocks and passes them to the lower layer.
The lower layer receives the blocks generated by the upper layer and lays them out on the physical device (e.g., a disk). It is also responsible of reading the blocks requested by the upper layer and transferring them to it.
Thanks to this design, three different lower layers were implemented, providing three file-systems:
- Fast File System (FFS): It assumes that the average file size is small and that some files (those in the same directory) will be accessed all at once. Given these assumptions, it divides the disk surface in different cylinder groups and lays out blocks that may be accessed together in the same group. This reduces expensive seeks.
- Log-structured File System (LFS): It assumes that the system bottleneck are writes to the disk, so it packs all writes in a concrete area of the disk, treating the overall space as an append-only log (no seeks required).
- Memory File System (MFS): Reserves a contiguous region of memory and lays out blocks in it. This is why MFS is considered inefficient, because it is using the concepts of an on-disk file-system over memory.
The different layers are clearly visible in the source code. The src/sys/ufs/ufs directory contains the upper layer's code while src/sys/ufs/ffs, src/sys/ufs/lfs and src/sys/ufs/mfs contain the three respective lower layers.
Note that the above is an over-simplification of the Real Thing (TM). If you want detailed information, read Chapter 6 of The Design and Implementation of the 4.4 BSD Operating System.