6914555 rfs4_op_rename() can double free converted_onm
[illumos-gate.git] / usr / src / uts / common / fs / xattr.c
blob070f43e5c66882f3142223b072b3f14c6b24183a
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
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>
50 typedef struct {
51 gfs_file_t xattr_gfs_private;
52 xattr_view_t xattr_view;
53 } xattr_file_t;
55 typedef struct {
56 gfs_dir_t xattr_gfs_private;
57 vnode_t *xattr_realvp; /* Only used for VOP_REALVP */
58 } xattr_dir_t;
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.
66 /* ARGSUSED */
67 static int
68 xattr_file_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
70 xattr_file_t *np = (*vpp)->v_data;
72 if ((np->xattr_view == XATTR_VIEW_READONLY) && (flags & FWRITE))
73 return (EACCES);
75 return (0);
78 /* ARGSUSED */
79 static int
80 xattr_file_access(vnode_t *vp, int mode, int flags, cred_t *cr,
81 caller_context_t *ct)
83 xattr_file_t *np = vp->v_data;
85 if ((np->xattr_view == XATTR_VIEW_READONLY) && (mode & VWRITE))
86 return (EACCES);
88 return (0);
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)
96 cleanlocks(vp, ddi_get_pid(), 0);
97 cleanshares(vp, ddi_get_pid());
98 return (0);
101 static int
102 xattr_common_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
104 xattr_fid_t *xfidp;
105 vnode_t *pvp, *savevp;
106 int error;
107 uint16_t orig_len;
109 if (fidp->fid_len < XATTR_FIDSZ) {
110 fidp->fid_len = XATTR_FIDSZ;
111 return (ENOSPC);
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);
119 mutex_exit(&savevp->v_lock);
121 xfidp = (xattr_fid_t *)fidp;
122 orig_len = fidp->fid_len;
123 fidp->fid_len = sizeof (xfidp->parent_fid);
125 error = VOP_FID(pvp, fidp, ct);
126 if (error) {
127 fidp->fid_len = orig_len;
128 return (error);
131 xfidp->parent_len = fidp->fid_len;
132 fidp->fid_len = XATTR_FIDSZ;
133 xfidp->dir_offset = gfs_file_inode(vp);
135 return (0);
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)
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;
152 xva_init(&xvattr);
154 if ((xoap = xva_getxoptattr(&xvattr)) == NULL)
155 return (EINVAL);
158 * For detecting ephemeral uid/gid
160 xvattr.xva_vattr.va_mask |= (AT_UID|AT_GID);
163 * We need to access the real fs object.
164 * vp points to a GFS file; ppvp points to the real object.
166 ppvp = gfs_file_parent(gfs_file_parent(vp));
169 * Iterate through the attrs associated with this view
172 for (attr = 0; attr < F_ATTR_ALL; attr++) {
173 if (xattr_view != attr_to_xattr_view(attr)) {
174 continue;
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;
233 error = VOP_GETATTR(ppvp, &xvattr.xva_vattr, 0, cr, ct);
234 if (error)
235 return (error);
238 * Process all the optional attributes together here. Notice that
239 * xoap was set when the optional attribute bits were set above.
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
317 * Check for optional ownersid/groupsid
320 if (xvattr.xva_vattr.va_uid > MAXUID) {
321 nvlist_t *nvl_sid;
323 if (nvlist_alloc(&nvl_sid, NV_UNIQUE_NAME, KM_SLEEP))
324 return (ENOMEM);
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);
334 nvlist_free(nvl_sid);
336 if (xvattr.xva_vattr.va_gid > MAXUID) {
337 nvlist_t *nvl_sid;
339 if (nvlist_alloc(&nvl_sid, NV_UNIQUE_NAME, KM_SLEEP))
340 return (ENOMEM);
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);
350 nvlist_free(nvl_sid);
353 return (0);
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.
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)
366 nvlist_t *nvl;
368 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP)) {
369 return (ENOMEM);
372 if (xattr_fill_nvlist(vp, xattr_view, nvl, cr, ct)) {
373 nvlist_free(nvl);
374 return (EFAULT);
377 VERIFY(nvlist_size(nvl, size, NV_ENCODE_XDR) == 0);
378 nvlist_free(nvl);
379 return (0);
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)
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;
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);
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);
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)
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;
435 * Validate file offset and fasttrack empty reads
437 if (uiop->uio_loffset < (offset_t)0)
438 return (EINVAL);
440 if (uiop->uio_resid == 0)
441 return (0);
443 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP))
444 return (ENOMEM);
446 if (xattr_fill_nvlist(vp, xattr_view, nvl, cr, ct)) {
447 nvlist_free(nvl);
448 return (EFAULT);
451 VERIFY(nvlist_size(nvl, &filesize, NV_ENCODE_XDR) == 0);
453 if (uiop->uio_loffset >= filesize) {
454 nvlist_free(nvl);
455 return (0);
458 buf = kmem_alloc(filesize, KM_SLEEP);
459 VERIFY(nvlist_pack(nvl, &buf, &filesize, NV_ENCODE_XDR,
460 KM_SLEEP) == 0);
462 error = uiomove((caddr_t)buf, filesize, UIO_READ, uiop);
463 kmem_free(buf, filesize);
464 nvlist_free(nvl);
465 return (error);
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)
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 */
484 if (vfs_has_feature(vp->v_vfsp, VFSFT_XVATTR) == 0)
485 return (EINVAL);
488 * Validate file offset and size.
490 if (uiop->uio_loffset < (offset_t)0)
491 return (EINVAL);
493 if (size == 0)
494 return (EINVAL);
496 xva_init(&xvattr);
498 if ((xoap = xva_getxoptattr(&xvattr)) == NULL) {
499 return (EINVAL);
503 * Copy and unpack the nvlist
505 buf = kmem_alloc(size, KM_SLEEP);
506 if (uiomove((caddr_t)buf, size, UIO_WRITE, uiop)) {
507 return (EFAULT);
510 if (nvlist_unpack(buf, size, &nvp, KM_SLEEP) != 0) {
511 kmem_free(buf, size);
512 uiop->uio_resid = size;
513 return (EINVAL);
515 kmem_free(buf, size);
518 * Fasttrack empty writes (nvlist with no nvpairs)
520 if (nvlist_next_nvpair(nvp, NULL) == 0)
521 return (0);
523 ppvp = gfs_file_parent(gfs_file_parent(vp));
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;
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.
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;
547 * Verify nvlist type matches required type and view is OK
550 if (type != attr_to_data_type(attr) ||
551 (attr_to_xattr_view(attr) == XATTR_VIEW_READONLY)) {
552 nvlist_free(nvp);
553 return (EINVAL);
557 * For OWNERSID/GROUPSID make sure the target
558 * file system support ephemeral ID's
560 if ((attr == F_OWNERSID || attr == F_GROUPSID) &&
561 (!(vp->v_vfsp->vfs_flag & VFS_XID))) {
562 nvlist_free(nvp);
563 return (EINVAL);
567 * Retrieve data from nvpair
569 switch (type) {
570 case DATA_TYPE_BOOLEAN_VALUE:
571 if (nvpair_value_boolean_value(pair, &value)) {
572 nvlist_free(nvp);
573 return (EINVAL);
575 break;
576 case DATA_TYPE_UINT64_ARRAY:
577 if (nvpair_value_uint64_array(pair, &times, &nelems)) {
578 nvlist_free(nvp);
579 return (EINVAL);
581 break;
582 case DATA_TYPE_NVLIST:
583 if (nvpair_value_nvlist(pair, &nvp_sid)) {
584 nvlist_free(nvp);
585 return (EINVAL);
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);
594 break;
595 default:
596 nvlist_free(nvp);
597 return (EINVAL);
600 switch (attr) {
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.
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);
662 * Now map domain+rid to ephemeral id's
664 * If mapping fails, then the uid/gid will
665 * be set to UID_NOBODY by Winchester.
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;
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);
687 break;
688 case F_REPARSE:
689 XVA_SET_REQ(&xvattr, XAT_REPARSE);
690 xoap->xoa_reparse = value;
691 break;
692 default:
693 break;
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;
702 nvlist_free(nvp);
703 return (error);
706 static int
707 xattr_file_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
708 caller_context_t *ct)
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));
721 vnodeops_t *xattr_file_ops;
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 }
741 vnode_t *
742 xattr_mkfile(vnode_t *pvp, xattr_view_t xattr_view)
744 vnode_t *vp;
745 xattr_file_t *np;
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);
754 vnode_t *
755 xattr_mkfile_ro(vnode_t *pvp)
757 return (xattr_mkfile(pvp, XATTR_VIEW_READONLY));
760 vnode_t *
761 xattr_mkfile_rw(vnode_t *pvp)
763 return (xattr_mkfile(pvp, XATTR_VIEW_READWRITE));
766 vnodeops_t *xattr_dir_ops;
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 },
774 #define XATTRDIR_NENTS ((sizeof (xattr_dirents) / sizeof (gfs_dirent_t)) - 1)
776 static int
777 is_sattr_name(char *s)
779 int i;
781 for (i = 0; i < XATTRDIR_NENTS; ++i) {
782 if (strcmp(s, xattr_dirents[i].gfse_name) == 0) {
783 return (1);
786 return (0);
790 * Given the name of an extended attribute file, determine if there is a
791 * normalization conflict with a sysattr view name.
794 xattr_sysattr_casechk(char *s)
796 int i;
798 for (i = 0; i < XATTRDIR_NENTS; ++i) {
799 if (strcasecmp(s, xattr_dirents[i].gfse_name) == 0)
800 return (1);
802 return (0);
805 static int
806 xattr_copy(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
807 cred_t *cr, caller_context_t *ct)
809 xvattr_t xvattr;
810 vnode_t *pdvp;
811 int error;
814 * Only copy system attrs if the views are the same
816 if (strcmp(snm, tnm) != 0)
817 return (EINVAL);
819 xva_init(&xvattr);
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);
834 pdvp = gfs_file_parent(sdvp);
835 error = VOP_GETATTR(pdvp, &xvattr.xva_vattr, 0, cr, ct);
836 if (error)
837 return (error);
839 pdvp = gfs_file_parent(tdvp);
840 error = VOP_SETATTR(pdvp, &xvattr.xva_vattr, 0, cr, ct);
841 return (error);
844 static int
845 xattr_dir_realdir(vnode_t *dvp, vnode_t **realdvp, int lookup_flags,
846 cred_t *cr, caller_context_t *ct)
848 vnode_t *pvp;
849 int error;
850 struct pathname pn;
851 char *startnm = "";
853 *realdvp = NULL;
855 pvp = gfs_file_parent(dvp);
857 error = pn_get(startnm, UIO_SYSSPACE, &pn);
858 if (error) {
859 VN_RELE(pvp);
860 return (error);
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.
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);
872 return (error);
875 /* ARGSUSED */
876 static int
877 xattr_dir_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
879 if (flags & FWRITE) {
880 return (EACCES);
883 return (0);
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)
891 return (0);
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.
901 #define PARENT_ATTRMASK (AT_UID|AT_GID|AT_RDEV|AT_CTIME|AT_MTIME)
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)
908 timestruc_t now;
909 vnode_t *pvp;
910 int error;
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);
919 vap->va_nlink += XATTRDIR_NENTS;
920 vap->va_size += XATTRDIR_NENTS;
921 return (0);
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.
929 if (vap->va_mask & PARENT_ATTRMASK) {
930 vattr_t pvattr;
931 uint_t off_bits;
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);
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.
951 off_bits = (pvattr.va_mask ^ PARENT_ATTRMASK) & PARENT_ATTRMASK;
952 pvattr.va_mask = vap->va_mask & ~off_bits;
953 *vap = pvattr;
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);
970 static int
971 xattr_dir_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
972 caller_context_t *ct)
974 vnode_t *realvp;
975 int error;
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.
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);
987 if (error == ENOENT) {
988 error = 0;
990 return (error);
993 /* ARGSUSED */
994 static int
995 xattr_dir_access(vnode_t *vp, int mode, int flags, cred_t *cr,
996 caller_context_t *ct)
998 int error;
999 vnode_t *realvp = NULL;
1001 if (mode & VWRITE) {
1002 return (EACCES);
1005 error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct);
1007 if (realvp)
1008 VN_RELE(realvp);
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.
1016 return ((error == ENOENT || error == EINVAL) ? 0 : error);
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)
1024 vnode_t *pvp;
1025 int error;
1027 *vpp = NULL;
1030 * Don't allow creation of extended attributes with sysattr names.
1032 if (is_sattr_name(name)) {
1033 return (gfs_dir_lookup(dvp, name, vpp, cr, 0, NULL, NULL));
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);
1043 return (error);
1046 static int
1047 xattr_dir_remove(vnode_t *dvp, char *name, cred_t *cr, caller_context_t *ct,
1048 int flags)
1050 vnode_t *pvp;
1051 int error;
1053 if (is_sattr_name(name)) {
1054 return (EACCES);
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);
1062 return (error);
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)
1069 vnode_t *pvp;
1070 int error;
1072 if (svp->v_flag & V_SYSATTR) {
1073 return (EINVAL);
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);
1081 return (error);
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)
1088 vnode_t *spvp, *tpvp;
1089 int error;
1090 int held_tgt;
1092 if (is_sattr_name(snm) || is_sattr_name(tnm))
1093 return (xattr_copy(sdvp, snm, tdvp, tnm, cr, ct));
1095 * We know that sdvp is a GFS dir, or we wouldn't be here.
1096 * Get the real unnamed directory.
1098 error = xattr_dir_realdir(sdvp, &spvp, LOOKUP_XATTR, cr, ct);
1099 if (error) {
1100 return (error);
1103 if (sdvp == tdvp) {
1105 * If the source and target are the same GFS directory, the
1106 * underlying unnamed source and target dir will be the same.
1108 tpvp = spvp;
1109 VN_HOLD(tpvp);
1110 held_tgt = 1;
1111 } else if (tdvp->v_flag & V_SYSATTR) {
1113 * If the target dir is a different GFS directory,
1114 * find its underlying unnamed dir.
1116 error = xattr_dir_realdir(tdvp, &tpvp, LOOKUP_XATTR, cr, ct);
1117 if (error) {
1118 VN_RELE(spvp);
1119 return (error);
1121 held_tgt = 1;
1122 } else {
1124 * Target dir is outside of GFS, pass it on through.
1126 tpvp = tdvp;
1127 held_tgt = 0;
1130 error = VOP_RENAME(spvp, snm, tpvp, tnm, cr, ct, flags);
1132 if (held_tgt) {
1133 VN_RELE(tpvp);
1135 VN_RELE(spvp);
1137 return (error);
1141 * readdir_xattr_casecmp: given a system attribute name, see if there
1142 * is a real xattr with the same normalized name.
1144 static int
1145 readdir_xattr_casecmp(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
1146 int *eflags)
1148 int error;
1149 vnode_t *vp;
1150 struct pathname pn;
1152 *eflags = 0;
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;
1164 pn_free(&pn);
1167 return (error);
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)
1174 vnode_t *pvp;
1175 int error;
1176 int local_eof;
1177 int reset_off = 0;
1178 int has_xattrs = 0;
1180 if (eofp == NULL) {
1181 eofp = &local_eof;
1183 *eofp = 0;
1186 * See if there is a real extended attribute directory.
1188 error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR, cr, ct);
1189 if (error == 0) {
1190 has_xattrs = 1;
1194 * Start by reading up the static entries.
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;
1202 if (has_xattrs) {
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.
1208 uiop->uio_loffset = GFS_STATIC_ENTRY_OFFSET;
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);
1215 if (error) {
1216 if (has_xattrs)
1217 VN_RELE(pvp);
1218 return (error);
1221 while ((error = gfs_readdir_pred(&gstate, uiop, &off)) == 0 &&
1222 !*eofp) {
1223 if (off >= 0 && off < dp->gfsd_nstatic) {
1224 int eflags;
1227 * Check to see if this sysattr set name has a
1228 * case-insensitive conflict with a real xattr
1229 * name.
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;
1239 ino = dp->gfsd_inode(dvp, off);
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;
1251 error = gfs_readdir_fini(&gstate, error, eofp, *eofp);
1252 if (error) {
1253 if (has_xattrs)
1254 VN_RELE(pvp);
1255 return (error);
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.
1264 if (*eofp == 0)
1265 return (EINVAL);
1266 reset_off = 1;
1269 if (!has_xattrs) {
1270 *eofp = 1;
1271 return (0);
1274 *eofp = 0;
1275 if (reset_off) {
1276 uiop->uio_loffset = 0;
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);
1283 return (error);
1286 /* ARGSUSED */
1287 static void
1288 xattr_dir_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
1290 gfs_file_t *fp;
1291 xattr_dir_t *xattr_dir;
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;
1299 mutex_exit(&vp->v_lock);
1300 fp = gfs_dir_inactive(vp);
1301 if (fp != NULL) {
1302 kmem_free(fp, fp->gfs_size);
1306 static int
1307 xattr_dir_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
1308 caller_context_t *ct)
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));
1321 /* ARGSUSED */
1322 static int
1323 xattr_dir_realvp(vnode_t *vp, vnode_t **realvp, caller_context_t *ct)
1325 xattr_dir_t *xattr_dir;
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;
1337 mutex_exit(&vp->v_lock);
1338 if ((error = xattr_dir_realdir(vp, &xdvp,
1339 LOOKUP_XATTR, kcred, NULL)) == 0) {
1341 * verify we aren't racing with another thread
1342 * to find the xattr_realvp
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);
1355 return (error);
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 }
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 }
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)
1391 vnode_t *pvp;
1392 struct pathname pn;
1393 int error;
1395 *vpp = NULL;
1396 *inop = 0;
1398 error = xattr_dir_realdir(vp, &pvp, LOOKUP_XATTR|CREATE_XATTR_DIR,
1399 cr, NULL);
1402 * Return ENOENT for EACCES requests during lookup. Once an
1403 * attribute create is attempted EACCES will be returned.
1405 if (error) {
1406 if (error == EACCES)
1407 return (ENOENT);
1408 return (error);
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);
1417 VN_RELE(pvp);
1419 return (error);
1422 /* ARGSUSED */
1423 static ino64_t
1424 xattrdir_do_ino(vnode_t *vp, int index)
1427 * We use index 0 for the directory fid. Start
1428 * the file numbering at 1.
1430 return ((ino64_t)index+1);
1433 void
1434 xattr_init(void)
1436 VERIFY(gfs_make_opsvec(xattr_opsvec) == 0);
1440 xattr_dir_lookup(vnode_t *dvp, vnode_t **vpp, int flags, cred_t *cr)
1442 int error = 0;
1444 *vpp = NULL;
1446 if (dvp->v_type != VDIR && dvp->v_type != VREG)
1447 return (EINVAL);
1449 mutex_enter(&dvp->v_lock);
1452 * If we're already in sysattr space, don't allow creation
1453 * of another level of sysattrs.
1455 if (dvp->v_flag & V_SYSATTR) {
1456 mutex_exit(&dvp->v_lock);
1457 return (EINVAL);
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;
1469 * We have to drop the lock on dvp. gfs_dir_create will
1470 * grab it for a VN_HOLD.
1472 mutex_exit(&dvp->v_lock);
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).
1480 error = VOP_PATHCONF(dvp, _PC_SATTR_ENABLED, &val, cr, NULL);
1481 if (error != 0 || val == 0)
1482 sysattrs_allowed = 0;
1484 if (!xattrs_allowed && !sysattrs_allowed)
1485 return (EINVAL);
1487 if (!sysattrs_allowed) {
1488 struct pathname pn;
1489 char *nm = "";
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);
1502 * Note that we act as if we were given CREATE_XATTR_DIR,
1503 * but only for creation of the GFS directory.
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) {
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.
1516 gfs_dir_t *dp = (*vpp)->v_data;
1518 ASSERT((*vpp)->v_count == 1);
1519 vn_free(*vpp);
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);
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.
1533 *vpp = dvp->v_xattrdir;
1534 VN_HOLD(*vpp);
1535 } else {
1536 (*vpp)->v_flag |= (V_XATTRDIR|V_SYSATTR);
1537 dvp->v_xattrdir = *vpp;
1540 mutex_exit(&dvp->v_lock);
1542 return (error);
1546 xattr_dir_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
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;
1555 *vpp = NULL;
1557 if (fidp->fid_len < XATTR_FIDSZ)
1558 return (EINVAL);
1560 xfidp = (xattr_fid_t *)fidp;
1561 orig_len = fidp->fid_len;
1562 fidp->fid_len = xfidp->parent_len;
1564 error = VFS_VGET(vfsp, &pvp, fidp);
1565 fidp->fid_len = orig_len;
1566 if (error)
1567 return (error);
1570 * Start by getting the GFS sysattr directory. We might need
1571 * to recreate it during the VOP_LOOKUP.
1573 nm = "";
1574 error = pn_get(nm, UIO_SYSSPACE, &pn);
1575 if (error) {
1576 VN_RELE(pvp);
1577 return (EINVAL);
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);
1587 if (xfidp->dir_offset == 0) {
1589 * If we were looking for the directory, we're done.
1591 *vpp = dvp;
1592 return (0);
1595 if (xfidp->dir_offset > XATTRDIR_NENTS) {
1596 VN_RELE(dvp);
1597 return (EINVAL);
1600 nm = xattr_dirents[xfidp->dir_offset - 1].gfse_name;
1602 error = pn_get(nm, UIO_SYSSPACE, &pn);
1603 if (error) {
1604 VN_RELE(dvp);
1605 return (EINVAL);
1608 error = VOP_LOOKUP(dvp, nm, vpp, &pn, 0, rootvp, CRED(), NULL,
1609 NULL, NULL);
1611 pn_free(&pn);
1612 VN_RELE(dvp);
1614 return (error);