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