summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--module/zfs/zfs_znode.c33
1 files changed, 26 insertions, 7 deletions
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);