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