xref: /illumos-gate/usr/src/boot/libsa/nfs.c (revision 22028508)
1199767f8SToomas Soome /*	$NetBSD: nfs.c,v 1.2 1998/01/24 12:43:09 drochner Exp $	*/
2199767f8SToomas Soome 
34fa25acaSToomas Soome /*
4199767f8SToomas Soome  *  Copyright (c) 1993 John Brezak
5199767f8SToomas Soome  *  All rights reserved.
6199767f8SToomas Soome  *
7199767f8SToomas Soome  *  Redistribution and use in source and binary forms, with or without
8199767f8SToomas Soome  *  modification, are permitted provided that the following conditions
9199767f8SToomas Soome  *  are met:
10199767f8SToomas Soome  *  1. Redistributions of source code must retain the above copyright
11199767f8SToomas Soome  *     notice, this list of conditions and the following disclaimer.
12199767f8SToomas Soome  *  2. Redistributions in binary form must reproduce the above copyright
13199767f8SToomas Soome  *     notice, this list of conditions and the following disclaimer in the
14199767f8SToomas Soome  *     documentation and/or other materials provided with the distribution.
15199767f8SToomas Soome  *  3. The name of the author may not be used to endorse or promote products
16199767f8SToomas Soome  *     derived from this software without specific prior written permission.
17199767f8SToomas Soome  *
18199767f8SToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
19199767f8SToomas Soome  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20199767f8SToomas Soome  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21199767f8SToomas Soome  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22199767f8SToomas Soome  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23199767f8SToomas Soome  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24199767f8SToomas Soome  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25199767f8SToomas Soome  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26199767f8SToomas Soome  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27199767f8SToomas Soome  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28199767f8SToomas Soome  * POSSIBILITY OF SUCH DAMAGE.
29199767f8SToomas Soome  */
30199767f8SToomas Soome 
31199767f8SToomas Soome #include <sys/cdefs.h>
32199767f8SToomas Soome 
33199767f8SToomas Soome #include <sys/param.h>
34199767f8SToomas Soome #include <sys/time.h>
35199767f8SToomas Soome #include <sys/socket.h>
36199767f8SToomas Soome #include <sys/stat.h>
37199767f8SToomas Soome #include <string.h>
38b68080e0SToomas Soome #include <stddef.h>
39199767f8SToomas Soome 
40199767f8SToomas Soome #include <netinet/in.h>
41199767f8SToomas Soome #include <netinet/in_systm.h>
42199767f8SToomas Soome 
43199767f8SToomas Soome #include "rpcv2.h"
44199767f8SToomas Soome #include "nfsv2.h"
45199767f8SToomas Soome 
46199767f8SToomas Soome #include "stand.h"
47199767f8SToomas Soome #include "net.h"
48199767f8SToomas Soome #include "netif.h"
49199767f8SToomas Soome #include "rpc.h"
50199767f8SToomas Soome 
51734b3a42SToomas Soome #define	NFS_DEBUGxx
52199767f8SToomas Soome 
53734b3a42SToomas Soome #define	NFSREAD_MIN_SIZE 1024
54734b3a42SToomas Soome #define	NFSREAD_MAX_SIZE 16384
55199767f8SToomas Soome 
56199767f8SToomas Soome /* NFSv3 definitions */
57199767f8SToomas Soome #define	NFS_V3MAXFHSIZE		64
58199767f8SToomas Soome #define	NFS_VER3		3
59199767f8SToomas Soome #define	RPCMNT_VER3		3
60199767f8SToomas Soome #define	NFSPROCV3_LOOKUP	3
61199767f8SToomas Soome #define	NFSPROCV3_READLINK	5
62199767f8SToomas Soome #define	NFSPROCV3_READ		6
63199767f8SToomas Soome #define	NFSPROCV3_READDIR	16
64199767f8SToomas Soome 
65199767f8SToomas Soome typedef struct {
66199767f8SToomas Soome 	uint32_t val[2];
67199767f8SToomas Soome } n_quad;
68199767f8SToomas Soome 
69199767f8SToomas Soome struct nfsv3_time {
70199767f8SToomas Soome 	uint32_t nfs_sec;
71199767f8SToomas Soome 	uint32_t nfs_nsec;
72199767f8SToomas Soome };
73199767f8SToomas Soome 
74199767f8SToomas Soome struct nfsv3_fattrs {
75199767f8SToomas Soome 	uint32_t fa_type;
76199767f8SToomas Soome 	uint32_t fa_mode;
77199767f8SToomas Soome 	uint32_t fa_nlink;
78199767f8SToomas Soome 	uint32_t fa_uid;
79199767f8SToomas Soome 	uint32_t fa_gid;
80199767f8SToomas Soome 	n_quad fa_size;
81199767f8SToomas Soome 	n_quad fa_used;
82199767f8SToomas Soome 	n_quad fa_rdev;
83199767f8SToomas Soome 	n_quad fa_fsid;
84199767f8SToomas Soome 	n_quad fa_fileid;
85199767f8SToomas Soome 	struct nfsv3_time fa_atime;
86199767f8SToomas Soome 	struct nfsv3_time fa_mtime;
87199767f8SToomas Soome 	struct nfsv3_time fa_ctime;
88199767f8SToomas Soome };
89199767f8SToomas Soome 
90199767f8SToomas Soome /*
91199767f8SToomas Soome  * For NFSv3, the file handle is variable in size, so most fixed sized
92199767f8SToomas Soome  * structures for arguments won't work. For most cases, a structure
93199767f8SToomas Soome  * that starts with any fixed size section is followed by an array
94199767f8SToomas Soome  * that covers the maximum size required.
95199767f8SToomas Soome  */
96199767f8SToomas Soome struct nfsv3_readdir_repl {
97199767f8SToomas Soome 	uint32_t errno;
98199767f8SToomas Soome 	uint32_t ok;
99199767f8SToomas Soome 	struct nfsv3_fattrs fa;
100199767f8SToomas Soome 	uint32_t cookiev0;
101199767f8SToomas Soome 	uint32_t cookiev1;
102199767f8SToomas Soome };
103199767f8SToomas Soome 
104199767f8SToomas Soome struct nfsv3_readdir_entry {
105199767f8SToomas Soome 	uint32_t follows;
106199767f8SToomas Soome 	uint32_t fid0;
107199767f8SToomas Soome 	uint32_t fid1;
108199767f8SToomas Soome 	uint32_t len;
109199767f8SToomas Soome 	uint32_t nameplus[0];
110199767f8SToomas Soome };
111199767f8SToomas Soome 
112199767f8SToomas Soome struct nfs_iodesc {
113199767f8SToomas Soome 	struct iodesc *iodesc;
114199767f8SToomas Soome 	off_t off;
115199767f8SToomas Soome 	uint32_t fhsize;
116734b3a42SToomas Soome 	uchar_t fh[NFS_V3MAXFHSIZE];
117199767f8SToomas Soome 	struct nfsv3_fattrs fa;	/* all in network order */
118199767f8SToomas Soome 	uint64_t cookie;
119199767f8SToomas Soome };
120199767f8SToomas Soome 
121199767f8SToomas Soome /*
122199767f8SToomas Soome  * XXX interactions with tftp? See nfswrapper.c for a confusing
123199767f8SToomas Soome  *     issue.
124199767f8SToomas Soome  */
125734b3a42SToomas Soome int nfs_open(const char *path, struct open_file *f);
126734b3a42SToomas Soome static int nfs_close(struct open_file *f);
127734b3a42SToomas Soome static int nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
128734b3a42SToomas Soome static off_t nfs_seek(struct open_file *f, off_t offset, int where);
129734b3a42SToomas Soome static int nfs_stat(struct open_file *f, struct stat *sb);
130734b3a42SToomas Soome static int nfs_readdir(struct open_file *f, struct dirent *d);
131199767f8SToomas Soome 
132199767f8SToomas Soome struct	nfs_iodesc nfs_root_node;
133199767f8SToomas Soome 
134199767f8SToomas Soome struct fs_ops nfs_fsops = {
135734b3a42SToomas Soome 	.fs_name = "nfs",
136734b3a42SToomas Soome 	.fo_open = nfs_open,
137734b3a42SToomas Soome 	.fo_close = nfs_close,
138734b3a42SToomas Soome 	.fo_read = nfs_read,
139734b3a42SToomas Soome 	.fo_write = null_write,
140734b3a42SToomas Soome 	.fo_seek = nfs_seek,
141734b3a42SToomas Soome 	.fo_stat = nfs_stat,
142734b3a42SToomas Soome 	.fo_readdir = nfs_readdir
143199767f8SToomas Soome };
144199767f8SToomas Soome 
145b68080e0SToomas Soome static int nfs_read_size = NFSREAD_MIN_SIZE;
146b68080e0SToomas Soome 
147b68080e0SToomas Soome /*
148b68080e0SToomas Soome  * Improve boot performance over NFS
149b68080e0SToomas Soome  */
150b68080e0SToomas Soome static void
set_nfs_read_size(void)151b68080e0SToomas Soome set_nfs_read_size(void)
152b68080e0SToomas Soome {
153b68080e0SToomas Soome 	char *env, *end;
154b68080e0SToomas Soome 	char buf[10];
155b68080e0SToomas Soome 
156b68080e0SToomas Soome 	if ((env = getenv("nfs.read_size")) != NULL) {
157b68080e0SToomas Soome 		errno = 0;
158b68080e0SToomas Soome 		nfs_read_size = strtol(env, &end, 0);
159b68080e0SToomas Soome 		if (errno != 0 || *env == '\0' || *end != '\0') {
160b68080e0SToomas Soome 			printf("%s: bad value: \"%s\", defaulting to %d\n",
161b68080e0SToomas Soome 			    "nfs.read_size", env, NFSREAD_MIN_SIZE);
162b68080e0SToomas Soome 			nfs_read_size = NFSREAD_MIN_SIZE;
163b68080e0SToomas Soome 		}
164b68080e0SToomas Soome 	}
165b68080e0SToomas Soome 	if (nfs_read_size < NFSREAD_MIN_SIZE) {
166b68080e0SToomas Soome 		printf("%s: bad value: \"%d\", defaulting to %d\n",
167b68080e0SToomas Soome 		    "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE);
168b68080e0SToomas Soome 		nfs_read_size = NFSREAD_MIN_SIZE;
169b68080e0SToomas Soome 	}
170b68080e0SToomas Soome 	if (nfs_read_size > NFSREAD_MAX_SIZE) {
171b68080e0SToomas Soome 		printf("%s: bad value: \"%d\", defaulting to %d\n",
172b68080e0SToomas Soome 		    "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE);
173b68080e0SToomas Soome 		nfs_read_size = NFSREAD_MAX_SIZE;
174b68080e0SToomas Soome 	}
175b68080e0SToomas Soome 	snprintf(buf, sizeof (buf), "%d", nfs_read_size);
176b68080e0SToomas Soome 	setenv("nfs.read_size", buf, 1);
177b68080e0SToomas Soome }
178b68080e0SToomas Soome 
179199767f8SToomas Soome /*
180199767f8SToomas Soome  * Fetch the root file handle (call mount daemon)
181199767f8SToomas Soome  * Return zero or error number.
182199767f8SToomas Soome  */
183199767f8SToomas Soome int
nfs_getrootfh(struct iodesc * d,char * path,uint32_t * fhlenp,uchar_t * fhp)184734b3a42SToomas Soome nfs_getrootfh(struct iodesc *d, char *path, uint32_t *fhlenp, uchar_t *fhp)
185199767f8SToomas Soome {
186859472daSToomas Soome 	void *pkt = NULL;
187199767f8SToomas Soome 	int len;
188199767f8SToomas Soome 	struct args {
189199767f8SToomas Soome 		uint32_t len;
190199767f8SToomas Soome 		char path[FNAME_SIZE];
191199767f8SToomas Soome 	} *args;
192199767f8SToomas Soome 	struct repl {
193199767f8SToomas Soome 		uint32_t errno;
194199767f8SToomas Soome 		uint32_t fhsize;
195734b3a42SToomas Soome 		uchar_t fh[NFS_V3MAXFHSIZE];
196199767f8SToomas Soome 		uint32_t authcnt;
197199767f8SToomas Soome 		uint32_t auth[7];
198199767f8SToomas Soome 	} *repl;
199199767f8SToomas Soome 	struct {
200199767f8SToomas Soome 		uint32_t h[RPC_HEADER_WORDS];
201199767f8SToomas Soome 		struct args d;
202199767f8SToomas Soome 	} sdata;
203199767f8SToomas Soome 	size_t cc;
204199767f8SToomas Soome 
205199767f8SToomas Soome #ifdef NFS_DEBUG
206199767f8SToomas Soome 	if (debug)
207199767f8SToomas Soome 		printf("nfs_getrootfh: %s\n", path);
208199767f8SToomas Soome #endif
209199767f8SToomas Soome 
210199767f8SToomas Soome 	args = &sdata.d;
211199767f8SToomas Soome 
212734b3a42SToomas Soome 	bzero(args, sizeof (*args));
213199767f8SToomas Soome 	len = strlen(path);
214734b3a42SToomas Soome 	if (len > sizeof (args->path))
215734b3a42SToomas Soome 		len = sizeof (args->path);
216199767f8SToomas Soome 	args->len = htonl(len);
217199767f8SToomas Soome 	bcopy(path, args->path, len);
218734b3a42SToomas Soome 	len = sizeof (uint32_t) + roundup(len, sizeof (uint32_t));
219199767f8SToomas Soome 
220199767f8SToomas Soome 	cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER3, RPCMNT_MOUNT,
221859472daSToomas Soome 	    args, len, (void **)&repl, &pkt);
222859472daSToomas Soome 	if (cc == -1) {
223859472daSToomas Soome 		free(pkt);
224199767f8SToomas Soome 		/* errno was set by rpc_call */
225199767f8SToomas Soome 		return (errno);
226859472daSToomas Soome 	}
227859472daSToomas Soome 	if (cc < 2 * sizeof (uint32_t)) {
228859472daSToomas Soome 		free(pkt);
229199767f8SToomas Soome 		return (EBADRPC);
230859472daSToomas Soome 	}
231859472daSToomas Soome 	if (repl->errno != 0) {
232859472daSToomas Soome 		free(pkt);
233199767f8SToomas Soome 		return (ntohl(repl->errno));
234859472daSToomas Soome 	}
235199767f8SToomas Soome 	*fhlenp = ntohl(repl->fhsize);
236199767f8SToomas Soome 	bcopy(repl->fh, fhp, *fhlenp);
237b68080e0SToomas Soome 
238b68080e0SToomas Soome 	set_nfs_read_size();
239859472daSToomas Soome 	free(pkt);
240199767f8SToomas Soome 	return (0);
241199767f8SToomas Soome }
242199767f8SToomas Soome 
243199767f8SToomas Soome /*
244199767f8SToomas Soome  * Lookup a file.  Store handle and attributes.
245199767f8SToomas Soome  * Return zero or error number.
246199767f8SToomas Soome  */
247199767f8SToomas Soome int
nfs_lookupfh(struct nfs_iodesc * d,const char * name,struct nfs_iodesc * newfd)248199767f8SToomas Soome nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd)
249199767f8SToomas Soome {
250859472daSToomas Soome 	void *pkt = NULL;
2514fa25acaSToomas Soome 	int len, pos;
252199767f8SToomas Soome 	struct args {
253199767f8SToomas Soome 		uint32_t fhsize;
254199767f8SToomas Soome 		uint32_t fhplusname[1 +
255734b3a42SToomas Soome 		    (NFS_V3MAXFHSIZE + FNAME_SIZE) / sizeof (uint32_t)];
256199767f8SToomas Soome 	} *args;
257199767f8SToomas Soome 	struct repl {
258199767f8SToomas Soome 		uint32_t errno;
259199767f8SToomas Soome 		uint32_t fhsize;
260199767f8SToomas Soome 		uint32_t fhplusattr[(NFS_V3MAXFHSIZE +
261734b3a42SToomas Soome 		    2 * (sizeof (uint32_t) +
262734b3a42SToomas Soome 		    sizeof (struct nfsv3_fattrs))) / sizeof (uint32_t)];
263199767f8SToomas Soome 	} *repl;
264199767f8SToomas Soome 	struct {
265199767f8SToomas Soome 		uint32_t h[RPC_HEADER_WORDS];
266199767f8SToomas Soome 		struct args d;
267199767f8SToomas Soome 	} sdata;
268199767f8SToomas Soome 	ssize_t cc;
269199767f8SToomas Soome 
270199767f8SToomas Soome #ifdef NFS_DEBUG
271199767f8SToomas Soome 	if (debug)
272199767f8SToomas Soome 		printf("lookupfh: called\n");
273199767f8SToomas Soome #endif
274199767f8SToomas Soome 
275199767f8SToomas Soome 	args = &sdata.d;
276199767f8SToomas Soome 
277734b3a42SToomas Soome 	bzero(args, sizeof (*args));
278199767f8SToomas Soome 	args->fhsize = htonl(d->fhsize);
279199767f8SToomas Soome 	bcopy(d->fh, args->fhplusname, d->fhsize);
280199767f8SToomas Soome 	len = strlen(name);
281199767f8SToomas Soome 	if (len > FNAME_SIZE)
282199767f8SToomas Soome 		len = FNAME_SIZE;
283734b3a42SToomas Soome 	pos = roundup(d->fhsize, sizeof (uint32_t)) / sizeof (uint32_t);
284199767f8SToomas Soome 	args->fhplusname[pos++] = htonl(len);
285199767f8SToomas Soome 	bcopy(name, &args->fhplusname[pos], len);
286734b3a42SToomas Soome 	len = sizeof (uint32_t) + pos * sizeof (uint32_t) +
287734b3a42SToomas Soome 	    roundup(len, sizeof (uint32_t));
288199767f8SToomas Soome 
289199767f8SToomas Soome 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_LOOKUP,
290859472daSToomas Soome 	    args, len, (void **)&repl, &pkt);
291859472daSToomas Soome 	if (cc == -1) {
292859472daSToomas Soome 		free(pkt);
293199767f8SToomas Soome 		return (errno);		/* XXX - from rpc_call */
294859472daSToomas Soome 	}
295734b3a42SToomas Soome 	if (cc < 2 * sizeof (uint32_t)) {
296859472daSToomas Soome 		free(pkt);
297199767f8SToomas Soome 		return (EIO);
298859472daSToomas Soome 	}
299859472daSToomas Soome 	if (repl->errno != 0) {
300859472daSToomas Soome 		free(pkt);
301199767f8SToomas Soome 		/* saerrno.h now matches NFS error numbers. */
302199767f8SToomas Soome 		return (ntohl(repl->errno));
303859472daSToomas Soome 	}
304199767f8SToomas Soome 	newfd->fhsize = ntohl(repl->fhsize);
305199767f8SToomas Soome 	bcopy(repl->fhplusattr, &newfd->fh, newfd->fhsize);
306734b3a42SToomas Soome 	pos = roundup(newfd->fhsize, sizeof (uint32_t)) / sizeof (uint32_t);
307859472daSToomas Soome 	if (repl->fhplusattr[pos++] == 0) {
308859472daSToomas Soome 		free(pkt);
309199767f8SToomas Soome 		return (EIO);
310859472daSToomas Soome 	}
311734b3a42SToomas Soome 	bcopy(&repl->fhplusattr[pos], &newfd->fa, sizeof (newfd->fa));
312859472daSToomas Soome 	free(pkt);
313199767f8SToomas Soome 	return (0);
314199767f8SToomas Soome }
315199767f8SToomas Soome 
316199767f8SToomas Soome /*
317199767f8SToomas Soome  * Get the destination of a symbolic link.
318199767f8SToomas Soome  */
319199767f8SToomas Soome int
nfs_readlink(struct nfs_iodesc * d,char * buf)320199767f8SToomas Soome nfs_readlink(struct nfs_iodesc *d, char *buf)
321199767f8SToomas Soome {
322859472daSToomas Soome 	void *pkt = NULL;
323199767f8SToomas Soome 	struct args {
324199767f8SToomas Soome 		uint32_t fhsize;
325734b3a42SToomas Soome 		uchar_t fh[NFS_V3MAXFHSIZE];
326199767f8SToomas Soome 	} *args;
327199767f8SToomas Soome 	struct repl {
328199767f8SToomas Soome 		uint32_t errno;
329199767f8SToomas Soome 		uint32_t ok;
330199767f8SToomas Soome 		struct nfsv3_fattrs fa;
331199767f8SToomas Soome 		uint32_t len;
332734b3a42SToomas Soome 		uchar_t path[NFS_MAXPATHLEN];
333199767f8SToomas Soome 	} *repl;
334199767f8SToomas Soome 	struct {
335199767f8SToomas Soome 		uint32_t h[RPC_HEADER_WORDS];
336199767f8SToomas Soome 		struct args d;
337199767f8SToomas Soome 	} sdata;
338199767f8SToomas Soome 	ssize_t cc;
339859472daSToomas Soome 	int rc = 0;
340199767f8SToomas Soome 
341199767f8SToomas Soome #ifdef NFS_DEBUG
342199767f8SToomas Soome 	if (debug)
343199767f8SToomas Soome 		printf("readlink: called\n");
344199767f8SToomas Soome #endif
345199767f8SToomas Soome 
346199767f8SToomas Soome 	args = &sdata.d;
347199767f8SToomas Soome 
348734b3a42SToomas Soome 	bzero(args, sizeof (*args));
349199767f8SToomas Soome 	args->fhsize = htonl(d->fhsize);
350199767f8SToomas Soome 	bcopy(d->fh, args->fh, d->fhsize);
351199767f8SToomas Soome 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READLINK,
352734b3a42SToomas Soome 	    args, sizeof (uint32_t) + roundup(d->fhsize, sizeof (uint32_t)),
353859472daSToomas Soome 	    (void **)&repl, &pkt);
354199767f8SToomas Soome 	if (cc == -1)
355199767f8SToomas Soome 		return (errno);
356199767f8SToomas Soome 
357734b3a42SToomas Soome 	if (cc < 2 * sizeof (uint32_t)) {
358859472daSToomas Soome 		rc = EIO;
359859472daSToomas Soome 		goto done;
360859472daSToomas Soome 	}
361199767f8SToomas Soome 
362859472daSToomas Soome 	if (repl->errno != 0) {
363859472daSToomas Soome 		rc = ntohl(repl->errno);
364859472daSToomas Soome 		goto done;
365859472daSToomas Soome 	}
366199767f8SToomas Soome 
367859472daSToomas Soome 	if (repl->ok == 0) {
368859472daSToomas Soome 		rc = EIO;
369859472daSToomas Soome 		goto done;
370859472daSToomas Soome 	}
371199767f8SToomas Soome 
372199767f8SToomas Soome 	repl->len = ntohl(repl->len);
373859472daSToomas Soome 	if (repl->len > NFS_MAXPATHLEN) {
374859472daSToomas Soome 		rc = ENAMETOOLONG;
375859472daSToomas Soome 		goto done;
376859472daSToomas Soome 	}
377199767f8SToomas Soome 
378199767f8SToomas Soome 	bcopy(repl->path, buf, repl->len);
379199767f8SToomas Soome 	buf[repl->len] = 0;
380859472daSToomas Soome done:
381859472daSToomas Soome 	free(pkt);
382859472daSToomas Soome 	return (rc);
383199767f8SToomas Soome }
384199767f8SToomas Soome 
385199767f8SToomas Soome /*
386199767f8SToomas Soome  * Read data from a file.
387199767f8SToomas Soome  * Return transfer count or -1 (and set errno)
388199767f8SToomas Soome  */
389199767f8SToomas Soome ssize_t
nfs_readdata(struct nfs_iodesc * d,off_t off,void * addr,size_t len)390199767f8SToomas Soome nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
391199767f8SToomas Soome {
392859472daSToomas Soome 	void *pkt = NULL;
393199767f8SToomas Soome 	struct args {
394199767f8SToomas Soome 		uint32_t fhsize;
395734b3a42SToomas Soome 		uint32_t fhoffcnt[NFS_V3MAXFHSIZE / sizeof (uint32_t) + 3];
396199767f8SToomas Soome 	} *args;
397199767f8SToomas Soome 	struct repl {
398199767f8SToomas Soome 		uint32_t errno;
399199767f8SToomas Soome 		uint32_t ok;
400199767f8SToomas Soome 		struct nfsv3_fattrs fa;
401199767f8SToomas Soome 		uint32_t count;
402199767f8SToomas Soome 		uint32_t eof;
403199767f8SToomas Soome 		uint32_t len;
404734b3a42SToomas Soome 		uchar_t data[NFSREAD_MAX_SIZE];
405199767f8SToomas Soome 	} *repl;
406199767f8SToomas Soome 	struct {
407199767f8SToomas Soome 		uint32_t h[RPC_HEADER_WORDS];
408199767f8SToomas Soome 		struct args d;
409199767f8SToomas Soome 	} sdata;
410199767f8SToomas Soome 	size_t cc;
411199767f8SToomas Soome 	long x;
412199767f8SToomas Soome 	int hlen, rlen, pos;
413199767f8SToomas Soome 
414199767f8SToomas Soome 	args = &sdata.d;
415199767f8SToomas Soome 
416734b3a42SToomas Soome 	bzero(args, sizeof (*args));
417199767f8SToomas Soome 	args->fhsize = htonl(d->fhsize);
418199767f8SToomas Soome 	bcopy(d->fh, args->fhoffcnt, d->fhsize);
419734b3a42SToomas Soome 	pos = roundup(d->fhsize, sizeof (uint32_t)) / sizeof (uint32_t);
420199767f8SToomas Soome 	args->fhoffcnt[pos++] = 0;
421199767f8SToomas Soome 	args->fhoffcnt[pos++] = htonl((uint32_t)off);
422b68080e0SToomas Soome 	if (len > nfs_read_size)
423b68080e0SToomas Soome 		len = nfs_read_size;
424199767f8SToomas Soome 	args->fhoffcnt[pos] = htonl((uint32_t)len);
425b68080e0SToomas Soome 	hlen = offsetof(struct repl, data[0]);
426199767f8SToomas Soome 
427199767f8SToomas Soome 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READ,
428734b3a42SToomas Soome 	    args, 4 * sizeof (uint32_t) + roundup(d->fhsize, sizeof (uint32_t)),
429859472daSToomas Soome 	    (void **)&repl, &pkt);
430859472daSToomas Soome 	if (cc == -1) {
431199767f8SToomas Soome 		/* errno was already set by rpc_call */
432199767f8SToomas Soome 		return (-1);
433859472daSToomas Soome 	}
434199767f8SToomas Soome 	if (cc < hlen) {
435199767f8SToomas Soome 		errno = EBADRPC;
436859472daSToomas Soome 		free(pkt);
437199767f8SToomas Soome 		return (-1);
438199767f8SToomas Soome 	}
439199767f8SToomas Soome 	if (repl->errno != 0) {
440199767f8SToomas Soome 		errno = ntohl(repl->errno);
441859472daSToomas Soome 		free(pkt);
442199767f8SToomas Soome 		return (-1);
443199767f8SToomas Soome 	}
444199767f8SToomas Soome 	rlen = cc - hlen;
445199767f8SToomas Soome 	x = ntohl(repl->count);
446199767f8SToomas Soome 	if (rlen < x) {
447199767f8SToomas Soome 		printf("nfsread: short packet, %d < %ld\n", rlen, x);
448199767f8SToomas Soome 		errno = EBADRPC;
449859472daSToomas Soome 		free(pkt);
450199767f8SToomas Soome 		return (-1);
451199767f8SToomas Soome 	}
452199767f8SToomas Soome 	bcopy(repl->data, addr, x);
453859472daSToomas Soome 	free(pkt);
454199767f8SToomas Soome 	return (x);
455199767f8SToomas Soome }
456199767f8SToomas Soome 
457199767f8SToomas Soome /*
458199767f8SToomas Soome  * Open a file.
459199767f8SToomas Soome  * return zero or error number
460199767f8SToomas Soome  */
461199767f8SToomas Soome int
nfs_open(const char * upath,struct open_file * f)462199767f8SToomas Soome nfs_open(const char *upath, struct open_file *f)
463199767f8SToomas Soome {
4646538c7b4SToomas Soome 	struct devdesc *dev;
465199767f8SToomas Soome 	struct iodesc *desc;
4664fa25acaSToomas Soome 	struct nfs_iodesc *currfd = NULL;
467199767f8SToomas Soome 	char buf[2 * NFS_V3MAXFHSIZE + 3];
468734b3a42SToomas Soome 	uchar_t *fh;
469199767f8SToomas Soome 	char *cp;
470199767f8SToomas Soome 	int i;
4714fa25acaSToomas Soome 	struct nfs_iodesc *newfd = NULL;
472199767f8SToomas Soome 	char *ncp;
473199767f8SToomas Soome 	int c;
474199767f8SToomas Soome 	char namebuf[NFS_MAXPATHLEN + 1];
475199767f8SToomas Soome 	char linkbuf[NFS_MAXPATHLEN + 1];
476199767f8SToomas Soome 	int nlinks = 0;
477199767f8SToomas Soome 	int error;
4784fa25acaSToomas Soome 	char *path = NULL;
479199767f8SToomas Soome 
480aa61755eSToomas Soome 	if (netproto != NET_NFS)
481aa61755eSToomas Soome 		return (EINVAL);
482aa61755eSToomas Soome 
4836538c7b4SToomas Soome 	dev = f->f_devdata;
484199767f8SToomas Soome #ifdef NFS_DEBUG
4854fa25acaSToomas Soome 	if (debug)
4864fa25acaSToomas Soome 		printf("nfs_open: %s (rootpath=%s)\n", upath, rootpath);
487199767f8SToomas Soome #endif
488199767f8SToomas Soome 	if (!rootpath[0]) {
489199767f8SToomas Soome 		printf("no rootpath, no nfs\n");
490199767f8SToomas Soome 		return (ENXIO);
491199767f8SToomas Soome 	}
492199767f8SToomas Soome 
493859472daSToomas Soome 	if (f->f_dev->dv_type != DEVT_NET)
494199767f8SToomas Soome 		return (EINVAL);
495199767f8SToomas Soome 
4966538c7b4SToomas Soome 	if (!(desc = socktodesc(*(int *)(dev->d_opendata))))
497199767f8SToomas Soome 		return (EINVAL);
498199767f8SToomas Soome 
499199767f8SToomas Soome 	/* Bind to a reserved port. */
500199767f8SToomas Soome 	desc->myport = htons(--rpc_port);
501199767f8SToomas Soome 	desc->destip = rootip;
502199767f8SToomas Soome 	if ((error = nfs_getrootfh(desc, rootpath, &nfs_root_node.fhsize,
503199767f8SToomas Soome 	    nfs_root_node.fh)))
504199767f8SToomas Soome 		return (error);
505199767f8SToomas Soome 	nfs_root_node.fa.fa_type  = htonl(NFDIR);
506199767f8SToomas Soome 	nfs_root_node.fa.fa_mode  = htonl(0755);
507199767f8SToomas Soome 	nfs_root_node.fa.fa_nlink = htonl(2);
508199767f8SToomas Soome 	nfs_root_node.iodesc = desc;
509199767f8SToomas Soome 
510199767f8SToomas Soome 	fh = &nfs_root_node.fh[0];
511199767f8SToomas Soome 	buf[0] = 'X';
512199767f8SToomas Soome 	cp = &buf[1];
513199767f8SToomas Soome 	for (i = 0; i < nfs_root_node.fhsize; i++, cp += 2)
514199767f8SToomas Soome 		sprintf(cp, "%02x", fh[i]);
515199767f8SToomas Soome 	sprintf(cp, "X");
516199767f8SToomas Soome 	setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
517199767f8SToomas Soome 	setenv("boot.nfsroot.path", rootpath, 1);
518199767f8SToomas Soome 	setenv("boot.nfsroot.nfshandle", buf, 1);
519199767f8SToomas Soome 	sprintf(buf, "%d", nfs_root_node.fhsize);
520199767f8SToomas Soome 	setenv("boot.nfsroot.nfshandlelen", buf, 1);
521199767f8SToomas Soome 
522199767f8SToomas Soome 	/* Allocate file system specific data structure */
523734b3a42SToomas Soome 	currfd = malloc(sizeof (*newfd));
524199767f8SToomas Soome 	if (currfd == NULL) {
525199767f8SToomas Soome 		error = ENOMEM;
526199767f8SToomas Soome 		goto out;
527199767f8SToomas Soome 	}
528734b3a42SToomas Soome 	bcopy(&nfs_root_node, currfd, sizeof (*currfd));
52900b63190SToomas Soome 	newfd = NULL;
530199767f8SToomas Soome 
531199767f8SToomas Soome 	cp = path = strdup(upath);
532199767f8SToomas Soome 	if (path == NULL) {
533199767f8SToomas Soome 		error = ENOMEM;
534199767f8SToomas Soome 		goto out;
535199767f8SToomas Soome 	}
536199767f8SToomas Soome 	while (*cp) {
537199767f8SToomas Soome 		/*
538199767f8SToomas Soome 		 * Remove extra separators
539199767f8SToomas Soome 		 */
540199767f8SToomas Soome 		while (*cp == '/')
541199767f8SToomas Soome 			cp++;
542199767f8SToomas Soome 
543199767f8SToomas Soome 		if (*cp == '\0')
544199767f8SToomas Soome 			break;
545199767f8SToomas Soome 		/*
546199767f8SToomas Soome 		 * Check that current node is a directory.
547199767f8SToomas Soome 		 */
548199767f8SToomas Soome 		if (currfd->fa.fa_type != htonl(NFDIR)) {
549199767f8SToomas Soome 			error = ENOTDIR;
550199767f8SToomas Soome 			goto out;
551199767f8SToomas Soome 		}
552199767f8SToomas Soome 
553199767f8SToomas Soome 		/* allocate file system specific data structure */
554734b3a42SToomas Soome 		newfd = malloc(sizeof (*newfd));
555199767f8SToomas Soome 		if (newfd == NULL) {
556199767f8SToomas Soome 			error = ENOMEM;
557199767f8SToomas Soome 			goto out;
558199767f8SToomas Soome 		}
559199767f8SToomas Soome 		newfd->iodesc = currfd->iodesc;
560199767f8SToomas Soome 
561199767f8SToomas Soome 		/*
562199767f8SToomas Soome 		 * Get next component of path name.
563199767f8SToomas Soome 		 */
564199767f8SToomas Soome 		{
565199767f8SToomas Soome 			int len = 0;
566199767f8SToomas Soome 
567199767f8SToomas Soome 			ncp = cp;
568199767f8SToomas Soome 			while ((c = *cp) != '\0' && c != '/') {
569199767f8SToomas Soome 				if (++len > NFS_MAXNAMLEN) {
570199767f8SToomas Soome 					error = ENOENT;
571199767f8SToomas Soome 					goto out;
572199767f8SToomas Soome 				}
573199767f8SToomas Soome 				cp++;
574199767f8SToomas Soome 			}
575199767f8SToomas Soome 			*cp = '\0';
576199767f8SToomas Soome 		}
577199767f8SToomas Soome 
578199767f8SToomas Soome 		/* lookup a file handle */
579199767f8SToomas Soome 		error = nfs_lookupfh(currfd, ncp, newfd);
580199767f8SToomas Soome 		*cp = c;
581199767f8SToomas Soome 		if (error)
582199767f8SToomas Soome 			goto out;
583199767f8SToomas Soome 
584199767f8SToomas Soome 		/*
585199767f8SToomas Soome 		 * Check for symbolic link
586199767f8SToomas Soome 		 */
587199767f8SToomas Soome 		if (newfd->fa.fa_type == htonl(NFLNK)) {
588199767f8SToomas Soome 			int link_len, len;
589199767f8SToomas Soome 
590199767f8SToomas Soome 			error = nfs_readlink(newfd, linkbuf);
591199767f8SToomas Soome 			if (error)
592199767f8SToomas Soome 				goto out;
593199767f8SToomas Soome 
594199767f8SToomas Soome 			link_len = strlen(linkbuf);
595199767f8SToomas Soome 			len = strlen(cp);
596199767f8SToomas Soome 
597734b3a42SToomas Soome 			if (link_len + len > MAXPATHLEN ||
598734b3a42SToomas Soome 			    ++nlinks > MAXSYMLINKS) {
599199767f8SToomas Soome 				error = ENOENT;
600199767f8SToomas Soome 				goto out;
601199767f8SToomas Soome 			}
602199767f8SToomas Soome 
603199767f8SToomas Soome 			bcopy(cp, &namebuf[link_len], len + 1);
604199767f8SToomas Soome 			bcopy(linkbuf, namebuf, link_len);
605199767f8SToomas Soome 
606199767f8SToomas Soome 			/*
607199767f8SToomas Soome 			 * If absolute pathname, restart at root.
608199767f8SToomas Soome 			 * If relative pathname, restart at parent directory.
609199767f8SToomas Soome 			 */
610199767f8SToomas Soome 			cp = namebuf;
611199767f8SToomas Soome 			if (*cp == '/')
612734b3a42SToomas Soome 				bcopy(&nfs_root_node, currfd, sizeof (*currfd));
613199767f8SToomas Soome 
614199767f8SToomas Soome 			free(newfd);
61500b63190SToomas Soome 			newfd = NULL;
616199767f8SToomas Soome 
617199767f8SToomas Soome 			continue;
618199767f8SToomas Soome 		}
619199767f8SToomas Soome 
620199767f8SToomas Soome 		free(currfd);
621199767f8SToomas Soome 		currfd = newfd;
62200b63190SToomas Soome 		newfd = NULL;
623199767f8SToomas Soome 	}
624199767f8SToomas Soome 
625199767f8SToomas Soome 	error = 0;
626199767f8SToomas Soome 
627199767f8SToomas Soome out:
628199767f8SToomas Soome 	free(newfd);
629199767f8SToomas Soome 	free(path);
630199767f8SToomas Soome 	if (!error) {
631199767f8SToomas Soome 		currfd->off = 0;
632199767f8SToomas Soome 		currfd->cookie = 0;
633734b3a42SToomas Soome 		f->f_fsdata = currfd;
634199767f8SToomas Soome 		return (0);
635199767f8SToomas Soome 	}
636199767f8SToomas Soome 
637199767f8SToomas Soome #ifdef NFS_DEBUG
638199767f8SToomas Soome 	if (debug)
639199767f8SToomas Soome 		printf("nfs_open: %s lookupfh failed: %s\n",
640199767f8SToomas Soome 		    path, strerror(error));
641199767f8SToomas Soome #endif
642199767f8SToomas Soome 	free(currfd);
643199767f8SToomas Soome 
644199767f8SToomas Soome 	return (error);
645199767f8SToomas Soome }
646199767f8SToomas Soome 
647199767f8SToomas Soome int
nfs_close(struct open_file * f)648199767f8SToomas Soome nfs_close(struct open_file *f)
649199767f8SToomas Soome {
650199767f8SToomas Soome 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
651199767f8SToomas Soome 
652199767f8SToomas Soome #ifdef NFS_DEBUG
653199767f8SToomas Soome 	if (debug)
654734b3a42SToomas Soome 		printf("nfs_close: fp=%#p\n", fp);
655199767f8SToomas Soome #endif
656199767f8SToomas Soome 
657859472daSToomas Soome 	free(fp);
658859472daSToomas Soome 	f->f_fsdata = NULL;
659199767f8SToomas Soome 
660199767f8SToomas Soome 	return (0);
661199767f8SToomas Soome }
662199767f8SToomas Soome 
663199767f8SToomas Soome /*
664199767f8SToomas Soome  * read a portion of a file
665199767f8SToomas Soome  */
666199767f8SToomas Soome int
nfs_read(struct open_file * f,void * buf,size_t size,size_t * resid)667199767f8SToomas Soome nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
668199767f8SToomas Soome {
669199767f8SToomas Soome 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
670199767f8SToomas Soome 	ssize_t cc;
671199767f8SToomas Soome 	char *addr = buf;
672199767f8SToomas Soome 
673199767f8SToomas Soome #ifdef NFS_DEBUG
674199767f8SToomas Soome 	if (debug)
675734b3a42SToomas Soome 		printf("nfs_read: size=%zu off=%j\n", size,
676734b3a42SToomas Soome 		    (intmax_t)fp->off);
677199767f8SToomas Soome #endif
678734b3a42SToomas Soome 	while (size > 0) {
679199767f8SToomas Soome 		twiddle(16);
680734b3a42SToomas Soome 		cc = nfs_readdata(fp, fp->off, addr, size);
681199767f8SToomas Soome 		/* XXX maybe should retry on certain errors */
682199767f8SToomas Soome 		if (cc == -1) {
683199767f8SToomas Soome #ifdef NFS_DEBUG
684199767f8SToomas Soome 			if (debug)
685199767f8SToomas Soome 				printf("nfs_read: read: %s", strerror(errno));
686199767f8SToomas Soome #endif
687199767f8SToomas Soome 			return (errno);	/* XXX - from nfs_readdata */
688199767f8SToomas Soome 		}
689199767f8SToomas Soome 		if (cc == 0) {
690199767f8SToomas Soome #ifdef NFS_DEBUG
691199767f8SToomas Soome 			if (debug)
692199767f8SToomas Soome 				printf("nfs_read: hit EOF unexpectantly");
693199767f8SToomas Soome #endif
694199767f8SToomas Soome 			goto ret;
695199767f8SToomas Soome 		}
696199767f8SToomas Soome 		fp->off += cc;
697199767f8SToomas Soome 		addr += cc;
698199767f8SToomas Soome 		size -= cc;
699199767f8SToomas Soome 	}
700199767f8SToomas Soome ret:
701199767f8SToomas Soome 	if (resid)
702199767f8SToomas Soome 		*resid = size;
703199767f8SToomas Soome 
704199767f8SToomas Soome 	return (0);
705199767f8SToomas Soome }
706199767f8SToomas Soome 
707199767f8SToomas Soome off_t
nfs_seek(struct open_file * f,off_t offset,int where)708199767f8SToomas Soome nfs_seek(struct open_file *f, off_t offset, int where)
709199767f8SToomas Soome {
710199767f8SToomas Soome 	struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
711199767f8SToomas Soome 	uint32_t size = ntohl(d->fa.fa_size.val[1]);
712199767f8SToomas Soome 
713199767f8SToomas Soome 	switch (where) {
714199767f8SToomas Soome 	case SEEK_SET:
715199767f8SToomas Soome 		d->off = offset;
716199767f8SToomas Soome 		break;
717199767f8SToomas Soome 	case SEEK_CUR:
718199767f8SToomas Soome 		d->off += offset;
719199767f8SToomas Soome 		break;
720199767f8SToomas Soome 	case SEEK_END:
721199767f8SToomas Soome 		d->off = size - offset;
722199767f8SToomas Soome 		break;
723199767f8SToomas Soome 	default:
724199767f8SToomas Soome 		errno = EINVAL;
725199767f8SToomas Soome 		return (-1);
726199767f8SToomas Soome 	}
727199767f8SToomas Soome 
728199767f8SToomas Soome 	return (d->off);
729199767f8SToomas Soome }
730199767f8SToomas Soome 
731199767f8SToomas Soome /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5, NFSOCK=6, NFFIFO=7 */
732199767f8SToomas Soome int nfs_stat_types[9] = {
733199767f8SToomas Soome 	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO, 0 };
734199767f8SToomas Soome 
735199767f8SToomas Soome int
nfs_stat(struct open_file * f,struct stat * sb)736199767f8SToomas Soome nfs_stat(struct open_file *f, struct stat *sb)
737199767f8SToomas Soome {
738199767f8SToomas Soome 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
739199767f8SToomas Soome 	uint32_t ftype, mode;
740199767f8SToomas Soome 
741199767f8SToomas Soome 	ftype = ntohl(fp->fa.fa_type);
742199767f8SToomas Soome 	mode  = ntohl(fp->fa.fa_mode);
743199767f8SToomas Soome 	mode |= nfs_stat_types[ftype & 7];
744199767f8SToomas Soome 
745199767f8SToomas Soome 	sb->st_mode  = mode;
746199767f8SToomas Soome 	sb->st_nlink = ntohl(fp->fa.fa_nlink);
747199767f8SToomas Soome 	sb->st_uid   = ntohl(fp->fa.fa_uid);
748199767f8SToomas Soome 	sb->st_gid   = ntohl(fp->fa.fa_gid);
749199767f8SToomas Soome 	sb->st_size  = ntohl(fp->fa.fa_size.val[1]);
750199767f8SToomas Soome 
751199767f8SToomas Soome 	return (0);
752199767f8SToomas Soome }
753199767f8SToomas Soome 
754199767f8SToomas Soome static int
nfs_readdir(struct open_file * f,struct dirent * d)755199767f8SToomas Soome nfs_readdir(struct open_file *f, struct dirent *d)
756199767f8SToomas Soome {
757199767f8SToomas Soome 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
758199767f8SToomas Soome 	struct nfsv3_readdir_repl *repl;
759199767f8SToomas Soome 	struct nfsv3_readdir_entry *rent;
760859472daSToomas Soome 	static void *pkt = NULL;
761199767f8SToomas Soome 	static char *buf;
762199767f8SToomas Soome 	static struct nfs_iodesc *pfp = NULL;
763199767f8SToomas Soome 	static uint64_t cookie = 0;
764199767f8SToomas Soome 	size_t cc;
765859472daSToomas Soome 	int pos, rc;
766199767f8SToomas Soome 
767199767f8SToomas Soome 	struct args {
768199767f8SToomas Soome 		uint32_t fhsize;
769199767f8SToomas Soome 		uint32_t fhpluscookie[5 + NFS_V3MAXFHSIZE];
770199767f8SToomas Soome 	} *args;
771199767f8SToomas Soome 	struct {
772199767f8SToomas Soome 		uint32_t h[RPC_HEADER_WORDS];
773199767f8SToomas Soome 		struct args d;
774199767f8SToomas Soome 	} sdata;
775199767f8SToomas Soome 
776199767f8SToomas Soome 	if (fp != pfp || fp->off != cookie) {
777199767f8SToomas Soome 		pfp = NULL;
778199767f8SToomas Soome 	refill:
779859472daSToomas Soome 		free(pkt);
780859472daSToomas Soome 		pkt = NULL;
781199767f8SToomas Soome 		args = &sdata.d;
782734b3a42SToomas Soome 		bzero(args, sizeof (*args));
783199767f8SToomas Soome 
784199767f8SToomas Soome 		args->fhsize = htonl(fp->fhsize);
785199767f8SToomas Soome 		bcopy(fp->fh, args->fhpluscookie, fp->fhsize);
786734b3a42SToomas Soome 		pos = roundup(fp->fhsize,
787734b3a42SToomas Soome 		    sizeof (uint32_t)) / sizeof (uint32_t);
788199767f8SToomas Soome 		args->fhpluscookie[pos++] = htonl(fp->off >> 32);
789199767f8SToomas Soome 		args->fhpluscookie[pos++] = htonl(fp->off);
790199767f8SToomas Soome 		args->fhpluscookie[pos++] = htonl(fp->cookie >> 32);
791199767f8SToomas Soome 		args->fhpluscookie[pos++] = htonl(fp->cookie);
792199767f8SToomas Soome 		args->fhpluscookie[pos] = htonl(NFS_READDIRSIZE);
793199767f8SToomas Soome 
794199767f8SToomas Soome 		cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READDIR,
795734b3a42SToomas Soome 		    args, 6 * sizeof (uint32_t) +
796734b3a42SToomas Soome 		    roundup(fp->fhsize, sizeof (uint32_t)),
797859472daSToomas Soome 		    (void **)&buf, &pkt);
798859472daSToomas Soome 		if (cc == -1) {
799859472daSToomas Soome 			rc = errno;
800859472daSToomas Soome 			goto err;
801859472daSToomas Soome 		}
802199767f8SToomas Soome 		repl = (struct nfsv3_readdir_repl *)buf;
803859472daSToomas Soome 		if (repl->errno != 0) {
804859472daSToomas Soome 			rc = ntohl(repl->errno);
805859472daSToomas Soome 			goto err;
806859472daSToomas Soome 		}
807199767f8SToomas Soome 		pfp = fp;
808199767f8SToomas Soome 		cookie = fp->off;
809199767f8SToomas Soome 		fp->cookie = ((uint64_t)ntohl(repl->cookiev0) << 32) |
810199767f8SToomas Soome 		    ntohl(repl->cookiev1);
811199767f8SToomas Soome 		buf += sizeof (struct nfsv3_readdir_repl);
812199767f8SToomas Soome 	}
813199767f8SToomas Soome 	rent = (struct nfsv3_readdir_entry *)buf;
814199767f8SToomas Soome 
815199767f8SToomas Soome 	if (rent->follows == 0) {
816199767f8SToomas Soome 		/* fid0 is actually eof */
817199767f8SToomas Soome 		if (rent->fid0 != 0) {
818859472daSToomas Soome 			rc = ENOENT;
819859472daSToomas Soome 			goto err;
820199767f8SToomas Soome 		}
821199767f8SToomas Soome 		goto refill;
822199767f8SToomas Soome 	}
823199767f8SToomas Soome 
824199767f8SToomas Soome 	d->d_namlen = ntohl(rent->len);
825199767f8SToomas Soome 	bcopy(rent->nameplus, d->d_name, d->d_namlen);
826199767f8SToomas Soome 	d->d_name[d->d_namlen] = '\0';
827199767f8SToomas Soome 
828734b3a42SToomas Soome 	pos = roundup(d->d_namlen, sizeof (uint32_t)) / sizeof (uint32_t);
829199767f8SToomas Soome 	fp->off = cookie = ((uint64_t)ntohl(rent->nameplus[pos]) << 32) |
830199767f8SToomas Soome 	    ntohl(rent->nameplus[pos + 1]);
831199767f8SToomas Soome 	pos += 2;
83238005992SToomas Soome 	buf = (char *)&rent->nameplus[pos];
833199767f8SToomas Soome 	return (0);
834859472daSToomas Soome 
835859472daSToomas Soome err:
836859472daSToomas Soome 	free(pkt);
837859472daSToomas Soome 	pkt = NULL;
838859472daSToomas Soome 	pfp = NULL;
839859472daSToomas Soome 	cookie = 0;
840859472daSToomas Soome 	return (rc);
841199767f8SToomas Soome }
842