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