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