nfs.c revision 252700
1/*	$NetBSD: nfs.c,v 1.2 1998/01/24 12:43:09 drochner Exp $	*/
2
3/*-
4 *  Copyright (c) 1993 John Brezak
5 *  All rights reserved.
6 *
7 *  Redistribution and use in source and binary forms, with or without
8 *  modification, are permitted provided that the following conditions
9 *  are met:
10 *  1. Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 *  2. Redistributions in binary form must reproduce the above copyright
13 *     notice, this list of conditions and the following disclaimer in the
14 *     documentation and/or other materials provided with the distribution.
15 *  3. The name of the author may not be used to endorse or promote products
16 *     derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD: stable/9/lib/libstand/nfs.c 252700 2013-07-04 15:19:45Z mav $");
33
34#include <sys/param.h>
35#include <sys/time.h>
36#include <sys/socket.h>
37#include <sys/stat.h>
38#include <string.h>
39
40#include <netinet/in.h>
41#include <netinet/in_systm.h>
42
43#include "rpcv2.h"
44#include "nfsv2.h"
45
46#include "stand.h"
47#include "net.h"
48#include "netif.h"
49#include "rpc.h"
50
51#define NFS_DEBUGxx
52
53#define NFSREAD_SIZE 1024
54
55/* Define our own NFS attributes without NQNFS stuff. */
56#ifdef OLD_NFSV2
57struct nfsv2_fattrs {
58	n_long	fa_type;
59	n_long	fa_mode;
60	n_long	fa_nlink;
61	n_long	fa_uid;
62	n_long	fa_gid;
63	n_long	fa_size;
64	n_long	fa_blocksize;
65	n_long	fa_rdev;
66	n_long	fa_blocks;
67	n_long	fa_fsid;
68	n_long	fa_fileid;
69	struct nfsv2_time fa_atime;
70	struct nfsv2_time fa_mtime;
71	struct nfsv2_time fa_ctime;
72};
73
74struct nfs_read_args {
75	u_char	fh[NFS_FHSIZE];
76	n_long	off;
77	n_long	len;
78	n_long	xxx;			/* XXX what's this for? */
79};
80
81/* Data part of nfs rpc reply (also the largest thing we receive) */
82struct nfs_read_repl {
83	n_long	errno;
84	struct	nfsv2_fattrs fa;
85	n_long	count;
86	u_char	data[NFSREAD_SIZE];
87};
88
89#ifndef NFS_NOSYMLINK
90struct nfs_readlnk_repl {
91	n_long	errno;
92	n_long	len;
93	char	path[NFS_MAXPATHLEN];
94};
95#endif
96
97struct nfs_readdir_args {
98	u_char	fh[NFS_FHSIZE];
99	n_long	cookie;
100	n_long	count;
101};
102
103struct nfs_readdir_data {
104	n_long	fileid;
105	n_long	len;
106	char	name[0];
107};
108
109struct nfs_readdir_off {
110	n_long	cookie;
111	n_long	follows;
112};
113
114struct nfs_iodesc {
115	struct	iodesc	*iodesc;
116	off_t	off;
117	u_char	fh[NFS_FHSIZE];
118	struct nfsv2_fattrs fa;	/* all in network order */
119};
120#else	/* !OLD_NFSV2 */
121
122/* NFSv3 definitions */
123#define	NFS_V3MAXFHSIZE		64
124#define	NFS_VER3		3
125#define	RPCMNT_VER3		3
126#define	NFSPROCV3_LOOKUP	3
127#define	NFSPROCV3_READLINK	5
128#define	NFSPROCV3_READ		6
129#define	NFSPROCV3_READDIR	16
130
131typedef struct {
132	uint32_t val[2];
133} n_quad;
134
135struct nfsv3_time {
136	uint32_t nfs_sec;
137	uint32_t nfs_nsec;
138};
139
140struct nfsv3_fattrs {
141	uint32_t fa_type;
142	uint32_t fa_mode;
143	uint32_t fa_nlink;
144	uint32_t fa_uid;
145	uint32_t fa_gid;
146	n_quad fa_size;
147	n_quad fa_used;
148	n_quad fa_rdev;
149	n_quad fa_fsid;
150	n_quad fa_fileid;
151	struct nfsv3_time fa_atime;
152	struct nfsv3_time fa_mtime;
153	struct nfsv3_time fa_ctime;
154};
155
156/*
157 * For NFSv3, the file handle is variable in size, so most fixed sized
158 * structures for arguments won't work. For most cases, a structure
159 * that starts with any fixed size section is followed by an array
160 * that covers the maximum size required.
161 */
162struct nfsv3_readdir_repl {
163	uint32_t errno;
164	uint32_t ok;
165	struct nfsv3_fattrs fa;
166	uint32_t cookiev0;
167	uint32_t cookiev1;
168};
169
170struct nfsv3_readdir_entry {
171	uint32_t follows;
172	uint32_t fid0;
173	uint32_t fid1;
174	uint32_t len;
175	uint32_t nameplus[0];
176};
177
178struct nfs_iodesc {
179	struct iodesc *iodesc;
180	off_t off;
181	uint32_t fhsize;
182	u_char fh[NFS_V3MAXFHSIZE];
183	struct nfsv3_fattrs fa;	/* all in network order */
184};
185#endif	/* OLD_NFSV2 */
186
187/*
188 * XXX interactions with tftp? See nfswrapper.c for a confusing
189 *     issue.
190 */
191int		nfs_open(const char *path, struct open_file *f);
192static int	nfs_close(struct open_file *f);
193static int	nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
194static int	nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid);
195static off_t	nfs_seek(struct open_file *f, off_t offset, int where);
196static int	nfs_stat(struct open_file *f, struct stat *sb);
197static int	nfs_readdir(struct open_file *f, struct dirent *d);
198
199struct	nfs_iodesc nfs_root_node;
200
201struct fs_ops nfs_fsops = {
202	"nfs",
203	nfs_open,
204	nfs_close,
205	nfs_read,
206	nfs_write,
207	nfs_seek,
208	nfs_stat,
209	nfs_readdir
210};
211
212#ifdef	OLD_NFSV2
213/*
214 * Fetch the root file handle (call mount daemon)
215 * Return zero or error number.
216 */
217int
218nfs_getrootfh(struct iodesc *d, char *path, u_char *fhp)
219{
220	int len;
221	struct args {
222		n_long	len;
223		char	path[FNAME_SIZE];
224	} *args;
225	struct repl {
226		n_long	errno;
227		u_char	fh[NFS_FHSIZE];
228	} *repl;
229	struct {
230		n_long	h[RPC_HEADER_WORDS];
231		struct args d;
232	} sdata;
233	struct {
234		n_long	h[RPC_HEADER_WORDS];
235		struct repl d;
236	} rdata;
237	size_t cc;
238
239#ifdef NFS_DEBUG
240	if (debug)
241		printf("nfs_getrootfh: %s\n", path);
242#endif
243
244	args = &sdata.d;
245	repl = &rdata.d;
246
247	bzero(args, sizeof(*args));
248	len = strlen(path);
249	if (len > sizeof(args->path))
250		len = sizeof(args->path);
251	args->len = htonl(len);
252	bcopy(path, args->path, len);
253	len = 4 + roundup(len, 4);
254
255	cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT,
256	    args, len, repl, sizeof(*repl));
257	if (cc == -1) {
258		/* errno was set by rpc_call */
259		return (errno);
260	}
261	if (cc < 4)
262		return (EBADRPC);
263	if (repl->errno)
264		return (ntohl(repl->errno));
265	bcopy(repl->fh, fhp, sizeof(repl->fh));
266	return (0);
267}
268
269/*
270 * Lookup a file.  Store handle and attributes.
271 * Return zero or error number.
272 */
273int
274nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd)
275{
276	int len, rlen;
277	struct args {
278		u_char	fh[NFS_FHSIZE];
279		n_long	len;
280		char	name[FNAME_SIZE];
281	} *args;
282	struct repl {
283		n_long	errno;
284		u_char	fh[NFS_FHSIZE];
285		struct	nfsv2_fattrs fa;
286	} *repl;
287	struct {
288		n_long	h[RPC_HEADER_WORDS];
289		struct args d;
290	} sdata;
291	struct {
292		n_long	h[RPC_HEADER_WORDS];
293		struct repl d;
294	} rdata;
295	ssize_t cc;
296
297#ifdef NFS_DEBUG
298	if (debug)
299		printf("lookupfh: called\n");
300#endif
301
302	args = &sdata.d;
303	repl = &rdata.d;
304
305	bzero(args, sizeof(*args));
306	bcopy(d->fh, args->fh, sizeof(args->fh));
307	len = strlen(name);
308	if (len > sizeof(args->name))
309		len = sizeof(args->name);
310	bcopy(name, args->name, len);
311	args->len = htonl(len);
312	len = 4 + roundup(len, 4);
313	len += NFS_FHSIZE;
314
315	rlen = sizeof(*repl);
316
317	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_LOOKUP,
318	    args, len, repl, rlen);
319	if (cc == -1)
320		return (errno);		/* XXX - from rpc_call */
321	if (cc < 4)
322		return (EIO);
323	if (repl->errno) {
324		/* saerrno.h now matches NFS error numbers. */
325		return (ntohl(repl->errno));
326	}
327	bcopy( repl->fh, &newfd->fh, sizeof(newfd->fh));
328	bcopy(&repl->fa, &newfd->fa, sizeof(newfd->fa));
329	return (0);
330}
331
332#ifndef NFS_NOSYMLINK
333/*
334 * Get the destination of a symbolic link.
335 */
336int
337nfs_readlink(struct nfs_iodesc *d, char *buf)
338{
339	struct {
340		n_long	h[RPC_HEADER_WORDS];
341		u_char fh[NFS_FHSIZE];
342	} sdata;
343	struct {
344		n_long	h[RPC_HEADER_WORDS];
345		struct nfs_readlnk_repl d;
346	} rdata;
347	ssize_t cc;
348
349#ifdef NFS_DEBUG
350	if (debug)
351		printf("readlink: called\n");
352#endif
353
354	bcopy(d->fh, sdata.fh, NFS_FHSIZE);
355	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READLINK,
356		      sdata.fh, NFS_FHSIZE,
357		      &rdata.d, sizeof(rdata.d));
358	if (cc == -1)
359		return (errno);
360
361	if (cc < 4)
362		return (EIO);
363
364	if (rdata.d.errno)
365		return (ntohl(rdata.d.errno));
366
367	rdata.d.len = ntohl(rdata.d.len);
368	if (rdata.d.len > NFS_MAXPATHLEN)
369		return (ENAMETOOLONG);
370
371	bcopy(rdata.d.path, buf, rdata.d.len);
372	buf[rdata.d.len] = 0;
373	return (0);
374}
375#endif
376
377/*
378 * Read data from a file.
379 * Return transfer count or -1 (and set errno)
380 */
381ssize_t
382nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
383{
384	struct nfs_read_args *args;
385	struct nfs_read_repl *repl;
386	struct {
387		n_long	h[RPC_HEADER_WORDS];
388		struct nfs_read_args d;
389	} sdata;
390	struct {
391		n_long	h[RPC_HEADER_WORDS];
392		struct nfs_read_repl d;
393	} rdata;
394	size_t cc;
395	long x;
396	int hlen, rlen;
397
398	args = &sdata.d;
399	repl = &rdata.d;
400
401	bcopy(d->fh, args->fh, NFS_FHSIZE);
402	args->off = htonl((n_long)off);
403	if (len > NFSREAD_SIZE)
404		len = NFSREAD_SIZE;
405	args->len = htonl((n_long)len);
406	args->xxx = htonl((n_long)0);
407	hlen = sizeof(*repl) - NFSREAD_SIZE;
408
409	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READ,
410	    args, sizeof(*args),
411	    repl, sizeof(*repl));
412	if (cc == -1) {
413		/* errno was already set by rpc_call */
414		return (-1);
415	}
416	if (cc < hlen) {
417		errno = EBADRPC;
418		return (-1);
419	}
420	if (repl->errno) {
421		errno = ntohl(repl->errno);
422		return (-1);
423	}
424	rlen = cc - hlen;
425	x = ntohl(repl->count);
426	if (rlen < x) {
427		printf("nfsread: short packet, %d < %ld\n", rlen, x);
428		errno = EBADRPC;
429		return(-1);
430	}
431	bcopy(repl->data, addr, x);
432	return (x);
433}
434
435/*
436 * Open a file.
437 * return zero or error number
438 */
439int
440nfs_open(const char *upath, struct open_file *f)
441{
442	struct iodesc *desc;
443	struct nfs_iodesc *currfd;
444	char buf[2 * NFS_FHSIZE + 3];
445	u_char *fh;
446	char *cp;
447	int i;
448#ifndef NFS_NOSYMLINK
449	struct nfs_iodesc *newfd;
450	struct nfsv2_fattrs *fa;
451	char *ncp;
452	int c;
453	char namebuf[NFS_MAXPATHLEN + 1];
454	char linkbuf[NFS_MAXPATHLEN + 1];
455	int nlinks = 0;
456#endif
457	int error;
458	char *path;
459
460#ifdef NFS_DEBUG
461 	if (debug)
462 	    printf("nfs_open: %s (rootpath=%s)\n", upath, rootpath);
463#endif
464	if (!rootpath[0]) {
465		printf("no rootpath, no nfs\n");
466		return (ENXIO);
467	}
468
469	/*
470	 * This is silly - we should look at dv_type but that value is
471	 * arch dependant and we can't use it here.
472	 */
473#ifndef __i386__
474	if (strcmp(f->f_dev->dv_name, "net") != 0)
475		return(EINVAL);
476#else
477	if (strcmp(f->f_dev->dv_name, "pxe") != 0)
478		return(EINVAL);
479#endif
480
481	if (!(desc = socktodesc(*(int *)(f->f_devdata))))
482		return(EINVAL);
483
484	/* Bind to a reserved port. */
485	desc->myport = htons(--rpc_port);
486	desc->destip = rootip;
487	if ((error = nfs_getrootfh(desc, rootpath, nfs_root_node.fh)))
488		return (error);
489	nfs_root_node.fa.fa_type  = htonl(NFDIR);
490	nfs_root_node.fa.fa_mode  = htonl(0755);
491	nfs_root_node.fa.fa_nlink = htonl(2);
492	nfs_root_node.iodesc = desc;
493
494	fh = &nfs_root_node.fh[0];
495	buf[0] = 'X';
496	cp = &buf[1];
497	for (i = 0; i < NFS_FHSIZE; i++, cp += 2)
498		sprintf(cp, "%02x", fh[i]);
499	sprintf(cp, "X");
500	setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
501	setenv("boot.nfsroot.path", rootpath, 1);
502	setenv("boot.nfsroot.nfshandle", buf, 1);
503
504	/* Allocate file system specific data structure */
505	currfd = malloc(sizeof(*newfd));
506	if (currfd == NULL) {
507		error = ENOMEM;
508		goto out;
509	}
510
511#ifndef NFS_NOSYMLINK
512	bcopy(&nfs_root_node, currfd, sizeof(*currfd));
513	newfd = 0;
514
515	cp = path = strdup(upath);
516	if (path == NULL) {
517	    error = ENOMEM;
518	    goto out;
519	}
520	while (*cp) {
521		/*
522		 * Remove extra separators
523		 */
524		while (*cp == '/')
525			cp++;
526
527		if (*cp == '\0')
528			break;
529		/*
530		 * Check that current node is a directory.
531		 */
532		if (currfd->fa.fa_type != htonl(NFDIR)) {
533			error = ENOTDIR;
534			goto out;
535		}
536
537		/* allocate file system specific data structure */
538		newfd = malloc(sizeof(*newfd));
539		newfd->iodesc = currfd->iodesc;
540
541		/*
542		 * Get next component of path name.
543		 */
544		{
545			int len = 0;
546
547			ncp = cp;
548			while ((c = *cp) != '\0' && c != '/') {
549				if (++len > NFS_MAXNAMLEN) {
550					error = ENOENT;
551					goto out;
552				}
553				cp++;
554			}
555			*cp = '\0';
556		}
557
558		/* lookup a file handle */
559		error = nfs_lookupfh(currfd, ncp, newfd);
560		*cp = c;
561		if (error)
562			goto out;
563
564		/*
565		 * Check for symbolic link
566		 */
567		if (newfd->fa.fa_type == htonl(NFLNK)) {
568			int link_len, len;
569
570			error = nfs_readlink(newfd, linkbuf);
571			if (error)
572				goto out;
573
574			link_len = strlen(linkbuf);
575			len = strlen(cp);
576
577			if (link_len + len > MAXPATHLEN
578			    || ++nlinks > MAXSYMLINKS) {
579				error = ENOENT;
580				goto out;
581			}
582
583			bcopy(cp, &namebuf[link_len], len + 1);
584			bcopy(linkbuf, namebuf, link_len);
585
586			/*
587			 * If absolute pathname, restart at root.
588			 * If relative pathname, restart at parent directory.
589			 */
590			cp = namebuf;
591			if (*cp == '/')
592				bcopy(&nfs_root_node, currfd, sizeof(*currfd));
593
594			free(newfd);
595			newfd = 0;
596
597			continue;
598		}
599
600		free(currfd);
601		currfd = newfd;
602		newfd = 0;
603	}
604
605	error = 0;
606
607out:
608	if (newfd)
609		free(newfd);
610	if (path)
611		free(path);
612#else
613        currfd->iodesc = desc;
614
615        error = nfs_lookupfh(&nfs_root_node, upath, currfd);
616#endif
617	if (!error) {
618		currfd->off = 0;
619		f->f_fsdata = (void *)currfd;
620		return (0);
621	}
622
623#ifdef NFS_DEBUG
624	if (debug)
625		printf("nfs_open: %s lookupfh failed: %s\n",
626		    path, strerror(error));
627#endif
628	free(currfd);
629
630	return (error);
631}
632
633int
634nfs_close(struct open_file *f)
635{
636	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
637
638#ifdef NFS_DEBUG
639	if (debug)
640		printf("nfs_close: fp=0x%lx\n", (u_long)fp);
641#endif
642
643	if (fp)
644		free(fp);
645	f->f_fsdata = (void *)0;
646
647	return (0);
648}
649
650/*
651 * read a portion of a file
652 */
653int
654nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
655{
656	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
657	ssize_t cc;
658	char *addr = buf;
659
660#ifdef NFS_DEBUG
661	if (debug)
662		printf("nfs_read: size=%lu off=%d\n", (u_long)size,
663		       (int)fp->off);
664#endif
665	while ((int)size > 0) {
666		twiddle();
667		cc = nfs_readdata(fp, fp->off, (void *)addr, size);
668		/* XXX maybe should retry on certain errors */
669		if (cc == -1) {
670#ifdef NFS_DEBUG
671			if (debug)
672				printf("nfs_read: read: %s", strerror(errno));
673#endif
674			return (errno);	/* XXX - from nfs_readdata */
675		}
676		if (cc == 0) {
677#ifdef NFS_DEBUG
678			if (debug)
679				printf("nfs_read: hit EOF unexpectantly");
680#endif
681			goto ret;
682		}
683		fp->off += cc;
684		addr += cc;
685		size -= cc;
686	}
687ret:
688	if (resid)
689		*resid = size;
690
691	return (0);
692}
693
694/*
695 * Not implemented.
696 */
697int
698nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
699{
700	return (EROFS);
701}
702
703off_t
704nfs_seek(struct open_file *f, off_t offset, int where)
705{
706	struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
707	n_long size = ntohl(d->fa.fa_size);
708
709	switch (where) {
710	case SEEK_SET:
711		d->off = offset;
712		break;
713	case SEEK_CUR:
714		d->off += offset;
715		break;
716	case SEEK_END:
717		d->off = size - offset;
718		break;
719	default:
720		errno = EINVAL;
721		return (-1);
722	}
723
724	return (d->off);
725}
726
727/* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 */
728int nfs_stat_types[8] = {
729	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 0 };
730
731int
732nfs_stat(struct open_file *f, struct stat *sb)
733{
734	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
735	n_long ftype, mode;
736
737	ftype = ntohl(fp->fa.fa_type);
738	mode  = ntohl(fp->fa.fa_mode);
739	mode |= nfs_stat_types[ftype & 7];
740
741	sb->st_mode  = mode;
742	sb->st_nlink = ntohl(fp->fa.fa_nlink);
743	sb->st_uid   = ntohl(fp->fa.fa_uid);
744	sb->st_gid   = ntohl(fp->fa.fa_gid);
745	sb->st_size  = ntohl(fp->fa.fa_size);
746
747	return (0);
748}
749
750static int
751nfs_readdir(struct open_file *f, struct dirent *d)
752{
753	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
754	struct nfs_readdir_args *args;
755	struct nfs_readdir_data *rd;
756	struct nfs_readdir_off  *roff = NULL;
757	static char *buf;
758	static n_long cookie = 0;
759	size_t cc;
760	n_long eof;
761
762	struct {
763		n_long h[RPC_HEADER_WORDS];
764		struct nfs_readdir_args d;
765	} sdata;
766	static struct {
767		n_long h[RPC_HEADER_WORDS];
768		u_char d[NFS_READDIRSIZE];
769	} rdata;
770
771	if (cookie == 0) {
772	refill:
773		args = &sdata.d;
774		bzero(args, sizeof(*args));
775
776		bcopy(fp->fh, args->fh, NFS_FHSIZE);
777		args->cookie = htonl(cookie);
778		args->count  = htonl(NFS_READDIRSIZE);
779
780		cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READDIR,
781			      args, sizeof(*args),
782			      rdata.d, sizeof(rdata.d));
783		buf  = rdata.d;
784		roff = (struct nfs_readdir_off *)buf;
785		if (ntohl(roff->cookie) != 0)
786			return EIO;
787	}
788	roff = (struct nfs_readdir_off *)buf;
789
790	if (ntohl(roff->follows) == 0) {
791		eof = ntohl((roff+1)->cookie);
792		if (eof) {
793			cookie = 0;
794			return ENOENT;
795		}
796		goto refill;
797	}
798
799	buf += sizeof(struct nfs_readdir_off);
800	rd = (struct nfs_readdir_data *)buf;
801	d->d_namlen = ntohl(rd->len);
802	bcopy(rd->name, d->d_name, d->d_namlen);
803	d->d_name[d->d_namlen] = '\0';
804
805	buf += (sizeof(struct nfs_readdir_data) + roundup(htonl(rd->len),4));
806	roff = (struct nfs_readdir_off *)buf;
807	cookie = ntohl(roff->cookie);
808	return 0;
809}
810#else	/* !OLD_NFSV2 */
811/*
812 * Fetch the root file handle (call mount daemon)
813 * Return zero or error number.
814 */
815int
816nfs_getrootfh(struct iodesc *d, char *path, uint32_t *fhlenp, u_char *fhp)
817{
818	int len;
819	struct args {
820		uint32_t len;
821		char path[FNAME_SIZE];
822	} *args;
823	struct repl {
824		uint32_t errno;
825		uint32_t fhsize;
826		u_char fh[NFS_V3MAXFHSIZE];
827		uint32_t authcnt;
828		uint32_t auth[7];
829	} *repl;
830	struct {
831		uint32_t h[RPC_HEADER_WORDS];
832		struct args d;
833	} sdata;
834	struct {
835		uint32_t h[RPC_HEADER_WORDS];
836		struct repl d;
837	} rdata;
838	size_t cc;
839
840#ifdef NFS_DEBUG
841	if (debug)
842		printf("nfs_getrootfh: %s\n", path);
843#endif
844
845	args = &sdata.d;
846	repl = &rdata.d;
847
848	bzero(args, sizeof(*args));
849	len = strlen(path);
850	if (len > sizeof(args->path))
851		len = sizeof(args->path);
852	args->len = htonl(len);
853	bcopy(path, args->path, len);
854	len = sizeof(uint32_t) + roundup(len, sizeof(uint32_t));
855
856	cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER3, RPCMNT_MOUNT,
857	    args, len, repl, sizeof(*repl));
858	if (cc == -1)
859		/* errno was set by rpc_call */
860		return (errno);
861	if (cc < 2 * sizeof (uint32_t))
862		return (EBADRPC);
863	if (repl->errno != 0)
864		return (ntohl(repl->errno));
865	*fhlenp = ntohl(repl->fhsize);
866	bcopy(repl->fh, fhp, *fhlenp);
867	return (0);
868}
869
870/*
871 * Lookup a file.  Store handle and attributes.
872 * Return zero or error number.
873 */
874int
875nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd)
876{
877	int len, rlen, pos;
878	struct args {
879		uint32_t fhsize;
880		uint32_t fhplusname[1 +
881		    (NFS_V3MAXFHSIZE + FNAME_SIZE) / sizeof(uint32_t)];
882	} *args;
883	struct repl {
884		uint32_t errno;
885		uint32_t fhsize;
886		uint32_t fhplusattr[(NFS_V3MAXFHSIZE +
887		    2 * (sizeof(uint32_t) +
888		    sizeof(struct nfsv3_fattrs))) / sizeof(uint32_t)];
889	} *repl;
890	struct {
891		uint32_t h[RPC_HEADER_WORDS];
892		struct args d;
893	} sdata;
894	struct {
895		uint32_t h[RPC_HEADER_WORDS];
896		struct repl d;
897	} rdata;
898	ssize_t cc;
899
900#ifdef NFS_DEBUG
901	if (debug)
902		printf("lookupfh: called\n");
903#endif
904
905	args = &sdata.d;
906	repl = &rdata.d;
907
908	bzero(args, sizeof(*args));
909	args->fhsize = htonl(d->fhsize);
910	bcopy(d->fh, args->fhplusname, d->fhsize);
911	len = strlen(name);
912	if (len > FNAME_SIZE)
913		len = FNAME_SIZE;
914	pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
915	args->fhplusname[pos++] = htonl(len);
916	bcopy(name, &args->fhplusname[pos], len);
917	len = sizeof(uint32_t) + pos * sizeof(uint32_t) +
918	    roundup(len, sizeof(uint32_t));
919
920	rlen = sizeof(*repl);
921
922	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_LOOKUP,
923	    args, len, repl, rlen);
924	if (cc == -1)
925		return (errno);		/* XXX - from rpc_call */
926	if (cc < 2 * sizeof(uint32_t))
927		return (EIO);
928	if (repl->errno != 0)
929		/* saerrno.h now matches NFS error numbers. */
930		return (ntohl(repl->errno));
931	newfd->fhsize = ntohl(repl->fhsize);
932	bcopy(repl->fhplusattr, &newfd->fh, newfd->fhsize);
933	pos = roundup(newfd->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
934	if (repl->fhplusattr[pos++] == 0)
935		return (EIO);
936	bcopy(&repl->fhplusattr[pos], &newfd->fa, sizeof(newfd->fa));
937	return (0);
938}
939
940#ifndef NFS_NOSYMLINK
941/*
942 * Get the destination of a symbolic link.
943 */
944int
945nfs_readlink(struct nfs_iodesc *d, char *buf)
946{
947	struct args {
948		uint32_t fhsize;
949		u_char fh[NFS_V3MAXFHSIZE];
950	} *args;
951	struct repl {
952		uint32_t errno;
953		uint32_t ok;
954		struct nfsv3_fattrs fa;
955		uint32_t len;
956		u_char path[NFS_MAXPATHLEN];
957	} *repl;
958	struct {
959		uint32_t h[RPC_HEADER_WORDS];
960		struct args d;
961	} sdata;
962	struct {
963		uint32_t h[RPC_HEADER_WORDS];
964		struct repl d;
965	} rdata;
966	ssize_t cc;
967
968#ifdef NFS_DEBUG
969	if (debug)
970		printf("readlink: called\n");
971#endif
972
973	args = &sdata.d;
974	repl = &rdata.d;
975
976	bzero(args, sizeof(*args));
977	args->fhsize = htonl(d->fhsize);
978	bcopy(d->fh, args->fh, d->fhsize);
979	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READLINK,
980	    args, sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)),
981	    repl, sizeof(*repl));
982	if (cc == -1)
983		return (errno);
984
985	if (cc < 2 * sizeof(uint32_t))
986		return (EIO);
987
988	if (repl->errno != 0)
989		return (ntohl(repl->errno));
990
991	if (repl->ok == 0)
992		return (EIO);
993
994	repl->len = ntohl(repl->len);
995	if (repl->len > NFS_MAXPATHLEN)
996		return (ENAMETOOLONG);
997
998	bcopy(repl->path, buf, repl->len);
999	buf[repl->len] = 0;
1000	return (0);
1001}
1002#endif
1003
1004/*
1005 * Read data from a file.
1006 * Return transfer count or -1 (and set errno)
1007 */
1008ssize_t
1009nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
1010{
1011	struct args {
1012		uint32_t fhsize;
1013		uint32_t fhoffcnt[NFS_V3MAXFHSIZE / sizeof(uint32_t) + 3];
1014	} *args;
1015	struct repl {
1016		uint32_t errno;
1017		uint32_t ok;
1018		struct nfsv3_fattrs fa;
1019		uint32_t count;
1020		uint32_t eof;
1021		uint32_t len;
1022		u_char data[NFSREAD_SIZE];
1023	} *repl;
1024	struct {
1025		uint32_t h[RPC_HEADER_WORDS];
1026		struct args d;
1027	} sdata;
1028	struct {
1029		uint32_t h[RPC_HEADER_WORDS];
1030		struct repl d;
1031	} rdata;
1032	size_t cc;
1033	long x;
1034	int hlen, rlen, pos;
1035
1036	args = &sdata.d;
1037	repl = &rdata.d;
1038
1039	bzero(args, sizeof(*args));
1040	args->fhsize = htonl(d->fhsize);
1041	bcopy(d->fh, args->fhoffcnt, d->fhsize);
1042	pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
1043	args->fhoffcnt[pos++] = 0;
1044	args->fhoffcnt[pos++] = htonl((uint32_t)off);
1045	if (len > NFSREAD_SIZE)
1046		len = NFSREAD_SIZE;
1047	args->fhoffcnt[pos] = htonl((uint32_t)len);
1048	hlen = sizeof(*repl) - NFSREAD_SIZE;
1049
1050	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READ,
1051	    args, 4 * sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)),
1052	    repl, sizeof(*repl));
1053	if (cc == -1)
1054		/* errno was already set by rpc_call */
1055		return (-1);
1056	if (cc < hlen) {
1057		errno = EBADRPC;
1058		return (-1);
1059	}
1060	if (repl->errno != 0) {
1061		errno = ntohl(repl->errno);
1062		return (-1);
1063	}
1064	rlen = cc - hlen;
1065	x = ntohl(repl->count);
1066	if (rlen < x) {
1067		printf("nfsread: short packet, %d < %ld\n", rlen, x);
1068		errno = EBADRPC;
1069		return (-1);
1070	}
1071	bcopy(repl->data, addr, x);
1072	return (x);
1073}
1074
1075/*
1076 * Open a file.
1077 * return zero or error number
1078 */
1079int
1080nfs_open(const char *upath, struct open_file *f)
1081{
1082	struct iodesc *desc;
1083	struct nfs_iodesc *currfd;
1084	char buf[2 * NFS_V3MAXFHSIZE + 3];
1085	u_char *fh;
1086	char *cp;
1087	int i;
1088#ifndef NFS_NOSYMLINK
1089	struct nfs_iodesc *newfd;
1090	struct nfsv3_fattrs *fa;
1091	char *ncp;
1092	int c;
1093	char namebuf[NFS_MAXPATHLEN + 1];
1094	char linkbuf[NFS_MAXPATHLEN + 1];
1095	int nlinks = 0;
1096#endif
1097	int error;
1098	char *path;
1099
1100#ifdef NFS_DEBUG
1101 	if (debug)
1102 	    printf("nfs_open: %s (rootpath=%s)\n", upath, rootpath);
1103#endif
1104	if (!rootpath[0]) {
1105		printf("no rootpath, no nfs\n");
1106		return (ENXIO);
1107	}
1108
1109	/*
1110	 * This is silly - we should look at dv_type but that value is
1111	 * arch dependant and we can't use it here.
1112	 */
1113#ifndef __i386__
1114	if (strcmp(f->f_dev->dv_name, "net") != 0)
1115		return (EINVAL);
1116#else
1117	if (strcmp(f->f_dev->dv_name, "pxe") != 0)
1118		return (EINVAL);
1119#endif
1120
1121	if (!(desc = socktodesc(*(int *)(f->f_devdata))))
1122		return (EINVAL);
1123
1124	/* Bind to a reserved port. */
1125	desc->myport = htons(--rpc_port);
1126	desc->destip = rootip;
1127	if ((error = nfs_getrootfh(desc, rootpath, &nfs_root_node.fhsize,
1128	    nfs_root_node.fh)))
1129		return (error);
1130	nfs_root_node.fa.fa_type  = htonl(NFDIR);
1131	nfs_root_node.fa.fa_mode  = htonl(0755);
1132	nfs_root_node.fa.fa_nlink = htonl(2);
1133	nfs_root_node.iodesc = desc;
1134
1135	fh = &nfs_root_node.fh[0];
1136	buf[0] = 'X';
1137	cp = &buf[1];
1138	for (i = 0; i < nfs_root_node.fhsize; i++, cp += 2)
1139		sprintf(cp, "%02x", fh[i]);
1140	sprintf(cp, "X");
1141	setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
1142	setenv("boot.nfsroot.path", rootpath, 1);
1143	setenv("boot.nfsroot.nfshandle", buf, 1);
1144	sprintf(buf, "%d", nfs_root_node.fhsize);
1145	setenv("boot.nfsroot.nfshandlelen", buf, 1);
1146
1147	/* Allocate file system specific data structure */
1148	currfd = malloc(sizeof(*newfd));
1149	if (currfd == NULL) {
1150		error = ENOMEM;
1151		goto out;
1152	}
1153#ifndef NFS_NOSYMLINK
1154	bcopy(&nfs_root_node, currfd, sizeof(*currfd));
1155	newfd = 0;
1156
1157	cp = path = strdup(upath);
1158	if (path == NULL) {
1159		error = ENOMEM;
1160		goto out;
1161	}
1162	while (*cp) {
1163		/*
1164		 * Remove extra separators
1165		 */
1166		while (*cp == '/')
1167			cp++;
1168
1169		if (*cp == '\0')
1170			break;
1171		/*
1172		 * Check that current node is a directory.
1173		 */
1174		if (currfd->fa.fa_type != htonl(NFDIR)) {
1175			error = ENOTDIR;
1176			goto out;
1177		}
1178
1179		/* allocate file system specific data structure */
1180		newfd = malloc(sizeof(*newfd));
1181		if (newfd == NULL) {
1182			error = ENOMEM;
1183			goto out;
1184		}
1185		newfd->iodesc = currfd->iodesc;
1186
1187		/*
1188		 * Get next component of path name.
1189		 */
1190		{
1191			int len = 0;
1192
1193			ncp = cp;
1194			while ((c = *cp) != '\0' && c != '/') {
1195				if (++len > NFS_MAXNAMLEN) {
1196					error = ENOENT;
1197					goto out;
1198				}
1199				cp++;
1200			}
1201			*cp = '\0';
1202		}
1203
1204		/* lookup a file handle */
1205		error = nfs_lookupfh(currfd, ncp, newfd);
1206		*cp = c;
1207		if (error)
1208			goto out;
1209
1210		/*
1211		 * Check for symbolic link
1212		 */
1213		if (newfd->fa.fa_type == htonl(NFLNK)) {
1214			int link_len, len;
1215
1216			error = nfs_readlink(newfd, linkbuf);
1217			if (error)
1218				goto out;
1219
1220			link_len = strlen(linkbuf);
1221			len = strlen(cp);
1222
1223			if (link_len + len > MAXPATHLEN
1224			    || ++nlinks > MAXSYMLINKS) {
1225				error = ENOENT;
1226				goto out;
1227			}
1228
1229			bcopy(cp, &namebuf[link_len], len + 1);
1230			bcopy(linkbuf, namebuf, link_len);
1231
1232			/*
1233			 * If absolute pathname, restart at root.
1234			 * If relative pathname, restart at parent directory.
1235			 */
1236			cp = namebuf;
1237			if (*cp == '/')
1238				bcopy(&nfs_root_node, currfd, sizeof(*currfd));
1239
1240			free(newfd);
1241			newfd = 0;
1242
1243			continue;
1244		}
1245
1246		free(currfd);
1247		currfd = newfd;
1248		newfd = 0;
1249	}
1250
1251	error = 0;
1252
1253out:
1254	free(newfd);
1255	free(path);
1256#else
1257	currfd->iodesc = desc;
1258
1259	error = nfs_lookupfh(&nfs_root_node, upath, currfd);
1260#endif
1261	if (!error) {
1262		currfd->off = 0;
1263		f->f_fsdata = (void *)currfd;
1264		return (0);
1265	}
1266
1267#ifdef NFS_DEBUG
1268	if (debug)
1269		printf("nfs_open: %s lookupfh failed: %s\n",
1270		    path, strerror(error));
1271#endif
1272	free(currfd);
1273
1274	return (error);
1275}
1276
1277int
1278nfs_close(struct open_file *f)
1279{
1280	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
1281
1282#ifdef NFS_DEBUG
1283	if (debug)
1284		printf("nfs_close: fp=0x%lx\n", (u_long)fp);
1285#endif
1286
1287	if (fp)
1288		free(fp);
1289	f->f_fsdata = (void *)0;
1290
1291	return (0);
1292}
1293
1294/*
1295 * read a portion of a file
1296 */
1297int
1298nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
1299{
1300	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
1301	ssize_t cc;
1302	char *addr = buf;
1303
1304#ifdef NFS_DEBUG
1305	if (debug)
1306		printf("nfs_read: size=%lu off=%d\n", (u_long)size,
1307		       (int)fp->off);
1308#endif
1309	while ((int)size > 0) {
1310		twiddle();
1311		cc = nfs_readdata(fp, fp->off, (void *)addr, size);
1312		/* XXX maybe should retry on certain errors */
1313		if (cc == -1) {
1314#ifdef NFS_DEBUG
1315			if (debug)
1316				printf("nfs_read: read: %s", strerror(errno));
1317#endif
1318			return (errno);	/* XXX - from nfs_readdata */
1319		}
1320		if (cc == 0) {
1321#ifdef NFS_DEBUG
1322			if (debug)
1323				printf("nfs_read: hit EOF unexpectantly");
1324#endif
1325			goto ret;
1326		}
1327		fp->off += cc;
1328		addr += cc;
1329		size -= cc;
1330	}
1331ret:
1332	if (resid)
1333		*resid = size;
1334
1335	return (0);
1336}
1337
1338/*
1339 * Not implemented.
1340 */
1341int
1342nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
1343{
1344	return (EROFS);
1345}
1346
1347off_t
1348nfs_seek(struct open_file *f, off_t offset, int where)
1349{
1350	struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
1351	uint32_t size = ntohl(d->fa.fa_size.val[1]);
1352
1353	switch (where) {
1354	case SEEK_SET:
1355		d->off = offset;
1356		break;
1357	case SEEK_CUR:
1358		d->off += offset;
1359		break;
1360	case SEEK_END:
1361		d->off = size - offset;
1362		break;
1363	default:
1364		errno = EINVAL;
1365		return (-1);
1366	}
1367
1368	return (d->off);
1369}
1370
1371/* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5, NFSOCK=6, NFFIFO=7 */
1372int nfs_stat_types[9] = {
1373	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO, 0 };
1374
1375int
1376nfs_stat(struct open_file *f, struct stat *sb)
1377{
1378	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
1379	uint32_t ftype, mode;
1380
1381	ftype = ntohl(fp->fa.fa_type);
1382	mode  = ntohl(fp->fa.fa_mode);
1383	mode |= nfs_stat_types[ftype & 7];
1384
1385	sb->st_mode  = mode;
1386	sb->st_nlink = ntohl(fp->fa.fa_nlink);
1387	sb->st_uid   = ntohl(fp->fa.fa_uid);
1388	sb->st_gid   = ntohl(fp->fa.fa_gid);
1389	sb->st_size  = ntohl(fp->fa.fa_size.val[1]);
1390
1391	return (0);
1392}
1393
1394static int
1395nfs_readdir(struct open_file *f, struct dirent *d)
1396{
1397	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
1398	struct nfsv3_readdir_repl *repl;
1399	struct nfsv3_readdir_entry *rent;
1400	static char *buf;
1401	static uint32_t cookie0 = 0;
1402	static uint32_t cookie1 = 0;
1403	size_t cc;
1404	static uint32_t cookieverf0 = 0;
1405	static uint32_t cookieverf1 = 0;
1406	int pos;
1407
1408	struct args {
1409		uint32_t fhsize;
1410		uint32_t fhpluscookie[5 + NFS_V3MAXFHSIZE];
1411	} *args;
1412	struct {
1413		uint32_t h[RPC_HEADER_WORDS];
1414		struct args d;
1415	} sdata;
1416	static struct {
1417		uint32_t h[RPC_HEADER_WORDS];
1418		u_char d[NFS_READDIRSIZE];
1419	} rdata;
1420
1421	if (cookie0 == 0 && cookie1 == 0) {
1422	refill:
1423		args = &sdata.d;
1424		bzero(args, sizeof(*args));
1425
1426		args->fhsize = htonl(fp->fhsize);
1427		bcopy(fp->fh, args->fhpluscookie, fp->fhsize);
1428		pos = roundup(fp->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
1429		args->fhpluscookie[pos++] = cookie0;
1430		args->fhpluscookie[pos++] = cookie1;
1431		args->fhpluscookie[pos++] = cookieverf0;
1432		args->fhpluscookie[pos++] = cookieverf1;
1433		args->fhpluscookie[pos] = htonl(NFS_READDIRSIZE);
1434
1435		cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READDIR,
1436		    args, 6 * sizeof(uint32_t) +
1437		    roundup(fp->fhsize, sizeof(uint32_t)),
1438		    rdata.d, sizeof(rdata.d));
1439		buf  = rdata.d;
1440		repl = (struct nfsv3_readdir_repl *)buf;
1441		if (repl->errno != 0)
1442			return (ntohl(repl->errno));
1443		cookieverf0 = repl->cookiev0;
1444		cookieverf1 = repl->cookiev1;
1445		buf += sizeof (struct nfsv3_readdir_repl);
1446	}
1447	rent = (struct nfsv3_readdir_entry *)buf;
1448
1449	if (rent->follows == 0) {
1450		/* fid0 is actually eof */
1451		if (rent->fid0 != 0) {
1452			cookie0 = 0;
1453			cookie1 = 0;
1454			cookieverf0 = 0;
1455			cookieverf1 = 0;
1456			return (ENOENT);
1457		}
1458		goto refill;
1459	}
1460
1461	d->d_namlen = ntohl(rent->len);
1462	bcopy(rent->nameplus, d->d_name, d->d_namlen);
1463	d->d_name[d->d_namlen] = '\0';
1464
1465	pos = roundup(d->d_namlen, sizeof(uint32_t)) / sizeof(uint32_t);
1466	cookie0 = rent->nameplus[pos++];
1467	cookie1 = rent->nameplus[pos++];
1468	buf = (u_char *)&rent->nameplus[pos];
1469	return (0);
1470}
1471#endif	/* OLD_NFSV2 */
1472