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