xref: /netbsd-src/sys/lib/libsa/nfs.c (revision d9158b13b5dfe46201430699a3f7a235ecf28df3)
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.1 1994/05/08 16:11:31 brezak 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_mtime.tv_sec);
175 		t = ntohl(rdata.fa.fa_atime.tv_sec);
176 		if (*tp < t)
177 			*tp = t;
178 	}
179 	if (sp)
180 		*sp = ntohl(rdata.fa.fa_size);
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_ctime.tv_sec);
249 	if (sp)
250 		*sp = ntohl(rdata.fa.fa_size);
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