iPXE - Open Source Boot Firmware

gcc is rather over-aggressive about optimising out static data structures
[ipxe.git] / src / proto / nfs.c
1 #include "etherboot.h"
2 #include "init.h"
3 #include "proto.h"
4 #include <gpxe/in.h>
5 #include "nic.h"
6
7 /* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read:
8  * large portions are copied verbatim) as distributed in OSKit 0.97.  A few
9  * changes were necessary to adapt the code to Etherboot and to fix several
10  * inconsistencies.  Also the RPC message preparation is done "by hand" to
11  * avoid adding netsprintf() which I find hard to understand and use.  */
12
13 /* NOTE 2: Etherboot does not care about things beyond the kernel image, so
14  * it loads the kernel image off the boot server (ARP_SERVER) and does not
15  * access the client root disk (root-path in dhcpd.conf), which would use
16  * ARP_ROOTSERVER.  The root disk is something the operating system we are
17  * about to load needs to use.  This is different from the OSKit 0.97 logic.  */
18
19 /* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14
20  * If a symlink is encountered, it is followed as far as possible (recursion
21  * possible, maximum 16 steps). There is no clearing of ".."'s inside the
22  * path, so please DON'T DO THAT. thx. */
23
24 #define START_OPORT 700         /* mountd usually insists on secure ports */
25 #define OPORT_SWEEP 200         /* make sure we don't leave secure range */
26
27 static int oport = START_OPORT;
28 static struct sockaddr_in mount_server;
29 static struct sockaddr_in nfs_server;
30 static unsigned long rpc_id;
31
32 /**************************************************************************
33 RPC_INIT - set up the ID counter to something fairly random
34 **************************************************************************/
35 static void rpc_init(void)
36 {
37         unsigned long t;
38
39         t = currticks();
40         rpc_id = t ^ (t << 8) ^ (t << 16);
41 }
42
43 /**************************************************************************
44 RPC_PRINTERROR - Print a low level RPC error message
45 **************************************************************************/
46 static void rpc_printerror(struct rpc_t *rpc)
47 {
48         if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
49             rpc->u.reply.astatus) {
50                 /* rpc_printerror() is called for any RPC related error,
51                  * suppress output if no low level RPC error happened.  */
52                 DBG("RPC error: (%d,%d,%d)\n", ntohl(rpc->u.reply.rstatus),
53                     ntohl(rpc->u.reply.verifier),
54                     ntohl(rpc->u.reply.astatus));
55         }
56 }
57
58 /**************************************************************************
59 AWAIT_RPC - Wait for an rpc packet
60 **************************************************************************/
61 static int await_rpc(int ival, void *ptr,
62                      unsigned short ptype __unused, struct iphdr *ip,
63                      struct udphdr *udp, struct tcphdr *tcp __unused)
64 {
65         struct rpc_t *rpc;
66         if (!udp) 
67                 return 0;
68         if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
69                 return 0;
70         if (ntohs(udp->dest) != ival)
71                 return 0;
72         if (nic.packetlen < ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr) + 8)
73                 return 0;
74         rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
75         if (*(unsigned long *)ptr != ntohl(rpc->u.reply.id))
76                 return 0;
77         if (MSG_REPLY != ntohl(rpc->u.reply.type))
78                 return 0;
79         return 1;
80 }
81
82 /**************************************************************************
83 RPC_LOOKUP - Lookup RPC Port numbers
84 **************************************************************************/
85 static int rpc_lookup(struct sockaddr_in *addr, int prog, int ver, int sport)
86 {
87         struct rpc_t buf, *rpc;
88         unsigned long id;
89         int retries;
90         long *p;
91
92         id = rpc_id++;
93         buf.u.call.id = htonl(id);
94         buf.u.call.type = htonl(MSG_CALL);
95         buf.u.call.rpcvers = htonl(2);  /* use RPC version 2 */
96         buf.u.call.prog = htonl(PROG_PORTMAP);
97         buf.u.call.vers = htonl(2);     /* portmapper is version 2 */
98         buf.u.call.proc = htonl(PORTMAP_GETPORT);
99         p = (long *)buf.u.call.data;
100         *p++ = 0; *p++ = 0;                             /* auth credential */
101         *p++ = 0; *p++ = 0;                             /* auth verifier */
102         *p++ = htonl(prog);
103         *p++ = htonl(ver);
104         *p++ = htonl(IP_UDP);
105         *p++ = 0;
106         for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
107                 long timeout;
108                 udp_transmit(addr->sin_addr.s_addr, sport, addr->sin_port,
109                         (char *)p - (char *)&buf, &buf);
110                 timeout = rfc2131_sleep_interval(TIMEOUT, retries);
111                 if (await_reply(await_rpc, sport, &id, timeout)) {
112                         rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
113                         if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
114                             rpc->u.reply.astatus) {
115                                 rpc_printerror(rpc);
116                                 return 0;
117                         } else {
118                                 return ntohl(rpc->u.reply.data[0]);
119                         }
120                 }
121         }
122         return 0;
123 }
124
125 /**************************************************************************
126 RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
127 **************************************************************************/
128 static long *rpc_add_credentials(long *p)
129 {
130         int hl;
131
132         /* Here's the executive summary on authentication requirements of the
133          * various NFS server implementations:  Linux accepts both AUTH_NONE
134          * and AUTH_UNIX authentication (also accepts an empty hostname field
135          * in the AUTH_UNIX scheme).  *BSD refuses AUTH_NONE, but accepts
136          * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
137          * scheme).  To be safe, use AUTH_UNIX and pass the hostname if we have
138          * it (if the BOOTP/DHCP reply didn't give one, just use an empty
139          * hostname).  */
140
141         hl = (hostnamelen + 3) & ~3;
142
143         /* Provide an AUTH_UNIX credential.  */
144         *p++ = htonl(1);                /* AUTH_UNIX */
145         *p++ = htonl(hl+20);            /* auth length */
146         *p++ = htonl(0);                /* stamp */
147         *p++ = htonl(hostnamelen);      /* hostname string */
148         if (hostnamelen & 3) {
149                 *(p + hostnamelen / 4) = 0; /* add zero padding */
150         }
151         memcpy(p, hostname, hostnamelen);
152         p += hl / 4;
153         *p++ = 0;                       /* uid */
154         *p++ = 0;                       /* gid */
155         *p++ = 0;                       /* auxiliary gid list */
156
157         /* Provide an AUTH_NONE verifier.  */
158         *p++ = 0;                       /* AUTH_NONE */
159         *p++ = 0;                       /* auth length */
160
161         return p;
162 }
163
164 /**************************************************************************
165 NFS_PRINTERROR - Print a NFS error message
166 **************************************************************************/
167 static void nfs_printerror(int err)
168 {
169         switch (-err) {
170         case NFSERR_PERM:
171                 printf("Not owner\n");
172                 break;
173         case NFSERR_NOENT:
174                 printf("No such file or directory\n");
175                 break;
176         case NFSERR_ACCES:
177                 printf("Permission denied\n");
178                 break;
179         case NFSERR_ISDIR:
180                 printf("Directory given where filename expected\n");
181                 break;
182         case NFSERR_INVAL:
183                 printf("Invalid filehandle\n");
184                 break; // INVAL is not defined in NFSv2, some NFS-servers
185                 // seem to use it in answers to v2 nevertheless.
186         case 9998:
187                 printf("low-level RPC failure (parameter decoding problem?)\n");
188                 break;
189         case 9999:
190                 printf("low-level RPC failure (authentication problem?)\n");
191                 break;
192         default:
193                 printf("Unknown NFS error %d\n", -err);
194         }
195 }
196
197 /**************************************************************************
198 NFS_MOUNT - Mount an NFS Filesystem
199 **************************************************************************/
200 static int nfs_mount(struct sockaddr_in *server, char *path, char *fh, int sport)
201 {
202         struct rpc_t buf, *rpc;
203         unsigned long id;
204         int retries;
205         long *p;
206         int pathlen = strlen(path);
207
208         id = rpc_id++;
209         buf.u.call.id = htonl(id);
210         buf.u.call.type = htonl(MSG_CALL);
211         buf.u.call.rpcvers = htonl(2);  /* use RPC version 2 */
212         buf.u.call.prog = htonl(PROG_MOUNT);
213         buf.u.call.vers = htonl(1);     /* mountd is version 1 */
214         buf.u.call.proc = htonl(MOUNT_ADDENTRY);
215         p = rpc_add_credentials((long *)buf.u.call.data);
216         *p++ = htonl(pathlen);
217         if (pathlen & 3) {
218                 *(p + pathlen / 4) = 0; /* add zero padding */
219         }
220         memcpy(p, path, pathlen);
221         p += (pathlen + 3) / 4;
222         for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
223                 long timeout;
224                 udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
225                         (char *)p - (char *)&buf, &buf);
226                 timeout = rfc2131_sleep_interval(TIMEOUT, retries);
227                 if (await_reply(await_rpc, sport, &id, timeout)) {
228                         rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
229                         if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
230                             rpc->u.reply.astatus || rpc->u.reply.data[0]) {
231                                 rpc_printerror(rpc);
232                                 if (rpc->u.reply.rstatus) {
233                                         /* RPC failed, no verifier, data[0] */
234                                         return -9999;
235                                 }
236                                 if (rpc->u.reply.astatus) {
237                                         /* RPC couldn't decode parameters */
238                                         return -9998;
239                                 }
240                                 return -ntohl(rpc->u.reply.data[0]);
241                         } else {
242                                 memcpy(fh, rpc->u.reply.data + 1, NFS_FHSIZE);
243                                 return 0;
244                         }
245                 }
246         }
247         return -1;
248 }
249
250 /**************************************************************************
251 NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
252 **************************************************************************/
253 static void nfs_umountall(struct sockaddr_in *server)
254 {
255         struct rpc_t buf, *rpc;
256         unsigned long id;
257         int retries;
258         long *p;
259
260         id = rpc_id++;
261         buf.u.call.id = htonl(id);
262         buf.u.call.type = htonl(MSG_CALL);
263         buf.u.call.rpcvers = htonl(2);  /* use RPC version 2 */
264         buf.u.call.prog = htonl(PROG_MOUNT);
265         buf.u.call.vers = htonl(1);     /* mountd is version 1 */
266         buf.u.call.proc = htonl(MOUNT_UMOUNTALL);
267         p = rpc_add_credentials((long *)buf.u.call.data);
268         for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
269                 long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
270                 udp_transmit(server->sin_addr.s_addr, oport, server->sin_port,
271                         (char *)p - (char *)&buf, &buf);
272                 if (await_reply(await_rpc, oport, &id, timeout)) {
273                         rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
274                         if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
275                             rpc->u.reply.astatus) {
276                                 rpc_printerror(rpc);
277                         }
278                         break;
279                 }
280         }
281 }
282
283 /**************************************************************************
284 NFS_RESET - Reset the NFS subsystem
285 **************************************************************************/
286 static void nfs_reset ( void ) {
287         /* If we have a mount server, call nfs_umountall() */
288         if ( mount_server.sin_addr.s_addr ) {
289                 nfs_umountall ( &mount_server );
290         }
291         /* Zero the data structures */
292         memset ( &mount_server, 0, sizeof ( mount_server ) );
293         memset ( &nfs_server, 0, sizeof ( nfs_server ) );
294 }
295
296 /***************************************************************************
297  * NFS_READLINK (AH 2003-07-14)
298  * This procedure is called when read of the first block fails -
299  * this probably happens when it's a directory or a symlink
300  * In case of successful readlink(), the dirname is manipulated,
301  * so that inside the nfs() function a recursion can be done.
302  **************************************************************************/
303 static int nfs_readlink(struct sockaddr_in *server, char *fh __unused,
304                         char *path, char *nfh, int sport)
305 {
306         struct rpc_t buf, *rpc;
307         unsigned long id;
308         long *p;
309         int retries;
310         int pathlen = strlen(path);
311
312         id = rpc_id++;
313         buf.u.call.id = htonl(id);
314         buf.u.call.type = htonl(MSG_CALL);
315         buf.u.call.rpcvers = htonl(2);  /* use RPC version 2 */
316         buf.u.call.prog = htonl(PROG_NFS);
317         buf.u.call.vers = htonl(2);     /* nfsd is version 2 */
318         buf.u.call.proc = htonl(NFS_READLINK);
319         p = rpc_add_credentials((long *)buf.u.call.data);
320         memcpy(p, nfh, NFS_FHSIZE);
321         p += (NFS_FHSIZE / 4);
322         for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
323                 long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
324                 udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
325                         (char *)p - (char *)&buf, &buf);
326                 if (await_reply(await_rpc, sport, &id, timeout)) {
327                         rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
328                         if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
329                             rpc->u.reply.astatus || rpc->u.reply.data[0]) {
330                                 rpc_printerror(rpc);
331                                 if (rpc->u.reply.rstatus) {
332                                         /* RPC failed, no verifier, data[0] */
333                                         return -9999;
334                                 }
335                                 if (rpc->u.reply.astatus) {
336                                         /* RPC couldn't decode parameters */
337                                         return -9998;
338                                 }
339                                 return -ntohl(rpc->u.reply.data[0]);
340                         } else {
341                                 // It *is* a link.
342                                 // If it's a relative link, append everything to dirname, filename TOO!
343                                 retries = strlen ( (char *)(&(rpc->u.reply.data[2]) ));
344                                 if ( *((char *)(&(rpc->u.reply.data[2]))) != '/' ) {
345                                         path[pathlen++] = '/';
346                                         while ( ( retries + pathlen ) > 298 ) {
347                                                 retries--;
348                                         }
349                                         if ( retries > 0 ) {
350                                                 memcpy(path + pathlen, &(rpc->u.reply.data[2]), retries + 1);
351                                         } else { retries = 0; }
352                                         path[pathlen + retries] = 0;
353                                 } else {
354                                         // Else make it the only path.
355                                         if ( retries > 298 ) { retries = 298; }
356                                         memcpy ( path, &(rpc->u.reply.data[2]), retries + 1 );
357                                         path[retries] = 0;
358                                 }
359                                 return 0;
360                         }
361                 }
362         }
363         return -1;
364 }
365 /**************************************************************************
366 NFS_LOOKUP - Lookup Pathname
367 **************************************************************************/
368 static int nfs_lookup(struct sockaddr_in *server, char *fh, char *path, char *nfh,
369         int sport)
370 {
371         struct rpc_t buf, *rpc;
372         unsigned long id;
373         long *p;
374         int retries;
375         int pathlen = strlen(path);
376
377         id = rpc_id++;
378         buf.u.call.id = htonl(id);
379         buf.u.call.type = htonl(MSG_CALL);
380         buf.u.call.rpcvers = htonl(2);  /* use RPC version 2 */
381         buf.u.call.prog = htonl(PROG_NFS);
382         buf.u.call.vers = htonl(2);     /* nfsd is version 2 */
383         buf.u.call.proc = htonl(NFS_LOOKUP);
384         p = rpc_add_credentials((long *)buf.u.call.data);
385         memcpy(p, fh, NFS_FHSIZE);
386         p += (NFS_FHSIZE / 4);
387         *p++ = htonl(pathlen);
388         if (pathlen & 3) {
389                 *(p + pathlen / 4) = 0; /* add zero padding */
390         }
391         memcpy(p, path, pathlen);
392         p += (pathlen + 3) / 4;
393         for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
394                 long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
395                 udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
396                         (char *)p - (char *)&buf, &buf);
397                 if (await_reply(await_rpc, sport, &id, timeout)) {
398                         rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
399                         if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
400                             rpc->u.reply.astatus || rpc->u.reply.data[0]) {
401                                 rpc_printerror(rpc);
402                                 if (rpc->u.reply.rstatus) {
403                                         /* RPC failed, no verifier, data[0] */
404                                         return -9999;
405                                 }
406                                 if (rpc->u.reply.astatus) {
407                                         /* RPC couldn't decode parameters */
408                                         return -9998;
409                                 }
410                                 return -ntohl(rpc->u.reply.data[0]);
411                         } else {
412                                 memcpy(nfh, rpc->u.reply.data + 1, NFS_FHSIZE);
413                                 return 0;
414                         }
415                 }
416         }
417         return -1;
418 }
419
420 /**************************************************************************
421 NFS_READ - Read File on NFS Server
422 **************************************************************************/
423 static int nfs_read(struct sockaddr_in *server, char *fh, int offset, int len,
424                     int sport)
425 {
426         struct rpc_t buf, *rpc;
427         unsigned long id;
428         int retries;
429         long *p;
430
431         static int tokens=0;
432         /*
433          * Try to implement something similar to a window protocol in
434          * terms of response to losses. On successful receive, increment
435          * the number of tokens by 1 (cap at 256). On failure, halve it.
436          * When the number of tokens is >= 2, use a very short timeout.
437          */
438
439         id = rpc_id++;
440         buf.u.call.id = htonl(id);
441         buf.u.call.type = htonl(MSG_CALL);
442         buf.u.call.rpcvers = htonl(2);  /* use RPC version 2 */
443         buf.u.call.prog = htonl(PROG_NFS);
444         buf.u.call.vers = htonl(2);     /* nfsd is version 2 */
445         buf.u.call.proc = htonl(NFS_READ);
446         p = rpc_add_credentials((long *)buf.u.call.data);
447         memcpy(p, fh, NFS_FHSIZE);
448         p += NFS_FHSIZE / 4;
449         *p++ = htonl(offset);
450         *p++ = htonl(len);
451         *p++ = 0;               /* unused parameter */
452         for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
453                 long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
454                 if (tokens >= 2)
455                         timeout = TICKS_PER_SEC/2;
456
457                 udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
458                         (char *)p - (char *)&buf, &buf);
459                 if (await_reply(await_rpc, sport, &id, timeout)) {
460                         if (tokens < 256)
461                                 tokens++;
462                         rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
463                         if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
464                             rpc->u.reply.astatus || rpc->u.reply.data[0]) {
465                                 rpc_printerror(rpc);
466                                 if (rpc->u.reply.rstatus) {
467                                         /* RPC failed, no verifier, data[0] */
468                                         return -9999;
469                                 }
470                                 if (rpc->u.reply.astatus) {
471                                         /* RPC couldn't decode parameters */
472                                         return -9998;
473                                 }
474                                 return -ntohl(rpc->u.reply.data[0]);
475                         } else {
476                                 return 0;
477                         }
478                 } else
479                         tokens >>= 1;
480         }
481         return -1;
482 }
483
484 /**************************************************************************
485 NFS - Download extended BOOTP data, or kernel image from NFS server
486 **************************************************************************/
487 static int nfs ( char *url __unused, struct sockaddr_in *server,
488                  char *name, struct buffer *buffer ) {
489         static int recursion = 0;
490         int sport;
491         int err, namelen = strlen(name);
492         char dirname[300], *fname;
493         char dirfh[NFS_FHSIZE];         /* file handle of directory */
494         char filefh[NFS_FHSIZE];        /* file handle of kernel image */
495         int rlen, size, offs, len;
496         struct rpc_t *rpc;
497
498         sport = oport++;
499         if (oport > START_OPORT+OPORT_SWEEP) {
500                 oport = START_OPORT;
501         }
502
503         mount_server.sin_addr = nfs_server.sin_addr = server->sin_addr;
504         mount_server.sin_port = rpc_lookup(server, PROG_MOUNT, 1, sport);
505         if ( ! mount_server.sin_port ) {
506                 DBG ( "Cannot get mount port from %!:%d\n",
507                       server->sin_addr.s_addr, server->sin_port );
508                 return 0;
509         }
510         nfs_server.sin_port = rpc_lookup(server, PROG_NFS, 2, sport);
511         if ( ! mount_server.sin_port ) {
512                 DBG ( "Cannot get nfs port from %!:%d\n",
513                       server->sin_addr.s_addr, server->sin_port );
514                 return 0;
515         }
516
517         if ( name != dirname ) {
518                 memcpy(dirname, name, namelen + 1);
519         }
520         recursion = 0;
521 nfssymlink:
522         if ( recursion > NFS_MAXLINKDEPTH ) {
523                 DBG ( "\nRecursion: More than %d symlinks followed. Abort.\n",
524                       NFS_MAXLINKDEPTH );
525                 return  0;
526         }
527         recursion++;
528         fname = dirname + (namelen - 1);
529         while (fname >= dirname) {
530                 if (*fname == '/') {
531                         *fname = '\0';
532                         fname++;
533                         break;
534                 }
535                 fname--;
536         }
537         if (fname < dirname) {
538                 DBG("can't parse file name %s\n", name);
539                 return 0;
540         }
541
542         err = nfs_mount(&mount_server, dirname, dirfh, sport);
543         if (err) {
544                 DBG("mounting %s: ", dirname);
545                 nfs_printerror(err);
546                 /* just to be sure... */
547                 nfs_reset();
548                 return 0;
549         }
550
551         err = nfs_lookup(&nfs_server, dirfh, fname, filefh, sport);
552         if (err) {
553                 DBG("looking up %s: ", fname);
554                 nfs_printerror(err);
555                 nfs_reset();
556                 return 0;
557         }
558
559         offs = 0;
560         size = -1;      /* will be set properly with the first reply */
561         len = NFS_READ_SIZE;    /* first request is always full size */
562         do {
563                 err = nfs_read(&nfs_server, filefh, offs, len, sport);
564                 if ((err <= -NFSERR_ISDIR)&&(err >= -NFSERR_INVAL) && (offs == 0)) {
565                         // An error occured. NFS servers tend to sending
566                         // errors 21 / 22 when symlink instead of real file
567                         // is requested. So check if it's a symlink!
568                         if ( nfs_readlink(&nfs_server, dirfh, dirname,
569                                           filefh, sport) == 0 ) {
570                                 printf("\nLoading symlink:%s ..",dirname);
571                                 goto nfssymlink;
572                         }
573                         nfs_printerror(err);
574                         nfs_reset();
575                         return 0;
576                 }
577                 if (err) {
578                         printf("\nError reading at offset %d: ", offs);
579                         nfs_printerror(err);
580                         nfs_reset();
581                         return 0;
582                 }
583
584                 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
585
586                 /* size must be found out early to allow EOF detection */
587                 if (size == -1) {
588                         size = ntohl(rpc->u.reply.data[6]);
589                 }
590                 rlen = ntohl(rpc->u.reply.data[18]);
591                 if (rlen > len) {
592                         rlen = len;     /* shouldn't happen...  */
593                 }
594
595                 if ( ! fill_buffer ( buffer, &rpc->u.reply.data[19],
596                                      offs, rlen ) ) {
597                         nfs_reset();
598                         return 0;
599                 }
600
601                 offs += rlen;
602                 /* last request is done with matching requested read size */
603                 if (size-offs < NFS_READ_SIZE) {
604                         len = size-offs;
605                 }
606         } while (len != 0);
607         /* len == 0 means that all the file has been read */
608         return 1;
609 }
610
611 INIT_FN ( INIT_RPC, rpc_init, nfs_reset, nfs_reset );
612
613 struct protocol nfs_protocol __protocol = {
614         .name = "nfs",
615         .default_port = SUNRPC_PORT,
616         .load = nfs,
617 };