Home | History | Annotate | Line # | Download | only in libsa
      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
     78 getnquad(n_long x[2]) {
     79 	return (uint64_t)ntohl(x[0]) << 32 | ntohl(x[1]);
     80 }
     81 
     82 static inline void
     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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    920 nfs_ls(struct open_file *f, const char *pattern)
    921 {
    922 	lsunsup("nfs");
    923 }
    924 #endif
    925