Home | History | Annotate | Download | only in fs
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"@(#)xattr.c	1.5	08/01/04 SMI"
     27 
     28 #include <sys/param.h>
     29 #include <sys/isa_defs.h>
     30 #include <sys/types.h>
     31 #include <sys/sysmacros.h>
     32 #include <sys/cred.h>
     33 #include <sys/systm.h>
     34 #include <sys/errno.h>
     35 #include <sys/fcntl.h>
     36 #include <sys/pathname.h>
     37 #include <sys/stat.h>
     38 #include <sys/vfs.h>
     39 #include <sys/acl.h>
     40 #include <sys/file.h>
     41 #include <sys/sunddi.h>
     42 #include <sys/debug.h>
     43 #include <sys/cmn_err.h>
     44 #include <sys/vnode.h>
     45 #include <sys/mode.h>
     46 #include <sys/nvpair.h>
     47 #include <sys/attr.h>
     48 #include <sys/gfs.h>
     49 #include <sys/mutex.h>
     50 #include <fs/fs_subr.h>
     51 #include <sys/kidmap.h>
     52 
     53 typedef struct {
     54 	gfs_file_t	gfs_private;
     55 	xattr_view_t	xattr_view;
     56 } xattr_file_t;
     57 
     58 /* ARGSUSED */
     59 static int
     60 xattr_file_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
     61 {
     62 	xattr_file_t *np = (*vpp)->v_data;
     63 
     64 	if ((np->xattr_view == XATTR_VIEW_READONLY) && (flags & FWRITE))
     65 		return (EACCES);
     66 
     67 	return (0);
     68 }
     69 
     70 /* ARGSUSED */
     71 static int
     72 xattr_file_access(vnode_t *vp, int mode, int flags, cred_t *cr,
     73     caller_context_t *ct)
     74 {
     75 	xattr_file_t *np = vp->v_data;
     76 
     77 	if ((np->xattr_view == XATTR_VIEW_READONLY) && (mode & VWRITE))
     78 		return (EACCES);
     79 
     80 	return (0);
     81 }
     82 
     83 /* ARGSUSED */
     84 static int
     85 xattr_file_close(vnode_t *vp, int flags, int count, offset_t off,
     86     cred_t *cr, caller_context_t *ct)
     87 {
     88 	cleanlocks(vp, ddi_get_pid(), 0);
     89 	cleanshares(vp, ddi_get_pid());
     90 	return (0);
     91 }
     92 
     93 static int
     94 xattr_common_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
     95 {
     96 	xattr_fid_t	*xfidp;
     97 	vnode_t		*pvp, *savevp;
     98 	int		error;
     99 	uint16_t	orig_len;
    100 
    101 	if (fidp->fid_len < XATTR_FIDSZ) {
    102 		fidp->fid_len = XATTR_FIDSZ;
    103 		return (ENOSPC);
    104 	}
    105 
    106 	savevp = pvp = gfs_file_parent(vp);
    107 	mutex_enter(&savevp->v_lock);
    108 	if (pvp->v_flag & V_XATTRDIR) {
    109 		pvp = gfs_file_parent(pvp);
    110 	}
    111 	mutex_exit(&savevp->v_lock);
    112 
    113 	xfidp = (xattr_fid_t *)fidp;
    114 	orig_len = fidp->fid_len;
    115 	fidp->fid_len = sizeof (xfidp->parent_fid);
    116 
    117 	error = VOP_FID(pvp, fidp, ct);
    118 	if (error) {
    119 		fidp->fid_len = orig_len;
    120 		return (error);
    121 	}
    122 
    123 	xfidp->parent_len = fidp->fid_len;
    124 	fidp->fid_len = XATTR_FIDSZ;
    125 	xfidp->dir_offset = gfs_file_inode(vp);
    126 
    127 	return (0);
    128 }
    129 
    130 /* ARGSUSED */
    131 static int
    132 xattr_fill_nvlist(vnode_t *vp, xattr_view_t xattr_view, nvlist_t *nvlp,
    133     cred_t *cr, caller_context_t *ct)
    134 {
    135 	int error;
    136 	f_attr_t attr;
    137 	uint64_t fsid;
    138 	xvattr_t xvattr;
    139 	xoptattr_t *xoap;	/* Pointer to optional attributes */
    140 	vnode_t *ppvp;
    141 	const char *domain;
    142 	uint32_t rid;
    143 
    144 	xva_init(&xvattr);
    145 
    146 	if ((xoap = xva_getxoptattr(&xvattr)) == NULL)
    147 		return (EINVAL);
    148 
    149 	/*
    150 	 * For detecting ephemeral uid/gid
    151 	 */
    152 	xvattr.xva_vattr.va_mask |= (AT_UID|AT_GID);
    153 
    154 	/*
    155 	 * We need to access the real fs object.
    156 	 * vp points to a GFS file; ppvp points to the real object.
    157 	 */
    158 	ppvp = gfs_file_parent(gfs_file_parent(vp));
    159 
    160 	/*
    161 	 * Iterate through the attrs associated with this view
    162 	 */
    163 
    164 	for (attr = 0; attr < F_ATTR_ALL; attr++) {
    165 		if (xattr_view != attr_to_xattr_view(attr)) {
    166 			continue;
    167 		}
    168 
    169 		switch (attr) {
    170 		case F_SYSTEM:
    171 			XVA_SET_REQ(&xvattr, XAT_SYSTEM);
    172 			break;
    173 		case F_READONLY:
    174 			XVA_SET_REQ(&xvattr, XAT_READONLY);
    175 			break;
    176 		case F_HIDDEN:
    177 			XVA_SET_REQ(&xvattr, XAT_HIDDEN);
    178 			break;
    179 		case F_ARCHIVE:
    180 			XVA_SET_REQ(&xvattr, XAT_ARCHIVE);
    181 			break;
    182 		case F_IMMUTABLE:
    183 			XVA_SET_REQ(&xvattr, XAT_IMMUTABLE);
    184 			break;
    185 		case F_APPENDONLY:
    186 			XVA_SET_REQ(&xvattr, XAT_APPENDONLY);
    187 			break;
    188 		case F_NOUNLINK:
    189 			XVA_SET_REQ(&xvattr, XAT_NOUNLINK);
    190 			break;
    191 		case F_OPAQUE:
    192 			XVA_SET_REQ(&xvattr, XAT_OPAQUE);
    193 			break;
    194 		case F_NODUMP:
    195 			XVA_SET_REQ(&xvattr, XAT_NODUMP);
    196 			break;
    197 		case F_AV_QUARANTINED:
    198 			XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
    199 			break;
    200 		case F_AV_MODIFIED:
    201 			XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
    202 			break;
    203 		case F_AV_SCANSTAMP:
    204 			if (ppvp->v_type == VREG)
    205 				XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
    206 			break;
    207 		case F_CRTIME:
    208 			XVA_SET_REQ(&xvattr, XAT_CREATETIME);
    209 			break;
    210 		case F_FSID:
    211 			fsid = (((uint64_t)vp->v_vfsp->vfs_fsid.val[0] << 32) |
    212 			    (uint64_t)(vp->v_vfsp->vfs_fsid.val[1] &
    213 			    0xffffffff));
    214 			VERIFY(nvlist_add_uint64(nvlp, attr_to_name(attr),
    215 			    fsid) == 0);
    216 			break;
    217 		default:
    218 			break;
    219 		}
    220 	}
    221 
    222 	error = VOP_GETATTR(ppvp, &xvattr.xva_vattr, 0, cr, ct);
    223 	if (error)
    224 		return (error);
    225 
    226 	/*
    227 	 * Process all the optional attributes together here.  Notice that
    228 	 * xoap was set when the optional attribute bits were set above.
    229 	 */
    230 	if ((xvattr.xva_vattr.va_mask & AT_XVATTR) && xoap) {
    231 		if (XVA_ISSET_RTN(&xvattr, XAT_READONLY)) {
    232 			VERIFY(nvlist_add_boolean_value(nvlp,
    233 			    attr_to_name(F_READONLY),
    234 			    xoap->xoa_readonly) == 0);
    235 		}
    236 		if (XVA_ISSET_RTN(&xvattr, XAT_HIDDEN)) {
    237 			VERIFY(nvlist_add_boolean_value(nvlp,
    238 			    attr_to_name(F_HIDDEN),
    239 			    xoap->xoa_hidden) == 0);
    240 		}
    241 		if (XVA_ISSET_RTN(&xvattr, XAT_SYSTEM)) {
    242 			VERIFY(nvlist_add_boolean_value(nvlp,
    243 			    attr_to_name(F_SYSTEM),
    244 			    xoap->xoa_system) == 0);
    245 		}
    246 		if (XVA_ISSET_RTN(&xvattr, XAT_ARCHIVE)) {
    247 			VERIFY(nvlist_add_boolean_value(nvlp,
    248 			    attr_to_name(F_ARCHIVE),
    249 			    xoap->xoa_archive) == 0);
    250 		}
    251 		if (XVA_ISSET_RTN(&xvattr, XAT_IMMUTABLE)) {
    252 			VERIFY(nvlist_add_boolean_value(nvlp,
    253 			    attr_to_name(F_IMMUTABLE),
    254 			    xoap->xoa_immutable) == 0);
    255 		}
    256 		if (XVA_ISSET_RTN(&xvattr, XAT_NOUNLINK)) {
    257 			VERIFY(nvlist_add_boolean_value(nvlp,
    258 			    attr_to_name(F_NOUNLINK),
    259 			    xoap->xoa_nounlink) == 0);
    260 		}
    261 		if (XVA_ISSET_RTN(&xvattr, XAT_APPENDONLY)) {
    262 			VERIFY(nvlist_add_boolean_value(nvlp,
    263 			    attr_to_name(F_APPENDONLY),
    264 			    xoap->xoa_appendonly) == 0);
    265 		}
    266 		if (XVA_ISSET_RTN(&xvattr, XAT_NODUMP)) {
    267 			VERIFY(nvlist_add_boolean_value(nvlp,
    268 			    attr_to_name(F_NODUMP),
    269 			    xoap->xoa_nodump) == 0);
    270 		}
    271 		if (XVA_ISSET_RTN(&xvattr, XAT_OPAQUE)) {
    272 			VERIFY(nvlist_add_boolean_value(nvlp,
    273 			    attr_to_name(F_OPAQUE),
    274 			    xoap->xoa_opaque) == 0);
    275 		}
    276 		if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED)) {
    277 			VERIFY(nvlist_add_boolean_value(nvlp,
    278 			    attr_to_name(F_AV_QUARANTINED),
    279 			    xoap->xoa_av_quarantined) == 0);
    280 		}
    281 		if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED)) {
    282 			VERIFY(nvlist_add_boolean_value(nvlp,
    283 			    attr_to_name(F_AV_MODIFIED),
    284 			    xoap->xoa_av_modified) == 0);
    285 		}
    286 		if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP)) {
    287 			VERIFY(nvlist_add_uint8_array(nvlp,
    288 			    attr_to_name(F_AV_SCANSTAMP),
    289 			    xoap->xoa_av_scanstamp,
    290 			    sizeof (xoap->xoa_av_scanstamp)) == 0);
    291 		}
    292 		if (XVA_ISSET_RTN(&xvattr, XAT_CREATETIME)) {
    293 			VERIFY(nvlist_add_uint64_array(nvlp,
    294 			    attr_to_name(F_CRTIME),
    295 			    (uint64_t *)&(xoap->xoa_createtime),
    296 			    sizeof (xoap->xoa_createtime) /
    297 			    sizeof (uint64_t)) == 0);
    298 		}
    299 	}
    300 	/*
    301 	 * Check for optional ownersid/groupsid
    302 	 */
    303 
    304 	if (xvattr.xva_vattr.va_uid > MAXUID) {
    305 		nvlist_t *nvl_sid;
    306 
    307 		if (nvlist_alloc(&nvl_sid, NV_UNIQUE_NAME, KM_SLEEP))
    308 			return (ENOMEM);
    309 
    310 		if (kidmap_getsidbyuid(crgetzone(cr), xvattr.xva_vattr.va_uid,
    311 		    &domain, &rid) == 0) {
    312 			VERIFY(nvlist_add_string(nvl_sid,
    313 			    SID_DOMAIN, domain) == 0);
    314 			VERIFY(nvlist_add_uint32(nvl_sid, SID_RID, rid) == 0);
    315 			VERIFY(nvlist_add_nvlist(nvlp, attr_to_name(F_OWNERSID),
    316 			    nvl_sid) == 0);
    317 		}
    318 		nvlist_free(nvl_sid);
    319 	}
    320 	if (xvattr.xva_vattr.va_gid > MAXUID) {
    321 		nvlist_t *nvl_sid;
    322 
    323 		if (nvlist_alloc(&nvl_sid, NV_UNIQUE_NAME, KM_SLEEP))
    324 			return (ENOMEM);
    325 
    326 		if (kidmap_getsidbygid(crgetzone(cr), xvattr.xva_vattr.va_gid,
    327 		    &domain, &rid) == 0) {
    328 			VERIFY(nvlist_add_string(nvl_sid,
    329 			    SID_DOMAIN, domain) == 0);
    330 			VERIFY(nvlist_add_uint32(nvl_sid, SID_RID, rid) == 0);
    331 			VERIFY(nvlist_add_nvlist(nvlp, attr_to_name(F_GROUPSID),
    332 			    nvl_sid) == 0);
    333 		}
    334 		nvlist_free(nvl_sid);
    335 	}
    336 
    337 	return (0);
    338 }
    339 
    340 /*
    341  * The size of a sysattr file is the size of the nvlist that will be
    342  * returned by xattr_file_read().  A call to xattr_file_write() could
    343  * change the size of that nvlist.  That size is not stored persistently
    344  * so xattr_fill_nvlist() calls VOP_GETATTR so that it can be calculated.
    345  */
    346 static int
    347 xattr_file_size(vnode_t *vp, xattr_view_t xattr_view, size_t *size,
    348     cred_t *cr, caller_context_t *ct)
    349 {
    350 	nvlist_t *nvl;
    351 
    352 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP)) {
    353 		return (ENOMEM);
    354 	}
    355 
    356 	if (xattr_fill_nvlist(vp, xattr_view, nvl, cr, ct)) {
    357 		nvlist_free(nvl);
    358 		return (EFAULT);
    359 	}
    360 
    361 	VERIFY(nvlist_size(nvl, size, NV_ENCODE_XDR) == 0);
    362 	nvlist_free(nvl);
    363 	return (0);
    364 }
    365 
    366 /* ARGSUSED */
    367 static int
    368 xattr_file_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
    369     caller_context_t *ct)
    370 {
    371 	xattr_file_t *np = vp->v_data;
    372 	timestruc_t now;
    373 	size_t size;
    374 	int error;
    375 	vnode_t *pvp;
    376 	vattr_t pvattr;
    377 
    378 	vap->va_type = VREG;
    379 	vap->va_mode = MAKEIMODE(vap->va_type,
    380 	    (np->xattr_view == XATTR_VIEW_READONLY ? 0444 : 0644));
    381 	vap->va_nodeid = gfs_file_inode(vp);
    382 	vap->va_nlink = 1;
    383 	pvp = gfs_file_parent(vp);
    384 	(void) memset(&pvattr, 0, sizeof (pvattr));
    385 	pvattr.va_mask = AT_CTIME|AT_MTIME;
    386 	error = VOP_GETATTR(pvp, &pvattr, flags, cr, ct);
    387 	if (error) {
    388 		return (error);
    389 	}
    390 	vap->va_ctime = pvattr.va_ctime;
    391 	vap->va_mtime = pvattr.va_mtime;
    392 	gethrestime(&now);
    393 	vap->va_atime = now;
    394 	vap->va_uid = 0;
    395 	vap->va_gid = 0;
    396 	vap->va_rdev = 0;
    397 	vap->va_blksize = DEV_BSIZE;
    398 	vap->va_seq = 0;
    399 	vap->va_fsid = vp->v_vfsp->vfs_dev;
    400 	error = xattr_file_size(vp, np->xattr_view, &size, cr, ct);
    401 	vap->va_size = size;
    402 	vap->va_nblocks = howmany(vap->va_size, vap->va_blksize);
    403 	return (error);
    404 }
    405 
    406 /* ARGSUSED */
    407 static int
    408 xattr_file_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
    409     caller_context_t *ct)
    410 {
    411 	xattr_file_t *np = vp->v_data;
    412 	xattr_view_t xattr_view = np->xattr_view;
    413 	char *buf;
    414 	size_t filesize;
    415 	nvlist_t *nvl;
    416 	int error;
    417 
    418 	/*
    419 	 * Validate file offset and fasttrack empty reads
    420 	 */
    421 	if (uiop->uio_loffset < (offset_t)0)
    422 		return (EINVAL);
    423 
    424 	if (uiop->uio_resid == 0)
    425 		return (0);
    426 
    427 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP))
    428 		return (ENOMEM);
    429 
    430 	if (xattr_fill_nvlist(vp, xattr_view, nvl, cr, ct)) {
    431 		nvlist_free(nvl);
    432 		return (EFAULT);
    433 	}
    434 
    435 	VERIFY(nvlist_size(nvl, &filesize, NV_ENCODE_XDR) == 0);
    436 
    437 	if (uiop->uio_loffset >= filesize) {
    438 		nvlist_free(nvl);
    439 		return (0);
    440 	}
    441 
    442 	buf = kmem_alloc(filesize, KM_SLEEP);
    443 	VERIFY(nvlist_pack(nvl, &buf, &filesize, NV_ENCODE_XDR,
    444 	    KM_SLEEP) == 0);
    445 
    446 	error = uiomove((caddr_t)buf, filesize, UIO_READ, uiop);
    447 	kmem_free(buf, filesize);
    448 	nvlist_free(nvl);
    449 	return (error);
    450 }
    451 
    452 /* ARGSUSED */
    453 static int
    454 xattr_file_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
    455     caller_context_t *ct)
    456 {
    457 	int error = 0;
    458 	char *buf;
    459 	char *domain;
    460 	uint32_t rid;
    461 	ssize_t size = uiop->uio_resid;
    462 	nvlist_t *nvp;
    463 	nvpair_t *pair = NULL;
    464 	vnode_t *ppvp;
    465 	xvattr_t xvattr;
    466 	xoptattr_t *xoap = NULL;	/* Pointer to optional attributes */
    467 
    468 	/*
    469 	 * Validate file offset and size.
    470 	 */
    471 	if (uiop->uio_loffset < (offset_t)0)
    472 		return (EINVAL);
    473 
    474 	if (size == 0)
    475 		return (EINVAL);
    476 
    477 	xva_init(&xvattr);
    478 
    479 	if ((xoap = xva_getxoptattr(&xvattr)) == NULL) {
    480 		return (EINVAL);
    481 	}
    482 
    483 	/*
    484 	 * Copy and unpack the nvlist
    485 	 */
    486 	buf = kmem_alloc(size, KM_SLEEP);
    487 	if (uiomove((caddr_t)buf, size, UIO_WRITE, uiop)) {
    488 		return (EFAULT);
    489 	}
    490 
    491 	if (nvlist_unpack(buf, size, &nvp, KM_SLEEP) != 0) {
    492 		kmem_free(buf, size);
    493 		uiop->uio_resid = size;
    494 		return (EINVAL);
    495 	}
    496 	kmem_free(buf, size);
    497 
    498 	/*
    499 	 * Fasttrack empty writes (nvlist with no nvpairs)
    500 	 */
    501 	if (nvlist_next_nvpair(nvp, NULL) == 0)
    502 		return (0);
    503 
    504 	ppvp = gfs_file_parent(gfs_file_parent(vp));
    505 
    506 	while (pair = nvlist_next_nvpair(nvp, pair)) {
    507 		data_type_t type;
    508 		f_attr_t attr;
    509 		boolean_t value;
    510 		uint64_t *time, *times;
    511 		uint_t elem, nelems;
    512 		nvlist_t *nvp_sid;
    513 		uint8_t *scanstamp;
    514 
    515 		/*
    516 		 * Validate the name and type of each attribute.
    517 		 * Log any unknown names and continue.  This will
    518 		 * help if additional attributes are added later.
    519 		 */
    520 		type = nvpair_type(pair);
    521 		if ((attr = name_to_attr(nvpair_name(pair))) == F_ATTR_INVAL) {
    522 			cmn_err(CE_WARN, "Unknown attribute %s",
    523 			    nvpair_name(pair));
    524 			continue;
    525 		}
    526 
    527 		/*
    528 		 * Verify nvlist type matches required type and view is OK
    529 		 */
    530 
    531 		if (type != attr_to_data_type(attr) ||
    532 		    (attr_to_xattr_view(attr) == XATTR_VIEW_READONLY)) {
    533 			nvlist_free(nvp);
    534 			return (EINVAL);
    535 		}
    536 
    537 		/*
    538 		 * For OWNERSID/GROUPSID make sure the target
    539 		 * file system support ephemeral ID's
    540 		 */
    541 		if ((attr == F_OWNERSID || attr == F_GROUPSID) &&
    542 		    (!(vp->v_vfsp->vfs_flag & VFS_XID))) {
    543 			nvlist_free(nvp);
    544 			return (EINVAL);
    545 		}
    546 
    547 		/*
    548 		 * Retrieve data from nvpair
    549 		 */
    550 		switch (type) {
    551 		case DATA_TYPE_BOOLEAN_VALUE:
    552 			if (nvpair_value_boolean_value(pair, &value)) {
    553 				nvlist_free(nvp);
    554 				return (EINVAL);
    555 			}
    556 			break;
    557 		case DATA_TYPE_UINT64_ARRAY:
    558 			if (nvpair_value_uint64_array(pair, &times, &nelems)) {
    559 				nvlist_free(nvp);
    560 				return (EINVAL);
    561 			}
    562 			break;
    563 		case DATA_TYPE_NVLIST:
    564 			if (nvpair_value_nvlist(pair, &nvp_sid)) {
    565 				nvlist_free(nvp);
    566 				return (EINVAL);
    567 			}
    568 			break;
    569 		case DATA_TYPE_UINT8_ARRAY:
    570 			if (nvpair_value_uint8_array(pair,
    571 			    &scanstamp, &nelems)) {
    572 				nvlist_free(nvp);
    573 				return (EINVAL);
    574 			}
    575 			break;
    576 		default:
    577 			nvlist_free(nvp);
    578 			return (EINVAL);
    579 		}
    580 
    581 		switch (attr) {
    582 		/*
    583 		 * If we have several similar optional attributes to
    584 		 * process then we should do it all together here so that
    585 		 * xoap and the requested bitmap can be set in one place.
    586 		 */
    587 		case F_READONLY:
    588 			XVA_SET_REQ(&xvattr, XAT_READONLY);
    589 			xoap->xoa_readonly = value;
    590 			break;
    591 		case F_HIDDEN:
    592 			XVA_SET_REQ(&xvattr, XAT_HIDDEN);
    593 			xoap->xoa_hidden = value;
    594 			break;
    595 		case F_SYSTEM:
    596 			XVA_SET_REQ(&xvattr, XAT_SYSTEM);
    597 			xoap->xoa_system = value;
    598 			break;
    599 		case F_ARCHIVE:
    600 			XVA_SET_REQ(&xvattr, XAT_ARCHIVE);
    601 			xoap->xoa_archive = value;
    602 			break;
    603 		case F_IMMUTABLE:
    604 			XVA_SET_REQ(&xvattr, XAT_IMMUTABLE);
    605 			xoap->xoa_immutable = value;
    606 			break;
    607 		case F_NOUNLINK:
    608 			XVA_SET_REQ(&xvattr, XAT_NOUNLINK);
    609 			xoap->xoa_nounlink = value;
    610 			break;
    611 		case F_APPENDONLY:
    612 			XVA_SET_REQ(&xvattr, XAT_APPENDONLY);
    613 			xoap->xoa_appendonly = value;
    614 			break;
    615 		case F_NODUMP:
    616 			XVA_SET_REQ(&xvattr, XAT_NODUMP);
    617 			xoap->xoa_nodump = value;
    618 			break;
    619 		case F_AV_QUARANTINED:
    620 			XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
    621 			xoap->xoa_av_quarantined = value;
    622 			break;
    623 		case F_AV_MODIFIED:
    624 			XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
    625 			xoap->xoa_av_modified = value;
    626 			break;
    627 		case F_CRTIME:
    628 			XVA_SET_REQ(&xvattr, XAT_CREATETIME);
    629 			time = (uint64_t *)&(xoap->xoa_createtime);
    630 			for (elem = 0; elem < nelems; elem++)
    631 				*time++ = times[elem];
    632 			break;
    633 		case F_OWNERSID:
    634 		case F_GROUPSID:
    635 			if (nvlist_lookup_string(nvp_sid, SID_DOMAIN,
    636 			    &domain) || nvlist_lookup_uint32(nvp_sid, SID_RID,
    637 			    &rid)) {
    638 				nvlist_free(nvp);
    639 				return (EINVAL);
    640 			}
    641 
    642 			/*
    643 			 * Now map domain+rid to ephemeral id's
    644 			 *
    645 			 * If mapping fails, then the uid/gid will
    646 			 * be set to UID_NOBODY by Winchester.
    647 			 */
    648 
    649 			if (attr == F_OWNERSID) {
    650 				(void) kidmap_getuidbysid(crgetzone(cr), domain,
    651 				    rid, &xvattr.xva_vattr.va_uid);
    652 				xvattr.xva_vattr.va_mask |= AT_UID;
    653 			} else {
    654 				(void) kidmap_getgidbysid(crgetzone(cr), domain,
    655 				    rid, &xvattr.xva_vattr.va_gid);
    656 				xvattr.xva_vattr.va_mask |= AT_GID;
    657 			}
    658 			break;
    659 		case F_AV_SCANSTAMP:
    660 			if (ppvp->v_type == VREG) {
    661 				XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
    662 				(void) memcpy(xoap->xoa_av_scanstamp,
    663 				    scanstamp, nelems);
    664 			} else {
    665 				nvlist_free(nvp);
    666 				return (EINVAL);
    667 			}
    668 			break;
    669 		default:
    670 			break;
    671 		}
    672 	}
    673 
    674 	ppvp = gfs_file_parent(gfs_file_parent(vp));
    675 	error = VOP_SETATTR(ppvp, &xvattr.xva_vattr, 0, cr, ct);
    676 	if (error)
    677 		uiop->uio_resid = size;
    678 
    679 	nvlist_free(nvp);
    680 	return (error);
    681 }
    682 
    683 static int
    684 xattr_file_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
    685     caller_context_t *ct)
    686 {
    687 	switch (cmd) {
    688 	case _PC_XATTR_EXISTS:
    689 	case _PC_SATTR_ENABLED:
    690 	case _PC_SATTR_EXISTS:
    691 		*valp = 0;
    692 		return (0);
    693 	default:
    694 		return (fs_pathconf(vp, cmd, valp, cr, ct));
    695 	}
    696 }
    697 
    698 vnodeops_t *xattr_file_ops;
    699 
    700 static const fs_operation_def_t xattr_file_tops[] = {
    701 	{ VOPNAME_OPEN,		{ .vop_open = xattr_file_open }		},
    702 	{ VOPNAME_CLOSE,	{ .vop_close = xattr_file_close }	},
    703 	{ VOPNAME_READ,		{ .vop_read = xattr_file_read }		},
    704 	{ VOPNAME_WRITE,	{ .vop_write = xattr_file_write }	},
    705 	{ VOPNAME_IOCTL,	{ .error = fs_ioctl }			},
    706 	{ VOPNAME_GETATTR,	{ .vop_getattr = xattr_file_getattr }	},
    707 	{ VOPNAME_ACCESS,	{ .vop_access = xattr_file_access }	},
    708 	{ VOPNAME_READDIR,	{ .error = fs_notdir }			},
    709 	{ VOPNAME_SEEK,		{ .vop_seek = fs_seek }			},
    710 	{ VOPNAME_INACTIVE,	{ .vop_inactive = gfs_vop_inactive }	},
    711 	{ VOPNAME_FID,		{ .vop_fid = xattr_common_fid }		},
    712 	{ VOPNAME_PATHCONF,	{ .vop_pathconf = xattr_file_pathconf }	},
    713 	{ VOPNAME_PUTPAGE,	{ .error = fs_putpage }			},
    714 	{ VOPNAME_FSYNC,	{ .error = fs_fsync }			},
    715 	{ NULL }
    716 };
    717 
    718 vnode_t *
    719 xattr_mkfile(vnode_t *pvp, xattr_view_t xattr_view)
    720 {
    721 	vnode_t *vp;
    722 	xattr_file_t *np;
    723 
    724 	vp = gfs_file_create(sizeof (xattr_file_t), pvp, xattr_file_ops);
    725 	np = vp->v_data;
    726 	np->xattr_view = xattr_view;
    727 	vp->v_flag |= V_SYSATTR;
    728 	return (vp);
    729 }
    730 
    731 vnode_t *
    732 xattr_mkfile_ro(vnode_t *pvp)
    733 {
    734 	return (xattr_mkfile(pvp, XATTR_VIEW_READONLY));
    735 }
    736 
    737 vnode_t *
    738 xattr_mkfile_rw(vnode_t *pvp)
    739 {
    740 	return (xattr_mkfile(pvp, XATTR_VIEW_READWRITE));
    741 }
    742 
    743 vnodeops_t *xattr_dir_ops;
    744 
    745 static gfs_dirent_t xattr_dirents[] = {
    746 	{ VIEW_READONLY, xattr_mkfile_ro, GFS_CACHE_VNODE, },
    747 	{ VIEW_READWRITE, xattr_mkfile_rw, GFS_CACHE_VNODE, },
    748 	{ NULL },
    749 };
    750 
    751 #define	XATTRDIR_NENTS	((sizeof (xattr_dirents) / sizeof (gfs_dirent_t)) - 1)
    752 
    753 static int
    754 is_sattr_name(char *s)
    755 {
    756 	int i;
    757 
    758 	for (i = 0; i < XATTRDIR_NENTS; ++i) {
    759 		if (strcmp(s, xattr_dirents[i].gfse_name) == 0) {
    760 			return (1);
    761 		}
    762 	}
    763 	return (0);
    764 }
    765 
    766 /*
    767  * Given the name of an extended attribute file, determine if there is a
    768  * normalization conflict with a sysattr view name.
    769  */
    770 int
    771 xattr_sysattr_casechk(char *s)
    772 {
    773 	int i;
    774 
    775 	for (i = 0; i < XATTRDIR_NENTS; ++i) {
    776 		if (strcasecmp(s, xattr_dirents[i].gfse_name) == 0)
    777 			return (1);
    778 	}
    779 	return (0);
    780 }
    781 
    782 static int
    783 xattr_copy(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
    784     cred_t *cr, caller_context_t *ct)
    785 {
    786 	xvattr_t xvattr;
    787 	vnode_t *pdvp;
    788 	int error;
    789 
    790 	/*
    791 	 * Only copy system attrs if the views are the same
    792 	 */
    793 	if (strcmp(snm, tnm) != 0)
    794 		return (EINVAL);
    795 
    796 	xva_init(&xvattr);
    797 
    798 	XVA_SET_REQ(&xvattr, XAT_SYSTEM);
    799 	XVA_SET_REQ(&xvattr, XAT_READONLY);
    800 	XVA_SET_REQ(&xvattr, XAT_HIDDEN);
    801 	XVA_SET_REQ(&xvattr, XAT_ARCHIVE);
    802 	XVA_SET_REQ(&xvattr, XAT_APPENDONLY);
    803 	XVA_SET_REQ(&xvattr, XAT_NOUNLINK);
    804 	XVA_SET_REQ(&xvattr, XAT_IMMUTABLE);
    805 	XVA_SET_REQ(&xvattr, XAT_NODUMP);
    806 	XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
    807 	XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
    808 	XVA_SET_REQ(&xvattr, XAT_CREATETIME);
    809 
    810 	pdvp = gfs_file_parent(sdvp);
    811 	error = VOP_GETATTR(pdvp, &xvattr.xva_vattr, 0, cr, ct);
    812 	if (error)
    813 		return (error);
    814 
    815 	pdvp = gfs_file_parent(tdvp);
    816 	error = VOP_SETATTR(pdvp, &xvattr.xva_vattr, 0, cr, ct);
    817 	return (error);
    818 }
    819 
    820 static int
    821 xattr_dir_realdir(vnode_t *dvp, vnode_t **realdvp, int lookup_flags,
    822     cred_t *cr, caller_context_t *ct)
    823 {
    824 	vnode_t *pvp;
    825 	int error;
    826 	struct pathname pn;
    827 	char *startnm = "";
    828 
    829 	*realdvp = NULL;
    830 
    831 	pvp = gfs_file_parent(dvp);
    832 
    833 	error = pn_get(startnm, UIO_SYSSPACE, &pn);
    834 	if (error) {
    835 		VN_RELE(pvp);
    836 		return (error);
    837 	}
    838 
    839 	/*
    840 	 * Set the LOOKUP_HAVE_SYSATTR_DIR flag so that we don't get into an
    841 	 * infinite loop with fop_lookup calling back to xattr_dir_lookup.
    842 	 */
    843 	lookup_flags |= LOOKUP_HAVE_SYSATTR_DIR;
    844 	error = VOP_LOOKUP(pvp, startnm, realdvp, &pn, lookup_flags,
    845 	    rootvp, cr, ct, NULL, NULL);
    846 	pn_free(&pn);
    847 
    848 	return (error);
    849 }
    850 
    851 /* ARGSUSED */
    852 static int
    853 xattr_dir_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
    854 {
    855 	if (flags & FWRITE) {
    856 		return (EACCES);
    857 	}
    858 
    859 	return (0);
    860 }
    861 
    862 /* ARGSUSED */
    863 static int
    864 xattr_dir_close(vnode_t *vpp, int flags, int count, offset_t off, cred_t *cr,
    865     caller_context_t *ct)
    866 {
    867 	return (0);
    868 }
    869 
    870 /* ARGSUSED */
    871 static int
    872 xattr_dir_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
    873     caller_context_t *ct)
    874 {
    875 	timestruc_t now;
    876 	vnode_t *pvp;
    877 	int error;
    878 	vattr_t pvattr;
    879 
    880 	error = xattr_dir_realdir(vp, &pvp, LOOKUP_XATTR, cr, ct);
    881 	if (error == 0) {
    882 		error = VOP_GETATTR(pvp, vap, 0, cr, ct);
    883 		VN_RELE(pvp);
    884 		if (error) {
    885 			return (error);
    886 		}
    887 		vap->va_nlink += XATTRDIR_NENTS;
    888 		vap->va_size += XATTRDIR_NENTS;
    889 		return (0);
    890 	}
    891 
    892 	/*
    893 	 * There is no real xattr directory.  Cobble together
    894 	 * an entry using info from the parent object.
    895 	 */
    896 	pvp = gfs_file_parent(vp);
    897 	(void) memset(&pvattr, 0, sizeof (pvattr));
    898 	pvattr.va_mask = AT_UID|AT_GID|AT_RDEV|AT_CTIME|AT_MTIME;
    899 	error = VOP_GETATTR(pvp, &pvattr, 0, cr, ct);
    900 	if (error) {
    901 		return (error);
    902 	}
    903 	*vap = pvattr;
    904 	vap->va_type = VDIR;
    905 	vap->va_mode = MAKEIMODE(vap->va_type, S_ISVTX | 0777);
    906 	vap->va_fsid = vp->v_vfsp->vfs_dev;
    907 	vap->va_nodeid = gfs_file_inode(vp);
    908 	vap->va_nlink = XATTRDIR_NENTS+2;
    909 	vap->va_size = vap->va_nlink;
    910 	gethrestime(&now);
    911 	vap->va_atime = now;
    912 	vap->va_blksize = 0;
    913 	vap->va_nblocks = 0;
    914 	vap->va_seq = 0;
    915 	return (0);
    916 }
    917 
    918 static int
    919 xattr_dir_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
    920     caller_context_t *ct)
    921 {
    922 	vnode_t *realvp;
    923 	int error;
    924 
    925 	/*
    926 	 * If there is a real xattr directory, do the setattr there.
    927 	 * Otherwise, just return success.  The GFS directory is transient,
    928 	 * and any setattr changes can disappear anyway.
    929 	 */
    930 	error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct);
    931 	if (error == 0) {
    932 		error = VOP_SETATTR(realvp, vap, flags, cr, ct);
    933 		VN_RELE(realvp);
    934 	}
    935 	if (error == ENOENT) {
    936 		error = 0;
    937 	}
    938 	return (error);
    939 }
    940 
    941 /* ARGSUSED */
    942 static int
    943 xattr_dir_access(vnode_t *vp, int mode, int flags, cred_t *cr,
    944     caller_context_t *ct)
    945 {
    946 	int error;
    947 	vnode_t *realvp = NULL;
    948 
    949 	if (mode & VWRITE) {
    950 		return (EACCES);
    951 	}
    952 
    953 	error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct);
    954 
    955 	if (realvp)
    956 		VN_RELE(realvp);
    957 
    958 	/*
    959 	 * No real xattr dir isn't an error
    960 	 * an error of EINVAL indicates attributes on attributes
    961 	 * are not supported.  In that case just allow access to the
    962 	 * transient directory.
    963 	 */
    964 	return ((error == ENOENT || error == EINVAL) ? 0 : error);
    965 }
    966 
    967 static int
    968 xattr_dir_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl,
    969     int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct,
    970     vsecattr_t *vsecp)
    971 {
    972 	vnode_t *pvp;
    973 	int error;
    974 
    975 	*vpp = NULL;
    976 
    977 	/*
    978 	 * Don't allow creation of extended attributes with sysattr names.
    979 	 */
    980 	if (is_sattr_name(name)) {
    981 		return (gfs_dir_lookup(dvp, name, vpp, cr));
    982 	}
    983 
    984 	error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR|CREATE_XATTR_DIR,
    985 	    cr, ct);
    986 	if (error == 0) {
    987 		error = VOP_CREATE(pvp, name, vap, excl, mode, vpp, cr, flag,
    988 		    ct, vsecp);
    989 		VN_RELE(pvp);
    990 	}
    991 	return (error);
    992 }
    993 
    994 static int
    995 xattr_dir_remove(vnode_t *dvp, char *name, cred_t *cr, caller_context_t *ct,
    996     int flags)
    997 {
    998 	vnode_t *pvp;
    999 	int error;
   1000 
   1001 	if (is_sattr_name(name)) {
   1002 		return (EACCES);
   1003 	}
   1004 
   1005 	error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR, cr, ct);
   1006 	if (error == 0) {
   1007 		error = VOP_REMOVE(pvp, name, cr, ct, flags);
   1008 		VN_RELE(pvp);
   1009 	}
   1010 	return (error);
   1011 }
   1012 
   1013 static int
   1014 xattr_dir_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr,
   1015     caller_context_t *ct, int flags)
   1016 {
   1017 	vnode_t *pvp;
   1018 	int error;
   1019 
   1020 	if (svp->v_flag & V_SYSATTR) {
   1021 		return (EINVAL);
   1022 	}
   1023 
   1024 	error = xattr_dir_realdir(tdvp, &pvp, LOOKUP_XATTR, cr, ct);
   1025 	if (error == 0) {
   1026 		error = VOP_LINK(pvp, svp, name, cr, ct, flags);
   1027 		VN_RELE(pvp);
   1028 	}
   1029 	return (error);
   1030 }
   1031 
   1032 static int
   1033 xattr_dir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
   1034     cred_t *cr, caller_context_t *ct, int flags)
   1035 {
   1036 	vnode_t *spvp, *tpvp;
   1037 	int error;
   1038 	int held_tgt;
   1039 
   1040 	if (is_sattr_name(snm) || is_sattr_name(tnm))
   1041 		return (xattr_copy(sdvp, snm, tdvp, tnm, cr, ct));
   1042 	/*
   1043 	 * We know that sdvp is a GFS dir, or we wouldn't be here.
   1044 	 * Get the real unnamed directory.
   1045 	 */
   1046 	error = xattr_dir_realdir(sdvp, &spvp, LOOKUP_XATTR, cr, ct);
   1047 	if (error) {
   1048 		return (error);
   1049 	}
   1050 
   1051 	if (sdvp == tdvp) {
   1052 		/*
   1053 		 * If the source and target are the same GFS directory, the
   1054 		 * underlying unnamed source and target dir will be the same.
   1055 		 */
   1056 		tpvp = spvp;
   1057 		VN_HOLD(tpvp);
   1058 		held_tgt = 1;
   1059 	} else if (tdvp->v_flag & V_SYSATTR) {
   1060 		/*
   1061 		 * If the target dir is a different GFS directory,
   1062 		 * find its underlying unnamed dir.
   1063 		 */
   1064 		error = xattr_dir_realdir(tdvp, &tpvp, LOOKUP_XATTR, cr, ct);
   1065 		if (error) {
   1066 			VN_RELE(spvp);
   1067 			return (error);
   1068 		}
   1069 		held_tgt = 1;
   1070 	} else {
   1071 		/*
   1072 		 * Target dir is outside of GFS, pass it on through.
   1073 		 */
   1074 		tpvp = tdvp;
   1075 		held_tgt = 0;
   1076 	}
   1077 
   1078 	error = VOP_RENAME(spvp, snm, tpvp, tnm, cr, ct, flags);
   1079 
   1080 	if (held_tgt) {
   1081 		VN_RELE(tpvp);
   1082 	}
   1083 	VN_RELE(spvp);
   1084 
   1085 	return (error);
   1086 }
   1087 
   1088 /*
   1089  * readdir_xattr_casecmp: given a system attribute name, see if there
   1090  * is a real xattr with the same normalized name.
   1091  */
   1092 static int
   1093 readdir_xattr_casecmp(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
   1094     int *eflags)
   1095 {
   1096 	int error;
   1097 	vnode_t *vp;
   1098 	struct pathname pn;
   1099 	int flags = FIGNORECASE;
   1100 
   1101 	*eflags = 0;
   1102 
   1103 	error = pn_get(nm, UIO_SYSSPACE, &pn);
   1104 	if (error == 0) {
   1105 		error = VOP_LOOKUP(dvp, nm, &vp, &pn, LOOKUP_XATTR, rootvp,
   1106 		    cr, ct, &flags, NULL);
   1107 		if (error == 0) {
   1108 			*eflags = ED_CASE_CONFLICT;
   1109 			VN_RELE(vp);
   1110 		} else if (error == ENOENT) {
   1111 			error = 0;
   1112 		}
   1113 		pn_free(&pn);
   1114 	}
   1115 
   1116 	return (error);
   1117 }
   1118 
   1119 static int
   1120 xattr_dir_readdir(vnode_t *dvp, uio_t *uiop, cred_t *cr, int *eofp,
   1121     caller_context_t *ct, int flags)
   1122 {
   1123 	vnode_t *pvp;
   1124 	int error;
   1125 	int local_eof;
   1126 	int reset_off = 0;
   1127 	int has_xattrs = 0;
   1128 
   1129 	if (eofp == NULL) {
   1130 		eofp = &local_eof;
   1131 	}
   1132 	*eofp = 0;
   1133 
   1134 	/*
   1135 	 * See if there is a real extended attribute directory.
   1136 	 */
   1137 	error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR, cr, ct);
   1138 	if (error == 0) {
   1139 		has_xattrs = 1;
   1140 	}
   1141 
   1142 	/*
   1143 	 * Start by reading up the static entries.
   1144 	 */
   1145 	if (uiop->uio_loffset == 0) {
   1146 		ino64_t pino, ino;
   1147 		offset_t off;
   1148 		gfs_dir_t *dp = dvp->v_data;
   1149 		gfs_readdir_state_t gstate;
   1150 
   1151 		if (has_xattrs) {
   1152 			/*
   1153 			 * If there is a real xattr dir, skip . and ..
   1154 			 * in the GFS dir.  We'll pick them up below
   1155 			 * when we call into the underlying fs.
   1156 			 */
   1157 			uiop->uio_loffset = GFS_STATIC_ENTRY_OFFSET;
   1158 		}
   1159 		error = gfs_get_parent_ino(dvp, cr, ct, &pino, &ino);
   1160 		if (error == 0) {
   1161 			error = gfs_readdir_init(&gstate, dp->gfsd_maxlen, 1,
   1162 			    uiop, pino, ino, flags);
   1163 		}
   1164 		if (error) {
   1165 			if (has_xattrs)
   1166 				VN_RELE(pvp);
   1167 			return (error);
   1168 		}
   1169 
   1170 		while ((error = gfs_readdir_pred(&gstate, uiop, &off)) == 0 &&
   1171 		    !*eofp) {
   1172 			if (off >= 0 && off < dp->gfsd_nstatic) {
   1173 				int eflags = 0;
   1174 
   1175 				/*
   1176 				 * Check to see if this sysattr set name has a
   1177 				 * case-insensitive conflict with a real xattr
   1178 				 * name.
   1179 				 */
   1180 				if ((flags & V_RDDIR_ENTFLAGS) && has_xattrs) {
   1181 					error = readdir_xattr_casecmp(pvp,
   1182 					    dp->gfsd_static[off].gfse_name,
   1183 					    cr, ct, &eflags);
   1184 					if (error)
   1185 						break;
   1186 				}
   1187 				ino = dp->gfsd_inode(dvp, off);
   1188 
   1189 				error = gfs_readdir_emit(&gstate, uiop, off,
   1190 				    ino, dp->gfsd_static[off].gfse_name,
   1191 				    eflags);
   1192 				if (error)
   1193 					break;
   1194 			} else {
   1195 				*eofp = 1;
   1196 			}
   1197 		}
   1198 
   1199 		error = gfs_readdir_fini(&gstate, error, eofp, *eofp);
   1200 		if (error) {
   1201 			if (has_xattrs)
   1202 				VN_RELE(pvp);
   1203 			return (error);
   1204 		}
   1205 
   1206 		/*
   1207 		 * We must read all of the static entries in the first
   1208 		 * call.  Otherwise we won't know if uio_loffset in a
   1209 		 * subsequent call refers to the static entries or to those
   1210 		 * in an underlying fs.
   1211 		 */
   1212 		ASSERT(*eofp);
   1213 		reset_off = 1;
   1214 	}
   1215 
   1216 	if (!has_xattrs) {
   1217 		*eofp = 1;
   1218 		return (0);
   1219 	}
   1220 
   1221 	*eofp = 0;
   1222 	if (reset_off) {
   1223 		uiop->uio_loffset = 0;
   1224 	}
   1225 	(void) VOP_RWLOCK(pvp, V_WRITELOCK_FALSE, NULL);
   1226 	error = VOP_READDIR(pvp, uiop, cr, eofp, ct, flags);
   1227 	VOP_RWUNLOCK(pvp, V_WRITELOCK_FALSE, NULL);
   1228 	VN_RELE(pvp);
   1229 
   1230 	return (error);
   1231 }
   1232 
   1233 /* ARGSUSED */
   1234 static void
   1235 xattr_dir_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
   1236 {
   1237 	gfs_file_t *fp;
   1238 
   1239 	fp = gfs_dir_inactive(vp);
   1240 	if (fp != NULL) {
   1241 		kmem_free(fp, fp->gfs_size);
   1242 	}
   1243 }
   1244 
   1245 static int
   1246 xattr_dir_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
   1247     caller_context_t *ct)
   1248 {
   1249 	switch (cmd) {
   1250 	case _PC_XATTR_EXISTS:
   1251 	case _PC_SATTR_ENABLED:
   1252 	case _PC_SATTR_EXISTS:
   1253 		*valp = 0;
   1254 		return (0);
   1255 	default:
   1256 		return (fs_pathconf(vp, cmd, valp, cr, ct));
   1257 	}
   1258 }
   1259 
   1260 static const fs_operation_def_t xattr_dir_tops[] = {
   1261 	{ VOPNAME_OPEN,		{ .vop_open = xattr_dir_open }		},
   1262 	{ VOPNAME_CLOSE,	{ .vop_close = xattr_dir_close }	},
   1263 	{ VOPNAME_IOCTL,	{ .error = fs_inval }			},
   1264 	{ VOPNAME_GETATTR,	{ .vop_getattr = xattr_dir_getattr }	},
   1265 	{ VOPNAME_SETATTR,	{ .vop_setattr = xattr_dir_setattr }	},
   1266 	{ VOPNAME_ACCESS,	{ .vop_access = xattr_dir_access }	},
   1267 	{ VOPNAME_READDIR,	{ .vop_readdir = xattr_dir_readdir }	},
   1268 	{ VOPNAME_LOOKUP,	{ .vop_lookup = gfs_vop_lookup }	},
   1269 	{ VOPNAME_CREATE,	{ .vop_create = xattr_dir_create }	},
   1270 	{ VOPNAME_REMOVE,	{ .vop_remove = xattr_dir_remove }	},
   1271 	{ VOPNAME_LINK,		{ .vop_link = xattr_dir_link }		},
   1272 	{ VOPNAME_RENAME,	{ .vop_rename = xattr_dir_rename }	},
   1273 	{ VOPNAME_MKDIR,	{ .error = fs_inval }			},
   1274 	{ VOPNAME_SEEK,		{ .vop_seek = fs_seek }			},
   1275 	{ VOPNAME_INACTIVE,	{ .vop_inactive = xattr_dir_inactive }	},
   1276 	{ VOPNAME_FID,		{ .vop_fid = xattr_common_fid }		},
   1277 	{ VOPNAME_PATHCONF,	{ .vop_pathconf = xattr_dir_pathconf }	},
   1278 	{ NULL, NULL }
   1279 };
   1280 
   1281 static gfs_opsvec_t xattr_opsvec[] = {
   1282 	{ "xattr dir", xattr_dir_tops, &xattr_dir_ops },
   1283 	{ "system attributes", xattr_file_tops, &xattr_file_ops },
   1284 	{ NULL, NULL, NULL }
   1285 };
   1286 
   1287 static int
   1288 xattr_lookup_cb(vnode_t *vp, const char *nm, vnode_t **vpp, ino64_t *inop,
   1289     cred_t *cr)
   1290 {
   1291 	vnode_t *pvp;
   1292 	struct pathname pn;
   1293 	int error;
   1294 
   1295 	*vpp = NULL;
   1296 	*inop = 0;
   1297 
   1298 	error = xattr_dir_realdir(vp, &pvp, LOOKUP_XATTR|CREATE_XATTR_DIR,
   1299 	    cr, NULL);
   1300 
   1301 	/*
   1302 	 * Return ENOENT for EACCES requests during lookup.  Once an
   1303 	 * attribute create is attempted EACCES will be returned.
   1304 	 */
   1305 	if (error) {
   1306 		if (error == EACCES)
   1307 			return (ENOENT);
   1308 		return (error);
   1309 	}
   1310 
   1311 	error = pn_get((char *)nm, UIO_SYSSPACE, &pn);
   1312 	if (error == 0) {
   1313 		error = VOP_LOOKUP(pvp, (char *)nm, vpp, &pn, 0, rootvp,
   1314 		    cr, NULL, NULL, NULL);
   1315 		pn_free(&pn);
   1316 	}
   1317 	VN_RELE(pvp);
   1318 
   1319 	return (error);
   1320 }
   1321 
   1322 /* ARGSUSED */
   1323 static ino64_t
   1324 xattrdir_do_ino(vnode_t *vp, int index)
   1325 {
   1326 	/*
   1327 	 * We use index 0 for the directory fid.  Start
   1328 	 * the file numbering at 1.
   1329 	 */
   1330 	return ((ino64_t)index+1);
   1331 }
   1332 
   1333 void
   1334 xattr_init(void)
   1335 {
   1336 	VERIFY(gfs_make_opsvec(xattr_opsvec) == 0);
   1337 }
   1338 
   1339 int
   1340 xattr_dir_lookup(vnode_t *dvp, vnode_t **vpp, int flags, cred_t *cr)
   1341 {
   1342 	int error = 0;
   1343 
   1344 	*vpp = NULL;
   1345 
   1346 	if (dvp->v_type != VDIR && dvp->v_type != VREG)
   1347 		return (EINVAL);
   1348 
   1349 	mutex_enter(&dvp->v_lock);
   1350 
   1351 	/*
   1352 	 * If we're already in sysattr space, don't allow creation
   1353 	 * of another level of sysattrs.
   1354 	 */
   1355 	if (dvp->v_flag & V_SYSATTR) {
   1356 		mutex_exit(&dvp->v_lock);
   1357 		return (EINVAL);
   1358 	}
   1359 
   1360 	if (dvp->v_xattrdir != NULL) {
   1361 		*vpp = dvp->v_xattrdir;
   1362 		VN_HOLD(*vpp);
   1363 	} else {
   1364 		ulong_t val;
   1365 		int xattrs_allowed = dvp->v_vfsp->vfs_flag & VFS_XATTR;
   1366 		int sysattrs_allowed = 1;
   1367 
   1368 		/*
   1369 		 * We have to drop the lock on dvp.  gfs_dir_create will
   1370 		 * grab it for a VN_HOLD.
   1371 		 */
   1372 		mutex_exit(&dvp->v_lock);
   1373 
   1374 		/*
   1375 		 * If dvp allows xattr creation, but not sysattr
   1376 		 * creation, return the real xattr dir vp. We can't
   1377 		 * use the vfs feature mask here because _PC_SATTR_ENABLED
   1378 		 * has vnode-level granularity (e.g. .zfs).
   1379 		 */
   1380 		error = VOP_PATHCONF(dvp, _PC_SATTR_ENABLED, &val, cr, NULL);
   1381 		if (error != 0 || val == 0)
   1382 			sysattrs_allowed = 0;
   1383 
   1384 		if (!xattrs_allowed && !sysattrs_allowed)
   1385 			return (EINVAL);
   1386 
   1387 		if (!sysattrs_allowed) {
   1388 			struct pathname pn;
   1389 			char *nm = "";
   1390 
   1391 			error = pn_get(nm, UIO_SYSSPACE, &pn);
   1392 			if (error)
   1393 				return (error);
   1394 			error = VOP_LOOKUP(dvp, nm, vpp, &pn,
   1395 			    flags|LOOKUP_HAVE_SYSATTR_DIR, rootvp, cr, NULL,
   1396 			    NULL, NULL);
   1397 			pn_free(&pn);
   1398 			return (error);
   1399 		}
   1400 
   1401 		/*
   1402 		 * Note that we act as if we were given CREATE_XATTR_DIR,
   1403 		 * but only for creation of the GFS directory.
   1404 		 */
   1405 		*vpp = gfs_dir_create(
   1406 		    sizeof (gfs_dir_t), dvp, xattr_dir_ops, xattr_dirents,
   1407 		    xattrdir_do_ino, MAXNAMELEN, NULL, xattr_lookup_cb);
   1408 		mutex_enter(&dvp->v_lock);
   1409 		if (dvp->v_xattrdir != NULL) {
   1410 			/*
   1411 			 * We lost the race to create the xattr dir.
   1412 			 * Destroy this one, use the winner.  We can't
   1413 			 * just call VN_RELE(*vpp), because the vnode
   1414 			 * is only partially initialized.
   1415 			 */
   1416 			gfs_dir_t *dp = (*vpp)->v_data;
   1417 
   1418 			ASSERT((*vpp)->v_count == 1);
   1419 			vn_free(*vpp);
   1420 
   1421 			mutex_destroy(&dp->gfsd_lock);
   1422 			kmem_free(dp->gfsd_static,
   1423 			    dp->gfsd_nstatic * sizeof (gfs_dirent_t));
   1424 			kmem_free(dp, dp->gfsd_file.gfs_size);
   1425 
   1426 			/*
   1427 			 * There is an implied VN_HOLD(dvp) here.  We should
   1428 			 * be doing a VN_RELE(dvp) to clean up the reference
   1429 			 * from *vpp, and then a VN_HOLD(dvp) for the new
   1430 			 * reference.  Instead, we just leave the count alone.
   1431 			 */
   1432 
   1433 			*vpp = dvp->v_xattrdir;
   1434 			VN_HOLD(*vpp);
   1435 		} else {
   1436 			(*vpp)->v_flag |= (V_XATTRDIR|V_SYSATTR);
   1437 			dvp->v_xattrdir = *vpp;
   1438 		}
   1439 	}
   1440 	mutex_exit(&dvp->v_lock);
   1441 
   1442 	return (error);
   1443 }
   1444 
   1445 int
   1446 xattr_dir_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
   1447 {
   1448 	int error;
   1449 	vnode_t *pvp, *dvp;
   1450 	xattr_fid_t *xfidp;
   1451 	struct pathname pn;
   1452 	char *nm;
   1453 	uint16_t orig_len;
   1454 
   1455 	*vpp = NULL;
   1456 
   1457 	if (fidp->fid_len < XATTR_FIDSZ)
   1458 		return (EINVAL);
   1459 
   1460 	xfidp = (xattr_fid_t *)fidp;
   1461 	orig_len = fidp->fid_len;
   1462 	fidp->fid_len = xfidp->parent_len;
   1463 
   1464 	error = VFS_VGET(vfsp, &pvp, fidp);
   1465 	fidp->fid_len = orig_len;
   1466 	if (error)
   1467 		return (error);
   1468 
   1469 	/*
   1470 	 * Start by getting the GFS sysattr directory.	We might need
   1471 	 * to recreate it during the VOP_LOOKUP.
   1472 	 */
   1473 	nm = "";
   1474 	error = pn_get(nm, UIO_SYSSPACE, &pn);
   1475 	if (error) {
   1476 		VN_RELE(pvp);
   1477 		return (EINVAL);
   1478 	}
   1479 
   1480 	error = VOP_LOOKUP(pvp, nm, &dvp, &pn, LOOKUP_XATTR|CREATE_XATTR_DIR,
   1481 	    rootvp, CRED(), NULL, NULL, NULL);
   1482 	pn_free(&pn);
   1483 	VN_RELE(pvp);
   1484 	if (error)
   1485 		return (error);
   1486 
   1487 	if (xfidp->dir_offset == 0) {
   1488 		/*
   1489 		 * If we were looking for the directory, we're done.
   1490 		 */
   1491 		*vpp = dvp;
   1492 		return (0);
   1493 	}
   1494 
   1495 	if (xfidp->dir_offset > XATTRDIR_NENTS) {
   1496 		VN_RELE(dvp);
   1497 		return (EINVAL);
   1498 	}
   1499 
   1500 	nm = xattr_dirents[xfidp->dir_offset - 1].gfse_name;
   1501 
   1502 	error = pn_get(nm, UIO_SYSSPACE, &pn);
   1503 	if (error) {
   1504 		VN_RELE(dvp);
   1505 		return (EINVAL);
   1506 	}
   1507 
   1508 	error = VOP_LOOKUP(dvp, nm, vpp, &pn, 0, rootvp, CRED(), NULL,
   1509 	    NULL, NULL);
   1510 
   1511 	pn_free(&pn);
   1512 	VN_RELE(dvp);
   1513 
   1514 	return (error);
   1515 }
   1516