15331Samw /*
25331Samw * CDDL HEADER START
35331Samw *
45331Samw * The contents of this file are subject to the terms of the
55331Samw * Common Development and Distribution License (the "License").
65331Samw * You may not use this file except in compliance with the License.
75331Samw *
85331Samw * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95331Samw * or http://www.opensolaris.org/os/licensing.
105331Samw * See the License for the specific language governing permissions
115331Samw * and limitations under the License.
125331Samw *
135331Samw * When distributing Covered Code, include this CDDL HEADER in each
145331Samw * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155331Samw * If applicable, add the following below this CDDL HEADER, with the
165331Samw * fields enclosed by brackets "[]" replaced with your own identifying
175331Samw * information: Portions Copyright [yyyy] [name of copyright owner]
185331Samw *
195331Samw * CDDL HEADER END
205331Samw */
215331Samw /*
2212083SMark.Shellenbaum@Sun.COM * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
235331Samw */
245331Samw
255331Samw #include <sys/param.h>
265331Samw #include <sys/isa_defs.h>
275331Samw #include <sys/types.h>
285331Samw #include <sys/sysmacros.h>
295331Samw #include <sys/cred.h>
305331Samw #include <sys/systm.h>
315331Samw #include <sys/errno.h>
325331Samw #include <sys/fcntl.h>
335331Samw #include <sys/pathname.h>
345331Samw #include <sys/stat.h>
355331Samw #include <sys/vfs.h>
365331Samw #include <sys/acl.h>
375331Samw #include <sys/file.h>
385331Samw #include <sys/sunddi.h>
395331Samw #include <sys/debug.h>
405331Samw #include <sys/cmn_err.h>
415331Samw #include <sys/vnode.h>
425331Samw #include <sys/mode.h>
435331Samw #include <sys/nvpair.h>
445331Samw #include <sys/attr.h>
455331Samw #include <sys/gfs.h>
465331Samw #include <sys/mutex.h>
475331Samw #include <fs/fs_subr.h>
485331Samw #include <sys/kidmap.h>
495331Samw
505331Samw typedef struct {
5111372SMark.Shellenbaum@Sun.COM gfs_file_t xattr_gfs_private;
525331Samw xattr_view_t xattr_view;
535331Samw } xattr_file_t;
545331Samw
5511372SMark.Shellenbaum@Sun.COM typedef struct {
5611372SMark.Shellenbaum@Sun.COM gfs_dir_t xattr_gfs_private;
5711372SMark.Shellenbaum@Sun.COM vnode_t *xattr_realvp; /* Only used for VOP_REALVP */
5811372SMark.Shellenbaum@Sun.COM } xattr_dir_t;
5911372SMark.Shellenbaum@Sun.COM
6011372SMark.Shellenbaum@Sun.COM /*
6111372SMark.Shellenbaum@Sun.COM * xattr_realvp is only used for VOP_REALVP, this is so we don't
6211372SMark.Shellenbaum@Sun.COM * keep an unnecessary hold on the *real* xattr dir unless we have
6311372SMark.Shellenbaum@Sun.COM * no other choice.
6411372SMark.Shellenbaum@Sun.COM */
6511372SMark.Shellenbaum@Sun.COM
665331Samw /* ARGSUSED */
675331Samw static int
xattr_file_open(vnode_t ** vpp,int flags,cred_t * cr,caller_context_t * ct)685331Samw xattr_file_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
695331Samw {
705331Samw xattr_file_t *np = (*vpp)->v_data;
715331Samw
725331Samw if ((np->xattr_view == XATTR_VIEW_READONLY) && (flags & FWRITE))
735331Samw return (EACCES);
745331Samw
755331Samw return (0);
765331Samw }
775331Samw
785331Samw /* ARGSUSED */
795331Samw static int
xattr_file_access(vnode_t * vp,int mode,int flags,cred_t * cr,caller_context_t * ct)805331Samw xattr_file_access(vnode_t *vp, int mode, int flags, cred_t *cr,
815331Samw caller_context_t *ct)
825331Samw {
835331Samw xattr_file_t *np = vp->v_data;
845331Samw
855331Samw if ((np->xattr_view == XATTR_VIEW_READONLY) && (mode & VWRITE))
865331Samw return (EACCES);
875331Samw
885331Samw return (0);
895331Samw }
905331Samw
915331Samw /* ARGSUSED */
925331Samw static int
xattr_file_close(vnode_t * vp,int flags,int count,offset_t off,cred_t * cr,caller_context_t * ct)935331Samw xattr_file_close(vnode_t *vp, int flags, int count, offset_t off,
945331Samw cred_t *cr, caller_context_t *ct)
955331Samw {
965331Samw cleanlocks(vp, ddi_get_pid(), 0);
975331Samw cleanshares(vp, ddi_get_pid());
985331Samw return (0);
995331Samw }
1005331Samw
1015331Samw static int
xattr_common_fid(vnode_t * vp,fid_t * fidp,caller_context_t * ct)1025331Samw xattr_common_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
1035331Samw {
1045331Samw xattr_fid_t *xfidp;
1055331Samw vnode_t *pvp, *savevp;
1065331Samw int error;
1075331Samw uint16_t orig_len;
1085331Samw
1095331Samw if (fidp->fid_len < XATTR_FIDSZ) {
1105331Samw fidp->fid_len = XATTR_FIDSZ;
1115331Samw return (ENOSPC);
1125331Samw }
1135331Samw
1145331Samw savevp = pvp = gfs_file_parent(vp);
1155331Samw mutex_enter(&savevp->v_lock);
1165331Samw if (pvp->v_flag & V_XATTRDIR) {
1175331Samw pvp = gfs_file_parent(pvp);
1185331Samw }
1195331Samw mutex_exit(&savevp->v_lock);
1205331Samw
1215331Samw xfidp = (xattr_fid_t *)fidp;
1225331Samw orig_len = fidp->fid_len;
1235331Samw fidp->fid_len = sizeof (xfidp->parent_fid);
1245331Samw
1255331Samw error = VOP_FID(pvp, fidp, ct);
1265331Samw if (error) {
1275331Samw fidp->fid_len = orig_len;
1285331Samw return (error);
1295331Samw }
1305331Samw
1315331Samw xfidp->parent_len = fidp->fid_len;
1325331Samw fidp->fid_len = XATTR_FIDSZ;
1335331Samw xfidp->dir_offset = gfs_file_inode(vp);
1345331Samw
1355331Samw return (0);
1365331Samw }
1375331Samw
1385331Samw /* ARGSUSED */
1395331Samw static int
xattr_fill_nvlist(vnode_t * vp,xattr_view_t xattr_view,nvlist_t * nvlp,cred_t * cr,caller_context_t * ct)1405331Samw xattr_fill_nvlist(vnode_t *vp, xattr_view_t xattr_view, nvlist_t *nvlp,
1415331Samw cred_t *cr, caller_context_t *ct)
1425331Samw {
1435331Samw int error;
1445331Samw f_attr_t attr;
1455331Samw uint64_t fsid;
1465331Samw xvattr_t xvattr;
1475331Samw xoptattr_t *xoap; /* Pointer to optional attributes */
1485331Samw vnode_t *ppvp;
1495331Samw const char *domain;
1505331Samw uint32_t rid;
1515331Samw
1525331Samw xva_init(&xvattr);
1535331Samw
1545331Samw if ((xoap = xva_getxoptattr(&xvattr)) == NULL)
1555331Samw return (EINVAL);
1565331Samw
1575331Samw /*
1585331Samw * For detecting ephemeral uid/gid
1595331Samw */
1605331Samw xvattr.xva_vattr.va_mask |= (AT_UID|AT_GID);
1615331Samw
1625331Samw /*
1635331Samw * We need to access the real fs object.
1645331Samw * vp points to a GFS file; ppvp points to the real object.
1655331Samw */
1665331Samw ppvp = gfs_file_parent(gfs_file_parent(vp));
1675331Samw
1685331Samw /*
1695331Samw * Iterate through the attrs associated with this view
1705331Samw */
1715331Samw
1725331Samw for (attr = 0; attr < F_ATTR_ALL; attr++) {
1735331Samw if (xattr_view != attr_to_xattr_view(attr)) {
1745331Samw continue;
1755331Samw }
1765331Samw
1775331Samw switch (attr) {
1785331Samw case F_SYSTEM:
1795331Samw XVA_SET_REQ(&xvattr, XAT_SYSTEM);
1805331Samw break;
1815331Samw case F_READONLY:
1825331Samw XVA_SET_REQ(&xvattr, XAT_READONLY);
1835331Samw break;
1845331Samw case F_HIDDEN:
1855331Samw XVA_SET_REQ(&xvattr, XAT_HIDDEN);
1865331Samw break;
1875331Samw case F_ARCHIVE:
1885331Samw XVA_SET_REQ(&xvattr, XAT_ARCHIVE);
1895331Samw break;
1905331Samw case F_IMMUTABLE:
1915331Samw XVA_SET_REQ(&xvattr, XAT_IMMUTABLE);
1925331Samw break;
1935331Samw case F_APPENDONLY:
1945331Samw XVA_SET_REQ(&xvattr, XAT_APPENDONLY);
1955331Samw break;
1965331Samw case F_NOUNLINK:
1975331Samw XVA_SET_REQ(&xvattr, XAT_NOUNLINK);
1985331Samw break;
1995331Samw case F_OPAQUE:
2005331Samw XVA_SET_REQ(&xvattr, XAT_OPAQUE);
2015331Samw break;
2025331Samw case F_NODUMP:
2035331Samw XVA_SET_REQ(&xvattr, XAT_NODUMP);
2045331Samw break;
2055331Samw case F_AV_QUARANTINED:
2065331Samw XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
2075331Samw break;
2085331Samw case F_AV_MODIFIED:
2095331Samw XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
2105331Samw break;
2115331Samw case F_AV_SCANSTAMP:
2125331Samw if (ppvp->v_type == VREG)
2135331Samw XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
2145331Samw break;
2155331Samw case F_CRTIME:
2165331Samw XVA_SET_REQ(&xvattr, XAT_CREATETIME);
2175331Samw break;
2185331Samw case F_FSID:
2195331Samw fsid = (((uint64_t)vp->v_vfsp->vfs_fsid.val[0] << 32) |
2205331Samw (uint64_t)(vp->v_vfsp->vfs_fsid.val[1] &
2215331Samw 0xffffffff));
2225331Samw VERIFY(nvlist_add_uint64(nvlp, attr_to_name(attr),
2235331Samw fsid) == 0);
2245331Samw break;
22510793Sdai.ngo@sun.com case F_REPARSE:
22610793Sdai.ngo@sun.com XVA_SET_REQ(&xvattr, XAT_REPARSE);
22710793Sdai.ngo@sun.com break;
22813043STim.Haley@Sun.COM case F_GEN:
22913043STim.Haley@Sun.COM XVA_SET_REQ(&xvattr, XAT_GEN);
23013043STim.Haley@Sun.COM break;
231*13082SJoyce.McIntosh@Sun.COM case F_OFFLINE:
232*13082SJoyce.McIntosh@Sun.COM XVA_SET_REQ(&xvattr, XAT_OFFLINE);
233*13082SJoyce.McIntosh@Sun.COM break;
234*13082SJoyce.McIntosh@Sun.COM case F_SPARSE:
235*13082SJoyce.McIntosh@Sun.COM XVA_SET_REQ(&xvattr, XAT_SPARSE);
236*13082SJoyce.McIntosh@Sun.COM break;
2375331Samw default:
2385331Samw break;
2395331Samw }
2405331Samw }
2415331Samw
2425331Samw error = VOP_GETATTR(ppvp, &xvattr.xva_vattr, 0, cr, ct);
2435331Samw if (error)
2445331Samw return (error);
2455331Samw
2465331Samw /*
2475331Samw * Process all the optional attributes together here. Notice that
2485331Samw * xoap was set when the optional attribute bits were set above.
2495331Samw */
2505331Samw if ((xvattr.xva_vattr.va_mask & AT_XVATTR) && xoap) {
2515331Samw if (XVA_ISSET_RTN(&xvattr, XAT_READONLY)) {
2525331Samw VERIFY(nvlist_add_boolean_value(nvlp,
2535331Samw attr_to_name(F_READONLY),
2545331Samw xoap->xoa_readonly) == 0);
2555331Samw }
2565331Samw if (XVA_ISSET_RTN(&xvattr, XAT_HIDDEN)) {
2575331Samw VERIFY(nvlist_add_boolean_value(nvlp,
2585331Samw attr_to_name(F_HIDDEN),
2595331Samw xoap->xoa_hidden) == 0);
2605331Samw }
2615331Samw if (XVA_ISSET_RTN(&xvattr, XAT_SYSTEM)) {
2625331Samw VERIFY(nvlist_add_boolean_value(nvlp,
2635331Samw attr_to_name(F_SYSTEM),
2645331Samw xoap->xoa_system) == 0);
2655331Samw }
2665331Samw if (XVA_ISSET_RTN(&xvattr, XAT_ARCHIVE)) {
2675331Samw VERIFY(nvlist_add_boolean_value(nvlp,
2685331Samw attr_to_name(F_ARCHIVE),
2695331Samw xoap->xoa_archive) == 0);
2705331Samw }
2715331Samw if (XVA_ISSET_RTN(&xvattr, XAT_IMMUTABLE)) {
2725331Samw VERIFY(nvlist_add_boolean_value(nvlp,
2735331Samw attr_to_name(F_IMMUTABLE),
2745331Samw xoap->xoa_immutable) == 0);
2755331Samw }
2765331Samw if (XVA_ISSET_RTN(&xvattr, XAT_NOUNLINK)) {
2775331Samw VERIFY(nvlist_add_boolean_value(nvlp,
2785331Samw attr_to_name(F_NOUNLINK),
2795331Samw xoap->xoa_nounlink) == 0);
2805331Samw }
2815331Samw if (XVA_ISSET_RTN(&xvattr, XAT_APPENDONLY)) {
2825331Samw VERIFY(nvlist_add_boolean_value(nvlp,
2835331Samw attr_to_name(F_APPENDONLY),
2845331Samw xoap->xoa_appendonly) == 0);
2855331Samw }
2865331Samw if (XVA_ISSET_RTN(&xvattr, XAT_NODUMP)) {
2875331Samw VERIFY(nvlist_add_boolean_value(nvlp,
2885331Samw attr_to_name(F_NODUMP),
2895331Samw xoap->xoa_nodump) == 0);
2905331Samw }
2915331Samw if (XVA_ISSET_RTN(&xvattr, XAT_OPAQUE)) {
2925331Samw VERIFY(nvlist_add_boolean_value(nvlp,
2935331Samw attr_to_name(F_OPAQUE),
2945331Samw xoap->xoa_opaque) == 0);
2955331Samw }
2965331Samw if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED)) {
2975331Samw VERIFY(nvlist_add_boolean_value(nvlp,
2985331Samw attr_to_name(F_AV_QUARANTINED),
2995331Samw xoap->xoa_av_quarantined) == 0);
3005331Samw }
3015331Samw if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED)) {
3025331Samw VERIFY(nvlist_add_boolean_value(nvlp,
3035331Samw attr_to_name(F_AV_MODIFIED),
3045331Samw xoap->xoa_av_modified) == 0);
3055331Samw }
3065331Samw if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP)) {
3075331Samw VERIFY(nvlist_add_uint8_array(nvlp,
3085331Samw attr_to_name(F_AV_SCANSTAMP),
3095331Samw xoap->xoa_av_scanstamp,
3105331Samw sizeof (xoap->xoa_av_scanstamp)) == 0);
3115331Samw }
3125331Samw if (XVA_ISSET_RTN(&xvattr, XAT_CREATETIME)) {
3135331Samw VERIFY(nvlist_add_uint64_array(nvlp,
3145331Samw attr_to_name(F_CRTIME),
3155331Samw (uint64_t *)&(xoap->xoa_createtime),
3165331Samw sizeof (xoap->xoa_createtime) /
3175331Samw sizeof (uint64_t)) == 0);
3185331Samw }
31910793Sdai.ngo@sun.com if (XVA_ISSET_RTN(&xvattr, XAT_REPARSE)) {
32010793Sdai.ngo@sun.com VERIFY(nvlist_add_boolean_value(nvlp,
32110793Sdai.ngo@sun.com attr_to_name(F_REPARSE),
32210793Sdai.ngo@sun.com xoap->xoa_reparse) == 0);
32310793Sdai.ngo@sun.com }
32413043STim.Haley@Sun.COM if (XVA_ISSET_RTN(&xvattr, XAT_GEN)) {
32513043STim.Haley@Sun.COM VERIFY(nvlist_add_uint64(nvlp,
32613043STim.Haley@Sun.COM attr_to_name(F_GEN),
32713043STim.Haley@Sun.COM xoap->xoa_generation) == 0);
32813043STim.Haley@Sun.COM }
329*13082SJoyce.McIntosh@Sun.COM if (XVA_ISSET_RTN(&xvattr, XAT_OFFLINE)) {
330*13082SJoyce.McIntosh@Sun.COM VERIFY(nvlist_add_boolean_value(nvlp,
331*13082SJoyce.McIntosh@Sun.COM attr_to_name(F_OFFLINE),
332*13082SJoyce.McIntosh@Sun.COM xoap->xoa_offline) == 0);
333*13082SJoyce.McIntosh@Sun.COM }
334*13082SJoyce.McIntosh@Sun.COM if (XVA_ISSET_RTN(&xvattr, XAT_SPARSE)) {
335*13082SJoyce.McIntosh@Sun.COM VERIFY(nvlist_add_boolean_value(nvlp,
336*13082SJoyce.McIntosh@Sun.COM attr_to_name(F_SPARSE),
337*13082SJoyce.McIntosh@Sun.COM xoap->xoa_sparse) == 0);
338*13082SJoyce.McIntosh@Sun.COM }
3395331Samw }
3405331Samw /*
3415331Samw * Check for optional ownersid/groupsid
3425331Samw */
3435331Samw
3445331Samw if (xvattr.xva_vattr.va_uid > MAXUID) {
3455331Samw nvlist_t *nvl_sid;
3465331Samw
3475331Samw if (nvlist_alloc(&nvl_sid, NV_UNIQUE_NAME, KM_SLEEP))
3485331Samw return (ENOMEM);
3495331Samw
3505771Sjp151216 if (kidmap_getsidbyuid(crgetzone(cr), xvattr.xva_vattr.va_uid,
3515331Samw &domain, &rid) == 0) {
3525331Samw VERIFY(nvlist_add_string(nvl_sid,
3535331Samw SID_DOMAIN, domain) == 0);
3545331Samw VERIFY(nvlist_add_uint32(nvl_sid, SID_RID, rid) == 0);
3555331Samw VERIFY(nvlist_add_nvlist(nvlp, attr_to_name(F_OWNERSID),
3565331Samw nvl_sid) == 0);
3575331Samw }
3585331Samw nvlist_free(nvl_sid);
3595331Samw }
3605331Samw if (xvattr.xva_vattr.va_gid > MAXUID) {
3615331Samw nvlist_t *nvl_sid;
3625331Samw
3635331Samw if (nvlist_alloc(&nvl_sid, NV_UNIQUE_NAME, KM_SLEEP))
3645331Samw return (ENOMEM);
3655331Samw
3665771Sjp151216 if (kidmap_getsidbygid(crgetzone(cr), xvattr.xva_vattr.va_gid,
3675331Samw &domain, &rid) == 0) {
3685331Samw VERIFY(nvlist_add_string(nvl_sid,
3695331Samw SID_DOMAIN, domain) == 0);
3705331Samw VERIFY(nvlist_add_uint32(nvl_sid, SID_RID, rid) == 0);
3715331Samw VERIFY(nvlist_add_nvlist(nvlp, attr_to_name(F_GROUPSID),
3725331Samw nvl_sid) == 0);
3735331Samw }
3745331Samw nvlist_free(nvl_sid);
3755331Samw }
3765331Samw
3775331Samw return (0);
3785331Samw }
3795331Samw
3805331Samw /*
3815331Samw * The size of a sysattr file is the size of the nvlist that will be
3825331Samw * returned by xattr_file_read(). A call to xattr_file_write() could
3835331Samw * change the size of that nvlist. That size is not stored persistently
3845331Samw * so xattr_fill_nvlist() calls VOP_GETATTR so that it can be calculated.
3855331Samw */
3865331Samw static int
xattr_file_size(vnode_t * vp,xattr_view_t xattr_view,size_t * size,cred_t * cr,caller_context_t * ct)3875331Samw xattr_file_size(vnode_t *vp, xattr_view_t xattr_view, size_t *size,
3885331Samw cred_t *cr, caller_context_t *ct)
3895331Samw {
3905331Samw nvlist_t *nvl;
3915331Samw
3925331Samw if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP)) {
3935331Samw return (ENOMEM);
3945331Samw }
3955331Samw
3965331Samw if (xattr_fill_nvlist(vp, xattr_view, nvl, cr, ct)) {
3975331Samw nvlist_free(nvl);
3985331Samw return (EFAULT);
3995331Samw }
4005331Samw
4015331Samw VERIFY(nvlist_size(nvl, size, NV_ENCODE_XDR) == 0);
4025331Samw nvlist_free(nvl);
4035331Samw return (0);
4045331Samw }
4055331Samw
4065331Samw /* ARGSUSED */
4075331Samw static int
xattr_file_getattr(vnode_t * vp,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)4085331Samw xattr_file_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
4095331Samw caller_context_t *ct)
4105331Samw {
4115331Samw xattr_file_t *np = vp->v_data;
4125331Samw timestruc_t now;
4135331Samw size_t size;
4145331Samw int error;
4155331Samw vnode_t *pvp;
4165331Samw vattr_t pvattr;
4175331Samw
4185331Samw vap->va_type = VREG;
4195331Samw vap->va_mode = MAKEIMODE(vap->va_type,
4205331Samw (np->xattr_view == XATTR_VIEW_READONLY ? 0444 : 0644));
4215331Samw vap->va_nodeid = gfs_file_inode(vp);
4225331Samw vap->va_nlink = 1;
4235331Samw pvp = gfs_file_parent(vp);
4245331Samw (void) memset(&pvattr, 0, sizeof (pvattr));
4255331Samw pvattr.va_mask = AT_CTIME|AT_MTIME;
4265331Samw error = VOP_GETATTR(pvp, &pvattr, flags, cr, ct);
4275331Samw if (error) {
4285331Samw return (error);
4295331Samw }
4305331Samw vap->va_ctime = pvattr.va_ctime;
4315331Samw vap->va_mtime = pvattr.va_mtime;
4325331Samw gethrestime(&now);
4335331Samw vap->va_atime = now;
4345331Samw vap->va_uid = 0;
4355331Samw vap->va_gid = 0;
4365331Samw vap->va_rdev = 0;
4375331Samw vap->va_blksize = DEV_BSIZE;
4385331Samw vap->va_seq = 0;
4395331Samw vap->va_fsid = vp->v_vfsp->vfs_dev;
4405331Samw error = xattr_file_size(vp, np->xattr_view, &size, cr, ct);
4415331Samw vap->va_size = size;
4425331Samw vap->va_nblocks = howmany(vap->va_size, vap->va_blksize);
4435331Samw return (error);
4445331Samw }
4455331Samw
4465331Samw /* ARGSUSED */
4475331Samw static int
xattr_file_read(vnode_t * vp,uio_t * uiop,int ioflag,cred_t * cr,caller_context_t * ct)4485331Samw xattr_file_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
4495331Samw caller_context_t *ct)
4505331Samw {
4515331Samw xattr_file_t *np = vp->v_data;
4525331Samw xattr_view_t xattr_view = np->xattr_view;
4535331Samw char *buf;
4545331Samw size_t filesize;
4555331Samw nvlist_t *nvl;
4565331Samw int error;
4575331Samw
4585331Samw /*
4595331Samw * Validate file offset and fasttrack empty reads
4605331Samw */
4615331Samw if (uiop->uio_loffset < (offset_t)0)
4625331Samw return (EINVAL);
4635331Samw
4645331Samw if (uiop->uio_resid == 0)
4655331Samw return (0);
4665331Samw
4675331Samw if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP))
4685331Samw return (ENOMEM);
4695331Samw
4705331Samw if (xattr_fill_nvlist(vp, xattr_view, nvl, cr, ct)) {
4715331Samw nvlist_free(nvl);
4725331Samw return (EFAULT);
4735331Samw }
4745331Samw
4755331Samw VERIFY(nvlist_size(nvl, &filesize, NV_ENCODE_XDR) == 0);
4765331Samw
4775331Samw if (uiop->uio_loffset >= filesize) {
4785331Samw nvlist_free(nvl);
4795331Samw return (0);
4805331Samw }
4815331Samw
4825331Samw buf = kmem_alloc(filesize, KM_SLEEP);
4835331Samw VERIFY(nvlist_pack(nvl, &buf, &filesize, NV_ENCODE_XDR,
4845331Samw KM_SLEEP) == 0);
4855331Samw
4865331Samw error = uiomove((caddr_t)buf, filesize, UIO_READ, uiop);
4875331Samw kmem_free(buf, filesize);
4885331Samw nvlist_free(nvl);
4895331Samw return (error);
4905331Samw }
4915331Samw
4925331Samw /* ARGSUSED */
4935331Samw static int
xattr_file_write(vnode_t * vp,uio_t * uiop,int ioflag,cred_t * cr,caller_context_t * ct)4945331Samw xattr_file_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
4955331Samw caller_context_t *ct)
4965331Samw {
4975331Samw int error = 0;
4985331Samw char *buf;
4995331Samw char *domain;
5005331Samw uint32_t rid;
5015331Samw ssize_t size = uiop->uio_resid;
5025331Samw nvlist_t *nvp;
5035331Samw nvpair_t *pair = NULL;
5045331Samw vnode_t *ppvp;
5055331Samw xvattr_t xvattr;
5065331Samw xoptattr_t *xoap = NULL; /* Pointer to optional attributes */
5075331Samw
5087757SJanice.Chang@Sun.COM if (vfs_has_feature(vp->v_vfsp, VFSFT_XVATTR) == 0)
5097757SJanice.Chang@Sun.COM return (EINVAL);
5107757SJanice.Chang@Sun.COM
5115331Samw /*
5125331Samw * Validate file offset and size.
5135331Samw */
5145331Samw if (uiop->uio_loffset < (offset_t)0)
5155331Samw return (EINVAL);
5165331Samw
5175331Samw if (size == 0)
5185331Samw return (EINVAL);
5195331Samw
5205331Samw xva_init(&xvattr);
5215331Samw
5225331Samw if ((xoap = xva_getxoptattr(&xvattr)) == NULL) {
5235331Samw return (EINVAL);
5245331Samw }
5255331Samw
5265331Samw /*
5275331Samw * Copy and unpack the nvlist
5285331Samw */
5295331Samw buf = kmem_alloc(size, KM_SLEEP);
5305331Samw if (uiomove((caddr_t)buf, size, UIO_WRITE, uiop)) {
5315331Samw return (EFAULT);
5325331Samw }
5335331Samw
5345331Samw if (nvlist_unpack(buf, size, &nvp, KM_SLEEP) != 0) {
5355331Samw kmem_free(buf, size);
5365331Samw uiop->uio_resid = size;
5375331Samw return (EINVAL);
5385331Samw }
5395331Samw kmem_free(buf, size);
5405331Samw
5415331Samw /*
5425331Samw * Fasttrack empty writes (nvlist with no nvpairs)
5435331Samw */
5445331Samw if (nvlist_next_nvpair(nvp, NULL) == 0)
5455331Samw return (0);
5465331Samw
5475331Samw ppvp = gfs_file_parent(gfs_file_parent(vp));
5485331Samw
5495331Samw while (pair = nvlist_next_nvpair(nvp, pair)) {
5505331Samw data_type_t type;
5515331Samw f_attr_t attr;
5525331Samw boolean_t value;
5535331Samw uint64_t *time, *times;
5545331Samw uint_t elem, nelems;
5555331Samw nvlist_t *nvp_sid;
5565331Samw uint8_t *scanstamp;
5575331Samw
5585331Samw /*
5595331Samw * Validate the name and type of each attribute.
5605331Samw * Log any unknown names and continue. This will
5615331Samw * help if additional attributes are added later.
5625331Samw */
5635331Samw type = nvpair_type(pair);
5645331Samw if ((attr = name_to_attr(nvpair_name(pair))) == F_ATTR_INVAL) {
5655331Samw cmn_err(CE_WARN, "Unknown attribute %s",
5665331Samw nvpair_name(pair));
5675331Samw continue;
5685331Samw }
5695331Samw
5705331Samw /*
5715331Samw * Verify nvlist type matches required type and view is OK
5725331Samw */
5735331Samw
5745331Samw if (type != attr_to_data_type(attr) ||
5755331Samw (attr_to_xattr_view(attr) == XATTR_VIEW_READONLY)) {
5765331Samw nvlist_free(nvp);
5775331Samw return (EINVAL);
5785331Samw }
5795331Samw
5805331Samw /*
5815331Samw * For OWNERSID/GROUPSID make sure the target
5825331Samw * file system support ephemeral ID's
5835331Samw */
5845331Samw if ((attr == F_OWNERSID || attr == F_GROUPSID) &&
5855331Samw (!(vp->v_vfsp->vfs_flag & VFS_XID))) {
5865331Samw nvlist_free(nvp);
5875331Samw return (EINVAL);
5885331Samw }
5895331Samw
5905331Samw /*
5915331Samw * Retrieve data from nvpair
5925331Samw */
5935331Samw switch (type) {
5945331Samw case DATA_TYPE_BOOLEAN_VALUE:
5955331Samw if (nvpair_value_boolean_value(pair, &value)) {
5965331Samw nvlist_free(nvp);
5975331Samw return (EINVAL);
5985331Samw }
5995331Samw break;
6005331Samw case DATA_TYPE_UINT64_ARRAY:
6015331Samw if (nvpair_value_uint64_array(pair, ×, &nelems)) {
6025331Samw nvlist_free(nvp);
6035331Samw return (EINVAL);
6045331Samw }
6055331Samw break;
6065331Samw case DATA_TYPE_NVLIST:
6075331Samw if (nvpair_value_nvlist(pair, &nvp_sid)) {
6085331Samw nvlist_free(nvp);
6095331Samw return (EINVAL);
6105331Samw }
6115331Samw break;
6125331Samw case DATA_TYPE_UINT8_ARRAY:
6135331Samw if (nvpair_value_uint8_array(pair,
6145331Samw &scanstamp, &nelems)) {
6155331Samw nvlist_free(nvp);
6165331Samw return (EINVAL);
6175331Samw }
6185331Samw break;
6195331Samw default:
6205331Samw nvlist_free(nvp);
6215331Samw return (EINVAL);
6225331Samw }
6235331Samw
6245331Samw switch (attr) {
6255331Samw /*
6265331Samw * If we have several similar optional attributes to
6275331Samw * process then we should do it all together here so that
6285331Samw * xoap and the requested bitmap can be set in one place.
6295331Samw */
6305331Samw case F_READONLY:
6315331Samw XVA_SET_REQ(&xvattr, XAT_READONLY);
6325331Samw xoap->xoa_readonly = value;
6335331Samw break;
6345331Samw case F_HIDDEN:
6355331Samw XVA_SET_REQ(&xvattr, XAT_HIDDEN);
6365331Samw xoap->xoa_hidden = value;
6375331Samw break;
6385331Samw case F_SYSTEM:
6395331Samw XVA_SET_REQ(&xvattr, XAT_SYSTEM);
6405331Samw xoap->xoa_system = value;
6415331Samw break;
6425331Samw case F_ARCHIVE:
6435331Samw XVA_SET_REQ(&xvattr, XAT_ARCHIVE);
6445331Samw xoap->xoa_archive = value;
6455331Samw break;
6465331Samw case F_IMMUTABLE:
6475331Samw XVA_SET_REQ(&xvattr, XAT_IMMUTABLE);
6485331Samw xoap->xoa_immutable = value;
6495331Samw break;
6505331Samw case F_NOUNLINK:
6515331Samw XVA_SET_REQ(&xvattr, XAT_NOUNLINK);
6525331Samw xoap->xoa_nounlink = value;
6535331Samw break;
6545331Samw case F_APPENDONLY:
6555331Samw XVA_SET_REQ(&xvattr, XAT_APPENDONLY);
6565331Samw xoap->xoa_appendonly = value;
6575331Samw break;
6585331Samw case F_NODUMP:
6595331Samw XVA_SET_REQ(&xvattr, XAT_NODUMP);
6605331Samw xoap->xoa_nodump = value;
6615331Samw break;
6625331Samw case F_AV_QUARANTINED:
6635331Samw XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
6645331Samw xoap->xoa_av_quarantined = value;
6655331Samw break;
6665331Samw case F_AV_MODIFIED:
6675331Samw XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
6685331Samw xoap->xoa_av_modified = value;
6695331Samw break;
6705331Samw case F_CRTIME:
6715331Samw XVA_SET_REQ(&xvattr, XAT_CREATETIME);
6725331Samw time = (uint64_t *)&(xoap->xoa_createtime);
6735331Samw for (elem = 0; elem < nelems; elem++)
6745331Samw *time++ = times[elem];
6755331Samw break;
6765331Samw case F_OWNERSID:
6775331Samw case F_GROUPSID:
6785331Samw if (nvlist_lookup_string(nvp_sid, SID_DOMAIN,
6795331Samw &domain) || nvlist_lookup_uint32(nvp_sid, SID_RID,
6805331Samw &rid)) {
6815331Samw nvlist_free(nvp);
6825331Samw return (EINVAL);
6835331Samw }
6845331Samw
6855331Samw /*
6865331Samw * Now map domain+rid to ephemeral id's
6875331Samw *
6885331Samw * If mapping fails, then the uid/gid will
6895331Samw * be set to UID_NOBODY by Winchester.
6905331Samw */
6915331Samw
6925331Samw if (attr == F_OWNERSID) {
6935771Sjp151216 (void) kidmap_getuidbysid(crgetzone(cr), domain,
6945771Sjp151216 rid, &xvattr.xva_vattr.va_uid);
6955331Samw xvattr.xva_vattr.va_mask |= AT_UID;
6965331Samw } else {
6975771Sjp151216 (void) kidmap_getgidbysid(crgetzone(cr), domain,
6985771Sjp151216 rid, &xvattr.xva_vattr.va_gid);
6995331Samw xvattr.xva_vattr.va_mask |= AT_GID;
7005331Samw }
7015331Samw break;
7025331Samw case F_AV_SCANSTAMP:
7035331Samw if (ppvp->v_type == VREG) {
7045331Samw XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
7055331Samw (void) memcpy(xoap->xoa_av_scanstamp,
7065331Samw scanstamp, nelems);
7075331Samw } else {
7085331Samw nvlist_free(nvp);
7095331Samw return (EINVAL);
7105331Samw }
7115331Samw break;
71210793Sdai.ngo@sun.com case F_REPARSE:
71310793Sdai.ngo@sun.com XVA_SET_REQ(&xvattr, XAT_REPARSE);
71410793Sdai.ngo@sun.com xoap->xoa_reparse = value;
71510793Sdai.ngo@sun.com break;
716*13082SJoyce.McIntosh@Sun.COM case F_OFFLINE:
717*13082SJoyce.McIntosh@Sun.COM XVA_SET_REQ(&xvattr, XAT_OFFLINE);
718*13082SJoyce.McIntosh@Sun.COM xoap->xoa_offline = value;
719*13082SJoyce.McIntosh@Sun.COM break;
720*13082SJoyce.McIntosh@Sun.COM case F_SPARSE:
721*13082SJoyce.McIntosh@Sun.COM XVA_SET_REQ(&xvattr, XAT_SPARSE);
722*13082SJoyce.McIntosh@Sun.COM xoap->xoa_sparse = value;
723*13082SJoyce.McIntosh@Sun.COM break;
7245331Samw default:
7255331Samw break;
7265331Samw }
7275331Samw }
7285331Samw
7295331Samw ppvp = gfs_file_parent(gfs_file_parent(vp));
7305331Samw error = VOP_SETATTR(ppvp, &xvattr.xva_vattr, 0, cr, ct);
7315331Samw if (error)
7325331Samw uiop->uio_resid = size;
7335331Samw
7345331Samw nvlist_free(nvp);
7355331Samw return (error);
7365331Samw }
7375331Samw
7385331Samw static int
xattr_file_pathconf(vnode_t * vp,int cmd,ulong_t * valp,cred_t * cr,caller_context_t * ct)7395331Samw xattr_file_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
7405331Samw caller_context_t *ct)
7415331Samw {
7425331Samw switch (cmd) {
7435331Samw case _PC_XATTR_EXISTS:
7445331Samw case _PC_SATTR_ENABLED:
7455331Samw case _PC_SATTR_EXISTS:
7465331Samw *valp = 0;
7475331Samw return (0);
7485331Samw default:
7495331Samw return (fs_pathconf(vp, cmd, valp, cr, ct));
7505331Samw }
7515331Samw }
7525331Samw
7535331Samw vnodeops_t *xattr_file_ops;
7545331Samw
7555331Samw static const fs_operation_def_t xattr_file_tops[] = {
7565331Samw { VOPNAME_OPEN, { .vop_open = xattr_file_open } },
7575331Samw { VOPNAME_CLOSE, { .vop_close = xattr_file_close } },
7585331Samw { VOPNAME_READ, { .vop_read = xattr_file_read } },
7595331Samw { VOPNAME_WRITE, { .vop_write = xattr_file_write } },
7605331Samw { VOPNAME_IOCTL, { .error = fs_ioctl } },
7615331Samw { VOPNAME_GETATTR, { .vop_getattr = xattr_file_getattr } },
7625331Samw { VOPNAME_ACCESS, { .vop_access = xattr_file_access } },
7635331Samw { VOPNAME_READDIR, { .error = fs_notdir } },
7645331Samw { VOPNAME_SEEK, { .vop_seek = fs_seek } },
7655331Samw { VOPNAME_INACTIVE, { .vop_inactive = gfs_vop_inactive } },
7665331Samw { VOPNAME_FID, { .vop_fid = xattr_common_fid } },
7675331Samw { VOPNAME_PATHCONF, { .vop_pathconf = xattr_file_pathconf } },
7685331Samw { VOPNAME_PUTPAGE, { .error = fs_putpage } },
7695331Samw { VOPNAME_FSYNC, { .error = fs_fsync } },
7705331Samw { NULL }
7715331Samw };
7725331Samw
7735331Samw vnode_t *
xattr_mkfile(vnode_t * pvp,xattr_view_t xattr_view)7745331Samw xattr_mkfile(vnode_t *pvp, xattr_view_t xattr_view)
7755331Samw {
7765331Samw vnode_t *vp;
7775331Samw xattr_file_t *np;
7785331Samw
7795331Samw vp = gfs_file_create(sizeof (xattr_file_t), pvp, xattr_file_ops);
7805331Samw np = vp->v_data;
7815331Samw np->xattr_view = xattr_view;
7825331Samw vp->v_flag |= V_SYSATTR;
7835331Samw return (vp);
7845331Samw }
7855331Samw
7865331Samw vnode_t *
xattr_mkfile_ro(vnode_t * pvp)7875331Samw xattr_mkfile_ro(vnode_t *pvp)
7885331Samw {
7895331Samw return (xattr_mkfile(pvp, XATTR_VIEW_READONLY));
7905331Samw }
7915331Samw
7925331Samw vnode_t *
xattr_mkfile_rw(vnode_t * pvp)7935331Samw xattr_mkfile_rw(vnode_t *pvp)
7945331Samw {
7955331Samw return (xattr_mkfile(pvp, XATTR_VIEW_READWRITE));
7965331Samw }
7975331Samw
7985331Samw vnodeops_t *xattr_dir_ops;
7995331Samw
8005331Samw static gfs_dirent_t xattr_dirents[] = {
8015331Samw { VIEW_READONLY, xattr_mkfile_ro, GFS_CACHE_VNODE, },
8025331Samw { VIEW_READWRITE, xattr_mkfile_rw, GFS_CACHE_VNODE, },
8035331Samw { NULL },
8045331Samw };
8055331Samw
8065331Samw #define XATTRDIR_NENTS ((sizeof (xattr_dirents) / sizeof (gfs_dirent_t)) - 1)
8075331Samw
8085331Samw static int
is_sattr_name(char * s)8095331Samw is_sattr_name(char *s)
8105331Samw {
8115331Samw int i;
8125331Samw
8135331Samw for (i = 0; i < XATTRDIR_NENTS; ++i) {
8145331Samw if (strcmp(s, xattr_dirents[i].gfse_name) == 0) {
8155331Samw return (1);
8165331Samw }
8175331Samw }
8185331Samw return (0);
8195331Samw }
8205331Samw
8215663Sck153898 /*
8225663Sck153898 * Given the name of an extended attribute file, determine if there is a
8235663Sck153898 * normalization conflict with a sysattr view name.
8245663Sck153898 */
8255663Sck153898 int
xattr_sysattr_casechk(char * s)8265663Sck153898 xattr_sysattr_casechk(char *s)
8275663Sck153898 {
8285663Sck153898 int i;
8295663Sck153898
8305663Sck153898 for (i = 0; i < XATTRDIR_NENTS; ++i) {
8315663Sck153898 if (strcasecmp(s, xattr_dirents[i].gfse_name) == 0)
8325663Sck153898 return (1);
8335663Sck153898 }
8345663Sck153898 return (0);
8355663Sck153898 }
8365663Sck153898
8375331Samw static int
xattr_copy(vnode_t * sdvp,char * snm,vnode_t * tdvp,char * tnm,cred_t * cr,caller_context_t * ct)8385331Samw xattr_copy(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
8395331Samw cred_t *cr, caller_context_t *ct)
8405331Samw {
8415331Samw xvattr_t xvattr;
8425331Samw vnode_t *pdvp;
8435331Samw int error;
8445331Samw
8455331Samw /*
8465331Samw * Only copy system attrs if the views are the same
8475331Samw */
8485331Samw if (strcmp(snm, tnm) != 0)
8495331Samw return (EINVAL);
8505331Samw
8515331Samw xva_init(&xvattr);
8525331Samw
8535331Samw XVA_SET_REQ(&xvattr, XAT_SYSTEM);
8545331Samw XVA_SET_REQ(&xvattr, XAT_READONLY);
8555331Samw XVA_SET_REQ(&xvattr, XAT_HIDDEN);
8565331Samw XVA_SET_REQ(&xvattr, XAT_ARCHIVE);
8575331Samw XVA_SET_REQ(&xvattr, XAT_APPENDONLY);
8585331Samw XVA_SET_REQ(&xvattr, XAT_NOUNLINK);
8595331Samw XVA_SET_REQ(&xvattr, XAT_IMMUTABLE);
8605331Samw XVA_SET_REQ(&xvattr, XAT_NODUMP);
8615331Samw XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
8625331Samw XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
8635331Samw XVA_SET_REQ(&xvattr, XAT_CREATETIME);
86410793Sdai.ngo@sun.com XVA_SET_REQ(&xvattr, XAT_REPARSE);
865*13082SJoyce.McIntosh@Sun.COM XVA_SET_REQ(&xvattr, XAT_OFFLINE);
866*13082SJoyce.McIntosh@Sun.COM XVA_SET_REQ(&xvattr, XAT_SPARSE);
8675331Samw
8685331Samw pdvp = gfs_file_parent(sdvp);
8695331Samw error = VOP_GETATTR(pdvp, &xvattr.xva_vattr, 0, cr, ct);
8705331Samw if (error)
8715331Samw return (error);
8725331Samw
8735331Samw pdvp = gfs_file_parent(tdvp);
8745331Samw error = VOP_SETATTR(pdvp, &xvattr.xva_vattr, 0, cr, ct);
8755331Samw return (error);
8765331Samw }
8775331Samw
8785331Samw static int
xattr_dir_realdir(vnode_t * dvp,vnode_t ** realdvp,int lookup_flags,cred_t * cr,caller_context_t * ct)8795331Samw xattr_dir_realdir(vnode_t *dvp, vnode_t **realdvp, int lookup_flags,
8805331Samw cred_t *cr, caller_context_t *ct)
8815331Samw {
8825331Samw vnode_t *pvp;
8835331Samw int error;
8845331Samw struct pathname pn;
8855331Samw char *startnm = "";
8865331Samw
8875331Samw *realdvp = NULL;
8885331Samw
8895331Samw pvp = gfs_file_parent(dvp);
8905331Samw
8915331Samw error = pn_get(startnm, UIO_SYSSPACE, &pn);
8925331Samw if (error) {
8935331Samw VN_RELE(pvp);
8945331Samw return (error);
8955331Samw }
8965331Samw
8975331Samw /*
8985331Samw * Set the LOOKUP_HAVE_SYSATTR_DIR flag so that we don't get into an
8995331Samw * infinite loop with fop_lookup calling back to xattr_dir_lookup.
9005331Samw */
9015331Samw lookup_flags |= LOOKUP_HAVE_SYSATTR_DIR;
9025331Samw error = VOP_LOOKUP(pvp, startnm, realdvp, &pn, lookup_flags,
9035331Samw rootvp, cr, ct, NULL, NULL);
9045331Samw pn_free(&pn);
9055331Samw
9065331Samw return (error);
9075331Samw }
9085331Samw
9095331Samw /* ARGSUSED */
9105331Samw static int
xattr_dir_open(vnode_t ** vpp,int flags,cred_t * cr,caller_context_t * ct)9115331Samw xattr_dir_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
9125331Samw {
9135331Samw if (flags & FWRITE) {
9145331Samw return (EACCES);
9155331Samw }
9165331Samw
9175331Samw return (0);
9185331Samw }
9195331Samw
9205331Samw /* ARGSUSED */
9215331Samw static int
xattr_dir_close(vnode_t * vpp,int flags,int count,offset_t off,cred_t * cr,caller_context_t * ct)9225331Samw xattr_dir_close(vnode_t *vpp, int flags, int count, offset_t off, cred_t *cr,
9235331Samw caller_context_t *ct)
9245331Samw {
9255331Samw return (0);
9265331Samw }
9275331Samw
9289750SGarima.Tripathi@Sun.COM /*
9299750SGarima.Tripathi@Sun.COM * Retrieve the attributes on an xattr directory. If there is a "real"
9309750SGarima.Tripathi@Sun.COM * xattr directory, use that. Otherwise, get the attributes (represented
9319750SGarima.Tripathi@Sun.COM * by PARENT_ATTRMASK) from the "parent" node and fill in the rest. Note
9329750SGarima.Tripathi@Sun.COM * that VOP_GETATTR() could turn off bits in the va_mask.
9339750SGarima.Tripathi@Sun.COM */
9349750SGarima.Tripathi@Sun.COM
9359750SGarima.Tripathi@Sun.COM #define PARENT_ATTRMASK (AT_UID|AT_GID|AT_RDEV|AT_CTIME|AT_MTIME)
9369750SGarima.Tripathi@Sun.COM
9375331Samw /* ARGSUSED */
9385331Samw static int
xattr_dir_getattr(vnode_t * vp,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)9395331Samw xattr_dir_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
9405331Samw caller_context_t *ct)
9415331Samw {
9425331Samw timestruc_t now;
9435331Samw vnode_t *pvp;
9445331Samw int error;
9455331Samw
9465331Samw error = xattr_dir_realdir(vp, &pvp, LOOKUP_XATTR, cr, ct);
9475331Samw if (error == 0) {
9485331Samw error = VOP_GETATTR(pvp, vap, 0, cr, ct);
9495331Samw VN_RELE(pvp);
9505331Samw if (error) {
9515331Samw return (error);
9525331Samw }
9535331Samw vap->va_nlink += XATTRDIR_NENTS;
9545331Samw vap->va_size += XATTRDIR_NENTS;
9555331Samw return (0);
9565331Samw }
9575331Samw
9585331Samw /*
9595331Samw * There is no real xattr directory. Cobble together
9609750SGarima.Tripathi@Sun.COM * an entry using info from the parent object (if needed)
9619750SGarima.Tripathi@Sun.COM * plus information common to all xattrs.
9625331Samw */
9639750SGarima.Tripathi@Sun.COM if (vap->va_mask & PARENT_ATTRMASK) {
9649750SGarima.Tripathi@Sun.COM vattr_t pvattr;
9659750SGarima.Tripathi@Sun.COM uint_t off_bits;
9669750SGarima.Tripathi@Sun.COM
9679750SGarima.Tripathi@Sun.COM pvp = gfs_file_parent(vp);
9689750SGarima.Tripathi@Sun.COM (void) memset(&pvattr, 0, sizeof (pvattr));
9699750SGarima.Tripathi@Sun.COM pvattr.va_mask = PARENT_ATTRMASK;
9709750SGarima.Tripathi@Sun.COM error = VOP_GETATTR(pvp, &pvattr, 0, cr, ct);
9719750SGarima.Tripathi@Sun.COM if (error) {
9729750SGarima.Tripathi@Sun.COM return (error);
9739750SGarima.Tripathi@Sun.COM }
9749750SGarima.Tripathi@Sun.COM
9759750SGarima.Tripathi@Sun.COM /*
9769750SGarima.Tripathi@Sun.COM * VOP_GETATTR() might have turned off some bits in
9779750SGarima.Tripathi@Sun.COM * pvattr.va_mask. This means that the underlying
9789750SGarima.Tripathi@Sun.COM * file system couldn't process those attributes.
9799750SGarima.Tripathi@Sun.COM * We need to make sure those bits get turned off
9809750SGarima.Tripathi@Sun.COM * in the vattr_t structure that gets passed back
9819750SGarima.Tripathi@Sun.COM * to the caller. Figure out which bits were turned
9829750SGarima.Tripathi@Sun.COM * off (if any) then set pvattr.va_mask before it
9839750SGarima.Tripathi@Sun.COM * gets copied to the vattr_t that the caller sees.
9849750SGarima.Tripathi@Sun.COM */
9859750SGarima.Tripathi@Sun.COM off_bits = (pvattr.va_mask ^ PARENT_ATTRMASK) & PARENT_ATTRMASK;
9869750SGarima.Tripathi@Sun.COM pvattr.va_mask = vap->va_mask & ~off_bits;
9879750SGarima.Tripathi@Sun.COM *vap = pvattr;
9885331Samw }
9899750SGarima.Tripathi@Sun.COM
9905331Samw vap->va_type = VDIR;
9915331Samw vap->va_mode = MAKEIMODE(vap->va_type, S_ISVTX | 0777);
9925331Samw vap->va_fsid = vp->v_vfsp->vfs_dev;
9935331Samw vap->va_nodeid = gfs_file_inode(vp);
9945331Samw vap->va_nlink = XATTRDIR_NENTS+2;
9955331Samw vap->va_size = vap->va_nlink;
9965331Samw gethrestime(&now);
9975331Samw vap->va_atime = now;
9985331Samw vap->va_blksize = 0;
9995331Samw vap->va_nblocks = 0;
10005331Samw vap->va_seq = 0;
10015331Samw return (0);
10025331Samw }
10035331Samw
10045331Samw static int
xattr_dir_setattr(vnode_t * vp,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)10055331Samw xattr_dir_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
10065331Samw caller_context_t *ct)
10075331Samw {
10085331Samw vnode_t *realvp;
10095331Samw int error;
10105331Samw
10115331Samw /*
10125331Samw * If there is a real xattr directory, do the setattr there.
10135331Samw * Otherwise, just return success. The GFS directory is transient,
10145331Samw * and any setattr changes can disappear anyway.
10155331Samw */
10165331Samw error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct);
10175331Samw if (error == 0) {
10185331Samw error = VOP_SETATTR(realvp, vap, flags, cr, ct);
10195331Samw VN_RELE(realvp);
10205331Samw }
10215331Samw if (error == ENOENT) {
10225331Samw error = 0;
10235331Samw }
10245331Samw return (error);
10255331Samw }
10265331Samw
10275331Samw /* ARGSUSED */
10285331Samw static int
xattr_dir_access(vnode_t * vp,int mode,int flags,cred_t * cr,caller_context_t * ct)10295331Samw xattr_dir_access(vnode_t *vp, int mode, int flags, cred_t *cr,
10305331Samw caller_context_t *ct)
10315331Samw {
10325331Samw int error;
10335331Samw vnode_t *realvp = NULL;
10345331Samw
10355331Samw if (mode & VWRITE) {
10365331Samw return (EACCES);
10375331Samw }
10385331Samw
10395331Samw error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct);
10405331Samw
10415331Samw if (realvp)
10425331Samw VN_RELE(realvp);
10435331Samw
10445331Samw /*
10455331Samw * No real xattr dir isn't an error
10465331Samw * an error of EINVAL indicates attributes on attributes
10475331Samw * are not supported. In that case just allow access to the
10485331Samw * transient directory.
10495331Samw */
10505331Samw return ((error == ENOENT || error == EINVAL) ? 0 : error);
10515331Samw }
10525331Samw
10535331Samw static int
xattr_dir_create(vnode_t * dvp,char * name,vattr_t * vap,vcexcl_t excl,int mode,vnode_t ** vpp,cred_t * cr,int flag,caller_context_t * ct,vsecattr_t * vsecp)10545331Samw xattr_dir_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl,
10555331Samw int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct,
10565331Samw vsecattr_t *vsecp)
10575331Samw {
10585331Samw vnode_t *pvp;
10595331Samw int error;
10605331Samw
10615331Samw *vpp = NULL;
10625331Samw
10635331Samw /*
10645331Samw * Don't allow creation of extended attributes with sysattr names.
10655331Samw */
10665331Samw if (is_sattr_name(name)) {
10676492Stimh return (gfs_dir_lookup(dvp, name, vpp, cr, 0, NULL, NULL));
10685331Samw }
10695331Samw
10705331Samw error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR|CREATE_XATTR_DIR,
10715331Samw cr, ct);
10725331Samw if (error == 0) {
10735331Samw error = VOP_CREATE(pvp, name, vap, excl, mode, vpp, cr, flag,
10745331Samw ct, vsecp);
10755331Samw VN_RELE(pvp);
10765331Samw }
10775331Samw return (error);
10785331Samw }
10795331Samw
10805331Samw static int
xattr_dir_remove(vnode_t * dvp,char * name,cred_t * cr,caller_context_t * ct,int flags)10815331Samw xattr_dir_remove(vnode_t *dvp, char *name, cred_t *cr, caller_context_t *ct,
10825331Samw int flags)
10835331Samw {
10845331Samw vnode_t *pvp;
10855331Samw int error;
10865331Samw
10875331Samw if (is_sattr_name(name)) {
10885331Samw return (EACCES);
10895331Samw }
10905331Samw
10915331Samw error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR, cr, ct);
10925331Samw if (error == 0) {
10935331Samw error = VOP_REMOVE(pvp, name, cr, ct, flags);
10945331Samw VN_RELE(pvp);
10955331Samw }
10965331Samw return (error);
10975331Samw }
10985331Samw
10995331Samw static int
xattr_dir_link(vnode_t * tdvp,vnode_t * svp,char * name,cred_t * cr,caller_context_t * ct,int flags)11005331Samw xattr_dir_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr,
11015331Samw caller_context_t *ct, int flags)
11025331Samw {
11035331Samw vnode_t *pvp;
11045331Samw int error;
11055331Samw
11065331Samw if (svp->v_flag & V_SYSATTR) {
11075331Samw return (EINVAL);
11085331Samw }
11095331Samw
11105331Samw error = xattr_dir_realdir(tdvp, &pvp, LOOKUP_XATTR, cr, ct);
11115331Samw if (error == 0) {
11125331Samw error = VOP_LINK(pvp, svp, name, cr, ct, flags);
11135331Samw VN_RELE(pvp);
11145331Samw }
11155331Samw return (error);
11165331Samw }
11175331Samw
11185331Samw static int
xattr_dir_rename(vnode_t * sdvp,char * snm,vnode_t * tdvp,char * tnm,cred_t * cr,caller_context_t * ct,int flags)11195331Samw xattr_dir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
11205331Samw cred_t *cr, caller_context_t *ct, int flags)
11215331Samw {
11225331Samw vnode_t *spvp, *tpvp;
11235331Samw int error;
11245331Samw int held_tgt;
11255331Samw
11265331Samw if (is_sattr_name(snm) || is_sattr_name(tnm))
11275331Samw return (xattr_copy(sdvp, snm, tdvp, tnm, cr, ct));
11285331Samw /*
11295331Samw * We know that sdvp is a GFS dir, or we wouldn't be here.
11305331Samw * Get the real unnamed directory.
11315331Samw */
11325331Samw error = xattr_dir_realdir(sdvp, &spvp, LOOKUP_XATTR, cr, ct);
11335331Samw if (error) {
11345331Samw return (error);
11355331Samw }
11365331Samw
11375331Samw if (sdvp == tdvp) {
11385331Samw /*
11395331Samw * If the source and target are the same GFS directory, the
11405331Samw * underlying unnamed source and target dir will be the same.
11415331Samw */
11425331Samw tpvp = spvp;
11435331Samw VN_HOLD(tpvp);
11445331Samw held_tgt = 1;
11455331Samw } else if (tdvp->v_flag & V_SYSATTR) {
11465331Samw /*
11475331Samw * If the target dir is a different GFS directory,
11485331Samw * find its underlying unnamed dir.
11495331Samw */
11505331Samw error = xattr_dir_realdir(tdvp, &tpvp, LOOKUP_XATTR, cr, ct);
11515331Samw if (error) {
11525331Samw VN_RELE(spvp);
11535331Samw return (error);
11545331Samw }
11555331Samw held_tgt = 1;
11565331Samw } else {
11575331Samw /*
11585331Samw * Target dir is outside of GFS, pass it on through.
11595331Samw */
11605331Samw tpvp = tdvp;
11615331Samw held_tgt = 0;
11625331Samw }
11635331Samw
11645331Samw error = VOP_RENAME(spvp, snm, tpvp, tnm, cr, ct, flags);
11655331Samw
11665331Samw if (held_tgt) {
11675331Samw VN_RELE(tpvp);
11685331Samw }
11695331Samw VN_RELE(spvp);
11705331Samw
11715331Samw return (error);
11725331Samw }
11735331Samw
11745663Sck153898 /*
11755663Sck153898 * readdir_xattr_casecmp: given a system attribute name, see if there
11765663Sck153898 * is a real xattr with the same normalized name.
11775663Sck153898 */
11785663Sck153898 static int
readdir_xattr_casecmp(vnode_t * dvp,char * nm,cred_t * cr,caller_context_t * ct,int * eflags)11795663Sck153898 readdir_xattr_casecmp(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
11805663Sck153898 int *eflags)
11815663Sck153898 {
11825663Sck153898 int error;
11835663Sck153898 vnode_t *vp;
11845663Sck153898 struct pathname pn;
11855663Sck153898
11865663Sck153898 *eflags = 0;
11875663Sck153898
11885663Sck153898 error = pn_get(nm, UIO_SYSSPACE, &pn);
11895663Sck153898 if (error == 0) {
11906492Stimh error = VOP_LOOKUP(dvp, nm, &vp, &pn,
11916492Stimh FIGNORECASE, rootvp, cr, ct, NULL, NULL);
11925663Sck153898 if (error == 0) {
11935663Sck153898 *eflags = ED_CASE_CONFLICT;
11945663Sck153898 VN_RELE(vp);
11955663Sck153898 } else if (error == ENOENT) {
11965663Sck153898 error = 0;
11975663Sck153898 }
11985663Sck153898 pn_free(&pn);
11995663Sck153898 }
12005663Sck153898
12015663Sck153898 return (error);
12025663Sck153898 }
12035663Sck153898
12045331Samw static int
xattr_dir_readdir(vnode_t * dvp,uio_t * uiop,cred_t * cr,int * eofp,caller_context_t * ct,int flags)12055331Samw xattr_dir_readdir(vnode_t *dvp, uio_t *uiop, cred_t *cr, int *eofp,
12065331Samw caller_context_t *ct, int flags)
12075331Samw {
12085331Samw vnode_t *pvp;
12095331Samw int error;
12105769Sck153898 int local_eof;
12115331Samw int reset_off = 0;
12125331Samw int has_xattrs = 0;
12135331Samw
12145331Samw if (eofp == NULL) {
12155331Samw eofp = &local_eof;
12165331Samw }
12175769Sck153898 *eofp = 0;
12185331Samw
12195331Samw /*
12205331Samw * See if there is a real extended attribute directory.
12215331Samw */
12225331Samw error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR, cr, ct);
12235331Samw if (error == 0) {
12245331Samw has_xattrs = 1;
12255331Samw }
12265331Samw
12275331Samw /*
12285331Samw * Start by reading up the static entries.
12295331Samw */
12305331Samw if (uiop->uio_loffset == 0) {
12315663Sck153898 ino64_t pino, ino;
12325663Sck153898 offset_t off;
12335663Sck153898 gfs_dir_t *dp = dvp->v_data;
12345663Sck153898 gfs_readdir_state_t gstate;
12355663Sck153898
12365331Samw if (has_xattrs) {
12375331Samw /*
12385331Samw * If there is a real xattr dir, skip . and ..
12395331Samw * in the GFS dir. We'll pick them up below
12405331Samw * when we call into the underlying fs.
12415331Samw */
12425331Samw uiop->uio_loffset = GFS_STATIC_ENTRY_OFFSET;
12435331Samw }
12445663Sck153898 error = gfs_get_parent_ino(dvp, cr, ct, &pino, &ino);
12455663Sck153898 if (error == 0) {
12465663Sck153898 error = gfs_readdir_init(&gstate, dp->gfsd_maxlen, 1,
12475663Sck153898 uiop, pino, ino, flags);
12485663Sck153898 }
12495331Samw if (error) {
12505663Sck153898 if (has_xattrs)
12515331Samw VN_RELE(pvp);
12525331Samw return (error);
12535331Samw }
12545663Sck153898
12555663Sck153898 while ((error = gfs_readdir_pred(&gstate, uiop, &off)) == 0 &&
12565663Sck153898 !*eofp) {
12575663Sck153898 if (off >= 0 && off < dp->gfsd_nstatic) {
12586492Stimh int eflags;
12595663Sck153898
12605663Sck153898 /*
12615663Sck153898 * Check to see if this sysattr set name has a
12625663Sck153898 * case-insensitive conflict with a real xattr
12635663Sck153898 * name.
12645663Sck153898 */
12656492Stimh eflags = 0;
12665663Sck153898 if ((flags & V_RDDIR_ENTFLAGS) && has_xattrs) {
12675663Sck153898 error = readdir_xattr_casecmp(pvp,
12685663Sck153898 dp->gfsd_static[off].gfse_name,
12695663Sck153898 cr, ct, &eflags);
12705663Sck153898 if (error)
12715663Sck153898 break;
12725663Sck153898 }
12735663Sck153898 ino = dp->gfsd_inode(dvp, off);
12745663Sck153898
12755663Sck153898 error = gfs_readdir_emit(&gstate, uiop, off,
12765663Sck153898 ino, dp->gfsd_static[off].gfse_name,
12775663Sck153898 eflags);
12785663Sck153898 if (error)
12795663Sck153898 break;
12805663Sck153898 } else {
12815663Sck153898 *eofp = 1;
12825663Sck153898 }
12835663Sck153898 }
12845663Sck153898
12855663Sck153898 error = gfs_readdir_fini(&gstate, error, eofp, *eofp);
12865663Sck153898 if (error) {
12875663Sck153898 if (has_xattrs)
12885663Sck153898 VN_RELE(pvp);
12895663Sck153898 return (error);
12905663Sck153898 }
12915663Sck153898
12925331Samw /*
12935331Samw * We must read all of the static entries in the first
12945331Samw * call. Otherwise we won't know if uio_loffset in a
12955331Samw * subsequent call refers to the static entries or to those
12965331Samw * in an underlying fs.
12975331Samw */
12989749STim.Haley@Sun.COM if (*eofp == 0)
12999749STim.Haley@Sun.COM return (EINVAL);
13005331Samw reset_off = 1;
13015331Samw }
13025331Samw
13035331Samw if (!has_xattrs) {
13045331Samw *eofp = 1;
13055331Samw return (0);
13065331Samw }
13075331Samw
13085331Samw *eofp = 0;
13095331Samw if (reset_off) {
13105331Samw uiop->uio_loffset = 0;
13115331Samw }
13125331Samw (void) VOP_RWLOCK(pvp, V_WRITELOCK_FALSE, NULL);
13135331Samw error = VOP_READDIR(pvp, uiop, cr, eofp, ct, flags);
13145331Samw VOP_RWUNLOCK(pvp, V_WRITELOCK_FALSE, NULL);
13155331Samw VN_RELE(pvp);
13165331Samw
13175331Samw return (error);
13185331Samw }
13195331Samw
13205331Samw /* ARGSUSED */
13215331Samw static void
xattr_dir_inactive(vnode_t * vp,cred_t * cr,caller_context_t * ct)13225331Samw xattr_dir_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
13235331Samw {
13245331Samw gfs_file_t *fp;
132511372SMark.Shellenbaum@Sun.COM xattr_dir_t *xattr_dir;
13265331Samw
132711372SMark.Shellenbaum@Sun.COM mutex_enter(&vp->v_lock);
132811372SMark.Shellenbaum@Sun.COM xattr_dir = vp->v_data;
132911372SMark.Shellenbaum@Sun.COM if (xattr_dir->xattr_realvp) {
133011372SMark.Shellenbaum@Sun.COM VN_RELE(xattr_dir->xattr_realvp);
133111372SMark.Shellenbaum@Sun.COM xattr_dir->xattr_realvp = NULL;
133211372SMark.Shellenbaum@Sun.COM }
133311372SMark.Shellenbaum@Sun.COM mutex_exit(&vp->v_lock);
13345331Samw fp = gfs_dir_inactive(vp);
13355331Samw if (fp != NULL) {
13365331Samw kmem_free(fp, fp->gfs_size);
13375331Samw }
13385331Samw }
13395331Samw
13405331Samw static int
xattr_dir_pathconf(vnode_t * vp,int cmd,ulong_t * valp,cred_t * cr,caller_context_t * ct)13415331Samw xattr_dir_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
13425331Samw caller_context_t *ct)
13435331Samw {
13445331Samw switch (cmd) {
13455331Samw case _PC_XATTR_EXISTS:
13465331Samw case _PC_SATTR_ENABLED:
13475331Samw case _PC_SATTR_EXISTS:
13485331Samw *valp = 0;
13495331Samw return (0);
13505331Samw default:
13515331Samw return (fs_pathconf(vp, cmd, valp, cr, ct));
13525331Samw }
13535331Samw }
13545331Samw
135511372SMark.Shellenbaum@Sun.COM /* ARGSUSED */
135611372SMark.Shellenbaum@Sun.COM static int
xattr_dir_realvp(vnode_t * vp,vnode_t ** realvp,caller_context_t * ct)135711372SMark.Shellenbaum@Sun.COM xattr_dir_realvp(vnode_t *vp, vnode_t **realvp, caller_context_t *ct)
135811372SMark.Shellenbaum@Sun.COM {
135911372SMark.Shellenbaum@Sun.COM xattr_dir_t *xattr_dir;
136011372SMark.Shellenbaum@Sun.COM
136111372SMark.Shellenbaum@Sun.COM mutex_enter(&vp->v_lock);
136211372SMark.Shellenbaum@Sun.COM xattr_dir = vp->v_data;
136311372SMark.Shellenbaum@Sun.COM if (xattr_dir->xattr_realvp) {
136411372SMark.Shellenbaum@Sun.COM *realvp = xattr_dir->xattr_realvp;
136512083SMark.Shellenbaum@Sun.COM mutex_exit(&vp->v_lock);
136612083SMark.Shellenbaum@Sun.COM return (0);
136711372SMark.Shellenbaum@Sun.COM } else {
136812083SMark.Shellenbaum@Sun.COM vnode_t *xdvp;
136912083SMark.Shellenbaum@Sun.COM int error;
137012083SMark.Shellenbaum@Sun.COM
137112083SMark.Shellenbaum@Sun.COM mutex_exit(&vp->v_lock);
137212083SMark.Shellenbaum@Sun.COM if ((error = xattr_dir_realdir(vp, &xdvp,
137312083SMark.Shellenbaum@Sun.COM LOOKUP_XATTR, kcred, NULL)) == 0) {
137412083SMark.Shellenbaum@Sun.COM /*
137512083SMark.Shellenbaum@Sun.COM * verify we aren't racing with another thread
137612083SMark.Shellenbaum@Sun.COM * to find the xattr_realvp
137712083SMark.Shellenbaum@Sun.COM */
137812083SMark.Shellenbaum@Sun.COM mutex_enter(&vp->v_lock);
137912083SMark.Shellenbaum@Sun.COM if (xattr_dir->xattr_realvp == NULL) {
138012083SMark.Shellenbaum@Sun.COM xattr_dir->xattr_realvp = xdvp;
138112083SMark.Shellenbaum@Sun.COM *realvp = xdvp;
138212083SMark.Shellenbaum@Sun.COM mutex_exit(&vp->v_lock);
138312083SMark.Shellenbaum@Sun.COM } else {
138412083SMark.Shellenbaum@Sun.COM *realvp = xattr_dir->xattr_realvp;
138512083SMark.Shellenbaum@Sun.COM mutex_exit(&vp->v_lock);
138612083SMark.Shellenbaum@Sun.COM VN_RELE(xdvp);
138712083SMark.Shellenbaum@Sun.COM }
138812083SMark.Shellenbaum@Sun.COM }
138912083SMark.Shellenbaum@Sun.COM return (error);
139011372SMark.Shellenbaum@Sun.COM }
139111372SMark.Shellenbaum@Sun.COM }
139211372SMark.Shellenbaum@Sun.COM
13935331Samw static const fs_operation_def_t xattr_dir_tops[] = {
13945331Samw { VOPNAME_OPEN, { .vop_open = xattr_dir_open } },
13955331Samw { VOPNAME_CLOSE, { .vop_close = xattr_dir_close } },
13965331Samw { VOPNAME_IOCTL, { .error = fs_inval } },
13975331Samw { VOPNAME_GETATTR, { .vop_getattr = xattr_dir_getattr } },
13985331Samw { VOPNAME_SETATTR, { .vop_setattr = xattr_dir_setattr } },
13995331Samw { VOPNAME_ACCESS, { .vop_access = xattr_dir_access } },
14005331Samw { VOPNAME_READDIR, { .vop_readdir = xattr_dir_readdir } },
14015331Samw { VOPNAME_LOOKUP, { .vop_lookup = gfs_vop_lookup } },
14025331Samw { VOPNAME_CREATE, { .vop_create = xattr_dir_create } },
14035331Samw { VOPNAME_REMOVE, { .vop_remove = xattr_dir_remove } },
14045331Samw { VOPNAME_LINK, { .vop_link = xattr_dir_link } },
14055331Samw { VOPNAME_RENAME, { .vop_rename = xattr_dir_rename } },
14065331Samw { VOPNAME_MKDIR, { .error = fs_inval } },
14075331Samw { VOPNAME_SEEK, { .vop_seek = fs_seek } },
14085331Samw { VOPNAME_INACTIVE, { .vop_inactive = xattr_dir_inactive } },
14095331Samw { VOPNAME_FID, { .vop_fid = xattr_common_fid } },
14105331Samw { VOPNAME_PATHCONF, { .vop_pathconf = xattr_dir_pathconf } },
141111372SMark.Shellenbaum@Sun.COM { VOPNAME_REALVP, { .vop_realvp = xattr_dir_realvp } },
14125331Samw { NULL, NULL }
14135331Samw };
14145331Samw
14155331Samw static gfs_opsvec_t xattr_opsvec[] = {
14165331Samw { "xattr dir", xattr_dir_tops, &xattr_dir_ops },
14175331Samw { "system attributes", xattr_file_tops, &xattr_file_ops },
14185331Samw { NULL, NULL, NULL }
14195331Samw };
14205331Samw
14215331Samw static int
xattr_lookup_cb(vnode_t * vp,const char * nm,vnode_t ** vpp,ino64_t * inop,cred_t * cr,int flags,int * deflags,pathname_t * rpnp)14225331Samw xattr_lookup_cb(vnode_t *vp, const char *nm, vnode_t **vpp, ino64_t *inop,
14236492Stimh cred_t *cr, int flags, int *deflags, pathname_t *rpnp)
14245331Samw {
14255331Samw vnode_t *pvp;
14265331Samw struct pathname pn;
14275331Samw int error;
14285331Samw
14295331Samw *vpp = NULL;
14305331Samw *inop = 0;
14315331Samw
14325331Samw error = xattr_dir_realdir(vp, &pvp, LOOKUP_XATTR|CREATE_XATTR_DIR,
14335331Samw cr, NULL);
14345331Samw
14355331Samw /*
14365331Samw * Return ENOENT for EACCES requests during lookup. Once an
14375331Samw * attribute create is attempted EACCES will be returned.
14385331Samw */
14395331Samw if (error) {
14405331Samw if (error == EACCES)
14415331Samw return (ENOENT);
14425331Samw return (error);
14435331Samw }
14445331Samw
14455331Samw error = pn_get((char *)nm, UIO_SYSSPACE, &pn);
14465331Samw if (error == 0) {
14476492Stimh error = VOP_LOOKUP(pvp, (char *)nm, vpp, &pn, flags, rootvp,
14486492Stimh cr, NULL, deflags, rpnp);
14495331Samw pn_free(&pn);
14505331Samw }
14515331Samw VN_RELE(pvp);
14525331Samw
14535331Samw return (error);
14545331Samw }
14555331Samw
14565331Samw /* ARGSUSED */
14575331Samw static ino64_t
xattrdir_do_ino(vnode_t * vp,int index)14585331Samw xattrdir_do_ino(vnode_t *vp, int index)
14595331Samw {
14605331Samw /*
14615331Samw * We use index 0 for the directory fid. Start
14625331Samw * the file numbering at 1.
14635331Samw */
14645331Samw return ((ino64_t)index+1);
14655331Samw }
14665331Samw
14675331Samw void
xattr_init(void)14685331Samw xattr_init(void)
14695331Samw {
14705331Samw VERIFY(gfs_make_opsvec(xattr_opsvec) == 0);
14715331Samw }
14725331Samw
14735331Samw int
xattr_dir_lookup(vnode_t * dvp,vnode_t ** vpp,int flags,cred_t * cr)14745331Samw xattr_dir_lookup(vnode_t *dvp, vnode_t **vpp, int flags, cred_t *cr)
14755331Samw {
14765331Samw int error = 0;
14775331Samw
14785331Samw *vpp = NULL;
14795331Samw
14805331Samw if (dvp->v_type != VDIR && dvp->v_type != VREG)
14815331Samw return (EINVAL);
14825331Samw
14835331Samw mutex_enter(&dvp->v_lock);
14845331Samw
14855331Samw /*
14865331Samw * If we're already in sysattr space, don't allow creation
14875331Samw * of another level of sysattrs.
14885331Samw */
14895331Samw if (dvp->v_flag & V_SYSATTR) {
14905331Samw mutex_exit(&dvp->v_lock);
14915331Samw return (EINVAL);
14925331Samw }
14935331Samw
14945331Samw if (dvp->v_xattrdir != NULL) {
14955331Samw *vpp = dvp->v_xattrdir;
14965331Samw VN_HOLD(*vpp);
14975331Samw } else {
14985331Samw ulong_t val;
14995331Samw int xattrs_allowed = dvp->v_vfsp->vfs_flag & VFS_XATTR;
15005331Samw int sysattrs_allowed = 1;
15015331Samw
15025331Samw /*
15035331Samw * We have to drop the lock on dvp. gfs_dir_create will
15045331Samw * grab it for a VN_HOLD.
15055331Samw */
15065331Samw mutex_exit(&dvp->v_lock);
15075331Samw
15085331Samw /*
15095331Samw * If dvp allows xattr creation, but not sysattr
15105331Samw * creation, return the real xattr dir vp. We can't
15115331Samw * use the vfs feature mask here because _PC_SATTR_ENABLED
15125331Samw * has vnode-level granularity (e.g. .zfs).
15135331Samw */
15145331Samw error = VOP_PATHCONF(dvp, _PC_SATTR_ENABLED, &val, cr, NULL);
15155331Samw if (error != 0 || val == 0)
15165331Samw sysattrs_allowed = 0;
15175331Samw
15185331Samw if (!xattrs_allowed && !sysattrs_allowed)
15195331Samw return (EINVAL);
15205331Samw
15215331Samw if (!sysattrs_allowed) {
15225331Samw struct pathname pn;
15235331Samw char *nm = "";
15245331Samw
15255331Samw error = pn_get(nm, UIO_SYSSPACE, &pn);
15265331Samw if (error)
15275331Samw return (error);
15285331Samw error = VOP_LOOKUP(dvp, nm, vpp, &pn,
15295331Samw flags|LOOKUP_HAVE_SYSATTR_DIR, rootvp, cr, NULL,
15305331Samw NULL, NULL);
15315331Samw pn_free(&pn);
15325331Samw return (error);
15335331Samw }
15345331Samw
15355331Samw /*
15365331Samw * Note that we act as if we were given CREATE_XATTR_DIR,
15375331Samw * but only for creation of the GFS directory.
15385331Samw */
15395331Samw *vpp = gfs_dir_create(
154011372SMark.Shellenbaum@Sun.COM sizeof (xattr_dir_t), dvp, xattr_dir_ops, xattr_dirents,
15415331Samw xattrdir_do_ino, MAXNAMELEN, NULL, xattr_lookup_cb);
15425331Samw mutex_enter(&dvp->v_lock);
15435331Samw if (dvp->v_xattrdir != NULL) {
15445331Samw /*
15455331Samw * We lost the race to create the xattr dir.
15465331Samw * Destroy this one, use the winner. We can't
15475331Samw * just call VN_RELE(*vpp), because the vnode
15485331Samw * is only partially initialized.
15495331Samw */
15505331Samw gfs_dir_t *dp = (*vpp)->v_data;
15515331Samw
15525331Samw ASSERT((*vpp)->v_count == 1);
15535331Samw vn_free(*vpp);
15545331Samw
15555331Samw mutex_destroy(&dp->gfsd_lock);
15565331Samw kmem_free(dp->gfsd_static,
15575331Samw dp->gfsd_nstatic * sizeof (gfs_dirent_t));
15585331Samw kmem_free(dp, dp->gfsd_file.gfs_size);
15595331Samw
15605331Samw /*
15615331Samw * There is an implied VN_HOLD(dvp) here. We should
15625331Samw * be doing a VN_RELE(dvp) to clean up the reference
15635331Samw * from *vpp, and then a VN_HOLD(dvp) for the new
15645331Samw * reference. Instead, we just leave the count alone.
15655331Samw */
15665331Samw
15675331Samw *vpp = dvp->v_xattrdir;
15685331Samw VN_HOLD(*vpp);
15695331Samw } else {
15705331Samw (*vpp)->v_flag |= (V_XATTRDIR|V_SYSATTR);
15715331Samw dvp->v_xattrdir = *vpp;
15725331Samw }
15735331Samw }
15745331Samw mutex_exit(&dvp->v_lock);
15755331Samw
15765331Samw return (error);
15775331Samw }
15785331Samw
15795331Samw int
xattr_dir_vget(vfs_t * vfsp,vnode_t ** vpp,fid_t * fidp)15805331Samw xattr_dir_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
15815331Samw {
15825331Samw int error;
15835331Samw vnode_t *pvp, *dvp;
15845331Samw xattr_fid_t *xfidp;
15855331Samw struct pathname pn;
15865331Samw char *nm;
15875331Samw uint16_t orig_len;
15885331Samw
15895331Samw *vpp = NULL;
15905331Samw
15915331Samw if (fidp->fid_len < XATTR_FIDSZ)
15925331Samw return (EINVAL);
15935331Samw
15945331Samw xfidp = (xattr_fid_t *)fidp;
15955331Samw orig_len = fidp->fid_len;
15965331Samw fidp->fid_len = xfidp->parent_len;
15975331Samw
15985331Samw error = VFS_VGET(vfsp, &pvp, fidp);
15995331Samw fidp->fid_len = orig_len;
16005331Samw if (error)
16015331Samw return (error);
16025331Samw
16035331Samw /*
16045331Samw * Start by getting the GFS sysattr directory. We might need
16055331Samw * to recreate it during the VOP_LOOKUP.
16065331Samw */
16075331Samw nm = "";
16085331Samw error = pn_get(nm, UIO_SYSSPACE, &pn);
16095331Samw if (error) {
16105331Samw VN_RELE(pvp);
16115331Samw return (EINVAL);
16125331Samw }
16135331Samw
16145331Samw error = VOP_LOOKUP(pvp, nm, &dvp, &pn, LOOKUP_XATTR|CREATE_XATTR_DIR,
16155331Samw rootvp, CRED(), NULL, NULL, NULL);
16165331Samw pn_free(&pn);
16175331Samw VN_RELE(pvp);
16185331Samw if (error)
16195331Samw return (error);
16205331Samw
16215331Samw if (xfidp->dir_offset == 0) {
16225331Samw /*
16235331Samw * If we were looking for the directory, we're done.
16245331Samw */
16255331Samw *vpp = dvp;
16265331Samw return (0);
16275331Samw }
16285331Samw
16295331Samw if (xfidp->dir_offset > XATTRDIR_NENTS) {
16305331Samw VN_RELE(dvp);
16315331Samw return (EINVAL);
16325331Samw }
16335331Samw
16345331Samw nm = xattr_dirents[xfidp->dir_offset - 1].gfse_name;
16355331Samw
16365331Samw error = pn_get(nm, UIO_SYSSPACE, &pn);
16375331Samw if (error) {
16385331Samw VN_RELE(dvp);
16395331Samw return (EINVAL);
16405331Samw }
16415331Samw
16425331Samw error = VOP_LOOKUP(dvp, nm, vpp, &pn, 0, rootvp, CRED(), NULL,
16435331Samw NULL, NULL);
16445331Samw
16455331Samw pn_free(&pn);
16465331Samw VN_RELE(dvp);
16475331Samw
16485331Samw return (error);
16495331Samw }
1650