xref: /netbsd-src/sys/lib/libsa/nfs.c (revision c12541367903baeb8a3869bff8a3373735082d17)
1 /*	$NetBSD: nfs.c,v 1.53 2024/06/29 07:49:36 rin Exp $	*/
2 
3 /*-
4  *  Copyright (c) 1993 John Brezak
5  *  All rights reserved.
6  *
7  *  Redistribution and use in source and binary forms, with or without
8  *  modification, are permitted provided that the following conditions
9  *  are met:
10  *  1. Redistributions of source code must retain the above copyright
11  *     notice, this list of conditions and the following disclaimer.
12  *  2. Redistributions in binary form must reproduce the above copyright
13  *     notice, this list of conditions and the following disclaimer in the
14  *     documentation and/or other materials provided with the distribution.
15  *  3. The name of the author may not be used to endorse or promote products
16  *     derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 /*
32  * XXX Does not currently implement:
33  * XXX
34  * XXX LIBSA_NO_FS_CLOSE
35  * XXX LIBSA_NO_FS_SEEK
36  * XXX LIBSA_NO_FS_WRITE
37  * XXX LIBSA_NO_FS_SYMLINK (does this even make sense?)
38  * XXX LIBSA_FS_SINGLECOMPONENT (does this even make sense?)
39  */
40 
41 #include <sys/param.h>
42 #include <sys/time.h>
43 #include <sys/socket.h>
44 #include <sys/stat.h>
45 #ifdef _STANDALONE
46 #include <lib/libkern/libkern.h>
47 #else
48 #include <string.h>
49 #endif
50 
51 #include <netinet/in.h>
52 #include <netinet/in_systm.h>
53 
54 #include "rpcv2.h"
55 #include "nfsv2.h"
56 #include "nfsv3.h"
57 
58 #include "stand.h"
59 #include "net.h"
60 #include "nfs.h"
61 #include "rpc.h"
62 
63 /* Storage for any filehandle (including length for V3) */
64 #define NFS_FHSTORE (NFS_FHSIZE < NFS_V3FHSIZE ? NFS_V3FHSIZE + 4: NFS_FHSIZE)
65 
66 /* Data part of nfs rpc reply (also the largest thing we receive) */
67 #define NFSREAD_SIZE 1024
68 
69 #ifndef NFS_NOSYMLINK
70 struct nfs_readlnk_repl {
71 	n_long	errno;
72 	n_long	len;
73 	char	path[NFS_MAXPATHLEN];
74 };
75 #endif
76 
77 static inline uint64_t
getnquad(n_long x[2])78 getnquad(n_long x[2]) {
79 	return (uint64_t)ntohl(x[0]) << 32 | ntohl(x[1]);
80 }
81 
82 static inline void
setnquad(n_long x[2],uint64_t v)83 setnquad(n_long x[2], uint64_t v)
84 {
85 	x[0] = htonl((n_long)(v >> 32));
86 	x[1] = htonl((n_long)(v & 0xffffffff));
87 }
88 
89 struct nfs_iodesc {
90 	struct	iodesc	*iodesc;
91 	off_t	off;
92 	int	version;
93 	u_char	fh[NFS_FHSTORE];
94 	union {
95 		/* all in network order */
96 		struct nfsv2_fattr v2;
97 		struct nfsv3_fattr v3;
98 	} u_fa;
99 };
100 
101 static inline size_t
fhstore(int version,u_char * fh)102 fhstore(int version, u_char *fh)
103 {
104 	size_t len;
105 
106 	switch (version) {
107 	case NFS_VER2:
108 		len = NFS_FHSIZE;
109 		break;
110 	case NFS_VER3:
111 		len = fh[0] << 24 | fh[1] << 16 | fh[2] << 8 | fh[3];
112 		if (len > NFS_V3FHSIZE)
113 			len = NFS_V3FHSIZE;
114 		len = 4 + roundup(len, 4);
115 		break;
116 	default:
117 		len = 0;
118 		break;
119 	}
120 
121 	return len;
122 }
123 
124 static inline size_t
fhcopy(int version,u_char * src,u_char * dst)125 fhcopy(int version, u_char *src, u_char *dst)
126 {
127 	size_t len = fhstore(version, src);
128 	memcpy(dst, src, len);
129 	return len;
130 }
131 
132 #define setfh(d, s) fhcopy((d)->version, (s), (d)->fh)
133 #define getfh(d, s) fhcopy((d)->version, (d)->fh, (s))
134 
135 
136 struct nfs_iodesc nfs_root_node;
137 
138 int	nfs_getrootfh(struct iodesc *, char *, u_char *, int *);
139 int	nfs_lookupfh(struct nfs_iodesc *, const char *, int,
140 	    struct nfs_iodesc *);
141 int	nfs_readlink(struct nfs_iodesc *, char *);
142 ssize_t	nfs_readdata(struct nfs_iodesc *, off_t, void *, size_t);
143 
144 /*
145  * Fetch the root file handle (call mount daemon)
146  * On error, return non-zero and set errno.
147  */
148 int
nfs_getrootfh(struct iodesc * d,char * path,u_char * fhp,int * versionp)149 nfs_getrootfh(struct iodesc *d, char *path, u_char *fhp, int *versionp)
150 {
151 	int len;
152 	struct args {
153 		n_long	len;
154 		char	path[FNAME_SIZE];
155 	} *args;
156 	struct repl {
157 		n_long	errno;
158 		u_char	fh[NFS_FHSTORE];
159 	} *repl;
160 	struct {
161 		n_long	h[RPC_HEADER_WORDS];
162 		struct args d;
163 	} sdata;
164 	struct {
165 		n_long	h[RPC_HEADER_WORDS];
166 		struct repl d;
167 	} rdata;
168 	ssize_t cc;
169 
170 #ifdef NFS_DEBUG
171 	if (debug)
172 		printf("%s: %s\n", __func__, path);
173 #endif
174 
175 	args = &sdata.d;
176 	repl = &rdata.d;
177 
178 	(void)memset(args, 0, sizeof(*args));
179 	len = strlen(path);
180 	if ((size_t)len > sizeof(args->path))
181 		len = sizeof(args->path);
182 	args->len = htonl(len);
183 	(void)memcpy(args->path, path, len);
184 	len = 4 + roundup(len, 4);
185 
186 	*versionp = NFS_VER3;
187 	cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER3, RPCMNT_MOUNT,
188 	    args, len, repl, sizeof(*repl));
189 	if (cc == -1 || cc < 4 || repl->errno) {
190 		*versionp = NFS_VER2;
191 		cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT,
192 		    args, len, repl, sizeof(*repl));
193 	}
194 	if (cc == -1) {
195 		/* errno was set by rpc_call */
196 		return -1;
197 	}
198 	if (cc < 4) {
199 		errno = EBADRPC;
200 		return -1;
201 	}
202 	if (repl->errno) {
203 		errno = ntohl(repl->errno);
204 		return -1;
205 	}
206 	fhcopy(*versionp, repl->fh, fhp);
207 	return 0;
208 }
209 
210 /*
211  * Lookup a file.  Store handle and attributes.
212  * Return zero or error number.
213  */
214 int
nfs_lookupfh(struct nfs_iodesc * d,const char * name,int len,struct nfs_iodesc * newfd)215 nfs_lookupfh(struct nfs_iodesc *d, const char *name, int len,
216 	struct nfs_iodesc *newfd)
217 {
218 	struct argsv2 {
219 		u_char	fh[NFS_FHSIZE];
220 		n_long	len;
221 		char	name[FNAME_SIZE];
222 	} *argsv2;
223 	struct argsv3 {
224 		u_char	fh[NFS_FHSTORE];
225 		n_long	len;
226 		char	name[FNAME_SIZE];
227 	} *argsv3;
228 	struct replv2 {
229 		n_long	errno;
230 		u_char	fh[NFS_FHSIZE];
231 		struct	nfsv2_fattr fa;
232 	} *replv2;
233 	struct replv3 {
234 		n_long	errno;
235 		u_char	fh[NFS_FHSTORE];
236 		n_long	fattrflag;
237 		struct	nfsv3_fattr fa;
238 		n_long	dattrflag;
239 		struct	nfsv3_fattr da;
240 	} *replv3;
241 	struct {
242 		n_long	h[RPC_HEADER_WORDS];
243 		union {
244 			struct argsv2 v2;
245 			struct argsv3 v3;
246 		} u_d;
247 	} sdata;
248 	struct {
249 		n_long	h[RPC_HEADER_WORDS];
250 		union {
251 			struct replv2 v2;
252 			struct replv3 v3;
253 		} u_d;
254 	} rdata;
255 	ssize_t cc;
256 	size_t alen;
257 
258 #ifdef NFS_DEBUG
259 	if (debug)
260 		printf("%s: called\n", __func__);
261 #endif
262 
263 	argsv2 = &sdata.u_d.v2;
264 	argsv3 = &sdata.u_d.v3;
265 	replv2 = &rdata.u_d.v2;
266 	replv3 = &rdata.u_d.v3;
267 
268 	switch (d->version) {
269 	case NFS_VER2:
270 		(void)memset(argsv2, 0, sizeof(*argsv2));
271 		getfh(d, argsv2->fh);
272 		if ((size_t)len > sizeof(argsv2->name))
273 			len = sizeof(argsv2->name);
274 		(void)memcpy(argsv2->name, name, len);
275 		argsv2->len = htonl(len);
276 
277 		/* padded name, name length */
278 		len = roundup(len, 4) + 4;
279 		/* filehandle size */
280 		alen = fhstore(d->version, argsv2->fh);
281 
282 		cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_LOOKUP,
283 		    argsv2, alen+len, replv2, sizeof(*replv2));
284 		break;
285 	case NFS_VER3:
286 		(void)memset(argsv3, 0, sizeof(*argsv3));
287 		getfh(d, argsv3->fh);
288 		if ((size_t)len > sizeof(argsv3->name))
289 			len = sizeof(argsv3->name);
290 		(void)memcpy(argsv3->name, name, len);
291 		argsv3->len = htonl(len);
292 
293 		/* padded name, name length */
294 		len = roundup(len, 4) + 4;
295 		/* filehandle size */
296 		alen = fhstore(d->version, argsv3->fh);
297 
298 		/* adjust for variable sized file handle */
299 		memmove(argsv3->fh + alen, &argsv3->len, len);
300 
301 		cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSV3PROC_LOOKUP,
302 		    argsv3, alen+len, replv3, sizeof(*replv3));
303 		break;
304 	default:
305 		return ENOSYS;
306 	}
307 
308 	if (cc == -1)
309 		return errno;		/* XXX - from rpc_call */
310 	if (cc < 4)
311 		return EIO;
312 
313 	switch (d->version) {
314 	case NFS_VER2:
315 		if (replv2->errno) {
316 			/* saerrno.h now matches NFS error numbers. */
317 			return ntohl(replv2->errno);
318 		}
319 
320 		setfh(newfd, replv2->fh);
321 		(void)memcpy(&newfd->u_fa.v2, &replv2->fa,
322 		    sizeof(newfd->u_fa.v2));
323 		break;
324 	case NFS_VER3:
325 		if (replv3->errno) {
326 			/* saerrno.h now matches NFS error numbers. */
327 			return ntohl(replv3->errno);
328 		}
329 
330 		setfh(newfd, replv3->fh);
331 
332 		if (replv3->fattrflag) {
333 			(void)memcpy(&newfd->u_fa.v3, &replv3->fa,
334 			    sizeof(newfd->u_fa.v3));
335 		}
336 		break;
337 	}
338 	return 0;
339 }
340 
341 #ifndef NFS_NOSYMLINK
342 /*
343  * Get the destination of a symbolic link.
344  */
345 int
nfs_readlink(struct nfs_iodesc * d,char * buf)346 nfs_readlink(struct nfs_iodesc *d, char *buf)
347 {
348 	struct {
349 		n_long	h[RPC_HEADER_WORDS];
350 		u_char	fh[NFS_FHSTORE];
351 	} sdata;
352 	struct {
353 		n_long	h[RPC_HEADER_WORDS];
354 		struct nfs_readlnk_repl d;
355 	} rdata;
356 	ssize_t cc;
357 
358 #ifdef NFS_DEBUG
359 	if (debug)
360 		printf("%s: called\n", __func__);
361 #endif
362 
363 	getfh(d, sdata.fh);
364 	cc = rpc_call(d->iodesc, NFS_PROG, d->version, NFSPROC_READLINK,
365 		      sdata.fh, fhstore(d->version, sdata.fh),
366 		      &rdata.d, sizeof(rdata.d));
367 	if (cc == -1)
368 		return errno;
369 
370 	if (cc < 4)
371 		return EIO;
372 
373 	if (rdata.d.errno)
374 		return ntohl(rdata.d.errno);
375 
376 	rdata.d.len = ntohl(rdata.d.len);
377 	if (rdata.d.len > NFS_MAXPATHLEN)
378 		return ENAMETOOLONG;
379 
380 	(void)memcpy(buf, rdata.d.path, rdata.d.len);
381 	buf[rdata.d.len] = 0;
382 	return 0;
383 }
384 #endif
385 
386 /*
387  * Read data from a file.
388  * Return transfer count or -1 (and set errno)
389  */
390 ssize_t
nfs_readdata(struct nfs_iodesc * d,off_t off,void * addr,size_t len)391 nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
392 {
393 	struct argsv2 {
394 		u_char	fh[NFS_FHSIZE];
395 		n_long	off;
396 		n_long	len;
397 		n_long	xxx;			/* XXX what's this for? */
398 	} *argsv2;
399 	struct argsv3 {
400 		u_char	fh[NFS_FHSTORE];
401 		n_long	off[2];
402 		n_long	len;
403 	} *argsv3;
404 	struct replv2 {
405 		n_long	errno;
406 		struct	nfsv2_fattr fa;
407 		n_long	count;
408 		u_char	data[NFSREAD_SIZE];
409 	} *replv2;
410 	struct replv3 {
411 		n_long	errno;
412 		n_long	attrflag;
413 		struct	nfsv3_fattr fa;
414 		n_long	count;
415 		n_long	eof;
416 		n_long	length;
417 		u_char	data[NFSREAD_SIZE];
418 	} *replv3;
419 	struct replv3_noattr {
420 		n_long	errno;
421 		n_long	attrflag;
422 		n_long	count;
423 		n_long	eof;
424 		n_long	length;
425 		u_char	data[NFSREAD_SIZE];
426 	} *replv3no;
427 	struct {
428 		n_long	h[RPC_HEADER_WORDS];
429 		union {
430 			struct argsv2 v2;
431 			struct argsv3 v3;
432 		} u_d;
433 	} sdata;
434 	struct {
435 		n_long	h[RPC_HEADER_WORDS];
436 		union {
437 			struct replv2 v2;
438 			struct replv3 v3;
439 		} u_d;
440 	} rdata;
441 	ssize_t cc;
442 	long x;
443 	size_t hlen, rlen, alen;
444 	u_char *data;
445 
446 	argsv2 = &sdata.u_d.v2;
447 	argsv3 = &sdata.u_d.v3;
448 	replv2 = &rdata.u_d.v2;
449 	replv3 = &rdata.u_d.v3;
450 
451 	if (len > NFSREAD_SIZE)
452 		len = NFSREAD_SIZE;
453 
454 	switch (d->version) {
455 	case NFS_VER2:
456 		getfh(d, argsv2->fh);
457 		argsv2->off = htonl((n_long)off);
458 		argsv2->len = htonl((n_long)len);
459 		argsv2->xxx = htonl((n_long)0);
460 		hlen = sizeof(*replv2) - NFSREAD_SIZE;
461 		cc = rpc_call(d->iodesc, NFS_PROG, d->version, NFSPROC_READ,
462 		    argsv2, sizeof(*argsv2),
463 		    replv2, sizeof(*replv2));
464 		break;
465 	case NFS_VER3:
466 		getfh(d, argsv3->fh);
467 		setnquad(argsv3->off, (uint64_t)off);
468 		argsv3->len = htonl((n_long)len);
469 		hlen = sizeof(*replv3) - NFSREAD_SIZE;
470 
471 		/* adjust for variable sized file handle */
472 		alen = sizeof(*argsv3) - offsetof(struct argsv3, off);
473 		memmove(argsv3->fh + fhstore(d->version, argsv3->fh),
474 		    &argsv3->off, alen);
475 		alen += fhstore(d->version, argsv3->fh);
476 
477 		cc = rpc_call(d->iodesc, NFS_PROG, d->version, NFSPROC_READ,
478 		    argsv3, alen,
479 		    replv3, sizeof(*replv3));
480 		break;
481 	default:
482 		errno = ENOSYS;
483 		return -1;
484 	}
485 
486 	if (cc == -1) {
487 		/* errno was already set by rpc_call */
488 		return -1;
489 	}
490 	if (cc < (ssize_t)hlen) {
491 		errno = EBADRPC;
492 		return -1;
493 	}
494 
495 	switch (d->version) {
496 	case NFS_VER2:
497 		if (replv2->errno) {
498 			errno = ntohl(replv2->errno);
499 			return -1;
500 		}
501 		x = ntohl(replv2->count);
502 		data = replv2->data;
503 		break;
504 	case NFS_VER3:
505 		if (replv3->errno) {
506 			errno = ntohl(replv3->errno);
507 			return -1;
508 		}
509 
510 		/* adjust for optional attributes */
511 		if (replv3->attrflag) {
512 			x = ntohl(replv3->length);
513 			data = replv3->data;
514 		} else {
515 			replv3no = (struct replv3_noattr *)replv3;
516 			x = ntohl(replv3no->length);
517 			data = replv3no->data;
518 		}
519 		break;
520 	default:
521 		errno = ENOSYS;
522 		return -1;
523 	}
524 
525 	rlen = cc - hlen;
526 	if (rlen < (size_t)x) {
527 		printf("%s: short packet, %zu < %ld\n", __func__, rlen, x);
528 		errno = EBADRPC;
529 		return -1;
530 	}
531 	(void)memcpy(addr, data, x);
532 	return x;
533 }
534 
535 /*
536  * nfs_mount - mount this nfs filesystem to a host
537  * On error, return non-zero and set errno.
538  */
539 int
nfs_mount(int sock,struct in_addr ip,char * path)540 nfs_mount(int sock, struct in_addr ip, char *path)
541 {
542 	struct iodesc *desc;
543 	struct nfsv2_fattr *fa2;
544 	struct nfsv3_fattr *fa3;
545 
546 	if (!(desc = socktodesc(sock))) {
547 		errno = EINVAL;
548 		return -1;
549 	}
550 
551 	/* Bind to a reserved port. */
552 	desc->myport = htons(--rpc_port);
553 	desc->destip = ip;
554 	if (nfs_getrootfh(desc, path, nfs_root_node.fh, &nfs_root_node.version))
555 		return -1;
556 	nfs_root_node.iodesc = desc;
557 	/* Fake up attributes for the root dir. */
558 	switch (nfs_root_node.version) {
559 	case NFS_VER2:
560 		fa2 = &nfs_root_node.u_fa.v2;
561 		fa2->fa_type  = htonl(NFDIR);
562 		fa2->fa_mode  = htonl(0755);
563 		fa2->fa_nlink = htonl(2);
564 		break;
565 	case NFS_VER3:
566 		fa3 = &nfs_root_node.u_fa.v3;
567 		fa3->fa_type  = htonl(NFDIR);
568 		fa3->fa_mode  = htonl(0755);
569 		fa3->fa_nlink = htonl(2);
570 		break;
571 	default:
572 		errno = ENOSYS;
573 		return -1;
574 	}
575 
576 #ifdef NFS_DEBUG
577 	if (debug)
578 		printf("%s: got fh for %s\n", __func__, path);
579 #endif
580 
581 	return 0;
582 }
583 
584 /*
585  * Open a file.
586  * return zero or error number
587  */
588 __compactcall int
nfs_open(const char * path,struct open_file * f)589 nfs_open(const char *path, struct open_file *f)
590 {
591 	struct nfs_iodesc *newfd, *currfd;
592 	const char *cp;
593 #ifndef NFS_NOSYMLINK
594 	const char *ncp;
595 	int c;
596 	char namebuf[NFS_MAXPATHLEN + 1];
597 	char linkbuf[NFS_MAXPATHLEN + 1];
598 	int nlinks = 0;
599 	n_long fa_type;
600 #endif
601 	int error = 0;
602 
603 #ifdef NFS_DEBUG
604  	if (debug)
605 		printf("%s: %s\n", __func__, path);
606 #endif
607 
608 #ifdef LIBSA_NFS_IMPLICIT_MOUNT
609 	if (nfs_mount(*((int *)(f->f_devdata)), rootip, rootpath))
610 		return errno;
611 #endif
612 
613 	if (nfs_root_node.iodesc == NULL) {
614 		printf("%s: must mount first.\n", __func__);
615 		return ENXIO;
616 	}
617 
618 	currfd = &nfs_root_node;
619 	newfd = 0;
620 
621 #ifndef NFS_NOSYMLINK
622 	cp = path;
623 	while (*cp) {
624 		/*
625 		 * Remove extra separators
626 		 */
627 		while (*cp == '/')
628 			cp++;
629 
630 		if (*cp == '\0')
631 			break;
632 		/*
633 		 * Check that current node is a directory.
634 		 */
635 		switch (currfd->version) {
636 		case NFS_VER2:
637 			fa_type = currfd->u_fa.v2.fa_type;
638 			break;
639 		case NFS_VER3:
640 			fa_type = currfd->u_fa.v3.fa_type;
641 			break;
642 		default:
643 			fa_type = htonl(NFNON);
644 			break;
645 		}
646 		if (fa_type != htonl(NFDIR)) {
647 			error = ENOTDIR;
648 			goto out;
649 		}
650 
651 		/* allocate file system specific data structure */
652 		newfd = alloc(sizeof(*newfd));
653 		newfd->iodesc = currfd->iodesc;
654 		newfd->off = 0;
655 		newfd->version = currfd->version;
656 
657 		/*
658 		 * Get next component of path name.
659 		 */
660 		{
661 			int len = 0;
662 
663 			ncp = cp;
664 			while ((c = *cp) != '\0' && c != '/') {
665 				if (++len > NFS_MAXNAMLEN) {
666 					error = ENOENT;
667 					goto out;
668 				}
669 				cp++;
670 			}
671 		}
672 
673 		/* lookup a file handle */
674 		error = nfs_lookupfh(currfd, ncp, cp - ncp, newfd);
675 		if (error)
676 			goto out;
677 
678 		/*
679 		 * Check for symbolic link
680 		 */
681 		switch (newfd->version) {
682 		case NFS_VER2:
683 			fa_type = newfd->u_fa.v2.fa_type;
684 			break;
685 		case NFS_VER3:
686 			fa_type = newfd->u_fa.v3.fa_type;
687 			break;
688 		default:
689 			fa_type = htonl(NFNON);
690 			break;
691 		}
692 		if (fa_type == htonl(NFLNK)) {
693 			int link_len, len;
694 
695 			error = nfs_readlink(newfd, linkbuf);
696 			if (error)
697 				goto out;
698 
699 			link_len = strlen(linkbuf);
700 			len = strlen(cp);
701 
702 			if (link_len + len > MAXPATHLEN
703 			    || ++nlinks > MAXSYMLINKS) {
704 				error = ENOENT;
705 				goto out;
706 			}
707 
708 			(void)memcpy(&namebuf[link_len], cp, len + 1);
709 			(void)memcpy(namebuf, linkbuf, link_len);
710 
711 			/*
712 			 * If absolute pathname, restart at root.
713 			 * If relative pathname, restart at parent directory.
714 			 */
715 			cp = namebuf;
716 			if (*cp == '/') {
717 				if (currfd != &nfs_root_node)
718 					dealloc(currfd, sizeof(*currfd));
719 				currfd = &nfs_root_node;
720 			}
721 
722 			dealloc(newfd, sizeof(*newfd));
723 			newfd = 0;
724 
725 			continue;
726 		}
727 
728 		if (currfd != &nfs_root_node)
729 			dealloc(currfd, sizeof(*currfd));
730 		currfd = newfd;
731 		newfd = 0;
732 	}
733 
734 	error = 0;
735 
736 out:
737 #else
738 	/* allocate file system specific data structure */
739 	currfd = alloc(sizeof(*currfd));
740 	currfd->iodesc = nfs_root_node.iodesc;
741 	currfd->off = 0;
742 	currfd->version = nfs_root_node.version;
743 
744 	cp = path;
745 	/*
746 	 * Remove extra separators
747 	 */
748 	while (*cp == '/')
749 		cp++;
750 
751 	/* XXX: Check for empty path here? */
752 
753 	error = nfs_lookupfh(&nfs_root_node, cp, strlen(cp), currfd);
754 #endif
755 	if (!error) {
756 		f->f_fsdata = (void *)currfd;
757 		fsmod = "nfs";
758 		return 0;
759 	}
760 
761 #ifdef NFS_DEBUG
762 	if (debug)
763 		printf("%s: %s lookupfh failed: %s\n", __func__,
764 		    path, strerror(error));
765 #endif
766 	if (currfd != &nfs_root_node)
767 		dealloc(currfd, sizeof(*currfd));
768 	if (newfd)
769 		dealloc(newfd, sizeof(*newfd));
770 
771 	return error;
772 }
773 
774 __compactcall int
nfs_close(struct open_file * f)775 nfs_close(struct open_file *f)
776 {
777 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
778 
779 #ifdef NFS_DEBUG
780 	if (debug)
781 		printf("%s: fp=%p\n", __func__, fp);
782 #endif
783 
784 	if (fp)
785 		dealloc(fp, sizeof(struct nfs_iodesc));
786 	f->f_fsdata = (void *)0;
787 
788 	return 0;
789 }
790 
791 /*
792  * read a portion of a file
793  */
794 __compactcall int
nfs_read(struct open_file * f,void * buf,size_t size,size_t * resid)795 nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
796 {
797 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
798 	ssize_t cc;
799 	char *addr = buf;
800 
801 #ifdef NFS_DEBUG
802 	if (debug)
803 		printf("%s: size=%zu off=%" PRIx64 "\n", __func__, size, fp->off);
804 #endif
805 	while ((int)size > 0) {
806 #if !defined(LIBSA_NO_TWIDDLE)
807 		twiddle();
808 #endif
809 		cc = nfs_readdata(fp, fp->off, (void *)addr, size);
810 		/* XXX maybe should retry on certain errors */
811 		if (cc == -1) {
812 #ifdef NFS_DEBUG
813 			if (debug)
814 				printf("%s: read: %s\n", __func__,
815 				    strerror(errno));
816 #endif
817 			return errno;	/* XXX - from nfs_readdata */
818 		}
819 		if (cc == 0) {
820 #ifdef NFS_DEBUG
821 			if (debug)
822 				printf("%s: hit EOF unexpectedly\n", __func__);
823 #endif
824 			goto ret;
825 		}
826 		fp->off += cc;
827 		addr += cc;
828 		size -= cc;
829 	}
830 ret:
831 	if (resid)
832 		*resid = size;
833 
834 	return 0;
835 }
836 
837 /*
838  * Not implemented.
839  */
840 __compactcall int
nfs_write(struct open_file * f,void * buf,size_t size,size_t * resid)841 nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
842 {
843 	return EROFS;
844 }
845 
846 __compactcall off_t
nfs_seek(struct open_file * f,off_t offset,int where)847 nfs_seek(struct open_file *f, off_t offset, int where)
848 {
849 	struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
850 	off_t size;
851 
852 	switch (d->version) {
853 	case NFS_VER2:
854 		size = ntohl(d->u_fa.v2.fa_size);
855 		break;
856 	case NFS_VER3:
857 		size = getnquad(d->u_fa.v3.fa_size);
858 		break;
859 	default:
860 		return -1;
861 	}
862 
863 	switch (where) {
864 	case SEEK_SET:
865 		d->off = offset;
866 		break;
867 	case SEEK_CUR:
868 		d->off += offset;
869 		break;
870 	case SEEK_END:
871 		d->off = size - offset;
872 		break;
873 	default:
874 		return -1;
875 	}
876 
877 	return d->off;
878 }
879 
880 /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 */
881 const int nfs_stat_types[8] = {
882 	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 0 };
883 
884 __compactcall int
nfs_stat(struct open_file * f,struct stat * sb)885 nfs_stat(struct open_file *f, struct stat *sb)
886 {
887 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
888 	n_long ftype, mode;
889 
890 	switch (fp->version) {
891 	case NFS_VER2:
892 		ftype = ntohl(fp->u_fa.v2.fa_type);
893 		mode  = ntohl(fp->u_fa.v2.fa_mode);
894 		sb->st_nlink = ntohl(fp->u_fa.v2.fa_nlink);
895 		sb->st_uid   = ntohl(fp->u_fa.v2.fa_uid);
896 		sb->st_gid   = ntohl(fp->u_fa.v2.fa_gid);
897 		sb->st_size  = ntohl(fp->u_fa.v2.fa_size);
898 		break;
899 	case NFS_VER3:
900 		ftype = ntohl(fp->u_fa.v3.fa_type);
901 		mode  = ntohl(fp->u_fa.v3.fa_mode);
902 		sb->st_nlink = ntohl(fp->u_fa.v3.fa_nlink);
903 		sb->st_uid   = ntohl(fp->u_fa.v3.fa_uid);
904 		sb->st_gid   = ntohl(fp->u_fa.v3.fa_gid);
905 		sb->st_size  = getnquad(fp->u_fa.v3.fa_size);
906 		break;
907 	default:
908 		return -1;
909 	}
910 
911 	mode |= nfs_stat_types[ftype & 7];
912 	sb->st_mode  = mode;
913 
914 	return 0;
915 }
916 
917 #if defined(LIBSA_ENABLE_LS_OP)
918 #include "ls.h"
919 __compactcall void
nfs_ls(struct open_file * f,const char * pattern)920 nfs_ls(struct open_file *f, const char *pattern)
921 {
922 	lsunsup("nfs");
923 }
924 #endif
925