Skip to content

Commit 9870149

Browse files
Chunwei Chenbehlendorf
Chunwei Chen
authored andcommitted
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 <david.chen@osnexus.com>
1 parent 7f547f8 commit 9870149

File tree

5 files changed

+68
-42
lines changed

5 files changed

+68
-42
lines changed

include/sys/zfs_znode.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ typedef struct znode {
194194
zfs_acl_t *z_acl_cached; /* cached acl */
195195
krwlock_t z_xattr_lock; /* xattr data lock */
196196
nvlist_t *z_xattr_cached; /* cached xattrs */
197+
uint64_t z_xattr_parent; /* parent obj for this xattr */
197198
list_node_t z_link_node; /* all znodes in fs link */
198199
sa_handle_t *z_sa_hdl; /* handle to sa data */
199200
boolean_t z_is_sa; /* are we native sa? */

module/zfs/zfs_acl.c

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2492,15 +2492,8 @@ zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr)
24922492
* If attribute then validate against base file
24932493
*/
24942494
if (is_attr) {
2495-
uint64_t parent;
2496-
2497-
if ((error = sa_lookup(zp->z_sa_hdl,
2498-
SA_ZPL_PARENT(ZTOZSB(zp)), &parent,
2499-
sizeof (parent))) != 0)
2500-
return (error);
2501-
25022495
if ((error = zfs_zget(ZTOZSB(zp),
2503-
parent, &xzp)) != 0) {
2496+
zp->z_xattr_parent, &xzp)) != 0) {
25042497
return (error);
25052498
}
25062499

