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 */
184240780Smav	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);
490240774Smav	nfs_root_node.fa.fa_type  = htonl(NFDIR);
491240774Smav	nfs_root_node.fa.fa_mode  = htonl(0755);
492240774Smav	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
505240774Smav	/* Allocate file system specific data structure */
506240774Smav	currfd = malloc(sizeof(*newfd));
507240774Smav	if (currfd == NULL) {
508240774Smav		error = ENOMEM;
509240774Smav		goto out;
510240774Smav	}
511240774Smav
51238451Smsmith#ifndef NFS_NOSYMLINK
513240774Smav	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;
592240774Smav			if (*cp == '/')
593240774Smav				bcopy(&nfs_root_node, currfd, sizeof(*currfd));
59438451Smsmith
59538451Smsmith			free(newfd);
59638451Smsmith			newfd = 0;
597197178Semaste
59838451Smsmith			continue;
59938451Smsmith		}
600197178Semaste
601240774Smav		free(currfd);
60238451Smsmith		currfd = newfd;
60338451Smsmith		newfd = 0;
60438451Smsmith	}
60538451Smsmith
60638451Smsmith	error = 0;
60738451Smsmith
60838451Smsmithout:
609240881Skevlo	free(newfd);
610240881Skevlo	free(path);
61138451Smsmith#else
61238451Smsmith        currfd->iodesc = desc;
61338451Smsmith
61439468Smsmith        error = nfs_lookupfh(&nfs_root_node, upath, currfd);
61538451Smsmith#endif
61638451Smsmith	if (!error) {
617240774Smav		currfd->off = 0;
61838451Smsmith		f->f_fsdata = (void *)currfd;
61938451Smsmith		return (0);
62038451Smsmith	}
621197178Semaste
62238451Smsmith#ifdef NFS_DEBUG
62338451Smsmith	if (debug)
62438451Smsmith		printf("nfs_open: %s lookupfh failed: %s\n",
62538451Smsmith		    path, strerror(error));
62638451Smsmith#endif
627240774Smav	free(currfd);
62838451Smsmith
62938451Smsmith	return (error);
63038451Smsmith}
63138451Smsmith
63238451Smsmithint
633197178Semastenfs_close(struct open_file *f)
63438451Smsmith{
63592913Sobrien	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
63638451Smsmith
63738451Smsmith#ifdef NFS_DEBUG
63838451Smsmith	if (debug)
63938451Smsmith		printf("nfs_close: fp=0x%lx\n", (u_long)fp);
64038451Smsmith#endif
64138451Smsmith
642240774Smav	if (fp)
64338451Smsmith		free(fp);
64438451Smsmith	f->f_fsdata = (void *)0;
645197178Semaste
64638451Smsmith	return (0);
64738451Smsmith}
64838451Smsmith
64938451Smsmith/*
65038451Smsmith * read a portion of a file
65138451Smsmith */
65238451Smsmithint
653197178Semastenfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
65438451Smsmith{
65592913Sobrien	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
65692913Sobrien	ssize_t cc;
65792913Sobrien	char *addr = buf;
658197178Semaste
65938451Smsmith#ifdef NFS_DEBUG
66038451Smsmith	if (debug)
66138451Smsmith		printf("nfs_read: size=%lu off=%d\n", (u_long)size,
66238451Smsmith		       (int)fp->off);
66338451Smsmith#endif
66438451Smsmith	while ((int)size > 0) {
66538451Smsmith		twiddle();
66638451Smsmith		cc = nfs_readdata(fp, fp->off, (void *)addr, size);
66738451Smsmith		/* XXX maybe should retry on certain errors */
66838451Smsmith		if (cc == -1) {
66938451Smsmith#ifdef NFS_DEBUG
67038451Smsmith			if (debug)
67138451Smsmith				printf("nfs_read: read: %s", strerror(errno));
67238451Smsmith#endif
67338451Smsmith			return (errno);	/* XXX - from nfs_readdata */
67438451Smsmith		}
67538451Smsmith		if (cc == 0) {
67638451Smsmith#ifdef NFS_DEBUG
67738451Smsmith			if (debug)
67838451Smsmith				printf("nfs_read: hit EOF unexpectantly");
67938451Smsmith#endif
68038451Smsmith			goto ret;
68138451Smsmith		}
68238451Smsmith		fp->off += cc;
68338451Smsmith		addr += cc;
68438451Smsmith		size -= cc;
68538451Smsmith	}
68638451Smsmithret:
68738451Smsmith	if (resid)
68838451Smsmith		*resid = size;
68938451Smsmith
69038451Smsmith	return (0);
69138451Smsmith}
69238451Smsmith
69338451Smsmith/*
69438451Smsmith * Not implemented.
69538451Smsmith */
69638451Smsmithint
697197178Semastenfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
69838451Smsmith{
69938451Smsmith	return (EROFS);
70038451Smsmith}
70138451Smsmith
70238451Smsmithoff_t
703197178Semastenfs_seek(struct open_file *f, off_t offset, int where)
70438451Smsmith{
70592913Sobrien	struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
70638451Smsmith	n_long size = ntohl(d->fa.fa_size);
70738451Smsmith
70838451Smsmith	switch (where) {
70938451Smsmith	case SEEK_SET:
71038451Smsmith		d->off = offset;
71138451Smsmith		break;
71238451Smsmith	case SEEK_CUR:
71338451Smsmith		d->off += offset;
71438451Smsmith		break;
71538451Smsmith	case SEEK_END:
71638451Smsmith		d->off = size - offset;
71738451Smsmith		break;
71838451Smsmith	default:
719124811Sjhb		errno = EINVAL;
72038451Smsmith		return (-1);
72138451Smsmith	}
72238451Smsmith
72338451Smsmith	return (d->off);
72438451Smsmith}
72538451Smsmith
72638451Smsmith/* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 */
72738451Smsmithint nfs_stat_types[8] = {
72838451Smsmith	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 0 };
72938451Smsmith
73038451Smsmithint
731197178Semastenfs_stat(struct open_file *f, struct stat *sb)
73238451Smsmith{
73338451Smsmith	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
73492913Sobrien	n_long ftype, mode;
73538451Smsmith
73638451Smsmith	ftype = ntohl(fp->fa.fa_type);
73738451Smsmith	mode  = ntohl(fp->fa.fa_mode);
73838451Smsmith	mode |= nfs_stat_types[ftype & 7];
73938451Smsmith
74038451Smsmith	sb->st_mode  = mode;
74138451Smsmith	sb->st_nlink = ntohl(fp->fa.fa_nlink);
74238451Smsmith	sb->st_uid   = ntohl(fp->fa.fa_uid);
74338451Smsmith	sb->st_gid   = ntohl(fp->fa.fa_gid);
74438451Smsmith	sb->st_size  = ntohl(fp->fa.fa_size);
74538451Smsmith
74638451Smsmith	return (0);
74738451Smsmith}
74859853Sps
74959853Spsstatic int
75059853Spsnfs_readdir(struct open_file *f, struct dirent *d)
75159853Sps{
75292913Sobrien	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
75359853Sps	struct nfs_readdir_args *args;
75459853Sps	struct nfs_readdir_data *rd;
75559853Sps	struct nfs_readdir_off  *roff = NULL;
75659853Sps	static char *buf;
757240780Smav	static struct nfs_iodesc *pfp = NULL;
75859853Sps	static n_long cookie = 0;
75959853Sps	size_t cc;
76059853Sps	n_long eof;
761197178Semaste
76259853Sps	struct {
76359853Sps		n_long h[RPC_HEADER_WORDS];
76459853Sps		struct nfs_readdir_args d;
76559853Sps	} sdata;
76659853Sps	static struct {
76759853Sps		n_long h[RPC_HEADER_WORDS];
76859853Sps		u_char d[NFS_READDIRSIZE];
76959853Sps	} rdata;
77059853Sps
771240780Smav	if (fp != pfp || fp->off != cookie) {
772240780Smav		pfp = NULL;
77359853Sps	refill:
77459853Sps		args = &sdata.d;
77559853Sps		bzero(args, sizeof(*args));
77659853Sps
77759853Sps		bcopy(fp->fh, args->fh, NFS_FHSIZE);
778240780Smav		args->cookie = htonl(fp->off);
77959853Sps		args->count  = htonl(NFS_READDIRSIZE);
780197178Semaste
78159853Sps		cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READDIR,
78259853Sps			      args, sizeof(*args),
78359853Sps			      rdata.d, sizeof(rdata.d));
78459853Sps		buf  = rdata.d;
78559853Sps		roff = (struct nfs_readdir_off *)buf;
78659853Sps		if (ntohl(roff->cookie) != 0)
787124811Sjhb			return EIO;
788240780Smav		pfp = fp;
789240780Smav		cookie = fp->off;
79059853Sps	}
79159853Sps	roff = (struct nfs_readdir_off *)buf;
79259853Sps
79359853Sps	if (ntohl(roff->follows) == 0) {
79459853Sps		eof = ntohl((roff+1)->cookie);
79559853Sps		if (eof) {
79659853Sps			cookie = 0;
797124811Sjhb			return ENOENT;
79859853Sps		}
79959853Sps		goto refill;
80059853Sps	}
80159853Sps
80259853Sps	buf += sizeof(struct nfs_readdir_off);
80359853Sps	rd = (struct nfs_readdir_data *)buf;
80459853Sps	d->d_namlen = ntohl(rd->len);
80559853Sps	bcopy(rd->name, d->d_name, d->d_namlen);
80659853Sps	d->d_name[d->d_namlen] = '\0';
80759853Sps
80859853Sps	buf += (sizeof(struct nfs_readdir_data) + roundup(htonl(rd->len),4));
80959853Sps	roff = (struct nfs_readdir_off *)buf;
810240780Smav	fp->off = cookie = ntohl(roff->cookie);
81159853Sps	return 0;
81259853Sps}
813212125Srmacklem#else	/* !OLD_NFSV2 */
814212125Srmacklem/*
815212125Srmacklem * Fetch the root file handle (call mount daemon)
816212125Srmacklem * Return zero or error number.
817212125Srmacklem */
818212125Srmacklemint
819212125Srmacklemnfs_getrootfh(struct iodesc *d, char *path, uint32_t *fhlenp, u_char *fhp)
820212125Srmacklem{
821212125Srmacklem	int len;
822212125Srmacklem	struct args {
823212125Srmacklem		uint32_t len;
824212125Srmacklem		char path[FNAME_SIZE];
825212125Srmacklem	} *args;
826212125Srmacklem	struct repl {
827212125Srmacklem		uint32_t errno;
828212125Srmacklem		uint32_t fhsize;
829212125Srmacklem		u_char fh[NFS_V3MAXFHSIZE];
830212125Srmacklem		uint32_t authcnt;
831212125Srmacklem		uint32_t auth[7];
832212125Srmacklem	} *repl;
833212125Srmacklem	struct {
834212125Srmacklem		uint32_t h[RPC_HEADER_WORDS];
835212125Srmacklem		struct args d;
836212125Srmacklem	} sdata;
837212125Srmacklem	struct {
838212125Srmacklem		uint32_t h[RPC_HEADER_WORDS];
839212125Srmacklem		struct repl d;
840212125Srmacklem	} rdata;
841212125Srmacklem	size_t cc;
842212125Srmacklem
843212125Srmacklem#ifdef NFS_DEBUG
844212125Srmacklem	if (debug)
845212125Srmacklem		printf("nfs_getrootfh: %s\n", path);
846212125Srmacklem#endif
847212125Srmacklem
848212125Srmacklem	args = &sdata.d;
849212125Srmacklem	repl = &rdata.d;
850212125Srmacklem
851212125Srmacklem	bzero(args, sizeof(*args));
852212125Srmacklem	len = strlen(path);
853212125Srmacklem	if (len > sizeof(args->path))
854212125Srmacklem		len = sizeof(args->path);
855212125Srmacklem	args->len = htonl(len);
856212125Srmacklem	bcopy(path, args->path, len);
857212125Srmacklem	len = sizeof(uint32_t) + roundup(len, sizeof(uint32_t));
858212125Srmacklem
859212125Srmacklem	cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER3, RPCMNT_MOUNT,
860212125Srmacklem	    args, len, repl, sizeof(*repl));
861212125Srmacklem	if (cc == -1)
862212125Srmacklem		/* errno was set by rpc_call */
863212125Srmacklem		return (errno);
864212125Srmacklem	if (cc < 2 * sizeof (uint32_t))
865212125Srmacklem		return (EBADRPC);
866212125Srmacklem	if (repl->errno != 0)
867212125Srmacklem		return (ntohl(repl->errno));
868212125Srmacklem	*fhlenp = ntohl(repl->fhsize);
869212125Srmacklem	bcopy(repl->fh, fhp, *fhlenp);
870212125Srmacklem	return (0);
871212125Srmacklem}
872212125Srmacklem
873212125Srmacklem/*
874212125Srmacklem * Lookup a file.  Store handle and attributes.
875212125Srmacklem * Return zero or error number.
876212125Srmacklem */
877212125Srmacklemint
878212125Srmacklemnfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd)
879212125Srmacklem{
880212125Srmacklem	int len, rlen, pos;
881212125Srmacklem	struct args {
882212125Srmacklem		uint32_t fhsize;
883212125Srmacklem		uint32_t fhplusname[1 +
884212125Srmacklem		    (NFS_V3MAXFHSIZE + FNAME_SIZE) / sizeof(uint32_t)];
885212125Srmacklem	} *args;
886212125Srmacklem	struct repl {
887212125Srmacklem		uint32_t errno;
888212125Srmacklem		uint32_t fhsize;
889212125Srmacklem		uint32_t fhplusattr[(NFS_V3MAXFHSIZE +
890212125Srmacklem		    2 * (sizeof(uint32_t) +
891212125Srmacklem		    sizeof(struct nfsv3_fattrs))) / sizeof(uint32_t)];
892212125Srmacklem	} *repl;
893212125Srmacklem	struct {
894212125Srmacklem		uint32_t h[RPC_HEADER_WORDS];
895212125Srmacklem		struct args d;
896212125Srmacklem	} sdata;
897212125Srmacklem	struct {
898212125Srmacklem		uint32_t h[RPC_HEADER_WORDS];
899212125Srmacklem		struct repl d;
900212125Srmacklem	} rdata;
901212125Srmacklem	ssize_t cc;
902212125Srmacklem
903212125Srmacklem#ifdef NFS_DEBUG
904212125Srmacklem	if (debug)
905212125Srmacklem		printf("lookupfh: called\n");
906212125Srmacklem#endif
907212125Srmacklem
908212125Srmacklem	args = &sdata.d;
909212125Srmacklem	repl = &rdata.d;
910212125Srmacklem
911212125Srmacklem	bzero(args, sizeof(*args));
912212125Srmacklem	args->fhsize = htonl(d->fhsize);
913212125Srmacklem	bcopy(d->fh, args->fhplusname, d->fhsize);
914212125Srmacklem	len = strlen(name);
915212125Srmacklem	if (len > FNAME_SIZE)
916212125Srmacklem		len = FNAME_SIZE;
917212125Srmacklem	pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
918212125Srmacklem	args->fhplusname[pos++] = htonl(len);
919212125Srmacklem	bcopy(name, &args->fhplusname[pos], len);
920212125Srmacklem	len = sizeof(uint32_t) + pos * sizeof(uint32_t) +
921212125Srmacklem	    roundup(len, sizeof(uint32_t));
922212125Srmacklem
923212125Srmacklem	rlen = sizeof(*repl);
924212125Srmacklem
925212125Srmacklem	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_LOOKUP,
926212125Srmacklem	    args, len, repl, rlen);
927212125Srmacklem	if (cc == -1)
928212125Srmacklem		return (errno);		/* XXX - from rpc_call */
929212125Srmacklem	if (cc < 2 * sizeof(uint32_t))
930212125Srmacklem		return (EIO);
931212125Srmacklem	if (repl->errno != 0)
932212125Srmacklem		/* saerrno.h now matches NFS error numbers. */
933212125Srmacklem		return (ntohl(repl->errno));
934212125Srmacklem	newfd->fhsize = ntohl(repl->fhsize);
935212125Srmacklem	bcopy(repl->fhplusattr, &newfd->fh, newfd->fhsize);
936212125Srmacklem	pos = roundup(newfd->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
937212125Srmacklem	if (repl->fhplusattr[pos++] == 0)
938212125Srmacklem		return (EIO);
939212125Srmacklem	bcopy(&repl->fhplusattr[pos], &newfd->fa, sizeof(newfd->fa));
940212125Srmacklem	return (0);
941212125Srmacklem}
942212125Srmacklem
943212125Srmacklem#ifndef NFS_NOSYMLINK
944212125Srmacklem/*
945212125Srmacklem * Get the destination of a symbolic link.
946212125Srmacklem */
947212125Srmacklemint
948212125Srmacklemnfs_readlink(struct nfs_iodesc *d, char *buf)
949212125Srmacklem{
950212125Srmacklem	struct args {
951212125Srmacklem		uint32_t fhsize;
952212125Srmacklem		u_char fh[NFS_V3MAXFHSIZE];
953212125Srmacklem	} *args;
954212125Srmacklem	struct repl {
955212125Srmacklem		uint32_t errno;
956212125Srmacklem		uint32_t ok;
957212125Srmacklem		struct nfsv3_fattrs fa;
958212125Srmacklem		uint32_t len;
959212125Srmacklem		u_char path[NFS_MAXPATHLEN];
960212125Srmacklem	} *repl;
961212125Srmacklem	struct {
962212125Srmacklem		uint32_t h[RPC_HEADER_WORDS];
963212125Srmacklem		struct args d;
964212125Srmacklem	} sdata;
965212125Srmacklem	struct {
966212125Srmacklem		uint32_t h[RPC_HEADER_WORDS];
967212125Srmacklem		struct repl d;
968212125Srmacklem	} rdata;
969212125Srmacklem	ssize_t cc;
970212125Srmacklem
971212125Srmacklem#ifdef NFS_DEBUG
972212125Srmacklem	if (debug)
973212125Srmacklem		printf("readlink: called\n");
974212125Srmacklem#endif
975212125Srmacklem
976212125Srmacklem	args = &sdata.d;
977212125Srmacklem	repl = &rdata.d;
978212125Srmacklem
979212125Srmacklem	bzero(args, sizeof(*args));
980212125Srmacklem	args->fhsize = htonl(d->fhsize);
981212125Srmacklem	bcopy(d->fh, args->fh, d->fhsize);
982212125Srmacklem	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READLINK,
983212125Srmacklem	    args, sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)),
984212125Srmacklem	    repl, sizeof(*repl));
985212125Srmacklem	if (cc == -1)
986212125Srmacklem		return (errno);
987212125Srmacklem
988212125Srmacklem	if (cc < 2 * sizeof(uint32_t))
989212125Srmacklem		return (EIO);
990212125Srmacklem
991212125Srmacklem	if (repl->errno != 0)
992212125Srmacklem		return (ntohl(repl->errno));
993212125Srmacklem
994212125Srmacklem	if (repl->ok == 0)
995212125Srmacklem		return (EIO);
996212125Srmacklem
997212125Srmacklem	repl->len = ntohl(repl->len);
998212125Srmacklem	if (repl->len > NFS_MAXPATHLEN)
999212125Srmacklem		return (ENAMETOOLONG);
1000212125Srmacklem
1001212125Srmacklem	bcopy(repl->path, buf, repl->len);
1002212125Srmacklem	buf[repl->len] = 0;
1003212125Srmacklem	return (0);
1004212125Srmacklem}
1005212125Srmacklem#endif
1006212125Srmacklem
1007212125Srmacklem/*
1008212125Srmacklem * Read data from a file.
1009212125Srmacklem * Return transfer count or -1 (and set errno)
1010212125Srmacklem */
1011212125Srmacklemssize_t
1012212125Srmacklemnfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
1013212125Srmacklem{
1014212125Srmacklem	struct args {
1015212125Srmacklem		uint32_t fhsize;
1016212125Srmacklem		uint32_t fhoffcnt[NFS_V3MAXFHSIZE / sizeof(uint32_t) + 3];
1017212125Srmacklem	} *args;
1018212125Srmacklem	struct repl {
1019212125Srmacklem		uint32_t errno;
1020212125Srmacklem		uint32_t ok;
1021212125Srmacklem		struct nfsv3_fattrs fa;
1022212125Srmacklem		uint32_t count;
1023212125Srmacklem		uint32_t eof;
1024212125Srmacklem		uint32_t len;
1025212125Srmacklem		u_char data[NFSREAD_SIZE];
1026212125Srmacklem	} *repl;
1027212125Srmacklem	struct {
1028212125Srmacklem		uint32_t h[RPC_HEADER_WORDS];
1029212125Srmacklem		struct args d;
1030212125Srmacklem	} sdata;
1031212125Srmacklem	struct {
1032212125Srmacklem		uint32_t h[RPC_HEADER_WORDS];
1033212125Srmacklem		struct repl d;
1034212125Srmacklem	} rdata;
1035212125Srmacklem	size_t cc;
1036212125Srmacklem	long x;
1037212125Srmacklem	int hlen, rlen, pos;
1038212125Srmacklem
1039212125Srmacklem	args = &sdata.d;
1040212125Srmacklem	repl = &rdata.d;
1041212125Srmacklem
1042212125Srmacklem	bzero(args, sizeof(*args));
1043212125Srmacklem	args->fhsize = htonl(d->fhsize);
1044212125Srmacklem	bcopy(d->fh, args->fhoffcnt, d->fhsize);
1045212125Srmacklem	pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
1046212125Srmacklem	args->fhoffcnt[pos++] = 0;
1047212125Srmacklem	args->fhoffcnt[pos++] = htonl((uint32_t)off);
1048212125Srmacklem	if (len > NFSREAD_SIZE)
1049212125Srmacklem		len = NFSREAD_SIZE;
1050212125Srmacklem	args->fhoffcnt[pos] = htonl((uint32_t)len);
1051212125Srmacklem	hlen = sizeof(*repl) - NFSREAD_SIZE;
1052212125Srmacklem
1053212125Srmacklem	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READ,
1054212125Srmacklem	    args, 4 * sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)),
1055212125Srmacklem	    repl, sizeof(*repl));
1056212125Srmacklem	if (cc == -1)
1057212125Srmacklem		/* errno was already set by rpc_call */
1058212125Srmacklem		return (-1);
1059212125Srmacklem	if (cc < hlen) {
1060212125Srmacklem		errno = EBADRPC;
1061212125Srmacklem		return (-1);
1062212125Srmacklem	}
1063212125Srmacklem	if (repl->errno != 0) {
1064212125Srmacklem		errno = ntohl(repl->errno);
1065212125Srmacklem		return (-1);
1066212125Srmacklem	}
1067212125Srmacklem	rlen = cc - hlen;
1068212125Srmacklem	x = ntohl(repl->count);
1069212125Srmacklem	if (rlen < x) {
1070212125Srmacklem		printf("nfsread: short packet, %d < %ld\n", rlen, x);
1071212125Srmacklem		errno = EBADRPC;
1072212125Srmacklem		return (-1);
1073212125Srmacklem	}
1074212125Srmacklem	bcopy(repl->data, addr, x);
1075212125Srmacklem	return (x);
1076212125Srmacklem}
1077212125Srmacklem
1078212125Srmacklem/*
1079212125Srmacklem * Open a file.
1080212125Srmacklem * return zero or error number
1081212125Srmacklem */
1082212125Srmacklemint
1083212125Srmacklemnfs_open(const char *upath, struct open_file *f)
1084212125Srmacklem{
1085212125Srmacklem	struct iodesc *desc;
1086212125Srmacklem	struct nfs_iodesc *currfd;
1087212125Srmacklem	char buf[2 * NFS_V3MAXFHSIZE + 3];
1088212125Srmacklem	u_char *fh;
1089212125Srmacklem	char *cp;
1090212125Srmacklem	int i;
1091212125Srmacklem#ifndef NFS_NOSYMLINK
1092212125Srmacklem	struct nfs_iodesc *newfd;
1093212125Srmacklem	struct nfsv3_fattrs *fa;
1094212125Srmacklem	char *ncp;
1095212125Srmacklem	int c;
1096212125Srmacklem	char namebuf[NFS_MAXPATHLEN + 1];
1097212125Srmacklem	char linkbuf[NFS_MAXPATHLEN + 1];
1098212125Srmacklem	int nlinks = 0;
1099212125Srmacklem#endif
1100212125Srmacklem	int error;
1101212125Srmacklem	char *path;
1102212125Srmacklem
1103212125Srmacklem#ifdef NFS_DEBUG
1104212125Srmacklem 	if (debug)
1105212125Srmacklem 	    printf("nfs_open: %s (rootpath=%s)\n", upath, rootpath);
1106212125Srmacklem#endif
1107212125Srmacklem	if (!rootpath[0]) {
1108212125Srmacklem		printf("no rootpath, no nfs\n");
1109212125Srmacklem		return (ENXIO);
1110212125Srmacklem	}
1111212125Srmacklem
1112212125Srmacklem	/*
1113212125Srmacklem	 * This is silly - we should look at dv_type but that value is
1114212125Srmacklem	 * arch dependant and we can't use it here.
1115212125Srmacklem	 */
1116212125Srmacklem#ifndef __i386__
1117212125Srmacklem	if (strcmp(f->f_dev->dv_name, "net") != 0)
1118212125Srmacklem		return (EINVAL);
1119212125Srmacklem#else
1120212125Srmacklem	if (strcmp(f->f_dev->dv_name, "pxe") != 0)
1121212125Srmacklem		return (EINVAL);
1122212125Srmacklem#endif
1123212125Srmacklem
1124212125Srmacklem	if (!(desc = socktodesc(*(int *)(f->f_devdata))))
1125212125Srmacklem		return (EINVAL);
1126212125Srmacklem
1127212125Srmacklem	/* Bind to a reserved port. */
1128212125Srmacklem	desc->myport = htons(--rpc_port);
1129212125Srmacklem	desc->destip = rootip;
1130212125Srmacklem	if ((error = nfs_getrootfh(desc, rootpath, &nfs_root_node.fhsize,
1131212125Srmacklem	    nfs_root_node.fh)))
1132212125Srmacklem		return (error);
1133240774Smav	nfs_root_node.fa.fa_type  = htonl(NFDIR);
1134240774Smav	nfs_root_node.fa.fa_mode  = htonl(0755);
1135240774Smav	nfs_root_node.fa.fa_nlink = htonl(2);
1136212125Srmacklem	nfs_root_node.iodesc = desc;
1137212125Srmacklem
1138212125Srmacklem	fh = &nfs_root_node.fh[0];
1139212125Srmacklem	buf[0] = 'X';
1140212125Srmacklem	cp = &buf[1];
1141212125Srmacklem	for (i = 0; i < nfs_root_node.fhsize; i++, cp += 2)
1142212125Srmacklem		sprintf(cp, "%02x", fh[i]);
1143212125Srmacklem	sprintf(cp, "X");
1144212125Srmacklem	setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
1145212125Srmacklem	setenv("boot.nfsroot.path", rootpath, 1);
1146212125Srmacklem	setenv("boot.nfsroot.nfshandle", buf, 1);
1147212125Srmacklem	sprintf(buf, "%d", nfs_root_node.fhsize);
1148212125Srmacklem	setenv("boot.nfsroot.nfshandlelen", buf, 1);
1149212125Srmacklem
1150240774Smav	/* Allocate file system specific data structure */
1151240774Smav	currfd = malloc(sizeof(*newfd));
1152240774Smav	if (currfd == NULL) {
1153240774Smav		error = ENOMEM;
1154240774Smav		goto out;
1155240774Smav	}
1156212125Srmacklem#ifndef NFS_NOSYMLINK
1157240774Smav	bcopy(&nfs_root_node, currfd, sizeof(*currfd));
1158212125Srmacklem	newfd = 0;
1159212125Srmacklem
1160212125Srmacklem	cp = path = strdup(upath);
1161212125Srmacklem	if (path == NULL) {
1162212125Srmacklem		error = ENOMEM;
1163212125Srmacklem		goto out;
1164212125Srmacklem	}
1165212125Srmacklem	while (*cp) {
1166212125Srmacklem		/*
1167212125Srmacklem		 * Remove extra separators
1168212125Srmacklem		 */
1169212125Srmacklem		while (*cp == '/')
1170212125Srmacklem			cp++;
1171212125Srmacklem
1172212125Srmacklem		if (*cp == '\0')
1173212125Srmacklem			break;
1174212125Srmacklem		/*
1175212125Srmacklem		 * Check that current node is a directory.
1176212125Srmacklem		 */
1177212125Srmacklem		if (currfd->fa.fa_type != htonl(NFDIR)) {
1178212125Srmacklem			error = ENOTDIR;
1179212125Srmacklem			goto out;
1180212125Srmacklem		}
1181212125Srmacklem
1182212125Srmacklem		/* allocate file system specific data structure */
1183212125Srmacklem		newfd = malloc(sizeof(*newfd));
1184212125Srmacklem		if (newfd == NULL) {
1185212125Srmacklem			error = ENOMEM;
1186212125Srmacklem			goto out;
1187212125Srmacklem		}
1188212125Srmacklem		newfd->iodesc = currfd->iodesc;
1189212125Srmacklem
1190212125Srmacklem		/*
1191212125Srmacklem		 * Get next component of path name.
1192212125Srmacklem		 */
1193212125Srmacklem		{
1194212125Srmacklem			int len = 0;
1195212125Srmacklem
1196212125Srmacklem			ncp = cp;
1197212125Srmacklem			while ((c = *cp) != '\0' && c != '/') {
1198212125Srmacklem				if (++len > NFS_MAXNAMLEN) {
1199212125Srmacklem					error = ENOENT;
1200212125Srmacklem					goto out;
1201212125Srmacklem				}
1202212125Srmacklem				cp++;
1203212125Srmacklem			}
1204212125Srmacklem			*cp = '\0';
1205212125Srmacklem		}
1206212125Srmacklem
1207212125Srmacklem		/* lookup a file handle */
1208212125Srmacklem		error = nfs_lookupfh(currfd, ncp, newfd);
1209212125Srmacklem		*cp = c;
1210212125Srmacklem		if (error)
1211212125Srmacklem			goto out;
1212212125Srmacklem
1213212125Srmacklem		/*
1214212125Srmacklem		 * Check for symbolic link
1215212125Srmacklem		 */
1216212125Srmacklem		if (newfd->fa.fa_type == htonl(NFLNK)) {
1217212125Srmacklem			int link_len, len;
1218212125Srmacklem
1219212125Srmacklem			error = nfs_readlink(newfd, linkbuf);
1220212125Srmacklem			if (error)
1221212125Srmacklem				goto out;
1222212125Srmacklem
1223212125Srmacklem			link_len = strlen(linkbuf);
1224212125Srmacklem			len = strlen(cp);
1225212125Srmacklem
1226212125Srmacklem			if (link_len + len > MAXPATHLEN
1227212125Srmacklem			    || ++nlinks > MAXSYMLINKS) {
1228212125Srmacklem				error = ENOENT;
1229212125Srmacklem				goto out;
1230212125Srmacklem			}
1231212125Srmacklem
1232212125Srmacklem			bcopy(cp, &namebuf[link_len], len + 1);
1233212125Srmacklem			bcopy(linkbuf, namebuf, link_len);
1234212125Srmacklem
1235212125Srmacklem			/*
1236212125Srmacklem			 * If absolute pathname, restart at root.
1237212125Srmacklem			 * If relative pathname, restart at parent directory.
1238212125Srmacklem			 */
1239212125Srmacklem			cp = namebuf;
1240240774Smav			if (*cp == '/')
1241240774Smav				bcopy(&nfs_root_node, currfd, sizeof(*currfd));
1242212125Srmacklem
1243212125Srmacklem			free(newfd);
1244212125Srmacklem			newfd = 0;
1245212125Srmacklem
1246212125Srmacklem			continue;
1247212125Srmacklem		}
1248212125Srmacklem
1249240774Smav		free(currfd);
1250212125Srmacklem		currfd = newfd;
1251212125Srmacklem		newfd = 0;
1252212125Srmacklem	}
1253212125Srmacklem
1254212125Srmacklem	error = 0;
1255212125Srmacklem
1256212125Srmacklemout:
1257240881Skevlo	free(newfd);
1258240881Skevlo	free(path);
1259212125Srmacklem#else
1260240774Smav	currfd->iodesc = desc;
1261212125Srmacklem
1262240774Smav	error = nfs_lookupfh(&nfs_root_node, upath, currfd);
1263212125Srmacklem#endif
1264212125Srmacklem	if (!error) {
1265240774Smav		currfd->off = 0;
1266240780Smav		currfd->cookie = 0;
1267212125Srmacklem		f->f_fsdata = (void *)currfd;
1268212125Srmacklem		return (0);
1269212125Srmacklem	}
1270212125Srmacklem
1271212125Srmacklem#ifdef NFS_DEBUG
1272212125Srmacklem	if (debug)
1273212125Srmacklem		printf("nfs_open: %s lookupfh failed: %s\n",
1274212125Srmacklem		    path, strerror(error));
1275212125Srmacklem#endif
1276240774Smav	free(currfd);
1277212125Srmacklem
1278212125Srmacklem	return (error);
1279212125Srmacklem}
1280212125Srmacklem
1281212125Srmacklemint
1282212125Srmacklemnfs_close(struct open_file *f)
1283212125Srmacklem{
1284212125Srmacklem	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
1285212125Srmacklem
1286212125Srmacklem#ifdef NFS_DEBUG
1287212125Srmacklem	if (debug)
1288212125Srmacklem		printf("nfs_close: fp=0x%lx\n", (u_long)fp);
1289212125Srmacklem#endif
1290212125Srmacklem
1291240774Smav	if (fp)
1292212125Srmacklem		free(fp);
1293212125Srmacklem	f->f_fsdata = (void *)0;
1294212125Srmacklem
1295212125Srmacklem	return (0);
1296212125Srmacklem}
1297212125Srmacklem
1298212125Srmacklem/*
1299212125Srmacklem * read a portion of a file
1300212125Srmacklem */
1301212125Srmacklemint
1302212125Srmacklemnfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
1303212125Srmacklem{
1304212125Srmacklem	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
1305212125Srmacklem	ssize_t cc;
1306212125Srmacklem	char *addr = buf;
1307212125Srmacklem
1308212125Srmacklem#ifdef NFS_DEBUG
1309212125Srmacklem	if (debug)
1310212125Srmacklem		printf("nfs_read: size=%lu off=%d\n", (u_long)size,
1311212125Srmacklem		       (int)fp->off);
1312212125Srmacklem#endif
1313212125Srmacklem	while ((int)size > 0) {
1314212125Srmacklem		twiddle();
1315212125Srmacklem		cc = nfs_readdata(fp, fp->off, (void *)addr, size);
1316212125Srmacklem		/* XXX maybe should retry on certain errors */
1317212125Srmacklem		if (cc == -1) {
1318212125Srmacklem#ifdef NFS_DEBUG
1319212125Srmacklem			if (debug)
1320212125Srmacklem				printf("nfs_read: read: %s", strerror(errno));
1321212125Srmacklem#endif
1322212125Srmacklem			return (errno);	/* XXX - from nfs_readdata */
1323212125Srmacklem		}
1324212125Srmacklem		if (cc == 0) {
1325212125Srmacklem#ifdef NFS_DEBUG
1326212125Srmacklem			if (debug)
1327212125Srmacklem				printf("nfs_read: hit EOF unexpectantly");
1328212125Srmacklem#endif
1329212125Srmacklem			goto ret;
1330212125Srmacklem		}
1331212125Srmacklem		fp->off += cc;
1332212125Srmacklem		addr += cc;
1333212125Srmacklem		size -= cc;
1334212125Srmacklem	}
1335212125Srmacklemret:
1336212125Srmacklem	if (resid)
1337212125Srmacklem		*resid = size;
1338212125Srmacklem
1339212125Srmacklem	return (0);
1340212125Srmacklem}
1341212125Srmacklem
1342212125Srmacklem/*
1343212125Srmacklem * Not implemented.
1344212125Srmacklem */
1345212125Srmacklemint
1346212125Srmacklemnfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
1347212125Srmacklem{
1348212125Srmacklem	return (EROFS);
1349212125Srmacklem}
1350212125Srmacklem
1351212125Srmacklemoff_t
1352212125Srmacklemnfs_seek(struct open_file *f, off_t offset, int where)
1353212125Srmacklem{
1354212125Srmacklem	struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
1355212125Srmacklem	uint32_t size = ntohl(d->fa.fa_size.val[1]);
1356212125Srmacklem
1357212125Srmacklem	switch (where) {
1358212125Srmacklem	case SEEK_SET:
1359212125Srmacklem		d->off = offset;
1360212125Srmacklem		break;
1361212125Srmacklem	case SEEK_CUR:
1362212125Srmacklem		d->off += offset;
1363212125Srmacklem		break;
1364212125Srmacklem	case SEEK_END:
1365212125Srmacklem		d->off = size - offset;
1366212125Srmacklem		break;
1367212125Srmacklem	default:
1368212125Srmacklem		errno = EINVAL;
1369212125Srmacklem		return (-1);
1370212125Srmacklem	}
1371212125Srmacklem
1372212125Srmacklem	return (d->off);
1373212125Srmacklem}
1374212125Srmacklem
1375212125Srmacklem/* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5, NFSOCK=6, NFFIFO=7 */
1376212125Srmacklemint nfs_stat_types[9] = {
1377212125Srmacklem	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO, 0 };
1378212125Srmacklem
1379212125Srmacklemint
1380212125Srmacklemnfs_stat(struct open_file *f, struct stat *sb)
1381212125Srmacklem{
1382212125Srmacklem	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
1383212125Srmacklem	uint32_t ftype, mode;
1384212125Srmacklem
1385212125Srmacklem	ftype = ntohl(fp->fa.fa_type);
1386212125Srmacklem	mode  = ntohl(fp->fa.fa_mode);
1387212125Srmacklem	mode |= nfs_stat_types[ftype & 7];
1388212125Srmacklem
1389212125Srmacklem	sb->st_mode  = mode;
1390212125Srmacklem	sb->st_nlink = ntohl(fp->fa.fa_nlink);
1391212125Srmacklem	sb->st_uid   = ntohl(fp->fa.fa_uid);
1392212125Srmacklem	sb->st_gid   = ntohl(fp->fa.fa_gid);
1393212125Srmacklem	sb->st_size  = ntohl(fp->fa.fa_size.val[1]);
1394212125Srmacklem
1395212125Srmacklem	return (0);
1396212125Srmacklem}
1397212125Srmacklem
1398212125Srmacklemstatic int
1399212125Srmacklemnfs_readdir(struct open_file *f, struct dirent *d)
1400212125Srmacklem{
1401212125Srmacklem	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
1402212125Srmacklem	struct nfsv3_readdir_repl *repl;
1403212125Srmacklem	struct nfsv3_readdir_entry *rent;
1404212125Srmacklem	static char *buf;
1405240780Smav	static struct nfs_iodesc *pfp = NULL;
1406240780Smav	static uint64_t cookie = 0;
1407212125Srmacklem	size_t cc;
1408212125Srmacklem	int pos;
1409212125Srmacklem
1410212125Srmacklem	struct args {
1411212125Srmacklem		uint32_t fhsize;
1412212125Srmacklem		uint32_t fhpluscookie[5 + NFS_V3MAXFHSIZE];
1413212125Srmacklem	} *args;
1414212125Srmacklem	struct {
1415212125Srmacklem		uint32_t h[RPC_HEADER_WORDS];
1416212125Srmacklem		struct args d;
1417212125Srmacklem	} sdata;
1418212125Srmacklem	static struct {
1419212125Srmacklem		uint32_t h[RPC_HEADER_WORDS];
1420212125Srmacklem		u_char d[NFS_READDIRSIZE];
1421212125Srmacklem	} rdata;
1422212125Srmacklem
1423240780Smav	if (fp != pfp || fp->off != cookie) {
1424240780Smav		pfp = NULL;
1425212125Srmacklem	refill:
1426212125Srmacklem		args = &sdata.d;
1427212125Srmacklem		bzero(args, sizeof(*args));
1428212125Srmacklem
1429212125Srmacklem		args->fhsize = htonl(fp->fhsize);
1430212125Srmacklem		bcopy(fp->fh, args->fhpluscookie, fp->fhsize);
1431212125Srmacklem		pos = roundup(fp->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
1432240780Smav		args->fhpluscookie[pos++] = htonl(fp->off >> 32);
1433240780Smav		args->fhpluscookie[pos++] = htonl(fp->off);
1434240780Smav		args->fhpluscookie[pos++] = htonl(fp->cookie >> 32);
1435240780Smav		args->fhpluscookie[pos++] = htonl(fp->cookie);
1436212125Srmacklem		args->fhpluscookie[pos] = htonl(NFS_READDIRSIZE);
1437212125Srmacklem
1438212125Srmacklem		cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READDIR,
1439212125Srmacklem		    args, 6 * sizeof(uint32_t) +
1440212125Srmacklem		    roundup(fp->fhsize, sizeof(uint32_t)),
1441212125Srmacklem		    rdata.d, sizeof(rdata.d));
1442212125Srmacklem		buf  = rdata.d;
1443212125Srmacklem		repl = (struct nfsv3_readdir_repl *)buf;
1444212125Srmacklem		if (repl->errno != 0)
1445212125Srmacklem			return (ntohl(repl->errno));
1446240780Smav		pfp = fp;
1447240780Smav		cookie = fp->off;
1448240780Smav		fp->cookie = ((uint64_t)ntohl(repl->cookiev0) << 32) |
1449240780Smav		    ntohl(repl->cookiev1);
1450212125Srmacklem		buf += sizeof (struct nfsv3_readdir_repl);
1451212125Srmacklem	}
1452212125Srmacklem	rent = (struct nfsv3_readdir_entry *)buf;
1453212125Srmacklem
1454212125Srmacklem	if (rent->follows == 0) {
1455212125Srmacklem		/* fid0 is actually eof */
1456212125Srmacklem		if (rent->fid0 != 0) {
1457240780Smav			cookie = 0;
1458212125Srmacklem			return (ENOENT);
1459212125Srmacklem		}
1460212125Srmacklem		goto refill;
1461212125Srmacklem	}
1462212125Srmacklem
1463212125Srmacklem	d->d_namlen = ntohl(rent->len);
1464212125Srmacklem	bcopy(rent->nameplus, d->d_name, d->d_namlen);
1465212125Srmacklem	d->d_name[d->d_namlen] = '\0';
1466212125Srmacklem
1467212125Srmacklem	pos = roundup(d->d_namlen, sizeof(uint32_t)) / sizeof(uint32_t);
1468252468Smav	fp->off = cookie = ((uint64_t)ntohl(rent->nameplus[pos]) << 32) |
1469252468Smav	    ntohl(rent->nameplus[pos + 1]);
1470252468Smav	pos += 2;
1471212125Srmacklem	buf = (u_char *)&rent->nameplus[pos];
1472212125Srmacklem	return (0);
1473212125Srmacklem}
1474212125Srmacklem#endif	/* OLD_NFSV2 */
1475