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