--- /dev/null
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+/*
+ * miscellaneous support functions for NFSv4
+ */
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/kauth.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mount_internal.h>
+#include <sys/vnode_internal.h>
+#include <sys/kpi_mbuf.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/malloc.h>
+#include <sys/syscall.h>
+#include <sys/ubc_internal.h>
+#include <sys/fcntl.h>
+#include <sys/quota.h>
+#include <sys/uio_internal.h>
+#include <sys/domain.h>
+#include <libkern/OSAtomic.h>
+#include <kern/thread_call.h>
+
+#include <sys/vm.h>
+#include <sys/vmparam.h>
+
+#include <sys/time.h>
+#include <kern/clock.h>
+
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsnode.h>
+#include <nfs/xdr_subs.h>
+#include <nfs/nfsm_subs.h>
+#include <nfs/nfs_gss.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nfs_lock.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <netinet/in.h>
+#include <net/kpi_interface.h>
+
+
+/*
+ * NFSv4 SETCLIENTID
+ */
+int
+nfs4_setclientid(struct nfsmount *nmp)
+{
+ struct sockaddr *saddr;
+ uint64_t verifier;
+ char id[128];
+ int idlen, len, error = 0, status, numops;
+ u_int64_t xid;
+ vfs_context_t ctx;
+ thread_t thd;
+ kauth_cred_t cred;
+ struct nfsm_chain nmreq, nmrep;
+
+ static uint8_t en0addr[6];
+ static uint8_t en0addr_set = 0;
+
+ lck_mtx_lock(nfs_request_mutex);
+ if (!en0addr_set) {
+ ifnet_t interface = NULL;
+ error = ifnet_find_by_name("en0", &interface);
+ if (!error)
+ error = ifnet_lladdr_copy_bytes(interface, en0addr, sizeof(en0addr));
+ if (error)
+ printf("nfs4_setclientid: error getting en0 address, %d\n", error);
+ if (!error)
+ en0addr_set = 1;
+ error = 0;
+ if (interface)
+ ifnet_release(interface);
+ }
+ lck_mtx_unlock(nfs_request_mutex);
+
+ ctx = vfs_context_kernel(); /* XXX */
+ thd = vfs_context_thread(ctx);
+ cred = vfs_context_ucred(ctx);
+
+ nfsm_chain_null(&nmreq);
+ nfsm_chain_null(&nmrep);
+
+ /* ID: en0_address + server_address */
+ idlen = len = sizeof(en0addr);
+ bcopy(en0addr, &id[0], len);
+ saddr = mbuf_data(nmp->nm_nam);
+ len = min(saddr->sa_len, sizeof(id)-idlen);
+ bcopy(saddr, &id[idlen], len);
+ idlen += len;
+
+ // SETCLIENTID
+ numops = 1;
+ nfsm_chain_build_alloc_init(error, &nmreq, 14 * NFSX_UNSIGNED + idlen);
+ nfsm_chain_add_compound_header(error, &nmreq, "setclientid", numops);
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_SETCLIENTID);
+ /* nfs_client_id4 client; */
+ nfsm_chain_add_64(error, &nmreq, nmp->nm_mounttime);
+ nfsm_chain_add_32(error, &nmreq, idlen);
+ nfsm_chain_add_opaque(error, &nmreq, id, idlen);
+ /* cb_client4 callback; */
+ /* We don't provide callback info yet */
+ nfsm_chain_add_32(error, &nmreq, 0); /* callback program */
+ nfsm_chain_add_string(error, &nmreq, "", 0); /* callback r_netid */
+ nfsm_chain_add_string(error, &nmreq, "", 0); /* callback r_addr */
+ nfsm_chain_add_32(error, &nmreq, 0); /* callback_ident */
+ nfsm_chain_build_done(error, &nmreq);
+ nfsm_assert(error, (numops == 0), EPROTO);
+ nfsmout_if(error);
+ error = nfs_request2(NULL, nmp->nm_mountp, &nmreq, NFSPROC4_COMPOUND, thd, cred, R_SETUP, &nmrep, &xid, &status);
+ nfsm_chain_skip_tag(error, &nmrep);
+ nfsm_chain_get_32(error, &nmrep, numops);
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_SETCLIENTID);
+ if (error == NFSERR_CLID_INUSE)
+ printf("nfs4_setclientid: client ID in use?\n");
+ nfsmout_if(error);
+ nfsm_chain_get_64(error, &nmrep, nmp->nm_clientid);
+ nfsm_chain_get_64(error, &nmrep, verifier);
+ nfsm_chain_cleanup(&nmreq);
+ nfsm_chain_cleanup(&nmrep);
+
+ // SETCLIENTID_CONFIRM
+ numops = 1;
+ nfsm_chain_build_alloc_init(error, &nmreq, 13 * NFSX_UNSIGNED);
+ nfsm_chain_add_compound_header(error, &nmreq, "setclientid_confirm", numops);
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_SETCLIENTID_CONFIRM);
+ nfsm_chain_add_64(error, &nmreq, nmp->nm_clientid);
+ nfsm_chain_add_64(error, &nmreq, verifier);
+ nfsm_chain_build_done(error, &nmreq);
+ nfsm_assert(error, (numops == 0), EPROTO);
+ nfsmout_if(error);
+ error = nfs_request2(NULL, nmp->nm_mountp, &nmreq, NFSPROC4_COMPOUND, thd, cred, R_SETUP, &nmrep, &xid, &status);
+ nfsm_chain_skip_tag(error, &nmrep);
+ nfsm_chain_get_32(error, &nmrep, numops);
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_SETCLIENTID_CONFIRM);
+ if (error)
+ printf("nfs4_setclientid: confirm error %d\n", error);
+nfsmout:
+ nfsm_chain_cleanup(&nmreq);
+ nfsm_chain_cleanup(&nmrep);
+ if (error)
+ printf("nfs4_setclientid failed, %d\n", error);
+ return (error);
+}
+
+/*
+ * periodic timer to renew lease state on server
+ */
+void
+nfs4_renew_timer(void *param0, __unused void *param1)
+{
+ struct nfsmount *nmp = param0;
+ int error = 0, status, numops, interval;
+ u_int64_t xid;
+ vfs_context_t ctx;
+ struct nfsm_chain nmreq, nmrep;
+
+ ctx = vfs_context_kernel(); /* XXX */
+
+ nfsm_chain_null(&nmreq);
+ nfsm_chain_null(&nmrep);
+
+ // RENEW
+ numops = 1;
+ nfsm_chain_build_alloc_init(error, &nmreq, 8 * NFSX_UNSIGNED);
+ nfsm_chain_add_compound_header(error, &nmreq, "renew", numops);
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_RENEW);
+ nfsm_chain_add_64(error, &nmreq, nmp->nm_clientid);
+ nfsm_chain_build_done(error, &nmreq);
+ nfsm_assert(error, (numops == 0), EPROTO);
+ nfsmout_if(error);
+ error = nfs_request(NULL, nmp->nm_mountp, &nmreq, NFSPROC4_COMPOUND, ctx, &nmrep, &xid, &status);
+ nfsm_chain_skip_tag(error, &nmrep);
+ nfsm_chain_get_32(error, &nmrep, numops);
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_RENEW);
+nfsmout:
+ if (error)
+ printf("nfs4_renew_timer: error %d\n", error);
+ nfsm_chain_cleanup(&nmreq);
+ nfsm_chain_cleanup(&nmrep);
+
+ interval = nmp->nm_fsattr.nfsa_lease / (error ? 4 : 2);
+ if (interval < 1)
+ interval = 1;
+ nfs_interval_timer_start(nmp->nm_renew_timer, interval * 1000);
+}
+
+/*
+ * Set a vnode attr's supported bits according to the given bitmap
+ */
+void
+nfs_vattr_set_supported(uint32_t *bitmap, struct vnode_attr *vap)
+{
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TYPE))
+ VATTR_SET_SUPPORTED(vap, va_type);
+ // if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_CHANGE))
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SIZE))
+ VATTR_SET_SUPPORTED(vap, va_data_size);
+ // if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_NAMED_ATTR))
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FSID))
+ VATTR_SET_SUPPORTED(vap, va_fsid);
+// if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_ACL))
+// VATTR_SET_SUPPORTED(vap, va_acl);
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_ARCHIVE))
+ VATTR_SET_SUPPORTED(vap, va_flags);
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FILEID))
+ VATTR_SET_SUPPORTED(vap, va_fileid);
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_HIDDEN))
+ VATTR_SET_SUPPORTED(vap, va_flags);
+ // if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MIMETYPE))
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MODE))
+ VATTR_SET_SUPPORTED(vap, va_mode);
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_NUMLINKS))
+ VATTR_SET_SUPPORTED(vap, va_nlink);
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_OWNER))
+ VATTR_SET_SUPPORTED(vap, va_uid);
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_OWNER_GROUP))
+ VATTR_SET_SUPPORTED(vap, va_gid);
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_RAWDEV))
+ VATTR_SET_SUPPORTED(vap, va_rdev);
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SPACE_USED))
+ VATTR_SET_SUPPORTED(vap, va_total_alloc);
+ // if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SYSTEM))
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_ACCESS))
+ VATTR_SET_SUPPORTED(vap, va_access_time);
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_BACKUP))
+ VATTR_SET_SUPPORTED(vap, va_backup_time);
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_CREATE))
+ VATTR_SET_SUPPORTED(vap, va_create_time);
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_METADATA))
+ VATTR_SET_SUPPORTED(vap, va_change_time);
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_MODIFY))
+ VATTR_SET_SUPPORTED(vap, va_modify_time);
+}
+
+/*
+ * Parse the attributes that are in the mbuf list and store them in
+ * the given structures.
+ */
+int
+nfs4_parsefattr(
+ struct nfsm_chain *nmc,
+ struct nfs_fsattr *nfsap,
+ struct nfs_vattr *nvap,
+ fhandle_t *fhp,
+ struct dqblk *dqbp)
+{
+ int error = 0, attrbytes;
+ uint32_t val, val2, val3, i, j;
+ uint32_t bitmap[NFS_ATTR_BITMAP_LEN], len;
+ char *s;
+ struct nfs_fsattr nfsa_dummy;
+ struct nfs_vattr nva_dummy;
+ struct dqblk dqb_dummy;
+
+ /* if not interested in some values... throw 'em into a local dummy variable */
+ if (!nfsap)
+ nfsap = &nfsa_dummy;
+ if (!nvap)
+ nvap = &nva_dummy;
+ if (!dqbp)
+ dqbp = &dqb_dummy;
+
+ attrbytes = val = val2 = val3 = 0;
+
+ len = NFS_ATTR_BITMAP_LEN;
+ nfsm_chain_get_bitmap(error, nmc, bitmap, len);
+ /* add bits to object/fs attr bitmaps */
+ for (i=0; i < NFS_ATTR_BITMAP_LEN; i++) {
+ nvap->nva_bitmap[i] |= bitmap[i] & nfs_object_attr_bitmap[i];
+ nfsap->nfsa_bitmap[i] |= bitmap[i] & nfs_fs_attr_bitmap[i];
+ }
+
+ nfsm_chain_get_32(error, nmc, attrbytes);
+ nfsmout_if(error);
+
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SUPPORTED_ATTRS)) {
+ len = NFS_ATTR_BITMAP_LEN;
+ nfsm_chain_get_bitmap(error, nmc, nfsap->nfsa_supp_attr, len);
+ attrbytes -= (len + 1) * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TYPE)) {
+ nfsm_chain_get_32(error, nmc, val);
+ nvap->nva_type = nfstov_type(val, NFS_VER4);
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FH_EXPIRE_TYPE)) {
+ nfsm_chain_get_32(error, nmc, val);
+ nfsmout_if(error);
+ if (val != NFS_FH_PERSISTENT)
+ printf("nfs: warning: non-persistent file handles!\n");
+ if (val & ~0xff)
+ printf("nfs: warning unknown fh type: 0x%x\n", val);
+ nfsap->nfsa_flags &= ~NFS_FSFLAG_FHTYPE_MASK;
+ nfsap->nfsa_flags |= val << 24;
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_CHANGE)) {
+ nfsm_chain_get_64(error, nmc, nvap->nva_change);
+ attrbytes -= 2 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SIZE)) {
+ nfsm_chain_get_64(error, nmc, nvap->nva_size);
+ attrbytes -= 2 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_LINK_SUPPORT)) {
+ nfsm_chain_get_32(error, nmc, val);
+ if (val)
+ nfsap->nfsa_flags |= NFS_FSFLAG_LINK;
+ else
+ nfsap->nfsa_flags &= ~NFS_FSFLAG_LINK;
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SYMLINK_SUPPORT)) {
+ nfsm_chain_get_32(error, nmc, val);
+ if (val)
+ nfsap->nfsa_flags |= NFS_FSFLAG_SYMLINK;
+ else
+ nfsap->nfsa_flags &= ~NFS_FSFLAG_SYMLINK;
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_NAMED_ATTR)) {
+ nfsm_chain_get_32(error, nmc, val);
+ if (val)
+ nvap->nva_flags |= NFS_FFLAG_NAMED_ATTR;
+ else
+ nvap->nva_flags &= ~NFS_FFLAG_NAMED_ATTR;
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FSID)) {
+ nfsm_chain_get_64(error, nmc, nvap->nva_fsid.major);
+ nfsm_chain_get_64(error, nmc, nvap->nva_fsid.minor);
+ attrbytes -= 4 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_UNIQUE_HANDLES)) {
+ nfsm_chain_get_32(error, nmc, val);
+ if (val)
+ nfsap->nfsa_flags |= NFS_FSFLAG_UNIQUE_FH;
+ else
+ nfsap->nfsa_flags &= ~NFS_FSFLAG_UNIQUE_FH;
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_LEASE_TIME)) {
+ nfsm_chain_get_32(error, nmc, nfsap->nfsa_lease);
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_RDATTR_ERROR)) {
+ nfsm_chain_get_32(error, nmc, error);
+ attrbytes -= NFSX_UNSIGNED;
+ nfsmout_if(error);
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_ACL)) { /* skip for now */
+ nfsm_chain_get_32(error, nmc, val); /* ACE count */
+ for (i=0; !error && (i < val); i++) {
+ nfsm_chain_adv(error, nmc, 3 * NFSX_UNSIGNED);
+ nfsm_chain_get_32(error, nmc, val2); /* string length */
+ nfsm_chain_adv(error, nmc, nfsm_rndup(val2));
+ attrbytes -= 4*NFSX_UNSIGNED + nfsm_rndup(val2);
+ nfsm_assert(error, (attrbytes >= 0), EBADRPC);
+ }
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_ACLSUPPORT)) {
+ nfsm_chain_get_32(error, nmc, val);
+ if (val)
+ nfsap->nfsa_flags |= NFS_FSFLAG_ACL;
+ else
+ nfsap->nfsa_flags &= ~NFS_FSFLAG_ACL;
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_ARCHIVE)) { /* SF_ARCHIVED */
+ nfsm_chain_get_32(error, nmc, val);
+ if (val)
+ nvap->nva_flags |= NFS_FFLAG_ARCHIVED;
+ else
+ nvap->nva_flags &= ~NFS_FFLAG_ARCHIVED;
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_CANSETTIME)) {
+ nfsm_chain_get_32(error, nmc, val);
+ if (val)
+ nfsap->nfsa_flags |= NFS_FSFLAG_SET_TIME;
+ else
+ nfsap->nfsa_flags &= ~NFS_FSFLAG_SET_TIME;
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_CASE_INSENSITIVE)) {
+ nfsm_chain_get_32(error, nmc, val);
+ if (val)
+ nfsap->nfsa_flags |= NFS_FSFLAG_CASE_INSENSITIVE;
+ else
+ nfsap->nfsa_flags &= ~NFS_FSFLAG_CASE_INSENSITIVE;
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_CASE_PRESERVING)) {
+ nfsm_chain_get_32(error, nmc, val);
+ if (val)
+ nfsap->nfsa_flags |= NFS_FSFLAG_CASE_PRESERVING;
+ else
+ nfsap->nfsa_flags &= ~NFS_FSFLAG_CASE_PRESERVING;
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_CHOWN_RESTRICTED)) {
+ nfsm_chain_get_32(error, nmc, val);
+ if (val)
+ nfsap->nfsa_flags |= NFS_FSFLAG_CHOWN_RESTRICTED;
+ else
+ nfsap->nfsa_flags &= ~NFS_FSFLAG_CHOWN_RESTRICTED;
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FILEHANDLE)) {
+ nfsm_chain_get_32(error, nmc, val);
+ if (fhp) {
+ fhp->fh_len = val;
+ nfsm_chain_get_opaque(error, nmc, nfsm_rndup(val), fhp->fh_data);
+ } else {
+ nfsm_chain_adv(error, nmc, nfsm_rndup(val));
+ }
+ attrbytes -= NFSX_UNSIGNED + nfsm_rndup(val);
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FILEID)) {
+ nfsm_chain_get_64(error, nmc, nvap->nva_fileid);
+ attrbytes -= 2 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FILES_AVAIL)) {
+ nfsm_chain_get_64(error, nmc, nfsap->nfsa_files_avail);
+ attrbytes -= 2 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FILES_FREE)) {
+ nfsm_chain_get_64(error, nmc, nfsap->nfsa_files_free);
+ attrbytes -= 2 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FILES_TOTAL)) {
+ nfsm_chain_get_64(error, nmc, nfsap->nfsa_files_total);
+ attrbytes -= 2 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FS_LOCATIONS)) { /* skip for now */
+ nfsm_chain_get_32(error, nmc, val); /* root path length */
+ nfsm_chain_adv(error, nmc, nfsm_rndup(val)); /* root path */
+ attrbytes -= (2 * NFSX_UNSIGNED) + nfsm_rndup(val);
+ nfsm_chain_get_32(error, nmc, val); /* location count */
+ for (i=0; !error && (i < val); i++) {
+ nfsm_chain_get_32(error, nmc, val2); /* server string length */
+ nfsm_chain_adv(error, nmc, nfsm_rndup(val2)); /* server string */
+ attrbytes -= (2 * NFSX_UNSIGNED) + nfsm_rndup(val2);
+ nfsm_chain_get_32(error, nmc, val2); /* pathname component count */
+ for (j=0; !error && (j < val2); j++) {
+ nfsm_chain_get_32(error, nmc, val3); /* component length */
+ nfsm_chain_adv(error, nmc, nfsm_rndup(val3)); /* component */
+ attrbytes -= NFSX_UNSIGNED + nfsm_rndup(val3);
+ nfsm_assert(error, (attrbytes >= 0), EBADRPC);
+ }
+ nfsm_assert(error, (attrbytes >= 0), EBADRPC);
+ }
+ nfsm_assert(error, (attrbytes >= 0), EBADRPC);
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_HIDDEN)) { /* UF_HIDDEN */
+ nfsm_chain_get_32(error, nmc, val);
+ if (val)
+ nvap->nva_flags |= NFS_FFLAG_HIDDEN;
+ else
+ nvap->nva_flags &= ~NFS_FFLAG_HIDDEN;
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_HOMOGENEOUS)) {
+ /* XXX If NOT homogeneous, we may need to clear flags on the mount */
+ nfsm_chain_get_32(error, nmc, val);
+ if (val)
+ nfsap->nfsa_flags |= NFS_FSFLAG_HOMOGENEOUS;
+ else
+ nfsap->nfsa_flags &= ~NFS_FSFLAG_HOMOGENEOUS;
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MAXFILESIZE)) {
+ nfsm_chain_get_64(error, nmc, nfsap->nfsa_maxfilesize);
+ attrbytes -= 2 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MAXLINK)) {
+ nfsm_chain_get_32(error, nmc, nvap->nva_maxlink);
+ if (!error && (nfsap->nfsa_maxlink > INT32_MAX))
+ nfsap->nfsa_maxlink = INT32_MAX;
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MAXNAME)) {
+ nfsm_chain_get_32(error, nmc, nfsap->nfsa_maxname);
+ if (!error && (nfsap->nfsa_maxname > INT32_MAX))
+ nfsap->nfsa_maxname = INT32_MAX;
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MAXREAD)) {
+ nfsm_chain_get_64(error, nmc, nfsap->nfsa_maxread);
+ attrbytes -= 2 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MAXWRITE)) {
+ nfsm_chain_get_64(error, nmc, nfsap->nfsa_maxwrite);
+ attrbytes -= 2 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MIMETYPE)) {
+ nfsm_chain_get_32(error, nmc, val);
+ nfsm_chain_adv(error, nmc, nfsm_rndup(val));
+ attrbytes -= NFSX_UNSIGNED + nfsm_rndup(val);
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MODE)) {
+ nfsm_chain_get_32(error, nmc, nvap->nva_mode);
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_NO_TRUNC)) {
+ nfsm_chain_get_32(error, nmc, val);
+ if (val)
+ nfsap->nfsa_flags |= NFS_FSFLAG_NO_TRUNC;
+ else
+ nfsap->nfsa_flags &= ~NFS_FSFLAG_NO_TRUNC;
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_NUMLINKS)) {
+ nfsm_chain_get_32(error, nmc, val);
+ nvap->nva_nlink = val;
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_OWNER)) { /* XXX ugly hack for now */
+ nfsm_chain_get_32(error, nmc, len);
+ nfsm_chain_get_opaque_pointer(error, nmc, len, s);
+ attrbytes -= NFSX_UNSIGNED + nfsm_rndup(len);
+ nfsmout_if(error);
+ if ((*s >= '0') && (*s <= '9'))
+ nvap->nva_uid = strtol(s, NULL, 10);
+ else if (!strncmp(s, "nobody@", 7))
+ nvap->nva_uid = -2;
+ else if (!strncmp(s, "root@", 5))
+ nvap->nva_uid = 0;
+ else
+ nvap->nva_uid = 99; /* unknown */
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_OWNER_GROUP)) { /* XXX ugly hack for now */
+ nfsm_chain_get_32(error, nmc, len);
+ nfsm_chain_get_opaque_pointer(error, nmc, len, s);
+ attrbytes -= NFSX_UNSIGNED + nfsm_rndup(len);
+ nfsmout_if(error);
+ if ((*s >= '0') && (*s <= '9'))
+ nvap->nva_gid = strtol(s, NULL, 10);
+ else if (!strncmp(s, "nobody@", 7))
+ nvap->nva_gid = -2;
+ else if (!strncmp(s, "root@", 5))
+ nvap->nva_uid = 0;
+ else
+ nvap->nva_gid = 99; /* unknown */
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_QUOTA_AVAIL_HARD)) {
+ nfsm_chain_get_64(error, nmc, dqbp->dqb_bhardlimit);
+ attrbytes -= 2 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_QUOTA_AVAIL_SOFT)) {
+ nfsm_chain_get_64(error, nmc, dqbp->dqb_bsoftlimit);
+ attrbytes -= 2 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_QUOTA_USED)) {
+ nfsm_chain_get_64(error, nmc, dqbp->dqb_curbytes);
+ attrbytes -= 2 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_RAWDEV)) {
+ nfsm_chain_get_32(error, nmc, nvap->nva_rawdev.specdata1);
+ nfsm_chain_get_32(error, nmc, nvap->nva_rawdev.specdata2);
+ attrbytes -= 2 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SPACE_AVAIL)) {
+ nfsm_chain_get_64(error, nmc, nfsap->nfsa_space_avail);
+ attrbytes -= 2 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SPACE_FREE)) {
+ nfsm_chain_get_64(error, nmc, nfsap->nfsa_space_free);
+ attrbytes -= 2 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SPACE_TOTAL)) {
+ nfsm_chain_get_64(error, nmc, nfsap->nfsa_space_total);
+ attrbytes -= 2 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SPACE_USED)) {
+ nfsm_chain_get_64(error, nmc, nvap->nva_bytes);
+ attrbytes -= 2 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SYSTEM)) {
+ /* we'd support this if we had a flag to map it to... */
+ nfsm_chain_adv(error, nmc, NFSX_UNSIGNED);
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_ACCESS)) {
+ nfsm_chain_get_64(error, nmc, nvap->nva_timesec[NFSTIME_ACCESS]);
+ nfsm_chain_get_32(error, nmc, nvap->nva_timensec[NFSTIME_ACCESS]);
+ attrbytes -= 3 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_ACCESS_SET)) {
+ nfsm_chain_adv(error, nmc, 4*NFSX_UNSIGNED); /* just skip it */
+ attrbytes -= 4 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_BACKUP)) {
+ nfsm_chain_get_64(error, nmc, nvap->nva_timesec[NFSTIME_BACKUP]);
+ nfsm_chain_get_32(error, nmc, nvap->nva_timensec[NFSTIME_BACKUP]);
+ attrbytes -= 3 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_CREATE)) {
+ nfsm_chain_get_64(error, nmc, nvap->nva_timesec[NFSTIME_CREATE]);
+ nfsm_chain_get_32(error, nmc, nvap->nva_timensec[NFSTIME_CREATE]);
+ attrbytes -= 3 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_DELTA)) { /* skip for now */
+ nfsm_chain_adv(error, nmc, 3*NFSX_UNSIGNED);
+ attrbytes -= 3 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_METADATA)) {
+ nfsm_chain_get_64(error, nmc, nvap->nva_timesec[NFSTIME_CHANGE]);
+ nfsm_chain_get_32(error, nmc, nvap->nva_timensec[NFSTIME_CHANGE]);
+ attrbytes -= 3 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_MODIFY)) {
+ nfsm_chain_get_64(error, nmc, nvap->nva_timesec[NFSTIME_MODIFY]);
+ nfsm_chain_get_32(error, nmc, nvap->nva_timensec[NFSTIME_MODIFY]);
+ attrbytes -= 3 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_MODIFY_SET)) {
+ nfsm_chain_adv(error, nmc, 4*NFSX_UNSIGNED); /* just skip it */
+ attrbytes -= 4 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MOUNTED_ON_FILEID)) { /* skip for now */
+ nfsm_chain_adv(error, nmc, 2*NFSX_UNSIGNED);
+ attrbytes -= 2 * NFSX_UNSIGNED;
+ }
+ /* advance over any leftover attrbytes */
+ nfsm_assert(error, (attrbytes >= 0), EBADRPC);
+ nfsm_chain_adv(error, nmc, nfsm_rndup(attrbytes));
+nfsmout:
+ return (error);
+}
+
+/*
+ * Add an NFSv4 "sattr" structure to an mbuf chain
+ */
+int
+nfsm_chain_add_fattr4_f(struct nfsm_chain *nmc, struct vnode_attr *vap, struct nfsmount *nmp)
+{
+ int error = 0, attrbytes, slen, i;
+ uint32_t *pattrbytes;
+ uint32_t bitmap[NFS_ATTR_BITMAP_LEN];
+ char s[32];
+
+ /*
+ * Do this in two passes.
+ * First calculate the bitmap, then pack
+ * everything together and set the size.
+ */
+
+ NFS_CLEAR_ATTRIBUTES(bitmap);
+ if (VATTR_IS_ACTIVE(vap, va_data_size))
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_SIZE);
+ if (VATTR_IS_ACTIVE(vap, va_acl)) {
+ // NFS_BITMAP_SET(bitmap, NFS_FATTR_ACL)
+ }
+ if (VATTR_IS_ACTIVE(vap, va_flags)) {
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_ARCHIVE);
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_HIDDEN);
+ }
+ // NFS_BITMAP_SET(bitmap, NFS_FATTR_MIMETYPE)
+ if (VATTR_IS_ACTIVE(vap, va_mode))
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_MODE);
+ if (VATTR_IS_ACTIVE(vap, va_uid))
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_OWNER);
+ if (VATTR_IS_ACTIVE(vap, va_gid))
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_OWNER_GROUP);
+ // NFS_BITMAP_SET(bitmap, NFS_FATTR_SYSTEM)
+ if (vap->va_vaflags & VA_UTIMES_NULL) {
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_TIME_ACCESS_SET);
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_TIME_MODIFY_SET);
+ } else {
+ if (VATTR_IS_ACTIVE(vap, va_access_time))
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_TIME_ACCESS_SET);
+ if (VATTR_IS_ACTIVE(vap, va_modify_time))
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_TIME_MODIFY_SET);
+ }
+ if (VATTR_IS_ACTIVE(vap, va_backup_time))
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_TIME_BACKUP);
+ if (VATTR_IS_ACTIVE(vap, va_create_time))
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_TIME_CREATE);
+ /* and limit to what is supported by server */
+ for (i=0; i < NFS_ATTR_BITMAP_LEN; i++)
+ bitmap[i] &= nmp->nm_fsattr.nfsa_supp_attr[i];
+
+ /*
+ * Now pack it all together:
+ * BITMAP, #BYTES, ATTRS
+ * Keep a pointer to the length so we can set it later.
+ */
+ nfsm_chain_add_bitmap(error, nmc, bitmap, NFS_ATTR_BITMAP_LEN);
+ attrbytes = 0;
+ nfsm_chain_add_32(error, nmc, attrbytes);
+ pattrbytes = (uint32_t*)(nmc->nmc_ptr - NFSX_UNSIGNED);
+
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SIZE)) {
+ nfsm_chain_add_64(error, nmc, vap->va_data_size);
+ attrbytes += 2*NFSX_UNSIGNED;
+ }
+ // NFS_BITMAP_ISSET(bitmap, NFS_FATTR_ACL)
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_ARCHIVE)) {
+ nfsm_chain_add_32(error, nmc, (vap->va_flags & SF_ARCHIVED) ? 1 : 0);
+ attrbytes += NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_HIDDEN)) {
+ nfsm_chain_add_32(error, nmc, (vap->va_flags & UF_HIDDEN) ? 1 : 0);
+ attrbytes += NFSX_UNSIGNED;
+ }
+ // NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MIMETYPE)
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MODE)) {
+ nfsm_chain_add_32(error, nmc, vap->va_mode);
+ attrbytes += NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_OWNER)) {
+ slen = snprintf(s, sizeof(s), "%d", vap->va_uid);
+ nfsm_chain_add_string(error, nmc, s, slen);
+ attrbytes += NFSX_UNSIGNED + nfsm_rndup(slen);
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_OWNER_GROUP)) {
+ slen = snprintf(s, sizeof(s), "%d", vap->va_gid);
+ nfsm_chain_add_string(error, nmc, s, slen);
+ attrbytes += NFSX_UNSIGNED + nfsm_rndup(slen);
+ }
+ // NFS_BITMAP_SET(bitmap, NFS_FATTR_SYSTEM)
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_ACCESS_SET)) {
+ if (vap->va_vaflags & VA_UTIMES_NULL) {
+ nfsm_chain_add_32(error, nmc, NFS_TIME_SET_TO_SERVER);
+ attrbytes += NFSX_UNSIGNED;
+ } else {
+ nfsm_chain_add_32(error, nmc, NFS_TIME_SET_TO_CLIENT);
+ nfsm_chain_add_64(error, nmc, vap->va_access_time.tv_sec);
+ nfsm_chain_add_32(error, nmc, vap->va_access_time.tv_nsec);
+ attrbytes += 4*NFSX_UNSIGNED;
+ }
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_BACKUP)) {
+ nfsm_chain_add_64(error, nmc, vap->va_backup_time.tv_sec);
+ nfsm_chain_add_32(error, nmc, vap->va_backup_time.tv_nsec);
+ attrbytes += 3*NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_CREATE)) {
+ nfsm_chain_add_64(error, nmc, vap->va_create_time.tv_sec);
+ nfsm_chain_add_32(error, nmc, vap->va_create_time.tv_nsec);
+ attrbytes += 3*NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_MODIFY_SET)) {
+ if (vap->va_vaflags & VA_UTIMES_NULL) {
+ nfsm_chain_add_32(error, nmc, NFS_TIME_SET_TO_SERVER);
+ attrbytes += NFSX_UNSIGNED;
+ } else {
+ nfsm_chain_add_32(error, nmc, NFS_TIME_SET_TO_CLIENT);
+ nfsm_chain_add_64(error, nmc, vap->va_modify_time.tv_sec);
+ nfsm_chain_add_32(error, nmc, vap->va_modify_time.tv_nsec);
+ attrbytes += 4*NFSX_UNSIGNED;
+ }
+ }
+ nfsmout_if(error);
+ /* Now, set the attribute data length */
+ *pattrbytes = txdr_unsigned(attrbytes);
+nfsmout:
+ return (error);
+}
+