From d97aa48f7c53e851675ad5d250daecee1bac81c1 Mon Sep 17 00:00:00 2001 From: Matthew Ahrens Date: Wed, 17 Sep 2014 17:25:10 +0200 Subject: [PATCH] Illumos 5139 - SEEK_HOLE failed to report a hole at end of file 5139 SEEK_HOLE failed to report a hole at end of file Reviewed by: Adam Leventhal Reviewed by: Alex Reece Reviewed by: Christopher Siden Reviewed by: George Wilson Reviewed by: Max Grossman Reviewed by: Peng Dai Reviewed by: Richard Elling Approved by: Dan McDonald References: https://www.illumos.org/issues/5139 https://github.com/illumos/illumos-gate/commit/0fbc0cd Ported by: Turbo Fredriksson Signed-off-by: Brian Behlendorf Closes #2714 --- module/zfs/dnode.c | 9 +++++++++ module/zfs/zfs_vnops.c | 21 ++++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/module/zfs/dnode.c b/module/zfs/dnode.c index 436816497..20fce9a2d 100644 --- a/module/zfs/dnode.c +++ b/module/zfs/dnode.c @@ -1902,6 +1902,15 @@ dnode_next_offset(dnode_t *dn, int flags, uint64_t *offset, flags, offset, lvl, blkfill, txg); } + /* + * There's always a "virtual hole" at the end of the object, even + * if all BP's which physically exist are non-holes. + */ + if ((flags & DNODE_FIND_HOLE) && error == ESRCH && txg == 0 && + minlvl == 1 && blkfill == 1 && !(flags & DNODE_FIND_BACKWARDS)) { + error = 0; + } + if (error == 0 && (flags & DNODE_FIND_BACKWARDS ? initial_offset < *offset : initial_offset > *offset)) error = SET_ERROR(ESRCH); diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c index ddd997fae..33f9e0ec9 100644 --- a/module/zfs/zfs_vnops.c +++ b/module/zfs/zfs_vnops.c @@ -274,16 +274,19 @@ zfs_holey_common(struct inode *ip, int cmd, loff_t *off) error = dmu_offset_next(ZTOZSB(zp)->z_os, zp->z_id, hole, &noff); - /* end of file? */ - if ((error == ESRCH) || (noff > file_sz)) { - /* - * Handle the virtual hole at the end of file. - */ - if (hole) { - *off = file_sz; - return (0); - } + if (error == ESRCH) return (SET_ERROR(ENXIO)); + + /* + * We could find a hole that begins after the logical end-of-file, + * because dmu_offset_next() only works on whole blocks. If the + * EOF falls mid-block, then indicate that the "virtual hole" + * at the end of the file begins at the logical EOF, rather than + * at the end of the last block. + */ + if (noff > file_sz) { + ASSERT(hole); + noff = file_sz; } if (noff < *off)