a0e1936b81d52abfc5ffd57b21282efd85a1ef3c
[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(struct iodesc *d, char *path, u_char *fhp)
149 {
150         int len;
151         struct args {
152                 n_long  len;
153                 char    path[FNAME_SIZE];
154         } *args;
155         struct repl {
156                 n_long  errno;
157                 u_char  fh[NFS_FHSIZE];
158         } *repl;
159         struct {
160                 n_long  h[RPC_HEADER_WORDS];
161                 struct args d;
162         } sdata;
163         struct {
164                 n_long  h[RPC_HEADER_WORDS];
165                 struct repl d;
166         } rdata;
167         size_t cc;
168         
169 #ifdef NFS_DEBUG
170         if (debug)
171                 printf("nfs_getrootfh: %s\n", path);
172 #endif
173
174         args = &sdata.d;
175         repl = &rdata.d;
176
177         bzero(args, sizeof(*args));
178         len = strlen(path);
179         if (len > sizeof(args->path))
180                 len = sizeof(args->path);
181         args->len = htonl(len);
182         bcopy(path, args->path, len);
183         len = 4 + roundup(len, 4);
184
185         cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT,
186             args, len, repl, sizeof(*repl));
187         if (cc == -1) {
188                 /* errno was set by rpc_call */
189                 return (errno);
190         }
191         if (cc < 4)
192                 return (EBADRPC);
193         if (repl->errno)
194                 return (ntohl(repl->errno));
195         bcopy(repl->fh, fhp, sizeof(repl->fh));
196         return (0);
197 }
198
199 /*
200  * Lookup a file.  Store handle and attributes.
201  * Return zero or error number.
202  */
203 int
204 nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd)
205 {
206         int len, rlen;
207         struct args {
208                 u_char  fh[NFS_FHSIZE];
209                 n_long  len;
210                 char    name[FNAME_SIZE];
211         } *args;
212         struct repl {
213                 n_long  errno;
214                 u_char  fh[NFS_FHSIZE];
215                 struct  nfsv2_fattrs fa;
216         } *repl;
217         struct {
218                 n_long  h[RPC_HEADER_WORDS];
219                 struct args d;
220         } sdata;
221         struct {
222                 n_long  h[RPC_HEADER_WORDS];
223                 struct repl d;
224         } rdata;
225         ssize_t cc;
226         
227 #ifdef NFS_DEBUG
228         if (debug)
229                 printf("lookupfh: called\n");
230 #endif
231
232         args = &sdata.d;
233         repl = &rdata.d;
234
235         bzero(args, sizeof(*args));
236         bcopy(d->fh, args->fh, sizeof(args->fh));
237         len = strlen(name);
238         if (len > sizeof(args->name))
239                 len = sizeof(args->name);
240         bcopy(name, args->name, len);
241         args->len = htonl(len);
242         len = 4 + roundup(len, 4);
243         len += NFS_FHSIZE;
244
245         rlen = sizeof(*repl);
246
247         cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_LOOKUP,
248             args, len, repl, rlen);
249         if (cc == -1)
250                 return (errno);         /* XXX - from rpc_call */
251         if (cc < 4)
252                 return (EIO);
253         if (repl->errno) {
254                 /* saerrno.h now matches NFS error numbers. */
255                 return (ntohl(repl->errno));
256         }
257         bcopy( repl->fh, &newfd->fh, sizeof(newfd->fh));
258         bcopy(&repl->fa, &newfd->fa, sizeof(newfd->fa));
259         return (0);
260 }
261
262 #ifndef NFS_NOSYMLINK
263 /*
264  * Get the destination of a symbolic link.
265  */
266 int
267 nfs_readlink(struct nfs_iodesc *d, char *buf)
268 {
269         struct {
270                 n_long  h[RPC_HEADER_WORDS];
271                 u_char fh[NFS_FHSIZE];
272         } sdata;
273         struct {
274                 n_long  h[RPC_HEADER_WORDS];
275                 struct nfs_readlnk_repl d;
276         } rdata;
277         ssize_t cc;
278
279 #ifdef NFS_DEBUG
280         if (debug)
281                 printf("readlink: called\n");
282 #endif
283
284         bcopy(d->fh, sdata.fh, NFS_FHSIZE);
285         cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READLINK,
286                       sdata.fh, NFS_FHSIZE,
287                       &rdata.d, sizeof(rdata.d));
288         if (cc == -1)
289                 return (errno);
290
291         if (cc < 4)
292                 return (EIO);
293         
294         if (rdata.d.errno)
295                 return (ntohl(rdata.d.errno));
296
297         rdata.d.len = ntohl(rdata.d.len);
298         if (rdata.d.len > NFS_MAXPATHLEN)
299                 return (ENAMETOOLONG);
300
301         bcopy(rdata.d.path, buf, rdata.d.len);
302         buf[rdata.d.len] = 0;
303         return (0);
304 }
305 #endif
306
307 /*
308  * Read data from a file.
309  * Return transfer count or -1 (and set errno)
310  */
311 ssize_t
312 nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
313 {
314         struct nfs_read_args *args;
315         struct nfs_read_repl *repl;
316         struct {
317                 n_long  h[RPC_HEADER_WORDS];
318                 struct nfs_read_args d;
319         } sdata;
320         struct {
321                 n_long  h[RPC_HEADER_WORDS];
322                 struct nfs_read_repl d;
323         } rdata;
324         size_t cc;
325         long x;
326         int hlen, rlen;
327
328         args = &sdata.d;
329         repl = &rdata.d;
330
331         bcopy(d->fh, args->fh, NFS_FHSIZE);
332         args->off = htonl((n_long)off);
333         if (len > NFSREAD_SIZE)
334                 len = NFSREAD_SIZE;
335         args->len = htonl((n_long)len);
336         args->xxx = htonl((n_long)0);
337         hlen = sizeof(*repl) - NFSREAD_SIZE;
338
339         cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READ,
340             args, sizeof(*args),
341             repl, sizeof(*repl));
342         if (cc == -1) {
343                 /* errno was already set by rpc_call */
344                 return (-1);
345         }
346         if (cc < hlen) {
347                 errno = EBADRPC;
348                 return (-1);
349         }
350         if (repl->errno) {
351                 errno = ntohl(repl->errno);
352                 return (-1);
353         }
354         rlen = cc - hlen;
355         x = ntohl(repl->count);
356         if (rlen < x) {
357                 printf("nfsread: short packet, %d < %ld\n", rlen, x);
358                 errno = EBADRPC;
359                 return(-1);
360         }
361         bcopy(repl->data, addr, x);
362         return (x);
363 }
364
365 /*
366  * Open a file.
367  * return zero or error number
368  */
369 int
370 nfs_open(const char *upath, struct open_file *f)
371 {
372         struct iodesc *desc;
373         struct nfs_iodesc *currfd;
374 #ifndef NFS_NOSYMLINK
375         struct nfs_iodesc *newfd;
376         struct nfsv2_fattrs *fa;
377         char *cp, *ncp;
378         int c;
379         char namebuf[NFS_MAXPATHLEN + 1];
380         char linkbuf[NFS_MAXPATHLEN + 1];
381         int nlinks = 0;
382 #endif
383         int error;
384         char *path;
385
386 #ifdef NFS_DEBUG
387         if (debug)
388             printf("nfs_open: %s (rootpath=%s)\n", path, rootpath);
389 #endif
390         if (!rootpath[0]) {
391                 printf("no rootpath, no nfs\n");
392                 return (ENXIO);
393         }
394
395         if (!(desc = socktodesc(*(int *)(f->f_devdata))))
396                 return(EINVAL);
397
398         /* Bind to a reserved port. */
399         desc->myport = htons(--rpc_port);
400         desc->destip = rootip;
401         if ((error = nfs_getrootfh(desc, rootpath, nfs_root_node.fh)))
402                 return (error);
403         nfs_root_node.iodesc = desc;
404
405 #ifndef NFS_NOSYMLINK
406         /* Fake up attributes for the root dir. */
407         fa = &nfs_root_node.fa;
408         fa->fa_type  = htonl(NFDIR);
409         fa->fa_mode  = htonl(0755);
410         fa->fa_nlink = htonl(2);
411
412         currfd = &nfs_root_node;
413         newfd = NULL;
414
415         cp = path = strdup(upath);
416         if (path == NULL) {
417             error = ENOMEM;
418             goto out;
419         }
420         while (*cp) {
421                 /*
422                  * Remove extra separators
423                  */
424                 while (*cp == '/')
425                         cp++;
426
427                 if (*cp == '\0')
428                         break;
429                 /*
430                  * Check that current node is a directory.
431                  */
432                 if (currfd->fa.fa_type != htonl(NFDIR)) {
433                         error = ENOTDIR;
434                         goto out;
435                 }
436                 
437                 /* allocate file system specific data structure */
438                 newfd = malloc(sizeof(*newfd));
439                 newfd->iodesc = currfd->iodesc;
440                 newfd->off = 0;
441         
442                 /*
443                  * Get next component of path name.
444                  */
445                 {
446                         int len = 0;
447                         
448                         ncp = cp;
449                         while ((c = *cp) != '\0' && c != '/') {
450                                 if (++len > NFS_MAXNAMLEN) {
451                                         error = ENOENT;
452                                         goto out;
453                                 }
454                                 cp++;
455                         }
456                         *cp = '\0';
457                 }
458                 
459                 /* lookup a file handle */
460                 error = nfs_lookupfh(currfd, ncp, newfd);
461                 *cp = c;
462                 if (error)
463                         goto out;
464                 
465                 /*
466                  * Check for symbolic link
467                  */
468                 if (newfd->fa.fa_type == htonl(NFLNK)) {
469                         int link_len, len;
470                         
471                         error = nfs_readlink(newfd, linkbuf);
472                         if (error)
473                                 goto out;
474
475                         link_len = strlen(linkbuf);
476                         len = strlen(cp);
477
478                         if (link_len + len > MAXPATHLEN
479                             || ++nlinks > MAXSYMLINKS) {
480                                 error = ENOENT;
481                                 goto out;
482                         }
483
484                         bcopy(cp, &namebuf[link_len], len + 1);
485                         bcopy(linkbuf, namebuf, link_len);
486                         
487                         /*
488                          * If absolute pathname, restart at root.
489                          * If relative pathname, restart at parent directory.
490                          */
491                         cp = namebuf;
492                         if (*cp == '/') {
493                                 if (currfd != &nfs_root_node)
494                                         free(currfd);
495                                 currfd = &nfs_root_node;
496                         }
497
498                         free(newfd);
499                         newfd = NULL;
500                         
501                         continue;
502                 }
503                 
504                 if (currfd != &nfs_root_node)
505                         free(currfd);
506                 currfd = newfd;
507                 newfd = NULL;
508         }
509
510         error = 0;
511
512 out:
513         if (newfd)
514                 free(newfd);
515         if (path)
516                 free(path);
517 #else
518         /* allocate file system specific data structure */
519         currfd = malloc(sizeof(*currfd));
520         currfd->iodesc = desc;
521         currfd->off = 0;
522
523         error = nfs_lookupfh(&nfs_root_node, upath, currfd);
524 #endif
525         if (!error) {
526                 f->f_fsdata = (void *)currfd;
527                 return (0);
528         }
529                 
530 #ifdef NFS_DEBUG
531         if (debug)
532                 printf("nfs_open: %s lookupfh failed: %s\n",
533                     path, strerror(error));
534 #endif
535 #ifndef NFS_NOSYMLINK
536         if (currfd != &nfs_root_node)
537 #endif
538                 free(currfd);
539
540         return (error);
541 }
542
543 int
544 nfs_close(struct open_file *f)
545 {
546         struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
547
548 #ifdef NFS_DEBUG
549         if (debug)
550                 printf("nfs_close: fp=0x%lx\n", (u_long)fp);
551 #endif
552
553         f->f_fsdata = NULL;
554         if (fp && fp != &nfs_root_node)
555                 free(fp);
556         
557         return (0);
558 }
559
560 /*
561  * read a portion of a file
562  *
563  * Parameters:
564  *      resid:  out
565  */
566 int
567 nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
568 {
569         struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
570         ssize_t cc;
571         char *addr = buf;
572         
573 #ifdef NFS_DEBUG
574         if (debug)
575                 printf("nfs_read: size=%lu off=%d\n", (u_long)size,
576                        (int)fp->off);
577 #endif
578         while ((int)size > 0) {
579                 twiddle();
580                 cc = nfs_readdata(fp, fp->off, (void *)addr, size);
581                 /* XXX maybe should retry on certain errors */
582                 if (cc == -1) {
583 #ifdef NFS_DEBUG
584                         if (debug)
585                                 printf("nfs_read: read: %s", strerror(errno));
586 #endif
587                         return (errno); /* XXX - from nfs_readdata */
588                 }
589                 if (cc == 0) {
590 #ifdef NFS_DEBUG
591                         if (debug)
592                                 printf("nfs_read: hit EOF unexpectantly");
593 #endif
594                         goto ret;
595                 }
596                 fp->off += cc;
597                 addr += cc;
598                 size -= cc;
599         }
600 ret:
601         if (resid)
602                 *resid = size;
603
604         return (0);
605 }
606
607 /*
608  * Not implemented.
609  *
610  * Parameters:
611  *      resid:  out
612  */
613 int
614 nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
615 {
616         return (EROFS);
617 }
618
619 off_t
620 nfs_seek(struct open_file *f, off_t offset, int where)
621 {
622         struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
623         n_long size = ntohl(d->fa.fa_size);
624
625         switch (where) {
626         case SEEK_SET:
627                 d->off = offset;
628                 break;
629         case SEEK_CUR:
630                 d->off += offset;
631                 break;
632         case SEEK_END:
633                 d->off = size - offset;
634                 break;
635         default:
636                 return (-1);
637         }
638
639         return (d->off);
640 }
641
642 /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 */
643 int nfs_stat_types[8] = {
644         0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 0 };
645
646 int
647 nfs_stat(struct open_file *f, struct stat *sb)
648 {
649         struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
650         n_long ftype, mode;
651
652         ftype = ntohl(fp->fa.fa_type);
653         mode  = ntohl(fp->fa.fa_mode);
654         mode |= nfs_stat_types[ftype & 7];
655
656         sb->st_mode  = mode;
657         sb->st_nlink = ntohl(fp->fa.fa_nlink);
658         sb->st_uid   = ntohl(fp->fa.fa_uid);
659         sb->st_gid   = ntohl(fp->fa.fa_gid);
660         sb->st_size  = ntohl(fp->fa.fa_size);
661
662         return (0);
663 }
664
665 static int
666 nfs_readdir(struct open_file *f, struct dirent *d)
667 {
668         struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
669         struct nfs_readdir_args *args;
670         struct nfs_readdir_data *rd;
671         struct nfs_readdir_off  *roff = NULL;
672         static char *buf;
673         static n_long cookie = 0;
674         size_t cc;
675         n_long eof;
676         
677         struct {
678                 n_long h[RPC_HEADER_WORDS];
679                 struct nfs_readdir_args d;
680         } sdata;
681         static struct {
682                 n_long h[RPC_HEADER_WORDS];
683                 u_char d[NFS_READDIRSIZE];
684         } rdata;
685
686         if (cookie == 0) {
687         refill:
688                 args = &sdata.d;
689                 bzero(args, sizeof(*args));
690
691                 bcopy(fp->fh, args->fh, NFS_FHSIZE);
692                 args->cookie = htonl(cookie);
693                 args->count  = htonl(NFS_READDIRSIZE);
694                 
695                 cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READDIR,
696                               args, sizeof(*args),
697                               rdata.d, sizeof(rdata.d));
698                 buf  = rdata.d;
699                 roff = (struct nfs_readdir_off *)buf;
700                 if (ntohl(roff->cookie) != 0)
701                         return 1;
702         }
703         roff = (struct nfs_readdir_off *)buf;
704
705         if (ntohl(roff->follows) == 0) {
706                 eof = ntohl((roff+1)->cookie);
707                 if (eof) {
708                         cookie = 0;
709                         return 1;
710                 }
711                 goto refill;
712         }
713
714         buf += sizeof(struct nfs_readdir_off);
715         rd = (struct nfs_readdir_data *)buf;
716         d->d_namlen = ntohl(rd->len);
717         bcopy(rd->name, d->d_name, d->d_namlen);
718         d->d_name[d->d_namlen] = '\0';
719
720         buf += (sizeof(struct nfs_readdir_data) + roundup(htonl(rd->len),4));
721         roff = (struct nfs_readdir_off *)buf;
722         cookie = ntohl(roff->cookie);
723         return 0;
724 }