xref: /openbsd/sys/lib/libsa/nfs.c (revision 5a0183c5)
1*5a0183c5Spatrick /*	$OpenBSD: nfs.c,v 1.15 2017/05/08 20:13:46 patrick Exp $	*/
279dbd5ceSniklas /*	$NetBSD: nfs.c,v 1.19 1996/10/13 02:29:04 christos Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /*-
5df930be7Sderaadt  *  Copyright (c) 1993 John Brezak
6df930be7Sderaadt  *  All rights reserved.
7df930be7Sderaadt  *
8df930be7Sderaadt  *  Redistribution and use in source and binary forms, with or without
9df930be7Sderaadt  *  modification, are permitted provided that the following conditions
10df930be7Sderaadt  *  are met:
11df930be7Sderaadt  *  1. Redistributions of source code must retain the above copyright
12df930be7Sderaadt  *     notice, this list of conditions and the following disclaimer.
13df930be7Sderaadt  *  2. Redistributions in binary form must reproduce the above copyright
14df930be7Sderaadt  *     notice, this list of conditions and the following disclaimer in the
15df930be7Sderaadt  *     documentation and/or other materials provided with the distribution.
16df930be7Sderaadt  *  3. The name of the author may not be used to endorse or promote products
17df930be7Sderaadt  *     derived from this software without specific prior written permission.
18df930be7Sderaadt  *
19df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
20df930be7Sderaadt  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21df930be7Sderaadt  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22df930be7Sderaadt  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
23df930be7Sderaadt  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24df930be7Sderaadt  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25df930be7Sderaadt  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27df930be7Sderaadt  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28df930be7Sderaadt  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29df930be7Sderaadt  * POSSIBILITY OF SUCH DAMAGE.
30df930be7Sderaadt  */
31df930be7Sderaadt 
32df930be7Sderaadt #include <sys/param.h>
33df930be7Sderaadt #include <sys/time.h>
34df930be7Sderaadt #include <sys/socket.h>
35df930be7Sderaadt #include <sys/stat.h>
36df930be7Sderaadt 
37df930be7Sderaadt #include <netinet/in.h>
38df930be7Sderaadt 
3979dbd5ceSniklas #include "rpcv2.h"
4079dbd5ceSniklas #include "nfsv2.h"
41df930be7Sderaadt 
42df930be7Sderaadt #include "stand.h"
43e76f679cSmickey #include "saerrno.h"
44df930be7Sderaadt #include "net.h"
45df930be7Sderaadt #include "netif.h"
46df930be7Sderaadt #include "nfs.h"
47df930be7Sderaadt #include "rpc.h"
48df930be7Sderaadt 
49df930be7Sderaadt /* Define our own NFS attributes without NQNFS stuff. */
50df930be7Sderaadt struct nfsv2_fattrs {
51eb76c208Smpi 	u_int32_t	fa_type;
52eb76c208Smpi 	u_int32_t	fa_mode;
53eb76c208Smpi 	u_int32_t	fa_nlink;
54eb76c208Smpi 	u_int32_t	fa_uid;
55eb76c208Smpi 	u_int32_t	fa_gid;
56eb76c208Smpi 	u_int32_t	fa_size;
57eb76c208Smpi 	u_int32_t	fa_blocksize;
58eb76c208Smpi 	u_int32_t	fa_rdev;
59eb76c208Smpi 	u_int32_t	fa_blocks;
60eb76c208Smpi 	u_int32_t	fa_fsid;
61eb76c208Smpi 	u_int32_t	fa_fileid;
62df930be7Sderaadt 	struct nfsv2_time fa_atime;
63df930be7Sderaadt 	struct nfsv2_time fa_mtime;
64df930be7Sderaadt 	struct nfsv2_time fa_ctime;
65df930be7Sderaadt };
66df930be7Sderaadt 
67df930be7Sderaadt 
68df930be7Sderaadt struct nfs_read_args {
69df930be7Sderaadt 	u_char	fh[NFS_FHSIZE];
70eb76c208Smpi 	u_int32_t	off;
71eb76c208Smpi 	u_int32_t	len;
72eb76c208Smpi 	u_int32_t	xxx;			/* XXX what's this for? */
73df930be7Sderaadt };
74df930be7Sderaadt 
75df930be7Sderaadt /* Data part of nfs rpc reply (also the largest thing we receive) */
76df930be7Sderaadt #define NFSREAD_SIZE 1024
77df930be7Sderaadt struct nfs_read_repl {
78eb76c208Smpi 	u_int32_t	errno;
79df930be7Sderaadt 	struct	nfsv2_fattrs fa;
80eb76c208Smpi 	u_int32_t	count;
81df930be7Sderaadt 	u_char	data[NFSREAD_SIZE];
82df930be7Sderaadt };
83df930be7Sderaadt 
8479dbd5ceSniklas struct nfs_readlnk_repl {
85eb76c208Smpi 	u_int32_t	errno;
86eb76c208Smpi 	u_int32_t	len;
8779dbd5ceSniklas 	char	path[NFS_MAXPATHLEN];
8879dbd5ceSniklas };
8979dbd5ceSniklas 
90df930be7Sderaadt struct nfs_iodesc {
91df930be7Sderaadt 	struct	iodesc	*iodesc;
92df930be7Sderaadt 	off_t	off;
93df930be7Sderaadt 	u_char	fh[NFS_FHSIZE];
94df930be7Sderaadt 	struct nfsv2_fattrs fa;	/* all in network order */
95df930be7Sderaadt };
96df930be7Sderaadt 
97df930be7Sderaadt struct nfs_iodesc nfs_root_node;
98df930be7Sderaadt 
99df930be7Sderaadt 
100df930be7Sderaadt /*
101df930be7Sderaadt  * Fetch the root file handle (call mount daemon)
102df930be7Sderaadt  * On error, return non-zero and set errno.
103df930be7Sderaadt  */
104599546b3Sderaadt static int
nfs_getrootfh(struct iodesc * d,const char * path,u_char * fhp)10558567fe6Smiod nfs_getrootfh(struct iodesc *d, const char *path, u_char *fhp)
106df930be7Sderaadt {
107599546b3Sderaadt 	int len;
108df930be7Sderaadt 	struct args {
109eb76c208Smpi 		u_int32_t	len;
110df930be7Sderaadt 		char	path[FNAME_SIZE];
111df930be7Sderaadt 	} *args;
112df930be7Sderaadt 	struct repl {
113eb76c208Smpi 		u_int32_t	errno;
114df930be7Sderaadt 		u_char	fh[NFS_FHSIZE];
115df930be7Sderaadt 	} *repl;
116df930be7Sderaadt 	struct {
117eb76c208Smpi 		u_int32_t	h[RPC_HEADER_WORDS];
118df930be7Sderaadt 		struct args d;
119df930be7Sderaadt 	} sdata;
120df930be7Sderaadt 	struct {
121eb76c208Smpi 		u_int32_t	h[RPC_HEADER_WORDS];
122df930be7Sderaadt 		struct repl d;
123df930be7Sderaadt 	} rdata;
124afcdb1c9Smiod 	ssize_t cc;
125df930be7Sderaadt 
126df930be7Sderaadt #ifdef NFS_DEBUG
127df930be7Sderaadt 	if (debug)
128df930be7Sderaadt 		printf("nfs_getrootfh: %s\n", path);
129df930be7Sderaadt #endif
130df930be7Sderaadt 
131df930be7Sderaadt 	args = &sdata.d;
132df930be7Sderaadt 	repl = &rdata.d;
133df930be7Sderaadt 
134df930be7Sderaadt 	bzero(args, sizeof(*args));
135df930be7Sderaadt 	len = strlen(path);
136df930be7Sderaadt 	if (len > sizeof(args->path))
137df930be7Sderaadt 		len = sizeof(args->path);
138df930be7Sderaadt 	args->len = htonl(len);
139df930be7Sderaadt 	bcopy(path, args->path, len);
140df930be7Sderaadt 	len = 4 + roundup(len, 4);
141df930be7Sderaadt 
142df930be7Sderaadt 	cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT,
143df930be7Sderaadt 	    args, len, repl, sizeof(*repl));
144df930be7Sderaadt 	if (cc == -1) {
145df930be7Sderaadt 		/* errno was set by rpc_call */
146df930be7Sderaadt 		return (-1);
147df930be7Sderaadt 	}
148df930be7Sderaadt 	if (cc < 4) {
149df930be7Sderaadt 		errno = EBADRPC;
150df930be7Sderaadt 		return (-1);
151df930be7Sderaadt 	}
152df930be7Sderaadt 	if (repl->errno) {
153df930be7Sderaadt 		errno = ntohl(repl->errno);
154df930be7Sderaadt 		return (-1);
155df930be7Sderaadt 	}
156df930be7Sderaadt 	bcopy(repl->fh, fhp, sizeof(repl->fh));
157df930be7Sderaadt 	return (0);
158df930be7Sderaadt }
159df930be7Sderaadt 
160df930be7Sderaadt /*
161df930be7Sderaadt  * Lookup a file.  Store handle and attributes.
162df930be7Sderaadt  * Return zero or error number.
163df930be7Sderaadt  */
164599546b3Sderaadt static int
nfs_lookupfh(struct nfs_iodesc * d,char * name,struct nfs_iodesc * newfd)165599546b3Sderaadt nfs_lookupfh(struct nfs_iodesc *d, char *name, struct nfs_iodesc *newfd)
166df930be7Sderaadt {
167599546b3Sderaadt 	int len, rlen;
168df930be7Sderaadt 	struct args {
169df930be7Sderaadt 		u_char	fh[NFS_FHSIZE];
170eb76c208Smpi 		u_int32_t	len;
171df930be7Sderaadt 		char	name[FNAME_SIZE];
172df930be7Sderaadt 	} *args;
173df930be7Sderaadt 	struct repl {
174eb76c208Smpi 		u_int32_t	errno;
175df930be7Sderaadt 		u_char	fh[NFS_FHSIZE];
176df930be7Sderaadt 		struct	nfsv2_fattrs fa;
177df930be7Sderaadt 	} *repl;
178df930be7Sderaadt 	struct {
179eb76c208Smpi 		u_int32_t	h[RPC_HEADER_WORDS];
180df930be7Sderaadt 		struct args d;
181df930be7Sderaadt 	} sdata;
182df930be7Sderaadt 	struct {
183eb76c208Smpi 		u_int32_t	h[RPC_HEADER_WORDS];
184df930be7Sderaadt 		struct repl d;
185df930be7Sderaadt 	} rdata;
186df930be7Sderaadt 	ssize_t cc;
187df930be7Sderaadt 
188df930be7Sderaadt #ifdef NFS_DEBUG
189df930be7Sderaadt 	if (debug)
190df930be7Sderaadt 		printf("lookupfh: called\n");
191df930be7Sderaadt #endif
192df930be7Sderaadt 
193df930be7Sderaadt 	args = &sdata.d;
194df930be7Sderaadt 	repl = &rdata.d;
195df930be7Sderaadt 
196df930be7Sderaadt 	bzero(args, sizeof(*args));
197df930be7Sderaadt 	bcopy(d->fh, args->fh, sizeof(args->fh));
198df930be7Sderaadt 	len = strlen(name);
199df930be7Sderaadt 	if (len > sizeof(args->name))
200df930be7Sderaadt 		len = sizeof(args->name);
201df930be7Sderaadt 	bcopy(name, args->name, len);
202df930be7Sderaadt 	args->len = htonl(len);
203df930be7Sderaadt 	len = 4 + roundup(len, 4);
204df930be7Sderaadt 	len += NFS_FHSIZE;
205df930be7Sderaadt 
206df930be7Sderaadt 	rlen = sizeof(*repl);
207df930be7Sderaadt 
208df930be7Sderaadt 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_LOOKUP,
209df930be7Sderaadt 	    args, len, repl, rlen);
210df930be7Sderaadt 	if (cc == -1)
211df930be7Sderaadt 		return (errno);		/* XXX - from rpc_call */
212df930be7Sderaadt 	if (cc < 4)
213df930be7Sderaadt 		return (EIO);
214df930be7Sderaadt 	if (repl->errno) {
215df930be7Sderaadt 		/* saerrno.h now matches NFS error numbers. */
216df930be7Sderaadt 		return (ntohl(repl->errno));
217df930be7Sderaadt 	}
218df930be7Sderaadt 	bcopy( repl->fh, &newfd->fh, sizeof(newfd->fh));
219df930be7Sderaadt 	bcopy(&repl->fa, &newfd->fa, sizeof(newfd->fa));
220df930be7Sderaadt 	return (0);
221df930be7Sderaadt }
222df930be7Sderaadt 
223df930be7Sderaadt /*
22479dbd5ceSniklas  * Get the destination of a symbolic link.
22579dbd5ceSniklas  */
226599546b3Sderaadt static int
nfs_readlink(struct nfs_iodesc * d,char * buf)227599546b3Sderaadt nfs_readlink(struct nfs_iodesc *d, char *buf)
22879dbd5ceSniklas {
22979dbd5ceSniklas 	struct {
230eb76c208Smpi 		u_int32_t	h[RPC_HEADER_WORDS];
23179dbd5ceSniklas 		u_char fh[NFS_FHSIZE];
23279dbd5ceSniklas 	} sdata;
23379dbd5ceSniklas 	struct {
234eb76c208Smpi 		u_int32_t	h[RPC_HEADER_WORDS];
23579dbd5ceSniklas 		struct nfs_readlnk_repl d;
23679dbd5ceSniklas 	} rdata;
23779dbd5ceSniklas 	ssize_t cc;
23879dbd5ceSniklas 
23979dbd5ceSniklas #ifdef NFS_DEBUG
24079dbd5ceSniklas 	if (debug)
24179dbd5ceSniklas 		printf("readlink: called\n");
24279dbd5ceSniklas #endif
24379dbd5ceSniklas 
24479dbd5ceSniklas 	bcopy(d->fh, sdata.fh, NFS_FHSIZE);
24579dbd5ceSniklas 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READLINK,
24679dbd5ceSniklas 	    sdata.fh, NFS_FHSIZE,
24779dbd5ceSniklas 	    &rdata.d, sizeof(rdata.d));
24879dbd5ceSniklas 	if (cc == -1)
24979dbd5ceSniklas 		return (errno);
25079dbd5ceSniklas 
25179dbd5ceSniklas 	if (cc < 4)
25279dbd5ceSniklas 		return (EIO);
25379dbd5ceSniklas 
25479dbd5ceSniklas 	if (rdata.d.errno)
25579dbd5ceSniklas 		return (ntohl(rdata.d.errno));
25679dbd5ceSniklas 
25779dbd5ceSniklas 	rdata.d.len = ntohl(rdata.d.len);
25879dbd5ceSniklas 	if (rdata.d.len > NFS_MAXPATHLEN)
25979dbd5ceSniklas 		return (ENAMETOOLONG);
26079dbd5ceSniklas 
26179dbd5ceSniklas 	bcopy(rdata.d.path, buf, rdata.d.len);
26279dbd5ceSniklas 	buf[rdata.d.len] = 0;
26379dbd5ceSniklas 	return (0);
26479dbd5ceSniklas }
26579dbd5ceSniklas 
26679dbd5ceSniklas /*
267df930be7Sderaadt  * Read data from a file.
268df930be7Sderaadt  * Return transfer count or -1 (and set errno)
269df930be7Sderaadt  */
270599546b3Sderaadt static ssize_t
nfs_readdata(struct nfs_iodesc * d,off_t off,void * addr,size_t len)271599546b3Sderaadt nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
272df930be7Sderaadt {
273df930be7Sderaadt 	struct nfs_read_args *args;
274df930be7Sderaadt 	struct nfs_read_repl *repl;
275df930be7Sderaadt 	struct {
276eb76c208Smpi 		u_int32_t	h[RPC_HEADER_WORDS];
277df930be7Sderaadt 		struct nfs_read_args d;
278df930be7Sderaadt 	} sdata;
279df930be7Sderaadt 	struct {
280eb76c208Smpi 		u_int32_t	h[RPC_HEADER_WORDS];
281df930be7Sderaadt 		struct nfs_read_repl d;
282df930be7Sderaadt 	} rdata;
283afcdb1c9Smiod 	ssize_t cc;
284df930be7Sderaadt 	long x;
285df930be7Sderaadt 	int hlen, rlen;
286df930be7Sderaadt 
287df930be7Sderaadt 	args = &sdata.d;
288df930be7Sderaadt 	repl = &rdata.d;
289df930be7Sderaadt 
290df930be7Sderaadt 	bcopy(d->fh, args->fh, NFS_FHSIZE);
291eb76c208Smpi 	args->off = htonl((u_int32_t)off);
292df930be7Sderaadt 	if (len > NFSREAD_SIZE)
293df930be7Sderaadt 		len = NFSREAD_SIZE;
294eb76c208Smpi 	args->len = htonl((u_int32_t)len);
295eb76c208Smpi 	args->xxx = htonl((u_int32_t)0);
296df930be7Sderaadt 	hlen = sizeof(*repl) - NFSREAD_SIZE;
297df930be7Sderaadt 
298df930be7Sderaadt 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READ,
299df930be7Sderaadt 	    args, sizeof(*args),
300df930be7Sderaadt 	    repl, sizeof(*repl));
301df930be7Sderaadt 	if (cc == -1) {
302df930be7Sderaadt 		/* errno was already set by rpc_call */
303df930be7Sderaadt 		return (-1);
304df930be7Sderaadt 	}
305df930be7Sderaadt 	if (cc < hlen) {
306df930be7Sderaadt 		errno = EBADRPC;
307df930be7Sderaadt 		return (-1);
308df930be7Sderaadt 	}
309df930be7Sderaadt 	if (repl->errno) {
310df930be7Sderaadt 		errno = ntohl(repl->errno);
311df930be7Sderaadt 		return (-1);
312df930be7Sderaadt 	}
313df930be7Sderaadt 	rlen = cc - hlen;
314df930be7Sderaadt 	x = ntohl(repl->count);
315df930be7Sderaadt 	if (rlen < x) {
316a4bb608eSderaadt 		printf("nfsread: short packet, %d < %ld\n", rlen, x);
317df930be7Sderaadt 		errno = EBADRPC;
318df930be7Sderaadt 		return(-1);
319df930be7Sderaadt 	}
320df930be7Sderaadt 	bcopy(repl->data, addr, x);
321df930be7Sderaadt 	return (x);
322df930be7Sderaadt }
323df930be7Sderaadt 
324df930be7Sderaadt /*
325df930be7Sderaadt  * nfs_mount - mount this nfs filesystem to a host
326df930be7Sderaadt  * On error, return non-zero and set errno.
327df930be7Sderaadt  */
328df930be7Sderaadt int
nfs_mount(int sock,struct in_addr ip,const char * path)32958567fe6Smiod nfs_mount(int sock, struct in_addr ip, const char *path)
330df930be7Sderaadt {
331df930be7Sderaadt 	struct iodesc *desc;
332df930be7Sderaadt 	struct nfsv2_fattrs *fa;
333df930be7Sderaadt 
334df930be7Sderaadt 	if (!(desc = socktodesc(sock))) {
335df930be7Sderaadt 		errno = EINVAL;
336df930be7Sderaadt 		return(-1);
337df930be7Sderaadt 	}
338df930be7Sderaadt 
339df930be7Sderaadt 	/* Bind to a reserved port. */
340df930be7Sderaadt 	desc->myport = htons(--rpc_port);
341df930be7Sderaadt 	desc->destip = ip;
342df930be7Sderaadt 	if (nfs_getrootfh(desc, path, nfs_root_node.fh))
343df930be7Sderaadt 		return (-1);
344df930be7Sderaadt 	nfs_root_node.iodesc = desc;
345df930be7Sderaadt 	/* Fake up attributes for the root dir. */
346df930be7Sderaadt 	fa = &nfs_root_node.fa;
347df930be7Sderaadt 	fa->fa_type  = htonl(NFDIR);
348df930be7Sderaadt 	fa->fa_mode  = htonl(0755);
349df930be7Sderaadt 	fa->fa_nlink = htonl(2);
350df930be7Sderaadt 
351df930be7Sderaadt #ifdef NFS_DEBUG
352df930be7Sderaadt 	if (debug)
353df930be7Sderaadt 		printf("nfs_mount: got fh for %s\n", path);
354df930be7Sderaadt #endif
355df930be7Sderaadt 
356df930be7Sderaadt 	return(0);
357df930be7Sderaadt }
358df930be7Sderaadt 
359df930be7Sderaadt /*
360df930be7Sderaadt  * Open a file.
361df930be7Sderaadt  * return zero or error number
362df930be7Sderaadt  */
363df930be7Sderaadt int
nfs_open(char * path,struct open_file * f)364599546b3Sderaadt nfs_open(char *path, struct open_file *f)
365df930be7Sderaadt {
36679dbd5ceSniklas 	struct nfs_iodesc *newfd, *currfd;
367599546b3Sderaadt 	char namebuf[NFS_MAXPATHLEN + 1], *cp, *ncp;
36879dbd5ceSniklas 	char linkbuf[NFS_MAXPATHLEN + 1];
369599546b3Sderaadt 	int nlinks = 0, error = 0, c;
370df930be7Sderaadt 
371df930be7Sderaadt #ifdef NFS_DEBUG
372df930be7Sderaadt 	if (debug)
373df930be7Sderaadt 		printf("nfs_open: %s\n", path);
374df930be7Sderaadt #endif
375df930be7Sderaadt 	if (nfs_root_node.iodesc == NULL) {
376df930be7Sderaadt 		printf("nfs_open: must mount first.\n");
377df930be7Sderaadt 		return (ENXIO);
378df930be7Sderaadt 	}
379df930be7Sderaadt 
38079dbd5ceSniklas 	currfd = &nfs_root_node;
38179dbd5ceSniklas 	newfd = 0;
38279dbd5ceSniklas 
38379dbd5ceSniklas 	cp = path;
38479dbd5ceSniklas 	while (*cp) {
38579dbd5ceSniklas 		/*
38679dbd5ceSniklas 		 * Remove extra separators
38779dbd5ceSniklas 		 */
38879dbd5ceSniklas 		while (*cp == '/')
38979dbd5ceSniklas 			cp++;
39079dbd5ceSniklas 
39179dbd5ceSniklas 		if (*cp == '\0')
39279dbd5ceSniklas 			break;
39379dbd5ceSniklas 		/*
39479dbd5ceSniklas 		 * Check that current node is a directory.
39579dbd5ceSniklas 		 */
39679dbd5ceSniklas 		if (currfd->fa.fa_type != htonl(NFDIR)) {
39779dbd5ceSniklas 			error = ENOTDIR;
39879dbd5ceSniklas 			goto out;
39979dbd5ceSniklas 		}
40079dbd5ceSniklas 
401df930be7Sderaadt 		/* allocate file system specific data structure */
402df930be7Sderaadt 		newfd = alloc(sizeof(*newfd));
40379dbd5ceSniklas 		newfd->iodesc = currfd->iodesc;
404df930be7Sderaadt 		newfd->off = 0;
405df930be7Sderaadt 
40679dbd5ceSniklas 		/*
40779dbd5ceSniklas 		 * Get next component of path name.
40879dbd5ceSniklas 		 */
40979dbd5ceSniklas 		{
410599546b3Sderaadt 			int len = 0;
41179dbd5ceSniklas 
41279dbd5ceSniklas 			ncp = cp;
41379dbd5ceSniklas 			while ((c = *cp) != '\0' && c != '/') {
41479dbd5ceSniklas 				if (++len > NFS_MAXNAMLEN) {
41579dbd5ceSniklas 					error = ENOENT;
41679dbd5ceSniklas 					goto out;
41779dbd5ceSniklas 				}
41879dbd5ceSniklas 				cp++;
41979dbd5ceSniklas 			}
42079dbd5ceSniklas 			*cp = '\0';
42179dbd5ceSniklas 		}
42279dbd5ceSniklas 
423df930be7Sderaadt 		/* lookup a file handle */
42479dbd5ceSniklas 		error = nfs_lookupfh(currfd, ncp, newfd);
42579dbd5ceSniklas 		*cp = c;
42679dbd5ceSniklas 		if (error)
42779dbd5ceSniklas 			goto out;
42879dbd5ceSniklas 
42979dbd5ceSniklas 		/*
43079dbd5ceSniklas 		 * Check for symbolic link
43179dbd5ceSniklas 		 */
43279dbd5ceSniklas 		if (newfd->fa.fa_type == htonl(NFLNK)) {
43379dbd5ceSniklas 			int link_len, len;
43479dbd5ceSniklas 
43579dbd5ceSniklas 			error = nfs_readlink(newfd, linkbuf);
43679dbd5ceSniklas 			if (error)
43779dbd5ceSniklas 				goto out;
43879dbd5ceSniklas 
43979dbd5ceSniklas 			link_len = strlen(linkbuf);
44079dbd5ceSniklas 			len = strlen(cp);
44179dbd5ceSniklas 
442599546b3Sderaadt 			if (link_len + len > MAXPATHLEN ||
443599546b3Sderaadt 			    ++nlinks > MAXSYMLINKS) {
44479dbd5ceSniklas 				error = ENOENT;
44579dbd5ceSniklas 				goto out;
44679dbd5ceSniklas 			}
44779dbd5ceSniklas 
44879dbd5ceSniklas 			bcopy(cp, &namebuf[link_len], len + 1);
44979dbd5ceSniklas 			bcopy(linkbuf, namebuf, link_len);
45079dbd5ceSniklas 
45179dbd5ceSniklas 			/*
45279dbd5ceSniklas 			 * If absolute pathname, restart at root.
45379dbd5ceSniklas 			 * If relative pathname, restart at parent directory.
45479dbd5ceSniklas 			 */
45579dbd5ceSniklas 			cp = namebuf;
45679dbd5ceSniklas 			if (*cp == '/') {
45779dbd5ceSniklas 				if (currfd != &nfs_root_node)
45879dbd5ceSniklas 					free(currfd, sizeof(*currfd));
45979dbd5ceSniklas 				currfd = &nfs_root_node;
46079dbd5ceSniklas 			}
46179dbd5ceSniklas 
46279dbd5ceSniklas 			free(newfd, sizeof(*newfd));
46379dbd5ceSniklas 			newfd = 0;
46479dbd5ceSniklas 
46579dbd5ceSniklas 			continue;
46679dbd5ceSniklas 		}
46779dbd5ceSniklas 
46879dbd5ceSniklas 		if (currfd != &nfs_root_node)
46979dbd5ceSniklas 			free(currfd, sizeof(*currfd));
47079dbd5ceSniklas 		currfd = newfd;
47179dbd5ceSniklas 		newfd = 0;
47279dbd5ceSniklas 	}
47379dbd5ceSniklas 
47479dbd5ceSniklas 	error = 0;
47579dbd5ceSniklas 
47679dbd5ceSniklas out:
477df930be7Sderaadt 	if (!error) {
47879dbd5ceSniklas 		f->f_fsdata = (void *)currfd;
479df930be7Sderaadt 		return (0);
480df930be7Sderaadt 	}
481df930be7Sderaadt 
482df930be7Sderaadt #ifdef NFS_DEBUG
483df930be7Sderaadt 	if (debug)
484df930be7Sderaadt 		printf("nfs_open: %s lookupfh failed: %s\n",
485df930be7Sderaadt 		    path, strerror(error));
486df930be7Sderaadt #endif
48779dbd5ceSniklas 	if (currfd != &nfs_root_node)
48879dbd5ceSniklas 		free(currfd, sizeof(*currfd));
48979dbd5ceSniklas 	if (newfd)
490df930be7Sderaadt 		free(newfd, sizeof(*newfd));
49179dbd5ceSniklas 
492df930be7Sderaadt 	return (error);
493df930be7Sderaadt }
494df930be7Sderaadt 
495df930be7Sderaadt int
nfs_close(struct open_file * f)496599546b3Sderaadt nfs_close(struct open_file *f)
497df930be7Sderaadt {
498599546b3Sderaadt 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
499df930be7Sderaadt 
500df930be7Sderaadt #ifdef NFS_DEBUG
501df930be7Sderaadt 	if (debug)
5026d86dcc1Smickey 		printf("nfs_close: fp=%p\n", fp);
503df930be7Sderaadt #endif
504df930be7Sderaadt 
505df930be7Sderaadt 	if (fp)
506df930be7Sderaadt 		free(fp, sizeof(struct nfs_iodesc));
50714bf419fSkrw 	f->f_fsdata = NULL;
508df930be7Sderaadt 
509df930be7Sderaadt 	return (0);
510df930be7Sderaadt }
511df930be7Sderaadt 
512df930be7Sderaadt /*
513df930be7Sderaadt  * read a portion of a file
514df930be7Sderaadt  */
515df930be7Sderaadt int
nfs_read(struct open_file * f,void * buf,size_t size,size_t * resid)516599546b3Sderaadt nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
517df930be7Sderaadt {
518599546b3Sderaadt 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
519599546b3Sderaadt 	ssize_t cc;
520599546b3Sderaadt 	char *addr = buf;
521df930be7Sderaadt 
522df930be7Sderaadt #ifdef NFS_DEBUG
523df930be7Sderaadt 	if (debug)
524df930be7Sderaadt 		printf("nfs_read: size=%d off=%d\n", size, (int)fp->off);
525df930be7Sderaadt #endif
526df930be7Sderaadt 	while ((int)size > 0) {
527df930be7Sderaadt 		twiddle();
528df930be7Sderaadt 		cc = nfs_readdata(fp, fp->off, (void *)addr, size);
529df930be7Sderaadt 		/* XXX maybe should retry on certain errors */
530df930be7Sderaadt 		if (cc == -1) {
531df930be7Sderaadt #ifdef NFS_DEBUG
532df930be7Sderaadt 			if (debug)
533df930be7Sderaadt 				printf("nfs_read: read: %s", strerror(errno));
534df930be7Sderaadt #endif
535df930be7Sderaadt 			return (errno);	/* XXX - from nfs_readdata */
536df930be7Sderaadt 		}
537df930be7Sderaadt 		if (cc == 0) {
538*5a0183c5Spatrick #ifdef NFS_DEBUG
539df930be7Sderaadt 			if (debug)
540df930be7Sderaadt 				printf("nfs_read: hit EOF unexpectantly");
541*5a0183c5Spatrick #endif
542df930be7Sderaadt 			goto ret;
543df930be7Sderaadt 		}
544df930be7Sderaadt 		fp->off += cc;
545df930be7Sderaadt 		addr += cc;
546df930be7Sderaadt 		size -= cc;
547df930be7Sderaadt 	}
548df930be7Sderaadt ret:
549df930be7Sderaadt 	if (resid)
550df930be7Sderaadt 		*resid = size;
551df930be7Sderaadt 
552df930be7Sderaadt 	return (0);
553df930be7Sderaadt }
554df930be7Sderaadt 
555df930be7Sderaadt /*
556df930be7Sderaadt  * Not implemented.
557df930be7Sderaadt  */
558df930be7Sderaadt int
nfs_write(struct open_file * f,void * buf,size_t size,size_t * resid)559599546b3Sderaadt nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
560df930be7Sderaadt {
561df930be7Sderaadt 	return (EROFS);
562df930be7Sderaadt }
563df930be7Sderaadt 
564df930be7Sderaadt off_t
nfs_seek(struct open_file * f,off_t offset,int where)565599546b3Sderaadt nfs_seek(struct open_file *f, off_t offset, int where)
566df930be7Sderaadt {
567599546b3Sderaadt 	struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
568eb76c208Smpi 	u_int32_t size = ntohl(d->fa.fa_size);
569df930be7Sderaadt 
570df930be7Sderaadt 	switch (where) {
571df930be7Sderaadt 	case SEEK_SET:
572df930be7Sderaadt 		d->off = offset;
573df930be7Sderaadt 		break;
574df930be7Sderaadt 	case SEEK_CUR:
575df930be7Sderaadt 		d->off += offset;
576df930be7Sderaadt 		break;
577df930be7Sderaadt 	case SEEK_END:
578df930be7Sderaadt 		d->off = size - offset;
579df930be7Sderaadt 		break;
580df930be7Sderaadt 	default:
581df930be7Sderaadt 		return (-1);
582df930be7Sderaadt 	}
583df930be7Sderaadt 
584df930be7Sderaadt 	return (d->off);
585df930be7Sderaadt }
586df930be7Sderaadt 
587df930be7Sderaadt /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 */
58858567fe6Smiod const int nfs_stat_types[8] = {
58958567fe6Smiod 	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 0
59058567fe6Smiod };
591df930be7Sderaadt 
592df930be7Sderaadt int
nfs_stat(struct open_file * f,struct stat * sb)593599546b3Sderaadt nfs_stat(struct open_file *f, struct stat *sb)
594df930be7Sderaadt {
595df930be7Sderaadt 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
596eb76c208Smpi 	u_int32_t ftype, mode;
597df930be7Sderaadt 
598df930be7Sderaadt 	ftype = ntohl(fp->fa.fa_type);
599df930be7Sderaadt 	mode  = ntohl(fp->fa.fa_mode);
600df930be7Sderaadt 	mode |= nfs_stat_types[ftype & 7];
601df930be7Sderaadt 
602df930be7Sderaadt 	sb->st_mode  = mode;
603df930be7Sderaadt 	sb->st_nlink = ntohl(fp->fa.fa_nlink);
604df930be7Sderaadt 	sb->st_uid   = ntohl(fp->fa.fa_uid);
605df930be7Sderaadt 	sb->st_gid   = ntohl(fp->fa.fa_gid);
606df930be7Sderaadt 	sb->st_size  = ntohl(fp->fa.fa_size);
607df930be7Sderaadt 
608df930be7Sderaadt 	return (0);
609df930be7Sderaadt }
610e76f679cSmickey 
611e76f679cSmickey /*
612e76f679cSmickey  * Not implemented.
613e76f679cSmickey  */
614e76f679cSmickey #ifndef NO_READDIR
615e76f679cSmickey int
nfs_readdir(struct open_file * f,char * name)616599546b3Sderaadt nfs_readdir(struct open_file *f, char *name)
617e76f679cSmickey {
618e76f679cSmickey 	return (EROFS);
619e76f679cSmickey }
620e76f679cSmickey #endif
621