xref: /titanic_51/usr/src/uts/common/fs/xattr.c (revision fd9ee8b58485b20072eeef1310a88ff348d5e7fa)
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;
22899d5e173STim Haley 		case F_GEN:
22999d5e173STim Haley 			XVA_SET_REQ(&xvattr, XAT_GEN);
23099d5e173STim Haley 			break;
231*fd9ee8b5Sjoyce mcintosh 		case F_OFFLINE:
232*fd9ee8b5Sjoyce mcintosh 			XVA_SET_REQ(&xvattr, XAT_OFFLINE);
233*fd9ee8b5Sjoyce mcintosh 			break;
234*fd9ee8b5Sjoyce mcintosh 		case F_SPARSE:
235*fd9ee8b5Sjoyce mcintosh 			XVA_SET_REQ(&xvattr, XAT_SPARSE);
236*fd9ee8b5Sjoyce mcintosh 			break;
237da6c28aaSamw 		default:
238da6c28aaSamw 			break;
239da6c28aaSamw 		}
240da6c28aaSamw 	}
241da6c28aaSamw 
242da6c28aaSamw 	error = VOP_GETATTR(ppvp, &xvattr.xva_vattr, 0, cr, ct);
243da6c28aaSamw 	if (error)
244da6c28aaSamw 		return (error);
245da6c28aaSamw 
246da6c28aaSamw 	/*
247da6c28aaSamw 	 * Process all the optional attributes together here.  Notice that
248da6c28aaSamw 	 * xoap was set when the optional attribute bits were set above.
249da6c28aaSamw 	 */
250da6c28aaSamw 	if ((xvattr.xva_vattr.va_mask & AT_XVATTR) && xoap) {
251da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_READONLY)) {
252da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
253da6c28aaSamw 			    attr_to_name(F_READONLY),
254da6c28aaSamw 			    xoap->xoa_readonly) == 0);
255da6c28aaSamw 		}
256da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_HIDDEN)) {
257da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
258da6c28aaSamw 			    attr_to_name(F_HIDDEN),
259da6c28aaSamw 			    xoap->xoa_hidden) == 0);
260da6c28aaSamw 		}
261da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_SYSTEM)) {
262da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
263da6c28aaSamw 			    attr_to_name(F_SYSTEM),
264da6c28aaSamw 			    xoap->xoa_system) == 0);
265da6c28aaSamw 		}
266da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_ARCHIVE)) {
267da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
268da6c28aaSamw 			    attr_to_name(F_ARCHIVE),
269da6c28aaSamw 			    xoap->xoa_archive) == 0);
270da6c28aaSamw 		}
271da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_IMMUTABLE)) {
272da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
273da6c28aaSamw 			    attr_to_name(F_IMMUTABLE),
274da6c28aaSamw 			    xoap->xoa_immutable) == 0);
275da6c28aaSamw 		}
276da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_NOUNLINK)) {
277da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
278da6c28aaSamw 			    attr_to_name(F_NOUNLINK),
279da6c28aaSamw 			    xoap->xoa_nounlink) == 0);
280da6c28aaSamw 		}
281da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_APPENDONLY)) {
282da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
283da6c28aaSamw 			    attr_to_name(F_APPENDONLY),
284da6c28aaSamw 			    xoap->xoa_appendonly) == 0);
285da6c28aaSamw 		}
286da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_NODUMP)) {
287da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
288da6c28aaSamw 			    attr_to_name(F_NODUMP),
289da6c28aaSamw 			    xoap->xoa_nodump) == 0);
290da6c28aaSamw 		}
291da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_OPAQUE)) {
292da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
293da6c28aaSamw 			    attr_to_name(F_OPAQUE),
294da6c28aaSamw 			    xoap->xoa_opaque) == 0);
295da6c28aaSamw 		}
296da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED)) {
297da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
298da6c28aaSamw 			    attr_to_name(F_AV_QUARANTINED),
299da6c28aaSamw 			    xoap->xoa_av_quarantined) == 0);
300da6c28aaSamw 		}
301da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED)) {
302da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
303da6c28aaSamw 			    attr_to_name(F_AV_MODIFIED),
304da6c28aaSamw 			    xoap->xoa_av_modified) == 0);
305da6c28aaSamw 		}
306da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP)) {
307da6c28aaSamw 			VERIFY(nvlist_add_uint8_array(nvlp,
308da6c28aaSamw 			    attr_to_name(F_AV_SCANSTAMP),
309da6c28aaSamw 			    xoap->xoa_av_scanstamp,
310da6c28aaSamw 			    sizeof (xoap->xoa_av_scanstamp)) == 0);
311da6c28aaSamw 		}
312da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_CREATETIME)) {
313da6c28aaSamw 			VERIFY(nvlist_add_uint64_array(nvlp,
314da6c28aaSamw 			    attr_to_name(F_CRTIME),
315da6c28aaSamw 			    (uint64_t *)&(xoap->xoa_createtime),
316da6c28aaSamw 			    sizeof (xoap->xoa_createtime) /
317da6c28aaSamw 			    sizeof (uint64_t)) == 0);
318da6c28aaSamw 		}
3197a286c47SDai Ngo 		if (XVA_ISSET_RTN(&xvattr, XAT_REPARSE)) {
3207a286c47SDai Ngo 			VERIFY(nvlist_add_boolean_value(nvlp,
3217a286c47SDai Ngo 			    attr_to_name(F_REPARSE),
3227a286c47SDai Ngo 			    xoap->xoa_reparse) == 0);
3237a286c47SDai Ngo 		}
32499d5e173STim Haley 		if (XVA_ISSET_RTN(&xvattr, XAT_GEN)) {
32599d5e173STim Haley 			VERIFY(nvlist_add_uint64(nvlp,
32699d5e173STim Haley 			    attr_to_name(F_GEN),
32799d5e173STim Haley 			    xoap->xoa_generation) == 0);
32899d5e173STim Haley 		}
329*fd9ee8b5Sjoyce mcintosh 		if (XVA_ISSET_RTN(&xvattr, XAT_OFFLINE)) {
330*fd9ee8b5Sjoyce mcintosh 			VERIFY(nvlist_add_boolean_value(nvlp,
331*fd9ee8b5Sjoyce mcintosh 			    attr_to_name(F_OFFLINE),
332*fd9ee8b5Sjoyce mcintosh 			    xoap->xoa_offline) == 0);
333*fd9ee8b5Sjoyce mcintosh 		}
334*fd9ee8b5Sjoyce mcintosh 		if (XVA_ISSET_RTN(&xvattr, XAT_SPARSE)) {
335*fd9ee8b5Sjoyce mcintosh 			VERIFY(nvlist_add_boolean_value(nvlp,
336*fd9ee8b5Sjoyce mcintosh 			    attr_to_name(F_SPARSE),
337*fd9ee8b5Sjoyce mcintosh 			    xoap->xoa_sparse) == 0);
338*fd9ee8b5Sjoyce mcintosh 		}
339da6c28aaSamw 	}
340da6c28aaSamw 	/*
341da6c28aaSamw 	 * Check for optional ownersid/groupsid
342da6c28aaSamw 	 */
343da6c28aaSamw 
344da6c28aaSamw 	if (xvattr.xva_vattr.va_uid > 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_getsidbyuid(crgetzone(cr), xvattr.xva_vattr.va_uid,
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_OWNERSID),
356da6c28aaSamw 			    nvl_sid) == 0);
357da6c28aaSamw 		}
358da6c28aaSamw 		nvlist_free(nvl_sid);
359da6c28aaSamw 	}
360da6c28aaSamw 	if (xvattr.xva_vattr.va_gid > MAXUID) {
361da6c28aaSamw 		nvlist_t *nvl_sid;
362da6c28aaSamw 
363da6c28aaSamw 		if (nvlist_alloc(&nvl_sid, NV_UNIQUE_NAME, KM_SLEEP))
364da6c28aaSamw 			return (ENOMEM);
365da6c28aaSamw 
366bda89588Sjp151216 		if (kidmap_getsidbygid(crgetzone(cr), xvattr.xva_vattr.va_gid,
367da6c28aaSamw 		    &domain, &rid) == 0) {
368da6c28aaSamw 			VERIFY(nvlist_add_string(nvl_sid,
369da6c28aaSamw 			    SID_DOMAIN, domain) == 0);
370da6c28aaSamw 			VERIFY(nvlist_add_uint32(nvl_sid, SID_RID, rid) == 0);
371da6c28aaSamw 			VERIFY(nvlist_add_nvlist(nvlp, attr_to_name(F_GROUPSID),
372da6c28aaSamw 			    nvl_sid) == 0);
373da6c28aaSamw 		}
374da6c28aaSamw 		nvlist_free(nvl_sid);
375da6c28aaSamw 	}
376da6c28aaSamw 
377da6c28aaSamw 	return (0);
378da6c28aaSamw }
379da6c28aaSamw 
380da6c28aaSamw /*
381da6c28aaSamw  * The size of a sysattr file is the size of the nvlist that will be
382da6c28aaSamw  * returned by xattr_file_read().  A call to xattr_file_write() could
383da6c28aaSamw  * change the size of that nvlist.  That size is not stored persistently
384da6c28aaSamw  * so xattr_fill_nvlist() calls VOP_GETATTR so that it can be calculated.
385da6c28aaSamw  */
386da6c28aaSamw static int
387da6c28aaSamw xattr_file_size(vnode_t *vp, xattr_view_t xattr_view, size_t *size,
388da6c28aaSamw     cred_t *cr, caller_context_t *ct)
389da6c28aaSamw {
390da6c28aaSamw 	nvlist_t *nvl;
391da6c28aaSamw 
392da6c28aaSamw 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP)) {
393da6c28aaSamw 		return (ENOMEM);
394da6c28aaSamw 	}
395da6c28aaSamw 
396da6c28aaSamw 	if (xattr_fill_nvlist(vp, xattr_view, nvl, cr, ct)) {
397da6c28aaSamw 		nvlist_free(nvl);
398da6c28aaSamw 		return (EFAULT);
399da6c28aaSamw 	}
400da6c28aaSamw 
401da6c28aaSamw 	VERIFY(nvlist_size(nvl, size, NV_ENCODE_XDR) == 0);
402da6c28aaSamw 	nvlist_free(nvl);
403da6c28aaSamw 	return (0);
404da6c28aaSamw }
405da6c28aaSamw 
406da6c28aaSamw /* ARGSUSED */
407da6c28aaSamw static int
408da6c28aaSamw xattr_file_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
409da6c28aaSamw     caller_context_t *ct)
410da6c28aaSamw {
411da6c28aaSamw 	xattr_file_t *np = vp->v_data;
412da6c28aaSamw 	timestruc_t now;
413da6c28aaSamw 	size_t size;
414da6c28aaSamw 	int error;
415da6c28aaSamw 	vnode_t *pvp;
416da6c28aaSamw 	vattr_t pvattr;
417da6c28aaSamw 
418da6c28aaSamw 	vap->va_type = VREG;
419da6c28aaSamw 	vap->va_mode = MAKEIMODE(vap->va_type,
420da6c28aaSamw 	    (np->xattr_view == XATTR_VIEW_READONLY ? 0444 : 0644));
421da6c28aaSamw 	vap->va_nodeid = gfs_file_inode(vp);
422da6c28aaSamw 	vap->va_nlink = 1;
423da6c28aaSamw 	pvp = gfs_file_parent(vp);
424da6c28aaSamw 	(void) memset(&pvattr, 0, sizeof (pvattr));
425da6c28aaSamw 	pvattr.va_mask = AT_CTIME|AT_MTIME;
426da6c28aaSamw 	error = VOP_GETATTR(pvp, &pvattr, flags, cr, ct);
427da6c28aaSamw 	if (error) {
428da6c28aaSamw 		return (error);
429da6c28aaSamw 	}
430da6c28aaSamw 	vap->va_ctime = pvattr.va_ctime;
431da6c28aaSamw 	vap->va_mtime = pvattr.va_mtime;
432da6c28aaSamw 	gethrestime(&now);
433da6c28aaSamw 	vap->va_atime = now;
434da6c28aaSamw 	vap->va_uid = 0;
435da6c28aaSamw 	vap->va_gid = 0;
436da6c28aaSamw 	vap->va_rdev = 0;
437da6c28aaSamw 	vap->va_blksize = DEV_BSIZE;
438da6c28aaSamw 	vap->va_seq = 0;
439da6c28aaSamw 	vap->va_fsid = vp->v_vfsp->vfs_dev;
440da6c28aaSamw 	error = xattr_file_size(vp, np->xattr_view, &size, cr, ct);
441da6c28aaSamw 	vap->va_size = size;
442da6c28aaSamw 	vap->va_nblocks = howmany(vap->va_size, vap->va_blksize);
443da6c28aaSamw 	return (error);
444da6c28aaSamw }
445da6c28aaSamw 
446da6c28aaSamw /* ARGSUSED */
447da6c28aaSamw static int
448da6c28aaSamw xattr_file_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
449da6c28aaSamw     caller_context_t *ct)
450da6c28aaSamw {
451da6c28aaSamw 	xattr_file_t *np = vp->v_data;
452da6c28aaSamw 	xattr_view_t xattr_view = np->xattr_view;
453da6c28aaSamw 	char *buf;
454da6c28aaSamw 	size_t filesize;
455da6c28aaSamw 	nvlist_t *nvl;
456da6c28aaSamw 	int error;
457da6c28aaSamw 
458da6c28aaSamw 	/*
459da6c28aaSamw 	 * Validate file offset and fasttrack empty reads
460da6c28aaSamw 	 */
461da6c28aaSamw 	if (uiop->uio_loffset < (offset_t)0)
462da6c28aaSamw 		return (EINVAL);
463da6c28aaSamw 
464da6c28aaSamw 	if (uiop->uio_resid == 0)
465da6c28aaSamw 		return (0);
466da6c28aaSamw 
467da6c28aaSamw 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP))
468da6c28aaSamw 		return (ENOMEM);
469da6c28aaSamw 
470da6c28aaSamw 	if (xattr_fill_nvlist(vp, xattr_view, nvl, cr, ct)) {
471da6c28aaSamw 		nvlist_free(nvl);
472da6c28aaSamw 		return (EFAULT);
473da6c28aaSamw 	}
474da6c28aaSamw 
475da6c28aaSamw 	VERIFY(nvlist_size(nvl, &filesize, NV_ENCODE_XDR) == 0);
476da6c28aaSamw 
477da6c28aaSamw 	if (uiop->uio_loffset >= filesize) {
478da6c28aaSamw 		nvlist_free(nvl);
479da6c28aaSamw 		return (0);
480da6c28aaSamw 	}
481da6c28aaSamw 
482da6c28aaSamw 	buf = kmem_alloc(filesize, KM_SLEEP);
483da6c28aaSamw 	VERIFY(nvlist_pack(nvl, &buf, &filesize, NV_ENCODE_XDR,
484da6c28aaSamw 	    KM_SLEEP) == 0);
485da6c28aaSamw 
486da6c28aaSamw 	error = uiomove((caddr_t)buf, filesize, UIO_READ, uiop);
487da6c28aaSamw 	kmem_free(buf, filesize);
488da6c28aaSamw 	nvlist_free(nvl);
489da6c28aaSamw 	return (error);
490da6c28aaSamw }
491da6c28aaSamw 
492da6c28aaSamw /* ARGSUSED */
493da6c28aaSamw static int
494da6c28aaSamw xattr_file_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
495da6c28aaSamw     caller_context_t *ct)
496da6c28aaSamw {
497da6c28aaSamw 	int error = 0;
498da6c28aaSamw 	char *buf;
499da6c28aaSamw 	char *domain;
500da6c28aaSamw 	uint32_t rid;
501da6c28aaSamw 	ssize_t size = uiop->uio_resid;
502da6c28aaSamw 	nvlist_t *nvp;
503da6c28aaSamw 	nvpair_t *pair = NULL;
504da6c28aaSamw 	vnode_t *ppvp;
505da6c28aaSamw 	xvattr_t xvattr;
506da6c28aaSamw 	xoptattr_t *xoap = NULL;	/* Pointer to optional attributes */
507da6c28aaSamw 
5089660e5cbSJanice Chang 	if (vfs_has_feature(vp->v_vfsp, VFSFT_XVATTR) == 0)
5099660e5cbSJanice Chang 		return (EINVAL);
5109660e5cbSJanice Chang 
511da6c28aaSamw 	/*
512da6c28aaSamw 	 * Validate file offset and size.
513da6c28aaSamw 	 */
514da6c28aaSamw 	if (uiop->uio_loffset < (offset_t)0)
515da6c28aaSamw 		return (EINVAL);
516da6c28aaSamw 
517da6c28aaSamw 	if (size == 0)
518da6c28aaSamw 		return (EINVAL);
519da6c28aaSamw 
520da6c28aaSamw 	xva_init(&xvattr);
521da6c28aaSamw 
522da6c28aaSamw 	if ((xoap = xva_getxoptattr(&xvattr)) == NULL) {
523da6c28aaSamw 		return (EINVAL);
524da6c28aaSamw 	}
525da6c28aaSamw 
526da6c28aaSamw 	/*
527da6c28aaSamw 	 * Copy and unpack the nvlist
528da6c28aaSamw 	 */
529da6c28aaSamw 	buf = kmem_alloc(size, KM_SLEEP);
530da6c28aaSamw 	if (uiomove((caddr_t)buf, size, UIO_WRITE, uiop)) {
531da6c28aaSamw 		return (EFAULT);
532da6c28aaSamw 	}
533da6c28aaSamw 
534da6c28aaSamw 	if (nvlist_unpack(buf, size, &nvp, KM_SLEEP) != 0) {
535da6c28aaSamw 		kmem_free(buf, size);
536da6c28aaSamw 		uiop->uio_resid = size;
537da6c28aaSamw 		return (EINVAL);
538da6c28aaSamw 	}
539da6c28aaSamw 	kmem_free(buf, size);
540da6c28aaSamw 
541da6c28aaSamw 	/*
542da6c28aaSamw 	 * Fasttrack empty writes (nvlist with no nvpairs)
543da6c28aaSamw 	 */
544da6c28aaSamw 	if (nvlist_next_nvpair(nvp, NULL) == 0)
545da6c28aaSamw 		return (0);
546da6c28aaSamw 
547da6c28aaSamw 	ppvp = gfs_file_parent(gfs_file_parent(vp));
548da6c28aaSamw 
549da6c28aaSamw 	while (pair = nvlist_next_nvpair(nvp, pair)) {
550da6c28aaSamw 		data_type_t type;
551da6c28aaSamw 		f_attr_t attr;
552da6c28aaSamw 		boolean_t value;
553da6c28aaSamw 		uint64_t *time, *times;
554da6c28aaSamw 		uint_t elem, nelems;
555da6c28aaSamw 		nvlist_t *nvp_sid;
556da6c28aaSamw 		uint8_t *scanstamp;
557da6c28aaSamw 
558da6c28aaSamw 		/*
559da6c28aaSamw 		 * Validate the name and type of each attribute.
560da6c28aaSamw 		 * Log any unknown names and continue.  This will
561da6c28aaSamw 		 * help if additional attributes are added later.
562da6c28aaSamw 		 */
563da6c28aaSamw 		type = nvpair_type(pair);
564da6c28aaSamw 		if ((attr = name_to_attr(nvpair_name(pair))) == F_ATTR_INVAL) {
565da6c28aaSamw 			cmn_err(CE_WARN, "Unknown attribute %s",
566da6c28aaSamw 			    nvpair_name(pair));
567da6c28aaSamw 			continue;
568da6c28aaSamw 		}
569da6c28aaSamw 
570da6c28aaSamw 		/*
571da6c28aaSamw 		 * Verify nvlist type matches required type and view is OK
572da6c28aaSamw 		 */
573da6c28aaSamw 
574da6c28aaSamw 		if (type != attr_to_data_type(attr) ||
575da6c28aaSamw 		    (attr_to_xattr_view(attr) == XATTR_VIEW_READONLY)) {
576da6c28aaSamw 			nvlist_free(nvp);
577da6c28aaSamw 			return (EINVAL);
578da6c28aaSamw 		}
579da6c28aaSamw 
580da6c28aaSamw 		/*
581da6c28aaSamw 		 * For OWNERSID/GROUPSID make sure the target
582da6c28aaSamw 		 * file system support ephemeral ID's
583da6c28aaSamw 		 */
584da6c28aaSamw 		if ((attr == F_OWNERSID || attr == F_GROUPSID) &&
585da6c28aaSamw 		    (!(vp->v_vfsp->vfs_flag & VFS_XID))) {
586da6c28aaSamw 			nvlist_free(nvp);
587da6c28aaSamw 			return (EINVAL);
588da6c28aaSamw 		}
589da6c28aaSamw 
590da6c28aaSamw 		/*
591da6c28aaSamw 		 * Retrieve data from nvpair
592da6c28aaSamw 		 */
593da6c28aaSamw 		switch (type) {
594da6c28aaSamw 		case DATA_TYPE_BOOLEAN_VALUE:
595da6c28aaSamw 			if (nvpair_value_boolean_value(pair, &value)) {
596da6c28aaSamw 				nvlist_free(nvp);
597da6c28aaSamw 				return (EINVAL);
598da6c28aaSamw 			}
599da6c28aaSamw 			break;
600da6c28aaSamw 		case DATA_TYPE_UINT64_ARRAY:
601da6c28aaSamw 			if (nvpair_value_uint64_array(pair, &times, &nelems)) {
602da6c28aaSamw 				nvlist_free(nvp);
603da6c28aaSamw 				return (EINVAL);
604da6c28aaSamw 			}
605da6c28aaSamw 			break;
606da6c28aaSamw 		case DATA_TYPE_NVLIST:
607da6c28aaSamw 			if (nvpair_value_nvlist(pair, &nvp_sid)) {
608da6c28aaSamw 				nvlist_free(nvp);
609da6c28aaSamw 				return (EINVAL);
610da6c28aaSamw 			}
611da6c28aaSamw 			break;
612da6c28aaSamw 		case DATA_TYPE_UINT8_ARRAY:
613da6c28aaSamw 			if (nvpair_value_uint8_array(pair,
614da6c28aaSamw 			    &scanstamp, &nelems)) {
615da6c28aaSamw 				nvlist_free(nvp);
616da6c28aaSamw 				return (EINVAL);
617da6c28aaSamw 			}
618da6c28aaSamw 			break;
619da6c28aaSamw 		default:
620da6c28aaSamw 			nvlist_free(nvp);
621da6c28aaSamw 			return (EINVAL);
622da6c28aaSamw 		}
623da6c28aaSamw 
624da6c28aaSamw 		switch (attr) {
625da6c28aaSamw 		/*
626da6c28aaSamw 		 * If we have several similar optional attributes to
627da6c28aaSamw 		 * process then we should do it all together here so that
628da6c28aaSamw 		 * xoap and the requested bitmap can be set in one place.
629da6c28aaSamw 		 */
630da6c28aaSamw 		case F_READONLY:
631da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_READONLY);
632da6c28aaSamw 			xoap->xoa_readonly = value;
633da6c28aaSamw 			break;
634da6c28aaSamw 		case F_HIDDEN:
635da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_HIDDEN);
636da6c28aaSamw 			xoap->xoa_hidden = value;
637da6c28aaSamw 			break;
638da6c28aaSamw 		case F_SYSTEM:
639da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_SYSTEM);
640da6c28aaSamw 			xoap->xoa_system = value;
641da6c28aaSamw 			break;
642da6c28aaSamw 		case F_ARCHIVE:
643da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_ARCHIVE);
644da6c28aaSamw 			xoap->xoa_archive = value;
645da6c28aaSamw 			break;
646da6c28aaSamw 		case F_IMMUTABLE:
647da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_IMMUTABLE);
648da6c28aaSamw 			xoap->xoa_immutable = value;
649da6c28aaSamw 			break;
650da6c28aaSamw 		case F_NOUNLINK:
651da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_NOUNLINK);
652da6c28aaSamw 			xoap->xoa_nounlink = value;
653da6c28aaSamw 			break;
654da6c28aaSamw 		case F_APPENDONLY:
655da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_APPENDONLY);
656da6c28aaSamw 			xoap->xoa_appendonly = value;
657da6c28aaSamw 			break;
658da6c28aaSamw 		case F_NODUMP:
659da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_NODUMP);
660da6c28aaSamw 			xoap->xoa_nodump = value;
661da6c28aaSamw 			break;
662da6c28aaSamw 		case F_AV_QUARANTINED:
663da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
664da6c28aaSamw 			xoap->xoa_av_quarantined = value;
665da6c28aaSamw 			break;
666da6c28aaSamw 		case F_AV_MODIFIED:
667da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
668da6c28aaSamw 			xoap->xoa_av_modified = value;
669da6c28aaSamw 			break;
670da6c28aaSamw 		case F_CRTIME:
671da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_CREATETIME);
672da6c28aaSamw 			time = (uint64_t *)&(xoap->xoa_createtime);
673da6c28aaSamw 			for (elem = 0; elem < nelems; elem++)
674da6c28aaSamw 				*time++ = times[elem];
675da6c28aaSamw 			break;
676da6c28aaSamw 		case F_OWNERSID:
677da6c28aaSamw 		case F_GROUPSID:
678da6c28aaSamw 			if (nvlist_lookup_string(nvp_sid, SID_DOMAIN,
679da6c28aaSamw 			    &domain) || nvlist_lookup_uint32(nvp_sid, SID_RID,
680da6c28aaSamw 			    &rid)) {
681da6c28aaSamw 				nvlist_free(nvp);
682da6c28aaSamw 				return (EINVAL);
683da6c28aaSamw 			}
684da6c28aaSamw 
685da6c28aaSamw 			/*
686da6c28aaSamw 			 * Now map domain+rid to ephemeral id's
687da6c28aaSamw 			 *
688da6c28aaSamw 			 * If mapping fails, then the uid/gid will
689da6c28aaSamw 			 * be set to UID_NOBODY by Winchester.
690da6c28aaSamw 			 */
691da6c28aaSamw 
692da6c28aaSamw 			if (attr == F_OWNERSID) {
693bda89588Sjp151216 				(void) kidmap_getuidbysid(crgetzone(cr), domain,
694bda89588Sjp151216 				    rid, &xvattr.xva_vattr.va_uid);
695da6c28aaSamw 				xvattr.xva_vattr.va_mask |= AT_UID;
696da6c28aaSamw 			} else {
697bda89588Sjp151216 				(void) kidmap_getgidbysid(crgetzone(cr), domain,
698bda89588Sjp151216 				    rid, &xvattr.xva_vattr.va_gid);
699da6c28aaSamw 				xvattr.xva_vattr.va_mask |= AT_GID;
700da6c28aaSamw 			}
701da6c28aaSamw 			break;
702da6c28aaSamw 		case F_AV_SCANSTAMP:
703da6c28aaSamw 			if (ppvp->v_type == VREG) {
704da6c28aaSamw 				XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
705da6c28aaSamw 				(void) memcpy(xoap->xoa_av_scanstamp,
706da6c28aaSamw 				    scanstamp, nelems);
707da6c28aaSamw 			} else {
708da6c28aaSamw 				nvlist_free(nvp);
709da6c28aaSamw 				return (EINVAL);
710da6c28aaSamw 			}
711da6c28aaSamw 			break;
7127a286c47SDai Ngo 		case F_REPARSE:
7137a286c47SDai Ngo 			XVA_SET_REQ(&xvattr, XAT_REPARSE);
7147a286c47SDai Ngo 			xoap->xoa_reparse = value;
7157a286c47SDai Ngo 			break;
716*fd9ee8b5Sjoyce mcintosh 		case F_OFFLINE:
717*fd9ee8b5Sjoyce mcintosh 			XVA_SET_REQ(&xvattr, XAT_OFFLINE);
718*fd9ee8b5Sjoyce mcintosh 			xoap->xoa_offline = value;
719*fd9ee8b5Sjoyce mcintosh 			break;
720*fd9ee8b5Sjoyce mcintosh 		case F_SPARSE:
721*fd9ee8b5Sjoyce mcintosh 			XVA_SET_REQ(&xvattr, XAT_SPARSE);
722*fd9ee8b5Sjoyce mcintosh 			xoap->xoa_sparse = value;
723*fd9ee8b5Sjoyce mcintosh 			break;
724da6c28aaSamw 		default:
725da6c28aaSamw 			break;
726da6c28aaSamw 		}
727da6c28aaSamw 	}
728da6c28aaSamw 
729da6c28aaSamw 	ppvp = gfs_file_parent(gfs_file_parent(vp));
730da6c28aaSamw 	error = VOP_SETATTR(ppvp, &xvattr.xva_vattr, 0, cr, ct);
731da6c28aaSamw 	if (error)
732da6c28aaSamw 		uiop->uio_resid = size;
733da6c28aaSamw 
734da6c28aaSamw 	nvlist_free(nvp);
735da6c28aaSamw 	return (error);
736da6c28aaSamw }
737da6c28aaSamw 
738da6c28aaSamw static int
739da6c28aaSamw xattr_file_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
740da6c28aaSamw     caller_context_t *ct)
741da6c28aaSamw {
742da6c28aaSamw 	switch (cmd) {
743da6c28aaSamw 	case _PC_XATTR_EXISTS:
744da6c28aaSamw 	case _PC_SATTR_ENABLED:
745da6c28aaSamw 	case _PC_SATTR_EXISTS:
746da6c28aaSamw 		*valp = 0;
747da6c28aaSamw 		return (0);
748da6c28aaSamw 	default:
749da6c28aaSamw 		return (fs_pathconf(vp, cmd, valp, cr, ct));
750da6c28aaSamw 	}
751da6c28aaSamw }
752da6c28aaSamw 
753da6c28aaSamw vnodeops_t *xattr_file_ops;
754da6c28aaSamw 
755da6c28aaSamw static const fs_operation_def_t xattr_file_tops[] = {
756da6c28aaSamw 	{ VOPNAME_OPEN,		{ .vop_open = xattr_file_open }		},
757da6c28aaSamw 	{ VOPNAME_CLOSE,	{ .vop_close = xattr_file_close }	},
758da6c28aaSamw 	{ VOPNAME_READ,		{ .vop_read = xattr_file_read }		},
759da6c28aaSamw 	{ VOPNAME_WRITE,	{ .vop_write = xattr_file_write }	},
760da6c28aaSamw 	{ VOPNAME_IOCTL,	{ .error = fs_ioctl }			},
761da6c28aaSamw 	{ VOPNAME_GETATTR,	{ .vop_getattr = xattr_file_getattr }	},
762da6c28aaSamw 	{ VOPNAME_ACCESS,	{ .vop_access = xattr_file_access }	},
763da6c28aaSamw 	{ VOPNAME_READDIR,	{ .error = fs_notdir }			},
764da6c28aaSamw 	{ VOPNAME_SEEK,		{ .vop_seek = fs_seek }			},
765da6c28aaSamw 	{ VOPNAME_INACTIVE,	{ .vop_inactive = gfs_vop_inactive }	},
766da6c28aaSamw 	{ VOPNAME_FID,		{ .vop_fid = xattr_common_fid }		},
767da6c28aaSamw 	{ VOPNAME_PATHCONF,	{ .vop_pathconf = xattr_file_pathconf }	},
768da6c28aaSamw 	{ VOPNAME_PUTPAGE,	{ .error = fs_putpage }			},
769da6c28aaSamw 	{ VOPNAME_FSYNC,	{ .error = fs_fsync }			},
770da6c28aaSamw 	{ NULL }
771da6c28aaSamw };
772da6c28aaSamw 
773da6c28aaSamw vnode_t *
774da6c28aaSamw xattr_mkfile(vnode_t *pvp, xattr_view_t xattr_view)
775da6c28aaSamw {
776da6c28aaSamw 	vnode_t *vp;
777da6c28aaSamw 	xattr_file_t *np;
778da6c28aaSamw 
779da6c28aaSamw 	vp = gfs_file_create(sizeof (xattr_file_t), pvp, xattr_file_ops);
780da6c28aaSamw 	np = vp->v_data;
781da6c28aaSamw 	np->xattr_view = xattr_view;
782da6c28aaSamw 	vp->v_flag |= V_SYSATTR;
783da6c28aaSamw 	return (vp);
784da6c28aaSamw }
785da6c28aaSamw 
786da6c28aaSamw vnode_t *
787da6c28aaSamw xattr_mkfile_ro(vnode_t *pvp)
788da6c28aaSamw {
789da6c28aaSamw 	return (xattr_mkfile(pvp, XATTR_VIEW_READONLY));
790da6c28aaSamw }
791da6c28aaSamw 
792da6c28aaSamw vnode_t *
793da6c28aaSamw xattr_mkfile_rw(vnode_t *pvp)
794da6c28aaSamw {
795da6c28aaSamw 	return (xattr_mkfile(pvp, XATTR_VIEW_READWRITE));
796da6c28aaSamw }
797da6c28aaSamw 
798da6c28aaSamw vnodeops_t *xattr_dir_ops;
799da6c28aaSamw 
800da6c28aaSamw static gfs_dirent_t xattr_dirents[] = {
801da6c28aaSamw 	{ VIEW_READONLY, xattr_mkfile_ro, GFS_CACHE_VNODE, },
802da6c28aaSamw 	{ VIEW_READWRITE, xattr_mkfile_rw, GFS_CACHE_VNODE, },
803da6c28aaSamw 	{ NULL },
804da6c28aaSamw };
805da6c28aaSamw 
806da6c28aaSamw #define	XATTRDIR_NENTS	((sizeof (xattr_dirents) / sizeof (gfs_dirent_t)) - 1)
807da6c28aaSamw 
808da6c28aaSamw static int
809da6c28aaSamw is_sattr_name(char *s)
810da6c28aaSamw {
811da6c28aaSamw 	int i;
812da6c28aaSamw 
813da6c28aaSamw 	for (i = 0; i < XATTRDIR_NENTS; ++i) {
814da6c28aaSamw 		if (strcmp(s, xattr_dirents[i].gfse_name) == 0) {
815da6c28aaSamw 			return (1);
816da6c28aaSamw 		}
817da6c28aaSamw 	}
818da6c28aaSamw 	return (0);
819da6c28aaSamw }
820da6c28aaSamw 
821b38f0970Sck153898 /*
822b38f0970Sck153898  * Given the name of an extended attribute file, determine if there is a
823b38f0970Sck153898  * normalization conflict with a sysattr view name.
824b38f0970Sck153898  */
825b38f0970Sck153898 int
826b38f0970Sck153898 xattr_sysattr_casechk(char *s)
827b38f0970Sck153898 {
828b38f0970Sck153898 	int i;
829b38f0970Sck153898 
830b38f0970Sck153898 	for (i = 0; i < XATTRDIR_NENTS; ++i) {
831b38f0970Sck153898 		if (strcasecmp(s, xattr_dirents[i].gfse_name) == 0)
832b38f0970Sck153898 			return (1);
833b38f0970Sck153898 	}
834b38f0970Sck153898 	return (0);
835b38f0970Sck153898 }
836b38f0970Sck153898 
837da6c28aaSamw static int
838da6c28aaSamw xattr_copy(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
839da6c28aaSamw     cred_t *cr, caller_context_t *ct)
840da6c28aaSamw {
841da6c28aaSamw 	xvattr_t xvattr;
842da6c28aaSamw 	vnode_t *pdvp;
843da6c28aaSamw 	int error;
844da6c28aaSamw 
845da6c28aaSamw 	/*
846da6c28aaSamw 	 * Only copy system attrs if the views are the same
847da6c28aaSamw 	 */
848da6c28aaSamw 	if (strcmp(snm, tnm) != 0)
849da6c28aaSamw 		return (EINVAL);
850da6c28aaSamw 
851da6c28aaSamw 	xva_init(&xvattr);
852da6c28aaSamw 
853da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_SYSTEM);
854da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_READONLY);
855da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_HIDDEN);
856da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_ARCHIVE);
857da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_APPENDONLY);
858da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_NOUNLINK);
859da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_IMMUTABLE);
860da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_NODUMP);
861da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
862da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
863da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_CREATETIME);
8647a286c47SDai Ngo 	XVA_SET_REQ(&xvattr, XAT_REPARSE);
865*fd9ee8b5Sjoyce mcintosh 	XVA_SET_REQ(&xvattr, XAT_OFFLINE);
866*fd9ee8b5Sjoyce mcintosh 	XVA_SET_REQ(&xvattr, XAT_SPARSE);
867da6c28aaSamw 
868da6c28aaSamw 	pdvp = gfs_file_parent(sdvp);
869da6c28aaSamw 	error = VOP_GETATTR(pdvp, &xvattr.xva_vattr, 0, cr, ct);
870da6c28aaSamw 	if (error)
871da6c28aaSamw 		return (error);
872da6c28aaSamw 
873da6c28aaSamw 	pdvp = gfs_file_parent(tdvp);
874da6c28aaSamw 	error = VOP_SETATTR(pdvp, &xvattr.xva_vattr, 0, cr, ct);
875da6c28aaSamw 	return (error);
876da6c28aaSamw }
877da6c28aaSamw 
878da6c28aaSamw static int
879da6c28aaSamw xattr_dir_realdir(vnode_t *dvp, vnode_t **realdvp, int lookup_flags,
880da6c28aaSamw     cred_t *cr, caller_context_t *ct)
881da6c28aaSamw {
882da6c28aaSamw 	vnode_t *pvp;
883da6c28aaSamw 	int error;
884da6c28aaSamw 	struct pathname pn;
885da6c28aaSamw 	char *startnm = "";
886da6c28aaSamw 
887da6c28aaSamw 	*realdvp = NULL;
888da6c28aaSamw 
889da6c28aaSamw 	pvp = gfs_file_parent(dvp);
890da6c28aaSamw 
891da6c28aaSamw 	error = pn_get(startnm, UIO_SYSSPACE, &pn);
892da6c28aaSamw 	if (error) {
893da6c28aaSamw 		VN_RELE(pvp);
894da6c28aaSamw 		return (error);
895da6c28aaSamw 	}
896da6c28aaSamw 
897da6c28aaSamw 	/*
898da6c28aaSamw 	 * Set the LOOKUP_HAVE_SYSATTR_DIR flag so that we don't get into an
899da6c28aaSamw 	 * infinite loop with fop_lookup calling back to xattr_dir_lookup.
900da6c28aaSamw 	 */
901da6c28aaSamw 	lookup_flags |= LOOKUP_HAVE_SYSATTR_DIR;
902da6c28aaSamw 	error = VOP_LOOKUP(pvp, startnm, realdvp, &pn, lookup_flags,
903da6c28aaSamw 	    rootvp, cr, ct, NULL, NULL);
904da6c28aaSamw 	pn_free(&pn);
905da6c28aaSamw 
906da6c28aaSamw 	return (error);
907da6c28aaSamw }
908da6c28aaSamw 
909da6c28aaSamw /* ARGSUSED */
910da6c28aaSamw static int
911da6c28aaSamw xattr_dir_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
912da6c28aaSamw {
913da6c28aaSamw 	if (flags & FWRITE) {
914da6c28aaSamw 		return (EACCES);
915da6c28aaSamw 	}
916da6c28aaSamw 
917da6c28aaSamw 	return (0);
918da6c28aaSamw }
919da6c28aaSamw 
920da6c28aaSamw /* ARGSUSED */
921da6c28aaSamw static int
922da6c28aaSamw xattr_dir_close(vnode_t *vpp, int flags, int count, offset_t off, cred_t *cr,
923da6c28aaSamw     caller_context_t *ct)
924da6c28aaSamw {
925da6c28aaSamw 	return (0);
926da6c28aaSamw }
927da6c28aaSamw 
92800ba712dSGarima Tripathi /*
92900ba712dSGarima Tripathi  * Retrieve the attributes on an xattr directory.  If there is a "real"
93000ba712dSGarima Tripathi  * xattr directory, use that.  Otherwise, get the attributes (represented
93100ba712dSGarima Tripathi  * by PARENT_ATTRMASK) from the "parent" node and fill in the rest.  Note
93200ba712dSGarima Tripathi  * that VOP_GETATTR() could turn off bits in the va_mask.
93300ba712dSGarima Tripathi  */
93400ba712dSGarima Tripathi 
93500ba712dSGarima Tripathi #define	PARENT_ATTRMASK	(AT_UID|AT_GID|AT_RDEV|AT_CTIME|AT_MTIME)
93600ba712dSGarima Tripathi 
937da6c28aaSamw /* ARGSUSED */
938da6c28aaSamw static int
939da6c28aaSamw xattr_dir_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
940da6c28aaSamw     caller_context_t *ct)
941da6c28aaSamw {
942da6c28aaSamw 	timestruc_t now;
943da6c28aaSamw 	vnode_t *pvp;
944da6c28aaSamw 	int error;
945da6c28aaSamw 
946da6c28aaSamw 	error = xattr_dir_realdir(vp, &pvp, LOOKUP_XATTR, cr, ct);
947da6c28aaSamw 	if (error == 0) {
948da6c28aaSamw 		error = VOP_GETATTR(pvp, vap, 0, cr, ct);
949da6c28aaSamw 		VN_RELE(pvp);
950da6c28aaSamw 		if (error) {
951da6c28aaSamw 			return (error);
952da6c28aaSamw 		}
953da6c28aaSamw 		vap->va_nlink += XATTRDIR_NENTS;
954da6c28aaSamw 		vap->va_size += XATTRDIR_NENTS;
955da6c28aaSamw 		return (0);
956da6c28aaSamw 	}
957da6c28aaSamw 
958da6c28aaSamw 	/*
959da6c28aaSamw 	 * There is no real xattr directory.  Cobble together
96000ba712dSGarima Tripathi 	 * an entry using info from the parent object (if needed)
96100ba712dSGarima Tripathi 	 * plus information common to all xattrs.
962da6c28aaSamw 	 */
96300ba712dSGarima Tripathi 	if (vap->va_mask & PARENT_ATTRMASK) {
96400ba712dSGarima Tripathi 		vattr_t pvattr;
96500ba712dSGarima Tripathi 		uint_t  off_bits;
96600ba712dSGarima Tripathi 
967da6c28aaSamw 		pvp = gfs_file_parent(vp);
968da6c28aaSamw 		(void) memset(&pvattr, 0, sizeof (pvattr));
96900ba712dSGarima Tripathi 		pvattr.va_mask = PARENT_ATTRMASK;
970da6c28aaSamw 		error = VOP_GETATTR(pvp, &pvattr, 0, cr, ct);
971da6c28aaSamw 		if (error) {
972da6c28aaSamw 			return (error);
973da6c28aaSamw 		}
97400ba712dSGarima Tripathi 
97500ba712dSGarima Tripathi 		/*
97600ba712dSGarima Tripathi 		 * VOP_GETATTR() might have turned off some bits in
97700ba712dSGarima Tripathi 		 * pvattr.va_mask.  This means that the underlying
97800ba712dSGarima Tripathi 		 * file system couldn't process those attributes.
97900ba712dSGarima Tripathi 		 * We need to make sure those bits get turned off
98000ba712dSGarima Tripathi 		 * in the vattr_t structure that gets passed back
98100ba712dSGarima Tripathi 		 * to the caller.  Figure out which bits were turned
98200ba712dSGarima Tripathi 		 * off (if any) then set pvattr.va_mask before it
98300ba712dSGarima Tripathi 		 * gets copied to the vattr_t that the caller sees.
98400ba712dSGarima Tripathi 		 */
98500ba712dSGarima Tripathi 		off_bits = (pvattr.va_mask ^ PARENT_ATTRMASK) & PARENT_ATTRMASK;
98600ba712dSGarima Tripathi 		pvattr.va_mask = vap->va_mask & ~off_bits;
987da6c28aaSamw 		*vap = pvattr;
98800ba712dSGarima Tripathi 	}
98900ba712dSGarima Tripathi 
990da6c28aaSamw 	vap->va_type = VDIR;
991da6c28aaSamw 	vap->va_mode = MAKEIMODE(vap->va_type, S_ISVTX | 0777);
992da6c28aaSamw 	vap->va_fsid = vp->v_vfsp->vfs_dev;
993da6c28aaSamw 	vap->va_nodeid = gfs_file_inode(vp);
994da6c28aaSamw 	vap->va_nlink = XATTRDIR_NENTS+2;
995da6c28aaSamw 	vap->va_size = vap->va_nlink;
996da6c28aaSamw 	gethrestime(&now);
997da6c28aaSamw 	vap->va_atime = now;
998da6c28aaSamw 	vap->va_blksize = 0;
999da6c28aaSamw 	vap->va_nblocks = 0;
1000da6c28aaSamw 	vap->va_seq = 0;
1001da6c28aaSamw 	return (0);
1002da6c28aaSamw }
1003da6c28aaSamw 
1004da6c28aaSamw static int
1005da6c28aaSamw xattr_dir_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
1006da6c28aaSamw     caller_context_t *ct)
1007da6c28aaSamw {
1008da6c28aaSamw 	vnode_t *realvp;
1009da6c28aaSamw 	int error;
1010da6c28aaSamw 
1011da6c28aaSamw 	/*
1012da6c28aaSamw 	 * If there is a real xattr directory, do the setattr there.
1013da6c28aaSamw 	 * Otherwise, just return success.  The GFS directory is transient,
1014da6c28aaSamw 	 * and any setattr changes can disappear anyway.
1015da6c28aaSamw 	 */
1016da6c28aaSamw 	error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct);
1017da6c28aaSamw 	if (error == 0) {
1018da6c28aaSamw 		error = VOP_SETATTR(realvp, vap, flags, cr, ct);
1019da6c28aaSamw 		VN_RELE(realvp);
1020da6c28aaSamw 	}
1021da6c28aaSamw 	if (error == ENOENT) {
1022da6c28aaSamw 		error = 0;
1023da6c28aaSamw 	}
1024da6c28aaSamw 	return (error);
1025da6c28aaSamw }
1026da6c28aaSamw 
1027da6c28aaSamw /* ARGSUSED */
1028da6c28aaSamw static int
1029da6c28aaSamw xattr_dir_access(vnode_t *vp, int mode, int flags, cred_t *cr,
1030da6c28aaSamw     caller_context_t *ct)
1031da6c28aaSamw {
1032da6c28aaSamw 	int error;
1033da6c28aaSamw 	vnode_t *realvp = NULL;
1034da6c28aaSamw 
1035da6c28aaSamw 	if (mode & VWRITE) {
1036da6c28aaSamw 		return (EACCES);
1037da6c28aaSamw 	}
1038da6c28aaSamw 
1039da6c28aaSamw 	error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct);
1040da6c28aaSamw 
1041da6c28aaSamw 	if (realvp)
1042da6c28aaSamw 		VN_RELE(realvp);
1043da6c28aaSamw 
1044da6c28aaSamw 	/*
1045da6c28aaSamw 	 * No real xattr dir isn't an error
1046da6c28aaSamw 	 * an error of EINVAL indicates attributes on attributes
1047da6c28aaSamw 	 * are not supported.  In that case just allow access to the
1048da6c28aaSamw 	 * transient directory.
1049da6c28aaSamw 	 */
1050da6c28aaSamw 	return ((error == ENOENT || error == EINVAL) ? 0 : error);
1051da6c28aaSamw }
1052da6c28aaSamw 
1053da6c28aaSamw static int
1054da6c28aaSamw xattr_dir_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl,
1055da6c28aaSamw     int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct,
1056da6c28aaSamw     vsecattr_t *vsecp)
1057da6c28aaSamw {
1058da6c28aaSamw 	vnode_t *pvp;
1059da6c28aaSamw 	int error;
1060da6c28aaSamw 
1061da6c28aaSamw 	*vpp = NULL;
1062da6c28aaSamw 
1063da6c28aaSamw 	/*
1064da6c28aaSamw 	 * Don't allow creation of extended attributes with sysattr names.
1065da6c28aaSamw 	 */
1066da6c28aaSamw 	if (is_sattr_name(name)) {
1067ab04eb8eStimh 		return (gfs_dir_lookup(dvp, name, vpp, cr, 0, NULL, NULL));
1068da6c28aaSamw 	}
1069da6c28aaSamw 
1070da6c28aaSamw 	error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR|CREATE_XATTR_DIR,
1071da6c28aaSamw 	    cr, ct);
1072da6c28aaSamw 	if (error == 0) {
1073da6c28aaSamw 		error = VOP_CREATE(pvp, name, vap, excl, mode, vpp, cr, flag,
1074da6c28aaSamw 		    ct, vsecp);
1075da6c28aaSamw 		VN_RELE(pvp);
1076da6c28aaSamw 	}
1077da6c28aaSamw 	return (error);
1078da6c28aaSamw }
1079da6c28aaSamw 
1080da6c28aaSamw static int
1081da6c28aaSamw xattr_dir_remove(vnode_t *dvp, char *name, cred_t *cr, caller_context_t *ct,
1082da6c28aaSamw     int flags)
1083da6c28aaSamw {
1084da6c28aaSamw 	vnode_t *pvp;
1085da6c28aaSamw 	int error;
1086da6c28aaSamw 
1087da6c28aaSamw 	if (is_sattr_name(name)) {
1088da6c28aaSamw 		return (EACCES);
1089da6c28aaSamw 	}
1090da6c28aaSamw 
1091da6c28aaSamw 	error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR, cr, ct);
1092da6c28aaSamw 	if (error == 0) {
1093da6c28aaSamw 		error = VOP_REMOVE(pvp, name, cr, ct, flags);
1094da6c28aaSamw 		VN_RELE(pvp);
1095da6c28aaSamw 	}
1096da6c28aaSamw 	return (error);
1097da6c28aaSamw }
1098da6c28aaSamw 
1099da6c28aaSamw static int
1100da6c28aaSamw xattr_dir_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr,
1101da6c28aaSamw     caller_context_t *ct, int flags)
1102da6c28aaSamw {
1103da6c28aaSamw 	vnode_t *pvp;
1104da6c28aaSamw 	int error;
1105da6c28aaSamw 
1106da6c28aaSamw 	if (svp->v_flag & V_SYSATTR) {
1107da6c28aaSamw 		return (EINVAL);
1108da6c28aaSamw 	}
1109da6c28aaSamw 
1110da6c28aaSamw 	error = xattr_dir_realdir(tdvp, &pvp, LOOKUP_XATTR, cr, ct);
1111da6c28aaSamw 	if (error == 0) {
1112da6c28aaSamw 		error = VOP_LINK(pvp, svp, name, cr, ct, flags);
1113da6c28aaSamw 		VN_RELE(pvp);
1114da6c28aaSamw 	}
1115da6c28aaSamw 	return (error);
1116da6c28aaSamw }
1117da6c28aaSamw 
1118da6c28aaSamw static int
1119da6c28aaSamw xattr_dir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
1120da6c28aaSamw     cred_t *cr, caller_context_t *ct, int flags)
1121da6c28aaSamw {
1122da6c28aaSamw 	vnode_t *spvp, *tpvp;
1123da6c28aaSamw 	int error;
1124da6c28aaSamw 	int held_tgt;
1125da6c28aaSamw 
1126da6c28aaSamw 	if (is_sattr_name(snm) || is_sattr_name(tnm))
1127da6c28aaSamw 		return (xattr_copy(sdvp, snm, tdvp, tnm, cr, ct));
1128da6c28aaSamw 	/*
1129da6c28aaSamw 	 * We know that sdvp is a GFS dir, or we wouldn't be here.
1130da6c28aaSamw 	 * Get the real unnamed directory.
1131da6c28aaSamw 	 */
1132da6c28aaSamw 	error = xattr_dir_realdir(sdvp, &spvp, LOOKUP_XATTR, cr, ct);
1133da6c28aaSamw 	if (error) {
1134da6c28aaSamw 		return (error);
1135da6c28aaSamw 	}
1136da6c28aaSamw 
1137da6c28aaSamw 	if (sdvp == tdvp) {
1138da6c28aaSamw 		/*
1139da6c28aaSamw 		 * If the source and target are the same GFS directory, the
1140da6c28aaSamw 		 * underlying unnamed source and target dir will be the same.
1141da6c28aaSamw 		 */
1142da6c28aaSamw 		tpvp = spvp;
1143da6c28aaSamw 		VN_HOLD(tpvp);
1144da6c28aaSamw 		held_tgt = 1;
1145da6c28aaSamw 	} else if (tdvp->v_flag & V_SYSATTR) {
1146da6c28aaSamw 		/*
1147da6c28aaSamw 		 * If the target dir is a different GFS directory,
1148da6c28aaSamw 		 * find its underlying unnamed dir.
1149da6c28aaSamw 		 */
1150da6c28aaSamw 		error = xattr_dir_realdir(tdvp, &tpvp, LOOKUP_XATTR, cr, ct);
1151da6c28aaSamw 		if (error) {
1152da6c28aaSamw 			VN_RELE(spvp);
1153da6c28aaSamw 			return (error);
1154da6c28aaSamw 		}
1155da6c28aaSamw 		held_tgt = 1;
1156da6c28aaSamw 	} else {
1157da6c28aaSamw 		/*
1158da6c28aaSamw 		 * Target dir is outside of GFS, pass it on through.
1159da6c28aaSamw 		 */
1160da6c28aaSamw 		tpvp = tdvp;
1161da6c28aaSamw 		held_tgt = 0;
1162da6c28aaSamw 	}
1163da6c28aaSamw 
1164da6c28aaSamw 	error = VOP_RENAME(spvp, snm, tpvp, tnm, cr, ct, flags);
1165da6c28aaSamw 
1166da6c28aaSamw 	if (held_tgt) {
1167da6c28aaSamw 		VN_RELE(tpvp);
1168da6c28aaSamw 	}
1169da6c28aaSamw 	VN_RELE(spvp);
1170da6c28aaSamw 
1171da6c28aaSamw 	return (error);
1172da6c28aaSamw }
1173da6c28aaSamw 
1174b38f0970Sck153898 /*
1175b38f0970Sck153898  * readdir_xattr_casecmp: given a system attribute name, see if there
1176b38f0970Sck153898  * is a real xattr with the same normalized name.
1177b38f0970Sck153898  */
1178b38f0970Sck153898 static int
1179b38f0970Sck153898 readdir_xattr_casecmp(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
1180b38f0970Sck153898     int *eflags)
1181b38f0970Sck153898 {
1182b38f0970Sck153898 	int error;
1183b38f0970Sck153898 	vnode_t *vp;
1184b38f0970Sck153898 	struct pathname pn;
1185b38f0970Sck153898 
1186b38f0970Sck153898 	*eflags = 0;
1187b38f0970Sck153898 
1188b38f0970Sck153898 	error = pn_get(nm, UIO_SYSSPACE, &pn);
1189b38f0970Sck153898 	if (error == 0) {
1190ab04eb8eStimh 		error = VOP_LOOKUP(dvp, nm, &vp, &pn,
1191ab04eb8eStimh 		    FIGNORECASE, rootvp, cr, ct, NULL, NULL);
1192b38f0970Sck153898 		if (error == 0) {
1193b38f0970Sck153898 			*eflags = ED_CASE_CONFLICT;
1194b38f0970Sck153898 			VN_RELE(vp);
1195b38f0970Sck153898 		} else if (error == ENOENT) {
1196b38f0970Sck153898 			error = 0;
1197b38f0970Sck153898 		}
1198b38f0970Sck153898 		pn_free(&pn);
1199b38f0970Sck153898 	}
1200b38f0970Sck153898 
1201b38f0970Sck153898 	return (error);
1202b38f0970Sck153898 }
1203b38f0970Sck153898 
1204da6c28aaSamw static int
1205da6c28aaSamw xattr_dir_readdir(vnode_t *dvp, uio_t *uiop, cred_t *cr, int *eofp,
1206da6c28aaSamw     caller_context_t *ct, int flags)
1207da6c28aaSamw {
1208da6c28aaSamw 	vnode_t *pvp;
1209da6c28aaSamw 	int error;
1210f5f5959bSck153898 	int local_eof;
1211da6c28aaSamw 	int reset_off = 0;
1212da6c28aaSamw 	int has_xattrs = 0;
1213da6c28aaSamw 
1214da6c28aaSamw 	if (eofp == NULL) {
1215da6c28aaSamw 		eofp = &local_eof;
1216da6c28aaSamw 	}
1217f5f5959bSck153898 	*eofp = 0;
1218da6c28aaSamw 
1219da6c28aaSamw 	/*
1220da6c28aaSamw 	 * See if there is a real extended attribute directory.
1221da6c28aaSamw 	 */
1222da6c28aaSamw 	error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR, cr, ct);
1223da6c28aaSamw 	if (error == 0) {
1224da6c28aaSamw 		has_xattrs = 1;
1225da6c28aaSamw 	}
1226da6c28aaSamw 
1227da6c28aaSamw 	/*
1228da6c28aaSamw 	 * Start by reading up the static entries.
1229da6c28aaSamw 	 */
1230da6c28aaSamw 	if (uiop->uio_loffset == 0) {
1231b38f0970Sck153898 		ino64_t pino, ino;
1232b38f0970Sck153898 		offset_t off;
1233b38f0970Sck153898 		gfs_dir_t *dp = dvp->v_data;
1234b38f0970Sck153898 		gfs_readdir_state_t gstate;
1235b38f0970Sck153898 
1236da6c28aaSamw 		if (has_xattrs) {
1237da6c28aaSamw 			/*
1238da6c28aaSamw 			 * If there is a real xattr dir, skip . and ..
1239da6c28aaSamw 			 * in the GFS dir.  We'll pick them up below
1240da6c28aaSamw 			 * when we call into the underlying fs.
1241da6c28aaSamw 			 */
1242da6c28aaSamw 			uiop->uio_loffset = GFS_STATIC_ENTRY_OFFSET;
1243da6c28aaSamw 		}
1244b38f0970Sck153898 		error = gfs_get_parent_ino(dvp, cr, ct, &pino, &ino);
1245b38f0970Sck153898 		if (error == 0) {
1246b38f0970Sck153898 			error = gfs_readdir_init(&gstate, dp->gfsd_maxlen, 1,
1247b38f0970Sck153898 			    uiop, pino, ino, flags);
1248da6c28aaSamw 		}
1249b38f0970Sck153898 		if (error) {
1250b38f0970Sck153898 			if (has_xattrs)
1251b38f0970Sck153898 				VN_RELE(pvp);
1252da6c28aaSamw 			return (error);
1253da6c28aaSamw 		}
1254b38f0970Sck153898 
1255b38f0970Sck153898 		while ((error = gfs_readdir_pred(&gstate, uiop, &off)) == 0 &&
1256b38f0970Sck153898 		    !*eofp) {
1257b38f0970Sck153898 			if (off >= 0 && off < dp->gfsd_nstatic) {
1258ab04eb8eStimh 				int eflags;
1259b38f0970Sck153898 
1260b38f0970Sck153898 				/*
1261b38f0970Sck153898 				 * Check to see if this sysattr set name has a
1262b38f0970Sck153898 				 * case-insensitive conflict with a real xattr
1263b38f0970Sck153898 				 * name.
1264b38f0970Sck153898 				 */
1265ab04eb8eStimh 				eflags = 0;
1266b38f0970Sck153898 				if ((flags & V_RDDIR_ENTFLAGS) && has_xattrs) {
1267b38f0970Sck153898 					error = readdir_xattr_casecmp(pvp,
1268b38f0970Sck153898 					    dp->gfsd_static[off].gfse_name,
1269b38f0970Sck153898 					    cr, ct, &eflags);
1270b38f0970Sck153898 					if (error)
1271b38f0970Sck153898 						break;
1272b38f0970Sck153898 				}
1273b38f0970Sck153898 				ino = dp->gfsd_inode(dvp, off);
1274b38f0970Sck153898 
1275b38f0970Sck153898 				error = gfs_readdir_emit(&gstate, uiop, off,
1276b38f0970Sck153898 				    ino, dp->gfsd_static[off].gfse_name,
1277b38f0970Sck153898 				    eflags);
1278b38f0970Sck153898 				if (error)
1279b38f0970Sck153898 					break;
1280b38f0970Sck153898 			} else {
1281b38f0970Sck153898 				*eofp = 1;
1282b38f0970Sck153898 			}
1283b38f0970Sck153898 		}
1284b38f0970Sck153898 
1285b38f0970Sck153898 		error = gfs_readdir_fini(&gstate, error, eofp, *eofp);
1286b38f0970Sck153898 		if (error) {
1287b38f0970Sck153898 			if (has_xattrs)
1288b38f0970Sck153898 				VN_RELE(pvp);
1289b38f0970Sck153898 			return (error);
1290b38f0970Sck153898 		}
1291b38f0970Sck153898 
1292da6c28aaSamw 		/*
1293da6c28aaSamw 		 * We must read all of the static entries in the first
1294da6c28aaSamw 		 * call.  Otherwise we won't know if uio_loffset in a
1295da6c28aaSamw 		 * subsequent call refers to the static entries or to those
1296da6c28aaSamw 		 * in an underlying fs.
1297da6c28aaSamw 		 */
1298e802abbdSTim Haley 		if (*eofp == 0)
1299e802abbdSTim Haley 			return (EINVAL);
1300da6c28aaSamw 		reset_off = 1;
1301da6c28aaSamw 	}
1302da6c28aaSamw 
1303da6c28aaSamw 	if (!has_xattrs) {
1304da6c28aaSamw 		*eofp = 1;
1305da6c28aaSamw 		return (0);
1306da6c28aaSamw 	}
1307da6c28aaSamw 
1308da6c28aaSamw 	*eofp = 0;
1309da6c28aaSamw 	if (reset_off) {
1310da6c28aaSamw 		uiop->uio_loffset = 0;
1311da6c28aaSamw 	}
1312da6c28aaSamw 	(void) VOP_RWLOCK(pvp, V_WRITELOCK_FALSE, NULL);
1313da6c28aaSamw 	error = VOP_READDIR(pvp, uiop, cr, eofp, ct, flags);
1314da6c28aaSamw 	VOP_RWUNLOCK(pvp, V_WRITELOCK_FALSE, NULL);
1315da6c28aaSamw 	VN_RELE(pvp);
1316da6c28aaSamw 
1317da6c28aaSamw 	return (error);
1318da6c28aaSamw }
1319da6c28aaSamw 
1320da6c28aaSamw /* ARGSUSED */
1321da6c28aaSamw static void
1322da6c28aaSamw xattr_dir_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
1323da6c28aaSamw {
1324da6c28aaSamw 	gfs_file_t *fp;
132568469adeSMark Shellenbaum 	xattr_dir_t *xattr_dir;
1326da6c28aaSamw 
132768469adeSMark Shellenbaum 	mutex_enter(&vp->v_lock);
132868469adeSMark Shellenbaum 	xattr_dir = vp->v_data;
132968469adeSMark Shellenbaum 	if (xattr_dir->xattr_realvp) {
133068469adeSMark Shellenbaum 		VN_RELE(xattr_dir->xattr_realvp);
133168469adeSMark Shellenbaum 		xattr_dir->xattr_realvp = NULL;
133268469adeSMark Shellenbaum 	}
133368469adeSMark Shellenbaum 	mutex_exit(&vp->v_lock);
1334da6c28aaSamw 	fp = gfs_dir_inactive(vp);
1335da6c28aaSamw 	if (fp != NULL) {
1336da6c28aaSamw 		kmem_free(fp, fp->gfs_size);
1337da6c28aaSamw 	}
1338da6c28aaSamw }
1339da6c28aaSamw 
1340da6c28aaSamw static int
1341da6c28aaSamw xattr_dir_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
1342da6c28aaSamw     caller_context_t *ct)
1343da6c28aaSamw {
1344da6c28aaSamw 	switch (cmd) {
1345da6c28aaSamw 	case _PC_XATTR_EXISTS:
1346da6c28aaSamw 	case _PC_SATTR_ENABLED:
1347da6c28aaSamw 	case _PC_SATTR_EXISTS:
1348da6c28aaSamw 		*valp = 0;
1349da6c28aaSamw 		return (0);
1350da6c28aaSamw 	default:
1351da6c28aaSamw 		return (fs_pathconf(vp, cmd, valp, cr, ct));
1352da6c28aaSamw 	}
1353da6c28aaSamw }
1354da6c28aaSamw 
135568469adeSMark Shellenbaum /* ARGSUSED */
135668469adeSMark Shellenbaum static int
135768469adeSMark Shellenbaum xattr_dir_realvp(vnode_t *vp, vnode_t **realvp, caller_context_t *ct)
135868469adeSMark Shellenbaum {
135968469adeSMark Shellenbaum 	xattr_dir_t *xattr_dir;
136068469adeSMark Shellenbaum 
136168469adeSMark Shellenbaum 	mutex_enter(&vp->v_lock);
136268469adeSMark Shellenbaum 	xattr_dir = vp->v_data;
136368469adeSMark Shellenbaum 	if (xattr_dir->xattr_realvp) {
136468469adeSMark Shellenbaum 		*realvp = xattr_dir->xattr_realvp;
136568469adeSMark Shellenbaum 		mutex_exit(&vp->v_lock);
13663fece860SMark Shellenbaum 		return (0);
13673fece860SMark Shellenbaum 	} else {
13683fece860SMark Shellenbaum 		vnode_t *xdvp;
13693fece860SMark Shellenbaum 		int error;
13703fece860SMark Shellenbaum 
13713fece860SMark Shellenbaum 		mutex_exit(&vp->v_lock);
13723fece860SMark Shellenbaum 		if ((error = xattr_dir_realdir(vp, &xdvp,
13733fece860SMark Shellenbaum 		    LOOKUP_XATTR, kcred, NULL)) == 0) {
13743fece860SMark Shellenbaum 			/*
13753fece860SMark Shellenbaum 			 * verify we aren't racing with another thread
13763fece860SMark Shellenbaum 			 * to find the xattr_realvp
13773fece860SMark Shellenbaum 			 */
13783fece860SMark Shellenbaum 			mutex_enter(&vp->v_lock);
13793fece860SMark Shellenbaum 			if (xattr_dir->xattr_realvp == NULL) {
13803fece860SMark Shellenbaum 				xattr_dir->xattr_realvp = xdvp;
13813fece860SMark Shellenbaum 				*realvp = xdvp;
13823fece860SMark Shellenbaum 				mutex_exit(&vp->v_lock);
13833fece860SMark Shellenbaum 			} else {
13843fece860SMark Shellenbaum 				*realvp = xattr_dir->xattr_realvp;
13853fece860SMark Shellenbaum 				mutex_exit(&vp->v_lock);
13863fece860SMark Shellenbaum 				VN_RELE(xdvp);
13873fece860SMark Shellenbaum 			}
13883fece860SMark Shellenbaum 		}
138968469adeSMark Shellenbaum 		return (error);
139068469adeSMark Shellenbaum 	}
13913fece860SMark Shellenbaum }
139268469adeSMark Shellenbaum 
1393da6c28aaSamw static const fs_operation_def_t xattr_dir_tops[] = {
1394da6c28aaSamw 	{ VOPNAME_OPEN,		{ .vop_open = xattr_dir_open }		},
1395da6c28aaSamw 	{ VOPNAME_CLOSE,	{ .vop_close = xattr_dir_close }	},
1396da6c28aaSamw 	{ VOPNAME_IOCTL,	{ .error = fs_inval }			},
1397da6c28aaSamw 	{ VOPNAME_GETATTR,	{ .vop_getattr = xattr_dir_getattr }	},
1398da6c28aaSamw 	{ VOPNAME_SETATTR,	{ .vop_setattr = xattr_dir_setattr }	},
1399da6c28aaSamw 	{ VOPNAME_ACCESS,	{ .vop_access = xattr_dir_access }	},
1400da6c28aaSamw 	{ VOPNAME_READDIR,	{ .vop_readdir = xattr_dir_readdir }	},
1401da6c28aaSamw 	{ VOPNAME_LOOKUP,	{ .vop_lookup = gfs_vop_lookup }	},
1402da6c28aaSamw 	{ VOPNAME_CREATE,	{ .vop_create = xattr_dir_create }	},
1403da6c28aaSamw 	{ VOPNAME_REMOVE,	{ .vop_remove = xattr_dir_remove }	},
1404da6c28aaSamw 	{ VOPNAME_LINK,		{ .vop_link = xattr_dir_link }		},
1405da6c28aaSamw 	{ VOPNAME_RENAME,	{ .vop_rename = xattr_dir_rename }	},
1406da6c28aaSamw 	{ VOPNAME_MKDIR,	{ .error = fs_inval }			},
1407da6c28aaSamw 	{ VOPNAME_SEEK,		{ .vop_seek = fs_seek }			},
1408da6c28aaSamw 	{ VOPNAME_INACTIVE,	{ .vop_inactive = xattr_dir_inactive }	},
1409da6c28aaSamw 	{ VOPNAME_FID,		{ .vop_fid = xattr_common_fid }		},
1410da6c28aaSamw 	{ VOPNAME_PATHCONF,	{ .vop_pathconf = xattr_dir_pathconf }	},
141168469adeSMark Shellenbaum 	{ VOPNAME_REALVP,	{ .vop_realvp = xattr_dir_realvp } },
1412da6c28aaSamw 	{ NULL, NULL }
1413da6c28aaSamw };
1414da6c28aaSamw 
1415da6c28aaSamw static gfs_opsvec_t xattr_opsvec[] = {
1416da6c28aaSamw 	{ "xattr dir", xattr_dir_tops, &xattr_dir_ops },
1417da6c28aaSamw 	{ "system attributes", xattr_file_tops, &xattr_file_ops },
1418da6c28aaSamw 	{ NULL, NULL, NULL }
1419da6c28aaSamw };
1420da6c28aaSamw 
1421da6c28aaSamw static int
1422da6c28aaSamw xattr_lookup_cb(vnode_t *vp, const char *nm, vnode_t **vpp, ino64_t *inop,
1423ab04eb8eStimh     cred_t *cr, int flags, int *deflags, pathname_t *rpnp)
1424da6c28aaSamw {
1425da6c28aaSamw 	vnode_t *pvp;
1426da6c28aaSamw 	struct pathname pn;
1427da6c28aaSamw 	int error;
1428da6c28aaSamw 
1429da6c28aaSamw 	*vpp = NULL;
1430da6c28aaSamw 	*inop = 0;
1431da6c28aaSamw 
1432da6c28aaSamw 	error = xattr_dir_realdir(vp, &pvp, LOOKUP_XATTR|CREATE_XATTR_DIR,
1433da6c28aaSamw 	    cr, NULL);
1434da6c28aaSamw 
1435da6c28aaSamw 	/*
1436da6c28aaSamw 	 * Return ENOENT for EACCES requests during lookup.  Once an
1437da6c28aaSamw 	 * attribute create is attempted EACCES will be returned.
1438da6c28aaSamw 	 */
1439da6c28aaSamw 	if (error) {
1440da6c28aaSamw 		if (error == EACCES)
1441da6c28aaSamw 			return (ENOENT);
1442da6c28aaSamw 		return (error);
1443da6c28aaSamw 	}
1444da6c28aaSamw 
1445da6c28aaSamw 	error = pn_get((char *)nm, UIO_SYSSPACE, &pn);
1446da6c28aaSamw 	if (error == 0) {
1447ab04eb8eStimh 		error = VOP_LOOKUP(pvp, (char *)nm, vpp, &pn, flags, rootvp,
1448ab04eb8eStimh 		    cr, NULL, deflags, rpnp);
1449da6c28aaSamw 		pn_free(&pn);
1450da6c28aaSamw 	}
1451da6c28aaSamw 	VN_RELE(pvp);
1452da6c28aaSamw 
1453da6c28aaSamw 	return (error);
1454da6c28aaSamw }
1455da6c28aaSamw 
1456da6c28aaSamw /* ARGSUSED */
1457da6c28aaSamw static ino64_t
1458da6c28aaSamw xattrdir_do_ino(vnode_t *vp, int index)
1459da6c28aaSamw {
1460da6c28aaSamw 	/*
1461da6c28aaSamw 	 * We use index 0 for the directory fid.  Start
1462da6c28aaSamw 	 * the file numbering at 1.
1463da6c28aaSamw 	 */
1464da6c28aaSamw 	return ((ino64_t)index+1);
1465da6c28aaSamw }
1466da6c28aaSamw 
1467da6c28aaSamw void
1468da6c28aaSamw xattr_init(void)
1469da6c28aaSamw {
1470da6c28aaSamw 	VERIFY(gfs_make_opsvec(xattr_opsvec) == 0);
1471da6c28aaSamw }
1472da6c28aaSamw 
1473da6c28aaSamw int
1474da6c28aaSamw xattr_dir_lookup(vnode_t *dvp, vnode_t **vpp, int flags, cred_t *cr)
1475da6c28aaSamw {
1476da6c28aaSamw 	int error = 0;
1477da6c28aaSamw 
1478da6c28aaSamw 	*vpp = NULL;
1479da6c28aaSamw 
1480da6c28aaSamw 	if (dvp->v_type != VDIR && dvp->v_type != VREG)
1481da6c28aaSamw 		return (EINVAL);
1482da6c28aaSamw 
1483da6c28aaSamw 	mutex_enter(&dvp->v_lock);
1484da6c28aaSamw 
1485da6c28aaSamw 	/*
1486da6c28aaSamw 	 * If we're already in sysattr space, don't allow creation
1487da6c28aaSamw 	 * of another level of sysattrs.
1488da6c28aaSamw 	 */
1489da6c28aaSamw 	if (dvp->v_flag & V_SYSATTR) {
1490da6c28aaSamw 		mutex_exit(&dvp->v_lock);
1491da6c28aaSamw 		return (EINVAL);
1492da6c28aaSamw 	}
1493da6c28aaSamw 
1494da6c28aaSamw 	if (dvp->v_xattrdir != NULL) {
1495da6c28aaSamw 		*vpp = dvp->v_xattrdir;
1496da6c28aaSamw 		VN_HOLD(*vpp);
1497da6c28aaSamw 	} else {
1498da6c28aaSamw 		ulong_t val;
1499da6c28aaSamw 		int xattrs_allowed = dvp->v_vfsp->vfs_flag & VFS_XATTR;
1500da6c28aaSamw 		int sysattrs_allowed = 1;
1501da6c28aaSamw 
1502da6c28aaSamw 		/*
1503da6c28aaSamw 		 * We have to drop the lock on dvp.  gfs_dir_create will
1504da6c28aaSamw 		 * grab it for a VN_HOLD.
1505da6c28aaSamw 		 */
1506da6c28aaSamw 		mutex_exit(&dvp->v_lock);
1507da6c28aaSamw 
1508da6c28aaSamw 		/*
1509da6c28aaSamw 		 * If dvp allows xattr creation, but not sysattr
1510da6c28aaSamw 		 * creation, return the real xattr dir vp. We can't
1511da6c28aaSamw 		 * use the vfs feature mask here because _PC_SATTR_ENABLED
1512da6c28aaSamw 		 * has vnode-level granularity (e.g. .zfs).
1513da6c28aaSamw 		 */
1514da6c28aaSamw 		error = VOP_PATHCONF(dvp, _PC_SATTR_ENABLED, &val, cr, NULL);
1515da6c28aaSamw 		if (error != 0 || val == 0)
1516da6c28aaSamw 			sysattrs_allowed = 0;
1517da6c28aaSamw 
1518da6c28aaSamw 		if (!xattrs_allowed && !sysattrs_allowed)
1519da6c28aaSamw 			return (EINVAL);
1520da6c28aaSamw 
1521da6c28aaSamw 		if (!sysattrs_allowed) {
1522da6c28aaSamw 			struct pathname pn;
1523da6c28aaSamw 			char *nm = "";
1524da6c28aaSamw 
1525da6c28aaSamw 			error = pn_get(nm, UIO_SYSSPACE, &pn);
1526da6c28aaSamw 			if (error)
1527da6c28aaSamw 				return (error);
1528da6c28aaSamw 			error = VOP_LOOKUP(dvp, nm, vpp, &pn,
1529da6c28aaSamw 			    flags|LOOKUP_HAVE_SYSATTR_DIR, rootvp, cr, NULL,
1530da6c28aaSamw 			    NULL, NULL);
1531da6c28aaSamw 			pn_free(&pn);
1532da6c28aaSamw 			return (error);
1533da6c28aaSamw 		}
1534da6c28aaSamw 
1535da6c28aaSamw 		/*
1536da6c28aaSamw 		 * Note that we act as if we were given CREATE_XATTR_DIR,
1537da6c28aaSamw 		 * but only for creation of the GFS directory.
1538da6c28aaSamw 		 */
1539da6c28aaSamw 		*vpp = gfs_dir_create(
154068469adeSMark Shellenbaum 		    sizeof (xattr_dir_t), dvp, xattr_dir_ops, xattr_dirents,
1541da6c28aaSamw 		    xattrdir_do_ino, MAXNAMELEN, NULL, xattr_lookup_cb);
1542da6c28aaSamw 		mutex_enter(&dvp->v_lock);
1543da6c28aaSamw 		if (dvp->v_xattrdir != NULL) {
1544da6c28aaSamw 			/*
1545da6c28aaSamw 			 * We lost the race to create the xattr dir.
1546da6c28aaSamw 			 * Destroy this one, use the winner.  We can't
1547da6c28aaSamw 			 * just call VN_RELE(*vpp), because the vnode
1548da6c28aaSamw 			 * is only partially initialized.
1549da6c28aaSamw 			 */
1550da6c28aaSamw 			gfs_dir_t *dp = (*vpp)->v_data;
1551da6c28aaSamw 
1552da6c28aaSamw 			ASSERT((*vpp)->v_count == 1);
1553da6c28aaSamw 			vn_free(*vpp);
1554da6c28aaSamw 
1555da6c28aaSamw 			mutex_destroy(&dp->gfsd_lock);
1556da6c28aaSamw 			kmem_free(dp->gfsd_static,
1557da6c28aaSamw 			    dp->gfsd_nstatic * sizeof (gfs_dirent_t));
1558da6c28aaSamw 			kmem_free(dp, dp->gfsd_file.gfs_size);
1559da6c28aaSamw 
1560da6c28aaSamw 			/*
1561da6c28aaSamw 			 * There is an implied VN_HOLD(dvp) here.  We should
1562da6c28aaSamw 			 * be doing a VN_RELE(dvp) to clean up the reference
1563da6c28aaSamw 			 * from *vpp, and then a VN_HOLD(dvp) for the new
1564da6c28aaSamw 			 * reference.  Instead, we just leave the count alone.
1565da6c28aaSamw 			 */
1566da6c28aaSamw 
1567da6c28aaSamw 			*vpp = dvp->v_xattrdir;
1568da6c28aaSamw 			VN_HOLD(*vpp);
1569da6c28aaSamw 		} else {
1570da6c28aaSamw 			(*vpp)->v_flag |= (V_XATTRDIR|V_SYSATTR);
1571da6c28aaSamw 			dvp->v_xattrdir = *vpp;
1572da6c28aaSamw 		}
1573da6c28aaSamw 	}
1574da6c28aaSamw 	mutex_exit(&dvp->v_lock);
1575da6c28aaSamw 
1576da6c28aaSamw 	return (error);
1577da6c28aaSamw }
1578da6c28aaSamw 
1579da6c28aaSamw int
1580da6c28aaSamw xattr_dir_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
1581da6c28aaSamw {
1582da6c28aaSamw 	int error;
1583da6c28aaSamw 	vnode_t *pvp, *dvp;
1584da6c28aaSamw 	xattr_fid_t *xfidp;
1585da6c28aaSamw 	struct pathname pn;
1586da6c28aaSamw 	char *nm;
1587da6c28aaSamw 	uint16_t orig_len;
1588da6c28aaSamw 
1589da6c28aaSamw 	*vpp = NULL;
1590da6c28aaSamw 
1591da6c28aaSamw 	if (fidp->fid_len < XATTR_FIDSZ)
1592da6c28aaSamw 		return (EINVAL);
1593da6c28aaSamw 
1594da6c28aaSamw 	xfidp = (xattr_fid_t *)fidp;
1595da6c28aaSamw 	orig_len = fidp->fid_len;
1596da6c28aaSamw 	fidp->fid_len = xfidp->parent_len;
1597da6c28aaSamw 
1598da6c28aaSamw 	error = VFS_VGET(vfsp, &pvp, fidp);
1599da6c28aaSamw 	fidp->fid_len = orig_len;
1600da6c28aaSamw 	if (error)
1601da6c28aaSamw 		return (error);
1602da6c28aaSamw 
1603da6c28aaSamw 	/*
1604da6c28aaSamw 	 * Start by getting the GFS sysattr directory.	We might need
1605da6c28aaSamw 	 * to recreate it during the VOP_LOOKUP.
1606da6c28aaSamw 	 */
1607da6c28aaSamw 	nm = "";
1608da6c28aaSamw 	error = pn_get(nm, UIO_SYSSPACE, &pn);
1609da6c28aaSamw 	if (error) {
1610da6c28aaSamw 		VN_RELE(pvp);
1611da6c28aaSamw 		return (EINVAL);
1612da6c28aaSamw 	}
1613da6c28aaSamw 
1614da6c28aaSamw 	error = VOP_LOOKUP(pvp, nm, &dvp, &pn, LOOKUP_XATTR|CREATE_XATTR_DIR,
1615da6c28aaSamw 	    rootvp, CRED(), NULL, NULL, NULL);
1616da6c28aaSamw 	pn_free(&pn);
1617da6c28aaSamw 	VN_RELE(pvp);
1618da6c28aaSamw 	if (error)
1619da6c28aaSamw 		return (error);
1620da6c28aaSamw 
1621da6c28aaSamw 	if (xfidp->dir_offset == 0) {
1622da6c28aaSamw 		/*
1623da6c28aaSamw 		 * If we were looking for the directory, we're done.
1624da6c28aaSamw 		 */
1625da6c28aaSamw 		*vpp = dvp;
1626da6c28aaSamw 		return (0);
1627da6c28aaSamw 	}
1628da6c28aaSamw 
1629da6c28aaSamw 	if (xfidp->dir_offset > XATTRDIR_NENTS) {
1630da6c28aaSamw 		VN_RELE(dvp);
1631da6c28aaSamw 		return (EINVAL);
1632da6c28aaSamw 	}
1633da6c28aaSamw 
1634da6c28aaSamw 	nm = xattr_dirents[xfidp->dir_offset - 1].gfse_name;
1635da6c28aaSamw 
1636da6c28aaSamw 	error = pn_get(nm, UIO_SYSSPACE, &pn);
1637da6c28aaSamw 	if (error) {
1638da6c28aaSamw 		VN_RELE(dvp);
1639da6c28aaSamw 		return (EINVAL);
1640da6c28aaSamw 	}
1641da6c28aaSamw 
1642da6c28aaSamw 	error = VOP_LOOKUP(dvp, nm, vpp, &pn, 0, rootvp, CRED(), NULL,
1643da6c28aaSamw 	    NULL, NULL);
1644da6c28aaSamw 
1645da6c28aaSamw 	pn_free(&pn);
1646da6c28aaSamw 	VN_RELE(dvp);
1647da6c28aaSamw 
1648da6c28aaSamw 	return (error);
1649da6c28aaSamw }
1650