138451Smsmith/*	$NetBSD: nfs.c,v 1.2 1998/01/24 12:43:09 drochner Exp $	*/
238451Smsmith
338451Smsmith/*-
438451Smsmith *  Copyright (c) 1993 John Brezak
538451Smsmith *  All rights reserved.
6197178Semaste *
738451Smsmith *  Redistribution and use in source and binary forms, with or without
838451Smsmith *  modification, are permitted provided that the following conditions
938451Smsmith *  are met:
1038451Smsmith *  1. Redistributions of source code must retain the above copyright
1138451Smsmith *     notice, this list of conditions and the following disclaimer.
1238451Smsmith *  2. Redistributions in binary form must reproduce the above copyright
1338451Smsmith *     notice, this list of conditions and the following disclaimer in the
1438451Smsmith *     documentation and/or other materials provided with the distribution.
1538451Smsmith *  3. The name of the author may not be used to endorse or promote products
1638451Smsmith *     derived from this software without specific prior written permission.
17197178Semaste *
1838451Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
1938451Smsmith * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2038451Smsmith * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2138451Smsmith * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
2238451Smsmith * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2338451Smsmith * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2438451Smsmith * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2538451Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2638451Smsmith * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
2738451Smsmith * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2838451Smsmith * POSSIBILITY OF SUCH DAMAGE.
2938451Smsmith */
3038451Smsmith
3184221Sdillon#include <sys/cdefs.h>
3284221Sdillon__FBSDID("$FreeBSD$");
3384221Sdillon
3438451Smsmith#include <sys/param.h>
3538451Smsmith#include <sys/time.h>
3638451Smsmith#include <sys/socket.h>
3738451Smsmith#include <sys/stat.h>
3838451Smsmith#include <string.h>
3938451Smsmith
4038451Smsmith#include <netinet/in.h>
4138451Smsmith#include <netinet/in_systm.h>
4238451Smsmith
4338451Smsmith#include "rpcv2.h"
4438451Smsmith#include "nfsv2.h"
4538451Smsmith
4638451Smsmith#include "stand.h"
4738451Smsmith#include "net.h"
4838451Smsmith#include "netif.h"
4938451Smsmith#include "rpc.h"
5038451Smsmith
5138451Smsmith#define NFS_DEBUGxx
5238451Smsmith
53212125Srmacklem#define NFSREAD_SIZE 1024
54212125Srmacklem
5538451Smsmith/* Define our own NFS attributes without NQNFS stuff. */
56212125Srmacklem#ifdef OLD_NFSV2
5738451Smsmithstruct nfsv2_fattrs {
5838451Smsmith	n_long	fa_type;
5938451Smsmith	n_long	fa_mode;
6038451Smsmith	n_long	fa_nlink;
6138451Smsmith	n_long	fa_uid;
6238451Smsmith	n_long	fa_gid;
6338451Smsmith	n_long	fa_size;
6438451Smsmith	n_long	fa_blocksize;
6538451Smsmith	n_long	fa_rdev;
6638451Smsmith	n_long	fa_blocks;
6738451Smsmith	n_long	fa_fsid;
6838451Smsmith	n_long	fa_fileid;
6938451Smsmith	struct nfsv2_time fa_atime;
7038451Smsmith	struct nfsv2_time fa_mtime;
7138451Smsmith	struct nfsv2_time fa_ctime;
7238451Smsmith};
7338451Smsmith
7438451Smsmithstruct nfs_read_args {
7538451Smsmith	u_char	fh[NFS_FHSIZE];
7638451Smsmith	n_long	off;
7738451Smsmith	n_long	len;
7838451Smsmith	n_long	xxx;			/* XXX what's this for? */
7938451Smsmith};
8038451Smsmith
8138451Smsmith/* Data part of nfs rpc reply (also the largest thing we receive) */
8238451Smsmithstruct nfs_read_repl {
8338451Smsmith	n_long	errno;
8438451Smsmith	struct	nfsv2_fattrs fa;
8538451Smsmith	n_long	count;
8638451Smsmith	u_char	data[NFSREAD_SIZE];
8738451Smsmith};
8838451Smsmith
8938451Smsmith#ifndef NFS_NOSYMLINK
9038451Smsmithstruct nfs_readlnk_repl {
9138451Smsmith	n_long	errno;
9238451Smsmith	n_long	len;
9338451Smsmith	char	path[NFS_MAXPATHLEN];
9438451Smsmith};
9538451Smsmith#endif
9638451Smsmith
9759853Spsstruct nfs_readdir_args {
9859853Sps	u_char	fh[NFS_FHSIZE];
9959853Sps	n_long	cookie;
10059853Sps	n_long	count;
10159853Sps};
10259853Sps
10359853Spsstruct nfs_readdir_data {
10459853Sps	n_long	fileid;
10559853Sps	n_long	len;
10659853Sps	char	name[0];
10759853Sps};
10859853Sps
10959853Spsstruct nfs_readdir_off {
11059853Sps	n_long	cookie;
11159853Sps	n_long	follows;
11259853Sps};
11359853Sps
11438451Smsmithstruct nfs_iodesc {
11538451Smsmith	struct	iodesc	*iodesc;
11638451Smsmith	off_t	off;
11738451Smsmith	u_char	fh[NFS_FHSIZE];
11838451Smsmith	struct nfsv2_fattrs fa;	/* all in network order */
11938451Smsmith};
120212125Srmacklem#else	/* !OLD_NFSV2 */
12138451Smsmith
122212125Srmacklem/* NFSv3 definitions */
123212125Srmacklem#define	NFS_V3MAXFHSIZE		64
124212125Srmacklem#define	NFS_VER3		3
125212125Srmacklem#define	RPCMNT_VER3		3
126212125Srmacklem#define	NFSPROCV3_LOOKUP	3
127212125Srmacklem#define	NFSPROCV3_READLINK	5
128212125Srmacklem#define	NFSPROCV3_READ		6
129212125Srmacklem#define	NFSPROCV3_READDIR	16
130212125Srmacklem
131212125Srmacklemtypedef struct {
132212125Srmacklem	uint32_t val[2];
133212125Srmacklem} n_quad;
134212125Srmacklem
135212125Srmacklemstruct nfsv3_time {
136212125Srmacklem	uint32_t nfs_sec;
137212125Srmacklem	uint32_t nfs_nsec;
138212125Srmacklem};
139212125Srmacklem
140212125Srmacklemstruct nfsv3_fattrs {
141212125Srmacklem	uint32_t fa_type;
142212125Srmacklem	uint32_t fa_mode;
143212125Srmacklem	uint32_t fa_nlink;
144212125Srmacklem	uint32_t fa_uid;
145212125Srmacklem	uint32_t fa_gid;
146212125Srmacklem	n_quad fa_size;
147212125Srmacklem	n_quad fa_used;
148212125Srmacklem	n_quad fa_rdev;
149212125Srmacklem	n_quad fa_fsid;
150212125Srmacklem	n_quad fa_fileid;
151212125Srmacklem	struct nfsv3_time fa_atime;
152212125Srmacklem	struct nfsv3_time fa_mtime;
153212125Srmacklem	struct nfsv3_time fa_ctime;
154212125Srmacklem};
155212125Srmacklem
15638451Smsmith/*
157212125Srmacklem * For NFSv3, the file handle is variable in size, so most fixed sized
158212125Srmacklem * structures for arguments won't work. For most cases, a structure
159212125Srmacklem * that starts with any fixed size section is followed by an array
160212125Srmacklem * that covers the maximum size required.
161212125Srmacklem */
162212125Srmacklemstruct nfsv3_readdir_repl {
163212125Srmacklem	uint32_t errno;
164212125Srmacklem	uint32_t ok;
165212125Srmacklem	struct nfsv3_fattrs fa;
166212125Srmacklem	uint32_t cookiev0;
167212125Srmacklem	uint32_t cookiev1;
168212125Srmacklem};
169212125Srmacklem
170212125Srmacklemstruct nfsv3_readdir_entry {
171212125Srmacklem	uint32_t follows;
172212125Srmacklem	uint32_t fid0;
173212125Srmacklem	uint32_t fid1;
174212125Srmacklem	uint32_t len;
175212125Srmacklem	uint32_t nameplus[0];
176212125Srmacklem};
177212125Srmacklem
178212125Srmacklemstruct nfs_iodesc {
179212125Srmacklem	struct iodesc *iodesc;
180212125Srmacklem	off_t off;
181212125Srmacklem	uint32_t fhsize;
182212125Srmacklem	u_char fh[NFS_V3MAXFHSIZE];
183212125Srmacklem	struct nfsv3_fattrs fa;	/* all in network order */
184252701Smav	uint64_t cookie;
185212125Srmacklem};
186212125Srmacklem#endif	/* OLD_NFSV2 */
187212125Srmacklem
188212125Srmacklem/*
18938451Smsmith * XXX interactions with tftp? See nfswrapper.c for a confusing
19038451Smsmith *     issue.
19138451Smsmith */
19239468Smsmithint		nfs_open(const char *path, struct open_file *f);
19338451Smsmithstatic int	nfs_close(struct open_file *f);
19438451Smsmithstatic int	nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
19538451Smsmithstatic int	nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid);
19638451Smsmithstatic off_t	nfs_seek(struct open_file *f, off_t offset, int where);
19738451Smsmithstatic int	nfs_stat(struct open_file *f, struct stat *sb);
19859853Spsstatic int	nfs_readdir(struct open_file *f, struct dirent *d);
19938451Smsmith
20065496Smsmithstruct	nfs_iodesc nfs_root_node;
20159824Sps
20238451Smsmithstruct fs_ops nfs_fsops = {
20359766Sjlemon	"nfs",
20459766Sjlemon	nfs_open,
20559766Sjlemon	nfs_close,
20659766Sjlemon	nfs_read,
20759766Sjlemon	nfs_write,
20859766Sjlemon	nfs_seek,
20959766Sjlemon	nfs_stat,
21059853Sps	nfs_readdir
21138451Smsmith};
21238451Smsmith
213212125Srmacklem#ifdef	OLD_NFSV2
21438451Smsmith/*
21538451Smsmith * Fetch the root file handle (call mount daemon)
21638451Smsmith * Return zero or error number.
21738451Smsmith */
21838451Smsmithint
219197178Semastenfs_getrootfh(struct iodesc *d, char *path, u_char *fhp)
22038451Smsmith{
22192913Sobrien	int len;
22238451Smsmith	struct args {
22338451Smsmith		n_long	len;
22438451Smsmith		char	path[FNAME_SIZE];
22538451Smsmith	} *args;
22638451Smsmith	struct repl {
22738451Smsmith		n_long	errno;
22838451Smsmith		u_char	fh[NFS_FHSIZE];
22938451Smsmith	} *repl;
23038451Smsmith	struct {
23138451Smsmith		n_long	h[RPC_HEADER_WORDS];
23238451Smsmith		struct args d;
23338451Smsmith	} sdata;
23438451Smsmith	struct {
23538451Smsmith		n_long	h[RPC_HEADER_WORDS];
23638451Smsmith		struct repl d;
23738451Smsmith	} rdata;
23838451Smsmith	size_t cc;
239197178Semaste
24038451Smsmith#ifdef NFS_DEBUG
24138451Smsmith	if (debug)
24238451Smsmith		printf("nfs_getrootfh: %s\n", path);
24338451Smsmith#endif
24438451Smsmith
24538451Smsmith	args = &sdata.d;
24638451Smsmith	repl = &rdata.d;
24738451Smsmith
24838451Smsmith	bzero(args, sizeof(*args));
24938451Smsmith	len = strlen(path);
25038451Smsmith	if (len > sizeof(args->path))
25138451Smsmith		len = sizeof(args->path);
25238451Smsmith	args->len = htonl(len);
25338451Smsmith	bcopy(path, args->path, len);
25438451Smsmith	len = 4 + roundup(len, 4);
25538451Smsmith
25638451Smsmith	cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT,
25738451Smsmith	    args, len, repl, sizeof(*repl));
25838451Smsmith	if (cc == -1) {
25938451Smsmith		/* errno was set by rpc_call */
26038451Smsmith		return (errno);
26138451Smsmith	}
26238451Smsmith	if (cc < 4)
26338451Smsmith		return (EBADRPC);
26438451Smsmith	if (repl->errno)
26538451Smsmith		return (ntohl(repl->errno));
26638451Smsmith	bcopy(repl->fh, fhp, sizeof(repl->fh));
26738451Smsmith	return (0);
26838451Smsmith}
26938451Smsmith
27038451Smsmith/*
27138451Smsmith * Lookup a file.  Store handle and attributes.
27238451Smsmith * Return zero or error number.
27338451Smsmith */
27438451Smsmithint
275197178Semastenfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd)
27638451Smsmith{
27792913Sobrien	int len, rlen;
27838451Smsmith	struct args {
27938451Smsmith		u_char	fh[NFS_FHSIZE];
28038451Smsmith		n_long	len;
28138451Smsmith		char	name[FNAME_SIZE];
28238451Smsmith	} *args;
28338451Smsmith	struct repl {
28438451Smsmith		n_long	errno;
28538451Smsmith		u_char	fh[NFS_FHSIZE];
28638451Smsmith		struct	nfsv2_fattrs fa;
28738451Smsmith	} *repl;
28838451Smsmith	struct {
28938451Smsmith		n_long	h[RPC_HEADER_WORDS];
29038451Smsmith		struct args d;
29138451Smsmith	} sdata;
29238451Smsmith	struct {
29338451Smsmith		n_long	h[RPC_HEADER_WORDS];
29438451Smsmith		struct repl d;
29538451Smsmith	} rdata;
29638451Smsmith	ssize_t cc;
297197178Semaste
29838451Smsmith#ifdef NFS_DEBUG
29938451Smsmith	if (debug)
30038451Smsmith		printf("lookupfh: called\n");
30138451Smsmith#endif
30238451Smsmith
30338451Smsmith	args = &sdata.d;
30438451Smsmith	repl = &rdata.d;
30538451Smsmith
30638451Smsmith	bzero(args, sizeof(*args));
30738451Smsmith	bcopy(d->fh, args->fh, sizeof(args->fh));
30838451Smsmith	len = strlen(name);
30938451Smsmith	if (len > sizeof(args->name))
31038451Smsmith		len = sizeof(args->name);
31138451Smsmith	bcopy(name, args->name, len);
31238451Smsmith	args->len = htonl(len);
31338451Smsmith	len = 4 + roundup(len, 4);
31438451Smsmith	len += NFS_FHSIZE;
31538451Smsmith
31638451Smsmith	rlen = sizeof(*repl);
31738451Smsmith
31838451Smsmith	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_LOOKUP,
31938451Smsmith	    args, len, repl, rlen);
32038451Smsmith	if (cc == -1)
32138451Smsmith		return (errno);		/* XXX - from rpc_call */
32238451Smsmith	if (cc < 4)
32338451Smsmith		return (EIO);
32438451Smsmith	if (repl->errno) {
32538451Smsmith		/* saerrno.h now matches NFS error numbers. */
32638451Smsmith		return (ntohl(repl->errno));
32738451Smsmith	}
32838451Smsmith	bcopy( repl->fh, &newfd->fh, sizeof(newfd->fh));
32938451Smsmith	bcopy(&repl->fa, &newfd->fa, sizeof(newfd->fa));
33038451Smsmith	return (0);
33138451Smsmith}
33238451Smsmith
33338451Smsmith#ifndef NFS_NOSYMLINK
33438451Smsmith/*
33538451Smsmith * Get the destination of a symbolic link.
33638451Smsmith */
33738451Smsmithint
338197178Semastenfs_readlink(struct nfs_iodesc *d, char *buf)
33938451Smsmith{
34038451Smsmith	struct {
34138451Smsmith		n_long	h[RPC_HEADER_WORDS];
34238451Smsmith		u_char fh[NFS_FHSIZE];
34338451Smsmith	} sdata;
34438451Smsmith	struct {
34538451Smsmith		n_long	h[RPC_HEADER_WORDS];
34638451Smsmith		struct nfs_readlnk_repl d;
34738451Smsmith	} rdata;
34838451Smsmith	ssize_t cc;
34938451Smsmith
35038451Smsmith#ifdef NFS_DEBUG
35138451Smsmith	if (debug)
35238451Smsmith		printf("readlink: called\n");
35338451Smsmith#endif
35438451Smsmith
35538451Smsmith	bcopy(d->fh, sdata.fh, NFS_FHSIZE);
35638451Smsmith	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READLINK,
35738451Smsmith		      sdata.fh, NFS_FHSIZE,
35838451Smsmith		      &rdata.d, sizeof(rdata.d));
35938451Smsmith	if (cc == -1)
36038451Smsmith		return (errno);
36138451Smsmith
36238451Smsmith	if (cc < 4)
36338451Smsmith		return (EIO);
364197178Semaste
36538451Smsmith	if (rdata.d.errno)
36638451Smsmith		return (ntohl(rdata.d.errno));
36738451Smsmith
36838451Smsmith	rdata.d.len = ntohl(rdata.d.len);
36938451Smsmith	if (rdata.d.len > NFS_MAXPATHLEN)
37038451Smsmith		return (ENAMETOOLONG);
37138451Smsmith
37238451Smsmith	bcopy(rdata.d.path, buf, rdata.d.len);
37338451Smsmith	buf[rdata.d.len] = 0;
37438451Smsmith	return (0);
37538451Smsmith}
37638451Smsmith#endif
37738451Smsmith
37838451Smsmith/*
37938451Smsmith * Read data from a file.
38038451Smsmith * Return transfer count or -1 (and set errno)
38138451Smsmith */
38238451Smsmithssize_t
383197178Semastenfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
38438451Smsmith{
38538451Smsmith	struct nfs_read_args *args;
38638451Smsmith	struct nfs_read_repl *repl;
38738451Smsmith	struct {
38838451Smsmith		n_long	h[RPC_HEADER_WORDS];
38938451Smsmith		struct nfs_read_args d;
39038451Smsmith	} sdata;
39138451Smsmith	struct {
39238451Smsmith		n_long	h[RPC_HEADER_WORDS];
39338451Smsmith		struct nfs_read_repl d;
39438451Smsmith	} rdata;
39538451Smsmith	size_t cc;
39638451Smsmith	long x;
39738451Smsmith	int hlen, rlen;
39838451Smsmith
39938451Smsmith	args = &sdata.d;
40038451Smsmith	repl = &rdata.d;
40138451Smsmith
40238451Smsmith	bcopy(d->fh, args->fh, NFS_FHSIZE);
40338451Smsmith	args->off = htonl((n_long)off);
40438451Smsmith	if (len > NFSREAD_SIZE)
40538451Smsmith		len = NFSREAD_SIZE;
40638451Smsmith	args->len = htonl((n_long)len);
40738451Smsmith	args->xxx = htonl((n_long)0);
40838451Smsmith	hlen = sizeof(*repl) - NFSREAD_SIZE;
40938451Smsmith
41038451Smsmith	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READ,
41138451Smsmith	    args, sizeof(*args),
41238451Smsmith	    repl, sizeof(*repl));
41338451Smsmith	if (cc == -1) {
41438451Smsmith		/* errno was already set by rpc_call */
41538451Smsmith		return (-1);
41638451Smsmith	}
41738451Smsmith	if (cc < hlen) {
41838451Smsmith		errno = EBADRPC;
41938451Smsmith		return (-1);
42038451Smsmith	}
42138451Smsmith	if (repl->errno) {
42238451Smsmith		errno = ntohl(repl->errno);
42338451Smsmith		return (-1);
42438451Smsmith	}
42538451Smsmith	rlen = cc - hlen;
42638451Smsmith	x = ntohl(repl->count);
42738451Smsmith	if (rlen < x) {
42838451Smsmith		printf("nfsread: short packet, %d < %ld\n", rlen, x);
42938451Smsmith		errno = EBADRPC;
43038451Smsmith		return(-1);
43138451Smsmith	}
43238451Smsmith	bcopy(repl->data, addr, x);
43338451Smsmith	return (x);
43438451Smsmith}
43538451Smsmith
43638451Smsmith/*
43738451Smsmith * Open a file.
43838451Smsmith * return zero or error number
43938451Smsmith */
44038451Smsmithint
441197178Semastenfs_open(const char *upath, struct open_file *f)
44238451Smsmith{
44338451Smsmith	struct iodesc *desc;
44438451Smsmith	struct nfs_iodesc *currfd;
445101112Sjake	char buf[2 * NFS_FHSIZE + 3];
446101112Sjake	u_char *fh;
447101112Sjake	char *cp;
448101112Sjake	int i;
44938451Smsmith#ifndef NFS_NOSYMLINK
45038451Smsmith	struct nfs_iodesc *newfd;
45138451Smsmith	struct nfsv2_fattrs *fa;
452101112Sjake	char *ncp;
45392913Sobrien	int c;
45438451Smsmith	char namebuf[NFS_MAXPATHLEN + 1];
45538451Smsmith	char linkbuf[NFS_MAXPATHLEN + 1];
45638451Smsmith	int nlinks = 0;
45738451Smsmith#endif
45838451Smsmith	int error;
45939468Smsmith	char *path;
46038451Smsmith
46138451Smsmith#ifdef NFS_DEBUG
46238451Smsmith 	if (debug)
463185155Sluigi 	    printf("nfs_open: %s (rootpath=%s)\n", upath, rootpath);
46438451Smsmith#endif
46538451Smsmith	if (!rootpath[0]) {
46638451Smsmith		printf("no rootpath, no nfs\n");
46738451Smsmith		return (ENXIO);
46838451Smsmith	}
46938451Smsmith
470177935Sdfr	/*
471177935Sdfr	 * This is silly - we should look at dv_type but that value is
472177935Sdfr	 * arch dependant and we can't use it here.
473177935Sdfr	 */
474111776Smarcel#ifndef __i386__
47599558Sjake	if (strcmp(f->f_dev->dv_name, "net") != 0)
47699558Sjake		return(EINVAL);
477177935Sdfr#else
478177935Sdfr	if (strcmp(f->f_dev->dv_name, "pxe") != 0)
479177935Sdfr		return(EINVAL);
48099558Sjake#endif
481111776Smarcel
48238451Smsmith	if (!(desc = socktodesc(*(int *)(f->f_devdata))))
48338451Smsmith		return(EINVAL);
48438451Smsmith
48538451Smsmith	/* Bind to a reserved port. */
48638451Smsmith	desc->myport = htons(--rpc_port);
48738451Smsmith	desc->destip = rootip;
48838451Smsmith	if ((error = nfs_getrootfh(desc, rootpath, nfs_root_node.fh)))
48938451Smsmith		return (error);
490252700Smav	nfs_root_node.fa.fa_type  = htonl(NFDIR);
491252700Smav	nfs_root_node.fa.fa_mode  = htonl(0755);
492252700Smav	nfs_root_node.fa.fa_nlink = htonl(2);
49338451Smsmith	nfs_root_node.iodesc = desc;
49438451Smsmith
495101112Sjake	fh = &nfs_root_node.fh[0];
496101112Sjake	buf[0] = 'X';
497101112Sjake	cp = &buf[1];
498101112Sjake	for (i = 0; i < NFS_FHSIZE; i++, cp += 2)
499101112Sjake		sprintf(cp, "%02x", fh[i]);
500101112Sjake	sprintf(cp, "X");
501101112Sjake	setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
502101112Sjake	setenv("boot.nfsroot.path", rootpath, 1);
503101112Sjake	setenv("boot.nfsroot.nfshandle", buf, 1);
504101112Sjake
505252700Smav	/* Allocate file system specific data structure */
506252700Smav	currfd = malloc(sizeof(*newfd));
507252700Smav	if (currfd == NULL) {
508252700Smav		error = ENOMEM;
509252700Smav		goto out;
510252700Smav	}
511252700Smav
51238451Smsmith#ifndef NFS_NOSYMLINK
513252700Smav	bcopy(&nfs_root_node, currfd, sizeof(*currfd));
51438451Smsmith	newfd = 0;
51538451Smsmith
51639468Smsmith	cp = path = strdup(upath);
51739468Smsmith	if (path == NULL) {
51839468Smsmith	    error = ENOMEM;
51939468Smsmith	    goto out;
52039468Smsmith	}
52138451Smsmith	while (*cp) {
52238451Smsmith		/*
52338451Smsmith		 * Remove extra separators
52438451Smsmith		 */
52538451Smsmith		while (*cp == '/')
52638451Smsmith			cp++;
52738451Smsmith
52838451Smsmith		if (*cp == '\0')
52938451Smsmith			break;
53038451Smsmith		/*
53138451Smsmith		 * Check that current node is a directory.
53238451Smsmith		 */
53338451Smsmith		if (currfd->fa.fa_type != htonl(NFDIR)) {
53438451Smsmith			error = ENOTDIR;
53538451Smsmith			goto out;
53638451Smsmith		}
537197178Semaste
53838451Smsmith		/* allocate file system specific data structure */
53938451Smsmith		newfd = malloc(sizeof(*newfd));
54038451Smsmith		newfd->iodesc = currfd->iodesc;
541197178Semaste
54238451Smsmith		/*
54338451Smsmith		 * Get next component of path name.
54438451Smsmith		 */
54538451Smsmith		{
54692913Sobrien			int len = 0;
547197178Semaste
54838451Smsmith			ncp = cp;
54938451Smsmith			while ((c = *cp) != '\0' && c != '/') {
55038451Smsmith				if (++len > NFS_MAXNAMLEN) {
55138451Smsmith					error = ENOENT;
55238451Smsmith					goto out;
55338451Smsmith				}
55438451Smsmith				cp++;
55538451Smsmith			}
55638451Smsmith			*cp = '\0';
55738451Smsmith		}
558197178Semaste
55938451Smsmith		/* lookup a file handle */
56038451Smsmith		error = nfs_lookupfh(currfd, ncp, newfd);
56138451Smsmith		*cp = c;
56238451Smsmith		if (error)
56338451Smsmith			goto out;
564197178Semaste
56538451Smsmith		/*
56638451Smsmith		 * Check for symbolic link
56738451Smsmith		 */
56838451Smsmith		if (newfd->fa.fa_type == htonl(NFLNK)) {
56938451Smsmith			int link_len, len;
570197178Semaste
57138451Smsmith			error = nfs_readlink(newfd, linkbuf);
57238451Smsmith			if (error)
57338451Smsmith				goto out;
57438451Smsmith
57538451Smsmith			link_len = strlen(linkbuf);
57638451Smsmith			len = strlen(cp);
57738451Smsmith
57838451Smsmith			if (link_len + len > MAXPATHLEN
57938451Smsmith			    || ++nlinks > MAXSYMLINKS) {
58038451Smsmith				error = ENOENT;
58138451Smsmith				goto out;
58238451Smsmith			}
58338451Smsmith
58438451Smsmith			bcopy(cp, &namebuf[link_len], len + 1);
58538451Smsmith			bcopy(linkbuf, namebuf, link_len);
586197178Semaste
58738451Smsmith			/*
58838451Smsmith			 * If absolute pathname, restart at root.
58938451Smsmith			 * If relative pathname, restart at parent directory.
59038451Smsmith			 */
59138451Smsmith			cp = namebuf;
592252700Smav			if (*cp == '/')
593252700Smav				bcopy(&nfs_root_node, currfd, sizeof(*currfd));
59438451Smsmith
59538451Smsmith			free(newfd);
59638451Smsmith			newfd = 0;
597197178Semaste
59838451Smsmith			continue;
59938451Smsmith		}
600197178Semaste
601252700Smav		free(currfd);
60238451Smsmith		currfd = newfd;
60338451Smsmith		newfd = 0;
60438451Smsmith	}
60538451Smsmith
60638451Smsmith	error = 0;
60738451Smsmith
60838451Smsmithout:
60938451Smsmith	if (newfd)
61038451Smsmith		free(newfd);
61139468Smsmith	if (path)
61239468Smsmith		free(path);
61338451Smsmith#else
61438451Smsmith        currfd->iodesc = desc;
61538451Smsmith
61639468Smsmith        error = nfs_lookupfh(&nfs_root_node, upath, currfd);
61738451Smsmith#endif
61838451Smsmith	if (!error) {
619252700Smav		currfd->off = 0;
62038451Smsmith		f->f_fsdata = (void *)currfd;
62138451Smsmith		return (0);
62238451Smsmith	}
623197178Semaste
62438451Smsmith#ifdef NFS_DEBUG
62538451Smsmith	if (debug)
62638451Smsmith		printf("nfs_open: %s lookupfh failed: %s\n",
62738451Smsmith		    path, strerror(error));
62838451Smsmith#endif
629252700Smav	free(currfd);
63038451Smsmith
63138451Smsmith	return (error);
63238451Smsmith}
63338451Smsmith
63438451Smsmithint
635197178Semastenfs_close(struct open_file *f)
63638451Smsmith{
63792913Sobrien	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
63838451Smsmith
63938451Smsmith#ifdef NFS_DEBUG
64038451Smsmith	if (debug)
64138451Smsmith		printf("nfs_close: fp=0x%lx\n", (u_long)fp);
64238451Smsmith#endif
64338451Smsmith
644252700Smav	if (fp)
64538451Smsmith		free(fp);
64638451Smsmith	f->f_fsdata = (void *)0;
647197178Semaste
64838451Smsmith	return (0);
64938451Smsmith}
65038451Smsmith
65138451Smsmith/*
65238451Smsmith * read a portion of a file
65338451Smsmith */
65438451Smsmithint
655197178Semastenfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
65638451Smsmith{
65792913Sobrien	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
65892913Sobrien	ssize_t cc;
65992913Sobrien	char *addr = buf;
660197178Semaste
66138451Smsmith#ifdef NFS_DEBUG
66238451Smsmith	if (debug)
66338451Smsmith		printf("nfs_read: size=%lu off=%d\n", (u_long)size,
66438451Smsmith		       (int)fp->off);
66538451Smsmith#endif
66638451Smsmith	while ((int)size > 0) {
66738451Smsmith		twiddle();
66838451Smsmith		cc = nfs_readdata(fp, fp->off, (void *)addr, size);
66938451Smsmith		/* XXX maybe should retry on certain errors */
67038451Smsmith		if (cc == -1) {
67138451Smsmith#ifdef NFS_DEBUG
67238451Smsmith			if (debug)
67338451Smsmith				printf("nfs_read: read: %s", strerror(errno));
67438451Smsmith#endif
67538451Smsmith			return (errno);	/* XXX - from nfs_readdata */
67638451Smsmith		}
67738451Smsmith		if (cc == 0) {
67838451Smsmith#ifdef NFS_DEBUG
67938451Smsmith			if (debug)
68038451Smsmith				printf("nfs_read: hit EOF unexpectantly");
68138451Smsmith#endif
68238451Smsmith			goto ret;
68338451Smsmith		}
68438451Smsmith		fp->off += cc;
68538451Smsmith		addr += cc;
68638451Smsmith		size -= cc;
68738451Smsmith	}
68838451Smsmithret:
68938451Smsmith	if (resid)
69038451Smsmith		*resid = size;
69138451Smsmith
69238451Smsmith	return (0);
69338451Smsmith}
69438451Smsmith
69538451Smsmith/*
69638451Smsmith * Not implemented.
69738451Smsmith */
69838451Smsmithint
699197178Semastenfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
70038451Smsmith{
70138451Smsmith	return (EROFS);
70238451Smsmith}
70338451Smsmith
70438451Smsmithoff_t
705197178Semastenfs_seek(struct open_file *f, off_t offset, int where)
70638451Smsmith{
70792913Sobrien	struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
70838451Smsmith	n_long size = ntohl(d->fa.fa_size);
70938451Smsmith
71038451Smsmith	switch (where) {
71138451Smsmith	case SEEK_SET:
71238451Smsmith		d->off = offset;
71338451Smsmith		break;
71438451Smsmith	case SEEK_CUR:
71538451Smsmith		d->off += offset;
71638451Smsmith		break;
71738451Smsmith	case SEEK_END:
71838451Smsmith		d->off = size - offset;
71938451Smsmith		break;
72038451Smsmith	default:
721124811Sjhb		errno = EINVAL;
72238451Smsmith		return (-1);
72338451Smsmith	}
72438451Smsmith
72538451Smsmith	return (d->off);
72638451Smsmith}
72738451Smsmith
72838451Smsmith/* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 */
72938451Smsmithint nfs_stat_types[8] = {
73038451Smsmith	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 0 };
73138451Smsmith
73238451Smsmithint
733197178Semastenfs_stat(struct open_file *f, struct stat *sb)
73438451Smsmith{
73538451Smsmith	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
73692913Sobrien	n_long ftype, mode;
73738451Smsmith
73838451Smsmith	ftype = ntohl(fp->fa.fa_type);
73938451Smsmith	mode  = ntohl(fp->fa.fa_mode);
74038451Smsmith	mode |= nfs_stat_types[ftype & 7];
74138451Smsmith
74238451Smsmith	sb->st_mode  = mode;
74338451Smsmith	sb->st_nlink = ntohl(fp->fa.fa_nlink);
74438451Smsmith	sb->st_uid   = ntohl(fp->fa.fa_uid);
74538451Smsmith	sb->st_gid   = ntohl(fp->fa.fa_gid);
74638451Smsmith	sb->st_size  = ntohl(fp->fa.fa_size);
74738451Smsmith
74838451Smsmith	return (0);
74938451Smsmith}
75059853Sps
75159853Spsstatic int
75259853Spsnfs_readdir(struct open_file *f, struct dirent *d)
75359853Sps{
75492913Sobrien	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
75559853Sps	struct nfs_readdir_args *args;
75659853Sps	struct nfs_readdir_data *rd;
75759853Sps	struct nfs_readdir_off  *roff = NULL;
75859853Sps	static char *buf;
759252701Smav	static struct nfs_iodesc *pfp = NULL;
76059853Sps	static n_long cookie = 0;
76159853Sps	size_t cc;
76259853Sps	n_long eof;
763197178Semaste
76459853Sps	struct {
76559853Sps		n_long h[RPC_HEADER_WORDS];
76659853Sps		struct nfs_readdir_args d;
76759853Sps	} sdata;
76859853Sps	static struct {
76959853Sps		n_long h[RPC_HEADER_WORDS];
77059853Sps		u_char d[NFS_READDIRSIZE];
77159853Sps	} rdata;
77259853Sps
773252701Smav	if (fp != pfp || fp->off != cookie) {
774252701Smav		pfp = NULL;
77559853Sps	refill:
77659853Sps		args = &sdata.d;
77759853Sps		bzero(args, sizeof(*args));
77859853Sps
77959853Sps		bcopy(fp->fh, args->fh, NFS_FHSIZE);
780252701Smav		args->cookie = htonl(fp->off);
78159853Sps		args->count  = htonl(NFS_READDIRSIZE);
782197178Semaste
78359853Sps		cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READDIR,
78459853Sps			      args, sizeof(*args),
78559853Sps			      rdata.d, sizeof(rdata.d));
78659853Sps		buf  = rdata.d;
78759853Sps		roff = (struct nfs_readdir_off *)buf;
78859853Sps		if (ntohl(roff->cookie) != 0)
789124811Sjhb			return EIO;
790252701Smav		pfp = fp;
791252701Smav		cookie = fp->off;
79259853Sps	}
79359853Sps	roff = (struct nfs_readdir_off *)buf;
79459853Sps
79559853Sps	if (ntohl(roff->follows) == 0) {
79659853Sps		eof = ntohl((roff+1)->cookie);
79759853Sps		if (eof) {
79859853Sps			cookie = 0;
799124811Sjhb			return ENOENT;
80059853Sps		}
80159853Sps		goto refill;
80259853Sps	}
80359853Sps
80459853Sps	buf += sizeof(struct nfs_readdir_off);
80559853Sps	rd = (struct nfs_readdir_data *)buf;
80659853Sps	d->d_namlen = ntohl(rd->len);
80759853Sps	bcopy(rd->name, d->d_name, d->d_namlen);
80859853Sps	d->d_name[d->d_namlen] = '\0';
80959853Sps
81059853Sps	buf += (sizeof(struct nfs_readdir_data) + roundup(htonl(rd->len),4));
81159853Sps	roff = (struct nfs_readdir_off *)buf;
812252701Smav	fp->off = cookie = ntohl(roff->cookie);
81359853Sps	return 0;
81459853Sps}
815212125Srmacklem#else	/* !OLD_NFSV2 */
816212125Srmacklem/*
817212125Srmacklem * Fetch the root file handle (call mount daemon)
818212125Srmacklem * Return zero or error number.
819212125Srmacklem */
820212125Srmacklemint
821212125Srmacklemnfs_getrootfh(struct iodesc *d, char *path, uint32_t *fhlenp, u_char *fhp)
822212125Srmacklem{
823212125Srmacklem	int len;
824212125Srmacklem	struct args {
825212125Srmacklem		uint32_t len;
826212125Srmacklem		char path[FNAME_SIZE];
827212125Srmacklem	} *args;
828212125Srmacklem	struct repl {
829212125Srmacklem		uint32_t errno;
830212125Srmacklem		uint32_t fhsize;
831212125Srmacklem		u_char fh[NFS_V3MAXFHSIZE];
832212125Srmacklem		uint32_t authcnt;
833212125Srmacklem		uint32_t auth[7];
834212125Srmacklem	} *repl;
835212125Srmacklem	struct {
836212125Srmacklem		uint32_t h[RPC_HEADER_WORDS];
837212125Srmacklem		struct args d;
838212125Srmacklem	} sdata;
839212125Srmacklem	struct {
840212125Srmacklem		uint32_t h[RPC_HEADER_WORDS];
841212125Srmacklem		struct repl d;
842212125Srmacklem	} rdata;
843212125Srmacklem	size_t cc;
844212125Srmacklem
845212125Srmacklem#ifdef NFS_DEBUG
846212125Srmacklem	if (debug)
847212125Srmacklem		printf("nfs_getrootfh: %s\n", path);
848212125Srmacklem#endif
849212125Srmacklem
850212125Srmacklem	args = &sdata.d;
851212125Srmacklem	repl = &rdata.d;
852212125Srmacklem
853212125Srmacklem	bzero(args, sizeof(*args));
854212125Srmacklem	len = strlen(path);
855212125Srmacklem	if (len > sizeof(args->path))
856212125Srmacklem		len = sizeof(args->path);
857212125Srmacklem	args->len = htonl(len);
858212125Srmacklem	bcopy(path, args->path, len);
859212125Srmacklem	len = sizeof(uint32_t) + roundup(len, sizeof(uint32_t));
860212125Srmacklem
861212125Srmacklem	cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER3, RPCMNT_MOUNT,
862212125Srmacklem	    args, len, repl, sizeof(*repl));
863212125Srmacklem	if (cc == -1)
864212125Srmacklem		/* errno was set by rpc_call */
865212125Srmacklem		return (errno);
866212125Srmacklem	if (cc < 2 * sizeof (uint32_t))
867212125Srmacklem		return (EBADRPC);
868212125Srmacklem	if (repl->errno != 0)
869212125Srmacklem		return (ntohl(repl->errno));
870212125Srmacklem	*fhlenp = ntohl(repl->fhsize);
871212125Srmacklem	bcopy(repl->fh, fhp, *fhlenp);
872212125Srmacklem	return (0);
873212125Srmacklem}
874212125Srmacklem
875212125Srmacklem/*
876212125Srmacklem * Lookup a file.  Store handle and attributes.
877212125Srmacklem * Return zero or error number.
878212125Srmacklem */
879212125Srmacklemint
880212125Srmacklemnfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd)
881212125Srmacklem{
882212125Srmacklem	int len, rlen, pos;
883212125Srmacklem	struct args {
884212125Srmacklem		uint32_t fhsize;
885212125Srmacklem		uint32_t fhplusname[1 +
886212125Srmacklem		    (NFS_V3MAXFHSIZE + FNAME_SIZE) / sizeof(uint32_t)];
887212125Srmacklem	} *args;
888212125Srmacklem	struct repl {
889212125Srmacklem		uint32_t errno;
890212125Srmacklem		uint32_t fhsize;
891212125Srmacklem		uint32_t fhplusattr[(NFS_V3MAXFHSIZE +
892212125Srmacklem		    2 * (sizeof(uint32_t) +
893212125Srmacklem		    sizeof(struct nfsv3_fattrs))) / sizeof(uint32_t)];
894212125Srmacklem	} *repl;
895212125Srmacklem	struct {
896212125Srmacklem		uint32_t h[RPC_HEADER_WORDS];
897212125Srmacklem		struct args d;
898212125Srmacklem	} sdata;
899212125Srmacklem	struct {
900212125Srmacklem		uint32_t h[RPC_HEADER_WORDS];
901212125Srmacklem		struct repl d;
902212125Srmacklem	} rdata;
903212125Srmacklem	ssize_t cc;
904212125Srmacklem
905212125Srmacklem#ifdef NFS_DEBUG
906212125Srmacklem	if (debug)
907212125Srmacklem		printf("lookupfh: called\n");
908212125Srmacklem#endif
909212125Srmacklem
910212125Srmacklem	args = &sdata.d;
911212125Srmacklem	repl = &rdata.d;
912212125Srmacklem
913212125Srmacklem	bzero(args, sizeof(*args));
914212125Srmacklem	args->fhsize = htonl(d->fhsize);
915212125Srmacklem	bcopy(d->fh, args->fhplusname, d->fhsize);
916212125Srmacklem	len = strlen(name);
917212125Srmacklem	if (len > FNAME_SIZE)
918212125Srmacklem		len = FNAME_SIZE;
919212125Srmacklem	pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
920212125Srmacklem	args->fhplusname[pos++] = htonl(len);
921212125Srmacklem	bcopy(name, &args->fhplusname[pos], len);
922212125Srmacklem	len = sizeof(uint32_t) + pos * sizeof(uint32_t) +
923212125Srmacklem	    roundup(len, sizeof(uint32_t));
924212125Srmacklem
925212125Srmacklem	rlen = sizeof(*repl);
926212125Srmacklem
927212125Srmacklem	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_LOOKUP,
928212125Srmacklem	    args, len, repl, rlen);
929212125Srmacklem	if (cc == -1)
930212125Srmacklem		return (errno);		/* XXX - from rpc_call */
931212125Srmacklem	if (cc < 2 * sizeof(uint32_t))
932212125Srmacklem		return (EIO);
933212125Srmacklem	if (repl->errno != 0)
934212125Srmacklem		/* saerrno.h now matches NFS error numbers. */
935212125Srmacklem		return (ntohl(repl->errno));
936212125Srmacklem	newfd->fhsize = ntohl(repl->fhsize);
937212125Srmacklem	bcopy(repl->fhplusattr, &newfd->fh, newfd->fhsize);
938212125Srmacklem	pos = roundup(newfd->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
939212125Srmacklem	if (repl->fhplusattr[pos++] == 0)
940212125Srmacklem		return (EIO);
941212125Srmacklem	bcopy(&repl->fhplusattr[pos], &newfd->fa, sizeof(newfd->fa));
942212125Srmacklem	return (0);
943212125Srmacklem}
944212125Srmacklem
945212125Srmacklem#ifndef NFS_NOSYMLINK
946212125Srmacklem/*
947212125Srmacklem * Get the destination of a symbolic link.
948212125Srmacklem */
949212125Srmacklemint
950212125Srmacklemnfs_readlink(struct nfs_iodesc *d, char *buf)
951212125Srmacklem{
952212125Srmacklem	struct args {
953212125Srmacklem		uint32_t fhsize;
954212125Srmacklem		u_char fh[NFS_V3MAXFHSIZE];
955212125Srmacklem	} *args;
956212125Srmacklem	struct repl {
957212125Srmacklem		uint32_t errno;
958212125Srmacklem		uint32_t ok;
959212125Srmacklem		struct nfsv3_fattrs fa;
960212125Srmacklem		uint32_t len;
961212125Srmacklem		u_char path[NFS_MAXPATHLEN];
962212125Srmacklem	} *repl;
963212125Srmacklem	struct {
964212125Srmacklem		uint32_t h[RPC_HEADER_WORDS];
965212125Srmacklem		struct args d;
966212125Srmacklem	} sdata;
967212125Srmacklem	struct {
968212125Srmacklem		uint32_t h[RPC_HEADER_WORDS];
969212125Srmacklem		struct repl d;
970212125Srmacklem	} rdata;
971212125Srmacklem	ssize_t cc;
972212125Srmacklem
973212125Srmacklem#ifdef NFS_DEBUG
974212125Srmacklem	if (debug)
975212125Srmacklem		printf("readlink: called\n");
976212125Srmacklem#endif
977212125Srmacklem
978212125Srmacklem	args = &sdata.d;
979212125Srmacklem	repl = &rdata.d;
980212125Srmacklem
981212125Srmacklem	bzero(args, sizeof(*args));
982212125Srmacklem	args->fhsize = htonl(d->fhsize);
983212125Srmacklem	bcopy(d->fh, args->fh, d->fhsize);
984212125Srmacklem	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READLINK,
985212125Srmacklem	    args, sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)),
986212125Srmacklem	    repl, sizeof(*repl));
987212125Srmacklem	if (cc == -1)
988212125Srmacklem		return (errno);
989212125Srmacklem
990212125Srmacklem	if (cc < 2 * sizeof(uint32_t))
991212125Srmacklem		return (EIO);
992212125Srmacklem
993212125Srmacklem	if (repl->errno != 0)
994212125Srmacklem		return (ntohl(repl->errno));
995212125Srmacklem
996212125Srmacklem	if (repl->ok == 0)
997212125Srmacklem		return (EIO);
998212125Srmacklem
999212125Srmacklem	repl->len = ntohl(repl->len);
1000212125Srmacklem	if (repl->len > NFS_MAXPATHLEN)
1001212125Srmacklem		return (ENAMETOOLONG);
1002212125Srmacklem
1003212125Srmacklem	bcopy(repl->path, buf, repl->len);
1004212125Srmacklem	buf[repl->len] = 0;
1005212125Srmacklem	return (0);
1006212125Srmacklem}
1007212125Srmacklem#endif
1008212125Srmacklem
1009212125Srmacklem/*
1010212125Srmacklem * Read data from a file.
1011212125Srmacklem * Return transfer count or -1 (and set errno)
1012212125Srmacklem */
1013212125Srmacklemssize_t
1014212125Srmacklemnfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
1015212125Srmacklem{
1016212125Srmacklem	struct args {
1017212125Srmacklem		uint32_t fhsize;
1018212125Srmacklem		uint32_t fhoffcnt[NFS_V3MAXFHSIZE / sizeof(uint32_t) + 3];
1019212125Srmacklem	} *args;
1020212125Srmacklem	struct repl {
1021212125Srmacklem		uint32_t errno;
1022212125Srmacklem		uint32_t ok;
1023212125Srmacklem		struct nfsv3_fattrs fa;
1024212125Srmacklem		uint32_t count;
1025212125Srmacklem		uint32_t eof;
1026212125Srmacklem		uint32_t len;
1027212125Srmacklem		u_char data[NFSREAD_SIZE];
1028212125Srmacklem	} *repl;
1029212125Srmacklem	struct {
1030212125Srmacklem		uint32_t h[RPC_HEADER_WORDS];
1031212125Srmacklem		struct args d;
1032212125Srmacklem	} sdata;
1033212125Srmacklem	struct {
1034212125Srmacklem		uint32_t h[RPC_HEADER_WORDS];
1035212125Srmacklem		struct repl d;
1036212125Srmacklem	} rdata;
1037212125Srmacklem	size_t cc;
1038212125Srmacklem	long x;
1039212125Srmacklem	int hlen, rlen, pos;
1040212125Srmacklem
1041212125Srmacklem	args = &sdata.d;
1042212125Srmacklem	repl = &rdata.d;
1043212125Srmacklem
1044212125Srmacklem	bzero(args, sizeof(*args));
1045212125Srmacklem	args->fhsize = htonl(d->fhsize);
1046212125Srmacklem	bcopy(d->fh, args->fhoffcnt, d->fhsize);
1047212125Srmacklem	pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
1048212125Srmacklem	args->fhoffcnt[pos++] = 0;
1049212125Srmacklem	args->fhoffcnt[pos++] = htonl((uint32_t)off);
1050212125Srmacklem	if (len > NFSREAD_SIZE)
1051212125Srmacklem		len = NFSREAD_SIZE;
1052212125Srmacklem	args->fhoffcnt[pos] = htonl((uint32_t)len);
1053212125Srmacklem	hlen = sizeof(*repl) - NFSREAD_SIZE;
1054212125Srmacklem
1055212125Srmacklem	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READ,
1056212125Srmacklem	    args, 4 * sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)),
1057212125Srmacklem	    repl, sizeof(*repl));
1058212125Srmacklem	if (cc == -1)
1059212125Srmacklem		/* errno was already set by rpc_call */
1060212125Srmacklem		return (-1);
1061212125Srmacklem	if (cc < hlen) {
1062212125Srmacklem		errno = EBADRPC;
1063212125Srmacklem		return (-1);
1064212125Srmacklem	}
1065212125Srmacklem	if (repl->errno != 0) {
1066212125Srmacklem		errno = ntohl(repl->errno);
1067212125Srmacklem		return (-1);
1068212125Srmacklem	}
1069212125Srmacklem	rlen = cc - hlen;
1070212125Srmacklem	x = ntohl(repl->count);
1071212125Srmacklem	if (rlen < x) {
1072212125Srmacklem		printf("nfsread: short packet, %d < %ld\n", rlen, x);
1073212125Srmacklem		errno = EBADRPC;
1074212125Srmacklem		return (-1);
1075212125Srmacklem	}
1076212125Srmacklem	bcopy(repl->data, addr, x);
1077212125Srmacklem	return (x);
1078212125Srmacklem}
1079212125Srmacklem
1080212125Srmacklem/*
1081212125Srmacklem * Open a file.
1082212125Srmacklem * return zero or error number
1083212125Srmacklem */
1084212125Srmacklemint
1085212125Srmacklemnfs_open(const char *upath, struct open_file *f)
1086212125Srmacklem{
1087212125Srmacklem	struct iodesc *desc;
1088212125Srmacklem	struct nfs_iodesc *currfd;
1089212125Srmacklem	char buf[2 * NFS_V3MAXFHSIZE + 3];
1090212125Srmacklem	u_char *fh;
1091212125Srmacklem	char *cp;
1092212125Srmacklem	int i;
1093212125Srmacklem#ifndef NFS_NOSYMLINK
1094212125Srmacklem	struct nfs_iodesc *newfd;
1095212125Srmacklem	struct nfsv3_fattrs *fa;
1096212125Srmacklem	char *ncp;
1097212125Srmacklem	int c;
1098212125Srmacklem	char namebuf[NFS_MAXPATHLEN + 1];
1099212125Srmacklem	char linkbuf[NFS_MAXPATHLEN + 1];
1100212125Srmacklem	int nlinks = 0;
1101212125Srmacklem#endif
1102212125Srmacklem	int error;
1103212125Srmacklem	char *path;
1104212125Srmacklem
1105212125Srmacklem#ifdef NFS_DEBUG
1106212125Srmacklem 	if (debug)
1107212125Srmacklem 	    printf("nfs_open: %s (rootpath=%s)\n", upath, rootpath);
1108212125Srmacklem#endif
1109212125Srmacklem	if (!rootpath[0]) {
1110212125Srmacklem		printf("no rootpath, no nfs\n");
1111212125Srmacklem		return (ENXIO);
1112212125Srmacklem	}
1113212125Srmacklem
1114212125Srmacklem	/*
1115212125Srmacklem	 * This is silly - we should look at dv_type but that value is
1116212125Srmacklem	 * arch dependant and we can't use it here.
1117212125Srmacklem	 */
1118212125Srmacklem#ifndef __i386__
1119212125Srmacklem	if (strcmp(f->f_dev->dv_name, "net") != 0)
1120212125Srmacklem		return (EINVAL);
1121212125Srmacklem#else
1122212125Srmacklem	if (strcmp(f->f_dev->dv_name, "pxe") != 0)
1123212125Srmacklem		return (EINVAL);
1124212125Srmacklem#endif
1125212125Srmacklem
1126212125Srmacklem	if (!(desc = socktodesc(*(int *)(f->f_devdata))))
1127212125Srmacklem		return (EINVAL);
1128212125Srmacklem
1129212125Srmacklem	/* Bind to a reserved port. */
1130212125Srmacklem	desc->myport = htons(--rpc_port);
1131212125Srmacklem	desc->destip = rootip;
1132212125Srmacklem	if ((error = nfs_getrootfh(desc, rootpath, &nfs_root_node.fhsize,
1133212125Srmacklem	    nfs_root_node.fh)))
1134212125Srmacklem		return (error);
1135252700Smav	nfs_root_node.fa.fa_type  = htonl(NFDIR);
1136252700Smav	nfs_root_node.fa.fa_mode  = htonl(0755);
1137252700Smav	nfs_root_node.fa.fa_nlink = htonl(2);
1138212125Srmacklem	nfs_root_node.iodesc = desc;
1139212125Srmacklem
1140212125Srmacklem	fh = &nfs_root_node.fh[0];
1141212125Srmacklem	buf[0] = 'X';
1142212125Srmacklem	cp = &buf[1];
1143212125Srmacklem	for (i = 0; i < nfs_root_node.fhsize; i++, cp += 2)
1144212125Srmacklem		sprintf(cp, "%02x", fh[i]);
1145212125Srmacklem	sprintf(cp, "X");
1146212125Srmacklem	setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
1147212125Srmacklem	setenv("boot.nfsroot.path", rootpath, 1);
1148212125Srmacklem	setenv("boot.nfsroot.nfshandle", buf, 1);
1149212125Srmacklem	sprintf(buf, "%d", nfs_root_node.fhsize);
1150212125Srmacklem	setenv("boot.nfsroot.nfshandlelen", buf, 1);
1151212125Srmacklem
1152252700Smav	/* Allocate file system specific data structure */
1153252700Smav	currfd = malloc(sizeof(*newfd));
1154252700Smav	if (currfd == NULL) {
1155252700Smav		error = ENOMEM;
1156252700Smav		goto out;
1157252700Smav	}
1158212125Srmacklem#ifndef NFS_NOSYMLINK
1159252700Smav	bcopy(&nfs_root_node, currfd, sizeof(*currfd));
1160212125Srmacklem	newfd = 0;
1161212125Srmacklem
1162212125Srmacklem	cp = path = strdup(upath);
1163212125Srmacklem	if (path == NULL) {
1164212125Srmacklem		error = ENOMEM;
1165212125Srmacklem		goto out;
1166212125Srmacklem	}
1167212125Srmacklem	while (*cp) {
1168212125Srmacklem		/*
1169212125Srmacklem		 * Remove extra separators
1170212125Srmacklem		 */
1171212125Srmacklem		while (*cp == '/')
1172212125Srmacklem			cp++;
1173212125Srmacklem
1174212125Srmacklem		if (*cp == '\0')
1175212125Srmacklem			break;
1176212125Srmacklem		/*
1177212125Srmacklem		 * Check that current node is a directory.
1178212125Srmacklem		 */
1179212125Srmacklem		if (currfd->fa.fa_type != htonl(NFDIR)) {
1180212125Srmacklem			error = ENOTDIR;
1181212125Srmacklem			goto out;
1182212125Srmacklem		}
1183212125Srmacklem
1184212125Srmacklem		/* allocate file system specific data structure */
1185212125Srmacklem		newfd = malloc(sizeof(*newfd));
1186212125Srmacklem		if (newfd == NULL) {
1187212125Srmacklem			error = ENOMEM;
1188212125Srmacklem			goto out;
1189212125Srmacklem		}
1190212125Srmacklem		newfd->iodesc = currfd->iodesc;
1191212125Srmacklem
1192212125Srmacklem		/*
1193212125Srmacklem		 * Get next component of path name.
1194212125Srmacklem		 */
1195212125Srmacklem		{
1196212125Srmacklem			int len = 0;
1197212125Srmacklem
1198212125Srmacklem			ncp = cp;
1199212125Srmacklem			while ((c = *cp) != '\0' && c != '/') {
1200212125Srmacklem				if (++len > NFS_MAXNAMLEN) {
1201212125Srmacklem					error = ENOENT;
1202212125Srmacklem					goto out;
1203212125Srmacklem				}
1204212125Srmacklem				cp++;
1205212125Srmacklem			}
1206212125Srmacklem			*cp = '\0';
1207212125Srmacklem		}
1208212125Srmacklem
1209212125Srmacklem		/* lookup a file handle */
1210212125Srmacklem		error = nfs_lookupfh(currfd, ncp, newfd);
1211212125Srmacklem		*cp = c;
1212212125Srmacklem		if (error)
1213212125Srmacklem			goto out;
1214212125Srmacklem
1215212125Srmacklem		/*
1216212125Srmacklem		 * Check for symbolic link
1217212125Srmacklem		 */
1218212125Srmacklem		if (newfd->fa.fa_type == htonl(NFLNK)) {
1219212125Srmacklem			int link_len, len;
1220212125Srmacklem
1221212125Srmacklem			error = nfs_readlink(newfd, linkbuf);
1222212125Srmacklem			if (error)
1223212125Srmacklem				goto out;
1224212125Srmacklem
1225212125Srmacklem			link_len = strlen(linkbuf);
1226212125Srmacklem			len = strlen(cp);
1227212125Srmacklem
1228212125Srmacklem			if (link_len + len > MAXPATHLEN
1229212125Srmacklem			    || ++nlinks > MAXSYMLINKS) {
1230212125Srmacklem				error = ENOENT;
1231212125Srmacklem				goto out;
1232212125Srmacklem			}
1233212125Srmacklem
1234212125Srmacklem			bcopy(cp, &namebuf[link_len], len + 1);
1235212125Srmacklem			bcopy(linkbuf, namebuf, link_len);
1236212125Srmacklem
1237212125Srmacklem			/*
1238212125Srmacklem			 * If absolute pathname, restart at root.
1239212125Srmacklem			 * If relative pathname, restart at parent directory.
1240212125Srmacklem			 */
1241212125Srmacklem			cp = namebuf;
1242252700Smav			if (*cp == '/')
1243252700Smav				bcopy(&nfs_root_node, currfd, sizeof(*currfd));
1244212125Srmacklem
1245212125Srmacklem			free(newfd);
1246212125Srmacklem			newfd = 0;
1247212125Srmacklem
1248212125Srmacklem			continue;
1249212125Srmacklem		}
1250212125Srmacklem
1251252700Smav		free(currfd);
1252212125Srmacklem		currfd = newfd;
1253212125Srmacklem		newfd = 0;
1254212125Srmacklem	}
1255212125Srmacklem
1256212125Srmacklem	error = 0;
1257212125Srmacklem
1258212125Srmacklemout:
1259212125Srmacklem	free(newfd);
1260212125Srmacklem	free(path);
1261212125Srmacklem#else
1262252700Smav	currfd->iodesc = desc;
1263212125Srmacklem
1264252700Smav	error = nfs_lookupfh(&nfs_root_node, upath, currfd);
1265212125Srmacklem#endif
1266212125Srmacklem	if (!error) {
1267252700Smav		currfd->off = 0;
1268252701Smav		currfd->cookie = 0;
1269212125Srmacklem		f->f_fsdata = (void *)currfd;
1270212125Srmacklem		return (0);
1271212125Srmacklem	}
1272212125Srmacklem
1273212125Srmacklem#ifdef NFS_DEBUG
1274212125Srmacklem	if (debug)
1275212125Srmacklem		printf("nfs_open: %s lookupfh failed: %s\n",
1276212125Srmacklem		    path, strerror(error));
1277212125Srmacklem#endif
1278252700Smav	free(currfd);
1279212125Srmacklem
1280212125Srmacklem	return (error);
1281212125Srmacklem}
1282212125Srmacklem
1283212125Srmacklemint
1284212125Srmacklemnfs_close(struct open_file *f)
1285212125Srmacklem{
1286212125Srmacklem	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
1287212125Srmacklem
1288212125Srmacklem#ifdef NFS_DEBUG
1289212125Srmacklem	if (debug)
1290212125Srmacklem		printf("nfs_close: fp=0x%lx\n", (u_long)fp);
1291212125Srmacklem#endif
1292212125Srmacklem
1293252700Smav	if (fp)
1294212125Srmacklem		free(fp);
1295212125Srmacklem	f->f_fsdata = (void *)0;
1296212125Srmacklem
1297212125Srmacklem	return (0);
1298212125Srmacklem}
1299212125Srmacklem
1300212125Srmacklem/*
1301212125Srmacklem * read a portion of a file
1302212125Srmacklem */
1303212125Srmacklemint
1304212125Srmacklemnfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
1305212125Srmacklem{
1306212125Srmacklem	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
1307212125Srmacklem	ssize_t cc;
1308212125Srmacklem	char *addr = buf;
1309212125Srmacklem
1310212125Srmacklem#ifdef NFS_DEBUG
1311212125Srmacklem	if (debug)
1312212125Srmacklem		printf("nfs_read: size=%lu off=%d\n", (u_long)size,
1313212125Srmacklem		       (int)fp->off);
1314212125Srmacklem#endif
1315212125Srmacklem	while ((int)size > 0) {
1316212125Srmacklem		twiddle();
1317212125Srmacklem		cc = nfs_readdata(fp, fp->off, (void *)addr, size);
1318212125Srmacklem		/* XXX maybe should retry on certain errors */
1319212125Srmacklem		if (cc == -1) {
1320212125Srmacklem#ifdef NFS_DEBUG
1321212125Srmacklem			if (debug)
1322212125Srmacklem				printf("nfs_read: read: %s", strerror(errno));
1323212125Srmacklem#endif
1324212125Srmacklem			return (errno);	/* XXX - from nfs_readdata */
1325212125Srmacklem		}
1326212125Srmacklem		if (cc == 0) {
1327212125Srmacklem#ifdef NFS_DEBUG
1328212125Srmacklem			if (debug)
1329212125Srmacklem				printf("nfs_read: hit EOF unexpectantly");
1330212125Srmacklem#endif
1331212125Srmacklem			goto ret;
1332212125Srmacklem		}
1333212125Srmacklem		fp->off += cc;
1334212125Srmacklem		addr += cc;
1335212125Srmacklem		size -= cc;
1336212125Srmacklem	}
1337212125Srmacklemret:
1338212125Srmacklem	if (resid)
1339212125Srmacklem		*resid = size;
1340212125Srmacklem
1341212125Srmacklem	return (0);
1342212125Srmacklem}
1343212125Srmacklem
1344212125Srmacklem/*
1345212125Srmacklem * Not implemented.
1346212125Srmacklem */
1347212125Srmacklemint
1348212125Srmacklemnfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
1349212125Srmacklem{
1350212125Srmacklem	return (EROFS);
1351212125Srmacklem}
1352212125Srmacklem
1353212125Srmacklemoff_t
1354212125Srmacklemnfs_seek(struct open_file *f, off_t offset, int where)
1355212125Srmacklem{
1356212125Srmacklem	struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
1357212125Srmacklem	uint32_t size = ntohl(d->fa.fa_size.val[1]);
1358212125Srmacklem
1359212125Srmacklem	switch (where) {
1360212125Srmacklem	case SEEK_SET:
1361212125Srmacklem		d->off = offset;
1362212125Srmacklem		break;
1363212125Srmacklem	case SEEK_CUR:
1364212125Srmacklem		d->off += offset;
1365212125Srmacklem		break;
1366212125Srmacklem	case SEEK_END:
1367212125Srmacklem		d->off = size - offset;
1368212125Srmacklem		break;
1369212125Srmacklem	default:
1370212125Srmacklem		errno = EINVAL;
1371212125Srmacklem		return (-1);
1372212125Srmacklem	}
1373212125Srmacklem
1374212125Srmacklem	return (d->off);
1375212125Srmacklem}
1376212125Srmacklem
1377212125Srmacklem/* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5, NFSOCK=6, NFFIFO=7 */
1378212125Srmacklemint nfs_stat_types[9] = {
1379212125Srmacklem	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO, 0 };
1380212125Srmacklem
1381212125Srmacklemint
1382212125Srmacklemnfs_stat(struct open_file *f, struct stat *sb)
1383212125Srmacklem{
1384212125Srmacklem	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
1385212125Srmacklem	uint32_t ftype, mode;
1386212125Srmacklem
1387212125Srmacklem	ftype = ntohl(fp->fa.fa_type);
1388212125Srmacklem	mode  = ntohl(fp->fa.fa_mode);
1389212125Srmacklem	mode |= nfs_stat_types[ftype & 7];
1390212125Srmacklem
1391212125Srmacklem	sb->st_mode  = mode;
1392212125Srmacklem	sb->st_nlink = ntohl(fp->fa.fa_nlink);
1393212125Srmacklem	sb->st_uid   = ntohl(fp->fa.fa_uid);
1394212125Srmacklem	sb->st_gid   = ntohl(fp->fa.fa_gid);
1395212125Srmacklem	sb->st_size  = ntohl(fp->fa.fa_size.val[1]);
1396212125Srmacklem
1397212125Srmacklem	return (0);
1398212125Srmacklem}
1399212125Srmacklem
1400212125Srmacklemstatic int
1401212125Srmacklemnfs_readdir(struct open_file *f, struct dirent *d)
1402212125Srmacklem{
1403212125Srmacklem	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
1404212125Srmacklem	struct nfsv3_readdir_repl *repl;
1405212125Srmacklem	struct nfsv3_readdir_entry *rent;
1406212125Srmacklem	static char *buf;
1407252701Smav	static struct nfs_iodesc *pfp = NULL;
1408252701Smav	static uint64_t cookie = 0;
1409212125Srmacklem	size_t cc;
1410212125Srmacklem	int pos;
1411212125Srmacklem
1412212125Srmacklem	struct args {
1413212125Srmacklem		uint32_t fhsize;
1414212125Srmacklem		uint32_t fhpluscookie[5 + NFS_V3MAXFHSIZE];
1415212125Srmacklem	} *args;
1416212125Srmacklem	struct {
1417212125Srmacklem		uint32_t h[RPC_HEADER_WORDS];
1418212125Srmacklem		struct args d;
1419212125Srmacklem	} sdata;
1420212125Srmacklem	static struct {
1421212125Srmacklem		uint32_t h[RPC_HEADER_WORDS];
1422212125Srmacklem		u_char d[NFS_READDIRSIZE];
1423212125Srmacklem	} rdata;
1424212125Srmacklem
1425252701Smav	if (fp != pfp || fp->off != cookie) {
1426252701Smav		pfp = NULL;
1427212125Srmacklem	refill:
1428212125Srmacklem		args = &sdata.d;
1429212125Srmacklem		bzero(args, sizeof(*args));
1430212125Srmacklem
1431212125Srmacklem		args->fhsize = htonl(fp->fhsize);
1432212125Srmacklem		bcopy(fp->fh, args->fhpluscookie, fp->fhsize);
1433212125Srmacklem		pos = roundup(fp->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
1434252701Smav		args->fhpluscookie[pos++] = htonl(fp->off >> 32);
1435252701Smav		args->fhpluscookie[pos++] = htonl(fp->off);
1436252701Smav		args->fhpluscookie[pos++] = htonl(fp->cookie >> 32);
1437252701Smav		args->fhpluscookie[pos++] = htonl(fp->cookie);
1438212125Srmacklem		args->fhpluscookie[pos] = htonl(NFS_READDIRSIZE);
1439212125Srmacklem
1440212125Srmacklem		cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READDIR,
1441212125Srmacklem		    args, 6 * sizeof(uint32_t) +
1442212125Srmacklem		    roundup(fp->fhsize, sizeof(uint32_t)),
1443212125Srmacklem		    rdata.d, sizeof(rdata.d));
1444212125Srmacklem		buf  = rdata.d;
1445212125Srmacklem		repl = (struct nfsv3_readdir_repl *)buf;
1446212125Srmacklem		if (repl->errno != 0)
1447212125Srmacklem			return (ntohl(repl->errno));
1448252701Smav		pfp = fp;
1449252701Smav		cookie = fp->off;
1450252701Smav		fp->cookie = ((uint64_t)ntohl(repl->cookiev0) << 32) |
1451252701Smav		    ntohl(repl->cookiev1);
1452212125Srmacklem		buf += sizeof (struct nfsv3_readdir_repl);
1453212125Srmacklem	}
1454212125Srmacklem	rent = (struct nfsv3_readdir_entry *)buf;
1455212125Srmacklem
1456212125Srmacklem	if (rent->follows == 0) {
1457212125Srmacklem		/* fid0 is actually eof */
1458212125Srmacklem		if (rent->fid0 != 0) {
1459252701Smav			cookie = 0;
1460212125Srmacklem			return (ENOENT);
1461212125Srmacklem		}
1462212125Srmacklem		goto refill;
1463212125Srmacklem	}
1464212125Srmacklem
1465212125Srmacklem	d->d_namlen = ntohl(rent->len);
1466212125Srmacklem	bcopy(rent->nameplus, d->d_name, d->d_namlen);
1467212125Srmacklem	d->d_name[d->d_namlen] = '\0';
1468212125Srmacklem
1469212125Srmacklem	pos = roundup(d->d_namlen, sizeof(uint32_t)) / sizeof(uint32_t);
1470252701Smav	fp->off = cookie = ((uint64_t)ntohl(rent->nameplus[pos]) << 32) |
1471252701Smav	    ntohl(rent->nameplus[pos + 1]);
1472252701Smav	pos += 2;
1473212125Srmacklem	buf = (u_char *)&rent->nameplus[pos];
1474212125Srmacklem	return (0);
1475212125Srmacklem}
1476212125Srmacklem#endif	/* OLD_NFSV2 */
1477