diff --git a/module/zfs/zfs_znode.c b/module/zfs/zfs_znode.c index 3caf1bdc1..d3b68403b 100644 --- a/module/zfs/zfs_znode.c +++ b/module/zfs/zfs_znode.c @@ -1255,7 +1255,6 @@ zfs_rezget(znode_t *zp) return (SET_ERROR(EIO)); } - zp->z_unlinked = (ZTOI(zp)->i_nlink == 0); set_nlink(ZTOI(zp), (uint32_t)links); zfs_set_inode_flags(zp, ZTOI(zp)); @@ -1263,6 +1262,19 @@ zfs_rezget(znode_t *zp) zp->z_atime_dirty = 0; zfs_inode_update(zp); + /* + * If the file has zero links, then it has been unlinked on the send + * side and it must be in the received unlinked set. + * We call zfs_znode_dmu_fini() now to prevent any accesses to the + * stale data and to prevent automatical removal of the file in + * zfs_zinactive(). The file will be removed either when it is removed + * on the send side and the next incremental stream is received or + * when the unlinked set gets processed. + */ + zp->z_unlinked = (ZTOI(zp)->i_nlink == 0); + if (zp->z_unlinked) + zfs_znode_dmu_fini(zp); + zfs_znode_hold_exit(zfsvfs, zh); return (0); @@ -1304,14 +1316,21 @@ zfs_zinactive(znode_t *zp) mutex_enter(&zp->z_lock); /* - * If this was the last reference to a file with no links, - * remove the file from the file system. + * If this was the last reference to a file with no links, remove + * the file from the file system unless the file system is mounted + * read-only. That can happen, for example, if the file system was + * originally read-write, the file was opened, then unlinked and + * the file system was made read-only before the file was finally + * closed. The file will remain in the unlinked set. */ if (zp->z_unlinked) { - mutex_exit(&zp->z_lock); - zfs_znode_hold_exit(zfsvfs, zh); - zfs_rmnode(zp); - return; + ASSERT(!zfsvfs->z_issnap); + if (!zfs_is_readonly(zfsvfs)) { + mutex_exit(&zp->z_lock); + zfs_znode_hold_exit(zfsvfs, zh); + zfs_rmnode(zp); + return; + } } mutex_exit(&zp->z_lock);