1/*
2 * Copyright (c) 2006-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29/*
30 * miscellaneous support functions for NFSv4
31 */
32#include <sys/param.h>
33#include <sys/proc.h>
34#include <sys/kauth.h>
35#include <sys/systm.h>
36#include <sys/kernel.h>
37#include <sys/mount_internal.h>
38#include <sys/vnode_internal.h>
39#include <sys/kpi_mbuf.h>
40#include <sys/socket.h>
41#include <sys/stat.h>
42#include <sys/malloc.h>
43#include <sys/syscall.h>
44#include <sys/ubc_internal.h>
45#include <sys/fcntl.h>
46#include <sys/quota.h>
47#include <sys/uio_internal.h>
48#include <sys/domain.h>
49#include <libkern/OSAtomic.h>
50#include <kern/thread_call.h>
51
52#include <sys/vm.h>
53#include <sys/vmparam.h>
54
55#include <sys/time.h>
56#include <kern/clock.h>
57
58#include <nfs/rpcv2.h>
59#include <nfs/nfsproto.h>
60#include <nfs/nfs.h>
61#include <nfs/nfsnode.h>
62#include <nfs/xdr_subs.h>
63#include <nfs/nfsm_subs.h>
64#include <nfs/nfs_gss.h>
65#include <nfs/nfsmount.h>
66#include <nfs/nfs_lock.h>
67
68#include <miscfs/specfs/specdev.h>
69
70#include <netinet/in.h>
71#include <net/kpi_interface.h>
72
73
74/*
75 * NFSv4 SETCLIENTID
76 */
77int
78nfs4_setclientid(struct nfsmount *nmp)
79{
80	struct sockaddr *saddr;
81	uint64_t verifier;
82	char id[128];
83	int idlen, len, error = 0, status, numops;
84	u_int64_t xid;
85	vfs_context_t ctx;
86	thread_t thd;
87	kauth_cred_t cred;
88	struct nfsm_chain nmreq, nmrep;
89
90	static uint8_t en0addr[6];
91	static uint8_t en0addr_set = 0;
92
93	lck_mtx_lock(nfs_request_mutex);
94	if (!en0addr_set) {
95		ifnet_t interface = NULL;
96		error = ifnet_find_by_name("en0", &interface);
97		if (!error)
98			error = ifnet_lladdr_copy_bytes(interface, en0addr, sizeof(en0addr));
99		if (error)
100			printf("nfs4_setclientid: error getting en0 address, %d\n", error);
101		if (!error)
102			en0addr_set = 1;
103		error = 0;
104		if (interface)
105			ifnet_release(interface);
106	}
107	lck_mtx_unlock(nfs_request_mutex);
108
109	ctx = vfs_context_kernel(); /* XXX */
110	thd = vfs_context_thread(ctx);
111	cred = vfs_context_ucred(ctx);
112
113	nfsm_chain_null(&nmreq);
114	nfsm_chain_null(&nmrep);
115
116	/* ID: en0_address + server_address */
117	idlen = len = sizeof(en0addr);
118	bcopy(en0addr, &id[0], len);
119	saddr = mbuf_data(nmp->nm_nam);
120	len = min(saddr->sa_len, sizeof(id)-idlen);
121	bcopy(saddr, &id[idlen], len);
122	idlen += len;
123
124	// SETCLIENTID
125	numops = 1;
126	nfsm_chain_build_alloc_init(error, &nmreq, 14 * NFSX_UNSIGNED + idlen);
127	nfsm_chain_add_compound_header(error, &nmreq, "setclientid", numops);
128	numops--;
129	nfsm_chain_add_32(error, &nmreq, NFS_OP_SETCLIENTID);
130	/* nfs_client_id4  client; */
131	nfsm_chain_add_64(error, &nmreq, nmp->nm_mounttime);
132	nfsm_chain_add_32(error, &nmreq, idlen);
133	nfsm_chain_add_opaque(error, &nmreq, id, idlen);
134	/* cb_client4      callback; */
135	/* We don't provide callback info yet */
136	nfsm_chain_add_32(error, &nmreq, 0); /* callback program */
137	nfsm_chain_add_string(error, &nmreq, "", 0); /* callback r_netid */
138	nfsm_chain_add_string(error, &nmreq, "", 0); /* callback r_addr */
139	nfsm_chain_add_32(error, &nmreq, 0); /* callback_ident */
140	nfsm_chain_build_done(error, &nmreq);
141	nfsm_assert(error, (numops == 0), EPROTO);
142	nfsmout_if(error);
143	error = nfs_request2(NULL, nmp->nm_mountp, &nmreq, NFSPROC4_COMPOUND, thd, cred, R_SETUP, &nmrep, &xid, &status);
144	nfsm_chain_skip_tag(error, &nmrep);
145	nfsm_chain_get_32(error, &nmrep, numops);
146	nfsm_chain_op_check(error, &nmrep, NFS_OP_SETCLIENTID);
147	if (error == NFSERR_CLID_INUSE)
148		printf("nfs4_setclientid: client ID in use?\n");
149	nfsmout_if(error);
150	nfsm_chain_get_64(error, &nmrep, nmp->nm_clientid);
151	nfsm_chain_get_64(error, &nmrep, verifier);
152	nfsm_chain_cleanup(&nmreq);
153	nfsm_chain_cleanup(&nmrep);
154
155	// SETCLIENTID_CONFIRM
156	numops = 1;
157	nfsm_chain_build_alloc_init(error, &nmreq, 13 * NFSX_UNSIGNED);
158	nfsm_chain_add_compound_header(error, &nmreq, "setclientid_confirm", numops);
159	numops--;
160	nfsm_chain_add_32(error, &nmreq, NFS_OP_SETCLIENTID_CONFIRM);
161	nfsm_chain_add_64(error, &nmreq, nmp->nm_clientid);
162	nfsm_chain_add_64(error, &nmreq, verifier);
163	nfsm_chain_build_done(error, &nmreq);
164	nfsm_assert(error, (numops == 0), EPROTO);
165	nfsmout_if(error);
166	error = nfs_request2(NULL, nmp->nm_mountp, &nmreq, NFSPROC4_COMPOUND, thd, cred, R_SETUP, &nmrep, &xid, &status);
167	nfsm_chain_skip_tag(error, &nmrep);
168	nfsm_chain_get_32(error, &nmrep, numops);
169	nfsm_chain_op_check(error, &nmrep, NFS_OP_SETCLIENTID_CONFIRM);
170	if (error)
171		printf("nfs4_setclientid: confirm error %d\n", error);
172nfsmout:
173	nfsm_chain_cleanup(&nmreq);
174	nfsm_chain_cleanup(&nmrep);
175	if (error)
176		printf("nfs4_setclientid failed, %d\n", error);
177	return (error);
178}
179
180/*
181 * periodic timer to renew lease state on server
182 */
183void
184nfs4_renew_timer(void *param0, __unused void *param1)
185{
186	struct nfsmount *nmp = param0;
187	int error = 0, status, numops, interval;
188	u_int64_t xid;
189	vfs_context_t ctx;
190	struct nfsm_chain nmreq, nmrep;
191
192	ctx = vfs_context_kernel(); /* XXX */
193
194	nfsm_chain_null(&nmreq);
195	nfsm_chain_null(&nmrep);
196
197	// RENEW
198	numops = 1;
199	nfsm_chain_build_alloc_init(error, &nmreq, 8 * NFSX_UNSIGNED);
200	nfsm_chain_add_compound_header(error, &nmreq, "renew", numops);
201	numops--;
202	nfsm_chain_add_32(error, &nmreq, NFS_OP_RENEW);
203	nfsm_chain_add_64(error, &nmreq, nmp->nm_clientid);
204	nfsm_chain_build_done(error, &nmreq);
205	nfsm_assert(error, (numops == 0), EPROTO);
206	nfsmout_if(error);
207	error = nfs_request(NULL, nmp->nm_mountp, &nmreq, NFSPROC4_COMPOUND, ctx, &nmrep, &xid, &status);
208	nfsm_chain_skip_tag(error, &nmrep);
209	nfsm_chain_get_32(error, &nmrep, numops);
210	nfsm_chain_op_check(error, &nmrep, NFS_OP_RENEW);
211nfsmout:
212	if (error)
213		printf("nfs4_renew_timer: error %d\n", error);
214	nfsm_chain_cleanup(&nmreq);
215	nfsm_chain_cleanup(&nmrep);
216
217	interval = nmp->nm_fsattr.nfsa_lease / (error ? 4 : 2);
218	if (interval < 1)
219		interval = 1;
220	nfs_interval_timer_start(nmp->nm_renew_timer, interval * 1000);
221}
222
223/*
224 * Set a vnode attr's supported bits according to the given bitmap
225 */
226void
227nfs_vattr_set_supported(uint32_t *bitmap, struct vnode_attr *vap)
228{
229	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TYPE))
230		VATTR_SET_SUPPORTED(vap, va_type);
231	// if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_CHANGE))
232	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SIZE))
233		VATTR_SET_SUPPORTED(vap, va_data_size);
234	// if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_NAMED_ATTR))
235	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FSID))
236		VATTR_SET_SUPPORTED(vap, va_fsid);
237//	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_ACL))
238//		VATTR_SET_SUPPORTED(vap, va_acl);
239	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_ARCHIVE))
240		VATTR_SET_SUPPORTED(vap, va_flags);
241	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FILEID))
242		VATTR_SET_SUPPORTED(vap, va_fileid);
243	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_HIDDEN))
244		VATTR_SET_SUPPORTED(vap, va_flags);
245	// if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MIMETYPE))
246	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MODE))
247		VATTR_SET_SUPPORTED(vap, va_mode);
248	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_NUMLINKS))
249		VATTR_SET_SUPPORTED(vap, va_nlink);
250	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_OWNER))
251		VATTR_SET_SUPPORTED(vap, va_uid);
252	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_OWNER_GROUP))
253		VATTR_SET_SUPPORTED(vap, va_gid);
254	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_RAWDEV))
255		VATTR_SET_SUPPORTED(vap, va_rdev);
256	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SPACE_USED))
257		VATTR_SET_SUPPORTED(vap, va_total_alloc);
258	// if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SYSTEM))
259	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_ACCESS))
260		VATTR_SET_SUPPORTED(vap, va_access_time);
261	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_BACKUP))
262		VATTR_SET_SUPPORTED(vap, va_backup_time);
263	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_CREATE))
264		VATTR_SET_SUPPORTED(vap, va_create_time);
265	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_METADATA))
266		VATTR_SET_SUPPORTED(vap, va_change_time);
267	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_MODIFY))
268		VATTR_SET_SUPPORTED(vap, va_modify_time);
269}
270
271/*
272 * Parse the attributes that are in the mbuf list and store them in
273 * the given structures.
274 */
275int
276nfs4_parsefattr(
277	struct nfsm_chain *nmc,
278	struct nfs_fsattr *nfsap,
279	struct nfs_vattr *nvap,
280	fhandle_t *fhp,
281	struct dqblk *dqbp)
282{
283	int error = 0, attrbytes;
284	uint32_t val, val2, val3, i, j;
285	uint32_t bitmap[NFS_ATTR_BITMAP_LEN], len;
286	char *s;
287	struct nfs_fsattr nfsa_dummy;
288	struct nfs_vattr nva_dummy;
289	struct dqblk dqb_dummy;
290
291	/* if not interested in some values... throw 'em into a local dummy variable */
292	if (!nfsap)
293		nfsap = &nfsa_dummy;
294	if (!nvap)
295		nvap = &nva_dummy;
296	if (!dqbp)
297		dqbp = &dqb_dummy;
298
299	attrbytes = val = val2 = val3 = 0;
300
301	len = NFS_ATTR_BITMAP_LEN;
302	nfsm_chain_get_bitmap(error, nmc, bitmap, len);
303	/* add bits to object/fs attr bitmaps */
304	for (i=0; i < NFS_ATTR_BITMAP_LEN; i++) {
305		nvap->nva_bitmap[i] |= bitmap[i] & nfs_object_attr_bitmap[i];
306		nfsap->nfsa_bitmap[i] |= bitmap[i] & nfs_fs_attr_bitmap[i];
307	}
308
309	nfsm_chain_get_32(error, nmc, attrbytes);
310	nfsmout_if(error);
311
312	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SUPPORTED_ATTRS)) {
313		len = NFS_ATTR_BITMAP_LEN;
314		nfsm_chain_get_bitmap(error, nmc, nfsap->nfsa_supp_attr, len);
315		attrbytes -= (len + 1) * NFSX_UNSIGNED;
316	}
317	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TYPE)) {
318		nfsm_chain_get_32(error, nmc, val);
319		nvap->nva_type = nfstov_type(val, NFS_VER4);
320		attrbytes -= NFSX_UNSIGNED;
321	}
322	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FH_EXPIRE_TYPE)) {
323		nfsm_chain_get_32(error, nmc, val);
324		nfsmout_if(error);
325		if (val != NFS_FH_PERSISTENT)
326			printf("nfs: warning: non-persistent file handles!\n");
327		if (val & ~0xff)
328			printf("nfs: warning unknown fh type: 0x%x\n", val);
329		nfsap->nfsa_flags &= ~NFS_FSFLAG_FHTYPE_MASK;
330		nfsap->nfsa_flags |= val << 24;
331		attrbytes -= NFSX_UNSIGNED;
332	}
333	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_CHANGE)) {
334		nfsm_chain_get_64(error, nmc, nvap->nva_change);
335		attrbytes -= 2 * NFSX_UNSIGNED;
336	}
337	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SIZE)) {
338		nfsm_chain_get_64(error, nmc, nvap->nva_size);
339		attrbytes -= 2 * NFSX_UNSIGNED;
340	}
341	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_LINK_SUPPORT)) {
342		nfsm_chain_get_32(error, nmc, val);
343		if (val)
344			nfsap->nfsa_flags |= NFS_FSFLAG_LINK;
345		else
346			nfsap->nfsa_flags &= ~NFS_FSFLAG_LINK;
347		attrbytes -= NFSX_UNSIGNED;
348	}
349	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SYMLINK_SUPPORT)) {
350		nfsm_chain_get_32(error, nmc, val);
351		if (val)
352			nfsap->nfsa_flags |= NFS_FSFLAG_SYMLINK;
353		else
354			nfsap->nfsa_flags &= ~NFS_FSFLAG_SYMLINK;
355		attrbytes -= NFSX_UNSIGNED;
356	}
357	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_NAMED_ATTR)) {
358		nfsm_chain_get_32(error, nmc, val);
359		if (val)
360			nvap->nva_flags |= NFS_FFLAG_NAMED_ATTR;
361		else
362			nvap->nva_flags &= ~NFS_FFLAG_NAMED_ATTR;
363		attrbytes -= NFSX_UNSIGNED;
364	}
365	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FSID)) {
366		nfsm_chain_get_64(error, nmc, nvap->nva_fsid.major);
367		nfsm_chain_get_64(error, nmc, nvap->nva_fsid.minor);
368		attrbytes -= 4 * NFSX_UNSIGNED;
369	}
370	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_UNIQUE_HANDLES)) {
371		nfsm_chain_get_32(error, nmc, val);
372		if (val)
373			nfsap->nfsa_flags |= NFS_FSFLAG_UNIQUE_FH;
374		else
375			nfsap->nfsa_flags &= ~NFS_FSFLAG_UNIQUE_FH;
376		attrbytes -= NFSX_UNSIGNED;
377	}
378	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_LEASE_TIME)) {
379		nfsm_chain_get_32(error, nmc, nfsap->nfsa_lease);
380		attrbytes -= NFSX_UNSIGNED;
381	}
382	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_RDATTR_ERROR)) {
383		nfsm_chain_get_32(error, nmc, error);
384		attrbytes -= NFSX_UNSIGNED;
385		nfsmout_if(error);
386	}
387	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_ACL)) { /* skip for now */
388		nfsm_chain_get_32(error, nmc, val); /* ACE count */
389		for (i=0; !error && (i < val); i++) {
390			nfsm_chain_adv(error, nmc, 3 * NFSX_UNSIGNED);
391			nfsm_chain_get_32(error, nmc, val2); /* string length */
392			nfsm_chain_adv(error, nmc, nfsm_rndup(val2));
393			attrbytes -= 4*NFSX_UNSIGNED + nfsm_rndup(val2);
394			nfsm_assert(error, (attrbytes >= 0), EBADRPC);
395		}
396	}
397	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_ACLSUPPORT)) {
398		nfsm_chain_get_32(error, nmc, val);
399		if (val)
400			nfsap->nfsa_flags |= NFS_FSFLAG_ACL;
401		else
402			nfsap->nfsa_flags &= ~NFS_FSFLAG_ACL;
403		attrbytes -= NFSX_UNSIGNED;
404	}
405	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_ARCHIVE)) { /* SF_ARCHIVED */
406		nfsm_chain_get_32(error, nmc, val);
407		if (val)
408			nvap->nva_flags |= NFS_FFLAG_ARCHIVED;
409		else
410			nvap->nva_flags &= ~NFS_FFLAG_ARCHIVED;
411		attrbytes -= NFSX_UNSIGNED;
412	}
413	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_CANSETTIME)) {
414		nfsm_chain_get_32(error, nmc, val);
415		if (val)
416			nfsap->nfsa_flags |= NFS_FSFLAG_SET_TIME;
417		else
418			nfsap->nfsa_flags &= ~NFS_FSFLAG_SET_TIME;
419		attrbytes -= NFSX_UNSIGNED;
420	}
421	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_CASE_INSENSITIVE)) {
422		nfsm_chain_get_32(error, nmc, val);
423		if (val)
424			nfsap->nfsa_flags |= NFS_FSFLAG_CASE_INSENSITIVE;
425		else
426			nfsap->nfsa_flags &= ~NFS_FSFLAG_CASE_INSENSITIVE;
427		attrbytes -= NFSX_UNSIGNED;
428	}
429	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_CASE_PRESERVING)) {
430		nfsm_chain_get_32(error, nmc, val);
431		if (val)
432			nfsap->nfsa_flags |= NFS_FSFLAG_CASE_PRESERVING;
433		else
434			nfsap->nfsa_flags &= ~NFS_FSFLAG_CASE_PRESERVING;
435		attrbytes -= NFSX_UNSIGNED;
436	}
437	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_CHOWN_RESTRICTED)) {
438		nfsm_chain_get_32(error, nmc, val);
439		if (val)
440			nfsap->nfsa_flags |= NFS_FSFLAG_CHOWN_RESTRICTED;
441		else
442			nfsap->nfsa_flags &= ~NFS_FSFLAG_CHOWN_RESTRICTED;
443		attrbytes -= NFSX_UNSIGNED;
444	}
445	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FILEHANDLE)) {
446		nfsm_chain_get_32(error, nmc, val);
447		if (fhp) {
448			fhp->fh_len = val;
449			nfsm_chain_get_opaque(error, nmc, nfsm_rndup(val), fhp->fh_data);
450		} else {
451			nfsm_chain_adv(error, nmc, nfsm_rndup(val));
452		}
453		attrbytes -= NFSX_UNSIGNED + nfsm_rndup(val);
454	}
455	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FILEID)) {
456		nfsm_chain_get_64(error, nmc, nvap->nva_fileid);
457		attrbytes -= 2 * NFSX_UNSIGNED;
458	}
459	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FILES_AVAIL)) {
460		nfsm_chain_get_64(error, nmc, nfsap->nfsa_files_avail);
461		attrbytes -= 2 * NFSX_UNSIGNED;
462	}
463	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FILES_FREE)) {
464		nfsm_chain_get_64(error, nmc, nfsap->nfsa_files_free);
465		attrbytes -= 2 * NFSX_UNSIGNED;
466	}
467	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FILES_TOTAL)) {
468		nfsm_chain_get_64(error, nmc, nfsap->nfsa_files_total);
469		attrbytes -= 2 * NFSX_UNSIGNED;
470	}
471	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FS_LOCATIONS)) { /* skip for now */
472		nfsm_chain_get_32(error, nmc, val); /* root path length */
473		nfsm_chain_adv(error, nmc, nfsm_rndup(val)); /* root path */
474		attrbytes -= (2 * NFSX_UNSIGNED) + nfsm_rndup(val);
475		nfsm_chain_get_32(error, nmc, val); /* location count */
476		for (i=0; !error && (i < val); i++) {
477			nfsm_chain_get_32(error, nmc, val2); /* server string length */
478			nfsm_chain_adv(error, nmc, nfsm_rndup(val2)); /* server string */
479			attrbytes -= (2 * NFSX_UNSIGNED) + nfsm_rndup(val2);
480			nfsm_chain_get_32(error, nmc, val2); /* pathname component count */
481			for (j=0; !error && (j < val2); j++) {
482				nfsm_chain_get_32(error, nmc, val3); /* component length */
483				nfsm_chain_adv(error, nmc, nfsm_rndup(val3)); /* component */
484				attrbytes -= NFSX_UNSIGNED + nfsm_rndup(val3);
485				nfsm_assert(error, (attrbytes >= 0), EBADRPC);
486			}
487			nfsm_assert(error, (attrbytes >= 0), EBADRPC);
488		}
489		nfsm_assert(error, (attrbytes >= 0), EBADRPC);
490	}
491	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_HIDDEN)) { /* UF_HIDDEN */
492		nfsm_chain_get_32(error, nmc, val);
493		if (val)
494			nvap->nva_flags |= NFS_FFLAG_HIDDEN;
495		else
496			nvap->nva_flags &= ~NFS_FFLAG_HIDDEN;
497		attrbytes -= NFSX_UNSIGNED;
498	}
499	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_HOMOGENEOUS)) {
500		/* XXX If NOT homogeneous, we may need to clear flags on the mount */
501		nfsm_chain_get_32(error, nmc, val);
502		if (val)
503			nfsap->nfsa_flags |= NFS_FSFLAG_HOMOGENEOUS;
504		else
505			nfsap->nfsa_flags &= ~NFS_FSFLAG_HOMOGENEOUS;
506		attrbytes -= NFSX_UNSIGNED;
507	}
508	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MAXFILESIZE)) {
509		nfsm_chain_get_64(error, nmc, nfsap->nfsa_maxfilesize);
510		attrbytes -= 2 * NFSX_UNSIGNED;
511	}
512	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MAXLINK)) {
513		nfsm_chain_get_32(error, nmc, nvap->nva_maxlink);
514		if (!error && (nfsap->nfsa_maxlink > INT32_MAX))
515			nfsap->nfsa_maxlink = INT32_MAX;
516		attrbytes -= NFSX_UNSIGNED;
517	}
518	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MAXNAME)) {
519		nfsm_chain_get_32(error, nmc, nfsap->nfsa_maxname);
520		if (!error && (nfsap->nfsa_maxname > INT32_MAX))
521			nfsap->nfsa_maxname = INT32_MAX;
522		attrbytes -= NFSX_UNSIGNED;
523	}
524	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MAXREAD)) {
525		nfsm_chain_get_64(error, nmc, nfsap->nfsa_maxread);
526		attrbytes -= 2 * NFSX_UNSIGNED;
527	}
528	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MAXWRITE)) {
529		nfsm_chain_get_64(error, nmc, nfsap->nfsa_maxwrite);
530		attrbytes -= 2 * NFSX_UNSIGNED;
531	}
532	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MIMETYPE)) {
533		nfsm_chain_get_32(error, nmc, val);
534		nfsm_chain_adv(error, nmc, nfsm_rndup(val));
535		attrbytes -= NFSX_UNSIGNED + nfsm_rndup(val);
536	}
537	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MODE)) {
538		nfsm_chain_get_32(error, nmc, nvap->nva_mode);
539		attrbytes -= NFSX_UNSIGNED;
540	}
541	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_NO_TRUNC)) {
542		nfsm_chain_get_32(error, nmc, val);
543		if (val)
544			nfsap->nfsa_flags |= NFS_FSFLAG_NO_TRUNC;
545		else
546			nfsap->nfsa_flags &= ~NFS_FSFLAG_NO_TRUNC;
547		attrbytes -= NFSX_UNSIGNED;
548	}
549	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_NUMLINKS)) {
550		nfsm_chain_get_32(error, nmc, val);
551		nvap->nva_nlink = val;
552		attrbytes -= NFSX_UNSIGNED;
553	}
554	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_OWNER)) { /* XXX ugly hack for now */
555		nfsm_chain_get_32(error, nmc, len);
556		nfsm_chain_get_opaque_pointer(error, nmc, len, s);
557		attrbytes -= NFSX_UNSIGNED + nfsm_rndup(len);
558		nfsmout_if(error);
559		if ((*s >= '0') && (*s <= '9'))
560			nvap->nva_uid = strtol(s, NULL, 10);
561		else if (!strncmp(s, "nobody@", 7))
562			nvap->nva_uid = -2;
563		else if (!strncmp(s, "root@", 5))
564			nvap->nva_uid = 0;
565		else
566			nvap->nva_uid = 99; /* unknown */
567	}
568	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_OWNER_GROUP)) { /* XXX ugly hack for now */
569		nfsm_chain_get_32(error, nmc, len);
570		nfsm_chain_get_opaque_pointer(error, nmc, len, s);
571		attrbytes -= NFSX_UNSIGNED + nfsm_rndup(len);
572		nfsmout_if(error);
573		if ((*s >= '0') && (*s <= '9'))
574			nvap->nva_gid = strtol(s, NULL, 10);
575		else if (!strncmp(s, "nobody@", 7))
576			nvap->nva_gid = -2;
577		else if (!strncmp(s, "root@", 5))
578			nvap->nva_uid = 0;
579		else
580			nvap->nva_gid = 99; /* unknown */
581	}
582	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_QUOTA_AVAIL_HARD)) {
583		nfsm_chain_get_64(error, nmc, dqbp->dqb_bhardlimit);
584		attrbytes -= 2 * NFSX_UNSIGNED;
585	}
586	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_QUOTA_AVAIL_SOFT)) {
587		nfsm_chain_get_64(error, nmc, dqbp->dqb_bsoftlimit);
588		attrbytes -= 2 * NFSX_UNSIGNED;
589	}
590	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_QUOTA_USED)) {
591		nfsm_chain_get_64(error, nmc, dqbp->dqb_curbytes);
592		attrbytes -= 2 * NFSX_UNSIGNED;
593	}
594	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_RAWDEV)) {
595		nfsm_chain_get_32(error, nmc, nvap->nva_rawdev.specdata1);
596		nfsm_chain_get_32(error, nmc, nvap->nva_rawdev.specdata2);
597		attrbytes -= 2 * NFSX_UNSIGNED;
598	}
599	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SPACE_AVAIL)) {
600		nfsm_chain_get_64(error, nmc, nfsap->nfsa_space_avail);
601		attrbytes -= 2 * NFSX_UNSIGNED;
602	}
603	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SPACE_FREE)) {
604		nfsm_chain_get_64(error, nmc, nfsap->nfsa_space_free);
605		attrbytes -= 2 * NFSX_UNSIGNED;
606	}
607	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SPACE_TOTAL)) {
608		nfsm_chain_get_64(error, nmc, nfsap->nfsa_space_total);
609		attrbytes -= 2 * NFSX_UNSIGNED;
610	}
611	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SPACE_USED)) {
612		nfsm_chain_get_64(error, nmc, nvap->nva_bytes);
613		attrbytes -= 2 * NFSX_UNSIGNED;
614	}
615	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SYSTEM)) {
616		/* we'd support this if we had a flag to map it to... */
617		nfsm_chain_adv(error, nmc, NFSX_UNSIGNED);
618		attrbytes -= NFSX_UNSIGNED;
619	}
620	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_ACCESS)) {
621		nfsm_chain_get_64(error, nmc, nvap->nva_timesec[NFSTIME_ACCESS]);
622		nfsm_chain_get_32(error, nmc, nvap->nva_timensec[NFSTIME_ACCESS]);
623		attrbytes -= 3 * NFSX_UNSIGNED;
624	}
625	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_ACCESS_SET)) {
626		nfsm_chain_adv(error, nmc, 4*NFSX_UNSIGNED); /* just skip it */
627		attrbytes -= 4 * NFSX_UNSIGNED;
628	}
629	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_BACKUP)) {
630		nfsm_chain_get_64(error, nmc, nvap->nva_timesec[NFSTIME_BACKUP]);
631		nfsm_chain_get_32(error, nmc, nvap->nva_timensec[NFSTIME_BACKUP]);
632		attrbytes -= 3 * NFSX_UNSIGNED;
633	}
634	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_CREATE)) {
635		nfsm_chain_get_64(error, nmc, nvap->nva_timesec[NFSTIME_CREATE]);
636		nfsm_chain_get_32(error, nmc, nvap->nva_timensec[NFSTIME_CREATE]);
637		attrbytes -= 3 * NFSX_UNSIGNED;
638	}
639	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_DELTA)) { /* skip for now */
640		nfsm_chain_adv(error, nmc, 3*NFSX_UNSIGNED);
641		attrbytes -= 3 * NFSX_UNSIGNED;
642	}
643	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_METADATA)) {
644		nfsm_chain_get_64(error, nmc, nvap->nva_timesec[NFSTIME_CHANGE]);
645		nfsm_chain_get_32(error, nmc, nvap->nva_timensec[NFSTIME_CHANGE]);
646		attrbytes -= 3 * NFSX_UNSIGNED;
647	}
648	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_MODIFY)) {
649		nfsm_chain_get_64(error, nmc, nvap->nva_timesec[NFSTIME_MODIFY]);
650		nfsm_chain_get_32(error, nmc, nvap->nva_timensec[NFSTIME_MODIFY]);
651		attrbytes -= 3 * NFSX_UNSIGNED;
652	}
653	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_MODIFY_SET)) {
654		nfsm_chain_adv(error, nmc, 4*NFSX_UNSIGNED); /* just skip it */
655		attrbytes -= 4 * NFSX_UNSIGNED;
656	}
657	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MOUNTED_ON_FILEID)) { /* skip for now */
658		nfsm_chain_adv(error, nmc, 2*NFSX_UNSIGNED);
659		attrbytes -= 2 * NFSX_UNSIGNED;
660	}
661	/* advance over any leftover attrbytes */
662	nfsm_assert(error, (attrbytes >= 0), EBADRPC);
663	nfsm_chain_adv(error, nmc, nfsm_rndup(attrbytes));
664nfsmout:
665	return (error);
666}
667
668/*
669 * Add an NFSv4 "sattr" structure to an mbuf chain
670 */
671int
672nfsm_chain_add_fattr4_f(struct nfsm_chain *nmc, struct vnode_attr *vap, struct nfsmount *nmp)
673{
674	int error = 0, attrbytes, slen, i;
675	uint32_t *pattrbytes;
676	uint32_t bitmap[NFS_ATTR_BITMAP_LEN];
677	char s[32];
678
679	/*
680	 * Do this in two passes.
681	 * First calculate the bitmap, then pack
682	 * everything together and set the size.
683	 */
684
685	NFS_CLEAR_ATTRIBUTES(bitmap);
686	if (VATTR_IS_ACTIVE(vap, va_data_size))
687		NFS_BITMAP_SET(bitmap, NFS_FATTR_SIZE);
688	if (VATTR_IS_ACTIVE(vap, va_acl)) {
689		// NFS_BITMAP_SET(bitmap, NFS_FATTR_ACL)
690	}
691	if (VATTR_IS_ACTIVE(vap, va_flags)) {
692		NFS_BITMAP_SET(bitmap, NFS_FATTR_ARCHIVE);
693		NFS_BITMAP_SET(bitmap, NFS_FATTR_HIDDEN);
694	}
695	// NFS_BITMAP_SET(bitmap, NFS_FATTR_MIMETYPE)
696	if (VATTR_IS_ACTIVE(vap, va_mode))
697		NFS_BITMAP_SET(bitmap, NFS_FATTR_MODE);
698	if (VATTR_IS_ACTIVE(vap, va_uid))
699		NFS_BITMAP_SET(bitmap, NFS_FATTR_OWNER);
700	if (VATTR_IS_ACTIVE(vap, va_gid))
701		NFS_BITMAP_SET(bitmap, NFS_FATTR_OWNER_GROUP);
702	// NFS_BITMAP_SET(bitmap, NFS_FATTR_SYSTEM)
703	if (vap->va_vaflags & VA_UTIMES_NULL) {
704		NFS_BITMAP_SET(bitmap, NFS_FATTR_TIME_ACCESS_SET);
705		NFS_BITMAP_SET(bitmap, NFS_FATTR_TIME_MODIFY_SET);
706	} else {
707		if (VATTR_IS_ACTIVE(vap, va_access_time))
708			NFS_BITMAP_SET(bitmap, NFS_FATTR_TIME_ACCESS_SET);
709		if (VATTR_IS_ACTIVE(vap, va_modify_time))
710			NFS_BITMAP_SET(bitmap, NFS_FATTR_TIME_MODIFY_SET);
711	}
712	if (VATTR_IS_ACTIVE(vap, va_backup_time))
713		NFS_BITMAP_SET(bitmap, NFS_FATTR_TIME_BACKUP);
714	if (VATTR_IS_ACTIVE(vap, va_create_time))
715		NFS_BITMAP_SET(bitmap, NFS_FATTR_TIME_CREATE);
716	/* and limit to what is supported by server */
717	for (i=0; i < NFS_ATTR_BITMAP_LEN; i++)
718		bitmap[i] &= nmp->nm_fsattr.nfsa_supp_attr[i];
719
720	/*
721	 * Now pack it all together:
722	 *     BITMAP, #BYTES, ATTRS
723	 * Keep a pointer to the length so we can set it later.
724	 */
725	nfsm_chain_add_bitmap(error, nmc, bitmap, NFS_ATTR_BITMAP_LEN);
726	attrbytes = 0;
727	nfsm_chain_add_32(error, nmc, attrbytes);
728	pattrbytes = (uint32_t*)(nmc->nmc_ptr - NFSX_UNSIGNED);
729
730	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SIZE)) {
731		nfsm_chain_add_64(error, nmc, vap->va_data_size);
732		attrbytes += 2*NFSX_UNSIGNED;
733	}
734	// NFS_BITMAP_ISSET(bitmap, NFS_FATTR_ACL)
735	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_ARCHIVE)) {
736		nfsm_chain_add_32(error, nmc, (vap->va_flags & SF_ARCHIVED) ? 1 : 0);
737		attrbytes += NFSX_UNSIGNED;
738	}
739	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_HIDDEN)) {
740		nfsm_chain_add_32(error, nmc, (vap->va_flags & UF_HIDDEN) ? 1 : 0);
741		attrbytes += NFSX_UNSIGNED;
742	}
743	// NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MIMETYPE)
744	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MODE)) {
745		nfsm_chain_add_32(error, nmc, vap->va_mode);
746		attrbytes += NFSX_UNSIGNED;
747	}
748	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_OWNER)) {
749		slen = snprintf(s, sizeof(s), "%d", vap->va_uid);
750		nfsm_chain_add_string(error, nmc, s, slen);
751		attrbytes += NFSX_UNSIGNED + nfsm_rndup(slen);
752	}
753	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_OWNER_GROUP)) {
754		slen = snprintf(s, sizeof(s), "%d", vap->va_gid);
755		nfsm_chain_add_string(error, nmc, s, slen);
756		attrbytes += NFSX_UNSIGNED + nfsm_rndup(slen);
757	}
758	// NFS_BITMAP_SET(bitmap, NFS_FATTR_SYSTEM)
759	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_ACCESS_SET)) {
760		if (vap->va_vaflags & VA_UTIMES_NULL) {
761			nfsm_chain_add_32(error, nmc, NFS_TIME_SET_TO_SERVER);
762			attrbytes += NFSX_UNSIGNED;
763		} else {
764			nfsm_chain_add_32(error, nmc, NFS_TIME_SET_TO_CLIENT);
765			nfsm_chain_add_64(error, nmc, vap->va_access_time.tv_sec);
766			nfsm_chain_add_32(error, nmc, vap->va_access_time.tv_nsec);
767			attrbytes += 4*NFSX_UNSIGNED;
768		}
769	}
770	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_BACKUP)) {
771		nfsm_chain_add_64(error, nmc, vap->va_backup_time.tv_sec);
772		nfsm_chain_add_32(error, nmc, vap->va_backup_time.tv_nsec);
773		attrbytes += 3*NFSX_UNSIGNED;
774	}
775	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_CREATE)) {
776		nfsm_chain_add_64(error, nmc, vap->va_create_time.tv_sec);
777		nfsm_chain_add_32(error, nmc, vap->va_create_time.tv_nsec);
778		attrbytes += 3*NFSX_UNSIGNED;
779	}
780	if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_MODIFY_SET)) {
781		if (vap->va_vaflags & VA_UTIMES_NULL) {
782			nfsm_chain_add_32(error, nmc, NFS_TIME_SET_TO_SERVER);
783			attrbytes += NFSX_UNSIGNED;
784		} else {
785			nfsm_chain_add_32(error, nmc, NFS_TIME_SET_TO_CLIENT);
786			nfsm_chain_add_64(error, nmc, vap->va_modify_time.tv_sec);
787			nfsm_chain_add_32(error, nmc, vap->va_modify_time.tv_nsec);
788			attrbytes += 4*NFSX_UNSIGNED;
789		}
790	}
791	nfsmout_if(error);
792	/* Now, set the attribute data length */
793	*pattrbytes = txdr_unsigned(attrbytes);
794nfsmout:
795	return (error);
796}
797
798