libc: Bring in getdate() from NetBSD for POSIX conformance.
[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         /* Avoid trying out nfs_open for disk devices in the EFI loader */
416 #ifndef __i386__
417         if (strcmp(f->f_dev->dv_name, "net") != 0)
418                 return (EINVAL);
419 #endif
420
421         if (!(desc = socktodesc(*(int *)(f->f_devdata))))
422                 return(EINVAL);
423
424         /* Bind to a reserved port. */
425         desc->myport = htons(rpc_newport());
426         desc->destip = rootip;
427         if ((error = nfs_getrootfh(desc, rootpath, nfs_root_node.fh)))
428                 return (error);
429         nfs_root_node.iodesc = desc;
430
431 #ifndef NFS_NOSYMLINK
432         /* Fake up attributes for the root dir. */
433         fa = &nfs_root_node.fa;
434         fa->fa_type  = htonl(NFDIR);
435         fa->fa_mode  = htonl(0755);
436         fa->fa_nlink = htonl(2);
437
438         currfd = &nfs_root_node;
439         newfd = NULL;
440
441         cp = path = strdup(upath);
442         if (path == NULL) {
443             error = ENOMEM;
444             goto out;
445         }
446         while (*cp) {
447                 /*
448                  * Remove extra separators
449                  */
450                 while (*cp == '/')
451                         cp++;
452
453                 if (*cp == '\0')
454                         break;
455                 /*
456                  * Check that current node is a directory.
457                  */
458                 if (currfd->fa.fa_type != htonl(NFDIR)) {
459                         error = ENOTDIR;
460                         goto out;
461                 }
462                 
463                 /* allocate file system specific data structure */
464                 newfd = malloc(sizeof(*newfd));
465                 newfd->iodesc = currfd->iodesc;
466                 newfd->off = 0;
467         
468                 /*
469                  * Get next component of path name.
470                  */
471                 {
472                         int len = 0;
473                         
474                         ncp = cp;
475                         while ((c = *cp) != '\0' && c != '/') {
476                                 if (++len > NFS_MAXNAMLEN) {
477                                         error = ENOENT;
478                                         goto out;
479                                 }
480                                 cp++;
481                         }
482                         *cp = '\0';
483                 }
484                 
485                 /* lookup a file handle */
486                 error = nfs_lookupfh(currfd, ncp, newfd);
487                 *cp = c;
488                 if (error)
489                         goto out;
490                 
491                 /*
492                  * Check for symbolic link
493                  */
494                 if (newfd->fa.fa_type == htonl(NFLNK)) {
495                         int link_len, len;
496                         
497                         error = nfs_readlink(newfd, linkbuf);
498                         if (error)
499                                 goto out;
500
501                         link_len = strlen(linkbuf);
502                         len = strlen(cp);
503
504                         if (link_len + len > MAXPATHLEN
505                             || ++nlinks > MAXSYMLINKS) {
506                                 error = ENOENT;
507                                 goto out;
508                         }
509
510                         bcopy(cp, &namebuf[link_len], len + 1);
511                         bcopy(linkbuf, namebuf, link_len);
512                         
513                         /*
514                          * If absolute pathname, restart at root.
515                          * If relative pathname, restart at parent directory.
516                          */
517                         cp = namebuf;
518                         if (*cp == '/') {
519                                 if (currfd != &nfs_root_node)
520                                         free(currfd);
521                                 currfd = &nfs_root_node;
522                         }
523
524                         free(newfd);
525                         newfd = NULL;
526                         
527                         continue;
528                 }
529                 
530                 if (currfd != &nfs_root_node)
531                         free(currfd);
532                 currfd = newfd;
533                 newfd = NULL;
534         }
535
536         error = 0;
537
538 out:
539         if (newfd)
540                 free(newfd);
541         if (path)
542                 free(path);
543 #else
544         /* allocate file system specific data structure */
545         currfd = malloc(sizeof(*currfd));
546         currfd->iodesc = desc;
547         currfd->off = 0;
548
549         error = nfs_lookupfh(&nfs_root_node, upath, currfd);
550 #endif
551         if (!error) {
552                 f->f_fsdata = (void *)currfd;
553                 return (0);
554         }
555                 
556 #ifdef NFS_DEBUG
557         if (debug)
558                 printf("nfs_open: %s lookupfh failed: %s\n",
559                     path, strerror(error));
560 #endif
561 #ifndef NFS_NOSYMLINK
562         if (currfd != &nfs_root_node)
563 #endif
564                 free(currfd);
565
566         return (error);
567 }
568
569 int
570 nfs_close(struct open_file *f)
571 {
572         struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
573
574 #ifdef NFS_DEBUG
575         if (debug)
576                 printf("nfs_close: fp=0x%lx\n", (u_long)fp);
577 #endif
578
579         f->f_fsdata = NULL;
580         if (fp && fp != &nfs_root_node)
581                 free(fp);
582         
583         return (0);
584 }
585
586 /*
587  * read a portion of a file
588  *
589  * Parameters:
590  *      resid:  out
591  */
592 int
593 nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
594 {
595         struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
596         ssize_t cc;
597         static int tc;
598         char *addr = buf;
599         
600 #ifdef NFS_DEBUG
601         if (debug)
602                 printf("nfs_read: size=%lu off=%d\n", (u_long)size,
603                        (int)fp->off);
604 #endif
605         while ((int)size > 0) {
606                 if (!(tc++ % 256))
607                         twiddle();
608                 cc = nfs_readdata(fp, fp->off, addr, size);
609                 /* XXX maybe should retry on certain errors */
610                 if (cc == -1) {
611 #ifdef NFS_DEBUG
612                         if (debug)
613                                 printf("nfs_read: read: %s", strerror(errno));
614 #endif
615                         return (errno); /* XXX - from nfs_readdata */
616                 }
617                 if (cc == 0) {
618 #ifdef NFS_DEBUG
619                         if (debug)
620                                 printf("nfs_read: hit EOF unexpectantly");
621 #endif
622                         goto ret;
623                 }
624                 fp->off += cc;
625                 addr += cc;
626                 size -= cc;
627         }
628 ret:
629         if (resid)
630                 *resid = size;
631
632         return (0);
633 }
634
635 /*
636  * Not implemented.
637  *
638  * Parameters:
639  *      resid:  out
640  */
641 int
642 nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
643 {
644         return (EROFS);
645 }
646
647 off_t
648 nfs_seek(struct open_file *f, off_t offset, int where)
649 {
650         struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
651         n_long size = ntohl(d->fa.fa_size);
652
653         switch (where) {
654         case SEEK_SET:
655                 d->off = offset;
656                 break;
657         case SEEK_CUR:
658                 d->off += offset;
659                 break;
660         case SEEK_END:
661                 d->off = size - offset;
662                 break;
663         default:
664                 return (-1);
665         }
666
667         return (d->off);
668 }
669
670 /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 */
671 int nfs_stat_types[8] = {
672         0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 0 };
673
674 int
675 nfs_stat(struct open_file *f, struct stat *sb)
676 {
677         struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
678         n_long ftype, mode;
679
680         ftype = ntohl(fp->fa.fa_type);
681         mode  = ntohl(fp->fa.fa_mode);
682         mode |= nfs_stat_types[ftype & 7];
683
684         sb->st_mode  = mode;
685         sb->st_nlink = ntohl(fp->fa.fa_nlink);
686         sb->st_uid   = ntohl(fp->fa.fa_uid);
687         sb->st_gid   = ntohl(fp->fa.fa_gid);
688         sb->st_size  = ntohl(fp->fa.fa_size);
689
690         return (0);
691 }
692
693 static int
694 nfs_readdir(struct open_file *f, struct dirent *d)
695 {
696         struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
697         struct nfs_readdir_args *args;
698         struct nfs_readdir_data *rd;
699         struct nfs_readdir_off  *roff = NULL;
700         static char *buf;
701         static n_long cookie = 0;
702         size_t cc;
703         n_long eof;
704         
705         struct {
706                 n_long h[RPC_HEADER_WORDS];
707                 struct nfs_readdir_args d;
708         } sdata;
709         static struct {
710                 n_long h[RPC_HEADER_WORDS];
711                 u_char d[NFS_READDIRSIZE];
712         } rdata;
713
714         if (cookie == 0) {
715         refill:
716                 args = &sdata.d;
717                 bzero(args, sizeof(*args));
718
719                 bcopy(fp->fh, args->fh, NFS_FHSIZE);
720                 args->cookie = htonl(cookie);
721                 args->count  = htonl(NFS_READDIRSIZE);
722                 
723                 cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READDIR,
724                               args, sizeof(*args),
725                               rdata.d, sizeof(rdata.d));
726                 buf  = rdata.d;
727                 roff = (struct nfs_readdir_off *)buf;
728                 if (ntohl(roff->cookie) != 0)
729                         return 1;
730         }
731         roff = (struct nfs_readdir_off *)buf;
732
733         if (ntohl(roff->follows) == 0) {
734                 eof = ntohl((roff+1)->cookie);
735                 if (eof) {
736                         cookie = 0;
737                         return 1;
738                 }
739                 goto refill;
740         }
741
742         buf += sizeof(struct nfs_readdir_off);
743         rd = (struct nfs_readdir_data *)buf;
744         d->d_namlen = ntohl(rd->len);
745         bcopy(rd->name, d->d_name, d->d_namlen);
746         d->d_name[d->d_namlen] = '\0';
747
748         buf += (sizeof(struct nfs_readdir_data) + roundup(htonl(rd->len),4));
749         roff = (struct nfs_readdir_off *)buf;
750         cookie = ntohl(roff->cookie);
751         return 0;
752 }