Round 5 fix: when a file is truncated between fstat and the snapshot read,
Content-Length/Range headers must match the actual bytes read, not the stale
fstat size.
Changes:
1. Replace os.pread with os.lseek + looped os.read (cross-platform + short-read safe)
2. _etag_and_snapshot now returns 3 values: (etag, snapshot, actual_size)
3. Caller uses actual_size for Content-Length/Range calculations
4. If actual_size != file_size (truncation), etag=None → streaming fallback
Regressions:
- test_short_read_truncation_uses_actual_size: Content-Length == body length
- test_short_read_no_etag_on_truncation: no ETag on truncated snapshot
- Updated test_etag_no_body_on_pread_failure for os.read (was os.pread)