xref: /illumos-gate/usr/src/uts/common/fs/xattr.c (revision bbf21555)
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.
239b247029SGordon Ross  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
249b247029SGordon Ross  */
259b247029SGordon Ross 
269b247029SGordon Ross /*
279b247029SGordon Ross  * Big Theory Statement for Extended Attribute (XATTR) directories
289b247029SGordon Ross  *
299b247029SGordon Ross  * The Solaris VFS layer presents extended file attributes using a special
309b247029SGordon Ross  * "XATTR" directory under files or directories that have extended file
31*bbf21555SRichard Lowe  * attributes.  See fsattr(7) for background.
329b247029SGordon Ross  *
339b247029SGordon Ross  * This design avoids the need for a separate set of VFS or vnode functions
349b247029SGordon Ross  * for operating on XATTR objects.  File system implementations that support
359b247029SGordon Ross  * XATTR instantiate a special XATTR directory using this module.
369b247029SGordon Ross  * Applications get to the XATTR directory by passing the LOOKUP_XATTR flag
379b247029SGordon Ross  * to fop_lookup.  Once the XATTR directory is obtained, all other file
389b247029SGordon Ross  * system operations on extended attributes happen via the normal vnode
399b247029SGordon Ross  * functions, applied to the XATTR directory or its contents.
409b247029SGordon Ross  *
419b247029SGordon Ross  * The XATTR directories returned by fop_lookup (with LOOKUP_XATTR) are
429b247029SGordon Ross  * implemented differntly, depending on whether the file system supports
439b247029SGordon Ross  * "extended attributes" (XATTR), "system attributes" (SYSATTR), or both.
449b247029SGordon Ross  *
459b247029SGordon Ross  * When SYSATTR=true, XATTR=true:
469b247029SGordon Ross  *	The XATTR directory is a "generic file system" (GFS) object
479b247029SGordon Ross  *	that adds the special system attribute names (SUNWattr*) to
489b247029SGordon Ross  *	the list of XATTR files presented by the underling FS.
499b247029SGordon Ross  *	In this case, many operations are "passed through" to the
509b247029SGordon Ross  *	lower-level FS.
519b247029SGordon Ross  *
529b247029SGordon Ross  * When SYSATTR=true, XATTR=false:
539b247029SGordon Ross  *	The XATTR directory is a "generic file system" (GFS) object,
549b247029SGordon Ross  *	presenting only the system attribute names (SUNWattr*)
559b247029SGordon Ross  *	In this case there's no lower-level FS, only the GFS object.
569b247029SGordon Ross  *
579b247029SGordon Ross  * When SYSATTR=false, XATTR=true:
589b247029SGordon Ross  *	The XATTR directory is implemented by the file system code,
599b247029SGordon Ross  *	and this module is not involved after xattr_dir_lookup()
609b247029SGordon Ross  *	returns the XATTR dir from the underlying file system.
619b247029SGordon Ross  *
629b247029SGordon Ross  * When SYSATTR=false, XATTR=false:
639b247029SGordon Ross  *	xattr_dir_lookup just returns EINVAL
649b247029SGordon Ross  *
659b247029SGordon Ross  * In the first two cases (where we have system attributes) this module
669b247029SGordon Ross  * implements what can be thought of as a "translucent" directory containing
679b247029SGordon Ross  * both the system attribute names (SUNWattr*) and whatever XATTR names may
689b247029SGordon Ross  * exist in the XATTR directory of the underlying file system, if any.
699b247029SGordon Ross  *
709b247029SGordon Ross  * This affects operations on the (GFS) XATTR directory as follows:
719b247029SGordon Ross  *
729b247029SGordon Ross  * readdir:	Merges the SUNWattr* names with any contents from the
739b247029SGordon Ross  *		underlying XATTR directory.
749b247029SGordon Ross  *
759b247029SGordon Ross  * rename:	If "to" or "from" is a SUNWattr name, special handling,
769b247029SGordon Ross  *		else pass through to the lower FS.
779b247029SGordon Ross  *
789b247029SGordon Ross  * link:	If "from" is a SUNWattr name, disallow.
799b247029SGordon Ross  *
809b247029SGordon Ross  * create:	If a SUNWattr name, disallow, else pass to lower FS.
819b247029SGordon Ross  * remove:	(same)
829b247029SGordon Ross  *
839b247029SGordon Ross  * open,close:	Just pass through to the XATTR dir in the lower FS.
849b247029SGordon Ross  *
859b247029SGordon Ross  * lookup:	Lookup an XATTR file in either the (GFS) XATTR directory
869b247029SGordon Ross  *		or the "real" XATTR directory of the underlying FS.
879b247029SGordon Ross  *		Note for file systems the support SYSATTR but not XATTR,
889b247029SGordon Ross  *		only the GFS XATTR directory will exist.  When both exist,
899b247029SGordon Ross  *		gfs_vop_lookup uses the xattr_lookup_cb callback function
909b247029SGordon Ross  *		which passes the lookup call through to the "real" FS.
919b247029SGordon Ross  *
929b247029SGordon Ross  * Operations on the XATTR _files_ are simpler:
939b247029SGordon Ross  *
949b247029SGordon Ross  * If the file vnode came from lookup at the GFS level, the file is one of
959b247029SGordon Ross  * the special SUNWattr* vnodes, and it's vnode operations (xattr_file_tops)
969b247029SGordon Ross  * allow only what's appropriate on these "files".
979b247029SGordon Ross  *
989b247029SGordon Ross  * If the file vnode came from the underlying FS, all operations on that
999b247029SGordon Ross  * object are handled through the vnode operations set by that FS.
100da6c28aaSamw  */
101da6c28aaSamw 
102da6c28aaSamw #include <sys/param.h>
103da6c28aaSamw #include <sys/isa_defs.h>
104da6c28aaSamw #include <sys/types.h>
105da6c28aaSamw #include <sys/sysmacros.h>
106da6c28aaSamw #include <sys/cred.h>
107da6c28aaSamw #include <sys/systm.h>
108da6c28aaSamw #include <sys/errno.h>
109da6c28aaSamw #include <sys/fcntl.h>
110da6c28aaSamw #include <sys/pathname.h>
111da6c28aaSamw #include <sys/stat.h>
112da6c28aaSamw #include <sys/vfs.h>
113da6c28aaSamw #include <sys/acl.h>
114da6c28aaSamw #include <sys/file.h>
115da6c28aaSamw #include <sys/sunddi.h>
116da6c28aaSamw #include <sys/debug.h>
117da6c28aaSamw #include <sys/cmn_err.h>
118da6c28aaSamw #include <sys/vnode.h>
119da6c28aaSamw #include <sys/mode.h>
120da6c28aaSamw #include <sys/nvpair.h>
121da6c28aaSamw #include <sys/attr.h>
122da6c28aaSamw #include <sys/gfs.h>
123da6c28aaSamw #include <sys/mutex.h>
124da6c28aaSamw #include <fs/fs_subr.h>
125da6c28aaSamw #include <sys/kidmap.h>
126da6c28aaSamw 
127da6c28aaSamw typedef struct {
12868469adeSMark Shellenbaum 	gfs_file_t	xattr_gfs_private;
129da6c28aaSamw 	xattr_view_t	xattr_view;
130da6c28aaSamw } xattr_file_t;
131da6c28aaSamw 
13268469adeSMark Shellenbaum typedef struct {
13368469adeSMark Shellenbaum 	gfs_dir_t	xattr_gfs_private;
1349b247029SGordon Ross 	vnode_t		*xattr_realvp;
13568469adeSMark Shellenbaum } xattr_dir_t;
13668469adeSMark Shellenbaum 
137da6c28aaSamw /* ARGSUSED */
138da6c28aaSamw static int
xattr_file_open(vnode_t ** vpp,int flags,cred_t * cr,caller_context_t * ct)139da6c28aaSamw xattr_file_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
140da6c28aaSamw {
141da6c28aaSamw 	xattr_file_t *np = (*vpp)->v_data;
142da6c28aaSamw 
143da6c28aaSamw 	if ((np->xattr_view == XATTR_VIEW_READONLY) && (flags & FWRITE))
144da6c28aaSamw 		return (EACCES);
145da6c28aaSamw 
146da6c28aaSamw 	return (0);
147da6c28aaSamw }
148da6c28aaSamw 
149da6c28aaSamw /* ARGSUSED */
150da6c28aaSamw static int
xattr_file_access(vnode_t * vp,int mode,int flags,cred_t * cr,caller_context_t * ct)151da6c28aaSamw xattr_file_access(vnode_t *vp, int mode, int flags, cred_t *cr,
152da6c28aaSamw     caller_context_t *ct)
153da6c28aaSamw {
154da6c28aaSamw 	xattr_file_t *np = vp->v_data;
155da6c28aaSamw 
156da6c28aaSamw 	if ((np->xattr_view == XATTR_VIEW_READONLY) && (mode & VWRITE))
157da6c28aaSamw 		return (EACCES);
158da6c28aaSamw 
159da6c28aaSamw 	return (0);
160da6c28aaSamw }
161da6c28aaSamw 
162da6c28aaSamw /* ARGSUSED */
163da6c28aaSamw static int
xattr_file_close(vnode_t * vp,int flags,int count,offset_t off,cred_t * cr,caller_context_t * ct)164da6c28aaSamw xattr_file_close(vnode_t *vp, int flags, int count, offset_t off,
165da6c28aaSamw     cred_t *cr, caller_context_t *ct)
166da6c28aaSamw {
167da6c28aaSamw 	cleanlocks(vp, ddi_get_pid(), 0);
168da6c28aaSamw 	cleanshares(vp, ddi_get_pid());
169da6c28aaSamw 	return (0);
170da6c28aaSamw }
171da6c28aaSamw 
172da6c28aaSamw static int
xattr_common_fid(vnode_t * vp,fid_t * fidp,caller_context_t * ct)173da6c28aaSamw xattr_common_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
174da6c28aaSamw {
175da6c28aaSamw 	xattr_fid_t	*xfidp;
176da6c28aaSamw 	vnode_t		*pvp, *savevp;
177da6c28aaSamw 	int		error;
178da6c28aaSamw 	uint16_t	orig_len;
179da6c28aaSamw 
180da6c28aaSamw 	if (fidp->fid_len < XATTR_FIDSZ) {
181da6c28aaSamw 		fidp->fid_len = XATTR_FIDSZ;
182da6c28aaSamw 		return (ENOSPC);
183da6c28aaSamw 	}
184da6c28aaSamw 
185da6c28aaSamw 	savevp = pvp = gfs_file_parent(vp);
186da6c28aaSamw 	mutex_enter(&savevp->v_lock);
187da6c28aaSamw 	if (pvp->v_flag & V_XATTRDIR) {
188da6c28aaSamw 		pvp = gfs_file_parent(pvp);
189da6c28aaSamw 	}
190da6c28aaSamw 	mutex_exit(&savevp->v_lock);
191da6c28aaSamw 
192da6c28aaSamw 	xfidp = (xattr_fid_t *)fidp;
193da6c28aaSamw 	orig_len = fidp->fid_len;
194da6c28aaSamw 	fidp->fid_len = sizeof (xfidp->parent_fid);
195da6c28aaSamw 
196da6c28aaSamw 	error = VOP_FID(pvp, fidp, ct);
197da6c28aaSamw 	if (error) {
198da6c28aaSamw 		fidp->fid_len = orig_len;
199da6c28aaSamw 		return (error);
200da6c28aaSamw 	}
201da6c28aaSamw 
202da6c28aaSamw 	xfidp->parent_len = fidp->fid_len;
203da6c28aaSamw 	fidp->fid_len = XATTR_FIDSZ;
204da6c28aaSamw 	xfidp->dir_offset = gfs_file_inode(vp);
205da6c28aaSamw 
206da6c28aaSamw 	return (0);
207da6c28aaSamw }
208da6c28aaSamw 
209da6c28aaSamw /* ARGSUSED */
210da6c28aaSamw static int
xattr_fill_nvlist(vnode_t * vp,xattr_view_t xattr_view,nvlist_t * nvlp,cred_t * cr,caller_context_t * ct)211da6c28aaSamw xattr_fill_nvlist(vnode_t *vp, xattr_view_t xattr_view, nvlist_t *nvlp,
212da6c28aaSamw     cred_t *cr, caller_context_t *ct)
213da6c28aaSamw {
214da6c28aaSamw 	int error;
215da6c28aaSamw 	f_attr_t attr;
216da6c28aaSamw 	uint64_t fsid;
217da6c28aaSamw 	xvattr_t xvattr;
218da6c28aaSamw 	xoptattr_t *xoap;	/* Pointer to optional attributes */
219da6c28aaSamw 	vnode_t *ppvp;
220da6c28aaSamw 	const char *domain;
221da6c28aaSamw 	uint32_t rid;
222da6c28aaSamw 
223da6c28aaSamw 	xva_init(&xvattr);
224da6c28aaSamw 
225da6c28aaSamw 	if ((xoap = xva_getxoptattr(&xvattr)) == NULL)
226da6c28aaSamw 		return (EINVAL);
227da6c28aaSamw 
228da6c28aaSamw 	/*
229da6c28aaSamw 	 * For detecting ephemeral uid/gid
230da6c28aaSamw 	 */
231da6c28aaSamw 	xvattr.xva_vattr.va_mask |= (AT_UID|AT_GID);
232da6c28aaSamw 
233da6c28aaSamw 	/*
234da6c28aaSamw 	 * We need to access the real fs object.
235da6c28aaSamw 	 * vp points to a GFS file; ppvp points to the real object.
236da6c28aaSamw 	 */
237da6c28aaSamw 	ppvp = gfs_file_parent(gfs_file_parent(vp));
238da6c28aaSamw 
239da6c28aaSamw 	/*
240da6c28aaSamw 	 * Iterate through the attrs associated with this view
241da6c28aaSamw 	 */
242da6c28aaSamw 
243da6c28aaSamw 	for (attr = 0; attr < F_ATTR_ALL; attr++) {
244da6c28aaSamw 		if (xattr_view != attr_to_xattr_view(attr)) {
245da6c28aaSamw 			continue;
246da6c28aaSamw 		}
247da6c28aaSamw 
248da6c28aaSamw 		switch (attr) {
249da6c28aaSamw 		case F_SYSTEM:
250da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_SYSTEM);
251da6c28aaSamw 			break;
252da6c28aaSamw 		case F_READONLY:
253da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_READONLY);
254da6c28aaSamw 			break;
255da6c28aaSamw 		case F_HIDDEN:
256da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_HIDDEN);
257da6c28aaSamw 			break;
258da6c28aaSamw 		case F_ARCHIVE:
259da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_ARCHIVE);
260da6c28aaSamw 			break;
261da6c28aaSamw 		case F_IMMUTABLE:
262da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_IMMUTABLE);
263da6c28aaSamw 			break;
264da6c28aaSamw 		case F_APPENDONLY:
265da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_APPENDONLY);
266da6c28aaSamw 			break;
267da6c28aaSamw 		case F_NOUNLINK:
268da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_NOUNLINK);
269da6c28aaSamw 			break;
270da6c28aaSamw 		case F_OPAQUE:
271da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_OPAQUE);
272da6c28aaSamw 			break;
273da6c28aaSamw 		case F_NODUMP:
274da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_NODUMP);
275da6c28aaSamw 			break;
276da6c28aaSamw 		case F_AV_QUARANTINED:
277da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
278da6c28aaSamw 			break;
279da6c28aaSamw 		case F_AV_MODIFIED:
280da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
281da6c28aaSamw 			break;
282da6c28aaSamw 		case F_AV_SCANSTAMP:
283da6c28aaSamw 			if (ppvp->v_type == VREG)
284da6c28aaSamw 				XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
285da6c28aaSamw 			break;
286da6c28aaSamw 		case F_CRTIME:
287da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_CREATETIME);
288da6c28aaSamw 			break;
289da6c28aaSamw 		case F_FSID:
290da6c28aaSamw 			fsid = (((uint64_t)vp->v_vfsp->vfs_fsid.val[0] << 32) |
291da6c28aaSamw 			    (uint64_t)(vp->v_vfsp->vfs_fsid.val[1] &
292da6c28aaSamw 			    0xffffffff));
293da6c28aaSamw 			VERIFY(nvlist_add_uint64(nvlp, attr_to_name(attr),
294da6c28aaSamw 			    fsid) == 0);
295da6c28aaSamw 			break;
2967a286c47SDai Ngo 		case F_REPARSE:
2977a286c47SDai Ngo 			XVA_SET_REQ(&xvattr, XAT_REPARSE);
2987a286c47SDai Ngo 			break;
29999d5e173STim Haley 		case F_GEN:
30099d5e173STim Haley 			XVA_SET_REQ(&xvattr, XAT_GEN);
30199d5e173STim Haley 			break;
302fd9ee8b5Sjoyce mcintosh 		case F_OFFLINE:
303fd9ee8b5Sjoyce mcintosh 			XVA_SET_REQ(&xvattr, XAT_OFFLINE);
304fd9ee8b5Sjoyce mcintosh 			break;
305fd9ee8b5Sjoyce mcintosh 		case F_SPARSE:
306fd9ee8b5Sjoyce mcintosh 			XVA_SET_REQ(&xvattr, XAT_SPARSE);
307fd9ee8b5Sjoyce mcintosh 			break;
308da6c28aaSamw 		default:
309da6c28aaSamw 			break;
310da6c28aaSamw 		}
311da6c28aaSamw 	}
312da6c28aaSamw 
313da6c28aaSamw 	error = VOP_GETATTR(ppvp, &xvattr.xva_vattr, 0, cr, ct);
314da6c28aaSamw 	if (error)
315da6c28aaSamw 		return (error);
316da6c28aaSamw 
317da6c28aaSamw 	/*
318da6c28aaSamw 	 * Process all the optional attributes together here.  Notice that
319da6c28aaSamw 	 * xoap was set when the optional attribute bits were set above.
320da6c28aaSamw 	 */
321da6c28aaSamw 	if ((xvattr.xva_vattr.va_mask & AT_XVATTR) && xoap) {
322da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_READONLY)) {
323da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
324da6c28aaSamw 			    attr_to_name(F_READONLY),
325da6c28aaSamw 			    xoap->xoa_readonly) == 0);
326da6c28aaSamw 		}
327da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_HIDDEN)) {
328da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
329da6c28aaSamw 			    attr_to_name(F_HIDDEN),
330da6c28aaSamw 			    xoap->xoa_hidden) == 0);
331da6c28aaSamw 		}
332da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_SYSTEM)) {
333da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
334da6c28aaSamw 			    attr_to_name(F_SYSTEM),
335da6c28aaSamw 			    xoap->xoa_system) == 0);
336da6c28aaSamw 		}
337da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_ARCHIVE)) {
338da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
339da6c28aaSamw 			    attr_to_name(F_ARCHIVE),
340da6c28aaSamw 			    xoap->xoa_archive) == 0);
341da6c28aaSamw 		}
342da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_IMMUTABLE)) {
343da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
344da6c28aaSamw 			    attr_to_name(F_IMMUTABLE),
345da6c28aaSamw 			    xoap->xoa_immutable) == 0);
346da6c28aaSamw 		}
347da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_NOUNLINK)) {
348da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
349da6c28aaSamw 			    attr_to_name(F_NOUNLINK),
350da6c28aaSamw 			    xoap->xoa_nounlink) == 0);
351da6c28aaSamw 		}
352da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_APPENDONLY)) {
353da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
354da6c28aaSamw 			    attr_to_name(F_APPENDONLY),
355da6c28aaSamw 			    xoap->xoa_appendonly) == 0);
356da6c28aaSamw 		}
357da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_NODUMP)) {
358da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
359da6c28aaSamw 			    attr_to_name(F_NODUMP),
360da6c28aaSamw 			    xoap->xoa_nodump) == 0);
361da6c28aaSamw 		}
362da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_OPAQUE)) {
363da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
364da6c28aaSamw 			    attr_to_name(F_OPAQUE),
365da6c28aaSamw 			    xoap->xoa_opaque) == 0);
366da6c28aaSamw 		}
367da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED)) {
368da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
369da6c28aaSamw 			    attr_to_name(F_AV_QUARANTINED),
370da6c28aaSamw 			    xoap->xoa_av_quarantined) == 0);
371da6c28aaSamw 		}
372da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED)) {
373da6c28aaSamw 			VERIFY(nvlist_add_boolean_value(nvlp,
374da6c28aaSamw 			    attr_to_name(F_AV_MODIFIED),
375da6c28aaSamw 			    xoap->xoa_av_modified) == 0);
376da6c28aaSamw 		}
377da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP)) {
378da6c28aaSamw 			VERIFY(nvlist_add_uint8_array(nvlp,
379da6c28aaSamw 			    attr_to_name(F_AV_SCANSTAMP),
380da6c28aaSamw 			    xoap->xoa_av_scanstamp,
381da6c28aaSamw 			    sizeof (xoap->xoa_av_scanstamp)) == 0);
382da6c28aaSamw 		}
383da6c28aaSamw 		if (XVA_ISSET_RTN(&xvattr, XAT_CREATETIME)) {
384da6c28aaSamw 			VERIFY(nvlist_add_uint64_array(nvlp,
385da6c28aaSamw 			    attr_to_name(F_CRTIME),
386da6c28aaSamw 			    (uint64_t *)&(xoap->xoa_createtime),
387da6c28aaSamw 			    sizeof (xoap->xoa_createtime) /
388da6c28aaSamw 			    sizeof (uint64_t)) == 0);
389da6c28aaSamw 		}
3907a286c47SDai Ngo 		if (XVA_ISSET_RTN(&xvattr, XAT_REPARSE)) {
3917a286c47SDai Ngo 			VERIFY(nvlist_add_boolean_value(nvlp,
3927a286c47SDai Ngo 			    attr_to_name(F_REPARSE),
3937a286c47SDai Ngo 			    xoap->xoa_reparse) == 0);
3947a286c47SDai Ngo 		}
39599d5e173STim Haley 		if (XVA_ISSET_RTN(&xvattr, XAT_GEN)) {
39699d5e173STim Haley 			VERIFY(nvlist_add_uint64(nvlp,
39799d5e173STim Haley 			    attr_to_name(F_GEN),
39899d5e173STim Haley 			    xoap->xoa_generation) == 0);
39999d5e173STim Haley 		}
400fd9ee8b5Sjoyce mcintosh 		if (XVA_ISSET_RTN(&xvattr, XAT_OFFLINE)) {
401fd9ee8b5Sjoyce mcintosh 			VERIFY(nvlist_add_boolean_value(nvlp,
402fd9ee8b5Sjoyce mcintosh 			    attr_to_name(F_OFFLINE),
403fd9ee8b5Sjoyce mcintosh 			    xoap->xoa_offline) == 0);
404fd9ee8b5Sjoyce mcintosh 		}
405fd9ee8b5Sjoyce mcintosh 		if (XVA_ISSET_RTN(&xvattr, XAT_SPARSE)) {
406fd9ee8b5Sjoyce mcintosh 			VERIFY(nvlist_add_boolean_value(nvlp,
407fd9ee8b5Sjoyce mcintosh 			    attr_to_name(F_SPARSE),
408fd9ee8b5Sjoyce mcintosh 			    xoap->xoa_sparse) == 0);
409fd9ee8b5Sjoyce mcintosh 		}
410da6c28aaSamw 	}
411da6c28aaSamw 	/*
412da6c28aaSamw 	 * Check for optional ownersid/groupsid
413da6c28aaSamw 	 */
414da6c28aaSamw 
415da6c28aaSamw 	if (xvattr.xva_vattr.va_uid > MAXUID) {
416da6c28aaSamw 		nvlist_t *nvl_sid;
417da6c28aaSamw 
418da6c28aaSamw 		if (nvlist_alloc(&nvl_sid, NV_UNIQUE_NAME, KM_SLEEP))
419da6c28aaSamw 			return (ENOMEM);
420da6c28aaSamw 
421bda89588Sjp 		if (kidmap_getsidbyuid(crgetzone(cr), xvattr.xva_vattr.va_uid,
422da6c28aaSamw 		    &domain, &rid) == 0) {
423da6c28aaSamw 			VERIFY(nvlist_add_string(nvl_sid,
424da6c28aaSamw 			    SID_DOMAIN, domain) == 0);
425da6c28aaSamw 			VERIFY(nvlist_add_uint32(nvl_sid, SID_RID, rid) == 0);
426da6c28aaSamw 			VERIFY(nvlist_add_nvlist(nvlp, attr_to_name(F_OWNERSID),
427da6c28aaSamw 			    nvl_sid) == 0);
428da6c28aaSamw 		}
429da6c28aaSamw 		nvlist_free(nvl_sid);
430da6c28aaSamw 	}
431da6c28aaSamw 	if (xvattr.xva_vattr.va_gid > MAXUID) {
432da6c28aaSamw 		nvlist_t *nvl_sid;
433da6c28aaSamw 
434da6c28aaSamw 		if (nvlist_alloc(&nvl_sid, NV_UNIQUE_NAME, KM_SLEEP))
435da6c28aaSamw 			return (ENOMEM);
436da6c28aaSamw 
437bda89588Sjp 		if (kidmap_getsidbygid(crgetzone(cr), xvattr.xva_vattr.va_gid,
438da6c28aaSamw 		    &domain, &rid) == 0) {
439da6c28aaSamw 			VERIFY(nvlist_add_string(nvl_sid,
440da6c28aaSamw 			    SID_DOMAIN, domain) == 0);
441da6c28aaSamw 			VERIFY(nvlist_add_uint32(nvl_sid, SID_RID, rid) == 0);
442da6c28aaSamw 			VERIFY(nvlist_add_nvlist(nvlp, attr_to_name(F_GROUPSID),
443da6c28aaSamw 			    nvl_sid) == 0);
444da6c28aaSamw 		}
445da6c28aaSamw 		nvlist_free(nvl_sid);
446da6c28aaSamw 	}
447da6c28aaSamw 
448da6c28aaSamw 	return (0);
449da6c28aaSamw }
450da6c28aaSamw 
451da6c28aaSamw /*
452da6c28aaSamw  * The size of a sysattr file is the size of the nvlist that will be
453da6c28aaSamw  * returned by xattr_file_read().  A call to xattr_file_write() could
454da6c28aaSamw  * change the size of that nvlist.  That size is not stored persistently
455da6c28aaSamw  * so xattr_fill_nvlist() calls VOP_GETATTR so that it can be calculated.
456da6c28aaSamw  */
457da6c28aaSamw static int
xattr_file_size(vnode_t * vp,xattr_view_t xattr_view,size_t * size,cred_t * cr,caller_context_t * ct)458da6c28aaSamw xattr_file_size(vnode_t *vp, xattr_view_t xattr_view, size_t *size,
459da6c28aaSamw     cred_t *cr, caller_context_t *ct)
460da6c28aaSamw {
461da6c28aaSamw 	nvlist_t *nvl;
462da6c28aaSamw 
463da6c28aaSamw 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP)) {
464da6c28aaSamw 		return (ENOMEM);
465da6c28aaSamw 	}
466da6c28aaSamw 
467da6c28aaSamw 	if (xattr_fill_nvlist(vp, xattr_view, nvl, cr, ct)) {
468da6c28aaSamw 		nvlist_free(nvl);
469da6c28aaSamw 		return (EFAULT);
470da6c28aaSamw 	}
471da6c28aaSamw 
472da6c28aaSamw 	VERIFY(nvlist_size(nvl, size, NV_ENCODE_XDR) == 0);
473da6c28aaSamw 	nvlist_free(nvl);
474da6c28aaSamw 	return (0);
475da6c28aaSamw }
476da6c28aaSamw 
477da6c28aaSamw /* ARGSUSED */
478da6c28aaSamw static int
xattr_file_getattr(vnode_t * vp,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)479da6c28aaSamw xattr_file_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
480da6c28aaSamw     caller_context_t *ct)
481da6c28aaSamw {
482da6c28aaSamw 	xattr_file_t *np = vp->v_data;
483da6c28aaSamw 	timestruc_t now;
484da6c28aaSamw 	size_t size;
485da6c28aaSamw 	int error;
486da6c28aaSamw 	vnode_t *pvp;
487da6c28aaSamw 	vattr_t pvattr;
488da6c28aaSamw 
489da6c28aaSamw 	vap->va_type = VREG;
490da6c28aaSamw 	vap->va_mode = MAKEIMODE(vap->va_type,
491da6c28aaSamw 	    (np->xattr_view == XATTR_VIEW_READONLY ? 0444 : 0644));
492da6c28aaSamw 	vap->va_nodeid = gfs_file_inode(vp);
493da6c28aaSamw 	vap->va_nlink = 1;
494da6c28aaSamw 	pvp = gfs_file_parent(vp);
495da6c28aaSamw 	(void) memset(&pvattr, 0, sizeof (pvattr));
496da6c28aaSamw 	pvattr.va_mask = AT_CTIME|AT_MTIME;
497da6c28aaSamw 	error = VOP_GETATTR(pvp, &pvattr, flags, cr, ct);
498da6c28aaSamw 	if (error) {
499da6c28aaSamw 		return (error);
500da6c28aaSamw 	}
501da6c28aaSamw 	vap->va_ctime = pvattr.va_ctime;
502da6c28aaSamw 	vap->va_mtime = pvattr.va_mtime;
503da6c28aaSamw 	gethrestime(&now);
504da6c28aaSamw 	vap->va_atime = now;
505da6c28aaSamw 	vap->va_uid = 0;
506da6c28aaSamw 	vap->va_gid = 0;
507da6c28aaSamw 	vap->va_rdev = 0;
508da6c28aaSamw 	vap->va_blksize = DEV_BSIZE;
509da6c28aaSamw 	vap->va_seq = 0;
510da6c28aaSamw 	vap->va_fsid = vp->v_vfsp->vfs_dev;
511da6c28aaSamw 	error = xattr_file_size(vp, np->xattr_view, &size, cr, ct);
512da6c28aaSamw 	vap->va_size = size;
513da6c28aaSamw 	vap->va_nblocks = howmany(vap->va_size, vap->va_blksize);
514da6c28aaSamw 	return (error);
515da6c28aaSamw }
516da6c28aaSamw 
517da6c28aaSamw /* ARGSUSED */
518da6c28aaSamw static int
xattr_file_read(vnode_t * vp,uio_t * uiop,int ioflag,cred_t * cr,caller_context_t * ct)519da6c28aaSamw xattr_file_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
520da6c28aaSamw     caller_context_t *ct)
521da6c28aaSamw {
522da6c28aaSamw 	xattr_file_t *np = vp->v_data;
523da6c28aaSamw 	xattr_view_t xattr_view = np->xattr_view;
524da6c28aaSamw 	char *buf;
525da6c28aaSamw 	size_t filesize;
526da6c28aaSamw 	nvlist_t *nvl;
527da6c28aaSamw 	int error;
528da6c28aaSamw 
529da6c28aaSamw 	/*
530da6c28aaSamw 	 * Validate file offset and fasttrack empty reads
531da6c28aaSamw 	 */
532da6c28aaSamw 	if (uiop->uio_loffset < (offset_t)0)
533da6c28aaSamw 		return (EINVAL);
534da6c28aaSamw 
535da6c28aaSamw 	if (uiop->uio_resid == 0)
536da6c28aaSamw 		return (0);
537da6c28aaSamw 
538da6c28aaSamw 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP))
539da6c28aaSamw 		return (ENOMEM);
540da6c28aaSamw 
541da6c28aaSamw 	if (xattr_fill_nvlist(vp, xattr_view, nvl, cr, ct)) {
542da6c28aaSamw 		nvlist_free(nvl);
543da6c28aaSamw 		return (EFAULT);
544da6c28aaSamw 	}
545da6c28aaSamw 
546da6c28aaSamw 	VERIFY(nvlist_size(nvl, &filesize, NV_ENCODE_XDR) == 0);
547da6c28aaSamw 
548da6c28aaSamw 	if (uiop->uio_loffset >= filesize) {
549da6c28aaSamw 		nvlist_free(nvl);
550da6c28aaSamw 		return (0);
551da6c28aaSamw 	}
552da6c28aaSamw 
553da6c28aaSamw 	buf = kmem_alloc(filesize, KM_SLEEP);
554da6c28aaSamw 	VERIFY(nvlist_pack(nvl, &buf, &filesize, NV_ENCODE_XDR,
555da6c28aaSamw 	    KM_SLEEP) == 0);
556da6c28aaSamw 
557da6c28aaSamw 	error = uiomove((caddr_t)buf, filesize, UIO_READ, uiop);
558da6c28aaSamw 	kmem_free(buf, filesize);
559da6c28aaSamw 	nvlist_free(nvl);
560da6c28aaSamw 	return (error);
561da6c28aaSamw }
562da6c28aaSamw 
563da6c28aaSamw /* ARGSUSED */
564da6c28aaSamw static int
xattr_file_write(vnode_t * vp,uio_t * uiop,int ioflag,cred_t * cr,caller_context_t * ct)565da6c28aaSamw xattr_file_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
566da6c28aaSamw     caller_context_t *ct)
567da6c28aaSamw {
568da6c28aaSamw 	int error = 0;
569da6c28aaSamw 	char *buf;
570da6c28aaSamw 	char *domain;
571da6c28aaSamw 	uint32_t rid;
572da6c28aaSamw 	ssize_t size = uiop->uio_resid;
573da6c28aaSamw 	nvlist_t *nvp;
574da6c28aaSamw 	nvpair_t *pair = NULL;
575da6c28aaSamw 	vnode_t *ppvp;
576da6c28aaSamw 	xvattr_t xvattr;
577da6c28aaSamw 	xoptattr_t *xoap = NULL;	/* Pointer to optional attributes */
578da6c28aaSamw 
5799660e5cbSJanice Chang 	if (vfs_has_feature(vp->v_vfsp, VFSFT_XVATTR) == 0)
5809660e5cbSJanice Chang 		return (EINVAL);
5819660e5cbSJanice Chang 
582da6c28aaSamw 	/*
583da6c28aaSamw 	 * Validate file offset and size.
584da6c28aaSamw 	 */
585da6c28aaSamw 	if (uiop->uio_loffset < (offset_t)0)
586da6c28aaSamw 		return (EINVAL);
587da6c28aaSamw 
588da6c28aaSamw 	if (size == 0)
589da6c28aaSamw 		return (EINVAL);
590da6c28aaSamw 
591da6c28aaSamw 	xva_init(&xvattr);
592da6c28aaSamw 
593da6c28aaSamw 	if ((xoap = xva_getxoptattr(&xvattr)) == NULL) {
594da6c28aaSamw 		return (EINVAL);
595da6c28aaSamw 	}
596da6c28aaSamw 
597da6c28aaSamw 	/*
598da6c28aaSamw 	 * Copy and unpack the nvlist
599da6c28aaSamw 	 */
600da6c28aaSamw 	buf = kmem_alloc(size, KM_SLEEP);
601da6c28aaSamw 	if (uiomove((caddr_t)buf, size, UIO_WRITE, uiop)) {
602da6c28aaSamw 		return (EFAULT);
603da6c28aaSamw 	}
604da6c28aaSamw 
605da6c28aaSamw 	if (nvlist_unpack(buf, size, &nvp, KM_SLEEP) != 0) {
606da6c28aaSamw 		kmem_free(buf, size);
607da6c28aaSamw 		uiop->uio_resid = size;
608da6c28aaSamw 		return (EINVAL);
609da6c28aaSamw 	}
610da6c28aaSamw 	kmem_free(buf, size);
611da6c28aaSamw 
612da6c28aaSamw 	/*
613da6c28aaSamw 	 * Fasttrack empty writes (nvlist with no nvpairs)
614da6c28aaSamw 	 */
615da6c28aaSamw 	if (nvlist_next_nvpair(nvp, NULL) == 0)
616da6c28aaSamw 		return (0);
617da6c28aaSamw 
618da6c28aaSamw 	ppvp = gfs_file_parent(gfs_file_parent(vp));
619da6c28aaSamw 
620da6c28aaSamw 	while (pair = nvlist_next_nvpair(nvp, pair)) {
621da6c28aaSamw 		data_type_t type;
622da6c28aaSamw 		f_attr_t attr;
623da6c28aaSamw 		boolean_t value;
624da6c28aaSamw 		uint64_t *time, *times;
625da6c28aaSamw 		uint_t elem, nelems;
626da6c28aaSamw 		nvlist_t *nvp_sid;
627da6c28aaSamw 		uint8_t *scanstamp;
628da6c28aaSamw 
629da6c28aaSamw 		/*
630da6c28aaSamw 		 * Validate the name and type of each attribute.
631da6c28aaSamw 		 * Log any unknown names and continue.  This will
632da6c28aaSamw 		 * help if additional attributes are added later.
633da6c28aaSamw 		 */
634da6c28aaSamw 		type = nvpair_type(pair);
635da6c28aaSamw 		if ((attr = name_to_attr(nvpair_name(pair))) == F_ATTR_INVAL) {
636da6c28aaSamw 			cmn_err(CE_WARN, "Unknown attribute %s",
637da6c28aaSamw 			    nvpair_name(pair));
638da6c28aaSamw 			continue;
639da6c28aaSamw 		}
640da6c28aaSamw 
641da6c28aaSamw 		/*
642da6c28aaSamw 		 * Verify nvlist type matches required type and view is OK
643da6c28aaSamw 		 */
644da6c28aaSamw 
645da6c28aaSamw 		if (type != attr_to_data_type(attr) ||
646da6c28aaSamw 		    (attr_to_xattr_view(attr) == XATTR_VIEW_READONLY)) {
647da6c28aaSamw 			nvlist_free(nvp);
648da6c28aaSamw 			return (EINVAL);
649da6c28aaSamw 		}
650da6c28aaSamw 
651da6c28aaSamw 		/*
652da6c28aaSamw 		 * For OWNERSID/GROUPSID make sure the target
653da6c28aaSamw 		 * file system support ephemeral ID's
654da6c28aaSamw 		 */
655da6c28aaSamw 		if ((attr == F_OWNERSID || attr == F_GROUPSID) &&
656da6c28aaSamw 		    (!(vp->v_vfsp->vfs_flag & VFS_XID))) {
657da6c28aaSamw 			nvlist_free(nvp);
658da6c28aaSamw 			return (EINVAL);
659da6c28aaSamw 		}
660da6c28aaSamw 
661da6c28aaSamw 		/*
662da6c28aaSamw 		 * Retrieve data from nvpair
663da6c28aaSamw 		 */
664da6c28aaSamw 		switch (type) {
665da6c28aaSamw 		case DATA_TYPE_BOOLEAN_VALUE:
666da6c28aaSamw 			if (nvpair_value_boolean_value(pair, &value)) {
667da6c28aaSamw 				nvlist_free(nvp);
668da6c28aaSamw 				return (EINVAL);
669da6c28aaSamw 			}
670da6c28aaSamw 			break;
671da6c28aaSamw 		case DATA_TYPE_UINT64_ARRAY:
672da6c28aaSamw 			if (nvpair_value_uint64_array(pair, &times, &nelems)) {
673da6c28aaSamw 				nvlist_free(nvp);
674da6c28aaSamw 				return (EINVAL);
675da6c28aaSamw 			}
676da6c28aaSamw 			break;
677da6c28aaSamw 		case DATA_TYPE_NVLIST:
678da6c28aaSamw 			if (nvpair_value_nvlist(pair, &nvp_sid)) {
679da6c28aaSamw 				nvlist_free(nvp);
680da6c28aaSamw 				return (EINVAL);
681da6c28aaSamw 			}
682da6c28aaSamw 			break;
683da6c28aaSamw 		case DATA_TYPE_UINT8_ARRAY:
684da6c28aaSamw 			if (nvpair_value_uint8_array(pair,
685da6c28aaSamw 			    &scanstamp, &nelems)) {
686da6c28aaSamw 				nvlist_free(nvp);
687da6c28aaSamw 				return (EINVAL);
688da6c28aaSamw 			}
689da6c28aaSamw 			break;
690da6c28aaSamw 		default:
691da6c28aaSamw 			nvlist_free(nvp);
692da6c28aaSamw 			return (EINVAL);
693da6c28aaSamw 		}
694da6c28aaSamw 
695da6c28aaSamw 		switch (attr) {
696da6c28aaSamw 		/*
697da6c28aaSamw 		 * If we have several similar optional attributes to
698da6c28aaSamw 		 * process then we should do it all together here so that
699da6c28aaSamw 		 * xoap and the requested bitmap can be set in one place.
700da6c28aaSamw 		 */
701da6c28aaSamw 		case F_READONLY:
702da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_READONLY);
703da6c28aaSamw 			xoap->xoa_readonly = value;
704da6c28aaSamw 			break;
705da6c28aaSamw 		case F_HIDDEN:
706da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_HIDDEN);
707da6c28aaSamw 			xoap->xoa_hidden = value;
708da6c28aaSamw 			break;
709da6c28aaSamw 		case F_SYSTEM:
710da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_SYSTEM);
711da6c28aaSamw 			xoap->xoa_system = value;
712da6c28aaSamw 			break;
713da6c28aaSamw 		case F_ARCHIVE:
714da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_ARCHIVE);
715da6c28aaSamw 			xoap->xoa_archive = value;
716da6c28aaSamw 			break;
717da6c28aaSamw 		case F_IMMUTABLE:
718da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_IMMUTABLE);
719da6c28aaSamw 			xoap->xoa_immutable = value;
720da6c28aaSamw 			break;
721da6c28aaSamw 		case F_NOUNLINK:
722da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_NOUNLINK);
723da6c28aaSamw 			xoap->xoa_nounlink = value;
724da6c28aaSamw 			break;
725da6c28aaSamw 		case F_APPENDONLY:
726da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_APPENDONLY);
727da6c28aaSamw 			xoap->xoa_appendonly = value;
728da6c28aaSamw 			break;
729da6c28aaSamw 		case F_NODUMP:
730da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_NODUMP);
731da6c28aaSamw 			xoap->xoa_nodump = value;
732da6c28aaSamw 			break;
733da6c28aaSamw 		case F_AV_QUARANTINED:
734da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
735da6c28aaSamw 			xoap->xoa_av_quarantined = value;
736da6c28aaSamw 			break;
737da6c28aaSamw 		case F_AV_MODIFIED:
738da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
739da6c28aaSamw 			xoap->xoa_av_modified = value;
740da6c28aaSamw 			break;
741da6c28aaSamw 		case F_CRTIME:
742da6c28aaSamw 			XVA_SET_REQ(&xvattr, XAT_CREATETIME);
743da6c28aaSamw 			time = (uint64_t *)&(xoap->xoa_createtime);
744da6c28aaSamw 			for (elem = 0; elem < nelems; elem++)
745da6c28aaSamw 				*time++ = times[elem];
746da6c28aaSamw 			break;
747da6c28aaSamw 		case F_OWNERSID:
748da6c28aaSamw 		case F_GROUPSID:
749da6c28aaSamw 			if (nvlist_lookup_string(nvp_sid, SID_DOMAIN,
750da6c28aaSamw 			    &domain) || nvlist_lookup_uint32(nvp_sid, SID_RID,
751da6c28aaSamw 			    &rid)) {
752da6c28aaSamw 				nvlist_free(nvp);
753da6c28aaSamw 				return (EINVAL);
754da6c28aaSamw 			}
755da6c28aaSamw 
756da6c28aaSamw 			/*
757da6c28aaSamw 			 * Now map domain+rid to ephemeral id's
758da6c28aaSamw 			 *
759da6c28aaSamw 			 * If mapping fails, then the uid/gid will
760da6c28aaSamw 			 * be set to UID_NOBODY by Winchester.
761da6c28aaSamw 			 */
762da6c28aaSamw 
763da6c28aaSamw 			if (attr == F_OWNERSID) {
764bda89588Sjp 				(void) kidmap_getuidbysid(crgetzone(cr), domain,
765bda89588Sjp 				    rid, &xvattr.xva_vattr.va_uid);
766da6c28aaSamw 				xvattr.xva_vattr.va_mask |= AT_UID;
767da6c28aaSamw 			} else {
768bda89588Sjp 				(void) kidmap_getgidbysid(crgetzone(cr), domain,
769bda89588Sjp 				    rid, &xvattr.xva_vattr.va_gid);
770da6c28aaSamw 				xvattr.xva_vattr.va_mask |= AT_GID;
771da6c28aaSamw 			}
772da6c28aaSamw 			break;
773da6c28aaSamw 		case F_AV_SCANSTAMP:
774da6c28aaSamw 			if (ppvp->v_type == VREG) {
775da6c28aaSamw 				XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
776da6c28aaSamw 				(void) memcpy(xoap->xoa_av_scanstamp,
777da6c28aaSamw 				    scanstamp, nelems);
778da6c28aaSamw 			} else {
779da6c28aaSamw 				nvlist_free(nvp);
780da6c28aaSamw 				return (EINVAL);
781da6c28aaSamw 			}
782da6c28aaSamw 			break;
7837a286c47SDai Ngo 		case F_REPARSE:
7847a286c47SDai Ngo 			XVA_SET_REQ(&xvattr, XAT_REPARSE);
7857a286c47SDai Ngo 			xoap->xoa_reparse = value;
7867a286c47SDai Ngo 			break;
787fd9ee8b5Sjoyce mcintosh 		case F_OFFLINE:
788fd9ee8b5Sjoyce mcintosh 			XVA_SET_REQ(&xvattr, XAT_OFFLINE);
789fd9ee8b5Sjoyce mcintosh 			xoap->xoa_offline = value;
790fd9ee8b5Sjoyce mcintosh 			break;
791fd9ee8b5Sjoyce mcintosh 		case F_SPARSE:
792fd9ee8b5Sjoyce mcintosh 			XVA_SET_REQ(&xvattr, XAT_SPARSE);
793fd9ee8b5Sjoyce mcintosh 			xoap->xoa_sparse = value;
794fd9ee8b5Sjoyce mcintosh 			break;
795da6c28aaSamw 		default:
796da6c28aaSamw 			break;
797da6c28aaSamw 		}
798da6c28aaSamw 	}
799da6c28aaSamw 
800da6c28aaSamw 	ppvp = gfs_file_parent(gfs_file_parent(vp));
801da6c28aaSamw 	error = VOP_SETATTR(ppvp, &xvattr.xva_vattr, 0, cr, ct);
802da6c28aaSamw 	if (error)
803da6c28aaSamw 		uiop->uio_resid = size;
804da6c28aaSamw 
805da6c28aaSamw 	nvlist_free(nvp);
806da6c28aaSamw 	return (error);
807da6c28aaSamw }
808da6c28aaSamw 
809da6c28aaSamw static int
xattr_file_pathconf(vnode_t * vp,int cmd,ulong_t * valp,cred_t * cr,caller_context_t * ct)810da6c28aaSamw xattr_file_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
811da6c28aaSamw     caller_context_t *ct)
812da6c28aaSamw {
813da6c28aaSamw 	switch (cmd) {
814da6c28aaSamw 	case _PC_XATTR_EXISTS:
815da6c28aaSamw 	case _PC_SATTR_ENABLED:
816da6c28aaSamw 	case _PC_SATTR_EXISTS:
817da6c28aaSamw 		*valp = 0;
818da6c28aaSamw 		return (0);
819da6c28aaSamw 	default:
820da6c28aaSamw 		return (fs_pathconf(vp, cmd, valp, cr, ct));
821da6c28aaSamw 	}
822da6c28aaSamw }
823da6c28aaSamw 
824da6c28aaSamw vnodeops_t *xattr_file_ops;
825da6c28aaSamw 
826da6c28aaSamw static const fs_operation_def_t xattr_file_tops[] = {
827da6c28aaSamw 	{ VOPNAME_OPEN,		{ .vop_open = xattr_file_open }		},
828da6c28aaSamw 	{ VOPNAME_CLOSE,	{ .vop_close = xattr_file_close }	},
829da6c28aaSamw 	{ VOPNAME_READ,		{ .vop_read = xattr_file_read }		},
830da6c28aaSamw 	{ VOPNAME_WRITE,	{ .vop_write = xattr_file_write }	},
831da6c28aaSamw 	{ VOPNAME_IOCTL,	{ .error = fs_ioctl }			},
832da6c28aaSamw 	{ VOPNAME_GETATTR,	{ .vop_getattr = xattr_file_getattr }	},
833da6c28aaSamw 	{ VOPNAME_ACCESS,	{ .vop_access = xattr_file_access }	},
834da6c28aaSamw 	{ VOPNAME_READDIR,	{ .error = fs_notdir }			},
835da6c28aaSamw 	{ VOPNAME_SEEK,		{ .vop_seek = fs_seek }			},
836da6c28aaSamw 	{ VOPNAME_INACTIVE,	{ .vop_inactive = gfs_vop_inactive }	},
837da6c28aaSamw 	{ VOPNAME_FID,		{ .vop_fid = xattr_common_fid }		},
838da6c28aaSamw 	{ VOPNAME_PATHCONF,	{ .vop_pathconf = xattr_file_pathconf }	},
839da6c28aaSamw 	{ VOPNAME_PUTPAGE,	{ .error = fs_putpage }			},
840da6c28aaSamw 	{ VOPNAME_FSYNC,	{ .error = fs_fsync }			},
841da6c28aaSamw 	{ NULL }
842da6c28aaSamw };
843da6c28aaSamw 
844da6c28aaSamw vnode_t *
xattr_mkfile(vnode_t * pvp,xattr_view_t xattr_view)845da6c28aaSamw xattr_mkfile(vnode_t *pvp, xattr_view_t xattr_view)
846da6c28aaSamw {
847da6c28aaSamw 	vnode_t *vp;
848da6c28aaSamw 	xattr_file_t *np;
849da6c28aaSamw 
850da6c28aaSamw 	vp = gfs_file_create(sizeof (xattr_file_t), pvp, xattr_file_ops);
851da6c28aaSamw 	np = vp->v_data;
852da6c28aaSamw 	np->xattr_view = xattr_view;
853da6c28aaSamw 	vp->v_flag |= V_SYSATTR;
854da6c28aaSamw 	return (vp);
855da6c28aaSamw }
856da6c28aaSamw 
857da6c28aaSamw vnode_t *
xattr_mkfile_ro(vnode_t * pvp)858da6c28aaSamw xattr_mkfile_ro(vnode_t *pvp)
859da6c28aaSamw {
860da6c28aaSamw 	return (xattr_mkfile(pvp, XATTR_VIEW_READONLY));
861da6c28aaSamw }
862da6c28aaSamw 
863da6c28aaSamw vnode_t *
xattr_mkfile_rw(vnode_t * pvp)864da6c28aaSamw xattr_mkfile_rw(vnode_t *pvp)
865da6c28aaSamw {
866da6c28aaSamw 	return (xattr_mkfile(pvp, XATTR_VIEW_READWRITE));
867da6c28aaSamw }
868da6c28aaSamw 
869da6c28aaSamw vnodeops_t *xattr_dir_ops;
870da6c28aaSamw 
871da6c28aaSamw static gfs_dirent_t xattr_dirents[] = {
872da6c28aaSamw 	{ VIEW_READONLY, xattr_mkfile_ro, GFS_CACHE_VNODE, },
873da6c28aaSamw 	{ VIEW_READWRITE, xattr_mkfile_rw, GFS_CACHE_VNODE, },
874da6c28aaSamw 	{ NULL },
875da6c28aaSamw };
876da6c28aaSamw 
877da6c28aaSamw #define	XATTRDIR_NENTS	((sizeof (xattr_dirents) / sizeof (gfs_dirent_t)) - 1)
878da6c28aaSamw 
879da6c28aaSamw static int
is_sattr_name(char * s)880da6c28aaSamw is_sattr_name(char *s)
881da6c28aaSamw {
882da6c28aaSamw 	int i;
883da6c28aaSamw 
884da6c28aaSamw 	for (i = 0; i < XATTRDIR_NENTS; ++i) {
885da6c28aaSamw 		if (strcmp(s, xattr_dirents[i].gfse_name) == 0) {
886da6c28aaSamw 			return (1);
887da6c28aaSamw 		}
888da6c28aaSamw 	}
889da6c28aaSamw 	return (0);
890da6c28aaSamw }
891da6c28aaSamw 
892b38f0970Sck /*
893b38f0970Sck  * Given the name of an extended attribute file, determine if there is a
894b38f0970Sck  * normalization conflict with a sysattr view name.
895b38f0970Sck  */
896b38f0970Sck int
xattr_sysattr_casechk(char * s)897b38f0970Sck xattr_sysattr_casechk(char *s)
898b38f0970Sck {
899b38f0970Sck 	int i;
900b38f0970Sck 
901b38f0970Sck 	for (i = 0; i < XATTRDIR_NENTS; ++i) {
902b38f0970Sck 		if (strcasecmp(s, xattr_dirents[i].gfse_name) == 0)
903b38f0970Sck 			return (1);
904b38f0970Sck 	}
905b38f0970Sck 	return (0);
906b38f0970Sck }
907b38f0970Sck 
908da6c28aaSamw static int
xattr_copy(vnode_t * sdvp,char * snm,vnode_t * tdvp,char * tnm,cred_t * cr,caller_context_t * ct)909da6c28aaSamw xattr_copy(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
910da6c28aaSamw     cred_t *cr, caller_context_t *ct)
911da6c28aaSamw {
912da6c28aaSamw 	xvattr_t xvattr;
913da6c28aaSamw 	vnode_t *pdvp;
914da6c28aaSamw 	int error;
915da6c28aaSamw 
916da6c28aaSamw 	/*
917da6c28aaSamw 	 * Only copy system attrs if the views are the same
918da6c28aaSamw 	 */
919da6c28aaSamw 	if (strcmp(snm, tnm) != 0)
920da6c28aaSamw 		return (EINVAL);
921da6c28aaSamw 
922da6c28aaSamw 	xva_init(&xvattr);
923da6c28aaSamw 
924da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_SYSTEM);
925da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_READONLY);
926da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_HIDDEN);
927da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_ARCHIVE);
928da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_APPENDONLY);
929da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_NOUNLINK);
930da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_IMMUTABLE);
931da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_NODUMP);
932da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
933da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
934da6c28aaSamw 	XVA_SET_REQ(&xvattr, XAT_CREATETIME);
9357a286c47SDai Ngo 	XVA_SET_REQ(&xvattr, XAT_REPARSE);
936fd9ee8b5Sjoyce mcintosh 	XVA_SET_REQ(&xvattr, XAT_OFFLINE);
937fd9ee8b5Sjoyce mcintosh 	XVA_SET_REQ(&xvattr, XAT_SPARSE);
938da6c28aaSamw 
939da6c28aaSamw 	pdvp = gfs_file_parent(sdvp);
940da6c28aaSamw 	error = VOP_GETATTR(pdvp, &xvattr.xva_vattr, 0, cr, ct);
941da6c28aaSamw 	if (error)
942da6c28aaSamw 		return (error);
943da6c28aaSamw 
944da6c28aaSamw 	pdvp = gfs_file_parent(tdvp);
945da6c28aaSamw 	error = VOP_SETATTR(pdvp, &xvattr.xva_vattr, 0, cr, ct);
946da6c28aaSamw 	return (error);
947da6c28aaSamw }
948da6c28aaSamw 
9499b247029SGordon Ross /*
9509b247029SGordon Ross  * Get the "real" XATTR directory associtated with the GFS XATTR directory.
9519b247029SGordon Ross  * Note: This does NOT take any additional hold on the returned real_vp,
9529b247029SGordon Ross  * because when this lookup succeeds we save the result in xattr_realvp
9539b247029SGordon Ross  * and keep that hold until the GFS XATTR directory goes inactive.
9549b247029SGordon Ross  */
955da6c28aaSamw static int
xattr_dir_realdir(vnode_t * gfs_dvp,vnode_t ** ret_vpp,int flags,cred_t * cr,caller_context_t * ct)9569b247029SGordon Ross xattr_dir_realdir(vnode_t *gfs_dvp, vnode_t **ret_vpp, int flags,
957da6c28aaSamw     cred_t *cr, caller_context_t *ct)
958da6c28aaSamw {
959da6c28aaSamw 	struct pathname pn;
9609b247029SGordon Ross 	char *nm = "";
9619b247029SGordon Ross 	xattr_dir_t *xattr_dir;
9629b247029SGordon Ross 	vnode_t *realvp;
9639b247029SGordon Ross 	int error;
964da6c28aaSamw 
9659b247029SGordon Ross 	*ret_vpp = NULL;
966da6c28aaSamw 
9679b247029SGordon Ross 	/*
9689b247029SGordon Ross 	 * Usually, we've already found the underlying XATTR directory
9699b247029SGordon Ross 	 * during some previous lookup and stored it in xattr_realvp.
9709b247029SGordon Ross 	 */
9719b247029SGordon Ross 	mutex_enter(&gfs_dvp->v_lock);
9729b247029SGordon Ross 	xattr_dir = gfs_dvp->v_data;
9739b247029SGordon Ross 	realvp = xattr_dir->xattr_realvp;
9749b247029SGordon Ross 	mutex_exit(&gfs_dvp->v_lock);
9759b247029SGordon Ross 	if (realvp != NULL) {
9769b247029SGordon Ross 		*ret_vpp = realvp;
9779b247029SGordon Ross 		return (0);
978da6c28aaSamw 	}
979da6c28aaSamw 
980da6c28aaSamw 	/*
9819b247029SGordon Ross 	 * Lookup the XATTR dir in the underlying FS, relative to our
9829b247029SGordon Ross 	 * "parent", which is the real object for which this GFS XATTR
9839b247029SGordon Ross 	 * directory was created.  Set the LOOKUP_HAVE_SYSATTR_DIR flag
9849b247029SGordon Ross 	 * so that we don't get into an infinite loop with fop_lookup
9859b247029SGordon Ross 	 * calling back to xattr_dir_lookup.
986da6c28aaSamw 	 */
9879b247029SGordon Ross 	error = pn_get(nm, UIO_SYSSPACE, &pn);
9889b247029SGordon Ross 	if (error != 0)
9899b247029SGordon Ross 		return (error);
9909b247029SGordon Ross 	error = VOP_LOOKUP(gfs_file_parent(gfs_dvp), nm, &realvp, &pn,
9919b247029SGordon Ross 	    flags | LOOKUP_HAVE_SYSATTR_DIR, rootvp, cr, ct, NULL, NULL);
992da6c28aaSamw 	pn_free(&pn);
9939b247029SGordon Ross 	if (error != 0)
9949b247029SGordon Ross 		return (error);
995da6c28aaSamw 
9969b247029SGordon Ross 	/*
9979b247029SGordon Ross 	 * Have the real XATTR directory.  Save it -- but first
9989b247029SGordon Ross 	 * check whether we lost a race doing the lookup.
9999b247029SGordon Ross 	 */
10009b247029SGordon Ross 	mutex_enter(&gfs_dvp->v_lock);
10019b247029SGordon Ross 	xattr_dir = gfs_dvp->v_data;
10029b247029SGordon Ross 	if (xattr_dir->xattr_realvp == NULL) {
10039b247029SGordon Ross 		/*
10049b247029SGordon Ross 		 * Note that the hold taken by the VOP_LOOKUP above is
10059b247029SGordon Ross 		 * retained from here until xattr_dir_inactive.
10069b247029SGordon Ross 		 */
10079b247029SGordon Ross 		xattr_dir->xattr_realvp = realvp;
10089b247029SGordon Ross 	} else {
10099b247029SGordon Ross 		/* We lost the race. */
10109b247029SGordon Ross 		VN_RELE(realvp);
10119b247029SGordon Ross 		realvp = xattr_dir->xattr_realvp;
10129b247029SGordon Ross 	}
10139b247029SGordon Ross 	mutex_exit(&gfs_dvp->v_lock);
10149b247029SGordon Ross 
10159b247029SGordon Ross 	*ret_vpp = realvp;
10169b247029SGordon Ross 	return (0);
1017da6c28aaSamw }
1018da6c28aaSamw 
1019da6c28aaSamw /* ARGSUSED */
1020da6c28aaSamw static int
xattr_dir_open(vnode_t ** vpp,int flags,cred_t * cr,caller_context_t * ct)1021da6c28aaSamw xattr_dir_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
1022da6c28aaSamw {
10239b247029SGordon Ross 	vnode_t *realvp;
10249b247029SGordon Ross 	int error;
10259b247029SGordon Ross 
1026da6c28aaSamw 	if (flags & FWRITE) {
1027da6c28aaSamw 		return (EACCES);
1028da6c28aaSamw 	}
1029da6c28aaSamw 
10309b247029SGordon Ross 	/*
10319b247029SGordon Ross 	 * If there is a real extended attribute directory,
10329b247029SGordon Ross 	 * let the underlying FS see the VOP_OPEN call;
10339b247029SGordon Ross 	 * otherwise just return zero.
10349b247029SGordon Ross 	 */
10359b247029SGordon Ross 	error = xattr_dir_realdir(*vpp, &realvp, LOOKUP_XATTR, cr, ct);
10369b247029SGordon Ross 	if (error == 0) {
10379b247029SGordon Ross 		error = VOP_OPEN(&realvp, flags, cr, ct);
10389b247029SGordon Ross 	} else {
10399b247029SGordon Ross 		error = 0;
10409b247029SGordon Ross 	}
10419b247029SGordon Ross 
10429b247029SGordon Ross 	return (error);
1043da6c28aaSamw }
1044da6c28aaSamw 
1045da6c28aaSamw /* ARGSUSED */
1046da6c28aaSamw static int
xattr_dir_close(vnode_t * vp,int flags,int count,offset_t off,cred_t * cr,caller_context_t * ct)10479b247029SGordon Ross xattr_dir_close(vnode_t *vp, int flags, int count, offset_t off, cred_t *cr,
1048da6c28aaSamw     caller_context_t *ct)
1049da6c28aaSamw {
10509b247029SGordon Ross 	vnode_t *realvp;
10519b247029SGordon Ross 	int error;
10529b247029SGordon Ross 
10539b247029SGordon Ross 	/*
10549b247029SGordon Ross 	 * If there is a real extended attribute directory,
10559b247029SGordon Ross 	 * let the underlying FS see the VOP_CLOSE call;
10569b247029SGordon Ross 	 * otherwise just return zero.
10579b247029SGordon Ross 	 */
10589b247029SGordon Ross 	error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct);
10599b247029SGordon Ross 	if (error == 0) {
10609b247029SGordon Ross 		error = VOP_CLOSE(realvp, flags, count, off, cr, ct);
10619b247029SGordon Ross 	} else {
10629b247029SGordon Ross 		error = 0;
10639b247029SGordon Ross 	}
10649b247029SGordon Ross 
10659b247029SGordon Ross 	return (error);
1066da6c28aaSamw }
1067da6c28aaSamw 
106800ba712dSGarima Tripathi /*
106900ba712dSGarima Tripathi  * Retrieve the attributes on an xattr directory.  If there is a "real"
107000ba712dSGarima Tripathi  * xattr directory, use that.  Otherwise, get the attributes (represented
107100ba712dSGarima Tripathi  * by PARENT_ATTRMASK) from the "parent" node and fill in the rest.  Note
107200ba712dSGarima Tripathi  * that VOP_GETATTR() could turn off bits in the va_mask.
107300ba712dSGarima Tripathi  */
107400ba712dSGarima Tripathi 
107500ba712dSGarima Tripathi #define	PARENT_ATTRMASK	(AT_UID|AT_GID|AT_RDEV|AT_CTIME|AT_MTIME)
107600ba712dSGarima Tripathi 
1077da6c28aaSamw /* ARGSUSED */
1078da6c28aaSamw static int
xattr_dir_getattr(vnode_t * vp,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)1079da6c28aaSamw xattr_dir_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
1080da6c28aaSamw     caller_context_t *ct)
1081da6c28aaSamw {
1082da6c28aaSamw 	timestruc_t now;
1083da6c28aaSamw 	vnode_t *pvp;
1084da6c28aaSamw 	int error;
1085da6c28aaSamw 
1086da6c28aaSamw 	error = xattr_dir_realdir(vp, &pvp, LOOKUP_XATTR, cr, ct);
1087da6c28aaSamw 	if (error == 0) {
1088da6c28aaSamw 		error = VOP_GETATTR(pvp, vap, 0, cr, ct);
1089da6c28aaSamw 		if (error) {
1090da6c28aaSamw 			return (error);
1091da6c28aaSamw 		}
1092da6c28aaSamw 		vap->va_nlink += XATTRDIR_NENTS;
1093da6c28aaSamw 		vap->va_size += XATTRDIR_NENTS;
1094da6c28aaSamw 		return (0);
1095da6c28aaSamw 	}
1096da6c28aaSamw 
1097da6c28aaSamw 	/*
1098da6c28aaSamw 	 * There is no real xattr directory.  Cobble together
109900ba712dSGarima Tripathi 	 * an entry using info from the parent object (if needed)
110000ba712dSGarima Tripathi 	 * plus information common to all xattrs.
1101da6c28aaSamw 	 */
110200ba712dSGarima Tripathi 	if (vap->va_mask & PARENT_ATTRMASK) {
110300ba712dSGarima Tripathi 		vattr_t pvattr;
110400ba712dSGarima Tripathi 		uint_t  off_bits;
110500ba712dSGarima Tripathi 
110600ba712dSGarima Tripathi 		pvp = gfs_file_parent(vp);
110700ba712dSGarima Tripathi 		(void) memset(&pvattr, 0, sizeof (pvattr));
110800ba712dSGarima Tripathi 		pvattr.va_mask = PARENT_ATTRMASK;
110900ba712dSGarima Tripathi 		error = VOP_GETATTR(pvp, &pvattr, 0, cr, ct);
111000ba712dSGarima Tripathi 		if (error) {
111100ba712dSGarima Tripathi 			return (error);
111200ba712dSGarima Tripathi 		}
111300ba712dSGarima Tripathi 
111400ba712dSGarima Tripathi 		/*
111500ba712dSGarima Tripathi 		 * VOP_GETATTR() might have turned off some bits in
111600ba712dSGarima Tripathi 		 * pvattr.va_mask.  This means that the underlying
111700ba712dSGarima Tripathi 		 * file system couldn't process those attributes.
111800ba712dSGarima Tripathi 		 * We need to make sure those bits get turned off
111900ba712dSGarima Tripathi 		 * in the vattr_t structure that gets passed back
112000ba712dSGarima Tripathi 		 * to the caller.  Figure out which bits were turned
112100ba712dSGarima Tripathi 		 * off (if any) then set pvattr.va_mask before it
112200ba712dSGarima Tripathi 		 * gets copied to the vattr_t that the caller sees.
112300ba712dSGarima Tripathi 		 */
112400ba712dSGarima Tripathi 		off_bits = (pvattr.va_mask ^ PARENT_ATTRMASK) & PARENT_ATTRMASK;
112500ba712dSGarima Tripathi 		pvattr.va_mask = vap->va_mask & ~off_bits;
112600ba712dSGarima Tripathi 		*vap = pvattr;
1127da6c28aaSamw 	}
112800ba712dSGarima Tripathi 
1129da6c28aaSamw 	vap->va_type = VDIR;
1130da6c28aaSamw 	vap->va_mode = MAKEIMODE(vap->va_type, S_ISVTX | 0777);
1131da6c28aaSamw 	vap->va_fsid = vp->v_vfsp->vfs_dev;
1132da6c28aaSamw 	vap->va_nodeid = gfs_file_inode(vp);
1133da6c28aaSamw 	vap->va_nlink = XATTRDIR_NENTS+2;
1134da6c28aaSamw 	vap->va_size = vap->va_nlink;
1135da6c28aaSamw 	gethrestime(&now);
1136da6c28aaSamw 	vap->va_atime = now;
1137da6c28aaSamw 	vap->va_blksize = 0;
1138da6c28aaSamw 	vap->va_nblocks = 0;
1139da6c28aaSamw 	vap->va_seq = 0;
1140da6c28aaSamw 	return (0);
1141da6c28aaSamw }
1142da6c28aaSamw 
1143da6c28aaSamw static int
xattr_dir_setattr(vnode_t * vp,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)1144da6c28aaSamw xattr_dir_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
1145da6c28aaSamw     caller_context_t *ct)
1146da6c28aaSamw {
1147da6c28aaSamw 	vnode_t *realvp;
1148da6c28aaSamw 	int error;
1149da6c28aaSamw 
1150da6c28aaSamw 	/*
1151da6c28aaSamw 	 * If there is a real xattr directory, do the setattr there.
1152da6c28aaSamw 	 * Otherwise, just return success.  The GFS directory is transient,
1153da6c28aaSamw 	 * and any setattr changes can disappear anyway.
1154da6c28aaSamw 	 */
1155da6c28aaSamw 	error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct);
1156da6c28aaSamw 	if (error == 0) {
1157da6c28aaSamw 		error = VOP_SETATTR(realvp, vap, flags, cr, ct);
1158da6c28aaSamw 	}
1159da6c28aaSamw 	if (error == ENOENT) {
1160da6c28aaSamw 		error = 0;
1161da6c28aaSamw 	}
1162da6c28aaSamw 	return (error);
1163da6c28aaSamw }
1164da6c28aaSamw 
1165da6c28aaSamw /* ARGSUSED */
1166da6c28aaSamw static int
xattr_dir_access(vnode_t * vp,int mode,int flags,cred_t * cr,caller_context_t * ct)1167da6c28aaSamw xattr_dir_access(vnode_t *vp, int mode, int flags, cred_t *cr,
1168da6c28aaSamw     caller_context_t *ct)
1169da6c28aaSamw {
1170da6c28aaSamw 	int error;
1171da6c28aaSamw 	vnode_t *realvp = NULL;
1172da6c28aaSamw 
1173da6c28aaSamw 	if (mode & VWRITE) {
1174da6c28aaSamw 		return (EACCES);
1175da6c28aaSamw 	}
1176da6c28aaSamw 
11779b247029SGordon Ross 	error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct);
11784286ffaeSGordon Ross 	if ((error == ENOENT || error == EINVAL)) {
11794286ffaeSGordon Ross 		/*
11804286ffaeSGordon Ross 		 * These errors mean there's no "real" xattr dir.
11814286ffaeSGordon Ross 		 * The GFS xattr dir always allows access.
11824286ffaeSGordon Ross 		 */
11834286ffaeSGordon Ross 		return (0);
11849b247029SGordon Ross 	}
11854286ffaeSGordon Ross 	if (error != 0) {
11864286ffaeSGordon Ross 		/*
11874286ffaeSGordon Ross 		 * The "real" xattr dir was not accessible.
11884286ffaeSGordon Ross 		 */
11894286ffaeSGordon Ross 		return (error);
11904286ffaeSGordon Ross 	}
11914286ffaeSGordon Ross 	/*
11924286ffaeSGordon Ross 	 * We got the "real" xattr dir.
11934286ffaeSGordon Ross 	 * Pass through the access call.
11944286ffaeSGordon Ross 	 */
11954286ffaeSGordon Ross 	error = VOP_ACCESS(realvp, mode, flags, cr, ct);
11969b247029SGordon Ross 
11979b247029SGordon Ross 	return (error);
1198da6c28aaSamw }
1199da6c28aaSamw 
1200da6c28aaSamw static int
xattr_dir_create(vnode_t * dvp,char * name,vattr_t * vap,vcexcl_t excl,int mode,vnode_t ** vpp,cred_t * cr,int flag,caller_context_t * ct,vsecattr_t * vsecp)1201da6c28aaSamw xattr_dir_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl,
1202da6c28aaSamw     int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct,
1203da6c28aaSamw     vsecattr_t *vsecp)
1204da6c28aaSamw {
1205da6c28aaSamw 	vnode_t *pvp;
1206da6c28aaSamw 	int error;
1207da6c28aaSamw 
1208da6c28aaSamw 	*vpp = NULL;
1209da6c28aaSamw 
1210da6c28aaSamw 	/*
1211da6c28aaSamw 	 * Don't allow creation of extended attributes with sysattr names.
1212da6c28aaSamw 	 */
1213da6c28aaSamw 	if (is_sattr_name(name)) {
1214ab04eb8eStimh 		return (gfs_dir_lookup(dvp, name, vpp, cr, 0, NULL, NULL));
1215da6c28aaSamw 	}
1216da6c28aaSamw 
1217da6c28aaSamw 	error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR|CREATE_XATTR_DIR,
1218da6c28aaSamw 	    cr, ct);
1219da6c28aaSamw 	if (error == 0) {
1220da6c28aaSamw 		error = VOP_CREATE(pvp, name, vap, excl, mode, vpp, cr, flag,
1221da6c28aaSamw 		    ct, vsecp);
1222da6c28aaSamw 	}
1223da6c28aaSamw 	return (error);
1224da6c28aaSamw }
1225da6c28aaSamw 
1226da6c28aaSamw static int
xattr_dir_remove(vnode_t * dvp,char * name,cred_t * cr,caller_context_t * ct,int flags)1227da6c28aaSamw xattr_dir_remove(vnode_t *dvp, char *name, cred_t *cr, caller_context_t *ct,
1228da6c28aaSamw     int flags)
1229da6c28aaSamw {
1230da6c28aaSamw 	vnode_t *pvp;
1231da6c28aaSamw 	int error;
1232da6c28aaSamw 
1233da6c28aaSamw 	if (is_sattr_name(name)) {
1234da6c28aaSamw 		return (EACCES);
1235da6c28aaSamw 	}
1236da6c28aaSamw 
1237da6c28aaSamw 	error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR, cr, ct);
1238da6c28aaSamw 	if (error == 0) {
1239da6c28aaSamw 		error = VOP_REMOVE(pvp, name, cr, ct, flags);
1240da6c28aaSamw 	}
1241da6c28aaSamw 	return (error);
1242da6c28aaSamw }
1243da6c28aaSamw 
1244da6c28aaSamw static int
xattr_dir_link(vnode_t * tdvp,vnode_t * svp,char * name,cred_t * cr,caller_context_t * ct,int flags)1245da6c28aaSamw xattr_dir_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr,
1246da6c28aaSamw     caller_context_t *ct, int flags)
1247da6c28aaSamw {
1248da6c28aaSamw 	vnode_t *pvp;
1249da6c28aaSamw 	int error;
1250da6c28aaSamw 
1251da6c28aaSamw 	if (svp->v_flag & V_SYSATTR) {
1252da6c28aaSamw 		return (EINVAL);
1253da6c28aaSamw 	}
1254da6c28aaSamw 
1255da6c28aaSamw 	error = xattr_dir_realdir(tdvp, &pvp, LOOKUP_XATTR, cr, ct);
1256da6c28aaSamw 	if (error == 0) {
1257da6c28aaSamw 		error = VOP_LINK(pvp, svp, name, cr, ct, flags);
1258da6c28aaSamw 	}
1259da6c28aaSamw 	return (error);
1260da6c28aaSamw }
1261da6c28aaSamw 
1262da6c28aaSamw static int
xattr_dir_rename(vnode_t * sdvp,char * snm,vnode_t * tdvp,char * tnm,cred_t * cr,caller_context_t * ct,int flags)1263da6c28aaSamw xattr_dir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
1264da6c28aaSamw     cred_t *cr, caller_context_t *ct, int flags)
1265da6c28aaSamw {
1266da6c28aaSamw 	vnode_t *spvp, *tpvp;
1267da6c28aaSamw 	int error;
1268da6c28aaSamw 
1269da6c28aaSamw 	if (is_sattr_name(snm) || is_sattr_name(tnm))
1270da6c28aaSamw 		return (xattr_copy(sdvp, snm, tdvp, tnm, cr, ct));
1271da6c28aaSamw 	/*
1272da6c28aaSamw 	 * We know that sdvp is a GFS dir, or we wouldn't be here.
1273da6c28aaSamw 	 * Get the real unnamed directory.
1274da6c28aaSamw 	 */
1275da6c28aaSamw 	error = xattr_dir_realdir(sdvp, &spvp, LOOKUP_XATTR, cr, ct);
1276da6c28aaSamw 	if (error) {
1277da6c28aaSamw 		return (error);
1278da6c28aaSamw 	}
1279da6c28aaSamw 
1280da6c28aaSamw 	if (sdvp == tdvp) {
1281da6c28aaSamw 		/*
1282da6c28aaSamw 		 * If the source and target are the same GFS directory, the
1283da6c28aaSamw 		 * underlying unnamed source and target dir will be the same.
1284da6c28aaSamw 		 */
1285da6c28aaSamw 		tpvp = spvp;
1286da6c28aaSamw 	} else if (tdvp->v_flag & V_SYSATTR) {
1287da6c28aaSamw 		/*
1288da6c28aaSamw 		 * If the target dir is a different GFS directory,
1289da6c28aaSamw 		 * find its underlying unnamed dir.
1290da6c28aaSamw 		 */
1291da6c28aaSamw 		error = xattr_dir_realdir(tdvp, &tpvp, LOOKUP_XATTR, cr, ct);
1292da6c28aaSamw 		if (error) {
1293da6c28aaSamw 			return (error);
1294da6c28aaSamw 		}
1295da6c28aaSamw 	} else {
1296da6c28aaSamw 		/*
1297da6c28aaSamw 		 * Target dir is outside of GFS, pass it on through.
1298da6c28aaSamw 		 */
1299da6c28aaSamw 		tpvp = tdvp;
1300da6c28aaSamw 	}
1301da6c28aaSamw 
1302da6c28aaSamw 	error = VOP_RENAME(spvp, snm, tpvp, tnm, cr, ct, flags);
1303da6c28aaSamw 
1304da6c28aaSamw 	return (error);
1305da6c28aaSamw }
1306da6c28aaSamw 
1307b38f0970Sck /*
1308b38f0970Sck  * readdir_xattr_casecmp: given a system attribute name, see if there
1309b38f0970Sck  * is a real xattr with the same normalized name.
1310b38f0970Sck  */
1311b38f0970Sck static int
readdir_xattr_casecmp(vnode_t * dvp,char * nm,cred_t * cr,caller_context_t * ct,int * eflags)1312b38f0970Sck readdir_xattr_casecmp(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
1313b38f0970Sck     int *eflags)
1314b38f0970Sck {
1315b38f0970Sck 	int error;
1316b38f0970Sck 	vnode_t *vp;
1317b38f0970Sck 	struct pathname pn;
1318b38f0970Sck 
1319b38f0970Sck 	*eflags = 0;
1320b38f0970Sck 
1321b38f0970Sck 	error = pn_get(nm, UIO_SYSSPACE, &pn);
1322b38f0970Sck 	if (error == 0) {
1323ab04eb8eStimh 		error = VOP_LOOKUP(dvp, nm, &vp, &pn,
1324ab04eb8eStimh 		    FIGNORECASE, rootvp, cr, ct, NULL, NULL);
1325b38f0970Sck 		if (error == 0) {
1326b38f0970Sck 			*eflags = ED_CASE_CONFLICT;
1327b38f0970Sck 			VN_RELE(vp);
1328b38f0970Sck 		} else if (error == ENOENT) {
1329b38f0970Sck 			error = 0;
1330b38f0970Sck 		}
1331b38f0970Sck 		pn_free(&pn);
1332b38f0970Sck 	}
1333b38f0970Sck 
1334b38f0970Sck 	return (error);
1335b38f0970Sck }
1336b38f0970Sck 
1337da6c28aaSamw static int
xattr_dir_readdir(vnode_t * dvp,uio_t * uiop,cred_t * cr,int * eofp,caller_context_t * ct,int flags)1338da6c28aaSamw xattr_dir_readdir(vnode_t *dvp, uio_t *uiop, cred_t *cr, int *eofp,
1339da6c28aaSamw     caller_context_t *ct, int flags)
1340da6c28aaSamw {
1341da6c28aaSamw 	vnode_t *pvp;
1342da6c28aaSamw 	int error;
1343f5f5959bSck 	int local_eof;
1344da6c28aaSamw 	int reset_off = 0;
1345da6c28aaSamw 	int has_xattrs = 0;
1346da6c28aaSamw 
1347da6c28aaSamw 	if (eofp == NULL) {
1348da6c28aaSamw 		eofp = &local_eof;
1349da6c28aaSamw 	}
1350f5f5959bSck 	*eofp = 0;
1351da6c28aaSamw 
1352da6c28aaSamw 	/*
1353da6c28aaSamw 	 * See if there is a real extended attribute directory.
1354da6c28aaSamw 	 */
1355da6c28aaSamw 	error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR, cr, ct);
1356da6c28aaSamw 	if (error == 0) {
1357da6c28aaSamw 		has_xattrs = 1;
1358da6c28aaSamw 	}
1359da6c28aaSamw 
1360da6c28aaSamw 	/*
1361da6c28aaSamw 	 * Start by reading up the static entries.
1362da6c28aaSamw 	 */
1363da6c28aaSamw 	if (uiop->uio_loffset == 0) {
1364b38f0970Sck 		ino64_t pino, ino;
1365b38f0970Sck 		offset_t off;
1366b38f0970Sck 		gfs_dir_t *dp = dvp->v_data;
1367b38f0970Sck 		gfs_readdir_state_t gstate;
1368b38f0970Sck 
1369da6c28aaSamw 		if (has_xattrs) {
1370da6c28aaSamw 			/*
1371da6c28aaSamw 			 * If there is a real xattr dir, skip . and ..
1372da6c28aaSamw 			 * in the GFS dir.  We'll pick them up below
1373da6c28aaSamw 			 * when we call into the underlying fs.
1374da6c28aaSamw 			 */
1375da6c28aaSamw 			uiop->uio_loffset = GFS_STATIC_ENTRY_OFFSET;
1376da6c28aaSamw 		}
1377b38f0970Sck 		error = gfs_get_parent_ino(dvp, cr, ct, &pino, &ino);
1378b38f0970Sck 		if (error == 0) {
1379b38f0970Sck 			error = gfs_readdir_init(&gstate, dp->gfsd_maxlen, 1,
1380b38f0970Sck 			    uiop, pino, ino, flags);
1381b38f0970Sck 		}
1382da6c28aaSamw 		if (error) {
1383b38f0970Sck 			return (error);
1384b38f0970Sck 		}
1385b38f0970Sck 
1386b38f0970Sck 		while ((error = gfs_readdir_pred(&gstate, uiop, &off)) == 0 &&
1387b38f0970Sck 		    !*eofp) {
1388b38f0970Sck 			if (off >= 0 && off < dp->gfsd_nstatic) {
1389ab04eb8eStimh 				int eflags;
1390b38f0970Sck 
1391b38f0970Sck 				/*
1392b38f0970Sck 				 * Check to see if this sysattr set name has a
1393b38f0970Sck 				 * case-insensitive conflict with a real xattr
1394b38f0970Sck 				 * name.
1395b38f0970Sck 				 */
1396ab04eb8eStimh 				eflags = 0;
1397b38f0970Sck 				if ((flags & V_RDDIR_ENTFLAGS) && has_xattrs) {
1398b38f0970Sck 					error = readdir_xattr_casecmp(pvp,
1399b38f0970Sck 					    dp->gfsd_static[off].gfse_name,
1400b38f0970Sck 					    cr, ct, &eflags);
1401b38f0970Sck 					if (error)
1402b38f0970Sck 						break;
1403b38f0970Sck 				}
1404b38f0970Sck 				ino = dp->gfsd_inode(dvp, off);
1405b38f0970Sck 
1406b38f0970Sck 				error = gfs_readdir_emit(&gstate, uiop, off,
1407b38f0970Sck 				    ino, dp->gfsd_static[off].gfse_name,
1408b38f0970Sck 				    eflags);
1409b38f0970Sck 				if (error)
1410b38f0970Sck 					break;
1411b38f0970Sck 			} else {
1412b38f0970Sck 				*eofp = 1;
1413da6c28aaSamw 			}
1414b38f0970Sck 		}
1415b38f0970Sck 
1416b38f0970Sck 		error = gfs_readdir_fini(&gstate, error, eofp, *eofp);
1417b38f0970Sck 		if (error) {
1418da6c28aaSamw 			return (error);
1419da6c28aaSamw 		}
1420b38f0970Sck 
1421da6c28aaSamw 		/*
1422da6c28aaSamw 		 * We must read all of the static entries in the first
1423da6c28aaSamw 		 * call.  Otherwise we won't know if uio_loffset in a
1424da6c28aaSamw 		 * subsequent call refers to the static entries or to those
1425da6c28aaSamw 		 * in an underlying fs.
1426da6c28aaSamw 		 */
1427e802abbdSTim Haley 		if (*eofp == 0)
1428e802abbdSTim Haley 			return (EINVAL);
1429da6c28aaSamw 		reset_off = 1;
1430da6c28aaSamw 	}
1431da6c28aaSamw 
1432da6c28aaSamw 	if (!has_xattrs) {
1433da6c28aaSamw 		*eofp = 1;
1434da6c28aaSamw 		return (0);
1435da6c28aaSamw 	}
1436da6c28aaSamw 
1437da6c28aaSamw 	*eofp = 0;
1438da6c28aaSamw 	if (reset_off) {
1439da6c28aaSamw 		uiop->uio_loffset = 0;
1440da6c28aaSamw 	}
1441da6c28aaSamw 	(void) VOP_RWLOCK(pvp, V_WRITELOCK_FALSE, NULL);
1442da6c28aaSamw 	error = VOP_READDIR(pvp, uiop, cr, eofp, ct, flags);
1443da6c28aaSamw 	VOP_RWUNLOCK(pvp, V_WRITELOCK_FALSE, NULL);
1444da6c28aaSamw 
1445da6c28aaSamw 	return (error);
1446da6c28aaSamw }
1447da6c28aaSamw 
14489b247029SGordon Ross /*
14499b247029SGordon Ross  * Last reference on a (GFS) XATTR directory.
14509b247029SGordon Ross  *
14519b247029SGordon Ross  * If there's a real XATTR directory in the underlying FS, we will have
14529b247029SGordon Ross  * taken a hold on that directory in xattr_dir_realdir.  Now that the
14539b247029SGordon Ross  * last hold on the GFS directory is gone, it's time to release that
14549b247029SGordon Ross  * hold on the underlying XATTR directory.
14559b247029SGordon Ross  */
1456da6c28aaSamw /* ARGSUSED */
1457da6c28aaSamw static void
xattr_dir_inactive(vnode_t * vp,cred_t * cr,caller_context_t * ct)1458da6c28aaSamw xattr_dir_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
1459da6c28aaSamw {
1460be93bc99SVitaliy Gusev 	xattr_dir_t *dp;
1461da6c28aaSamw 
1462be93bc99SVitaliy Gusev 	dp = gfs_dir_inactive(vp);	/* will track v_count */
1463be93bc99SVitaliy Gusev 	if (dp != NULL) {
1464be93bc99SVitaliy Gusev 		/* vp was freed */
1465be93bc99SVitaliy Gusev 		if (dp->xattr_realvp != NULL)
1466be93bc99SVitaliy Gusev 			VN_RELE(dp->xattr_realvp);
1467be93bc99SVitaliy Gusev 
1468be93bc99SVitaliy Gusev 		kmem_free(dp, ((gfs_file_t *)dp)->gfs_size);
1469da6c28aaSamw 	}
1470da6c28aaSamw }
1471da6c28aaSamw 
1472da6c28aaSamw static int
xattr_dir_pathconf(vnode_t * vp,int cmd,ulong_t * valp,cred_t * cr,caller_context_t * ct)1473da6c28aaSamw xattr_dir_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
1474da6c28aaSamw     caller_context_t *ct)
1475da6c28aaSamw {
1476da6c28aaSamw 	switch (cmd) {
1477da6c28aaSamw 	case _PC_XATTR_EXISTS:
1478da6c28aaSamw 	case _PC_SATTR_ENABLED:
1479da6c28aaSamw 	case _PC_SATTR_EXISTS:
1480da6c28aaSamw 		*valp = 0;
1481da6c28aaSamw 		return (0);
1482da6c28aaSamw 	default:
1483da6c28aaSamw 		return (fs_pathconf(vp, cmd, valp, cr, ct));
1484da6c28aaSamw 	}
1485da6c28aaSamw }
1486da6c28aaSamw 
148768469adeSMark Shellenbaum /* ARGSUSED */
148868469adeSMark Shellenbaum static int
xattr_dir_realvp(vnode_t * vp,vnode_t ** realvp,caller_context_t * ct)148968469adeSMark Shellenbaum xattr_dir_realvp(vnode_t *vp, vnode_t **realvp, caller_context_t *ct)
149068469adeSMark Shellenbaum {
14919b247029SGordon Ross 	int error;
149268469adeSMark Shellenbaum 
14939b247029SGordon Ross 	error = xattr_dir_realdir(vp, realvp, LOOKUP_XATTR, kcred, NULL);
14949b247029SGordon Ross 	return (error);
14953fece860SMark Shellenbaum 
149668469adeSMark Shellenbaum }
149768469adeSMark Shellenbaum 
1498da6c28aaSamw static const fs_operation_def_t xattr_dir_tops[] = {
1499da6c28aaSamw 	{ VOPNAME_OPEN,		{ .vop_open = xattr_dir_open }		},
1500da6c28aaSamw 	{ VOPNAME_CLOSE,	{ .vop_close = xattr_dir_close }	},
1501da6c28aaSamw 	{ VOPNAME_IOCTL,	{ .error = fs_inval }			},
1502da6c28aaSamw 	{ VOPNAME_GETATTR,	{ .vop_getattr = xattr_dir_getattr }	},
1503da6c28aaSamw 	{ VOPNAME_SETATTR,	{ .vop_setattr = xattr_dir_setattr }	},
1504da6c28aaSamw 	{ VOPNAME_ACCESS,	{ .vop_access = xattr_dir_access }	},
1505da6c28aaSamw 	{ VOPNAME_READDIR,	{ .vop_readdir = xattr_dir_readdir }	},
1506da6c28aaSamw 	{ VOPNAME_LOOKUP,	{ .vop_lookup = gfs_vop_lookup }	},
1507da6c28aaSamw 	{ VOPNAME_CREATE,	{ .vop_create = xattr_dir_create }	},
1508da6c28aaSamw 	{ VOPNAME_REMOVE,	{ .vop_remove = xattr_dir_remove }	},
1509da6c28aaSamw 	{ VOPNAME_LINK,		{ .vop_link = xattr_dir_link }		},
1510da6c28aaSamw 	{ VOPNAME_RENAME,	{ .vop_rename = xattr_dir_rename }	},
1511da6c28aaSamw 	{ VOPNAME_MKDIR,	{ .error = fs_inval }			},
1512da6c28aaSamw 	{ VOPNAME_SEEK,		{ .vop_seek = fs_seek }			},
1513da6c28aaSamw 	{ VOPNAME_INACTIVE,	{ .vop_inactive = xattr_dir_inactive }	},
1514da6c28aaSamw 	{ VOPNAME_FID,		{ .vop_fid = xattr_common_fid }		},
1515da6c28aaSamw 	{ VOPNAME_PATHCONF,	{ .vop_pathconf = xattr_dir_pathconf }	},
151668469adeSMark Shellenbaum 	{ VOPNAME_REALVP,	{ .vop_realvp = xattr_dir_realvp } },
1517da6c28aaSamw 	{ NULL, NULL }
1518da6c28aaSamw };
1519da6c28aaSamw 
1520da6c28aaSamw static gfs_opsvec_t xattr_opsvec[] = {
1521da6c28aaSamw 	{ "xattr dir", xattr_dir_tops, &xattr_dir_ops },
1522da6c28aaSamw 	{ "system attributes", xattr_file_tops, &xattr_file_ops },
1523da6c28aaSamw 	{ NULL, NULL, NULL }
1524da6c28aaSamw };
1525da6c28aaSamw 
15269b247029SGordon Ross /*
15279b247029SGordon Ross  * Callback supporting lookup in a GFS XATTR directory.
15289b247029SGordon Ross  */
1529da6c28aaSamw static int
xattr_lookup_cb(vnode_t * vp,const char * nm,vnode_t ** vpp,ino64_t * inop,cred_t * cr,int flags,int * deflags,pathname_t * rpnp)1530da6c28aaSamw xattr_lookup_cb(vnode_t *vp, const char *nm, vnode_t **vpp, ino64_t *inop,
1531ab04eb8eStimh     cred_t *cr, int flags, int *deflags, pathname_t *rpnp)
1532da6c28aaSamw {
1533da6c28aaSamw 	vnode_t *pvp;
1534da6c28aaSamw 	struct pathname pn;
1535da6c28aaSamw 	int error;
1536da6c28aaSamw 
1537da6c28aaSamw 	*vpp = NULL;
1538da6c28aaSamw 	*inop = 0;
1539da6c28aaSamw 
15409b247029SGordon Ross 	error = xattr_dir_realdir(vp, &pvp, LOOKUP_XATTR, cr, NULL);
1541da6c28aaSamw 
1542da6c28aaSamw 	/*
1543da6c28aaSamw 	 * Return ENOENT for EACCES requests during lookup.  Once an
1544da6c28aaSamw 	 * attribute create is attempted EACCES will be returned.
1545da6c28aaSamw 	 */
1546da6c28aaSamw 	if (error) {
1547da6c28aaSamw 		if (error == EACCES)
1548da6c28aaSamw 			return (ENOENT);
1549da6c28aaSamw 		return (error);
1550da6c28aaSamw 	}
1551da6c28aaSamw 
1552da6c28aaSamw 	error = pn_get((char *)nm, UIO_SYSSPACE, &pn);
1553da6c28aaSamw 	if (error == 0) {
1554ab04eb8eStimh 		error = VOP_LOOKUP(pvp, (char *)nm, vpp, &pn, flags, rootvp,
1555ab04eb8eStimh 		    cr, NULL, deflags, rpnp);
1556da6c28aaSamw 		pn_free(&pn);
1557da6c28aaSamw 	}
1558da6c28aaSamw 
1559da6c28aaSamw 	return (error);
1560da6c28aaSamw }
1561da6c28aaSamw 
1562da6c28aaSamw /* ARGSUSED */
1563da6c28aaSamw static ino64_t
xattrdir_do_ino(vnode_t * vp,int index)1564da6c28aaSamw xattrdir_do_ino(vnode_t *vp, int index)
1565da6c28aaSamw {
1566da6c28aaSamw 	/*
1567da6c28aaSamw 	 * We use index 0 for the directory fid.  Start
1568da6c28aaSamw 	 * the file numbering at 1.
1569da6c28aaSamw 	 */
1570da6c28aaSamw 	return ((ino64_t)index+1);
1571da6c28aaSamw }
1572da6c28aaSamw 
1573da6c28aaSamw void
xattr_init(void)1574da6c28aaSamw xattr_init(void)
1575da6c28aaSamw {
1576da6c28aaSamw 	VERIFY(gfs_make_opsvec(xattr_opsvec) == 0);
1577da6c28aaSamw }
1578da6c28aaSamw 
15799b247029SGordon Ross /*
15809b247029SGordon Ross  * Get the XATTR dir for some file or directory.
15819b247029SGordon Ross  * See vnode.c: fop_lookup()
15829b247029SGordon Ross  *
15839b247029SGordon Ross  * Note this only gets the GFS XATTR directory.  We'll get the
15849b247029SGordon Ross  * real XATTR directory later, in xattr_dir_realdir.
15859b247029SGordon Ross  */
1586da6c28aaSamw int
xattr_dir_lookup(vnode_t * dvp,vnode_t ** vpp,int flags,cred_t * cr)1587da6c28aaSamw xattr_dir_lookup(vnode_t *dvp, vnode_t **vpp, int flags, cred_t *cr)
1588da6c28aaSamw {
1589da6c28aaSamw 	int error = 0;
1590da6c28aaSamw 
1591da6c28aaSamw 	*vpp = NULL;
1592da6c28aaSamw 
1593da6c28aaSamw 	if (dvp->v_type != VDIR && dvp->v_type != VREG)
1594da6c28aaSamw 		return (EINVAL);
1595da6c28aaSamw 
1596da6c28aaSamw 	mutex_enter(&dvp->v_lock);
1597da6c28aaSamw 
1598da6c28aaSamw 	/*
1599da6c28aaSamw 	 * If we're already in sysattr space, don't allow creation
1600da6c28aaSamw 	 * of another level of sysattrs.
1601da6c28aaSamw 	 */
1602da6c28aaSamw 	if (dvp->v_flag & V_SYSATTR) {
1603da6c28aaSamw 		mutex_exit(&dvp->v_lock);
1604da6c28aaSamw 		return (EINVAL);
1605da6c28aaSamw 	}
1606da6c28aaSamw 
1607da6c28aaSamw 	if (dvp->v_xattrdir != NULL) {
1608da6c28aaSamw 		*vpp = dvp->v_xattrdir;
1609da6c28aaSamw 		VN_HOLD(*vpp);
1610da6c28aaSamw 	} else {
1611da6c28aaSamw 		ulong_t val;
1612da6c28aaSamw 		int xattrs_allowed = dvp->v_vfsp->vfs_flag & VFS_XATTR;
1613da6c28aaSamw 		int sysattrs_allowed = 1;
1614da6c28aaSamw 
1615da6c28aaSamw 		/*
1616da6c28aaSamw 		 * We have to drop the lock on dvp.  gfs_dir_create will
1617da6c28aaSamw 		 * grab it for a VN_HOLD.
1618da6c28aaSamw 		 */
1619da6c28aaSamw 		mutex_exit(&dvp->v_lock);
1620da6c28aaSamw 
1621da6c28aaSamw 		/*
1622da6c28aaSamw 		 * If dvp allows xattr creation, but not sysattr
1623da6c28aaSamw 		 * creation, return the real xattr dir vp. We can't
1624da6c28aaSamw 		 * use the vfs feature mask here because _PC_SATTR_ENABLED
1625da6c28aaSamw 		 * has vnode-level granularity (e.g. .zfs).
1626da6c28aaSamw 		 */
1627da6c28aaSamw 		error = VOP_PATHCONF(dvp, _PC_SATTR_ENABLED, &val, cr, NULL);
1628da6c28aaSamw 		if (error != 0 || val == 0)
1629da6c28aaSamw 			sysattrs_allowed = 0;
1630da6c28aaSamw 
1631da6c28aaSamw 		if (!xattrs_allowed && !sysattrs_allowed)
1632da6c28aaSamw 			return (EINVAL);
1633da6c28aaSamw 
1634da6c28aaSamw 		if (!sysattrs_allowed) {
1635da6c28aaSamw 			struct pathname pn;
1636da6c28aaSamw 			char *nm = "";
1637da6c28aaSamw 
1638da6c28aaSamw 			error = pn_get(nm, UIO_SYSSPACE, &pn);
1639da6c28aaSamw 			if (error)
1640da6c28aaSamw 				return (error);
1641da6c28aaSamw 			error = VOP_LOOKUP(dvp, nm, vpp, &pn,
1642da6c28aaSamw 			    flags|LOOKUP_HAVE_SYSATTR_DIR, rootvp, cr, NULL,
1643da6c28aaSamw 			    NULL, NULL);
1644da6c28aaSamw 			pn_free(&pn);
1645da6c28aaSamw 			return (error);
1646da6c28aaSamw 		}
1647da6c28aaSamw 
1648da6c28aaSamw 		/*
1649da6c28aaSamw 		 * Note that we act as if we were given CREATE_XATTR_DIR,
1650da6c28aaSamw 		 * but only for creation of the GFS directory.
1651da6c28aaSamw 		 */
1652da6c28aaSamw 		*vpp = gfs_dir_create(
165368469adeSMark Shellenbaum 		    sizeof (xattr_dir_t), dvp, xattr_dir_ops, xattr_dirents,
1654da6c28aaSamw 		    xattrdir_do_ino, MAXNAMELEN, NULL, xattr_lookup_cb);
1655da6c28aaSamw 		mutex_enter(&dvp->v_lock);
1656da6c28aaSamw 		if (dvp->v_xattrdir != NULL) {
1657da6c28aaSamw 			/*
1658da6c28aaSamw 			 * We lost the race to create the xattr dir.
1659da6c28aaSamw 			 * Destroy this one, use the winner.  We can't
1660da6c28aaSamw 			 * just call VN_RELE(*vpp), because the vnode
1661da6c28aaSamw 			 * is only partially initialized.
1662da6c28aaSamw 			 */
1663da6c28aaSamw 			gfs_dir_t *dp = (*vpp)->v_data;
1664da6c28aaSamw 
1665da6c28aaSamw 			ASSERT((*vpp)->v_count == 1);
1666da6c28aaSamw 			vn_free(*vpp);
1667f4f14d92SVitaliy Gusev 			VN_RELE_LOCKED(dvp);
1668da6c28aaSamw 
1669da6c28aaSamw 			mutex_destroy(&dp->gfsd_lock);
1670da6c28aaSamw 			kmem_free(dp->gfsd_static,
1671da6c28aaSamw 			    dp->gfsd_nstatic * sizeof (gfs_dirent_t));
1672da6c28aaSamw 			kmem_free(dp, dp->gfsd_file.gfs_size);
1673da6c28aaSamw 
1674f4f14d92SVitaliy Gusev 			/* dvp was held by winner in gfs_dir_create */
1675da6c28aaSamw 			*vpp = dvp->v_xattrdir;
1676da6c28aaSamw 			VN_HOLD(*vpp);
1677da6c28aaSamw 		} else {
1678f4f14d92SVitaliy Gusev 			/* winner */
1679da6c28aaSamw 			(*vpp)->v_flag |= (V_XATTRDIR|V_SYSATTR);
1680da6c28aaSamw 			dvp->v_xattrdir = *vpp;
1681da6c28aaSamw 		}
1682da6c28aaSamw 	}
1683da6c28aaSamw 	mutex_exit(&dvp->v_lock);
1684da6c28aaSamw 
1685da6c28aaSamw 	return (error);
1686da6c28aaSamw }
1687da6c28aaSamw 
1688da6c28aaSamw int
xattr_dir_vget(vfs_t * vfsp,vnode_t ** vpp,fid_t * fidp)1689da6c28aaSamw xattr_dir_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
1690da6c28aaSamw {
1691da6c28aaSamw 	int error;
1692da6c28aaSamw 	vnode_t *pvp, *dvp;
1693da6c28aaSamw 	xattr_fid_t *xfidp;
1694da6c28aaSamw 	struct pathname pn;
1695da6c28aaSamw 	char *nm;
1696da6c28aaSamw 	uint16_t orig_len;
1697da6c28aaSamw 
1698da6c28aaSamw 	*vpp = NULL;
1699da6c28aaSamw 
1700da6c28aaSamw 	if (fidp->fid_len < XATTR_FIDSZ)
1701da6c28aaSamw 		return (EINVAL);
1702da6c28aaSamw 
1703da6c28aaSamw 	xfidp = (xattr_fid_t *)fidp;
1704da6c28aaSamw 	orig_len = fidp->fid_len;
1705da6c28aaSamw 	fidp->fid_len = xfidp->parent_len;
1706da6c28aaSamw 
1707da6c28aaSamw 	error = VFS_VGET(vfsp, &pvp, fidp);
1708da6c28aaSamw 	fidp->fid_len = orig_len;
1709da6c28aaSamw 	if (error)
1710da6c28aaSamw 		return (error);
1711da6c28aaSamw 
1712da6c28aaSamw 	/*
1713da6c28aaSamw 	 * Start by getting the GFS sysattr directory.	We might need
1714da6c28aaSamw 	 * to recreate it during the VOP_LOOKUP.
1715da6c28aaSamw 	 */
1716da6c28aaSamw 	nm = "";
1717da6c28aaSamw 	error = pn_get(nm, UIO_SYSSPACE, &pn);
1718da6c28aaSamw 	if (error) {
1719da6c28aaSamw 		VN_RELE(pvp);
1720da6c28aaSamw 		return (EINVAL);
1721da6c28aaSamw 	}
1722da6c28aaSamw 
1723da6c28aaSamw 	error = VOP_LOOKUP(pvp, nm, &dvp, &pn, LOOKUP_XATTR|CREATE_XATTR_DIR,
1724da6c28aaSamw 	    rootvp, CRED(), NULL, NULL, NULL);
1725da6c28aaSamw 	pn_free(&pn);
1726da6c28aaSamw 	VN_RELE(pvp);
1727da6c28aaSamw 	if (error)
1728da6c28aaSamw 		return (error);
1729da6c28aaSamw 
1730da6c28aaSamw 	if (xfidp->dir_offset == 0) {
1731da6c28aaSamw 		/*
1732da6c28aaSamw 		 * If we were looking for the directory, we're done.
1733da6c28aaSamw 		 */
1734da6c28aaSamw 		*vpp = dvp;
1735da6c28aaSamw 		return (0);
1736da6c28aaSamw 	}
1737da6c28aaSamw 
1738da6c28aaSamw 	if (xfidp->dir_offset > XATTRDIR_NENTS) {
1739da6c28aaSamw 		VN_RELE(dvp);
1740da6c28aaSamw 		return (EINVAL);
1741da6c28aaSamw 	}
1742da6c28aaSamw 
1743da6c28aaSamw 	nm = xattr_dirents[xfidp->dir_offset - 1].gfse_name;
1744da6c28aaSamw 
1745da6c28aaSamw 	error = pn_get(nm, UIO_SYSSPACE, &pn);
1746da6c28aaSamw 	if (error) {
1747da6c28aaSamw 		VN_RELE(dvp);
1748da6c28aaSamw 		return (EINVAL);
1749da6c28aaSamw 	}
1750da6c28aaSamw 
1751da6c28aaSamw 	error = VOP_LOOKUP(dvp, nm, vpp, &pn, 0, rootvp, CRED(), NULL,
1752da6c28aaSamw 	    NULL, NULL);
1753da6c28aaSamw 
1754da6c28aaSamw 	pn_free(&pn);
1755da6c28aaSamw 	VN_RELE(dvp);
1756da6c28aaSamw 
1757da6c28aaSamw 	return (error);
1758da6c28aaSamw }
1759