diff options
author | Chunwei Chen <[email protected]> | 2016-10-12 17:30:46 -0700 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2016-11-04 10:46:40 -0700 |
commit | 987014903f9d36783547188b6ad00f01d9a076bd (patch) | |
tree | b128ec88349ecf9eae9fb75b11bc9e33d5e51f6b /module/zfs/zfs_log.c | |
parent | 7f547f85fe783a6ac69ce250b361436b9c4888a6 (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.c | 38 |
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)); |