The intent is to replace vn_bmap_seekhole() for lseek(SEEK_DATA) calls.
The algorithm used by vn_bmap_seekhole() is very slow for large sparse
files, and it seems we can't do better without extending the VFS
(adding a new VOP or so). In particular, syzkaller generates test
programs which truncate a file to a very large size and then call
lseek(fd, 0, SEEK_DATA), which does not complete in any sane amount of
time, during which the process is unkillable.
ufs_bmap_seekdata() has the obvious implementation: starting from the
caller-provided offset, we search for a direct block or indirect block
entry which references a non-zero disk block.
When discussing the problem, kib suggested that we modify
vn_bmap_seekhole() to periodically check whether the RLIMIT_CPU limit
was exceeded by the current process, and abort if so. That would
be a simpler solution but would require some synchronization with
lim_cb(), and ufs_bmap_seekdata() will give better performance for
real-world cases. However, if the preference is to avoid adding more
UFS code, I can go that route.
vn_bmap_seekhole() is still used for SEEK_HOLE, as I believe it is
still sufficient and I did not want to complicate ufs_bmap_seekhole().
ufs_bmap_seekhole() does not attempt to handle snapshot files yet. If
we agree that this approach is ok, I'll try to implement that.