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