Index: stand/libsa/zfs/zfsimpl.c =================================================================== --- stand/libsa/zfs/zfsimpl.c +++ stand/libsa/zfs/zfsimpl.c @@ -151,6 +151,8 @@ static uint64_t dnode_cache_bn; static char *dnode_cache_buf; +static int zfs_dnode_stat(const spa_t *spa, const dnode_phys_t *dn, struct stat *sb); + static int zio_read(const spa_t *spa, const blkptr_t *bp, void *buf); static int zfs_get_root(const spa_t *spa, uint64_t *objid); static int zfs_rlookup(const spa_t *spa, uint64_t objnum, char *result); @@ -2360,8 +2362,24 @@ const blkptr_t *indbp; blkptr_t bp; - if (bn > dnode->dn_maxblkid) - return (EIO); + if (bn > dnode->dn_maxblkid) { + struct stat sb; + + /* + * A tail hole will be represented by an abrupt end of + * datablks, rather than a series of hole bp. + */ + rc = zfs_dnode_stat(spa, dnode, &sb); + if (rc != 0) + return (rc); + + if (offset >= sb.st_size || + (offset + buflen) > sb.st_size) + return (EIO); + + memset(buf, '\0', buflen); + return (0); + } if (dnode == dnode_cache_obj && bn == dnode_cache_bn) goto cached; @@ -3521,7 +3539,7 @@ } static int -zfs_dnode_stat(const spa_t *spa, dnode_phys_t *dn, struct stat *sb) +zfs_dnode_stat(const spa_t *spa, const dnode_phys_t *dn, struct stat *sb) { if (dn->dn_bonustype != DMU_OT_SA) {