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