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