Before going into ChangeEvents(), the code was calling stat on the file
to know where it was at, which is incorrect as stat could return the new
file size post truncation. Instead we now ask the file descriptor about
our current offset, so we can compare our offset to the file size to try
to detect truncation.
Truncation detection remains brittle, but this closes an annoying race
we frequently run into.
at least, `tail -f` (not `tail -F` which is analogous to ReOpen)
reopens truncated files.
this change introduces the FileChanges struct to abstract the change
notifications for file changes, deletions and truncations.