summaryrefslogtreecommitdiffstats
path: root/module/zfs/zfs_log.c
diff options
context:
space:
mode:
authorChunwei Chen <[email protected]>2016-10-12 17:30:46 -0700
committerBrian Behlendorf <[email protected]>2016-11-04 10:46:40 -0700
commit987014903f9d36783547188b6ad00f01d9a076bd (patch)
treeb128ec88349ecf9eae9fb75b11bc9e33d5e51f6b /module/zfs/zfs_log.c
parent7f547f85fe783a6ac69ce250b361436b9c4888a6 (diff)
Fix unlinked file cannot do xattr operations
Currently, doing things like fsetxattr(2) on an unlinked file will result in ENODATA. There's two places that cause this: zfs_dirent_lock and zfs_zget. The fix in zfs_dirent_lock is pretty straightforward. In zfs_zget though, we need it to not return error when the zp is unlinked. This is a pretty big change in behavior, but skimming through all the callers, I don't think this change would cause any problem. Also there's nothing preventing z_unlinked from being set after the z_lock mutex is dropped before but before zfs_zget returns anyway. The rest of the stuff is to make sure we don't log xattr stuff when owner is unlinked. Signed-off-by: Chunwei Chen <[email protected]>
Diffstat (limited to 'module/zfs/zfs_log.c')
-rw-r--r--module/zfs/zfs_log.c38
1 files changed, 34 insertions, 4 deletions
diff --git a/module/zfs/zfs_log.c b/module/zfs/zfs_log.c
index 69efb3c16..8bb8b0a97 100644
--- a/module/zfs/zfs_log.c
+++ b/module/zfs/zfs_log.c
@@ -212,6 +212,34 @@ zfs_log_fuid_domains(zfs_fuid_info_t *fuidp, void *start)
}
/*
+ * If zp is an xattr node, check whether the xattr owner is unlinked.
+ * We don't want to log anything if the owner is unlinked.
+ */
+static int
+zfs_xattr_owner_unlinked(znode_t *zp)
+{
+ int unlinked = 0;
+ znode_t *dzp;
+ igrab(ZTOI(zp));
+ /*
+ * if zp is XATTR node, keep walking up via z_xattr_parent until we
+ * get the owner
+ */
+ while (zp->z_pflags & ZFS_XATTR) {
+ ASSERT3U(zp->z_xattr_parent, !=, 0);
+ if (zfs_zget(ZTOZSB(zp), zp->z_xattr_parent, &dzp) != 0) {
+ unlinked = 1;
+ break;
+ }
+ iput(ZTOI(zp));
+ zp = dzp;
+ unlinked = zp->z_unlinked;
+ }
+ iput(ZTOI(zp));
+ return (unlinked);
+}
+
+/*
* Handles TX_CREATE, TX_CREATE_ATTR, TX_MKDIR, TX_MKDIR_ATTR and
* TK_MKXATTR transactions.
*
@@ -247,7 +275,7 @@ zfs_log_create(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype,
size_t namesize = strlen(name) + 1;
size_t fuidsz = 0;
- if (zil_replaying(zilog, tx))
+ if (zil_replaying(zilog, tx) || zfs_xattr_owner_unlinked(dzp))
return;
/*
@@ -352,7 +380,7 @@ zfs_log_remove(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype,
lr_remove_t *lr;
size_t namesize = strlen(name) + 1;
- if (zil_replaying(zilog, tx))
+ if (zil_replaying(zilog, tx) || zfs_xattr_owner_unlinked(dzp))
return;
itx = zil_itx_create(txtype, sizeof (*lr) + namesize);
@@ -463,7 +491,8 @@ zfs_log_write(zilog_t *zilog, dmu_tx_t *tx, int txtype,
uintptr_t fsync_cnt;
ssize_t immediate_write_sz;
- if (zil_replaying(zilog, tx) || zp->z_unlinked) {
+ if (zil_replaying(zilog, tx) || zp->z_unlinked ||
+ zfs_xattr_owner_unlinked(zp)) {
if (callback != NULL)
callback(callback_data);
return;
@@ -543,7 +572,8 @@ zfs_log_truncate(zilog_t *zilog, dmu_tx_t *tx, int txtype,
itx_t *itx;
lr_truncate_t *lr;
- if (zil_replaying(zilog, tx) || zp->z_unlinked)
+ if (zil_replaying(zilog, tx) || zp->z_unlinked ||
+ zfs_xattr_owner_unlinked(zp))
return;
itx = zil_itx_create(txtype, sizeof (*lr));