module/zfs/zfs_dir.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ zfs_dirent_lock(zfs_dirlock_t **dlpp, znode_t *dzp, char *name, znode_t **zpp,
240240

241241
mutex_enter(&dzp->z_lock);
242242
for (;;) {
243-
if (dzp->z_unlinked) {
243+
if (dzp->z_unlinked && !(flag & ZXATTR)) {
244244
mutex_exit(&dzp->z_lock);
245245
if (!(flag & ZHAVELOCK))
246246
rw_exit(&dzp->z_name_lock);
@@ -998,8 +998,9 @@ zfs_make_xattrdir(znode_t *zp, vattr_t *vap, struct inode **xipp, cred_t *cr)
998998
VERIFY(0 == sa_update(zp->z_sa_hdl, SA_ZPL_XATTR(zsb), &xzp->z_id,
999999
sizeof (xzp->z_id), tx));
10001000

1001-
(void) zfs_log_create(zsb->z_log, tx, TX_MKXATTR, zp,
1002-
xzp, "", NULL, acl_ids.z_fuidp, vap);
1001+
if (!zp->z_unlinked)
1002+
(void) zfs_log_create(zsb->z_log, tx, TX_MKXATTR, zp,
1003+
xzp, "", NULL, acl_ids.z_fuidp, vap);
10031004

10041005
zfs_acl_ids_free(&acl_ids);
10051006
dmu_tx_commit(tx);

module/zfs/zfs_log.c

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,34 @@ zfs_log_fuid_domains(zfs_fuid_info_t *fuidp, void *start)
211211
return (start);
212212
}
213213

214+
/*
215+
* If zp is an xattr node, check whether the xattr owner is unlinked.
216+
* We don't want to log anything if the owner is unlinked.
217+
*/
218+
static int
219+
zfs_xattr_owner_unlinked(znode_t *zp)
220+
{
221+
int unlinked = 0;
222+
znode_t *dzp;
223+
igrab(ZTOI(zp));
224+
/*
225+
* if zp is XATTR node, keep walking up via z_xattr_parent until we
226+
* get the owner
227+
*/
228+
while (zp->z_pflags & ZFS_XATTR) {
229+
ASSERT3U(zp->z_xattr_parent, !=, 0);
230+
if (zfs_zget(ZTOZSB(zp), zp->z_xattr_parent, &dzp) != 0) {
231+
unlinked = 1;
232+
break;
233+
}
234+
iput(ZTOI(zp));
235+
zp = dzp;
236+
unlinked = zp->z_unlinked;
237+
}
238+
iput(ZTOI(zp));
239+
return (unlinked);
240+
}
241+
214242
/*
215243
* Handles TX_CREATE, TX_CREATE_ATTR, TX_MKDIR, TX_MKDIR_ATTR and
216244
* TK_MKXATTR transactions.
@@ -247,7 +275,7 @@ zfs_log_create(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype,
247275
size_t namesize = strlen(name) + 1;
248276
size_t fuidsz = 0;
249277

250-
if (zil_replaying(zilog, tx))
278+
if (zil_replaying(zilog, tx) || zfs_xattr_owner_unlinked(dzp))
251279
return;
252280

253281
/*
@@ -352,7 +380,7 @@ zfs_log_remove(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype,
352380
lr_remove_t *lr;
353381
size_t namesize = strlen(name) + 1;
354382

355-
if (zil_replaying(zilog, tx))
383+
if (zil_replaying(zilog, tx) || zfs_xattr_owner_unlinked(dzp))
356384
return;
357385

358386
itx = zil_itx_create(txtype, sizeof (*lr) + namesize);
@@ -463,7 +491,8 @@ zfs_log_write(zilog_t *zilog, dmu_tx_t *tx, int txtype,
463491
uintptr_t fsync_cnt;
464492
ssize_t immediate_write_sz;
465493

466-
if (zil_replaying(zilog, tx) || zp->z_unlinked) {
494+
if (zil_replaying(zilog, tx) || zp->z_unlinked ||
495+
zfs_xattr_owner_unlinked(zp)) {
467496
if (callback != NULL)
468497
callback(callback_data);
469498
return;
@@ -543,7 +572,8 @@ zfs_log_truncate(zilog_t *zilog, dmu_tx_t *tx, int txtype,
543572
itx_t *itx;
544573
lr_truncate_t *lr;
545574

546-
if (zil_replaying(zilog, tx) || zp->z_unlinked)
575+
if (zil_replaying(zilog, tx) || zp->z_unlinked ||
576+
zfs_xattr_owner_unlinked(zp))
547577
return;
548578

549579
itx = zil_itx_create(txtype, sizeof (*lr));

module/zfs/zfs_znode.c

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ zfs_znode_cache_constructor(void *buf, void *arg, int kmflags)
119119
zp->z_dirlocks = NULL;
120120
zp->z_acl_cached = NULL;
121121
zp->z_xattr_cached = NULL;
122+
zp->z_xattr_parent = 0;
122123
zp->z_moved = 0;
123124
return (0);
124125
}
@@ -590,6 +591,10 @@ zfs_znode_alloc(zfs_sb_t *zsb, dmu_buf_t *db, int blksz,
590591
zfs_uid_write(ip, z_uid);
591592
zfs_gid_write(ip, z_gid);
592593

594+
/* Cache the xattr parent id */
595+
if (zp->z_pflags & ZFS_XATTR)
596+
zp->z_xattr_parent = parent;
597+
593598
ZFS_TIME_DECODE(&ip->i_atime, atime);
594599
ZFS_TIME_DECODE(&ip->i_mtime, mtime);
595600
ZFS_TIME_DECODE(&ip->i_ctime, ctime);
@@ -1060,34 +1065,30 @@ zfs_zget(zfs_sb_t *zsb, uint64_t obj_num, znode_t **zpp)
10601065

10611066
mutex_enter(&zp->z_lock);
10621067
ASSERT3U(zp->z_id, ==, obj_num);
1063-
if (zp->z_unlinked) {
1064-
err = SET_ERROR(ENOENT);
1065-
} else {
1066-
/*
1067-
* If igrab() returns NULL the VFS has independently
1068-
* determined the inode should be evicted and has
1069-
* called iput_final() to start the eviction process.
1070-
* The SA handle is still valid but because the VFS
1071-
* requires that the eviction succeed we must drop
1072-
* our locks and references to allow the eviction to
1073-
* complete. The zfs_zget() may then be retried.
1074-
*
1075-
* This unlikely case could be optimized by registering
1076-
* a sops->drop_inode() callback. The callback would
1077-
* need to detect the active SA hold thereby informing
1078-
* the VFS that this inode should not be evicted.
1079-
*/
1080-
if (igrab(ZTOI(zp)) == NULL) {
1081-
mutex_exit(&zp->z_lock);
1082-
sa_buf_rele(db, NULL);
1083-
zfs_znode_hold_exit(zsb, zh);
1084-
/* inode might need this to finish evict */
1085-
cond_resched();
1086-
goto again;
1087-
}
1088-
*zpp = zp;
1089-
err = 0;
1068+
/*
1069+
* If igrab() returns NULL the VFS has independently
1070+
* determined the inode should be evicted and has
1071+
* called iput_final() to start the eviction process.
1072+
* The SA handle is still valid but because the VFS
1073+
* requires that the eviction succeed we must drop
1074+
* our locks and references to allow the eviction to
1075+
* complete. The zfs_zget() may then be retried.
1076+
*
1077+
* This unlikely case could be optimized by registering
1078+
* a sops->drop_inode() callback. The callback would
1079+
* need to detect the active SA hold thereby informing
1080+
* the VFS that this inode should not be evicted.
1081+
*/
1082+
if (igrab(ZTOI(zp)) == NULL) {
1083+
mutex_exit(&zp->z_lock);
1084+
sa_buf_rele(db, NULL);
1085+
zfs_znode_hold_exit(zsb, zh);
1086+
/* inode might need this to finish evict */
1087+
cond_resched();
1088+
goto again;
10901089
}
1090+
*zpp = zp;
1091+
err = 0;
10911092
mutex_exit(&zp->z_lock);
10921093
sa_buf_rele(db, NULL);
10931094
zfs_znode_hold_exit(zsb, zh);

0 commit comments

Comments
 (0)