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	"%Z%%M%	%I%	%E% 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, 0, NULL, NULL));
    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)