Ok, the work on fast fsck is finished. I integrated it with fsck_ffs(8).
I also changed the way how gjournaled file system is created. Now you need to use ‘-j’ option for newfs(8) to create gjournaled file system. You can also use ‘tunefs -j enable’ on an existing file system. You don’t need to provide ‘gjournal’ mount option any more, but when file system is gjournaled it will be visible in mount options.
I removed a hack of how gjournal was looking for gjournaled file systems. It is much nicer now.
Currently fsck will scan all cylinder groups, which is fast, but it could be a lot faster when I could add cs_nunrefs field to csum structure – file system keeps statistics about all cylinder groups after the super-block and having an information about which cylinder groups have unreferenced inodes would save us a lot of seeking. Unfortunately it will change UFS on-disk layout which is not acceptable at this point.
After publishing patches I implemented few suggestions from Robert Watson.
I also implemented better handling of journal overflow. Now one can define at which point journal switch is forced. This can be defined in percents via kern.geom.journal.force_switch. Not ideal, but much better than panicing.
BIO_FLUSH support was added to da(4) disks.
I’m in the process of reimplementing how removed, but still open objects (files, directories) are handled in case of a power failure or system crash.
Currently there is a magic .deleted/ directory where such objects are moved. On boot mount(8) can simply ‘rm -rf .deleted/*’.
The solution I’m implementing currently is to increase a counter in cylinder group (CG) when such situation takes place (objects is removed, but still open). When it is closed, I simply decrease the counter. On boot, a much faster fsck version checks the counter in every CG and if it is greater than 0, it scans all inodes in the CG. If it finds allocated inode, but with linkcnt=0, it cleans the inode, ie. remove all allocated blocks, marks as unallocated, etc.
Because of the complexity of UFS on-disk layout it is not a trivial task. To remove an inode completely I need to:
1. Go through the list of direct blocks pointers and mark the blocks in freeblock bitmaps as free.
2. The same as in 1, but for indirect blocks (single, double and triple).
3. Watch out for fragments. Fragments are only used for small files (which use only direct pointed blocks) and a fragment can be only at the end of the file.
4. Update blocks and fragements counters in three places.
5. Update clusters bitmap.
6. Update pointer of the last used block and fragment if needed.
7. Zero-fill the entire inode.
8. Mark an inode as free in inode’s bitmap.
9. Update inodes related counters in three places.
10. Update pointer of the last used inode.
11. Pray for success.
Most of the things I already implemented, but this is not the code I’d like to show. I’m thinking about extending libufs(3), because now its usefullness is very limited.