/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 2018 Nexenta Systems, Inc. */ #include #include #include #include #include #include #include #include #include void rfs4_init_compound_state(struct compound_state *); bitmap4 rfs4_supported_attrs; int MSG_PRT_DEBUG = FALSE; /* If building with DEBUG enabled, enable mandattr tunable by default */ #ifdef DEBUG #ifndef RFS4_SUPPORT_MANDATTR_ONLY #define RFS4_SUPPORT_MANDATTR_ONLY #endif #endif /* * If building with mandattr only code, disable it by default. * To enable, set rfs4_mandattr_only in /etc/system and reboot. * When building without mandattr ifdef, the compiler should * optimize away the the comparisons because RFS4_MANDATTR_ONLY * is defined to be 0. */ #ifdef RFS4_SUPPORT_MANDATTR_ONLY #define NFS4_LAST_MANDATTR FATTR4_RDATTR_ERROR #define RFS4_MANDATTR_ONLY rfs4_mandattr_only int rfs4_mandattr_only = 0; #else #define RFS4_MANDATTR_ONLY 0 #endif static void rfs4_ntov_init(void); static int rfs4_fattr4_supported_attrs(); static int rfs4_fattr4_type(); static int rfs4_fattr4_fh_expire_type(); static int rfs4_fattr4_change(); static int rfs4_fattr4_size(); static int rfs4_fattr4_link_support(); static int rfs4_fattr4_symlink_support(); static int rfs4_fattr4_named_attr(); static int rfs4_fattr4_fsid(); static int rfs4_fattr4_unique_handles(); static int rfs4_fattr4_lease_time(); static int rfs4_fattr4_rdattr_error(); static int rfs4_fattr4_acl(); static int rfs4_fattr4_aclsupport(); static int rfs4_fattr4_archive(); static int rfs4_fattr4_cansettime(); static int rfs4_fattr4_case_insensitive(); static int rfs4_fattr4_case_preserving(); static int rfs4_fattr4_chown_restricted(); static int rfs4_fattr4_filehandle(); static int rfs4_fattr4_fileid(); static int rfs4_fattr4_files_avail(); static int rfs4_fattr4_files_free(); static int rfs4_fattr4_files_total(); static int rfs4_fattr4_fs_locations(); static int rfs4_fattr4_hidden(); static int rfs4_fattr4_homogeneous(); static int rfs4_fattr4_maxfilesize(); static int rfs4_fattr4_maxlink(); static int rfs4_fattr4_maxname(); static int rfs4_fattr4_maxread(); static int rfs4_fattr4_maxwrite(); static int rfs4_fattr4_mimetype(); static int rfs4_fattr4_mode(); static int rfs4_fattr4_no_trunc(); static int rfs4_fattr4_numlinks(); static int rfs4_fattr4_owner(); static int rfs4_fattr4_owner_group(); static int rfs4_fattr4_quota_avail_hard(); static int rfs4_fattr4_quota_avail_soft(); static int rfs4_fattr4_quota_used(); static int rfs4_fattr4_rawdev(); static int rfs4_fattr4_space_avail(); static int rfs4_fattr4_space_free(); static int rfs4_fattr4_space_total(); static int rfs4_fattr4_space_used(); static int rfs4_fattr4_system(); static int rfs4_fattr4_time_access(); static int rfs4_fattr4_time_access_set(); static int rfs4_fattr4_time_backup(); static int rfs4_fattr4_time_create(); static int rfs4_fattr4_time_delta(); static int rfs4_fattr4_time_metadata(); static int rfs4_fattr4_time_modify(); static int rfs4_fattr4_time_modify_set(); /* * Initialize the supported attributes */ void rfs4_attr_init() { int i; struct nfs4_svgetit_arg sarg; struct compound_state cs; struct statvfs64 sb; rfs4_init_compound_state(&cs); /* * This is global state checking, called once. We might be in * non-global-zone context here (say a modload happens from a zone * process) so in this case, we want the global-zone root vnode. */ cs.vp = rootvp; cs.fh.nfs_fh4_val = NULL; cs.cr = kcred; /* * Get all the supported attributes */ sarg.op = NFS4ATTR_SUPPORTED; sarg.cs = &cs; sarg.vap->va_mask = AT_ALL; sarg.sbp = &sb; sarg.flag = 0; sarg.rdattr_error = NFS4_OK; sarg.rdattr_error_req = FALSE; sarg.is_referral = B_FALSE; rfs4_ntov_init(); rfs4_supported_attrs = 0; for (i = 0; i < NFS4_MAXNUM_ATTRS; i++) { #ifdef RFS4_SUPPORT_MANDATTR_ONLY if (rfs4_mandattr_only == TRUE && i > NFS4_LAST_MANDATTR) continue; #endif if ((*nfs4_ntov_map[i].sv_getit)(NFS4ATTR_SUPPORTED, &sarg, NULL) == 0) { rfs4_supported_attrs |= nfs4_ntov_map[i].fbit; } } } /* * The following rfs4_fattr4_* functions convert between the fattr4 * arguments/attributes and the system (e.g. vattr) values. The following * commands are currently in use: * * NFS4ATTR_SUPPORTED: checks if the attribute in question is supported: * sarg.op = SUPPORTED - all supported attrs * sarg.op = GETIT - only supported readable attrs * sarg.op = SETIT - only supported writable attrs * * NFS4ATTR_GETIT: getattr type conversion - convert system values * (e.g. vattr struct) to fattr4 type values to be returned to the * user - usually in response to nfsv4 getattr request. * * NFS4ATTR_SETIT: convert fattr4 type values to system values to use by * setattr. Allows only read/write and write attributes, * even if not supported by the filesystem. Note that ufs only allows setattr * of owner/group, mode, size, atime/mtime. * * NFS4ATTR_VERIT: convert fattr4 type values to system values to use by * verify/nverify. Implemented to allow * almost everything that can be returned by getattr into known structs * (like vfsstat64 or vattr_t), that is, both read only and read/write attrs. * The function will return -1 if it found that the arguments don't match. * This applies to system-wide values that don't require a VOP_GETATTR * or other further checks to verify. It will return no error if they * either match or were retrieved successfully for later checking. * * NFS4ATTR_FREEIT: free up any space allocated by either of the above. * The sargp->op should be either NFS4ATTR_GETIT or NFS4ATTR_SETIT * to indicate which op was used to allocate the space. * * XXX Note: these functions are currently used by the server only. A * XXX different method of conversion is used on the client side. * XXX Eventually combining the two (possibly by adding NFS4ATTR_CLNT_GETIT * XXX and SETIT) may be a cleaner approach. */ /* * Mandatory attributes */ /* ARGSUSED */ static int rfs4_fattr4_supported_attrs(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: na->supported_attrs = rfs4_supported_attrs; break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: /* * Compare the input bitmap to the server's bitmap */ if (na->supported_attrs != rfs4_supported_attrs) { error = -1; /* no match */ } break; case NFS4ATTR_FREEIT: break; } return (error); } /* * Translate vnode vtype to nfsv4_ftype. */ static nfs_ftype4 vt_to_nf4[] = { 0, NF4REG, NF4DIR, NF4BLK, NF4CHR, NF4LNK, NF4FIFO, 0, 0, NF4SOCK, 0 }; /* ARGSUSED */ static int rfs4_fattr4_type(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_TYPE)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->vap->va_mask & AT_TYPE); /* * if xattr flag not set, use v4_to_nf4 mapping; * otherwise verify xattr flag is in sync with va_type * and set xattr types. */ if (! (sarg->xattr & (FH4_NAMEDATTR | FH4_ATTRDIR))) na->type = vt_to_nf4[sarg->vap->va_type]; else { /* * FH4 flag was set. Dir type maps to attrdir, * and all other types map to namedattr. */ if (sarg->vap->va_type == VDIR) na->type = NF4ATTRDIR; else na->type = NF4NAMEDATTR; } break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: /* * Compare the input type to the object type on server */ ASSERT(sarg->vap->va_mask & AT_TYPE); if (sarg->vap->va_type != nf4_to_vt[na->type]) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int fattr4_get_fh_expire_type(struct exportinfo *exi, uint32_t *fh_expire_typep) { #ifdef VOLATILE_FH_TEST int ex_flags; if (exi == NULL) return (ESTALE); ex_flags = exi->exi_export.ex_flags; if ((ex_flags & (EX_VOLFH | EX_VOLRNM | EX_VOLMIG | EX_NOEXPOPEN)) == 0) { *fh_expire_typep = FH4_PERSISTENT; return (0); } *fh_expire_typep = 0; if (ex_flags & EX_NOEXPOPEN) { /* file handles should not expire with open - not used */ *fh_expire_typep = FH4_NOEXPIRE_WITH_OPEN; } if (ex_flags & EX_VOLFH) { /* * file handles may expire any time - on share here. * If volatile any, no need to check other flags. */ *fh_expire_typep |= FH4_VOLATILE_ANY; return (0); } if (ex_flags & EX_VOLRNM) { /* file handles may expire on rename */ *fh_expire_typep |= FH4_VOL_RENAME; } if (ex_flags & EX_VOLMIG) { /* file handles may expire on migration - not used */ *fh_expire_typep |= FH4_VOL_MIGRATION; } #else /* not VOLATILE_FH_TEST */ *fh_expire_typep = FH4_PERSISTENT; #endif /* VOLATILE_FH_TEST */ return (0); } /* * At this point the only volatile filehandles we allow (for test purposes * only) are either fh's that expire when the filesystem is shared (reshared), * fh's that expire on a rename and persistent ones. */ /* ARGSUSED */ static int rfs4_fattr4_fh_expire_type(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { uint32_t fh_expire_type; int error = 0; switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: error = fattr4_get_fh_expire_type(sarg->cs->exi, &na->fh_expire_type); break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: error = fattr4_get_fh_expire_type(sarg->cs->exi, &fh_expire_type); if (!error && (na->fh_expire_type != fh_expire_type)) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } static int fattr4_get_change(struct nfs4_svgetit_arg *sarg, fattr4_change *changep) { vattr_t vap2[1], *vap = sarg->vap; struct compound_state *cs = sarg->cs; vnode_t *vp = cs->vp; nfsstat4 status; timespec_t vis_change; if ((vap->va_mask & AT_CTIME) == 0) { if (sarg->rdattr_error && (vp == NULL)) { return (-1); /* may be okay if rdattr_error */ } ASSERT(vp != NULL); vap = vap2; vap->va_mask = AT_CTIME; status = rfs4_vop_getattr(vp, vap, 0, cs->cr); if (status != NFS4_OK) return (geterrno4(status)); } NFS4_SET_FATTR4_CHANGE(*changep, vap->va_ctime); if (nfs_visible_change(cs->exi, vp, &vis_change)) { fattr4_change visch; NFS4_SET_FATTR4_CHANGE(visch, vis_change); if (visch > *changep) *changep = visch; } return (0); } /* ARGSUSED */ static int rfs4_fattr4_change(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; fattr4_change change; uint_t mask; vattr_t *vap = sarg->vap; switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: error = fattr4_get_change(sarg, &na->change); break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: mask = vap->va_mask; vap->va_mask &= ~AT_CTIME; /* force a VOP_GETATTR */ error = fattr4_get_change(sarg, &change); vap->va_mask = mask; if (!error && (na->change != change)) error = -1; break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_size(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; switch (cmd) { case NFS4ATTR_SUPPORTED: break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_SIZE)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->vap->va_mask & AT_SIZE); na->size = sarg->vap->va_size; break; case NFS4ATTR_SETIT: ASSERT(sarg->vap->va_mask & AT_SIZE); sarg->vap->va_size = na->size; break; case NFS4ATTR_VERIT: ASSERT(sarg->vap->va_mask & AT_SIZE); if (sarg->vap->va_size != na->size) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* * XXX - need VOP extension to ask file system (e.g. pcfs) if it supports * hard links. */ /* ARGSUSED */ static int rfs4_fattr4_link_support(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: na->link_support = TRUE; break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: if (!na->link_support) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* * XXX - need VOP extension to ask file system (e.g. pcfs) if it supports * sym links. */ /* ARGSUSED */ static int rfs4_fattr4_symlink_support(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: na->symlink_support = TRUE; break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: if (!na->symlink_support) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_named_attr(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; ulong_t val; switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && (sarg->cs->vp == NULL)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->cs->vp != NULL); /* * Solaris xattr model requires that VFS_XATTR is set * in file systems enabled for generic xattr. If VFS_XATTR * not set, no need to call pathconf for _PC_XATTR_EXISTS.. * * However the VFS_XATTR flag doesn't indicate sysattr support * so always check for sysattrs and then only do the * _PC_XATTR_EXISTS pathconf if needed. */ val = 0; error = VOP_PATHCONF(sarg->cs->vp, _PC_SATTR_EXISTS, &val, sarg->cs->cr, NULL); if ((error || val == 0) && sarg->cs->vp->v_vfsp->vfs_flag & VFS_XATTR) { error = VOP_PATHCONF(sarg->cs->vp, _PC_XATTR_EXISTS, &val, sarg->cs->cr, NULL); if (error) break; } na->named_attr = (val ? TRUE : FALSE); break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: ASSERT(sarg->cs->vp != NULL); if (sarg->cs->vp->v_vfsp->vfs_flag & VFS_XATTR) { error = VOP_PATHCONF(sarg->cs->vp, _PC_SATTR_EXISTS, &val, sarg->cs->cr, NULL); if (error || val == 0) error = VOP_PATHCONF(sarg->cs->vp, _PC_XATTR_EXISTS, &val, sarg->cs->cr, NULL); if (error) break; } else val = 0; if (na->named_attr != (val ? TRUE : FALSE)) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_fsid(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; int *pmaj = (int *)&na->fsid.major; /* * fsid_t is 64bits so it fits completely in fattr4_fsid.major. * fattr4_fsid.minor is always set to 0 since it isn't needed (yet). */ switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->is_referral) { na->fsid.major = 1; na->fsid.minor = 0; } else if (sarg->cs->exi->exi_volatile_dev) { pmaj[0] = sarg->cs->exi->exi_fsid.val[0]; pmaj[1] = sarg->cs->exi->exi_fsid.val[1]; na->fsid.minor = 0; } else { na->fsid.major = getmajor(sarg->vap->va_fsid); na->fsid.minor = getminor(sarg->vap->va_fsid); } break; case NFS4ATTR_SETIT: error = EINVAL; break; case NFS4ATTR_VERIT: if (sarg->is_referral) { if (na->fsid.major != 1 || na->fsid.minor != 0) error = -1; } else if (sarg->cs->exi->exi_volatile_dev) { if (pmaj[0] != sarg->cs->exi->exi_fsid.val[0] || pmaj[1] != sarg->cs->exi->exi_fsid.val[1] || na->fsid.minor != 0) error = -1; } else { if (na->fsid.major != getmajor(sarg->vap->va_fsid) || na->fsid.minor != getminor(sarg->vap->va_fsid)) error = -1; } break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_unique_handles(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { /* * XXX * For now, we can't support this. Problem of /export, beinging * a file system, /export/a and /export/b shared separately, * and /export/a/l and /export/b/l are ahrd links of each other. */ int error = 0; switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: na->unique_handles = FALSE; break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: if (na->unique_handles) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_lease_time(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: na->lease_time = rfs4_lease_time; break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: if (na->lease_time != rfs4_lease_time) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_rdattr_error(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; switch (cmd) { case NFS4ATTR_SUPPORTED: if ((sarg->op == NFS4ATTR_SETIT) || (sarg->op == NFS4ATTR_VERIT)) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: ASSERT(sarg->rdattr_error_req); na->rdattr_error = sarg->rdattr_error; break; case NFS4ATTR_SETIT: case NFS4ATTR_VERIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_FREEIT: break; } return (error); } /* * Server side compare of a filehandle from the wire to a native * server filehandle. */ static int rfs4fhcmp(nfs_fh4 *wirefh, nfs_fh4 *srvfh) { nfs_fh4_fmt_t fh; ASSERT(IS_P2ALIGNED(wirefh->nfs_fh4_val, sizeof (uint32_t))); bzero(&fh, sizeof (nfs_fh4_fmt_t)); if (!xdr_inline_decode_nfs_fh4((uint32_t *)wirefh->nfs_fh4_val, &fh, wirefh->nfs_fh4_len)) return (1); return (bcmp(srvfh->nfs_fh4_val, &fh, srvfh->nfs_fh4_len)); } /* ARGSUSED */ static int rfs4_fattr4_filehandle(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { nfs_fh4 *fh; switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) return (EINVAL); return (0); /* this attr is supported */ case NFS4ATTR_GETIT: /* * If sarg->cs->fh is all zeros then should makefh a new * one, otherwise, copy that one over. */ fh = &sarg->cs->fh; if (sarg->cs->fh.nfs_fh4_len == 0) { if (sarg->rdattr_error && (sarg->cs->vp == NULL)) return (-1); /* okay if rdattr_error */ ASSERT(sarg->cs->vp != NULL); na->filehandle.nfs_fh4_val = kmem_alloc(NFS_FH4_LEN, KM_SLEEP); return (makefh4(&na->filehandle, sarg->cs->vp, sarg->cs->exi)); } na->filehandle.nfs_fh4_val = kmem_alloc(fh->nfs_fh4_len, KM_SLEEP); nfs_fh4_copy(fh, &na->filehandle); return (0); case NFS4ATTR_SETIT: /* * read-only attr */ return (EINVAL); case NFS4ATTR_VERIT: /* * A verify of a filehandle will have the client sending * the raw format which needs to be compared to the * native format. */ if (rfs4fhcmp(&na->filehandle, &sarg->cs->fh) == 1) return (-1); /* no match */ return (0); case NFS4ATTR_FREEIT: if (sarg->op != NFS4ATTR_GETIT) return (0); if (na->filehandle.nfs_fh4_val == NULL) return (0); kmem_free(na->filehandle.nfs_fh4_val, na->filehandle.nfs_fh4_len); na->filehandle.nfs_fh4_val = NULL; na->filehandle.nfs_fh4_len = 0; return (0); } return (0); } /* * Recommended attributes */ /* ARGSUSED */ static int rfs4_fattr4_acl(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; vsecattr_t vs_native, vs_ace4; ulong_t whichacl; nfsstat4 status; vattr_t va, *vap = sarg->vap; vnode_t *vp = sarg->cs->vp; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: break; case NFS4ATTR_VERIT: case NFS4ATTR_GETIT: if (sarg->rdattr_error && (vp == NULL)) { return (-1); } ASSERT(vp != NULL); bzero(&vs_native, sizeof (vs_native)); /* see which ACLs fs supports */ error = VOP_PATHCONF(vp, _PC_ACL_ENABLED, &whichacl, sarg->cs->cr, NULL); if (error != 0) { /* * If we got an error, then the filesystem * likely does not understand the _PC_ACL_ENABLED * pathconf. In this case, we fall back to trying * POSIX-draft (aka UFS-style) ACLs, since that's * the behavior used by earlier version of NFS. */ error = 0; whichacl = _ACL_ACLENT_ENABLED; } if (!(whichacl & (_ACL_ACE_ENABLED | _ACL_ACLENT_ENABLED))) { /* * If the file system supports neither ACE nor * ACLENT ACLs we will fall back to UFS-style ACLs * like we did above if there was an error upon * calling VOP_PATHCONF. * * ACE and ACLENT type ACLs are the only interfaces * supported thus far. If any other bits are set on * 'whichacl' upon return from VOP_PATHCONF, we will * ignore them. */ whichacl = _ACL_ACLENT_ENABLED; } if (whichacl & _ACL_ACE_ENABLED) vs_native.vsa_mask = VSA_ACE | VSA_ACECNT; else if (whichacl & _ACL_ACLENT_ENABLED) vs_native.vsa_mask = VSA_ACL | VSA_ACLCNT | VSA_DFACL | VSA_DFACLCNT; if (error != 0) break; /* get the ACL, and translate it into nfsace4 style */ error = VOP_GETSECATTR(vp, &vs_native, 0, sarg->cs->cr, NULL); if (error != 0) break; if (whichacl & _ACL_ACE_ENABLED) { error = vs_acet_to_ace4(&vs_native, &vs_ace4, TRUE); vs_acet_destroy(&vs_native); } else { error = vs_aent_to_ace4(&vs_native, &vs_ace4, vp->v_type == VDIR, TRUE); vs_aent_destroy(&vs_native); } if (error != 0) break; if (cmd == NFS4ATTR_GETIT) { na->acl.fattr4_acl_len = vs_ace4.vsa_aclcnt; /* see case NFS4ATTR_FREEIT for this being freed */ na->acl.fattr4_acl_val = vs_ace4.vsa_aclentp; } else { if (na->acl.fattr4_acl_len != vs_ace4.vsa_aclcnt) error = -1; /* no match */ else if (ln_ace4_cmp(na->acl.fattr4_acl_val, vs_ace4.vsa_aclentp, vs_ace4.vsa_aclcnt) != 0) error = -1; /* no match */ } break; case NFS4ATTR_SETIT: if (sarg->rdattr_error && (vp == NULL)) { return (-1); } ASSERT(vp != NULL); /* prepare vs_ace4 from fattr4 data */ bzero(&vs_ace4, sizeof (vs_ace4)); vs_ace4.vsa_mask = VSA_ACE | VSA_ACECNT; vs_ace4.vsa_aclcnt = na->acl.fattr4_acl_len; vs_ace4.vsa_aclentp = na->acl.fattr4_acl_val; vs_ace4.vsa_aclentsz = vs_ace4.vsa_aclcnt * sizeof (ace_t); /* make sure we have correct owner/group */ if ((vap->va_mask & (AT_UID | AT_GID)) != (AT_UID | AT_GID)) { vap = &va; vap->va_mask = AT_UID | AT_GID; status = rfs4_vop_getattr(vp, vap, 0, sarg->cs->cr); if (status != NFS4_OK) return (geterrno4(status)); } /* see which ACLs the fs supports */ error = VOP_PATHCONF(vp, _PC_ACL_ENABLED, &whichacl, sarg->cs->cr, NULL); if (error != 0) { /* * If we got an error, then the filesystem * likely does not understand the _PC_ACL_ENABLED * pathconf. In this case, we fall back to trying * POSIX-draft (aka UFS-style) ACLs, since that's * the behavior used by earlier version of NFS. */ error = 0; whichacl = _ACL_ACLENT_ENABLED; } if (!(whichacl & (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED))) { /* * If the file system supports neither ACE nor * ACLENT ACLs we will fall back to UFS-style ACLs * like we did above if there was an error upon * calling VOP_PATHCONF. * * ACE and ACLENT type ACLs are the only interfaces * supported thus far. If any other bits are set on * 'whichacl' upon return from VOP_PATHCONF, we will * ignore them. */ whichacl = _ACL_ACLENT_ENABLED; } if (whichacl & _ACL_ACE_ENABLED) { error = vs_ace4_to_acet(&vs_ace4, &vs_native, vap->va_uid, vap->va_gid, TRUE); if (error != 0) break; (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL); error = VOP_SETSECATTR(vp, &vs_native, 0, sarg->cs->cr, NULL); VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); vs_acet_destroy(&vs_native); } else if (whichacl & _ACL_ACLENT_ENABLED) { error = vs_ace4_to_aent(&vs_ace4, &vs_native, vap->va_uid, vap->va_gid, vp->v_type == VDIR, TRUE); if (error != 0) break; (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL); error = VOP_SETSECATTR(vp, &vs_native, 0, sarg->cs->cr, NULL); VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); vs_aent_destroy(&vs_native); } break; case NFS4ATTR_FREEIT: if (sarg->op == NFS4ATTR_GETIT) { vs_ace4.vsa_mask = VSA_ACE | VSA_ACECNT; vs_ace4.vsa_aclcnt = na->acl.fattr4_acl_len; vs_ace4.vsa_aclentp = na->acl.fattr4_acl_val; vs_ace4_destroy(&vs_ace4); } break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_aclsupport(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* supported */ case NFS4ATTR_GETIT: na->aclsupport = ACL4_SUPPORT_ALLOW_ACL | ACL4_SUPPORT_DENY_ACL; break; case NFS4ATTR_SETIT: error = EINVAL; break; case NFS4ATTR_VERIT: if (na->aclsupport != (ACL4_SUPPORT_ALLOW_ACL | ACL4_SUPPORT_DENY_ACL)) error = -1; /* no match */ break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_archive(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { return (ENOTSUP); } /* ARGSUSED */ static int rfs4_fattr4_cansettime(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: na->cansettime = TRUE; break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: if (!na->cansettime) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* * XXX - need VOP extension to ask file system (e.g. pcfs) if it supports * case insensitive. */ /* ARGSUSED */ static int rfs4_fattr4_case_insensitive(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: na->case_insensitive = FALSE; break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: if (!na->case_insensitive) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_case_preserving(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: na->case_preserving = TRUE; break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: if (!na->case_preserving) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* fattr4_chown_restricted should reall be fattr4_chown_allowed */ /* ARGSUSED */ static int rfs4_fattr4_chown_restricted(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; ulong_t val; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && (sarg->cs->vp == NULL)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->cs->vp != NULL); error = VOP_PATHCONF(sarg->cs->vp, _PC_CHOWN_RESTRICTED, &val, sarg->cs->cr, NULL); if (error) break; na->chown_restricted = (val == 1); break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: ASSERT(sarg->cs->vp != NULL); error = VOP_PATHCONF(sarg->cs->vp, _PC_CHOWN_RESTRICTED, &val, sarg->cs->cr, NULL); if (error) break; if (na->chown_restricted != (val == 1)) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_fileid(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_NODEID)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->vap->va_mask & AT_NODEID); na->fileid = sarg->vap->va_nodeid; break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: ASSERT(sarg->vap->va_mask & AT_NODEID); if (sarg->vap->va_nodeid != na->fileid) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_get_mntdfileid(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg) { int error = 0; vattr_t *vap, va; vnode_t *stubvp = NULL, *vp; vp = sarg->cs->vp; sarg->mntdfid_set = FALSE; /* * VROOT object or zone's root, must untraverse. * * NOTE: Not doing reality checks on curzone vs. compound * state vnode because it will mismatch once at initialization * if a non-global-zone triggers the module load, BUT in that case * the vp is literally "/" which has VROOT set. */ if ((vp->v_flag & VROOT) || VN_IS_CURZONEROOT(vp)) { /* extra hold for vp since untraverse might rele */ VN_HOLD(vp); stubvp = untraverse(vp, ZONE_ROOTVP()); /* * If vp/stubvp are same, we must be at system-or-zone * root because untraverse returned same vp * for a VROOT object. sarg->vap was setup * before we got here, so there's no need to do * another getattr -- just use the one in sarg. */ if (VN_CMP(vp, stubvp)) { ASSERT(VN_IS_CURZONEROOT(vp)); vap = sarg->vap; } else { va.va_mask = AT_NODEID; vap = &va; error = rfs4_vop_getattr(stubvp, vap, 0, sarg->cs->cr); } /* * Done with stub, time to rele. If vp and stubvp * were the same, then we need to rele either vp or * stubvp. If they weren't the same, then untraverse() * already took case of the extra hold on vp, and only * the stub needs to be rele'd. Both cases are handled * by unconditionally rele'ing the stub. */ VN_RELE(stubvp); } else vap = sarg->vap; /* * At this point, vap should contain "correct" AT_NODEID -- * (for V_ROOT case, nodeid of stub, for non-VROOT case, * nodeid of vp). If error or AT_NODEID not available, then * make the obligatory (yet mysterious) rdattr_error * check that is so common in the attr code. */ if (!error && (vap->va_mask & AT_NODEID)) { sarg->mounted_on_fileid = vap->va_nodeid; sarg->mntdfid_set = TRUE; } else if (sarg->rdattr_error) error = -1; /* * error describes these cases: * 0 : success * -1: failure due to previous attr processing error (rddir only). * * : new attr failure (if rddir, caller will set rdattr_error) */ return (error); } /* ARGSUSED */ static int rfs4_fattr4_mounted_on_fileid(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: case NFS4ATTR_VERIT: if (!sarg->mntdfid_set) error = rfs4_get_mntdfileid(cmd, sarg); if (!error && sarg->mntdfid_set) { if (cmd == NFS4ATTR_GETIT) na->mounted_on_fileid = sarg->mounted_on_fileid; else if (na->mounted_on_fileid != sarg->mounted_on_fileid) error = -1; } break; case NFS4ATTR_SETIT: /* read-only attr */ error = EINVAL; break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_files_avail(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && (sarg->sbp == NULL)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->sbp != NULL); na->files_avail = sarg->sbp->f_favail; break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: ASSERT(sarg->sbp != NULL); if (sarg->sbp->f_favail != na->files_avail) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_files_free(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && (sarg->sbp == NULL)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->sbp != NULL); na->files_free = sarg->sbp->f_ffree; break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: ASSERT(sarg->sbp != NULL); if (sarg->sbp->f_ffree != na->files_free) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_files_total(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && (sarg->sbp == NULL)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->sbp != NULL); na->files_total = sarg->sbp->f_files; break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: ASSERT(sarg->sbp != NULL); if (sarg->sbp->f_files != na->files_total) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } static void rfs4_free_pathname4(pathname4 *pn4) { int i, len; utf8string *utf8s; if (pn4 == NULL || (len = pn4->pathname4_len) == 0 || (utf8s = pn4->pathname4_val) == NULL) return; for (i = 0; i < len; i++, utf8s++) { if (utf8s->utf8string_val == NULL || utf8s->utf8string_len == 0) continue; kmem_free(utf8s->utf8string_val, utf8s->utf8string_len); utf8s->utf8string_val = NULL; } kmem_free(pn4->pathname4_val, sizeof (utf8string) * pn4->pathname4_len); pn4->pathname4_val = 0; } static void rfs4_free_fs_location4(fs_location4 *fsl4) { if (fsl4 == NULL) return; rfs4_free_pathname4((pathname4 *)&fsl4->server_len); rfs4_free_pathname4(&fsl4->rootpath); } void rfs4_free_fs_locations4(fs_locations4 *fsls4) { int i, len; fs_location4 *fsl4; if (fsls4 == NULL) return; /* free fs_root */ rfs4_free_pathname4(&fsls4->fs_root); if ((len = fsls4->locations_len) == 0 || (fsl4 = fsls4->locations_val) == NULL) return; /* free fs_location4 */ for (i = 0; i < len; i++) { rfs4_free_fs_location4(fsl4); fsl4++; } kmem_free(fsls4->locations_val, sizeof (fs_location4) * len); fsls4->locations_val = NULL; } /* ARGSUSED */ static int rfs4_fattr4_fs_locations(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; fs_locations4 *fsl; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT || sarg->op == NFS4ATTR_VERIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: { kstat_named_t *stat = sarg->cs->exi->exi_ne->ne_globals->svstat[NFS_V4]; fsl = fetch_referral(sarg->cs->vp, sarg->cs->cr); if (fsl == NULL) (void) memset(&(na->fs_locations), 0, sizeof (fs_locations4)); else { na->fs_locations = *fsl; kmem_free(fsl, sizeof (fs_locations4)); } stat[NFS_REFERRALS].value.ui64++; break; } case NFS4ATTR_FREEIT: if (sarg->op == NFS4ATTR_SETIT || sarg->op == NFS4ATTR_VERIT) error = EINVAL; rfs4_free_fs_locations4(&na->fs_locations); break; case NFS4ATTR_SETIT: case NFS4ATTR_VERIT: /* * read-only attr */ error = EINVAL; break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_hidden(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { return (ENOTSUP); } /* ARGSUSED */ static int rfs4_fattr4_homogeneous(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: na->homogeneous = TRUE; /* XXX - need a VOP extension */ break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: if (!na->homogeneous) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_maxfilesize(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; ulong_t val; fattr4_maxfilesize maxfilesize; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && (sarg->cs->vp == NULL)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->cs->vp != NULL); error = VOP_PATHCONF(sarg->cs->vp, _PC_FILESIZEBITS, &val, sarg->cs->cr, NULL); if (error) break; /* * If the underlying file system does not support * _PC_FILESIZEBITS, return a reasonable default. Note that * error code on VOP_PATHCONF will be 0, even if the underlying * file system does not support _PC_FILESIZEBITS. */ if (val == (ulong_t)-1) { na->maxfilesize = MAXOFF32_T; } else { if (val >= (sizeof (uint64_t) * 8)) na->maxfilesize = INT64_MAX; else na->maxfilesize = ((1LL << (val - 1)) - 1); } break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: ASSERT(sarg->cs->vp != NULL); error = VOP_PATHCONF(sarg->cs->vp, _PC_FILESIZEBITS, &val, sarg->cs->cr, NULL); if (error) break; /* * If the underlying file system does not support * _PC_FILESIZEBITS, return a reasonable default. Note that * error code on VOP_PATHCONF will be 0, even if the underlying * file system does not support _PC_FILESIZEBITS. */ if (val == (ulong_t)-1) { maxfilesize = MAXOFF32_T; } else { if (val >= (sizeof (uint64_t) * 8)) maxfilesize = INT64_MAX; else maxfilesize = ((1LL << (val - 1)) - 1); } if (na->maxfilesize != maxfilesize) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_maxlink(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; ulong_t val; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && (sarg->cs->vp == NULL)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->cs->vp != NULL); error = VOP_PATHCONF(sarg->cs->vp, _PC_LINK_MAX, &val, sarg->cs->cr, NULL); if (error == 0) { na->maxlink = val; } break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: ASSERT(sarg->cs->vp != NULL); error = VOP_PATHCONF(sarg->cs->vp, _PC_LINK_MAX, &val, sarg->cs->cr, NULL); if (!error && (na->maxlink != (uint32_t)val)) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_maxname(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; ulong_t val; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && (sarg->cs->vp == NULL)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->cs->vp != NULL); error = VOP_PATHCONF(sarg->cs->vp, _PC_NAME_MAX, &val, sarg->cs->cr, NULL); if (error == 0) { na->maxname = val; } break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: ASSERT(sarg->cs->vp != NULL); error = VOP_PATHCONF(sarg->cs->vp, _PC_NAME_MAX, &val, sarg->cs->cr, NULL); if (!error && (na->maxname != val)) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_maxread(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: na->maxread = rfs4_tsize(sarg->cs->req); break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: if (na->maxread != rfs4_tsize(sarg->cs->req)) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_maxwrite(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: na->maxwrite = rfs4_tsize(sarg->cs->req); break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: if (na->maxwrite != rfs4_tsize(sarg->cs->req)) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_mimetype(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { return (ENOTSUP); } /* ARGSUSED */ static int rfs4_fattr4_mode(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_MODE)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->vap->va_mask & AT_MODE); na->mode = sarg->vap->va_mode; break; case NFS4ATTR_SETIT: ASSERT(sarg->vap->va_mask & AT_MODE); sarg->vap->va_mode = na->mode; /* * If the filesystem is exported with nosuid, then mask off * the setuid and setgid bits. */ if (sarg->cs->vp->v_type == VREG && (sarg->cs->exi->exi_export.ex_flags & EX_NOSUID)) sarg->vap->va_mode &= ~(VSUID | VSGID); break; case NFS4ATTR_VERIT: ASSERT(sarg->vap->va_mask & AT_MODE); if (sarg->vap->va_mode != na->mode) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_no_trunc(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: na->no_trunc = TRUE; break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: if (!na->no_trunc) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_numlinks(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_NLINK)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->vap->va_mask & AT_NLINK); na->numlinks = sarg->vap->va_nlink; break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: ASSERT(sarg->vap->va_mask & AT_NLINK); if (sarg->vap->va_nlink != na->numlinks) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_owner(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; uid_t uid; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_UID)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->vap->va_mask & AT_UID); /* * There are well defined polices for what happens on server- * side GETATTR when uid to attribute string conversion cannot * occur. Please refer to nfs4_idmap.c for details. */ error = nfs_idmap_uid_str(sarg->vap->va_uid, &na->owner, TRUE); switch (error) { case ECONNREFUSED: error = NFS4ERR_DELAY; break; default: break; } break; case NFS4ATTR_SETIT: ASSERT(sarg->vap->va_mask & AT_UID); /* * There are well defined policies for what happens on server- * side SETATTR of 'owner' when a "user@domain" mapping cannot * occur. Please refer to nfs4_idmap.c for details. * * Any other errors, such as the mapping not being found by * nfsmapid(8), and interrupted clnt_call, etc, will result * in NFS4ERR_BADOWNER. * * XXX need to return consistent errors, perhaps all * server side attribute routines should return NFS4ERR*. */ error = nfs_idmap_str_uid(&na->owner, &sarg->vap->va_uid, TRUE); switch (error) { case NFS4_OK: case ENOTSUP: /* * Ignore warning that we are the * nfsmapid (can't happen on srv) */ error = 0; MSG_PRT_DEBUG = FALSE; break; case ECOMM: case ECONNREFUSED: if (!MSG_PRT_DEBUG) { /* * printed just once per daemon death, * inform the user and then stay silent */ cmn_err(CE_WARN, "!Unable to contact " "nfsmapid"); MSG_PRT_DEBUG = TRUE; } error = NFS4ERR_DELAY; break; case EINVAL: error = NFS4ERR_INVAL; break; default: error = NFS4ERR_BADOWNER; break; } break; case NFS4ATTR_VERIT: ASSERT(sarg->vap->va_mask & AT_UID); error = nfs_idmap_str_uid(&na->owner, &uid, TRUE); /* * Ignore warning that we are the nfsmapid (can't happen on srv) */ if (error == ENOTSUP) error = 0; if (error) error = -1; /* no match */ else if (sarg->vap->va_uid != uid) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: if (sarg->op == NFS4ATTR_GETIT) { if (na->owner.utf8string_val) { UTF8STRING_FREE(na->owner) bzero(&na->owner, sizeof (na->owner)); } } break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_owner_group(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; gid_t gid; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_GID)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->vap->va_mask & AT_GID); /* * There are well defined polices for what happens on server- * side GETATTR when gid to attribute string conversion cannot * occur. Please refer to nfs4_idmap.c for details. */ error = nfs_idmap_gid_str(sarg->vap->va_gid, &na->owner_group, TRUE); switch (error) { case ECONNREFUSED: error = NFS4ERR_DELAY; break; default: break; } break; case NFS4ATTR_SETIT: ASSERT(sarg->vap->va_mask & AT_GID); /* * There are well defined policies for what happens on server- * side SETATTR of 'owner_group' when a "group@domain" mapping * cannot occur. Please refer to nfs4_idmap.c for details. * * Any other errors, such as the mapping not being found by * nfsmapid(8), and interrupted clnt_call, etc, will result * in NFS4ERR_BADOWNER. * * XXX need to return consistent errors, perhaps all * server side attribute routines should return NFS4ERR*. */ error = nfs_idmap_str_gid(&na->owner_group, &sarg->vap->va_gid, TRUE); switch (error) { case NFS4_OK: case ENOTSUP: /* * Ignore warning that we are the * nfsmapid (can't happen on srv) */ error = 0; MSG_PRT_DEBUG = FALSE; break; case ECOMM: case ECONNREFUSED: if (!MSG_PRT_DEBUG) { /* * printed just once per daemon death, * inform the user and then stay silent */ cmn_err(CE_WARN, "!Unable to contact " "nfsmapid"); MSG_PRT_DEBUG = TRUE; } error = NFS4ERR_DELAY; break; case EINVAL: error = NFS4ERR_INVAL; break; default: error = NFS4ERR_BADOWNER; break; } break; case NFS4ATTR_VERIT: ASSERT(sarg->vap->va_mask & AT_GID); error = nfs_idmap_str_gid(&na->owner_group, &gid, TRUE); /* * Ignore warning that we are the nfsmapid (can't happen on srv) */ if (error == ENOTSUP) error = 0; if (error) error = -1; /* no match */ else if (sarg->vap->va_gid != gid) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: if (sarg->op == NFS4ATTR_GETIT) { if (na->owner_group.utf8string_val) { UTF8STRING_FREE(na->owner_group) bzero(&na->owner_group, sizeof (na->owner_group)); } } break; } return (error); } /* XXX - quota attributes should be supportable on Solaris 2 */ /* ARGSUSED */ static int rfs4_fattr4_quota_avail_hard(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { return (ENOTSUP); } /* ARGSUSED */ static int rfs4_fattr4_quota_avail_soft(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { return (ENOTSUP); } /* ARGSUSED */ static int rfs4_fattr4_quota_used(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { return (ENOTSUP); } /* ARGSUSED */ static int rfs4_fattr4_rawdev(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_RDEV)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->vap->va_mask & AT_RDEV); na->rawdev.specdata1 = (uint32)getmajor(sarg->vap->va_rdev); na->rawdev.specdata2 = (uint32)getminor(sarg->vap->va_rdev); break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: ASSERT(sarg->vap->va_mask & AT_RDEV); if ((na->rawdev.specdata1 != (uint32)getmajor(sarg->vap->va_rdev)) || (na->rawdev.specdata2 != (uint32)getminor(sarg->vap->va_rdev))) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_space_avail(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && (sarg->sbp == NULL)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->sbp != NULL); if (sarg->sbp->f_bavail != (fsblkcnt64_t)-1) { na->space_avail = (fattr4_space_avail) sarg->sbp->f_frsize * (fattr4_space_avail) sarg->sbp->f_bavail; } else { na->space_avail = (fattr4_space_avail) sarg->sbp->f_bavail; } break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: ASSERT(sarg->sbp != NULL); if (sarg->sbp->f_bavail != na->space_avail) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_space_free(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && (sarg->sbp == NULL)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->sbp != NULL); if (sarg->sbp->f_bfree != (fsblkcnt64_t)-1) { na->space_free = (fattr4_space_free) sarg->sbp->f_frsize * (fattr4_space_free) sarg->sbp->f_bfree; } else { na->space_free = (fattr4_space_free) sarg->sbp->f_bfree; } break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: ASSERT(sarg->sbp != NULL); if (sarg->sbp->f_bfree != na->space_free) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_space_total(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error_req && (sarg->sbp == NULL)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->sbp != NULL); if (sarg->sbp->f_blocks != (fsblkcnt64_t)-1) { na->space_total = (fattr4_space_total) sarg->sbp->f_frsize * (fattr4_space_total) sarg->sbp->f_blocks; } else { na->space_total = (fattr4_space_total) sarg->sbp->f_blocks; } break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: ASSERT(sarg->sbp != NULL); if (sarg->sbp->f_blocks != na->space_total) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_space_used(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_NBLOCKS)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->vap->va_mask & AT_NBLOCKS); na->space_used = (fattr4_space_used) DEV_BSIZE * (fattr4_space_used) sarg->vap->va_nblocks; break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: ASSERT(sarg->vap->va_mask & AT_NBLOCKS); if (sarg->vap->va_nblocks != na->space_used) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_system(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { return (ENOTSUP); } /* ARGSUSED */ static int rfs4_fattr4_time_access(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; timestruc_t atime; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_ATIME)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->vap->va_mask & AT_ATIME); error = nfs4_time_vton(&sarg->vap->va_atime, &na->time_access); break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: ASSERT(sarg->vap->va_mask & AT_ATIME); error = nfs4_time_ntov(&na->time_access, &atime); if (error) break; if (bcmp(&atime, &sarg->vap->va_atime, sizeof (atime))) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* * XXX - need to support the setting of access time */ /* ARGSUSED */ static int rfs4_fattr4_time_access_set(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; settime4 *ta; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if ((sarg->op == NFS4ATTR_GETIT) || (sarg->op == NFS4ATTR_VERIT)) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: case NFS4ATTR_VERIT: /* * write only attr */ error = EINVAL; break; case NFS4ATTR_SETIT: ASSERT(sarg->vap->va_mask & AT_ATIME); /* * Set access time (by server or by client) */ ta = &na->time_access_set; if (ta->set_it == SET_TO_CLIENT_TIME4) { error = nfs4_time_ntov(&ta->time, &sarg->vap->va_atime); } else if (ta->set_it == SET_TO_SERVER_TIME4) { gethrestime(&sarg->vap->va_atime); } else { error = EINVAL; } break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_time_backup(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { return (ENOTSUP); } /* ARGSUSED */ static int rfs4_fattr4_time_create(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { return (ENOTSUP); } /* ARGSUSED */ static int rfs4_fattr4_time_delta(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: na->time_delta.seconds = 0; na->time_delta.nseconds = 1000; break; case NFS4ATTR_SETIT: /* * write only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: if ((na->time_delta.seconds != 0) || (na->time_delta.nseconds != 1000)) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_time_metadata(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; timestruc_t ctime; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_CTIME)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->vap->va_mask & AT_CTIME); error = nfs4_time_vton(&sarg->vap->va_ctime, &na->time_metadata); break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: ASSERT(sarg->vap->va_mask & AT_CTIME); error = nfs4_time_ntov(&na->time_metadata, &ctime); if (error) break; if (bcmp(&ctime, &sarg->vap->va_ctime, sizeof (ctime))) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* ARGSUSED */ static int rfs4_fattr4_time_modify(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; timestruc_t mtime; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if (sarg->op == NFS4ATTR_SETIT) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_MTIME)) { error = -1; /* may be okay if rdattr_error */ break; } ASSERT(sarg->vap->va_mask & AT_MTIME); error = nfs4_time_vton(&sarg->vap->va_mtime, &na->time_modify); break; case NFS4ATTR_SETIT: /* * read-only attr */ error = EINVAL; break; case NFS4ATTR_VERIT: ASSERT(sarg->vap->va_mask & AT_MTIME); error = nfs4_time_ntov(&na->time_modify, &mtime); if (error) break; if (bcmp(&mtime, &sarg->vap->va_mtime, sizeof (mtime))) error = -1; /* no match */ break; case NFS4ATTR_FREEIT: break; } return (error); } /* * XXX - need to add support for setting modify time */ /* ARGSUSED */ static int rfs4_fattr4_time_modify_set(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; settime4 *tm; if (RFS4_MANDATTR_ONLY) return (ENOTSUP); switch (cmd) { case NFS4ATTR_SUPPORTED: if ((sarg->op == NFS4ATTR_GETIT) || (sarg->op == NFS4ATTR_VERIT)) error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: case NFS4ATTR_VERIT: /* * write only attr */ error = EINVAL; break; case NFS4ATTR_SETIT: ASSERT(sarg->vap->va_mask & AT_MTIME); /* * Set modify time (by server or by client) */ tm = &na->time_modify_set; if (tm->set_it == SET_TO_CLIENT_TIME4) { error = nfs4_time_ntov(&tm->time, &sarg->vap->va_mtime); sarg->flag = ATTR_UTIME; } else if (tm->set_it == SET_TO_SERVER_TIME4) { gethrestime(&sarg->vap->va_mtime); } else { error = EINVAL; } break; case NFS4ATTR_FREEIT: break; } return (error); } static void rfs4_ntov_init(void) { /* index must be same as corresponding FATTR4_* define */ nfs4_ntov_map[0].sv_getit = rfs4_fattr4_supported_attrs; nfs4_ntov_map[1].sv_getit = rfs4_fattr4_type; nfs4_ntov_map[2].sv_getit = rfs4_fattr4_fh_expire_type; nfs4_ntov_map[3].sv_getit = rfs4_fattr4_change; nfs4_ntov_map[4].sv_getit = rfs4_fattr4_size; nfs4_ntov_map[5].sv_getit = rfs4_fattr4_link_support; nfs4_ntov_map[6].sv_getit = rfs4_fattr4_symlink_support; nfs4_ntov_map[7].sv_getit = rfs4_fattr4_named_attr; nfs4_ntov_map[8].sv_getit = rfs4_fattr4_fsid; nfs4_ntov_map[9].sv_getit = rfs4_fattr4_unique_handles; nfs4_ntov_map[10].sv_getit = rfs4_fattr4_lease_time; nfs4_ntov_map[11].sv_getit = rfs4_fattr4_rdattr_error; nfs4_ntov_map[12].sv_getit = rfs4_fattr4_acl; nfs4_ntov_map[13].sv_getit = rfs4_fattr4_aclsupport; nfs4_ntov_map[14].sv_getit = rfs4_fattr4_archive; nfs4_ntov_map[15].sv_getit = rfs4_fattr4_cansettime; nfs4_ntov_map[16].sv_getit = rfs4_fattr4_case_insensitive; nfs4_ntov_map[17].sv_getit = rfs4_fattr4_case_preserving; nfs4_ntov_map[18].sv_getit = rfs4_fattr4_chown_restricted; nfs4_ntov_map[19].sv_getit = rfs4_fattr4_filehandle; nfs4_ntov_map[20].sv_getit = rfs4_fattr4_fileid; nfs4_ntov_map[21].sv_getit = rfs4_fattr4_files_avail; nfs4_ntov_map[22].sv_getit = rfs4_fattr4_files_free; nfs4_ntov_map[23].sv_getit = rfs4_fattr4_files_total; nfs4_ntov_map[24].sv_getit = rfs4_fattr4_fs_locations; nfs4_ntov_map[25].sv_getit = rfs4_fattr4_hidden; nfs4_ntov_map[26].sv_getit = rfs4_fattr4_homogeneous; nfs4_ntov_map[27].sv_getit = rfs4_fattr4_maxfilesize; nfs4_ntov_map[28].sv_getit = rfs4_fattr4_maxlink; nfs4_ntov_map[29].sv_getit = rfs4_fattr4_maxname; nfs4_ntov_map[30].sv_getit = rfs4_fattr4_maxread; nfs4_ntov_map[31].sv_getit = rfs4_fattr4_maxwrite; nfs4_ntov_map[32].sv_getit = rfs4_fattr4_mimetype; nfs4_ntov_map[33].sv_getit = rfs4_fattr4_mode; nfs4_ntov_map[34].sv_getit = rfs4_fattr4_no_trunc; nfs4_ntov_map[35].sv_getit = rfs4_fattr4_numlinks; nfs4_ntov_map[36].sv_getit = rfs4_fattr4_owner; nfs4_ntov_map[37].sv_getit = rfs4_fattr4_owner_group; nfs4_ntov_map[38].sv_getit = rfs4_fattr4_quota_avail_hard; nfs4_ntov_map[39].sv_getit = rfs4_fattr4_quota_avail_soft; nfs4_ntov_map[40].sv_getit = rfs4_fattr4_quota_used; nfs4_ntov_map[41].sv_getit = rfs4_fattr4_rawdev; nfs4_ntov_map[42].sv_getit = rfs4_fattr4_space_avail; nfs4_ntov_map[43].sv_getit = rfs4_fattr4_space_free; nfs4_ntov_map[44].sv_getit = rfs4_fattr4_space_total; nfs4_ntov_map[45].sv_getit = rfs4_fattr4_space_used; nfs4_ntov_map[46].sv_getit = rfs4_fattr4_system; nfs4_ntov_map[47].sv_getit = rfs4_fattr4_time_access; nfs4_ntov_map[48].sv_getit = rfs4_fattr4_time_access_set; nfs4_ntov_map[49].sv_getit = rfs4_fattr4_time_backup; nfs4_ntov_map[50].sv_getit = rfs4_fattr4_time_create; nfs4_ntov_map[51].sv_getit = rfs4_fattr4_time_delta; nfs4_ntov_map[52].sv_getit = rfs4_fattr4_time_metadata; nfs4_ntov_map[53].sv_getit = rfs4_fattr4_time_modify; nfs4_ntov_map[54].sv_getit = rfs4_fattr4_time_modify_set; nfs4_ntov_map[55].sv_getit = rfs4_fattr4_mounted_on_fileid; }