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