xref: /onnv-gate/usr/src/uts/common/fs/xattr.c (revision 13082:81ec56bf6147)
15331Samw /*
25331Samw  * CDDL HEADER START
35331Samw  *
45331Samw  * The contents of this file are subject to the terms of the
55331Samw  * Common Development and Distribution License (the "License").
65331Samw  * You may not use this file except in compliance with the License.
75331Samw  *
85331Samw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95331Samw  * or http://www.opensolaris.org/os/licensing.
105331Samw  * See the License for the specific language governing permissions
115331Samw  * and limitations under the License.
125331Samw  *
135331Samw  * When distributing Covered Code, include this CDDL HEADER in each
145331Samw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155331Samw  * If applicable, add the following below this CDDL HEADER, with the
165331Samw  * fields enclosed by brackets "[]" replaced with your own identifying
175331Samw  * information: Portions Copyright [yyyy] [name of copyright owner]
185331Samw  *
195331Samw  * CDDL HEADER END
205331Samw  */
215331Samw /*
2212083SMark.Shellenbaum@Sun.COM  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
235331Samw  */
245331Samw 
255331Samw #include <sys/param.h>
265331Samw #include <sys/isa_defs.h>
275331Samw #include <sys/types.h>
285331Samw #include <sys/sysmacros.h>
295331Samw #include <sys/cred.h>
305331Samw #include <sys/systm.h>
315331Samw #include <sys/errno.h>
325331Samw #include <sys/fcntl.h>
335331Samw #include <sys/pathname.h>
345331Samw #include <sys/stat.h>
355331Samw #include <sys/vfs.h>
365331Samw #include <sys/acl.h>
375331Samw #include <sys/file.h>
385331Samw #include <sys/sunddi.h>
395331Samw #include <sys/debug.h>
405331Samw #include <sys/cmn_err.h>
415331Samw #include <sys/vnode.h>
425331Samw #include <sys/mode.h>
435331Samw #include <sys/nvpair.h>
445331Samw #include <sys/attr.h>
455331Samw #include <sys/gfs.h>
465331Samw #include <sys/mutex.h>
475331Samw #include <fs/fs_subr.h>
485331Samw #include <sys/kidmap.h>
495331Samw 
505331Samw typedef struct {
5111372SMark.Shellenbaum@Sun.COM 	gfs_file_t	xattr_gfs_private;
525331Samw 	xattr_view_t	xattr_view;
535331Samw } xattr_file_t;
545331Samw 
5511372SMark.Shellenbaum@Sun.COM typedef struct {
5611372SMark.Shellenbaum@Sun.COM 	gfs_dir_t	xattr_gfs_private;
5711372SMark.Shellenbaum@Sun.COM 	vnode_t		*xattr_realvp;  /* Only used for VOP_REALVP */
5811372SMark.Shellenbaum@Sun.COM } xattr_dir_t;
5911372SMark.Shellenbaum@Sun.COM 
6011372SMark.Shellenbaum@Sun.COM /*
6111372SMark.Shellenbaum@Sun.COM  * xattr_realvp is only used for VOP_REALVP, this is so we don't
6211372SMark.Shellenbaum@Sun.COM  * keep an unnecessary hold on the *real* xattr dir unless we have
6311372SMark.Shellenbaum@Sun.COM  * no other choice.
6411372SMark.Shellenbaum@Sun.COM  */
6511372SMark.Shellenbaum@Sun.COM 
665331Samw /* ARGSUSED */
675331Samw static int
xattr_file_open(vnode_t ** vpp,int flags,cred_t * cr,caller_context_t * ct)685331Samw xattr_file_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
695331Samw {
705331Samw 	xattr_file_t *np = (*vpp)->v_data;
715331Samw 
725331Samw 	if ((np->xattr_view == XATTR_VIEW_READONLY) && (flags & FWRITE))
735331Samw 		return (EACCES);
745331Samw 
755331Samw 	return (0);
765331Samw }
775331Samw 
785331Samw /* ARGSUSED */
795331Samw static int
xattr_file_access(vnode_t * vp,int mode,int flags,cred_t * cr,caller_context_t * ct)805331Samw xattr_file_access(vnode_t *vp, int mode, int flags, cred_t *cr,
815331Samw     caller_context_t *ct)
825331Samw {
835331Samw 	xattr_file_t *np = vp->v_data;
845331Samw 
855331Samw 	if ((np->xattr_view == XATTR_VIEW_READONLY) && (mode & VWRITE))
865331Samw 		return (EACCES);
875331Samw 
885331Samw 	return (0);
895331Samw }
905331Samw 
915331Samw /* ARGSUSED */
925331Samw static int
xattr_file_close(vnode_t * vp,int flags,int count,offset_t off,cred_t * cr,caller_context_t * ct)935331Samw xattr_file_close(vnode_t *vp, int flags, int count, offset_t off,
945331Samw     cred_t *cr, caller_context_t *ct)
955331Samw {
965331Samw 	cleanlocks(vp, ddi_get_pid(), 0);
975331Samw 	cleanshares(vp, ddi_get_pid());
985331Samw 	return (0);
995331Samw }
1005331Samw 
1015331Samw static int
xattr_common_fid(vnode_t * vp,fid_t * fidp,caller_context_t * ct)1025331Samw xattr_common_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
1035331Samw {
1045331Samw 	xattr_fid_t	*xfidp;
1055331Samw 	vnode_t		*pvp, *savevp;
1065331Samw 	int		error;
1075331Samw 	uint16_t	orig_len;
1085331Samw 
1095331Samw 	if (fidp->fid_len < XATTR_FIDSZ) {
1105331Samw 		fidp->fid_len = XATTR_FIDSZ;
1115331Samw 		return (ENOSPC);
1125331Samw 	}
1135331Samw 
1145331Samw 	savevp = pvp = gfs_file_parent(vp);
1155331Samw 	mutex_enter(&savevp->v_lock);
1165331Samw 	if (pvp->v_flag & V_XATTRDIR) {
1175331Samw 		pvp = gfs_file_parent(pvp);
1185331Samw 	}
1195331Samw 	mutex_exit(&savevp->v_lock);
1205331Samw 
1215331Samw 	xfidp = (xattr_fid_t *)fidp;
1225331Samw 	orig_len = fidp->fid_len;
1235331Samw 	fidp->fid_len = sizeof (xfidp->parent_fid);
1245331Samw 
1255331Samw 	error = VOP_FID(pvp, fidp, ct);
1265331Samw 	if (error) {
1275331Samw 		fidp->fid_len = orig_len;
1285331Samw 		return (error);
1295331Samw 	}
1305331Samw 
1315331Samw 	xfidp->parent_len = fidp->fid_len;
1325331Samw 	fidp->fid_len = XATTR_FIDSZ;
1335331Samw 	xfidp->dir_offset = gfs_file_inode(vp);
1345331Samw 
1355331Samw 	return (0);
1365331Samw }
1375331Samw 
1385331Samw /* ARGSUSED */
1395331Samw static int
xattr_fill_nvlist(vnode_t * vp,xattr_view_t xattr_view,nvlist_t * nvlp,cred_t * cr,caller_context_t * ct)1405331Samw xattr_fill_nvlist(vnode_t *vp, xattr_view_t xattr_view, nvlist_t *nvlp,
1415331Samw     cred_t *cr, caller_context_t *ct)
1425331Samw {
1435331Samw 	int error;
1445331Samw 	f_attr_t attr;
1455331Samw 	uint64_t fsid;
1465331Samw 	xvattr_t xvattr;
1475331Samw 	xoptattr_t *xoap;	/* Pointer to optional attributes */
1485331Samw 	vnode_t *ppvp;
1495331Samw 	const char *domain;
1505331Samw 	uint32_t rid;
1515331Samw 
1525331Samw 	xva_init(&xvattr);
1535331Samw 
1545331Samw 	if ((xoap = xva_getxoptattr(&xvattr)) == NULL)
1555331Samw 		return (EINVAL);
1565331Samw 
1575331Samw 	/*
1585331Samw 	 * For detecting ephemeral uid/gid
1595331Samw 	 */
1605331Samw 	xvattr.xva_vattr.va_mask |= (AT_UID|AT_GID);
1615331Samw 
1625331Samw 	/*
1635331Samw 	 * We need to access the real fs object.
1645331Samw 	 * vp points to a GFS file; ppvp points to the real object.
1655331Samw 	 */
1665331Samw 	ppvp = gfs_file_parent(gfs_file_parent(vp));
1675331Samw 
1685331Samw 	/*
1695331Samw 	 * Iterate through the attrs associated with this view
1705331Samw 	 */
1715331Samw 
1725331Samw 	for (attr = 0; attr < F_ATTR_ALL; attr++) {
1735331Samw 		if (xattr_view != attr_to_xattr_view(attr)) {
1745331Samw 			continue;
1755331Samw 		}
1765331Samw 
1775331Samw 		switch (attr) {
1785331Samw 		case F_SYSTEM:
1795331Samw 			XVA_SET_REQ(&xvattr, XAT_SYSTEM);
1805331Samw 			break;
1815331Samw 		case F_READONLY:
1825331Samw 			XVA_SET_REQ(&xvattr, XAT_READONLY);
1835331Samw 			break;
1845331Samw 		case F_HIDDEN:
1855331Samw 			XVA_SET_REQ(&xvattr, XAT_HIDDEN);
1865331Samw 			break;
1875331Samw 		case F_ARCHIVE:
1885331Samw 			XVA_SET_REQ(&xvattr, XAT_ARCHIVE);
1895331Samw 			break;
1905331Samw 		case F_IMMUTABLE:
1915331Samw 			XVA_SET_REQ(&xvattr, XAT_IMMUTABLE);
1925331Samw 			break;
1935331Samw 		case F_APPENDONLY:
1945331Samw 			XVA_SET_REQ(&xvattr, XAT_APPENDONLY);
1955331Samw 			break;
1965331Samw 		case F_NOUNLINK:
1975331Samw 			XVA_SET_REQ(&xvattr, XAT_NOUNLINK);
1985331Samw 			break;
1995331Samw 		case F_OPAQUE:
2005331Samw 			XVA_SET_REQ(&xvattr, XAT_OPAQUE);
2015331Samw 			break;
2025331Samw 		case F_NODUMP:
2035331Samw 			XVA_SET_REQ(&xvattr, XAT_NODUMP);
2045331Samw 			break;
2055331Samw 		case F_AV_QUARANTINED:
2065331Samw 			XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
2075331Samw 			break;
2085331Samw 		case F_AV_MODIFIED:
2095331Samw 			XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
2105331Samw 			break;
2115331Samw 		case F_AV_SCANSTAMP:
2125331Samw 			if (ppvp->v_type == VREG)
2135331Samw 				XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
2145331Samw 			break;
2155331Samw 		case F_CRTIME:
2165331Samw 			XVA_SET_REQ(&xvattr, XAT_CREATETIME);
2175331Samw 			break;
2185331Samw 		case F_FSID:
2195331Samw 			fsid = (((uint64_t)vp->v_vfsp->vfs_fsid.val[0] << 32) |
2205331Samw 			    (uint64_t)(vp->v_vfsp->vfs_fsid.val[1] &
2215331Samw 			    0xffffffff));
2225331Samw 			VERIFY(nvlist_add_uint64(nvlp, attr_to_name(attr),
2235331Samw 			    fsid) == 0);
2245331Samw 			break;
22510793Sdai.ngo@sun.com 		case F_REPARSE:
22610793Sdai.ngo@sun.com 			XVA_SET_REQ(&xvattr, XAT_REPARSE);
22710793Sdai.ngo@sun.com 			break;
22813043STim.Haley@Sun.COM 		case F_GEN:
22913043STim.Haley@Sun.COM 			XVA_SET_REQ(&xvattr, XAT_GEN);
23013043STim.Haley@Sun.COM 			break;
231*13082SJoyce.McIntosh@Sun.COM 		case F_OFFLINE:
232*13082SJoyce.McIntosh@Sun.COM 			XVA_SET_REQ(&xvattr, XAT_OFFLINE);
233*13082SJoyce.McIntosh@Sun.COM 			break;
234*13082SJoyce.McIntosh@Sun.COM 		case F_SPARSE:
235*13082SJoyce.McIntosh@Sun.COM 			XVA_SET_REQ(&xvattr, XAT_SPARSE);
236*13082SJoyce.McIntosh@Sun.COM 			break;
2375331Samw 		default:
2385331Samw 			break;
2395331Samw 		}
2405331Samw 	}
2415331Samw 
2425331Samw 	error = VOP_GETATTR(ppvp, &xvattr.xva_vattr, 0, cr, ct);
2435331Samw 	if (error)
2445331Samw 		return (error);
2455331Samw 
2465331Samw 	/*
2475331Samw 	 * Process all the optional attributes together here.  Notice that
2485331Samw 	 * xoap was set when the optional attribute bits were set above.
2495331Samw 	 */
2505331Samw 	if ((xvattr.xva_vattr.va_mask & AT_XVATTR) && xoap) {
2515331Samw 		if (XVA_ISSET_RTN(&xvattr, XAT_READONLY)) {
2525331Samw 			VERIFY(nvlist_add_boolean_value(nvlp,
2535331Samw 			    attr_to_name(F_READONLY),
2545331Samw 			    xoap->xoa_readonly) == 0);
2555331Samw 		}
2565331Samw 		if (XVA_ISSET_RTN(&xvattr, XAT_HIDDEN)) {
2575331Samw 			VERIFY(nvlist_add_boolean_value(nvlp,
2585331Samw 			    attr_to_name(F_HIDDEN),
2595331Samw 			    xoap->xoa_hidden) == 0);
2605331Samw 		}
2615331Samw 		if (XVA_ISSET_RTN(&xvattr, XAT_SYSTEM)) {
2625331Samw 			VERIFY(nvlist_add_boolean_value(nvlp,
2635331Samw 			    attr_to_name(F_SYSTEM),
2645331Samw 			    xoap->xoa_system) == 0);
2655331Samw 		}
2665331Samw 		if (XVA_ISSET_RTN(&xvattr, XAT_ARCHIVE)) {
2675331Samw 			VERIFY(nvlist_add_boolean_value(nvlp,
2685331Samw 			    attr_to_name(F_ARCHIVE),
2695331Samw 			    xoap->xoa_archive) == 0);
2705331Samw 		}
2715331Samw 		if (XVA_ISSET_RTN(&xvattr, XAT_IMMUTABLE)) {
2725331Samw 			VERIFY(nvlist_add_boolean_value(nvlp,
2735331Samw 			    attr_to_name(F_IMMUTABLE),
2745331Samw 			    xoap->xoa_immutable) == 0);
2755331Samw 		}
2765331Samw 		if (XVA_ISSET_RTN(&xvattr, XAT_NOUNLINK)) {
2775331Samw 			VERIFY(nvlist_add_boolean_value(nvlp,
2785331Samw 			    attr_to_name(F_NOUNLINK),
2795331Samw 			    xoap->xoa_nounlink) == 0);
2805331Samw 		}
2815331Samw 		if (XVA_ISSET_RTN(&xvattr, XAT_APPENDONLY)) {
2825331Samw 			VERIFY(nvlist_add_boolean_value(nvlp,
2835331Samw 			    attr_to_name(F_APPENDONLY),
2845331Samw 			    xoap->xoa_appendonly) == 0);
2855331Samw 		}
2865331Samw 		if (XVA_ISSET_RTN(&xvattr, XAT_NODUMP)) {
2875331Samw 			VERIFY(nvlist_add_boolean_value(nvlp,
2885331Samw 			    attr_to_name(F_NODUMP),
2895331Samw 			    xoap->xoa_nodump) == 0);
2905331Samw 		}
2915331Samw 		if (XVA_ISSET_RTN(&xvattr, XAT_OPAQUE)) {
2925331Samw 			VERIFY(nvlist_add_boolean_value(nvlp,
2935331Samw 			    attr_to_name(F_OPAQUE),
2945331Samw 			    xoap->xoa_opaque) == 0);
2955331Samw 		}
2965331Samw 		if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED)) {
2975331Samw 			VERIFY(nvlist_add_boolean_value(nvlp,
2985331Samw 			    attr_to_name(F_AV_QUARANTINED),
2995331Samw 			    xoap->xoa_av_quarantined) == 0);
3005331Samw 		}
3015331Samw 		if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED)) {
3025331Samw 			VERIFY(nvlist_add_boolean_value(nvlp,
3035331Samw 			    attr_to_name(F_AV_MODIFIED),
3045331Samw 			    xoap->xoa_av_modified) == 0);
3055331Samw 		}
3065331Samw 		if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP)) {
3075331Samw 			VERIFY(nvlist_add_uint8_array(nvlp,
3085331Samw 			    attr_to_name(F_AV_SCANSTAMP),
3095331Samw 			    xoap->xoa_av_scanstamp,
3105331Samw 			    sizeof (xoap->xoa_av_scanstamp)) == 0);
3115331Samw 		}
3125331Samw 		if (XVA_ISSET_RTN(&xvattr, XAT_CREATETIME)) {
3135331Samw 			VERIFY(nvlist_add_uint64_array(nvlp,
3145331Samw 			    attr_to_name(F_CRTIME),
3155331Samw 			    (uint64_t *)&(xoap->xoa_createtime),
3165331Samw 			    sizeof (xoap->xoa_createtime) /
3175331Samw 			    sizeof (uint64_t)) == 0);
3185331Samw 		}
31910793Sdai.ngo@sun.com 		if (XVA_ISSET_RTN(&xvattr, XAT_REPARSE)) {
32010793Sdai.ngo@sun.com 			VERIFY(nvlist_add_boolean_value(nvlp,
32110793Sdai.ngo@sun.com 			    attr_to_name(F_REPARSE),
32210793Sdai.ngo@sun.com 			    xoap->xoa_reparse) == 0);
32310793Sdai.ngo@sun.com 		}
32413043STim.Haley@Sun.COM 		if (XVA_ISSET_RTN(&xvattr, XAT_GEN)) {
32513043STim.Haley@Sun.COM 			VERIFY(nvlist_add_uint64(nvlp,
32613043STim.Haley@Sun.COM 			    attr_to_name(F_GEN),
32713043STim.Haley@Sun.COM 			    xoap->xoa_generation) == 0);
32813043STim.Haley@Sun.COM 		}
329*13082SJoyce.McIntosh@Sun.COM 		if (XVA_ISSET_RTN(&xvattr, XAT_OFFLINE)) {
330*13082SJoyce.McIntosh@Sun.COM 			VERIFY(nvlist_add_boolean_value(nvlp,
331*13082SJoyce.McIntosh@Sun.COM 			    attr_to_name(F_OFFLINE),
332*13082SJoyce.McIntosh@Sun.COM 			    xoap->xoa_offline) == 0);
333*13082SJoyce.McIntosh@Sun.COM 		}
334*13082SJoyce.McIntosh@Sun.COM 		if (XVA_ISSET_RTN(&xvattr, XAT_SPARSE)) {
335*13082SJoyce.McIntosh@Sun.COM 			VERIFY(nvlist_add_boolean_value(nvlp,
336*13082SJoyce.McIntosh@Sun.COM 			    attr_to_name(F_SPARSE),
337*13082SJoyce.McIntosh@Sun.COM 			    xoap->xoa_sparse) == 0);
338*13082SJoyce.McIntosh@Sun.COM 		}
3395331Samw 	}
3405331Samw 	/*
3415331Samw 	 * Check for optional ownersid/groupsid
3425331Samw 	 */
3435331Samw 
3445331Samw 	if (xvattr.xva_vattr.va_uid > MAXUID) {
3455331Samw 		nvlist_t *nvl_sid;
3465331Samw 
3475331Samw 		if (nvlist_alloc(&nvl_sid, NV_UNIQUE_NAME, KM_SLEEP))
3485331Samw 			return (ENOMEM);
3495331Samw 
3505771Sjp151216 		if (kidmap_getsidbyuid(crgetzone(cr), xvattr.xva_vattr.va_uid,
3515331Samw 		    &domain, &rid) == 0) {
3525331Samw 			VERIFY(nvlist_add_string(nvl_sid,
3535331Samw 			    SID_DOMAIN, domain) == 0);
3545331Samw 			VERIFY(nvlist_add_uint32(nvl_sid, SID_RID, rid) == 0);
3555331Samw 			VERIFY(nvlist_add_nvlist(nvlp, attr_to_name(F_OWNERSID),
3565331Samw 			    nvl_sid) == 0);
3575331Samw 		}
3585331Samw 		nvlist_free(nvl_sid);
3595331Samw 	}
3605331Samw 	if (xvattr.xva_vattr.va_gid > MAXUID) {
3615331Samw 		nvlist_t *nvl_sid;
3625331Samw 
3635331Samw 		if (nvlist_alloc(&nvl_sid, NV_UNIQUE_NAME, KM_SLEEP))
3645331Samw 			return (ENOMEM);
3655331Samw 
3665771Sjp151216 		if (kidmap_getsidbygid(crgetzone(cr), xvattr.xva_vattr.va_gid,
3675331Samw 		    &domain, &rid) == 0) {
3685331Samw 			VERIFY(nvlist_add_string(nvl_sid,
3695331Samw 			    SID_DOMAIN, domain) == 0);
3705331Samw 			VERIFY(nvlist_add_uint32(nvl_sid, SID_RID, rid) == 0);
3715331Samw 			VERIFY(nvlist_add_nvlist(nvlp, attr_to_name(F_GROUPSID),
3725331Samw 			    nvl_sid) == 0);
3735331Samw 		}
3745331Samw 		nvlist_free(nvl_sid);
3755331Samw 	}
3765331Samw 
3775331Samw 	return (0);
3785331Samw }
3795331Samw 
3805331Samw /*
3815331Samw  * The size of a sysattr file is the size of the nvlist that will be
3825331Samw  * returned by xattr_file_read().  A call to xattr_file_write() could
3835331Samw  * change the size of that nvlist.  That size is not stored persistently
3845331Samw  * so xattr_fill_nvlist() calls VOP_GETATTR so that it can be calculated.
3855331Samw  */
3865331Samw static int
xattr_file_size(vnode_t * vp,xattr_view_t xattr_view,size_t * size,cred_t * cr,caller_context_t * ct)3875331Samw xattr_file_size(vnode_t *vp, xattr_view_t xattr_view, size_t *size,
3885331Samw     cred_t *cr, caller_context_t *ct)
3895331Samw {
3905331Samw 	nvlist_t *nvl;
3915331Samw 
3925331Samw 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP)) {
3935331Samw 		return (ENOMEM);
3945331Samw 	}
3955331Samw 
3965331Samw 	if (xattr_fill_nvlist(vp, xattr_view, nvl, cr, ct)) {
3975331Samw 		nvlist_free(nvl);
3985331Samw 		return (EFAULT);
3995331Samw 	}
4005331Samw 
4015331Samw 	VERIFY(nvlist_size(nvl, size, NV_ENCODE_XDR) == 0);
4025331Samw 	nvlist_free(nvl);
4035331Samw 	return (0);
4045331Samw }
4055331Samw 
4065331Samw /* ARGSUSED */
4075331Samw static int
xattr_file_getattr(vnode_t * vp,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)4085331Samw xattr_file_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
4095331Samw     caller_context_t *ct)
4105331Samw {
4115331Samw 	xattr_file_t *np = vp->v_data;
4125331Samw 	timestruc_t now;
4135331Samw 	size_t size;
4145331Samw 	int error;
4155331Samw 	vnode_t *pvp;
4165331Samw 	vattr_t pvattr;
4175331Samw 
4185331Samw 	vap->va_type = VREG;
4195331Samw 	vap->va_mode = MAKEIMODE(vap->va_type,
4205331Samw 	    (np->xattr_view == XATTR_VIEW_READONLY ? 0444 : 0644));
4215331Samw 	vap->va_nodeid = gfs_file_inode(vp);
4225331Samw 	vap->va_nlink = 1;
4235331Samw 	pvp = gfs_file_parent(vp);
4245331Samw 	(void) memset(&pvattr, 0, sizeof (pvattr));
4255331Samw 	pvattr.va_mask = AT_CTIME|AT_MTIME;
4265331Samw 	error = VOP_GETATTR(pvp, &pvattr, flags, cr, ct);
4275331Samw 	if (error) {
4285331Samw 		return (error);
4295331Samw 	}
4305331Samw 	vap->va_ctime = pvattr.va_ctime;
4315331Samw 	vap->va_mtime = pvattr.va_mtime;
4325331Samw 	gethrestime(&now);
4335331Samw 	vap->va_atime = now;
4345331Samw 	vap->va_uid = 0;
4355331Samw 	vap->va_gid = 0;
4365331Samw 	vap->va_rdev = 0;
4375331Samw 	vap->va_blksize = DEV_BSIZE;
4385331Samw 	vap->va_seq = 0;
4395331Samw 	vap->va_fsid = vp->v_vfsp->vfs_dev;
4405331Samw 	error = xattr_file_size(vp, np->xattr_view, &size, cr, ct);
4415331Samw 	vap->va_size = size;
4425331Samw 	vap->va_nblocks = howmany(vap->va_size, vap->va_blksize);
4435331Samw 	return (error);
4445331Samw }
4455331Samw 
4465331Samw /* ARGSUSED */
4475331Samw static int
xattr_file_read(vnode_t * vp,uio_t * uiop,int ioflag,cred_t * cr,caller_context_t * ct)4485331Samw xattr_file_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
4495331Samw     caller_context_t *ct)
4505331Samw {
4515331Samw 	xattr_file_t *np = vp->v_data;
4525331Samw 	xattr_view_t xattr_view = np->xattr_view;
4535331Samw 	char *buf;
4545331Samw 	size_t filesize;
4555331Samw 	nvlist_t *nvl;
4565331Samw 	int error;
4575331Samw 
4585331Samw 	/*
4595331Samw 	 * Validate file offset and fasttrack empty reads
4605331Samw 	 */
4615331Samw 	if (uiop->uio_loffset < (offset_t)0)
4625331Samw 		return (EINVAL);
4635331Samw 
4645331Samw 	if (uiop->uio_resid == 0)
4655331Samw 		return (0);
4665331Samw 
4675331Samw 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP))
4685331Samw 		return (ENOMEM);
4695331Samw 
4705331Samw 	if (xattr_fill_nvlist(vp, xattr_view, nvl, cr, ct)) {
4715331Samw 		nvlist_free(nvl);
4725331Samw 		return (EFAULT);
4735331Samw 	}
4745331Samw 
4755331Samw 	VERIFY(nvlist_size(nvl, &filesize, NV_ENCODE_XDR) == 0);
4765331Samw 
4775331Samw 	if (uiop->uio_loffset >= filesize) {
4785331Samw 		nvlist_free(nvl);
4795331Samw 		return (0);
4805331Samw 	}
4815331Samw 
4825331Samw 	buf = kmem_alloc(filesize, KM_SLEEP);
4835331Samw 	VERIFY(nvlist_pack(nvl, &buf, &filesize, NV_ENCODE_XDR,
4845331Samw 	    KM_SLEEP) == 0);
4855331Samw 
4865331Samw 	error = uiomove((caddr_t)buf, filesize, UIO_READ, uiop);
4875331Samw 	kmem_free(buf, filesize);
4885331Samw 	nvlist_free(nvl);
4895331Samw 	return (error);
4905331Samw }
4915331Samw 
4925331Samw /* ARGSUSED */
4935331Samw static int
xattr_file_write(vnode_t * vp,uio_t * uiop,int ioflag,cred_t * cr,caller_context_t * ct)4945331Samw xattr_file_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
4955331Samw     caller_context_t *ct)
4965331Samw {
4975331Samw 	int error = 0;
4985331Samw 	char *buf;
4995331Samw 	char *domain;
5005331Samw 	uint32_t rid;
5015331Samw 	ssize_t size = uiop->uio_resid;
5025331Samw 	nvlist_t *nvp;
5035331Samw 	nvpair_t *pair = NULL;
5045331Samw 	vnode_t *ppvp;
5055331Samw 	xvattr_t xvattr;
5065331Samw 	xoptattr_t *xoap = NULL;	/* Pointer to optional attributes */
5075331Samw 
5087757SJanice.Chang@Sun.COM 	if (vfs_has_feature(vp->v_vfsp, VFSFT_XVATTR) == 0)
5097757SJanice.Chang@Sun.COM 		return (EINVAL);
5107757SJanice.Chang@Sun.COM 
5115331Samw 	/*
5125331Samw 	 * Validate file offset and size.
5135331Samw 	 */
5145331Samw 	if (uiop->uio_loffset < (offset_t)0)
5155331Samw 		return (EINVAL);
5165331Samw 
5175331Samw 	if (size == 0)
5185331Samw 		return (EINVAL);
5195331Samw 
5205331Samw 	xva_init(&xvattr);
5215331Samw 
5225331Samw 	if ((xoap = xva_getxoptattr(&xvattr)) == NULL) {
5235331Samw 		return (EINVAL);
5245331Samw 	}
5255331Samw 
5265331Samw 	/*
5275331Samw 	 * Copy and unpack the nvlist
5285331Samw 	 */
5295331Samw 	buf = kmem_alloc(size, KM_SLEEP);
5305331Samw 	if (uiomove((caddr_t)buf, size, UIO_WRITE, uiop)) {
5315331Samw 		return (EFAULT);
5325331Samw 	}
5335331Samw 
5345331Samw 	if (nvlist_unpack(buf, size, &nvp, KM_SLEEP) != 0) {
5355331Samw 		kmem_free(buf, size);
5365331Samw 		uiop->uio_resid = size;
5375331Samw 		return (EINVAL);
5385331Samw 	}
5395331Samw 	kmem_free(buf, size);
5405331Samw 
5415331Samw 	/*
5425331Samw 	 * Fasttrack empty writes (nvlist with no nvpairs)
5435331Samw 	 */
5445331Samw 	if (nvlist_next_nvpair(nvp, NULL) == 0)
5455331Samw 		return (0);
5465331Samw 
5475331Samw 	ppvp = gfs_file_parent(gfs_file_parent(vp));
5485331Samw 
5495331Samw 	while (pair = nvlist_next_nvpair(nvp, pair)) {
5505331Samw 		data_type_t type;
5515331Samw 		f_attr_t attr;
5525331Samw 		boolean_t value;
5535331Samw 		uint64_t *time, *times;
5545331Samw 		uint_t elem, nelems;
5555331Samw 		nvlist_t *nvp_sid;
5565331Samw 		uint8_t *scanstamp;
5575331Samw 
5585331Samw 		/*
5595331Samw 		 * Validate the name and type of each attribute.
5605331Samw 		 * Log any unknown names and continue.  This will
5615331Samw 		 * help if additional attributes are added later.
5625331Samw 		 */
5635331Samw 		type = nvpair_type(pair);
5645331Samw 		if ((attr = name_to_attr(nvpair_name(pair))) == F_ATTR_INVAL) {
5655331Samw 			cmn_err(CE_WARN, "Unknown attribute %s",
5665331Samw 			    nvpair_name(pair));
5675331Samw 			continue;
5685331Samw 		}
5695331Samw 
5705331Samw 		/*
5715331Samw 		 * Verify nvlist type matches required type and view is OK
5725331Samw 		 */
5735331Samw 
5745331Samw 		if (type != attr_to_data_type(attr) ||
5755331Samw 		    (attr_to_xattr_view(attr) == XATTR_VIEW_READONLY)) {
5765331Samw 			nvlist_free(nvp);
5775331Samw 			return (EINVAL);
5785331Samw 		}
5795331Samw 
5805331Samw 		/*
5815331Samw 		 * For OWNERSID/GROUPSID make sure the target
5825331Samw 		 * file system support ephemeral ID's
5835331Samw 		 */
5845331Samw 		if ((attr == F_OWNERSID || attr == F_GROUPSID) &&
5855331Samw 		    (!(vp->v_vfsp->vfs_flag & VFS_XID))) {
5865331Samw 			nvlist_free(nvp);
5875331Samw 			return (EINVAL);
5885331Samw 		}
5895331Samw 
5905331Samw 		/*
5915331Samw 		 * Retrieve data from nvpair
5925331Samw 		 */
5935331Samw 		switch (type) {
5945331Samw 		case DATA_TYPE_BOOLEAN_VALUE:
5955331Samw 			if (nvpair_value_boolean_value(pair, &value)) {
5965331Samw 				nvlist_free(nvp);
5975331Samw 				return (EINVAL);
5985331Samw 			}
5995331Samw 			break;
6005331Samw 		case DATA_TYPE_UINT64_ARRAY:
6015331Samw 			if (nvpair_value_uint64_array(pair, &times, &nelems)) {
6025331Samw 				nvlist_free(nvp);
6035331Samw 				return (EINVAL);
6045331Samw 			}
6055331Samw 			break;
6065331Samw 		case DATA_TYPE_NVLIST:
6075331Samw 			if (nvpair_value_nvlist(pair, &nvp_sid)) {
6085331Samw 				nvlist_free(nvp);
6095331Samw 				return (EINVAL);
6105331Samw 			}
6115331Samw 			break;
6125331Samw 		case DATA_TYPE_UINT8_ARRAY:
6135331Samw 			if (nvpair_value_uint8_array(pair,
6145331Samw 			    &scanstamp, &nelems)) {
6155331Samw 				nvlist_free(nvp);
6165331Samw 				return (EINVAL);
6175331Samw 			}
6185331Samw 			break;
6195331Samw 		default:
6205331Samw 			nvlist_free(nvp);
6215331Samw 			return (EINVAL);
6225331Samw 		}
6235331Samw 
6245331Samw 		switch (attr) {
6255331Samw 		/*
6265331Samw 		 * If we have several similar optional attributes to
6275331Samw 		 * process then we should do it all together here so that
6285331Samw 		 * xoap and the requested bitmap can be set in one place.
6295331Samw 		 */
6305331Samw 		case F_READONLY:
6315331Samw 			XVA_SET_REQ(&xvattr, XAT_READONLY);
6325331Samw 			xoap->xoa_readonly = value;
6335331Samw 			break;
6345331Samw 		case F_HIDDEN:
6355331Samw 			XVA_SET_REQ(&xvattr, XAT_HIDDEN);
6365331Samw 			xoap->xoa_hidden = value;
6375331Samw 			break;
6385331Samw 		case F_SYSTEM:
6395331Samw 			XVA_SET_REQ(&xvattr, XAT_SYSTEM);
6405331Samw 			xoap->xoa_system = value;
6415331Samw 			break;
6425331Samw 		case F_ARCHIVE:
6435331Samw 			XVA_SET_REQ(&xvattr, XAT_ARCHIVE);
6445331Samw 			xoap->xoa_archive = value;
6455331Samw 			break;
6465331Samw 		case F_IMMUTABLE:
6475331Samw 			XVA_SET_REQ(&xvattr, XAT_IMMUTABLE);
6485331Samw 			xoap->xoa_immutable = value;
6495331Samw 			break;
6505331Samw 		case F_NOUNLINK:
6515331Samw 			XVA_SET_REQ(&xvattr, XAT_NOUNLINK);
6525331Samw 			xoap->xoa_nounlink = value;
6535331Samw 			break;
6545331Samw 		case F_APPENDONLY:
6555331Samw 			XVA_SET_REQ(&xvattr, XAT_APPENDONLY);
6565331Samw 			xoap->xoa_appendonly = value;
6575331Samw 			break;
6585331Samw 		case F_NODUMP:
6595331Samw 			XVA_SET_REQ(&xvattr, XAT_NODUMP);
6605331Samw 			xoap->xoa_nodump = value;
6615331Samw 			break;
6625331Samw 		case F_AV_QUARANTINED:
6635331Samw 			XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
6645331Samw 			xoap->xoa_av_quarantined = value;
6655331Samw 			break;
6665331Samw 		case F_AV_MODIFIED:
6675331Samw 			XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
6685331Samw 			xoap->xoa_av_modified = value;
6695331Samw 			break;
6705331Samw 		case F_CRTIME:
6715331Samw 			XVA_SET_REQ(&xvattr, XAT_CREATETIME);
6725331Samw 			time = (uint64_t *)&(xoap->xoa_createtime);
6735331Samw 			for (elem = 0; elem < nelems; elem++)
6745331Samw 				*time++ = times[elem];
6755331Samw 			break;
6765331Samw 		case F_OWNERSID:
6775331Samw 		case F_GROUPSID:
6785331Samw 			if (nvlist_lookup_string(nvp_sid, SID_DOMAIN,
6795331Samw 			    &domain) || nvlist_lookup_uint32(nvp_sid, SID_RID,
6805331Samw 			    &rid)) {
6815331Samw 				nvlist_free(nvp);
6825331Samw 				return (EINVAL);
6835331Samw 			}
6845331Samw 
6855331Samw 			/*
6865331Samw 			 * Now map domain+rid to ephemeral id's
6875331Samw 			 *
6885331Samw 			 * If mapping fails, then the uid/gid will
6895331Samw 			 * be set to UID_NOBODY by Winchester.
6905331Samw 			 */
6915331Samw 
6925331Samw 			if (attr == F_OWNERSID) {
6935771Sjp151216 				(void) kidmap_getuidbysid(crgetzone(cr), domain,
6945771Sjp151216 				    rid, &xvattr.xva_vattr.va_uid);
6955331Samw 				xvattr.xva_vattr.va_mask |= AT_UID;
6965331Samw 			} else {
6975771Sjp151216 				(void) kidmap_getgidbysid(crgetzone(cr), domain,
6985771Sjp151216 				    rid, &xvattr.xva_vattr.va_gid);
6995331Samw 				xvattr.xva_vattr.va_mask |= AT_GID;
7005331Samw 			}
7015331Samw 			break;
7025331Samw 		case F_AV_SCANSTAMP:
7035331Samw 			if (ppvp->v_type == VREG) {
7045331Samw 				XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
7055331Samw 				(void) memcpy(xoap->xoa_av_scanstamp,
7065331Samw 				    scanstamp, nelems);
7075331Samw 			} else {
7085331Samw 				nvlist_free(nvp);
7095331Samw 				return (EINVAL);
7105331Samw 			}
7115331Samw 			break;
71210793Sdai.ngo@sun.com 		case F_REPARSE:
71310793Sdai.ngo@sun.com 			XVA_SET_REQ(&xvattr, XAT_REPARSE);
71410793Sdai.ngo@sun.com 			xoap->xoa_reparse = value;
71510793Sdai.ngo@sun.com 			break;
716*13082SJoyce.McIntosh@Sun.COM 		case F_OFFLINE:
717*13082SJoyce.McIntosh@Sun.COM 			XVA_SET_REQ(&xvattr, XAT_OFFLINE);
718*13082SJoyce.McIntosh@Sun.COM 			xoap->xoa_offline = value;
719*13082SJoyce.McIntosh@Sun.COM 			break;
720*13082SJoyce.McIntosh@Sun.COM 		case F_SPARSE:
721*13082SJoyce.McIntosh@Sun.COM 			XVA_SET_REQ(&xvattr, XAT_SPARSE);
722*13082SJoyce.McIntosh@Sun.COM 			xoap->xoa_sparse = value;
723*13082SJoyce.McIntosh@Sun.COM 			break;
7245331Samw 		default:
7255331Samw 			break;
7265331Samw 		}
7275331Samw 	}
7285331Samw 
7295331Samw 	ppvp = gfs_file_parent(gfs_file_parent(vp));
7305331Samw 	error = VOP_SETATTR(ppvp, &xvattr.xva_vattr, 0, cr, ct);
7315331Samw 	if (error)
7325331Samw 		uiop->uio_resid = size;
7335331Samw 
7345331Samw 	nvlist_free(nvp);
7355331Samw 	return (error);
7365331Samw }
7375331Samw 
7385331Samw static int
xattr_file_pathconf(vnode_t * vp,int cmd,ulong_t * valp,cred_t * cr,caller_context_t * ct)7395331Samw xattr_file_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
7405331Samw     caller_context_t *ct)
7415331Samw {
7425331Samw 	switch (cmd) {
7435331Samw 	case _PC_XATTR_EXISTS:
7445331Samw 	case _PC_SATTR_ENABLED:
7455331Samw 	case _PC_SATTR_EXISTS:
7465331Samw 		*valp = 0;
7475331Samw 		return (0);
7485331Samw 	default:
7495331Samw 		return (fs_pathconf(vp, cmd, valp, cr, ct));
7505331Samw 	}
7515331Samw }
7525331Samw 
7535331Samw vnodeops_t *xattr_file_ops;
7545331Samw 
7555331Samw static const fs_operation_def_t xattr_file_tops[] = {
7565331Samw 	{ VOPNAME_OPEN,		{ .vop_open = xattr_file_open }		},
7575331Samw 	{ VOPNAME_CLOSE,	{ .vop_close = xattr_file_close }	},
7585331Samw 	{ VOPNAME_READ,		{ .vop_read = xattr_file_read }		},
7595331Samw 	{ VOPNAME_WRITE,	{ .vop_write = xattr_file_write }	},
7605331Samw 	{ VOPNAME_IOCTL,	{ .error = fs_ioctl }			},
7615331Samw 	{ VOPNAME_GETATTR,	{ .vop_getattr = xattr_file_getattr }	},
7625331Samw 	{ VOPNAME_ACCESS,	{ .vop_access = xattr_file_access }	},
7635331Samw 	{ VOPNAME_READDIR,	{ .error = fs_notdir }			},
7645331Samw 	{ VOPNAME_SEEK,		{ .vop_seek = fs_seek }			},
7655331Samw 	{ VOPNAME_INACTIVE,	{ .vop_inactive = gfs_vop_inactive }	},
7665331Samw 	{ VOPNAME_FID,		{ .vop_fid = xattr_common_fid }		},
7675331Samw 	{ VOPNAME_PATHCONF,	{ .vop_pathconf = xattr_file_pathconf }	},
7685331Samw 	{ VOPNAME_PUTPAGE,	{ .error = fs_putpage }			},
7695331Samw 	{ VOPNAME_FSYNC,	{ .error = fs_fsync }			},
7705331Samw 	{ NULL }
7715331Samw };
7725331Samw 
7735331Samw vnode_t *
xattr_mkfile(vnode_t * pvp,xattr_view_t xattr_view)7745331Samw xattr_mkfile(vnode_t *pvp, xattr_view_t xattr_view)
7755331Samw {
7765331Samw 	vnode_t *vp;
7775331Samw 	xattr_file_t *np;
7785331Samw 
7795331Samw 	vp = gfs_file_create(sizeof (xattr_file_t), pvp, xattr_file_ops);
7805331Samw 	np = vp->v_data;
7815331Samw 	np->xattr_view = xattr_view;
7825331Samw 	vp->v_flag |= V_SYSATTR;
7835331Samw 	return (vp);
7845331Samw }
7855331Samw 
7865331Samw vnode_t *
xattr_mkfile_ro(vnode_t * pvp)7875331Samw xattr_mkfile_ro(vnode_t *pvp)
7885331Samw {
7895331Samw 	return (xattr_mkfile(pvp, XATTR_VIEW_READONLY));
7905331Samw }
7915331Samw 
7925331Samw vnode_t *
xattr_mkfile_rw(vnode_t * pvp)7935331Samw xattr_mkfile_rw(vnode_t *pvp)
7945331Samw {
7955331Samw 	return (xattr_mkfile(pvp, XATTR_VIEW_READWRITE));
7965331Samw }
7975331Samw 
7985331Samw vnodeops_t *xattr_dir_ops;
7995331Samw 
8005331Samw static gfs_dirent_t xattr_dirents[] = {
8015331Samw 	{ VIEW_READONLY, xattr_mkfile_ro, GFS_CACHE_VNODE, },
8025331Samw 	{ VIEW_READWRITE, xattr_mkfile_rw, GFS_CACHE_VNODE, },
8035331Samw 	{ NULL },
8045331Samw };
8055331Samw 
8065331Samw #define	XATTRDIR_NENTS	((sizeof (xattr_dirents) / sizeof (gfs_dirent_t)) - 1)
8075331Samw 
8085331Samw static int
is_sattr_name(char * s)8095331Samw is_sattr_name(char *s)
8105331Samw {
8115331Samw 	int i;
8125331Samw 
8135331Samw 	for (i = 0; i < XATTRDIR_NENTS; ++i) {
8145331Samw 		if (strcmp(s, xattr_dirents[i].gfse_name) == 0) {
8155331Samw 			return (1);
8165331Samw 		}
8175331Samw 	}
8185331Samw 	return (0);
8195331Samw }
8205331Samw 
8215663Sck153898 /*
8225663Sck153898  * Given the name of an extended attribute file, determine if there is a
8235663Sck153898  * normalization conflict with a sysattr view name.
8245663Sck153898  */
8255663Sck153898 int
xattr_sysattr_casechk(char * s)8265663Sck153898 xattr_sysattr_casechk(char *s)
8275663Sck153898 {
8285663Sck153898 	int i;
8295663Sck153898 
8305663Sck153898 	for (i = 0; i < XATTRDIR_NENTS; ++i) {
8315663Sck153898 		if (strcasecmp(s, xattr_dirents[i].gfse_name) == 0)
8325663Sck153898 			return (1);
8335663Sck153898 	}
8345663Sck153898 	return (0);
8355663Sck153898 }
8365663Sck153898 
8375331Samw static int
xattr_copy(vnode_t * sdvp,char * snm,vnode_t * tdvp,char * tnm,cred_t * cr,caller_context_t * ct)8385331Samw xattr_copy(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
8395331Samw     cred_t *cr, caller_context_t *ct)
8405331Samw {
8415331Samw 	xvattr_t xvattr;
8425331Samw 	vnode_t *pdvp;
8435331Samw 	int error;
8445331Samw 
8455331Samw 	/*
8465331Samw 	 * Only copy system attrs if the views are the same
8475331Samw 	 */
8485331Samw 	if (strcmp(snm, tnm) != 0)
8495331Samw 		return (EINVAL);
8505331Samw 
8515331Samw 	xva_init(&xvattr);
8525331Samw 
8535331Samw 	XVA_SET_REQ(&xvattr, XAT_SYSTEM);
8545331Samw 	XVA_SET_REQ(&xvattr, XAT_READONLY);
8555331Samw 	XVA_SET_REQ(&xvattr, XAT_HIDDEN);
8565331Samw 	XVA_SET_REQ(&xvattr, XAT_ARCHIVE);
8575331Samw 	XVA_SET_REQ(&xvattr, XAT_APPENDONLY);
8585331Samw 	XVA_SET_REQ(&xvattr, XAT_NOUNLINK);
8595331Samw 	XVA_SET_REQ(&xvattr, XAT_IMMUTABLE);
8605331Samw 	XVA_SET_REQ(&xvattr, XAT_NODUMP);
8615331Samw 	XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
8625331Samw 	XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
8635331Samw 	XVA_SET_REQ(&xvattr, XAT_CREATETIME);
86410793Sdai.ngo@sun.com 	XVA_SET_REQ(&xvattr, XAT_REPARSE);
865*13082SJoyce.McIntosh@Sun.COM 	XVA_SET_REQ(&xvattr, XAT_OFFLINE);
866*13082SJoyce.McIntosh@Sun.COM 	XVA_SET_REQ(&xvattr, XAT_SPARSE);
8675331Samw 
8685331Samw 	pdvp = gfs_file_parent(sdvp);
8695331Samw 	error = VOP_GETATTR(pdvp, &xvattr.xva_vattr, 0, cr, ct);
8705331Samw 	if (error)
8715331Samw 		return (error);
8725331Samw 
8735331Samw 	pdvp = gfs_file_parent(tdvp);
8745331Samw 	error = VOP_SETATTR(pdvp, &xvattr.xva_vattr, 0, cr, ct);
8755331Samw 	return (error);
8765331Samw }
8775331Samw 
8785331Samw static int
xattr_dir_realdir(vnode_t * dvp,vnode_t ** realdvp,int lookup_flags,cred_t * cr,caller_context_t * ct)8795331Samw xattr_dir_realdir(vnode_t *dvp, vnode_t **realdvp, int lookup_flags,
8805331Samw     cred_t *cr, caller_context_t *ct)
8815331Samw {
8825331Samw 	vnode_t *pvp;
8835331Samw 	int error;
8845331Samw 	struct pathname pn;
8855331Samw 	char *startnm = "";
8865331Samw 
8875331Samw 	*realdvp = NULL;
8885331Samw 
8895331Samw 	pvp = gfs_file_parent(dvp);
8905331Samw 
8915331Samw 	error = pn_get(startnm, UIO_SYSSPACE, &pn);
8925331Samw 	if (error) {
8935331Samw 		VN_RELE(pvp);
8945331Samw 		return (error);
8955331Samw 	}
8965331Samw 
8975331Samw 	/*
8985331Samw 	 * Set the LOOKUP_HAVE_SYSATTR_DIR flag so that we don't get into an
8995331Samw 	 * infinite loop with fop_lookup calling back to xattr_dir_lookup.
9005331Samw 	 */
9015331Samw 	lookup_flags |= LOOKUP_HAVE_SYSATTR_DIR;
9025331Samw 	error = VOP_LOOKUP(pvp, startnm, realdvp, &pn, lookup_flags,
9035331Samw 	    rootvp, cr, ct, NULL, NULL);
9045331Samw 	pn_free(&pn);
9055331Samw 
9065331Samw 	return (error);
9075331Samw }
9085331Samw 
9095331Samw /* ARGSUSED */
9105331Samw static int
xattr_dir_open(vnode_t ** vpp,int flags,cred_t * cr,caller_context_t * ct)9115331Samw xattr_dir_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
9125331Samw {
9135331Samw 	if (flags & FWRITE) {
9145331Samw 		return (EACCES);
9155331Samw 	}
9165331Samw 
9175331Samw 	return (0);
9185331Samw }
9195331Samw 
9205331Samw /* ARGSUSED */
9215331Samw static int
xattr_dir_close(vnode_t * vpp,int flags,int count,offset_t off,cred_t * cr,caller_context_t * ct)9225331Samw xattr_dir_close(vnode_t *vpp, int flags, int count, offset_t off, cred_t *cr,
9235331Samw     caller_context_t *ct)
9245331Samw {
9255331Samw 	return (0);
9265331Samw }
9275331Samw 
9289750SGarima.Tripathi@Sun.COM /*
9299750SGarima.Tripathi@Sun.COM  * Retrieve the attributes on an xattr directory.  If there is a "real"
9309750SGarima.Tripathi@Sun.COM  * xattr directory, use that.  Otherwise, get the attributes (represented
9319750SGarima.Tripathi@Sun.COM  * by PARENT_ATTRMASK) from the "parent" node and fill in the rest.  Note
9329750SGarima.Tripathi@Sun.COM  * that VOP_GETATTR() could turn off bits in the va_mask.
9339750SGarima.Tripathi@Sun.COM  */
9349750SGarima.Tripathi@Sun.COM 
9359750SGarima.Tripathi@Sun.COM #define	PARENT_ATTRMASK	(AT_UID|AT_GID|AT_RDEV|AT_CTIME|AT_MTIME)
9369750SGarima.Tripathi@Sun.COM 
9375331Samw /* ARGSUSED */
9385331Samw static int
xattr_dir_getattr(vnode_t * vp,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)9395331Samw xattr_dir_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
9405331Samw     caller_context_t *ct)
9415331Samw {
9425331Samw 	timestruc_t now;
9435331Samw 	vnode_t *pvp;
9445331Samw 	int error;
9455331Samw 
9465331Samw 	error = xattr_dir_realdir(vp, &pvp, LOOKUP_XATTR, cr, ct);
9475331Samw 	if (error == 0) {
9485331Samw 		error = VOP_GETATTR(pvp, vap, 0, cr, ct);
9495331Samw 		VN_RELE(pvp);
9505331Samw 		if (error) {
9515331Samw 			return (error);
9525331Samw 		}
9535331Samw 		vap->va_nlink += XATTRDIR_NENTS;
9545331Samw 		vap->va_size += XATTRDIR_NENTS;
9555331Samw 		return (0);
9565331Samw 	}
9575331Samw 
9585331Samw 	/*
9595331Samw 	 * There is no real xattr directory.  Cobble together
9609750SGarima.Tripathi@Sun.COM 	 * an entry using info from the parent object (if needed)
9619750SGarima.Tripathi@Sun.COM 	 * plus information common to all xattrs.
9625331Samw 	 */
9639750SGarima.Tripathi@Sun.COM 	if (vap->va_mask & PARENT_ATTRMASK) {
9649750SGarima.Tripathi@Sun.COM 		vattr_t pvattr;
9659750SGarima.Tripathi@Sun.COM 		uint_t  off_bits;
9669750SGarima.Tripathi@Sun.COM 
9679750SGarima.Tripathi@Sun.COM 		pvp = gfs_file_parent(vp);
9689750SGarima.Tripathi@Sun.COM 		(void) memset(&pvattr, 0, sizeof (pvattr));
9699750SGarima.Tripathi@Sun.COM 		pvattr.va_mask = PARENT_ATTRMASK;
9709750SGarima.Tripathi@Sun.COM 		error = VOP_GETATTR(pvp, &pvattr, 0, cr, ct);
9719750SGarima.Tripathi@Sun.COM 		if (error) {
9729750SGarima.Tripathi@Sun.COM 			return (error);
9739750SGarima.Tripathi@Sun.COM 		}
9749750SGarima.Tripathi@Sun.COM 
9759750SGarima.Tripathi@Sun.COM 		/*
9769750SGarima.Tripathi@Sun.COM 		 * VOP_GETATTR() might have turned off some bits in
9779750SGarima.Tripathi@Sun.COM 		 * pvattr.va_mask.  This means that the underlying
9789750SGarima.Tripathi@Sun.COM 		 * file system couldn't process those attributes.
9799750SGarima.Tripathi@Sun.COM 		 * We need to make sure those bits get turned off
9809750SGarima.Tripathi@Sun.COM 		 * in the vattr_t structure that gets passed back
9819750SGarima.Tripathi@Sun.COM 		 * to the caller.  Figure out which bits were turned
9829750SGarima.Tripathi@Sun.COM 		 * off (if any) then set pvattr.va_mask before it
9839750SGarima.Tripathi@Sun.COM 		 * gets copied to the vattr_t that the caller sees.
9849750SGarima.Tripathi@Sun.COM 		 */
9859750SGarima.Tripathi@Sun.COM 		off_bits = (pvattr.va_mask ^ PARENT_ATTRMASK) & PARENT_ATTRMASK;
9869750SGarima.Tripathi@Sun.COM 		pvattr.va_mask = vap->va_mask & ~off_bits;
9879750SGarima.Tripathi@Sun.COM 		*vap = pvattr;
9885331Samw 	}
9899750SGarima.Tripathi@Sun.COM 
9905331Samw 	vap->va_type = VDIR;
9915331Samw 	vap->va_mode = MAKEIMODE(vap->va_type, S_ISVTX | 0777);
9925331Samw 	vap->va_fsid = vp->v_vfsp->vfs_dev;
9935331Samw 	vap->va_nodeid = gfs_file_inode(vp);
9945331Samw 	vap->va_nlink = XATTRDIR_NENTS+2;
9955331Samw 	vap->va_size = vap->va_nlink;
9965331Samw 	gethrestime(&now);
9975331Samw 	vap->va_atime = now;
9985331Samw 	vap->va_blksize = 0;
9995331Samw 	vap->va_nblocks = 0;
10005331Samw 	vap->va_seq = 0;
10015331Samw 	return (0);
10025331Samw }
10035331Samw 
10045331Samw static int
xattr_dir_setattr(vnode_t * vp,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)10055331Samw xattr_dir_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
10065331Samw     caller_context_t *ct)
10075331Samw {
10085331Samw 	vnode_t *realvp;
10095331Samw 	int error;
10105331Samw 
10115331Samw 	/*
10125331Samw 	 * If there is a real xattr directory, do the setattr there.
10135331Samw 	 * Otherwise, just return success.  The GFS directory is transient,
10145331Samw 	 * and any setattr changes can disappear anyway.
10155331Samw 	 */
10165331Samw 	error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct);
10175331Samw 	if (error == 0) {
10185331Samw 		error = VOP_SETATTR(realvp, vap, flags, cr, ct);
10195331Samw 		VN_RELE(realvp);
10205331Samw 	}
10215331Samw 	if (error == ENOENT) {
10225331Samw 		error = 0;
10235331Samw 	}
10245331Samw 	return (error);
10255331Samw }
10265331Samw 
10275331Samw /* ARGSUSED */
10285331Samw static int
xattr_dir_access(vnode_t * vp,int mode,int flags,cred_t * cr,caller_context_t * ct)10295331Samw xattr_dir_access(vnode_t *vp, int mode, int flags, cred_t *cr,
10305331Samw     caller_context_t *ct)
10315331Samw {
10325331Samw 	int error;
10335331Samw 	vnode_t *realvp = NULL;
10345331Samw 
10355331Samw 	if (mode & VWRITE) {
10365331Samw 		return (EACCES);
10375331Samw 	}
10385331Samw 
10395331Samw 	error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct);
10405331Samw 
10415331Samw 	if (realvp)
10425331Samw 		VN_RELE(realvp);
10435331Samw 
10445331Samw 	/*
10455331Samw 	 * No real xattr dir isn't an error
10465331Samw 	 * an error of EINVAL indicates attributes on attributes
10475331Samw 	 * are not supported.  In that case just allow access to the
10485331Samw 	 * transient directory.
10495331Samw 	 */
10505331Samw 	return ((error == ENOENT || error == EINVAL) ? 0 : error);
10515331Samw }
10525331Samw 
10535331Samw static int
xattr_dir_create(vnode_t * dvp,char * name,vattr_t * vap,vcexcl_t excl,int mode,vnode_t ** vpp,cred_t * cr,int flag,caller_context_t * ct,vsecattr_t * vsecp)10545331Samw xattr_dir_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl,
10555331Samw     int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct,
10565331Samw     vsecattr_t *vsecp)
10575331Samw {
10585331Samw 	vnode_t *pvp;
10595331Samw 	int error;
10605331Samw 
10615331Samw 	*vpp = NULL;
10625331Samw 
10635331Samw 	/*
10645331Samw 	 * Don't allow creation of extended attributes with sysattr names.
10655331Samw 	 */
10665331Samw 	if (is_sattr_name(name)) {
10676492Stimh 		return (gfs_dir_lookup(dvp, name, vpp, cr, 0, NULL, NULL));
10685331Samw 	}
10695331Samw 
10705331Samw 	error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR|CREATE_XATTR_DIR,
10715331Samw 	    cr, ct);
10725331Samw 	if (error == 0) {
10735331Samw 		error = VOP_CREATE(pvp, name, vap, excl, mode, vpp, cr, flag,
10745331Samw 		    ct, vsecp);
10755331Samw 		VN_RELE(pvp);
10765331Samw 	}
10775331Samw 	return (error);
10785331Samw }
10795331Samw 
10805331Samw static int
xattr_dir_remove(vnode_t * dvp,char * name,cred_t * cr,caller_context_t * ct,int flags)10815331Samw xattr_dir_remove(vnode_t *dvp, char *name, cred_t *cr, caller_context_t *ct,
10825331Samw     int flags)
10835331Samw {
10845331Samw 	vnode_t *pvp;
10855331Samw 	int error;
10865331Samw 
10875331Samw 	if (is_sattr_name(name)) {
10885331Samw 		return (EACCES);
10895331Samw 	}
10905331Samw 
10915331Samw 	error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR, cr, ct);
10925331Samw 	if (error == 0) {
10935331Samw 		error = VOP_REMOVE(pvp, name, cr, ct, flags);
10945331Samw 		VN_RELE(pvp);
10955331Samw 	}
10965331Samw 	return (error);
10975331Samw }
10985331Samw 
10995331Samw static int
xattr_dir_link(vnode_t * tdvp,vnode_t * svp,char * name,cred_t * cr,caller_context_t * ct,int flags)11005331Samw xattr_dir_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr,
11015331Samw     caller_context_t *ct, int flags)
11025331Samw {
11035331Samw 	vnode_t *pvp;
11045331Samw 	int error;
11055331Samw 
11065331Samw 	if (svp->v_flag & V_SYSATTR) {
11075331Samw 		return (EINVAL);
11085331Samw 	}
11095331Samw 
11105331Samw 	error = xattr_dir_realdir(tdvp, &pvp, LOOKUP_XATTR, cr, ct);
11115331Samw 	if (error == 0) {
11125331Samw 		error = VOP_LINK(pvp, svp, name, cr, ct, flags);
11135331Samw 		VN_RELE(pvp);
11145331Samw 	}
11155331Samw 	return (error);
11165331Samw }
11175331Samw 
11185331Samw static int
xattr_dir_rename(vnode_t * sdvp,char * snm,vnode_t * tdvp,char * tnm,cred_t * cr,caller_context_t * ct,int flags)11195331Samw xattr_dir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
11205331Samw     cred_t *cr, caller_context_t *ct, int flags)
11215331Samw {
11225331Samw 	vnode_t *spvp, *tpvp;
11235331Samw 	int error;
11245331Samw 	int held_tgt;
11255331Samw 
11265331Samw 	if (is_sattr_name(snm) || is_sattr_name(tnm))
11275331Samw 		return (xattr_copy(sdvp, snm, tdvp, tnm, cr, ct));
11285331Samw 	/*
11295331Samw 	 * We know that sdvp is a GFS dir, or we wouldn't be here.
11305331Samw 	 * Get the real unnamed directory.
11315331Samw 	 */
11325331Samw 	error = xattr_dir_realdir(sdvp, &spvp, LOOKUP_XATTR, cr, ct);
11335331Samw 	if (error) {
11345331Samw 		return (error);
11355331Samw 	}
11365331Samw 
11375331Samw 	if (sdvp == tdvp) {
11385331Samw 		/*
11395331Samw 		 * If the source and target are the same GFS directory, the
11405331Samw 		 * underlying unnamed source and target dir will be the same.
11415331Samw 		 */
11425331Samw 		tpvp = spvp;
11435331Samw 		VN_HOLD(tpvp);
11445331Samw 		held_tgt = 1;
11455331Samw 	} else if (tdvp->v_flag & V_SYSATTR) {
11465331Samw 		/*
11475331Samw 		 * If the target dir is a different GFS directory,
11485331Samw 		 * find its underlying unnamed dir.
11495331Samw 		 */
11505331Samw 		error = xattr_dir_realdir(tdvp, &tpvp, LOOKUP_XATTR, cr, ct);
11515331Samw 		if (error) {
11525331Samw 			VN_RELE(spvp);
11535331Samw 			return (error);
11545331Samw 		}
11555331Samw 		held_tgt = 1;
11565331Samw 	} else {
11575331Samw 		/*
11585331Samw 		 * Target dir is outside of GFS, pass it on through.
11595331Samw 		 */
11605331Samw 		tpvp = tdvp;
11615331Samw 		held_tgt = 0;
11625331Samw 	}
11635331Samw 
11645331Samw 	error = VOP_RENAME(spvp, snm, tpvp, tnm, cr, ct, flags);
11655331Samw 
11665331Samw 	if (held_tgt) {
11675331Samw 		VN_RELE(tpvp);
11685331Samw 	}
11695331Samw 	VN_RELE(spvp);
11705331Samw 
11715331Samw 	return (error);
11725331Samw }
11735331Samw 
11745663Sck153898 /*
11755663Sck153898  * readdir_xattr_casecmp: given a system attribute name, see if there
11765663Sck153898  * is a real xattr with the same normalized name.
11775663Sck153898  */
11785663Sck153898 static int
readdir_xattr_casecmp(vnode_t * dvp,char * nm,cred_t * cr,caller_context_t * ct,int * eflags)11795663Sck153898 readdir_xattr_casecmp(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
11805663Sck153898     int *eflags)
11815663Sck153898 {
11825663Sck153898 	int error;
11835663Sck153898 	vnode_t *vp;
11845663Sck153898 	struct pathname pn;
11855663Sck153898 
11865663Sck153898 	*eflags = 0;
11875663Sck153898 
11885663Sck153898 	error = pn_get(nm, UIO_SYSSPACE, &pn);
11895663Sck153898 	if (error == 0) {
11906492Stimh 		error = VOP_LOOKUP(dvp, nm, &vp, &pn,
11916492Stimh 		    FIGNORECASE, rootvp, cr, ct, NULL, NULL);
11925663Sck153898 		if (error == 0) {
11935663Sck153898 			*eflags = ED_CASE_CONFLICT;
11945663Sck153898 			VN_RELE(vp);
11955663Sck153898 		} else if (error == ENOENT) {
11965663Sck153898 			error = 0;
11975663Sck153898 		}
11985663Sck153898 		pn_free(&pn);
11995663Sck153898 	}
12005663Sck153898 
12015663Sck153898 	return (error);
12025663Sck153898 }
12035663Sck153898 
12045331Samw static int
xattr_dir_readdir(vnode_t * dvp,uio_t * uiop,cred_t * cr,int * eofp,caller_context_t * ct,int flags)12055331Samw xattr_dir_readdir(vnode_t *dvp, uio_t *uiop, cred_t *cr, int *eofp,
12065331Samw     caller_context_t *ct, int flags)
12075331Samw {
12085331Samw 	vnode_t *pvp;
12095331Samw 	int error;
12105769Sck153898 	int local_eof;
12115331Samw 	int reset_off = 0;
12125331Samw 	int has_xattrs = 0;
12135331Samw 
12145331Samw 	if (eofp == NULL) {
12155331Samw 		eofp = &local_eof;
12165331Samw 	}
12175769Sck153898 	*eofp = 0;
12185331Samw 
12195331Samw 	/*
12205331Samw 	 * See if there is a real extended attribute directory.
12215331Samw 	 */
12225331Samw 	error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR, cr, ct);
12235331Samw 	if (error == 0) {
12245331Samw 		has_xattrs = 1;
12255331Samw 	}
12265331Samw 
12275331Samw 	/*
12285331Samw 	 * Start by reading up the static entries.
12295331Samw 	 */
12305331Samw 	if (uiop->uio_loffset == 0) {
12315663Sck153898 		ino64_t pino, ino;
12325663Sck153898 		offset_t off;
12335663Sck153898 		gfs_dir_t *dp = dvp->v_data;
12345663Sck153898 		gfs_readdir_state_t gstate;
12355663Sck153898 
12365331Samw 		if (has_xattrs) {
12375331Samw 			/*
12385331Samw 			 * If there is a real xattr dir, skip . and ..
12395331Samw 			 * in the GFS dir.  We'll pick them up below
12405331Samw 			 * when we call into the underlying fs.
12415331Samw 			 */
12425331Samw 			uiop->uio_loffset = GFS_STATIC_ENTRY_OFFSET;
12435331Samw 		}
12445663Sck153898 		error = gfs_get_parent_ino(dvp, cr, ct, &pino, &ino);
12455663Sck153898 		if (error == 0) {
12465663Sck153898 			error = gfs_readdir_init(&gstate, dp->gfsd_maxlen, 1,
12475663Sck153898 			    uiop, pino, ino, flags);
12485663Sck153898 		}
12495331Samw 		if (error) {
12505663Sck153898 			if (has_xattrs)
12515331Samw 				VN_RELE(pvp);
12525331Samw 			return (error);
12535331Samw 		}
12545663Sck153898 
12555663Sck153898 		while ((error = gfs_readdir_pred(&gstate, uiop, &off)) == 0 &&
12565663Sck153898 		    !*eofp) {
12575663Sck153898 			if (off >= 0 && off < dp->gfsd_nstatic) {
12586492Stimh 				int eflags;
12595663Sck153898 
12605663Sck153898 				/*
12615663Sck153898 				 * Check to see if this sysattr set name has a
12625663Sck153898 				 * case-insensitive conflict with a real xattr
12635663Sck153898 				 * name.
12645663Sck153898 				 */
12656492Stimh 				eflags = 0;
12665663Sck153898 				if ((flags & V_RDDIR_ENTFLAGS) && has_xattrs) {
12675663Sck153898 					error = readdir_xattr_casecmp(pvp,
12685663Sck153898 					    dp->gfsd_static[off].gfse_name,
12695663Sck153898 					    cr, ct, &eflags);
12705663Sck153898 					if (error)
12715663Sck153898 						break;
12725663Sck153898 				}
12735663Sck153898 				ino = dp->gfsd_inode(dvp, off);
12745663Sck153898 
12755663Sck153898 				error = gfs_readdir_emit(&gstate, uiop, off,
12765663Sck153898 				    ino, dp->gfsd_static[off].gfse_name,
12775663Sck153898 				    eflags);
12785663Sck153898 				if (error)
12795663Sck153898 					break;
12805663Sck153898 			} else {
12815663Sck153898 				*eofp = 1;
12825663Sck153898 			}
12835663Sck153898 		}
12845663Sck153898 
12855663Sck153898 		error = gfs_readdir_fini(&gstate, error, eofp, *eofp);
12865663Sck153898 		if (error) {
12875663Sck153898 			if (has_xattrs)
12885663Sck153898 				VN_RELE(pvp);
12895663Sck153898 			return (error);
12905663Sck153898 		}
12915663Sck153898 
12925331Samw 		/*
12935331Samw 		 * We must read all of the static entries in the first
12945331Samw 		 * call.  Otherwise we won't know if uio_loffset in a
12955331Samw 		 * subsequent call refers to the static entries or to those
12965331Samw 		 * in an underlying fs.
12975331Samw 		 */
12989749STim.Haley@Sun.COM 		if (*eofp == 0)
12999749STim.Haley@Sun.COM 			return (EINVAL);
13005331Samw 		reset_off = 1;
13015331Samw 	}
13025331Samw 
13035331Samw 	if (!has_xattrs) {
13045331Samw 		*eofp = 1;
13055331Samw 		return (0);
13065331Samw 	}
13075331Samw 
13085331Samw 	*eofp = 0;
13095331Samw 	if (reset_off) {
13105331Samw 		uiop->uio_loffset = 0;
13115331Samw 	}
13125331Samw 	(void) VOP_RWLOCK(pvp, V_WRITELOCK_FALSE, NULL);
13135331Samw 	error = VOP_READDIR(pvp, uiop, cr, eofp, ct, flags);
13145331Samw 	VOP_RWUNLOCK(pvp, V_WRITELOCK_FALSE, NULL);
13155331Samw 	VN_RELE(pvp);
13165331Samw 
13175331Samw 	return (error);
13185331Samw }
13195331Samw 
13205331Samw /* ARGSUSED */
13215331Samw static void
xattr_dir_inactive(vnode_t * vp,cred_t * cr,caller_context_t * ct)13225331Samw xattr_dir_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
13235331Samw {
13245331Samw 	gfs_file_t *fp;
132511372SMark.Shellenbaum@Sun.COM 	xattr_dir_t *xattr_dir;
13265331Samw 
132711372SMark.Shellenbaum@Sun.COM 	mutex_enter(&vp->v_lock);
132811372SMark.Shellenbaum@Sun.COM 	xattr_dir = vp->v_data;
132911372SMark.Shellenbaum@Sun.COM 	if (xattr_dir->xattr_realvp) {
133011372SMark.Shellenbaum@Sun.COM 		VN_RELE(xattr_dir->xattr_realvp);
133111372SMark.Shellenbaum@Sun.COM 		xattr_dir->xattr_realvp = NULL;
133211372SMark.Shellenbaum@Sun.COM 	}
133311372SMark.Shellenbaum@Sun.COM 	mutex_exit(&vp->v_lock);
13345331Samw 	fp = gfs_dir_inactive(vp);
13355331Samw 	if (fp != NULL) {
13365331Samw 		kmem_free(fp, fp->gfs_size);
13375331Samw 	}
13385331Samw }
13395331Samw 
13405331Samw static int
xattr_dir_pathconf(vnode_t * vp,int cmd,ulong_t * valp,cred_t * cr,caller_context_t * ct)13415331Samw xattr_dir_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
13425331Samw     caller_context_t *ct)
13435331Samw {
13445331Samw 	switch (cmd) {
13455331Samw 	case _PC_XATTR_EXISTS:
13465331Samw 	case _PC_SATTR_ENABLED:
13475331Samw 	case _PC_SATTR_EXISTS:
13485331Samw 		*valp = 0;
13495331Samw 		return (0);
13505331Samw 	default:
13515331Samw 		return (fs_pathconf(vp, cmd, valp, cr, ct));
13525331Samw 	}
13535331Samw }
13545331Samw 
135511372SMark.Shellenbaum@Sun.COM /* ARGSUSED */
135611372SMark.Shellenbaum@Sun.COM static int
xattr_dir_realvp(vnode_t * vp,vnode_t ** realvp,caller_context_t * ct)135711372SMark.Shellenbaum@Sun.COM xattr_dir_realvp(vnode_t *vp, vnode_t **realvp, caller_context_t *ct)
135811372SMark.Shellenbaum@Sun.COM {
135911372SMark.Shellenbaum@Sun.COM 	xattr_dir_t *xattr_dir;
136011372SMark.Shellenbaum@Sun.COM 
136111372SMark.Shellenbaum@Sun.COM 	mutex_enter(&vp->v_lock);
136211372SMark.Shellenbaum@Sun.COM 	xattr_dir = vp->v_data;
136311372SMark.Shellenbaum@Sun.COM 	if (xattr_dir->xattr_realvp) {
136411372SMark.Shellenbaum@Sun.COM 		*realvp = xattr_dir->xattr_realvp;
136512083SMark.Shellenbaum@Sun.COM 		mutex_exit(&vp->v_lock);
136612083SMark.Shellenbaum@Sun.COM 		return (0);
136711372SMark.Shellenbaum@Sun.COM 	} else {
136812083SMark.Shellenbaum@Sun.COM 		vnode_t *xdvp;
136912083SMark.Shellenbaum@Sun.COM 		int error;
137012083SMark.Shellenbaum@Sun.COM 
137112083SMark.Shellenbaum@Sun.COM 		mutex_exit(&vp->v_lock);
137212083SMark.Shellenbaum@Sun.COM 		if ((error = xattr_dir_realdir(vp, &xdvp,
137312083SMark.Shellenbaum@Sun.COM 		    LOOKUP_XATTR, kcred, NULL)) == 0) {
137412083SMark.Shellenbaum@Sun.COM 			/*
137512083SMark.Shellenbaum@Sun.COM 			 * verify we aren't racing with another thread
137612083SMark.Shellenbaum@Sun.COM 			 * to find the xattr_realvp
137712083SMark.Shellenbaum@Sun.COM 			 */
137812083SMark.Shellenbaum@Sun.COM 			mutex_enter(&vp->v_lock);
137912083SMark.Shellenbaum@Sun.COM 			if (xattr_dir->xattr_realvp == NULL) {
138012083SMark.Shellenbaum@Sun.COM 				xattr_dir->xattr_realvp = xdvp;
138112083SMark.Shellenbaum@Sun.COM 				*realvp = xdvp;
138212083SMark.Shellenbaum@Sun.COM 				mutex_exit(&vp->v_lock);
138312083SMark.Shellenbaum@Sun.COM 			} else {
138412083SMark.Shellenbaum@Sun.COM 				*realvp = xattr_dir->xattr_realvp;
138512083SMark.Shellenbaum@Sun.COM 				mutex_exit(&vp->v_lock);
138612083SMark.Shellenbaum@Sun.COM 				VN_RELE(xdvp);
138712083SMark.Shellenbaum@Sun.COM 			}
138812083SMark.Shellenbaum@Sun.COM 		}
138912083SMark.Shellenbaum@Sun.COM 		return (error);
139011372SMark.Shellenbaum@Sun.COM 	}
139111372SMark.Shellenbaum@Sun.COM }
139211372SMark.Shellenbaum@Sun.COM 
13935331Samw static const fs_operation_def_t xattr_dir_tops[] = {
13945331Samw 	{ VOPNAME_OPEN,		{ .vop_open = xattr_dir_open }		},
13955331Samw 	{ VOPNAME_CLOSE,	{ .vop_close = xattr_dir_close }	},
13965331Samw 	{ VOPNAME_IOCTL,	{ .error = fs_inval }			},
13975331Samw 	{ VOPNAME_GETATTR,	{ .vop_getattr = xattr_dir_getattr }	},
13985331Samw 	{ VOPNAME_SETATTR,	{ .vop_setattr = xattr_dir_setattr }	},
13995331Samw 	{ VOPNAME_ACCESS,	{ .vop_access = xattr_dir_access }	},
14005331Samw 	{ VOPNAME_READDIR,	{ .vop_readdir = xattr_dir_readdir }	},
14015331Samw 	{ VOPNAME_LOOKUP,	{ .vop_lookup = gfs_vop_lookup }	},
14025331Samw 	{ VOPNAME_CREATE,	{ .vop_create = xattr_dir_create }	},
14035331Samw 	{ VOPNAME_REMOVE,	{ .vop_remove = xattr_dir_remove }	},
14045331Samw 	{ VOPNAME_LINK,		{ .vop_link = xattr_dir_link }		},
14055331Samw 	{ VOPNAME_RENAME,	{ .vop_rename = xattr_dir_rename }	},
14065331Samw 	{ VOPNAME_MKDIR,	{ .error = fs_inval }			},
14075331Samw 	{ VOPNAME_SEEK,		{ .vop_seek = fs_seek }			},
14085331Samw 	{ VOPNAME_INACTIVE,	{ .vop_inactive = xattr_dir_inactive }	},
14095331Samw 	{ VOPNAME_FID,		{ .vop_fid = xattr_common_fid }		},
14105331Samw 	{ VOPNAME_PATHCONF,	{ .vop_pathconf = xattr_dir_pathconf }	},
141111372SMark.Shellenbaum@Sun.COM 	{ VOPNAME_REALVP,	{ .vop_realvp = xattr_dir_realvp } },
14125331Samw 	{ NULL, NULL }
14135331Samw };
14145331Samw 
14155331Samw static gfs_opsvec_t xattr_opsvec[] = {
14165331Samw 	{ "xattr dir", xattr_dir_tops, &xattr_dir_ops },
14175331Samw 	{ "system attributes", xattr_file_tops, &xattr_file_ops },
14185331Samw 	{ NULL, NULL, NULL }
14195331Samw };
14205331Samw 
14215331Samw static int
xattr_lookup_cb(vnode_t * vp,const char * nm,vnode_t ** vpp,ino64_t * inop,cred_t * cr,int flags,int * deflags,pathname_t * rpnp)14225331Samw xattr_lookup_cb(vnode_t *vp, const char *nm, vnode_t **vpp, ino64_t *inop,
14236492Stimh     cred_t *cr, int flags, int *deflags, pathname_t *rpnp)
14245331Samw {
14255331Samw 	vnode_t *pvp;
14265331Samw 	struct pathname pn;
14275331Samw 	int error;
14285331Samw 
14295331Samw 	*vpp = NULL;
14305331Samw 	*inop = 0;
14315331Samw 
14325331Samw 	error = xattr_dir_realdir(vp, &pvp, LOOKUP_XATTR|CREATE_XATTR_DIR,
14335331Samw 	    cr, NULL);
14345331Samw 
14355331Samw 	/*
14365331Samw 	 * Return ENOENT for EACCES requests during lookup.  Once an
14375331Samw 	 * attribute create is attempted EACCES will be returned.
14385331Samw 	 */
14395331Samw 	if (error) {
14405331Samw 		if (error == EACCES)
14415331Samw 			return (ENOENT);
14425331Samw 		return (error);
14435331Samw 	}
14445331Samw 
14455331Samw 	error = pn_get((char *)nm, UIO_SYSSPACE, &pn);
14465331Samw 	if (error == 0) {
14476492Stimh 		error = VOP_LOOKUP(pvp, (char *)nm, vpp, &pn, flags, rootvp,
14486492Stimh 		    cr, NULL, deflags, rpnp);
14495331Samw 		pn_free(&pn);
14505331Samw 	}
14515331Samw 	VN_RELE(pvp);
14525331Samw 
14535331Samw 	return (error);
14545331Samw }
14555331Samw 
14565331Samw /* ARGSUSED */
14575331Samw static ino64_t
xattrdir_do_ino(vnode_t * vp,int index)14585331Samw xattrdir_do_ino(vnode_t *vp, int index)
14595331Samw {
14605331Samw 	/*
14615331Samw 	 * We use index 0 for the directory fid.  Start
14625331Samw 	 * the file numbering at 1.
14635331Samw 	 */
14645331Samw 	return ((ino64_t)index+1);
14655331Samw }
14665331Samw 
14675331Samw void
xattr_init(void)14685331Samw xattr_init(void)
14695331Samw {
14705331Samw 	VERIFY(gfs_make_opsvec(xattr_opsvec) == 0);
14715331Samw }
14725331Samw 
14735331Samw int
xattr_dir_lookup(vnode_t * dvp,vnode_t ** vpp,int flags,cred_t * cr)14745331Samw xattr_dir_lookup(vnode_t *dvp, vnode_t **vpp, int flags, cred_t *cr)
14755331Samw {
14765331Samw 	int error = 0;
14775331Samw 
14785331Samw 	*vpp = NULL;
14795331Samw 
14805331Samw 	if (dvp->v_type != VDIR && dvp->v_type != VREG)
14815331Samw 		return (EINVAL);
14825331Samw 
14835331Samw 	mutex_enter(&dvp->v_lock);
14845331Samw 
14855331Samw 	/*
14865331Samw 	 * If we're already in sysattr space, don't allow creation
14875331Samw 	 * of another level of sysattrs.
14885331Samw 	 */
14895331Samw 	if (dvp->v_flag & V_SYSATTR) {
14905331Samw 		mutex_exit(&dvp->v_lock);
14915331Samw 		return (EINVAL);
14925331Samw 	}
14935331Samw 
14945331Samw 	if (dvp->v_xattrdir != NULL) {
14955331Samw 		*vpp = dvp->v_xattrdir;
14965331Samw 		VN_HOLD(*vpp);
14975331Samw 	} else {
14985331Samw 		ulong_t val;
14995331Samw 		int xattrs_allowed = dvp->v_vfsp->vfs_flag & VFS_XATTR;
15005331Samw 		int sysattrs_allowed = 1;
15015331Samw 
15025331Samw 		/*
15035331Samw 		 * We have to drop the lock on dvp.  gfs_dir_create will
15045331Samw 		 * grab it for a VN_HOLD.
15055331Samw 		 */
15065331Samw 		mutex_exit(&dvp->v_lock);
15075331Samw 
15085331Samw 		/*
15095331Samw 		 * If dvp allows xattr creation, but not sysattr
15105331Samw 		 * creation, return the real xattr dir vp. We can't
15115331Samw 		 * use the vfs feature mask here because _PC_SATTR_ENABLED
15125331Samw 		 * has vnode-level granularity (e.g. .zfs).
15135331Samw 		 */
15145331Samw 		error = VOP_PATHCONF(dvp, _PC_SATTR_ENABLED, &val, cr, NULL);
15155331Samw 		if (error != 0 || val == 0)
15165331Samw 			sysattrs_allowed = 0;
15175331Samw 
15185331Samw 		if (!xattrs_allowed && !sysattrs_allowed)
15195331Samw 			return (EINVAL);
15205331Samw 
15215331Samw 		if (!sysattrs_allowed) {
15225331Samw 			struct pathname pn;
15235331Samw 			char *nm = "";
15245331Samw 
15255331Samw 			error = pn_get(nm, UIO_SYSSPACE, &pn);
15265331Samw 			if (error)
15275331Samw 				return (error);
15285331Samw 			error = VOP_LOOKUP(dvp, nm, vpp, &pn,
15295331Samw 			    flags|LOOKUP_HAVE_SYSATTR_DIR, rootvp, cr, NULL,
15305331Samw 			    NULL, NULL);
15315331Samw 			pn_free(&pn);
15325331Samw 			return (error);
15335331Samw 		}
15345331Samw 
15355331Samw 		/*
15365331Samw 		 * Note that we act as if we were given CREATE_XATTR_DIR,
15375331Samw 		 * but only for creation of the GFS directory.
15385331Samw 		 */
15395331Samw 		*vpp = gfs_dir_create(
154011372SMark.Shellenbaum@Sun.COM 		    sizeof (xattr_dir_t), dvp, xattr_dir_ops, xattr_dirents,
15415331Samw 		    xattrdir_do_ino, MAXNAMELEN, NULL, xattr_lookup_cb);
15425331Samw 		mutex_enter(&dvp->v_lock);
15435331Samw 		if (dvp->v_xattrdir != NULL) {
15445331Samw 			/*
15455331Samw 			 * We lost the race to create the xattr dir.
15465331Samw 			 * Destroy this one, use the winner.  We can't
15475331Samw 			 * just call VN_RELE(*vpp), because the vnode
15485331Samw 			 * is only partially initialized.
15495331Samw 			 */
15505331Samw 			gfs_dir_t *dp = (*vpp)->v_data;
15515331Samw 
15525331Samw 			ASSERT((*vpp)->v_count == 1);
15535331Samw 			vn_free(*vpp);
15545331Samw 
15555331Samw 			mutex_destroy(&dp->gfsd_lock);
15565331Samw 			kmem_free(dp->gfsd_static,
15575331Samw 			    dp->gfsd_nstatic * sizeof (gfs_dirent_t));
15585331Samw 			kmem_free(dp, dp->gfsd_file.gfs_size);
15595331Samw 
15605331Samw 			/*
15615331Samw 			 * There is an implied VN_HOLD(dvp) here.  We should
15625331Samw 			 * be doing a VN_RELE(dvp) to clean up the reference
15635331Samw 			 * from *vpp, and then a VN_HOLD(dvp) for the new
15645331Samw 			 * reference.  Instead, we just leave the count alone.
15655331Samw 			 */
15665331Samw 
15675331Samw 			*vpp = dvp->v_xattrdir;
15685331Samw 			VN_HOLD(*vpp);
15695331Samw 		} else {
15705331Samw 			(*vpp)->v_flag |= (V_XATTRDIR|V_SYSATTR);
15715331Samw 			dvp->v_xattrdir = *vpp;
15725331Samw 		}
15735331Samw 	}
15745331Samw 	mutex_exit(&dvp->v_lock);
15755331Samw 
15765331Samw 	return (error);
15775331Samw }
15785331Samw 
15795331Samw int
xattr_dir_vget(vfs_t * vfsp,vnode_t ** vpp,fid_t * fidp)15805331Samw xattr_dir_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
15815331Samw {
15825331Samw 	int error;
15835331Samw 	vnode_t *pvp, *dvp;
15845331Samw 	xattr_fid_t *xfidp;
15855331Samw 	struct pathname pn;
15865331Samw 	char *nm;
15875331Samw 	uint16_t orig_len;
15885331Samw 
15895331Samw 	*vpp = NULL;
15905331Samw 
15915331Samw 	if (fidp->fid_len < XATTR_FIDSZ)
15925331Samw 		return (EINVAL);
15935331Samw 
15945331Samw 	xfidp = (xattr_fid_t *)fidp;
15955331Samw 	orig_len = fidp->fid_len;
15965331Samw 	fidp->fid_len = xfidp->parent_len;
15975331Samw 
15985331Samw 	error = VFS_VGET(vfsp, &pvp, fidp);
15995331Samw 	fidp->fid_len = orig_len;
16005331Samw 	if (error)
16015331Samw 		return (error);
16025331Samw 
16035331Samw 	/*
16045331Samw 	 * Start by getting the GFS sysattr directory.	We might need
16055331Samw 	 * to recreate it during the VOP_LOOKUP.
16065331Samw 	 */
16075331Samw 	nm = "";
16085331Samw 	error = pn_get(nm, UIO_SYSSPACE, &pn);
16095331Samw 	if (error) {
16105331Samw 		VN_RELE(pvp);
16115331Samw 		return (EINVAL);
16125331Samw 	}
16135331Samw 
16145331Samw 	error = VOP_LOOKUP(pvp, nm, &dvp, &pn, LOOKUP_XATTR|CREATE_XATTR_DIR,
16155331Samw 	    rootvp, CRED(), NULL, NULL, NULL);
16165331Samw 	pn_free(&pn);
16175331Samw 	VN_RELE(pvp);
16185331Samw 	if (error)
16195331Samw 		return (error);
16205331Samw 
16215331Samw 	if (xfidp->dir_offset == 0) {
16225331Samw 		/*
16235331Samw 		 * If we were looking for the directory, we're done.
16245331Samw 		 */
16255331Samw 		*vpp = dvp;
16265331Samw 		return (0);
16275331Samw 	}
16285331Samw 
16295331Samw 	if (xfidp->dir_offset > XATTRDIR_NENTS) {
16305331Samw 		VN_RELE(dvp);
16315331Samw 		return (EINVAL);
16325331Samw 	}
16335331Samw 
16345331Samw 	nm = xattr_dirents[xfidp->dir_offset - 1].gfse_name;
16355331Samw 
16365331Samw 	error = pn_get(nm, UIO_SYSSPACE, &pn);
16375331Samw 	if (error) {
16385331Samw 		VN_RELE(dvp);
16395331Samw 		return (EINVAL);
16405331Samw 	}
16415331Samw 
16425331Samw 	error = VOP_LOOKUP(dvp, nm, vpp, &pn, 0, rootvp, CRED(), NULL,
16435331Samw 	    NULL, NULL);
16445331Samw 
16455331Samw 	pn_free(&pn);
16465331Samw 	VN_RELE(dvp);
16475331Samw 
16485331Samw 	return (error);
16495331Samw }
1650