pxeboot - Add option to improve NFS performance
[dragonfly.git] / lib / libstand / nfs.c
1 /* $FreeBSD: src/lib/libstand/nfs.c,v 1.2.6.3 2000/09/10 01:33:25 ps Exp $ */
2 /*      $NetBSD: nfs.c,v 1.2 1998/01/24 12:43:09 drochner Exp $ */
3
4 /*-
5  *  Copyright (c) 1993 John Brezak
6  *  All rights reserved.
7  * 
8  *  Redistribution and use in source and binary forms, with or without
9  *  modification, are permitted provided that the following conditions
10  *  are met:
11  *  1. Redistributions of source code must retain the above copyright
12  *     notice, this list of conditions and the following disclaimer.
13  *  2. Redistributions in binary form must reproduce the above copyright
14  *     notice, this list of conditions and the following disclaimer in the
15  *     documentation and/or other materials provided with the distribution.
16  *  3. The name of the author may not be used to endorse or promote products
17  *     derived from this software without specific prior written permission.
18  * 
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
23  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include <sys/param.h>
33 #include <sys/time.h>
34 #include <sys/socket.h>
35 #include <sys/stat.h>
36 #include <string.h>
37 #include <stddef.h>
38
39 #include <netinet/in.h>
40 #include <netinet/in_systm.h>
41
42 #include "rpcv2.h"
43 #include "nfsv2.h"
44
45 #include "stand.h"
46 #include "net.h"
47 #include "netif.h"
48 #include "rpc.h"
49
50 #define NFS_DEBUGxx
51
52 /* Define our own NFS attributes without NQNFS stuff. */
53 struct nfsv2_fattrs {
54         n_long  fa_type;
55         n_long  fa_mode;
56         n_long  fa_nlink;
57         n_long  fa_uid;
58         n_long  fa_gid;
59         n_long  fa_size;
60         n_long  fa_blocksize;
61         n_long  fa_rdev;
62         n_long  fa_blocks;
63         n_long  fa_fsid;
64         n_long  fa_fileid;
65         struct nfsv2_time fa_atime;
66         struct nfsv2_time fa_mtime;
67         struct nfsv2_time fa_ctime;
68 };
69
70
71 struct nfs_read_args {
72         u_char  fh[NFS_FHSIZE];
73         n_long  off;
74         n_long  len;
75         n_long  xxx;                    /* XXX what's this for? */
76 };
77
78 /*
79  * Data part of nfs rpc reply (also the largest thing we receive).
80  * Worry about the size of the structure declared on the stack.
81  */
82
83 #define NFSREAD_MIN_SIZE 1024
84 #define NFSREAD_MAX_SIZE 4096
85
86 struct nfs_read_repl {
87         n_long  errno;
88         struct  nfsv2_fattrs fa;
89         n_long  count;
90         u_char  data[NFSREAD_MAX_SIZE];
91 };
92
93 #ifndef NFS_NOSYMLINK
94 struct nfs_readlnk_repl {
95         n_long  errno;
96         n_long  len;
97         char    path[NFS_MAXPATHLEN];
98 };
99 #endif
100
101 struct nfs_readdir_args {
102         u_char  fh[NFS_FHSIZE];
103         n_long  cookie;
104         n_long  count;
105 };
106
107 struct nfs_readdir_data {
108         n_long  fileid;
109         n_long  len;
110         char    name[0];
111 };
112
113 struct nfs_readdir_off {
114         n_long  cookie;
115         n_long  follows;
116 };
117
118 struct nfs_iodesc {
119         struct  iodesc  *iodesc;
120         off_t   off;
121         u_char  fh[NFS_FHSIZE];
122         struct nfsv2_fattrs fa; /* all in network order */
123 };
124
125 /*
126  * XXX interactions with tftp? See nfswrapper.c for a confusing
127  *     issue.
128  */
129 int             nfs_open(const char *path, struct open_file *f);
130 static int      nfs_close(struct open_file *f);
131 static int      nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
132 static int      nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid);
133 static off_t    nfs_seek(struct open_file *f, off_t offset, int where);
134 static int      nfs_stat(struct open_file *f, struct stat *sb);
135 static int      nfs_readdir(struct open_file *f, struct dirent *d);
136
137 struct  nfs_iodesc nfs_root_node;
138
139 struct fs_ops nfs_fsops = {
140         "nfs",
141         nfs_open,
142         nfs_close,
143         nfs_read,
144         nfs_write,
145         nfs_seek,
146         nfs_stat,
147         nfs_readdir
148 };
149
150 static int nfs_read_size = NFSREAD_MIN_SIZE;
151
152 /*
153  * Fetch the root file handle (call mount daemon)
154  * Return zero or error number.
155  */
156 int
157 nfs_getrootfh(struct iodesc *d, char *path, u_char *fhp)
158 {
159         int len;
160         struct args {
161                 n_long  len;
162                 char    path[FNAME_SIZE];
163         } *args;
164         struct repl {
165                 n_long  errno;
166                 u_char  fh[NFS_FHSIZE];
167         } *repl;
168         struct {
169                 n_long  h[RPC_HEADER_WORDS];
170                 struct args d;
171         } sdata;
172         struct {
173                 n_long  h[RPC_HEADER_WORDS];
174                 struct repl d;
175         } rdata;
176         size_t cc;
177         
178 #ifdef NFS_DEBUG
179         if (debug)
180                 printf("nfs_getrootfh: %s\n", path);
181 #endif
182
183         args = &sdata.d;
184         repl = &rdata.d;
185
186         bzero(args, sizeof(*args));
187         len = strlen(path);
188         if (len > sizeof(args->path))
189                 len = sizeof(args->path);
190         args->len = htonl(len);
191         bcopy(path, args->path, len);
192         len = 4 + roundup(len, 4);
193
194         cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT,
195             args, len, repl, sizeof(*repl));
196         if (cc == -1) {
197                 /* errno was set by rpc_call */
198                 return (errno);
199         }
200         if (cc < 4)
201                 return (EBADRPC);
202         if (repl->errno)
203                 return (ntohl(repl->errno));
204         bcopy(repl->fh, fhp, sizeof(repl->fh));
205
206         /*
207          * Improve boot performance over NFS
208          */
209         if (getenv("nfs.read_size") != NULL)
210                 nfs_read_size = strtol(getenv("nfs.read_size"), NULL, 0);
211         if (nfs_read_size < NFSREAD_MIN_SIZE)
212                 nfs_read_size = NFSREAD_MIN_SIZE;
213         if (nfs_read_size > NFSREAD_MAX_SIZE)
214                 nfs_read_size = NFSREAD_MAX_SIZE;
215
216         return (0);
217 }
218
219 /*
220  * Lookup a file.  Store handle and attributes.
221  * Return zero or error number.
222  */
223 int
224 nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd)
225 {
226         int len, rlen;
227         struct args {
228                 u_char  fh[NFS_FHSIZE];
229                 n_long  len;
230                 char    name[FNAME_SIZE];
231         } *args;
232         struct repl {
233                 n_long  errno;
234                 u_char  fh[NFS_FHSIZE];
235                 struct  nfsv2_fattrs fa;
236         } *repl;
237         struct {
238                 n_long  h[RPC_HEADER_WORDS];
239                 struct args d;
240         } sdata;
241         struct {
242                 n_long  h[RPC_HEADER_WORDS];
243                 struct repl d;
244         } rdata;
245         ssize_t cc;
246         
247 #ifdef NFS_DEBUG
248         if (debug)
249                 printf("lookupfh: called\n");
250 #endif
251
252         args = &sdata.d;
253         repl = &rdata.d;
254
255         bzero(args, sizeof(*args));
256         bcopy(d->fh, args->fh, sizeof(args->fh));
257         len = strlen(name);
258         if (len > sizeof(args->name))
259                 len = sizeof(args->name);
260         bcopy(name, args->name, len);
261         args->len = htonl(len);
262         len = 4 + roundup(len, 4);
263         len += NFS_FHSIZE;
264
265         rlen = sizeof(*repl);
266
267         cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_LOOKUP,
268             args, len, repl, rlen);
269         if (cc == -1)
270                 return (errno);         /* XXX - from rpc_call */
271         if (cc < 4)
272                 return (EIO);
273         if (repl->errno) {
274                 /* saerrno.h now matches NFS error numbers. */
275                 return (ntohl(repl->errno));
276         }
277         bcopy( repl->fh, &newfd->fh, sizeof(newfd->fh));
278         bcopy(&repl->fa, &newfd->fa, sizeof(newfd->fa));
279         return (0);
280 }
281
282 #ifndef NFS_NOSYMLINK
283 /*
284  * Get the destination of a symbolic link.
285  */
286 int
287 nfs_readlink(struct nfs_iodesc *d, char *buf)
288 {
289         struct {
290                 n_long  h[RPC_HEADER_WORDS];
291                 u_char fh[NFS_FHSIZE];
292         } sdata;
293         struct {
294                 n_long  h[RPC_HEADER_WORDS];
295                 struct nfs_readlnk_repl d;
296         } rdata;
297         ssize_t cc;
298
299 #ifdef NFS_DEBUG
300         if (debug)
301                 printf("readlink: called\n");
302 #endif
303
304         bcopy(d->fh, sdata.fh, NFS_FHSIZE);
305         cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READLINK,
306                       sdata.fh, NFS_FHSIZE,
307                       &rdata.d, sizeof(rdata.d));
308         if (cc == -1)
309                 return (errno);
310
311         if (cc < 4)
312                 return (EIO);
313         
314         if (rdata.d.errno)
315                 return (ntohl(rdata.d.errno));
316
317         rdata.d.len = ntohl(rdata.d.len);
318         if (rdata.d.len > NFS_MAXPATHLEN)
319                 return (ENAMETOOLONG);
320
321         bcopy(rdata.d.path, buf, rdata.d.len);
322         buf[rdata.d.len] = 0;
323         return (0);
324 }
325 #endif
326
327 /*
328  * Read data from a file.
329  * Return transfer count or -1 (and set errno)
330  */
331 ssize_t
332 nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
333 {
334         struct nfs_read_args *args;
335         struct nfs_read_repl *repl;
336         struct {
337                 n_long  h[RPC_HEADER_WORDS];
338                 struct nfs_read_args d;
339         } sdata;
340         struct {
341                 n_long  h[RPC_HEADER_WORDS];
342                 struct nfs_read_repl d;
343         } rdata;
344         size_t cc;
345         long x;
346         int hlen, rlen;
347
348         args = &sdata.d;
349         repl = &rdata.d;
350
351         bcopy(d->fh, args->fh, NFS_FHSIZE);
352         args->off = htonl((n_long)off);
353         if (len > nfs_read_size)
354                 len = nfs_read_size;
355         args->len = htonl((n_long)len);
356         args->xxx = htonl((n_long)0);
357         hlen = offsetof(struct nfs_read_repl, data[0]);
358
359         cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READ,
360             args, sizeof(*args),
361             repl, sizeof(*repl));
362         if (cc == -1) {
363                 /* errno was already set by rpc_call */
364                 return (-1);
365         }
366         if (cc < hlen) {
367                 errno = EBADRPC;
368                 return (-1);
369         }
370         if (repl->errno) {
371                 errno = ntohl(repl->errno);
372                 return (-1);
373         }
374         rlen = cc - hlen;
375         x = ntohl(repl->count);
376         if (rlen < x) {
377                 printf("nfsread: short packet, %d < %ld\n", rlen, x);
378                 errno = EBADRPC;
379                 return(-1);
380         }
381         bcopy(repl->data, addr, x);
382         return (x);
383 }
384
385 /*
386  * Open a file.
387  * return zero or error number
388  */
389 int
390 nfs_open(const char *upath, struct open_file *f)
391 {
392         struct iodesc *desc;
393         struct nfs_iodesc *currfd;
394 #ifndef NFS_NOSYMLINK
395         struct nfs_iodesc *newfd;
396         struct nfsv2_fattrs *fa;
397         char *cp, *ncp;
398         int c;
399         char namebuf[NFS_MAXPATHLEN + 1];
400         char linkbuf[NFS_MAXPATHLEN + 1];
401         int nlinks = 0;
402 #endif
403         int error;
404         char *path;
405
406 #ifdef NFS_DEBUG
407         if (debug)
408             printf("nfs_open: %s (rootpath=%s)\n", path, rootpath);
409 #endif
410         if (!rootpath[0]) {
411                 printf("no rootpath, no nfs\n");
412                 return (ENXIO);
413         }
414
415         if (!(desc = socktodesc(*(int *)(f->f_devdata))))
416                 return(EINVAL);
417
418         /* Bind to a reserved port. */
419         desc->myport = htons(rpc_newport());
420         desc->destip = rootip;
421         if ((error = nfs_getrootfh(desc, rootpath, nfs_root_node.fh)))
422                 return (error);
423         nfs_root_node.iodesc = desc;
424
425 #ifndef NFS_NOSYMLINK
426         /* Fake up attributes for the root dir. */
427         fa = &nfs_root_node.fa;
428         fa->fa_type  = htonl(NFDIR);
429         fa->fa_mode  = htonl(0755);
430         fa->fa_nlink = htonl(2);
431
432         currfd = &nfs_root_node;
433         newfd = NULL;
434
435         cp = path = strdup(upath);
436         if (path == NULL) {
437             error = ENOMEM;
438             goto out;
439         }
440         while (*cp) {
441                 /*
442                  * Remove extra separators
443                  */
444                 while (*cp == '/')
445                         cp++;
446
447                 if (*cp == '\0')
448                         break;
449                 /*
450                  * Check that current node is a directory.
451                  */
452                 if (currfd->fa.fa_type != htonl(NFDIR)) {
453                         error = ENOTDIR;
454                         goto out;
455                 }
456                 
457                 /* allocate file system specific data structure */
458                 newfd = malloc(sizeof(*newfd));
459                 newfd->iodesc = currfd->iodesc;
460                 newfd->off = 0;
461         
462                 /*
463                  * Get next component of path name.
464                  */
465                 {
466                         int len = 0;
467                         
468                         ncp = cp;
469                         while ((c = *cp) != '\0' && c != '/') {
470                                 if (++len > NFS_MAXNAMLEN) {
471                                         error = ENOENT;
472                                         goto out;
473                                 }
474                                 cp++;
475                         }
476                         *cp = '\0';
477                 }
478                 
479                 /* lookup a file handle */
480                 error = nfs_lookupfh(currfd, ncp, newfd);
481                 *cp = c;
482                 if (error)
483                         goto out;
484                 
485                 /*
486                  * Check for symbolic link
487                  */
488                 if (newfd->fa.fa_type == htonl(NFLNK)) {
489                         int link_len, len;
490                         
491                         error = nfs_readlink(newfd, linkbuf);
492                         if (error)
493                                 goto out;
494
495                         link_len = strlen(linkbuf);
496                         len = strlen(cp);
497
498                         if (link_len + len > MAXPATHLEN
499                             || ++nlinks > MAXSYMLINKS) {
500                                 error = ENOENT;
501                                 goto out;
502                         }
503
504                         bcopy(cp, &namebuf[link_len], len + 1);
505                         bcopy(linkbuf, namebuf, link_len);
506                         
507                         /*
508                          * If absolute pathname, restart at root.
509                          * If relative pathname, restart at parent directory.
510                          */
511                         cp = namebuf;
512                         if (*cp == '/') {
513                                 if (currfd != &nfs_root_node)
514                                         free(currfd);
515                                 currfd = &nfs_root_node;
516                         }
517
518                         free(newfd);
519                         newfd = NULL;
520                         
521                         continue;
522                 }
523                 
524                 if (currfd != &nfs_root_node)
525                         free(currfd);
526                 currfd = newfd;
527                 newfd = NULL;
528         }
529
530         error = 0;
531
532 out:
533         if (newfd)
534                 free(newfd);
535         if (path)
536                 free(path);
537 #else
538         /* allocate file system specific data structure */
539         currfd = malloc(sizeof(*currfd));
540         currfd->iodesc = desc;
541         currfd->off = 0;
542
543         error = nfs_lookupfh(&nfs_root_node, upath, currfd);
544 #endif
545         if (!error) {
546                 f->f_fsdata = (void *)currfd;
547                 return (0);
548         }
549                 
550 #ifdef NFS_DEBUG
551         if (debug)
552                 printf("nfs_open: %s lookupfh failed: %s\n",
553                     path, strerror(error));
554 #endif
555 #ifndef NFS_NOSYMLINK
556         if (currfd != &nfs_root_node)
557 #endif
558                 free(currfd);
559
560         return (error);
561 }
562
563 int
564 nfs_close(struct open_file *f)
565 {
566         struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
567
568 #ifdef NFS_DEBUG
569         if (debug)
570                 printf("nfs_close: fp=0x%lx\n", (u_long)fp);
571 #endif
572
573         f->f_fsdata = NULL;
574         if (fp && fp != &nfs_root_node)
575                 free(fp);
576         
577         return (0);
578 }
579
580 /*
581  * read a portion of a file
582  *
583  * Parameters:
584  *      resid:  out
585  */
586 int
587 nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
588 {
589         struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
590         ssize_t cc;
591         char *addr = buf;
592         
593 #ifdef NFS_DEBUG
594         if (debug)
595                 printf("nfs_read: size=%lu off=%d\n", (u_long)size,
596                        (int)fp->off);
597 #endif
598         while ((int)size > 0) {
599                 twiddle();
600                 cc = nfs_readdata(fp, fp->off, addr, size);
601                 /* XXX maybe should retry on certain errors */
602                 if (cc == -1) {
603 #ifdef NFS_DEBUG
604                         if (debug)
605                                 printf("nfs_read: read: %s", strerror(errno));
606 #endif
607                         return (errno); /* XXX - from nfs_readdata */
608                 }
609                 if (cc == 0) {
610 #ifdef NFS_DEBUG
611                         if (debug)
612                                 printf("nfs_read: hit EOF unexpectantly");
613 #endif
614                         goto ret;
615                 }
616                 fp->off += cc;
617                 addr += cc;
618                 size -= cc;
619         }
620 ret:
621         if (resid)
622                 *resid = size;
623
624         return (0);
625 }
626
627 /*
628  * Not implemented.
629  *
630  * Parameters:
631  *      resid:  out
632  */
633 int
634 nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
635 {
636         return (EROFS);
637 }
638
639 off_t
640 nfs_seek(struct open_file *f, off_t offset, int where)
641 {
642         struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
643         n_long size = ntohl(d->fa.fa_size);
644
645         switch (where) {
646         case SEEK_SET:
647                 d->off = offset;
648                 break;
649         case SEEK_CUR:
650                 d->off += offset;
651                 break;
652         case SEEK_END:
653                 d->off = size - offset;
654                 break;
655         default:
656                 return (-1);
657         }
658
659         return (d->off);
660 }
661
662 /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 */
663 int nfs_stat_types[8] = {
664         0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 0 };
665
666 int
667 nfs_stat(struct open_file *f, struct stat *sb)
668 {
669         struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
670         n_long ftype, mode;
671
672         ftype = ntohl(fp->fa.fa_type);
673         mode  = ntohl(fp->fa.fa_mode);
674         mode |= nfs_stat_types[ftype & 7];
675
676         sb->st_mode  = mode;
677         sb->st_nlink = ntohl(fp->fa.fa_nlink);
678         sb->st_uid   = ntohl(fp->fa.fa_uid);
679         sb->st_gid   = ntohl(fp->fa.fa_gid);
680         sb->st_size  = ntohl(fp->fa.fa_size);
681
682         return (0);
683 }
684
685 static int
686 nfs_readdir(struct open_file *f, struct dirent *d)
687 {
688         struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
689         struct nfs_readdir_args *args;
690         struct nfs_readdir_data *rd;
691         struct nfs_readdir_off  *roff = NULL;
692         static char *buf;
693         static n_long cookie = 0;
694         size_t cc;
695         n_long eof;
696         
697         struct {
698                 n_long h[RPC_HEADER_WORDS];
699                 struct nfs_readdir_args d;
700         } sdata;
701         static struct {
702                 n_long h[RPC_HEADER_WORDS];
703                 u_char d[NFS_READDIRSIZE];
704         } rdata;
705
706         if (cookie == 0) {
707         refill:
708                 args = &sdata.d;
709                 bzero(args, sizeof(*args));
710
711                 bcopy(fp->fh, args->fh, NFS_FHSIZE);
712                 args->cookie = htonl(cookie);
713                 args->count  = htonl(NFS_READDIRSIZE);
714                 
715                 cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READDIR,
716                               args, sizeof(*args),
717                               rdata.d, sizeof(rdata.d));
718                 buf  = rdata.d;
719                 roff = (struct nfs_readdir_off *)buf;
720                 if (ntohl(roff->cookie) != 0)
721                         return 1;
722         }
723         roff = (struct nfs_readdir_off *)buf;
724
725         if (ntohl(roff->follows) == 0) {
726                 eof = ntohl((roff+1)->cookie);
727                 if (eof) {
728                         cookie = 0;
729                         return 1;
730                 }
731                 goto refill;
732         }
733
734         buf += sizeof(struct nfs_readdir_off);
735         rd = (struct nfs_readdir_data *)buf;
736         d->d_namlen = ntohl(rd->len);
737         bcopy(rd->name, d->d_name, d->d_namlen);
738         d->d_name[d->d_namlen] = '\0';
739
740         buf += (sizeof(struct nfs_readdir_data) + roundup(htonl(rd->len),4));
741         roff = (struct nfs_readdir_off *)buf;
742         cookie = ntohl(roff->cookie);
743         return 0;
744 }