Home | History | Annotate | Line # | Download | only in libsa
nfs.c revision 1.2
      1 /*-
      2  *  Copyright (c) 1993 John Brezak
      3  *  All rights reserved.
      4  *
      5  *  Redistribution and use in source and binary forms, with or without
      6  *  modification, are permitted provided that the following conditions
      7  *  are met:
      8  *  1. Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
     20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     24  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
     25  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  *
     28  *	$Id: nfs.c,v 1.2 1994/06/20 07:50:17 glass Exp $
     29  */
     30 
     31 #include <sys/param.h>
     32 #include <sys/time.h>
     33 #include <sys/socket.h>
     34 #include <sys/stat.h>
     35 #include <string.h>
     36 
     37 #include <netinet/in.h>
     38 #include <netinet/in_systm.h>
     39 
     40 #include <nfs/rpcv2.h>
     41 #include <nfs/nfsv2.h>
     42 #undef NFSX_FATTR
     43 
     44 #include "stand.h"
     45 #include "net.h"
     46 #include "netif.h"
     47 #include "nfs.h"
     48 #include "rpc.h"
     49 
     50 struct nfs_call_data {
     51 	u_char	fh[NFS_FHSIZE];
     52 	u_long	off;
     53 	u_long	len;
     54 	u_long	xxx;			/* XXX what's this for? */
     55 };
     56 
     57 /* Data part of nfs rpc reply (also the largest thing we receive) */
     58 struct nfs_reply_data {
     59 	u_long	errno;
     60 #ifndef NFSX_FATTR
     61 	struct	nfsv2_fattr fa;
     62 #else
     63 	u_char	fa[NFSX_FATTR(0)];
     64 #endif
     65 	u_long	count;
     66 	u_char	data[1200];
     67 };
     68 #define NFSREAD_SIZE sizeof(((struct nfs_reply_data *)0)->data)
     69 
     70 /* max number of nfs reads pending */
     71 #define NFS_COUNT 10
     72 
     73 static struct nfsstate {
     74 	u_long	off;
     75 	u_long	len;
     76 	int	done;
     77 	void	*addr;
     78 	u_long	xid;
     79 } nfsstate[NFS_COUNT];
     80 
     81 static u_long nfscc;
     82 
     83 struct nfs_iodesc {
     84 	off_t	off;
     85 	size_t	size;
     86 	u_char	*fh;
     87 	struct	iodesc	*iodesc;
     88 };
     89 
     90 /* Fetch (mount) file handle */
     91 static int
     92 getmountfh(d, path, fhp)
     93 	register struct iodesc *d;
     94 	char *path;
     95 	u_char *fhp;
     96 {
     97 	register int len;
     98 	struct {
     99 		u_long	len;
    100 		char	path[FNAME_SIZE];
    101 	} sdata;
    102 	struct {
    103 		u_long	errno;
    104 		u_char	fh[NFS_FHSIZE];
    105 	} rdata;
    106 	int cc;
    107 
    108 #ifdef NFS_DEBUG
    109 	if (debug)
    110 	    printf("getmountfh: called\n");
    111 #endif
    112 	bzero(&sdata, sizeof(sdata));
    113 	len = strlen(path);
    114 	if (len > sizeof(sdata.path))
    115 		len = sizeof(sdata.path);
    116 	bcopy(path, sdata.path, len);
    117 	sdata.len = htonl(len);
    118 	len = sizeof(sdata) - sizeof(sdata.path) + roundup(len, sizeof(long));
    119 
    120 	if ((cc = callrpc(d, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT,
    121 	    &sdata, len, &rdata, sizeof(rdata))) < 0)
    122 		    return(-1);
    123 	if (cc < sizeof(rdata.errno))
    124 		panic("getmountfh: callrpc small read");
    125 	if (rdata.errno) {
    126 		errno = ntohl(rdata.errno);
    127 		return(-1);
    128 	}
    129 	bcopy(rdata.fh, fhp, sizeof(rdata.fh));
    130 	return(0);
    131 }
    132 
    133 /* Fetch file timestamp and size */
    134 static int
    135 getnfsinfo(d, tp, sp, fp, mp, up, gp)
    136 	register struct nfs_iodesc *d;
    137 	register time_t *tp;
    138 	u_long *sp, *fp;
    139 	mode_t *mp;
    140 	uid_t *up;
    141 	gid_t *gp;
    142 {
    143 	register int rlen;
    144 	register u_long t;
    145 	struct {
    146 		u_long	errno;
    147 		struct	nfsv2_fattr fa;
    148 	} rdata;
    149 	int cc;
    150 
    151 #ifdef NFS_DEBUG
    152  	if (debug)
    153  	    printf("getnfsinfo: called\n");
    154 #endif
    155 	rlen = sizeof(rdata);
    156 #if 0
    157 #ifdef NFSX_FATTR
    158 #if NFSX_FATTR(1) > NFSX_FATTR(0)
    159 	/* nqnfs makes this more painful than it needs to be */
    160 	rlen -= NFSX_FATTR(1) - NFSX_FATTR(0);
    161 #endif
    162 #endif
    163 #endif
    164 	if ((cc = callrpc(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_GETATTR,
    165 		          d->fh, NFS_FHSIZE, &rdata, rlen)) < 0)
    166 		return(-1);
    167 	if (cc < sizeof(rdata.errno))
    168 		panic("getnfsinfo: callrpc small read");
    169 	if (rdata.errno) {
    170 		errno = ntohl(rdata.errno);
    171 		return(-1);
    172 	}
    173 	if (tp) {
    174 		*tp = ntohl(rdata.fa.fa_nfsmtime.nfs_sec);
    175 		t = ntohl(rdata.fa.fa_nfsatime.nfs_sec);
    176 		if (*tp < t)
    177 			*tp = t;
    178 	}
    179 	if (sp)
    180 		*sp = ntohl(rdata.fa.fa_nfssize);
    181 	if (fp)
    182 		*fp = ntohl(rdata.fa.fa_type);
    183 	if (mp)
    184 		*mp = ntohl(rdata.fa.fa_mode);
    185 	if (up)
    186 		*up = ntohl(rdata.fa.fa_uid);
    187 	if (gp)
    188 		*gp = ntohl(rdata.fa.fa_gid);
    189 	return(0);
    190 }
    191 
    192 /* Lookup a file. Optionally return timestamp and size */
    193 static int
    194 lookupfh(d, name, fhp, tp, sp, fp)
    195 	struct nfs_iodesc *d;
    196 	char *name;
    197 	u_char *fhp;
    198 	time_t *tp;
    199 	u_long *sp, *fp;
    200 {
    201 	register int len, rlen;
    202 	struct {
    203 		u_char	fh[NFS_FHSIZE];
    204 		u_long	len;
    205 		char	name[FNAME_SIZE];
    206 	} sdata;
    207 	struct {
    208 		u_long	errno;
    209 		u_char	fh[NFS_FHSIZE];
    210 		struct	nfsv2_fattr fa;
    211 	} rdata;
    212 	int cc;
    213 
    214 #ifdef NFS_DEBUG
    215 	if (debug)
    216 	    printf("lookupfh: called\n");
    217 #endif
    218 
    219 	bzero(&sdata, sizeof(sdata));
    220 	bcopy(d->fh, sdata.fh, sizeof(sdata.fh));
    221 	len = strlen(name);
    222 	if (len > sizeof(sdata.name))
    223 		len = sizeof(sdata.name);
    224 	bcopy(name, sdata.name, len);
    225 	sdata.len = htonl(len);
    226 	len = sizeof(sdata) - sizeof(sdata.name) + roundup(len, sizeof(long));
    227 
    228 	rlen = sizeof(rdata);
    229 #if 0
    230 #ifdef NFSX_FATTR
    231 #if NFSX_FATTR(1) > NFSX_FATTR(0)
    232 	/* nqnfs makes this more painful than it needs to be */
    233 	rlen -= NFSX_FATTR(1) - NFSX_FATTR(0);
    234 #endif
    235 #endif
    236 #endif
    237 	if ((cc = callrpc(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_LOOKUP,
    238 	    &sdata, len, &rdata, rlen)) < 0)
    239 		return (-1);
    240 	if (cc < sizeof(rdata.errno))
    241 		panic("lookupfh: callrpc small read");
    242 	if (rdata.errno) {
    243 		errno = ntohl(rdata.errno);
    244 		return(-1);
    245 	}
    246 	bcopy(rdata.fh, fhp, sizeof(rdata.fh));
    247 	if (tp)
    248 		*tp = ntohl(rdata.fa.fa_nfsctime.nfs_sec);
    249 	if (sp)
    250 		*sp = ntohl(rdata.fa.fa_nfssize);
    251 	if (fp)
    252 		*fp = ntohl(rdata.fa.fa_type);
    253 	return (0);
    254 }
    255 
    256 static int
    257 sendreaddata(d, pkt, len)
    258 	register struct nfs_iodesc *d;
    259 	register void *pkt;
    260 	register int len;
    261 {
    262 	register int i;
    263 	register u_long cc;
    264 	register struct rpc_call *rpc;
    265 	register struct nfs_call_data *nfs;
    266 	register struct nfsstate *ns;
    267 
    268 #ifdef NFS_DEBUG
    269 	if (debug)
    270 	    printf("sendreaddata: called\n");
    271 #endif
    272 
    273 	if (len != sizeof(*rpc) + sizeof(*nfs))
    274 		panic("sendreaddata: bad buffer (%d != %d)",
    275 		    len, sizeof(*rpc) + sizeof(*nfs));
    276 	rpc = pkt;
    277 	nfs = (struct nfs_call_data *)(rpc + 1);
    278 	for (i = 0, ns = nfsstate; i < NFS_COUNT; ++i, ++ns) {
    279 		if (ns->done)
    280 			continue;
    281 
    282 		rpc->rp_xid = ns->xid;
    283 		nfs->off = htonl(ns->off);
    284 		nfs->len = htonl(ns->len);
    285 		cc = sendudp(d->iodesc, rpc, len);
    286 
    287 		if (cc != len)
    288 			panic("sendreaddata: short write (%d != %d)", cc, len);
    289 	}
    290 	/* XXX we may have actually sent a lot more bytes... */
    291 
    292 	return (len);
    293 }
    294 
    295 /* Returns char count if done else -1 (and errno == 0) */
    296 static int
    297 recvreaddata(d, pkt, len)
    298 	register struct nfs_iodesc *d;
    299 	register void *pkt;
    300 	int len;
    301 {
    302 	register int i;
    303 	register struct rpc_reply *rpc;
    304 	register struct nfs_reply_data *nfs;
    305 	register struct nfsstate *ns;
    306 
    307 #ifdef NFS_DEBUG
    308 	if (debug)
    309 	    printf("recvreaddata: called\n");
    310 #endif
    311 	rpc = (struct rpc_reply *)checkudp(d->iodesc, pkt, &len);
    312 	if (rpc == NULL || len < sizeof(*rpc)) {
    313 		errno = 0;
    314 		return (-1);
    315 	}
    316 	len -= sizeof(*rpc);
    317 
    318 	NTOHL(rpc->rp_direction);
    319 	NTOHL(rpc->rp_stat);
    320 
    321 	if (rpc->rp_direction != REPLY || rpc->rp_stat != MSG_ACCEPTED) {
    322 		errno = 0;
    323 		return (-1);
    324 	}
    325 
    326 	for (i = 0, ns = nfsstate; i < NFS_COUNT; ++i, ++ns)
    327 		if (rpc->rp_xid == ns->xid)
    328 			break;
    329 	if (i >= NFS_COUNT) {
    330 		errno = 0;
    331 		return (-1);
    332 	}
    333 
    334 	if (ns->done) {
    335 		errno = 0;
    336 		return (-1);
    337 	}
    338 #ifdef NFS_DEBUG
    339 	if (debug)
    340 	    printf("recvreaddata: ns=%x\n", (u_int)ns);
    341 #endif
    342 	nfs = (struct nfs_reply_data *)(rpc + 1);
    343 	if (len < sizeof(nfs->errno))
    344 		panic("recvreaddata: bad read %d", len);
    345 	if (nfs->errno) {
    346 		errno = ntohl(nfs->errno);
    347 		return (-1);
    348 	}
    349 	if (len < sizeof(*nfs) - sizeof(nfs->data))
    350 		panic("recvreaddata: less than nfs sized %d", len);
    351 	len -= sizeof(*nfs) - sizeof(nfs->data);
    352 
    353 	if (len < nfs->count)
    354 		panic("recvreaddata: short read (%d < %d)", len, nfs->count);
    355 	len = nfs->count;
    356 	if (len > ns->len)
    357 		panic("recvreaddata: huge read (%d > %d)", len, ns->len);
    358 
    359 
    360 #ifdef NFS_DEBUG
    361 	if (debug)
    362 	    printf("recvreaddata: read %d bytes.\n", len);
    363 #endif
    364 	bcopy(nfs->data, ns->addr, len);
    365 	ns->done = 1;
    366 	nfscc += len;
    367 
    368 	if (len < ns->len) {
    369 		/* If first packet assume no more data to read */
    370 		if (i == 0)
    371 			return (0);
    372 
    373 		/* Short read, assume we are at EOF */
    374 		++i;
    375 		++ns;
    376 		while (i < NFS_COUNT) {
    377 			ns->done = 1;
    378 			++i;
    379 			++ns;
    380 		}
    381 	}
    382 
    383 	for (i = 0, ns = nfsstate; i < NFS_COUNT; ++i, ++ns)
    384 		if (!ns->done) {
    385 			errno = 0;
    386 			return (-1);
    387 		}
    388 
    389 	/* Return data count (thus indicating success) */
    390 	return (nfscc);
    391 }
    392 
    393 /* Read data from a file */
    394 static int
    395 readdata(d, off, addr, len)
    396 	register struct nfs_iodesc *d;
    397 	register u_long off;
    398 	register void *addr;
    399 	register u_long len;
    400 {
    401 	register int i, cc;
    402 	register struct rpc_call *rpc;
    403 	register struct nfsstate *ns;
    404 	struct {
    405 		u_char	header[HEADER_SIZE];
    406 		struct	rpc_call rpc;
    407 		struct	nfs_call_data nfs;
    408 	} sdata;
    409 	struct {
    410 		u_char	header[HEADER_SIZE];
    411 		struct	rpc_call rpc;
    412 		struct	nfs_reply_data nfs;
    413 	} rdata;
    414 
    415 #ifdef NFS_DEBUG
    416 	if (debug)
    417 	    printf("readdata: addr=%x, off=%d len=%d\n", (u_int)addr, off, len);
    418 #endif
    419 	if (len == 0)
    420 		return (0);
    421 	d->iodesc->destport = getport(d->iodesc, NFS_PROG, NFS_VER2);
    422 
    423 	bzero(&sdata, sizeof(sdata));
    424 
    425 	for (i = 0, ns = nfsstate; i < NFS_COUNT; ++i, ++ns) {
    426 		if (len <= 0) {
    427 			ns->done = 1;
    428 			continue;
    429 		}
    430 		ns->done = 0;
    431 
    432 		ns->xid = d->iodesc->xid;
    433 		++d->iodesc->xid;
    434 
    435 		ns->off = off;
    436 		ns->len = len;
    437 		if (ns->len > NFSREAD_SIZE)
    438 			ns->len = NFSREAD_SIZE;
    439 #ifdef notdef
    440 /* XXX to align or not align? It doesn't seem to speed things up... */
    441 		if ((ns->off % NFSREAD_SIZE) != 0)
    442 			ns->len -= off % NFSREAD_SIZE;
    443 #endif
    444 
    445 		off += ns->len;
    446 		len -= ns->len;
    447 
    448 		ns->addr = addr;
    449 		addr += NFSREAD_SIZE;
    450 	}
    451 
    452 	rpc = &sdata.rpc;
    453 	rpc->rp_rpcvers = htonl(RPC_MSG_VERSION);
    454 	rpc->rp_prog = htonl(NFS_PROG);
    455 	rpc->rp_vers = htonl(NFS_VER2);
    456 	rpc->rp_proc = htonl(NFSPROC_READ);
    457 	bcopy(d->fh, sdata.nfs.fh, sizeof(sdata.nfs.fh));
    458 
    459 	nfscc = 0;
    460 	cc = sendrecv(d->iodesc,
    461 		      sendreaddata, &sdata.rpc,
    462 		      sizeof(struct rpc_call) + sizeof(struct nfs_call_data),
    463 		      recvreaddata,
    464 			((u_char *)&rdata.rpc) - HEADER_SIZE, HEADER_SIZE +
    465 			  sizeof(struct rpc_call) + sizeof(struct nfs_reply_data));
    466 	return (cc);
    467 }
    468 
    469 static struct iodesc *mountfs;
    470 static u_char mountfh[NFS_FHSIZE];
    471 static time_t mounttime;
    472 
    473 /*
    474  * nfs_mount - mount this nfs filesystem to a host
    475  */
    476 int
    477 nfs_mount(sock, ip, path)
    478 	int sock;
    479 	n_long ip;
    480 	char *path;
    481 {
    482 	struct iodesc *desc;
    483 	struct nfs_iodesc *fp;
    484 	u_long ftype;
    485 
    486 	if (!(desc = socktodesc(sock))) {
    487 		errno = EINVAL;
    488 		return(-1);
    489 	}
    490 	bcopy(&desc->myea[4], &desc->myport, 2);
    491 	desc->destip = ip;
    492 	getmountfh(desc, path, mountfh);
    493 
    494 	fp = alloc(sizeof(struct nfs_iodesc));
    495 	fp->iodesc = desc;
    496 	fp->fh = mountfh;
    497 	fp->off = 0;
    498 	if (getnfsinfo(fp, &mounttime, NULL, &ftype, NULL, NULL, NULL) < 0) {
    499 		free(fp, sizeof(struct nfs_iodesc));
    500 		return(-1);
    501 	}
    502 
    503 	if (ftype != NFDIR) {
    504 		free(fp, sizeof(struct nfs_iodesc));
    505 	    	errno = EINVAL;
    506 		printf("nfs_mount: bad mount ftype %d", ftype);
    507 		return(-1);
    508 	}
    509 #ifdef NFS_DEBUG
    510 	if (debug)
    511 		printf("nfs_mount: got fh for %s, mtime=%d, ftype=%d\n",
    512 			path, mounttime, ftype);
    513 #endif
    514 	mountfs = desc;
    515 	free(fp, sizeof(struct nfs_iodesc));
    516 
    517 	return(0);
    518 }
    519 
    520 /*
    521  * Open a file.
    522  */
    523 int
    524 nfs_open(path, f)
    525 	char *path;
    526 	struct open_file *f;
    527 {
    528 	register struct nfs_iodesc *fp;
    529 	u_char *imagefh;
    530 	u_long size, ftype;
    531 	int rc = 0;
    532 
    533 #ifdef NFS_DEBUG
    534  	if (debug)
    535  	    printf("nfs_open: %s\n", path);
    536 #endif
    537 	if (!mountfs) {
    538 		errno = EIO;
    539 		printf("nfs_open: must mount first.\n");
    540 		return(-1);
    541 	}
    542 
    543 	/* allocate file system specific data structure */
    544 	fp = alloc(sizeof(struct nfs_iodesc));
    545 	fp->iodesc = mountfs;
    546 	fp->fh = mountfh;
    547 	fp->off = 0;
    548 
    549 	f->f_fsdata = (void *)fp;
    550 	imagefh = alloc(NFS_FHSIZE);
    551 	bzero(imagefh, NFS_FHSIZE);
    552 
    553 	/* lookup a file handle */
    554 	rc = lookupfh(fp, path, imagefh, NULL, &size, &ftype);
    555 	if (rc < 0) {
    556 #ifdef NFS_DEBUG
    557 		if (debug)
    558 			printf("nfs_open: %s lookupfh failed: %s\n", path, strerror(errno));
    559 #endif
    560 		f->f_fsdata = (void *)0;
    561 		free(fp, sizeof(struct nfs_iodesc));
    562 		free(imagefh, NFS_FHSIZE);
    563 		return(rc);
    564 	}
    565 	fp->fh = imagefh;
    566 
    567 #ifdef NFS_DEBUG
    568 	if (debug)
    569 		printf("nfs_open: %s success, size=%d ftype=%d\n",
    570 			path, size, ftype);
    571 #endif
    572 	fp->size = size;
    573 
    574 	return(rc);
    575 }
    576 
    577 int
    578 nfs_close(f)
    579 	struct open_file *f;
    580 {
    581 	register struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
    582 
    583 #ifdef NFS_DEBUG
    584 	if (debug)
    585 		printf("nfs_close: called\n");
    586 #endif
    587 	f->f_fsdata = (void *)0;
    588 	if (fp == (struct nfs_iodesc *)0)
    589 		return (0);
    590 
    591 	free(fp->fh, NFS_FHSIZE);
    592 	free(fp, sizeof(struct nfs_iodesc));
    593 
    594 	return (0);
    595 }
    596 
    597 /*
    598  * read a portion of a file
    599  */
    600 int
    601 nfs_read(f, addr, size, resid)
    602 	struct open_file *f;
    603 	char *addr;
    604 	u_int size;
    605 	u_int *resid;	/* out */
    606 {
    607 	register struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
    608 	register int cc;
    609 
    610 #ifdef NFS_DEBUG
    611 	if (debug)
    612 		printf("nfs_read: size=%d off=%d\n", size, fp->off);
    613 #endif
    614 	while (size > 0) {
    615 		cc = readdata(fp->iodesc, fp->off, (void *)addr, size);
    616 		if (cc <= 0) {
    617 			/* XXX maybe should retry on certain errors */
    618 			if (cc < 0) {
    619 #ifdef NFS_DEBUG
    620 				if (debug)
    621 					printf("nfs_read: read: %s",
    622 						strerror(errno));
    623 #endif
    624 				return (-1);
    625 			}
    626 			if (debug)
    627 				printf("nfs_read: hit EOF unexpectantly");
    628 			goto ret;
    629 		}
    630 		fp->off += cc;
    631 		addr += cc;
    632 		size -= cc;
    633 	}
    634 ret:
    635 	if (resid)
    636 		*resid = size;
    637 
    638 	return (0);
    639 }
    640 
    641 /*
    642  * Not implemented.
    643  */
    644 int
    645 nfs_write(f, start, size, resid)
    646 	struct open_file *f;
    647 	char *start;
    648 	u_int size;
    649 	u_int *resid;	/* out */
    650 {
    651 	errno = EROFS;
    652 
    653 	return (-1);
    654 }
    655 
    656 off_t
    657 nfs_seek(f, offset, where)
    658 	struct open_file *f;
    659 	off_t offset;
    660 	int where;
    661 {
    662 	register struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
    663 
    664 	switch (where) {
    665 	case SEEK_SET:
    666 		fp->off = offset;
    667 		break;
    668 	case SEEK_CUR:
    669 		fp->off += offset;
    670 		break;
    671 	case SEEK_END:
    672 		fp->off = fp->size - offset;
    673 		break;
    674 	default:
    675 		return (-1);
    676 	}
    677 	return (fp->off);
    678 }
    679 
    680 int
    681 nfs_stat(f, sb)
    682 	struct open_file *f;
    683 	struct stat *sb;
    684 {
    685 	register struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
    686 	mode_t mode = 0;
    687 	u_long ftype = 0;
    688 
    689 #ifdef NFS_DEBUG
    690  	if (debug)
    691  	    printf("nfs_stat: called\n");
    692 #endif
    693 	if (getnfsinfo(fp, &mounttime, &sb->st_size, &ftype, &mode, &sb->st_uid, &sb->st_gid) < 0)
    694 		return(-1);
    695 
    696 	/* create a mode */
    697 	switch (ftype) {
    698 	case NFNON:
    699 		sb->st_mode = 0;
    700 		break;
    701 	case NFREG:
    702 		sb->st_mode = S_IFREG;
    703 		break;
    704 	case NFDIR:
    705 		sb->st_mode = S_IFDIR;
    706 		break;
    707 	case NFBLK:
    708 		sb->st_mode = S_IFBLK;
    709 		break;
    710 	case NFCHR:
    711 		sb->st_mode = S_IFCHR;
    712 		break;
    713 	case NFLNK:
    714 		sb->st_mode = S_IFLNK;
    715 		break;
    716 	}
    717 	sb->st_mode |= mode;
    718 
    719 	return (0);
    720 }
    721