sbin/hammer: Use big-block append offset to limit recovery scan range
This commit is to fix a remaining issue mentioned in
e3cefcca,
which recovers irrelevant files from old filesystem even with the
scan range limit introduced by
e3cefcca and quick scan mode
introduced by
e819b271.
As shown in an example below, whenever a filesystem is recreated
and the current one uses less space than the old filesystem, the
command is likely to recover files from old filesystem (even with
e3cefcca and
e819b271), because B-Tree big-blocks could have nodes
from old filesystem after their append offset, especially if the
block is the last one in B-Tree zone.
In order to avoid recovery of irrelevant files, the command needs
to check if scanning offset is beyond append offset of the B-Tree
big-block that contains this offset, and ignore all nodes beyond
the append offset. [*] shows this situation. Note that the append
offset is checked only if layer1/2 entries that point to this
B-Tree big-block have good CRC result.
This applies to both default and quick scan mode, but not to full
scan mode. Full scan scans everything no matter what.
--------------------------------------------------------> offset
|--------------------------------------------------| volume size
|<----------------------------------------->| previously used
|<---->| previously unused
|<----------------------------------->| currently used
|<---------->| currently unused
... -------------------------->| full scan
... ---------------->| default scan
... --->||<------->||<------->||<--->| default scan [*]
... |<-->| ... |<-->| ... |<-->| quick scan
... |<->| ... |<->| ... |<->| quick scan [*]
===== comparison of recovered files
1. Zero clear the first 1GB of /dev/da1.
# dd if=/dev/zero of=/dev/da1 bs=1M count=1K
1024+0 records in
1024+0 records out
1073741824 bytes transferred in 2.714761 secs (
395519867 bytes/sec)
2. Create a filesystem and clone 968MB dragonfly source.
# newfs_hammer -L TEST /dev/da1 > /dev/null
# mount_hammer /dev/da1 /HAMMER
# cd /HAMMER
# git clone /usr/local/src/dragonfly > /dev/null 2>&1
# du -sh .
968M .
# cd
# umount /HAMMER
3. Create a filesystem again with 1 regular file.
# newfs_hammer -L TEST /dev/da1 > /dev/null
# mount_hammer /dev/da1 /HAMMER
# cd /HAMMER
# ls -l
total 0
# echo test > test
# cat ./test
test
# cd
# umount /HAMMER
4-1. Recover a filesystem assuming it only has 1 regular file.
# rm -rf /tmp/a
# hammer -f /dev/da1 recover /tmp/a recover > /dev/null
# cat /tmp/a/PFS00000/test
test
# tree /tmp/a | wc -l
19659
# du -a /tmp/a | grep obj_0x | wc -l
19661
4-2. Do the same as 4-1 using this commit.
# rm -rf /tmp/b
# hammer -f /dev/da1 recover /tmp/b recover > /dev/null
# cat /tmp/b/PFS00000/test
test
# tree /tmp/b
/tmp/b
`-- PFS00000
`-- test
1 directory, 1 file
#