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