nfs.c revision 124811
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.
638451Smsmith *
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.
1738451Smsmith *
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: head/lib/libstand/nfs.c 124811 2004-01-21 20:12:23Z jhb $");
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
5338451Smsmith/* Define our own NFS attributes without NQNFS stuff. */
5438451Smsmithstruct nfsv2_fattrs {
5538451Smsmith	n_long	fa_type;
5638451Smsmith	n_long	fa_mode;
5738451Smsmith	n_long	fa_nlink;
5838451Smsmith	n_long	fa_uid;
5938451Smsmith	n_long	fa_gid;
6038451Smsmith	n_long	fa_size;
6138451Smsmith	n_long	fa_blocksize;
6238451Smsmith	n_long	fa_rdev;
6338451Smsmith	n_long	fa_blocks;
6438451Smsmith	n_long	fa_fsid;
6538451Smsmith	n_long	fa_fileid;
6638451Smsmith	struct nfsv2_time fa_atime;
6738451Smsmith	struct nfsv2_time fa_mtime;
6838451Smsmith	struct nfsv2_time fa_ctime;
6938451Smsmith};
7038451Smsmith
7138451Smsmith
7238451Smsmithstruct nfs_read_args {
7338451Smsmith	u_char	fh[NFS_FHSIZE];
7438451Smsmith	n_long	off;
7538451Smsmith	n_long	len;
7638451Smsmith	n_long	xxx;			/* XXX what's this for? */
7738451Smsmith};
7838451Smsmith
7938451Smsmith/* Data part of nfs rpc reply (also the largest thing we receive) */
8038451Smsmith#define NFSREAD_SIZE 1024
8138451Smsmithstruct nfs_read_repl {
8238451Smsmith	n_long	errno;
8338451Smsmith	struct	nfsv2_fattrs fa;
8438451Smsmith	n_long	count;
8538451Smsmith	u_char	data[NFSREAD_SIZE];
8638451Smsmith};
8738451Smsmith
8838451Smsmith#ifndef NFS_NOSYMLINK
8938451Smsmithstruct nfs_readlnk_repl {
9038451Smsmith	n_long	errno;
9138451Smsmith	n_long	len;
9238451Smsmith	char	path[NFS_MAXPATHLEN];
9338451Smsmith};
9438451Smsmith#endif
9538451Smsmith
9659853Spsstruct nfs_readdir_args {
9759853Sps	u_char	fh[NFS_FHSIZE];
9859853Sps	n_long	cookie;
9959853Sps	n_long	count;
10059853Sps};
10159853Sps
10259853Spsstruct nfs_readdir_data {
10359853Sps	n_long	fileid;
10459853Sps	n_long	len;
10559853Sps	char	name[0];
10659853Sps};
10759853Sps
10859853Spsstruct nfs_readdir_off {
10959853Sps	n_long	cookie;
11059853Sps	n_long	follows;
11159853Sps};
11259853Sps
11338451Smsmithstruct nfs_iodesc {
11438451Smsmith	struct	iodesc	*iodesc;
11538451Smsmith	off_t	off;
11638451Smsmith	u_char	fh[NFS_FHSIZE];
11738451Smsmith	struct nfsv2_fattrs fa;	/* all in network order */
11838451Smsmith};
11938451Smsmith
12038451Smsmith/*
12138451Smsmith * XXX interactions with tftp? See nfswrapper.c for a confusing
12238451Smsmith *     issue.
12338451Smsmith */
12439468Smsmithint		nfs_open(const char *path, struct open_file *f);
12538451Smsmithstatic int	nfs_close(struct open_file *f);
12638451Smsmithstatic int	nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
12738451Smsmithstatic int	nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid);
12838451Smsmithstatic off_t	nfs_seek(struct open_file *f, off_t offset, int where);
12938451Smsmithstatic int	nfs_stat(struct open_file *f, struct stat *sb);
13059853Spsstatic int	nfs_readdir(struct open_file *f, struct dirent *d);
13138451Smsmith
13265496Smsmithstruct	nfs_iodesc nfs_root_node;
13359824Sps
13438451Smsmithstruct fs_ops nfs_fsops = {
13559766Sjlemon	"nfs",
13659766Sjlemon	nfs_open,
13759766Sjlemon	nfs_close,
13859766Sjlemon	nfs_read,
13959766Sjlemon	nfs_write,
14059766Sjlemon	nfs_seek,
14159766Sjlemon	nfs_stat,
14259853Sps	nfs_readdir
14338451Smsmith};
14438451Smsmith
14538451Smsmith/*
14638451Smsmith * Fetch the root file handle (call mount daemon)
14738451Smsmith * Return zero or error number.
14838451Smsmith */
14938451Smsmithint
15038451Smsmithnfs_getrootfh(d, path, fhp)
15192913Sobrien	struct iodesc *d;
15238451Smsmith	char *path;
15338451Smsmith	u_char *fhp;
15438451Smsmith{
15592913Sobrien	int len;
15638451Smsmith	struct args {
15738451Smsmith		n_long	len;
15838451Smsmith		char	path[FNAME_SIZE];
15938451Smsmith	} *args;
16038451Smsmith	struct repl {
16138451Smsmith		n_long	errno;
16238451Smsmith		u_char	fh[NFS_FHSIZE];
16338451Smsmith	} *repl;
16438451Smsmith	struct {
16538451Smsmith		n_long	h[RPC_HEADER_WORDS];
16638451Smsmith		struct args d;
16738451Smsmith	} sdata;
16838451Smsmith	struct {
16938451Smsmith		n_long	h[RPC_HEADER_WORDS];
17038451Smsmith		struct repl d;
17138451Smsmith	} rdata;
17238451Smsmith	size_t cc;
17338451Smsmith
17438451Smsmith#ifdef NFS_DEBUG
17538451Smsmith	if (debug)
17638451Smsmith		printf("nfs_getrootfh: %s\n", path);
17738451Smsmith#endif
17838451Smsmith
17938451Smsmith	args = &sdata.d;
18038451Smsmith	repl = &rdata.d;
18138451Smsmith
18238451Smsmith	bzero(args, sizeof(*args));
18338451Smsmith	len = strlen(path);
18438451Smsmith	if (len > sizeof(args->path))
18538451Smsmith		len = sizeof(args->path);
18638451Smsmith	args->len = htonl(len);
18738451Smsmith	bcopy(path, args->path, len);
18838451Smsmith	len = 4 + roundup(len, 4);
18938451Smsmith
19038451Smsmith	cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT,
19138451Smsmith	    args, len, repl, sizeof(*repl));
19238451Smsmith	if (cc == -1) {
19338451Smsmith		/* errno was set by rpc_call */
19438451Smsmith		return (errno);
19538451Smsmith	}
19638451Smsmith	if (cc < 4)
19738451Smsmith		return (EBADRPC);
19838451Smsmith	if (repl->errno)
19938451Smsmith		return (ntohl(repl->errno));
20038451Smsmith	bcopy(repl->fh, fhp, sizeof(repl->fh));
20138451Smsmith	return (0);
20238451Smsmith}
20338451Smsmith
20438451Smsmith/*
20538451Smsmith * Lookup a file.  Store handle and attributes.
20638451Smsmith * Return zero or error number.
20738451Smsmith */
20838451Smsmithint
20938451Smsmithnfs_lookupfh(d, name, newfd)
21038451Smsmith	struct nfs_iodesc *d;
21139468Smsmith	const char *name;
21238451Smsmith	struct nfs_iodesc *newfd;
21338451Smsmith{
21492913Sobrien	int len, rlen;
21538451Smsmith	struct args {
21638451Smsmith		u_char	fh[NFS_FHSIZE];
21738451Smsmith		n_long	len;
21838451Smsmith		char	name[FNAME_SIZE];
21938451Smsmith	} *args;
22038451Smsmith	struct repl {
22138451Smsmith		n_long	errno;
22238451Smsmith		u_char	fh[NFS_FHSIZE];
22338451Smsmith		struct	nfsv2_fattrs fa;
22438451Smsmith	} *repl;
22538451Smsmith	struct {
22638451Smsmith		n_long	h[RPC_HEADER_WORDS];
22738451Smsmith		struct args d;
22838451Smsmith	} sdata;
22938451Smsmith	struct {
23038451Smsmith		n_long	h[RPC_HEADER_WORDS];
23138451Smsmith		struct repl d;
23238451Smsmith	} rdata;
23338451Smsmith	ssize_t cc;
23438451Smsmith
23538451Smsmith#ifdef NFS_DEBUG
23638451Smsmith	if (debug)
23738451Smsmith		printf("lookupfh: called\n");
23838451Smsmith#endif
23938451Smsmith
24038451Smsmith	args = &sdata.d;
24138451Smsmith	repl = &rdata.d;
24238451Smsmith
24338451Smsmith	bzero(args, sizeof(*args));
24438451Smsmith	bcopy(d->fh, args->fh, sizeof(args->fh));
24538451Smsmith	len = strlen(name);
24638451Smsmith	if (len > sizeof(args->name))
24738451Smsmith		len = sizeof(args->name);
24838451Smsmith	bcopy(name, args->name, len);
24938451Smsmith	args->len = htonl(len);
25038451Smsmith	len = 4 + roundup(len, 4);
25138451Smsmith	len += NFS_FHSIZE;
25238451Smsmith
25338451Smsmith	rlen = sizeof(*repl);
25438451Smsmith
25538451Smsmith	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_LOOKUP,
25638451Smsmith	    args, len, repl, rlen);
25738451Smsmith	if (cc == -1)
25838451Smsmith		return (errno);		/* XXX - from rpc_call */
25938451Smsmith	if (cc < 4)
26038451Smsmith		return (EIO);
26138451Smsmith	if (repl->errno) {
26238451Smsmith		/* saerrno.h now matches NFS error numbers. */
26338451Smsmith		return (ntohl(repl->errno));
26438451Smsmith	}
26538451Smsmith	bcopy( repl->fh, &newfd->fh, sizeof(newfd->fh));
26638451Smsmith	bcopy(&repl->fa, &newfd->fa, sizeof(newfd->fa));
26738451Smsmith	return (0);
26838451Smsmith}
26938451Smsmith
27038451Smsmith#ifndef NFS_NOSYMLINK
27138451Smsmith/*
27238451Smsmith * Get the destination of a symbolic link.
27338451Smsmith */
27438451Smsmithint
27538451Smsmithnfs_readlink(d, buf)
27638451Smsmith	struct nfs_iodesc *d;
27738451Smsmith	char *buf;
27838451Smsmith{
27938451Smsmith	struct {
28038451Smsmith		n_long	h[RPC_HEADER_WORDS];
28138451Smsmith		u_char fh[NFS_FHSIZE];
28238451Smsmith	} sdata;
28338451Smsmith	struct {
28438451Smsmith		n_long	h[RPC_HEADER_WORDS];
28538451Smsmith		struct nfs_readlnk_repl d;
28638451Smsmith	} rdata;
28738451Smsmith	ssize_t cc;
28838451Smsmith
28938451Smsmith#ifdef NFS_DEBUG
29038451Smsmith	if (debug)
29138451Smsmith		printf("readlink: called\n");
29238451Smsmith#endif
29338451Smsmith
29438451Smsmith	bcopy(d->fh, sdata.fh, NFS_FHSIZE);
29538451Smsmith	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READLINK,
29638451Smsmith		      sdata.fh, NFS_FHSIZE,
29738451Smsmith		      &rdata.d, sizeof(rdata.d));
29838451Smsmith	if (cc == -1)
29938451Smsmith		return (errno);
30038451Smsmith
30138451Smsmith	if (cc < 4)
30238451Smsmith		return (EIO);
30338451Smsmith
30438451Smsmith	if (rdata.d.errno)
30538451Smsmith		return (ntohl(rdata.d.errno));
30638451Smsmith
30738451Smsmith	rdata.d.len = ntohl(rdata.d.len);
30838451Smsmith	if (rdata.d.len > NFS_MAXPATHLEN)
30938451Smsmith		return (ENAMETOOLONG);
31038451Smsmith
31138451Smsmith	bcopy(rdata.d.path, buf, rdata.d.len);
31238451Smsmith	buf[rdata.d.len] = 0;
31338451Smsmith	return (0);
31438451Smsmith}
31538451Smsmith#endif
31638451Smsmith
31738451Smsmith/*
31838451Smsmith * Read data from a file.
31938451Smsmith * Return transfer count or -1 (and set errno)
32038451Smsmith */
32138451Smsmithssize_t
32238451Smsmithnfs_readdata(d, off, addr, len)
32338451Smsmith	struct nfs_iodesc *d;
32438451Smsmith	off_t off;
32538451Smsmith	void *addr;
32638451Smsmith	size_t len;
32738451Smsmith{
32838451Smsmith	struct nfs_read_args *args;
32938451Smsmith	struct nfs_read_repl *repl;
33038451Smsmith	struct {
33138451Smsmith		n_long	h[RPC_HEADER_WORDS];
33238451Smsmith		struct nfs_read_args d;
33338451Smsmith	} sdata;
33438451Smsmith	struct {
33538451Smsmith		n_long	h[RPC_HEADER_WORDS];
33638451Smsmith		struct nfs_read_repl d;
33738451Smsmith	} rdata;
33838451Smsmith	size_t cc;
33938451Smsmith	long x;
34038451Smsmith	int hlen, rlen;
34138451Smsmith
34238451Smsmith	args = &sdata.d;
34338451Smsmith	repl = &rdata.d;
34438451Smsmith
34538451Smsmith	bcopy(d->fh, args->fh, NFS_FHSIZE);
34638451Smsmith	args->off = htonl((n_long)off);
34738451Smsmith	if (len > NFSREAD_SIZE)
34838451Smsmith		len = NFSREAD_SIZE;
34938451Smsmith	args->len = htonl((n_long)len);
35038451Smsmith	args->xxx = htonl((n_long)0);
35138451Smsmith	hlen = sizeof(*repl) - NFSREAD_SIZE;
35238451Smsmith
35338451Smsmith	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READ,
35438451Smsmith	    args, sizeof(*args),
35538451Smsmith	    repl, sizeof(*repl));
35638451Smsmith	if (cc == -1) {
35738451Smsmith		/* errno was already set by rpc_call */
35838451Smsmith		return (-1);
35938451Smsmith	}
36038451Smsmith	if (cc < hlen) {
36138451Smsmith		errno = EBADRPC;
36238451Smsmith		return (-1);
36338451Smsmith	}
36438451Smsmith	if (repl->errno) {
36538451Smsmith		errno = ntohl(repl->errno);
36638451Smsmith		return (-1);
36738451Smsmith	}
36838451Smsmith	rlen = cc - hlen;
36938451Smsmith	x = ntohl(repl->count);
37038451Smsmith	if (rlen < x) {
37138451Smsmith		printf("nfsread: short packet, %d < %ld\n", rlen, x);
37238451Smsmith		errno = EBADRPC;
37338451Smsmith		return(-1);
37438451Smsmith	}
37538451Smsmith	bcopy(repl->data, addr, x);
37638451Smsmith	return (x);
37738451Smsmith}
37838451Smsmith
37938451Smsmith/*
38038451Smsmith * Open a file.
38138451Smsmith * return zero or error number
38238451Smsmith */
38338451Smsmithint
38439468Smsmithnfs_open(upath, f)
38539468Smsmith	const char *upath;
38638451Smsmith	struct open_file *f;
38738451Smsmith{
38838451Smsmith	struct iodesc *desc;
38938451Smsmith	struct nfs_iodesc *currfd;
390101112Sjake	char buf[2 * NFS_FHSIZE + 3];
391101112Sjake	u_char *fh;
392101112Sjake	char *cp;
393101112Sjake	int i;
39438451Smsmith#ifndef NFS_NOSYMLINK
39538451Smsmith	struct nfs_iodesc *newfd;
39638451Smsmith	struct nfsv2_fattrs *fa;
397101112Sjake	char *ncp;
39892913Sobrien	int c;
39938451Smsmith	char namebuf[NFS_MAXPATHLEN + 1];
40038451Smsmith	char linkbuf[NFS_MAXPATHLEN + 1];
40138451Smsmith	int nlinks = 0;
40238451Smsmith#endif
40338451Smsmith	int error;
40439468Smsmith	char *path;
40538451Smsmith
40638451Smsmith#ifdef NFS_DEBUG
40738451Smsmith 	if (debug)
40838451Smsmith 	    printf("nfs_open: %s (rootpath=%s)\n", path, rootpath);
40938451Smsmith#endif
41038451Smsmith	if (!rootpath[0]) {
41138451Smsmith		printf("no rootpath, no nfs\n");
41238451Smsmith		return (ENXIO);
41338451Smsmith	}
41438451Smsmith
415111776Smarcel#ifndef __i386__
41699558Sjake	if (strcmp(f->f_dev->dv_name, "net") != 0)
41799558Sjake		return(EINVAL);
41899558Sjake#endif
419111776Smarcel
42038451Smsmith	if (!(desc = socktodesc(*(int *)(f->f_devdata))))
42138451Smsmith		return(EINVAL);
42238451Smsmith
42338451Smsmith	/* Bind to a reserved port. */
42438451Smsmith	desc->myport = htons(--rpc_port);
42538451Smsmith	desc->destip = rootip;
42638451Smsmith	if ((error = nfs_getrootfh(desc, rootpath, nfs_root_node.fh)))
42738451Smsmith		return (error);
42838451Smsmith	nfs_root_node.iodesc = desc;
42938451Smsmith
430101112Sjake	fh = &nfs_root_node.fh[0];
431101112Sjake	buf[0] = 'X';
432101112Sjake	cp = &buf[1];
433101112Sjake	for (i = 0; i < NFS_FHSIZE; i++, cp += 2)
434101112Sjake		sprintf(cp, "%02x", fh[i]);
435101112Sjake	sprintf(cp, "X");
436101112Sjake	setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
437101112Sjake	setenv("boot.nfsroot.path", rootpath, 1);
438101112Sjake	setenv("boot.nfsroot.nfshandle", buf, 1);
439101112Sjake
44038451Smsmith#ifndef NFS_NOSYMLINK
44138451Smsmith	/* Fake up attributes for the root dir. */
44238451Smsmith	fa = &nfs_root_node.fa;
44338451Smsmith	fa->fa_type  = htonl(NFDIR);
44438451Smsmith	fa->fa_mode  = htonl(0755);
44538451Smsmith	fa->fa_nlink = htonl(2);
44638451Smsmith
44738451Smsmith	currfd = &nfs_root_node;
44838451Smsmith	newfd = 0;
44938451Smsmith
45039468Smsmith	cp = path = strdup(upath);
45139468Smsmith	if (path == NULL) {
45239468Smsmith	    error = ENOMEM;
45339468Smsmith	    goto out;
45439468Smsmith	}
45538451Smsmith	while (*cp) {
45638451Smsmith		/*
45738451Smsmith		 * Remove extra separators
45838451Smsmith		 */
45938451Smsmith		while (*cp == '/')
46038451Smsmith			cp++;
46138451Smsmith
46238451Smsmith		if (*cp == '\0')
46338451Smsmith			break;
46438451Smsmith		/*
46538451Smsmith		 * Check that current node is a directory.
46638451Smsmith		 */
46738451Smsmith		if (currfd->fa.fa_type != htonl(NFDIR)) {
46838451Smsmith			error = ENOTDIR;
46938451Smsmith			goto out;
47038451Smsmith		}
47138451Smsmith
47238451Smsmith		/* allocate file system specific data structure */
47338451Smsmith		newfd = malloc(sizeof(*newfd));
47438451Smsmith		newfd->iodesc = currfd->iodesc;
47538451Smsmith		newfd->off = 0;
47638451Smsmith
47738451Smsmith		/*
47838451Smsmith		 * Get next component of path name.
47938451Smsmith		 */
48038451Smsmith		{
48192913Sobrien			int len = 0;
48238451Smsmith
48338451Smsmith			ncp = cp;
48438451Smsmith			while ((c = *cp) != '\0' && c != '/') {
48538451Smsmith				if (++len > NFS_MAXNAMLEN) {
48638451Smsmith					error = ENOENT;
48738451Smsmith					goto out;
48838451Smsmith				}
48938451Smsmith				cp++;
49038451Smsmith			}
49138451Smsmith			*cp = '\0';
49238451Smsmith		}
49338451Smsmith
49438451Smsmith		/* lookup a file handle */
49538451Smsmith		error = nfs_lookupfh(currfd, ncp, newfd);
49638451Smsmith		*cp = c;
49738451Smsmith		if (error)
49838451Smsmith			goto out;
49938451Smsmith
50038451Smsmith		/*
50138451Smsmith		 * Check for symbolic link
50238451Smsmith		 */
50338451Smsmith		if (newfd->fa.fa_type == htonl(NFLNK)) {
50438451Smsmith			int link_len, len;
50538451Smsmith
50638451Smsmith			error = nfs_readlink(newfd, linkbuf);
50738451Smsmith			if (error)
50838451Smsmith				goto out;
50938451Smsmith
51038451Smsmith			link_len = strlen(linkbuf);
51138451Smsmith			len = strlen(cp);
51238451Smsmith
51338451Smsmith			if (link_len + len > MAXPATHLEN
51438451Smsmith			    || ++nlinks > MAXSYMLINKS) {
51538451Smsmith				error = ENOENT;
51638451Smsmith				goto out;
51738451Smsmith			}
51838451Smsmith
51938451Smsmith			bcopy(cp, &namebuf[link_len], len + 1);
52038451Smsmith			bcopy(linkbuf, namebuf, link_len);
52138451Smsmith
52238451Smsmith			/*
52338451Smsmith			 * If absolute pathname, restart at root.
52438451Smsmith			 * If relative pathname, restart at parent directory.
52538451Smsmith			 */
52638451Smsmith			cp = namebuf;
52738451Smsmith			if (*cp == '/') {
52838451Smsmith				if (currfd != &nfs_root_node)
52938451Smsmith					free(currfd);
53038451Smsmith				currfd = &nfs_root_node;
53138451Smsmith			}
53238451Smsmith
53338451Smsmith			free(newfd);
53438451Smsmith			newfd = 0;
53538451Smsmith
53638451Smsmith			continue;
53738451Smsmith		}
53838451Smsmith
53938451Smsmith		if (currfd != &nfs_root_node)
54038451Smsmith			free(currfd);
54138451Smsmith		currfd = newfd;
54238451Smsmith		newfd = 0;
54338451Smsmith	}
54438451Smsmith
54538451Smsmith	error = 0;
54638451Smsmith
54738451Smsmithout:
54838451Smsmith	if (newfd)
54938451Smsmith		free(newfd);
55039468Smsmith	if (path)
55139468Smsmith		free(path);
55238451Smsmith#else
55338451Smsmith        /* allocate file system specific data structure */
55438451Smsmith        currfd = malloc(sizeof(*currfd));
55538451Smsmith        currfd->iodesc = desc;
55638451Smsmith        currfd->off = 0;
55738451Smsmith
55839468Smsmith        error = nfs_lookupfh(&nfs_root_node, upath, currfd);
55938451Smsmith#endif
56038451Smsmith	if (!error) {
56138451Smsmith		f->f_fsdata = (void *)currfd;
56238451Smsmith		return (0);
56338451Smsmith	}
56438451Smsmith
56538451Smsmith#ifdef NFS_DEBUG
56638451Smsmith	if (debug)
56738451Smsmith		printf("nfs_open: %s lookupfh failed: %s\n",
56838451Smsmith		    path, strerror(error));
56938451Smsmith#endif
57038451Smsmith#ifndef NFS_NOSYMLINK
57138451Smsmith	if (currfd != &nfs_root_node)
57238451Smsmith#endif
57338451Smsmith		free(currfd);
57438451Smsmith
57538451Smsmith	return (error);
57638451Smsmith}
57738451Smsmith
57838451Smsmithint
57938451Smsmithnfs_close(f)
58038451Smsmith	struct open_file *f;
58138451Smsmith{
58292913Sobrien	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
58338451Smsmith
58438451Smsmith#ifdef NFS_DEBUG
58538451Smsmith	if (debug)
58638451Smsmith		printf("nfs_close: fp=0x%lx\n", (u_long)fp);
58738451Smsmith#endif
58838451Smsmith
58959824Sps	if (fp != &nfs_root_node && fp)
59038451Smsmith		free(fp);
59138451Smsmith	f->f_fsdata = (void *)0;
59238451Smsmith
59338451Smsmith	return (0);
59438451Smsmith}
59538451Smsmith
59638451Smsmith/*
59738451Smsmith * read a portion of a file
59838451Smsmith */
59938451Smsmithint
60038451Smsmithnfs_read(f, buf, size, resid)
60138451Smsmith	struct open_file *f;
60238451Smsmith	void *buf;
60338451Smsmith	size_t size;
60438451Smsmith	size_t *resid;	/* out */
60538451Smsmith{
60692913Sobrien	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
60792913Sobrien	ssize_t cc;
60892913Sobrien	char *addr = buf;
60938451Smsmith
61038451Smsmith#ifdef NFS_DEBUG
61138451Smsmith	if (debug)
61238451Smsmith		printf("nfs_read: size=%lu off=%d\n", (u_long)size,
61338451Smsmith		       (int)fp->off);
61438451Smsmith#endif
61538451Smsmith	while ((int)size > 0) {
61638451Smsmith		twiddle();
61738451Smsmith		cc = nfs_readdata(fp, fp->off, (void *)addr, size);
61838451Smsmith		/* XXX maybe should retry on certain errors */
61938451Smsmith		if (cc == -1) {
62038451Smsmith#ifdef NFS_DEBUG
62138451Smsmith			if (debug)
62238451Smsmith				printf("nfs_read: read: %s", strerror(errno));
62338451Smsmith#endif
62438451Smsmith			return (errno);	/* XXX - from nfs_readdata */
62538451Smsmith		}
62638451Smsmith		if (cc == 0) {
62738451Smsmith#ifdef NFS_DEBUG
62838451Smsmith			if (debug)
62938451Smsmith				printf("nfs_read: hit EOF unexpectantly");
63038451Smsmith#endif
63138451Smsmith			goto ret;
63238451Smsmith		}
63338451Smsmith		fp->off += cc;
63438451Smsmith		addr += cc;
63538451Smsmith		size -= cc;
63638451Smsmith	}
63738451Smsmithret:
63838451Smsmith	if (resid)
63938451Smsmith		*resid = size;
64038451Smsmith
64138451Smsmith	return (0);
64238451Smsmith}
64338451Smsmith
64438451Smsmith/*
64538451Smsmith * Not implemented.
64638451Smsmith */
64738451Smsmithint
64838451Smsmithnfs_write(f, buf, size, resid)
64938451Smsmith	struct open_file *f;
65038451Smsmith	void *buf;
65138451Smsmith	size_t size;
65238451Smsmith	size_t *resid;	/* out */
65338451Smsmith{
65438451Smsmith	return (EROFS);
65538451Smsmith}
65638451Smsmith
65738451Smsmithoff_t
65838451Smsmithnfs_seek(f, offset, where)
65938451Smsmith	struct open_file *f;
66038451Smsmith	off_t offset;
66138451Smsmith	int where;
66238451Smsmith{
66392913Sobrien	struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
66438451Smsmith	n_long size = ntohl(d->fa.fa_size);
66538451Smsmith
66638451Smsmith	switch (where) {
66738451Smsmith	case SEEK_SET:
66838451Smsmith		d->off = offset;
66938451Smsmith		break;
67038451Smsmith	case SEEK_CUR:
67138451Smsmith		d->off += offset;
67238451Smsmith		break;
67338451Smsmith	case SEEK_END:
67438451Smsmith		d->off = size - offset;
67538451Smsmith		break;
67638451Smsmith	default:
677124811Sjhb		errno = EINVAL;
67838451Smsmith		return (-1);
67938451Smsmith	}
68038451Smsmith
68138451Smsmith	return (d->off);
68238451Smsmith}
68338451Smsmith
68438451Smsmith/* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 */
68538451Smsmithint nfs_stat_types[8] = {
68638451Smsmith	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 0 };
68738451Smsmith
68838451Smsmithint
68938451Smsmithnfs_stat(f, sb)
69038451Smsmith	struct open_file *f;
69138451Smsmith	struct stat *sb;
69238451Smsmith{
69338451Smsmith	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
69492913Sobrien	n_long ftype, mode;
69538451Smsmith
69638451Smsmith	ftype = ntohl(fp->fa.fa_type);
69738451Smsmith	mode  = ntohl(fp->fa.fa_mode);
69838451Smsmith	mode |= nfs_stat_types[ftype & 7];
69938451Smsmith
70038451Smsmith	sb->st_mode  = mode;
70138451Smsmith	sb->st_nlink = ntohl(fp->fa.fa_nlink);
70238451Smsmith	sb->st_uid   = ntohl(fp->fa.fa_uid);
70338451Smsmith	sb->st_gid   = ntohl(fp->fa.fa_gid);
70438451Smsmith	sb->st_size  = ntohl(fp->fa.fa_size);
70538451Smsmith
70638451Smsmith	return (0);
70738451Smsmith}
70859853Sps
70959853Spsstatic int
71059853Spsnfs_readdir(struct open_file *f, struct dirent *d)
71159853Sps{
71292913Sobrien	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
71359853Sps	struct nfs_readdir_args *args;
71459853Sps	struct nfs_readdir_data *rd;
71559853Sps	struct nfs_readdir_off  *roff = NULL;
71659853Sps	static char *buf;
71759853Sps	static n_long cookie = 0;
71859853Sps	size_t cc;
71959853Sps	n_long eof;
72059853Sps
72159853Sps	struct {
72259853Sps		n_long h[RPC_HEADER_WORDS];
72359853Sps		struct nfs_readdir_args d;
72459853Sps	} sdata;
72559853Sps	static struct {
72659853Sps		n_long h[RPC_HEADER_WORDS];
72759853Sps		u_char d[NFS_READDIRSIZE];
72859853Sps	} rdata;
72959853Sps
73059853Sps	if (cookie == 0) {
73159853Sps	refill:
73259853Sps		args = &sdata.d;
73359853Sps		bzero(args, sizeof(*args));
73459853Sps
73559853Sps		bcopy(fp->fh, args->fh, NFS_FHSIZE);
73659853Sps		args->cookie = htonl(cookie);
73759853Sps		args->count  = htonl(NFS_READDIRSIZE);
73859853Sps
73959853Sps		cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READDIR,
74059853Sps			      args, sizeof(*args),
74159853Sps			      rdata.d, sizeof(rdata.d));
74259853Sps		buf  = rdata.d;
74359853Sps		roff = (struct nfs_readdir_off *)buf;
74459853Sps		if (ntohl(roff->cookie) != 0)
745124811Sjhb			return EIO;
74659853Sps	}
74759853Sps	roff = (struct nfs_readdir_off *)buf;
74859853Sps
74959853Sps	if (ntohl(roff->follows) == 0) {
75059853Sps		eof = ntohl((roff+1)->cookie);
75159853Sps		if (eof) {
75259853Sps			cookie = 0;
753124811Sjhb			return ENOENT;
75459853Sps		}
75559853Sps		goto refill;
75659853Sps	}
75759853Sps
75859853Sps	buf += sizeof(struct nfs_readdir_off);
75959853Sps	rd = (struct nfs_readdir_data *)buf;
76059853Sps	d->d_namlen = ntohl(rd->len);
76159853Sps	bcopy(rd->name, d->d_name, d->d_namlen);
76259853Sps	d->d_name[d->d_namlen] = '\0';
76359853Sps
76459853Sps	buf += (sizeof(struct nfs_readdir_data) + roundup(htonl(rd->len),4));
76559853Sps	roff = (struct nfs_readdir_off *)buf;
76659853Sps	cookie = ntohl(roff->cookie);
76759853Sps	return 0;
76859853Sps}
769