Updated to new protocol API, made to compile.
[people/xl0/gpxe.git] / src / proto / nfs.c
1 #include "etherboot.h"
2 #include "init.h"
3 #include "proto.h"
4 #include "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         if ( ! addr->sin_port ) {
107                 addr->sin_port = SUNRPC_PORT;
108         }
109         for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
110                 long timeout;
111                 udp_transmit(addr->sin_addr.s_addr, sport, addr->sin_port,
112                         (char *)p - (char *)&buf, &buf);
113                 timeout = rfc2131_sleep_interval(TIMEOUT, retries);
114                 if (await_reply(await_rpc, sport, &id, timeout)) {
115                         rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
116                         if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
117                             rpc->u.reply.astatus) {
118                                 rpc_printerror(rpc);
119                                 return 0;
120                         } else {
121                                 return ntohl(rpc->u.reply.data[0]);
122                         }
123                 }
124         }
125         return 0;
126 }
127
128 /**************************************************************************
129 RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
130 **************************************************************************/
131 static long *rpc_add_credentials(long *p)
132 {
133         int hl;
134
135         /* Here's the executive summary on authentication requirements of the
136          * various NFS server implementations:  Linux accepts both AUTH_NONE
137          * and AUTH_UNIX authentication (also accepts an empty hostname field
138          * in the AUTH_UNIX scheme).  *BSD refuses AUTH_NONE, but accepts
139          * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
140          * scheme).  To be safe, use AUTH_UNIX and pass the hostname if we have
141          * it (if the BOOTP/DHCP reply didn't give one, just use an empty
142          * hostname).  */
143
144         hl = (hostnamelen + 3) & ~3;
145
146         /* Provide an AUTH_UNIX credential.  */
147         *p++ = htonl(1);                /* AUTH_UNIX */
148         *p++ = htonl(hl+20);            /* auth length */
149         *p++ = htonl(0);                /* stamp */
150         *p++ = htonl(hostnamelen);      /* hostname string */
151         if (hostnamelen & 3) {
152                 *(p + hostnamelen / 4) = 0; /* add zero padding */
153         }
154         memcpy(p, hostname, hostnamelen);
155         p += hl / 4;
156         *p++ = 0;                       /* uid */
157         *p++ = 0;                       /* gid */
158         *p++ = 0;                       /* auxiliary gid list */
159
160         /* Provide an AUTH_NONE verifier.  */
161         *p++ = 0;                       /* AUTH_NONE */
162         *p++ = 0;                       /* auth length */
163
164         return p;
165 }
166
167 /**************************************************************************
168 NFS_PRINTERROR - Print a NFS error message
169 **************************************************************************/
170 static void nfs_printerror(int err)
171 {
172         switch (-err) {
173         case NFSERR_PERM:
174                 printf("Not owner\n");
175                 break;
176         case NFSERR_NOENT:
177                 printf("No such file or directory\n");
178                 break;
179         case NFSERR_ACCES:
180                 printf("Permission denied\n");
181                 break;
182         case NFSERR_ISDIR:
183                 printf("Directory given where filename expected\n");
184                 break;
185         case NFSERR_INVAL:
186                 printf("Invalid filehandle\n");
187                 break; // INVAL is not defined in NFSv2, some NFS-servers
188                 // seem to use it in answers to v2 nevertheless.
189         case 9998:
190                 printf("low-level RPC failure (parameter decoding problem?)\n");
191                 break;
192         case 9999:
193                 printf("low-level RPC failure (authentication problem?)\n");
194                 break;
195         default:
196                 printf("Unknown NFS error %d\n", -err);
197         }
198 }
199
200 /**************************************************************************
201 NFS_MOUNT - Mount an NFS Filesystem
202 **************************************************************************/
203 static int nfs_mount(struct sockaddr_in *server, char *path, char *fh, int sport)
204 {
205         struct rpc_t buf, *rpc;
206         unsigned long id;
207         int retries;
208         long *p;
209         int pathlen = strlen(path);
210
211         id = rpc_id++;
212         buf.u.call.id = htonl(id);
213         buf.u.call.type = htonl(MSG_CALL);
214         buf.u.call.rpcvers = htonl(2);  /* use RPC version 2 */
215         buf.u.call.prog = htonl(PROG_MOUNT);
216         buf.u.call.vers = htonl(1);     /* mountd is version 1 */
217         buf.u.call.proc = htonl(MOUNT_ADDENTRY);
218         p = rpc_add_credentials((long *)buf.u.call.data);
219         *p++ = htonl(pathlen);
220         if (pathlen & 3) {
221                 *(p + pathlen / 4) = 0; /* add zero padding */
222         }
223         memcpy(p, path, pathlen);
224         p += (pathlen + 3) / 4;
225         for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
226                 long timeout;
227                 udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
228                         (char *)p - (char *)&buf, &buf);
229                 timeout = rfc2131_sleep_interval(TIMEOUT, retries);
230                 if (await_reply(await_rpc, sport, &id, timeout)) {
231                         rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
232                         if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
233                             rpc->u.reply.astatus || rpc->u.reply.data[0]) {
234                                 rpc_printerror(rpc);
235                                 if (rpc->u.reply.rstatus) {
236                                         /* RPC failed, no verifier, data[0] */
237                                         return -9999;
238                                 }
239                                 if (rpc->u.reply.astatus) {
240                                         /* RPC couldn't decode parameters */
241                                         return -9998;
242                                 }
243                                 return -ntohl(rpc->u.reply.data[0]);
244                         } else {
245                                 memcpy(fh, rpc->u.reply.data + 1, NFS_FHSIZE);
246                                 return 0;
247                         }
248                 }
249         }
250         return -1;
251 }
252
253 /**************************************************************************
254 NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
255 **************************************************************************/
256 static void nfs_umountall(struct sockaddr_in *server)
257 {
258         struct rpc_t buf, *rpc;
259         unsigned long id;
260         int retries;
261         long *p;
262
263         id = rpc_id++;
264         buf.u.call.id = htonl(id);
265         buf.u.call.type = htonl(MSG_CALL);
266         buf.u.call.rpcvers = htonl(2);  /* use RPC version 2 */
267         buf.u.call.prog = htonl(PROG_MOUNT);
268         buf.u.call.vers = htonl(1);     /* mountd is version 1 */
269         buf.u.call.proc = htonl(MOUNT_UMOUNTALL);
270         p = rpc_add_credentials((long *)buf.u.call.data);
271         for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
272                 long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
273                 udp_transmit(server->sin_addr.s_addr, oport, server->sin_port,
274                         (char *)p - (char *)&buf, &buf);
275                 if (await_reply(await_rpc, oport, &id, timeout)) {
276                         rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
277                         if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
278                             rpc->u.reply.astatus) {
279                                 rpc_printerror(rpc);
280                         }
281                         break;
282                 }
283         }
284 }
285
286 /**************************************************************************
287 NFS_RESET - Reset the NFS subsystem
288 **************************************************************************/
289 static void nfs_reset ( void ) {
290         /* If we have a mount server, call nfs_umountall() */
291         if ( mount_server.sin_addr.s_addr ) {
292                 nfs_umountall ( &mount_server );
293         }
294         /* Zero the data structures */
295         memset ( &mount_server, 0, sizeof ( mount_server ) );
296         memset ( &nfs_server, 0, sizeof ( nfs_server ) );
297 }
298
299 /***************************************************************************
300  * NFS_READLINK (AH 2003-07-14)
301  * This procedure is called when read of the first block fails -
302  * this probably happens when it's a directory or a symlink
303  * In case of successful readlink(), the dirname is manipulated,
304  * so that inside the nfs() function a recursion can be done.
305  **************************************************************************/
306 static int nfs_readlink(struct sockaddr_in *server, char *fh __unused,
307                         char *path, char *nfh, int sport)
308 {
309         struct rpc_t buf, *rpc;
310         unsigned long id;
311         long *p;
312         int retries;
313         int pathlen = strlen(path);
314
315         id = rpc_id++;
316         buf.u.call.id = htonl(id);
317         buf.u.call.type = htonl(MSG_CALL);
318         buf.u.call.rpcvers = htonl(2);  /* use RPC version 2 */
319         buf.u.call.prog = htonl(PROG_NFS);
320         buf.u.call.vers = htonl(2);     /* nfsd is version 2 */
321         buf.u.call.proc = htonl(NFS_READLINK);
322         p = rpc_add_credentials((long *)buf.u.call.data);
323         memcpy(p, nfh, NFS_FHSIZE);
324         p += (NFS_FHSIZE / 4);
325         for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
326                 long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
327                 udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
328                         (char *)p - (char *)&buf, &buf);
329                 if (await_reply(await_rpc, sport, &id, timeout)) {
330                         rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
331                         if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
332                             rpc->u.reply.astatus || rpc->u.reply.data[0]) {
333                                 rpc_printerror(rpc);
334                                 if (rpc->u.reply.rstatus) {
335                                         /* RPC failed, no verifier, data[0] */
336                                         return -9999;
337                                 }
338                                 if (rpc->u.reply.astatus) {
339                                         /* RPC couldn't decode parameters */
340                                         return -9998;
341                                 }
342                                 return -ntohl(rpc->u.reply.data[0]);
343                         } else {
344                                 // It *is* a link.
345                                 // If it's a relative link, append everything to dirname, filename TOO!
346                                 retries = strlen ( (char *)(&(rpc->u.reply.data[2]) ));
347                                 if ( *((char *)(&(rpc->u.reply.data[2]))) != '/' ) {
348                                         path[pathlen++] = '/';
349                                         while ( ( retries + pathlen ) > 298 ) {
350                                                 retries--;
351                                         }
352                                         if ( retries > 0 ) {
353                                                 memcpy(path + pathlen, &(rpc->u.reply.data[2]), retries + 1);
354                                         } else { retries = 0; }
355                                         path[pathlen + retries] = 0;
356                                 } else {
357                                         // Else make it the only path.
358                                         if ( retries > 298 ) { retries = 298; }
359                                         memcpy ( path, &(rpc->u.reply.data[2]), retries + 1 );
360                                         path[retries] = 0;
361                                 }
362                                 return 0;
363                         }
364                 }
365         }
366         return -1;
367 }
368 /**************************************************************************
369 NFS_LOOKUP - Lookup Pathname
370 **************************************************************************/
371 static int nfs_lookup(struct sockaddr_in *server, char *fh, char *path, char *nfh,
372         int sport)
373 {
374         struct rpc_t buf, *rpc;
375         unsigned long id;
376         long *p;
377         int retries;
378         int pathlen = strlen(path);
379
380         id = rpc_id++;
381         buf.u.call.id = htonl(id);
382         buf.u.call.type = htonl(MSG_CALL);
383         buf.u.call.rpcvers = htonl(2);  /* use RPC version 2 */
384         buf.u.call.prog = htonl(PROG_NFS);
385         buf.u.call.vers = htonl(2);     /* nfsd is version 2 */
386         buf.u.call.proc = htonl(NFS_LOOKUP);
387         p = rpc_add_credentials((long *)buf.u.call.data);
388         memcpy(p, fh, NFS_FHSIZE);
389         p += (NFS_FHSIZE / 4);
390         *p++ = htonl(pathlen);
391         if (pathlen & 3) {
392                 *(p + pathlen / 4) = 0; /* add zero padding */
393         }
394         memcpy(p, path, pathlen);
395         p += (pathlen + 3) / 4;
396         for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
397                 long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
398                 udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
399                         (char *)p - (char *)&buf, &buf);
400                 if (await_reply(await_rpc, sport, &id, timeout)) {
401                         rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
402                         if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
403                             rpc->u.reply.astatus || rpc->u.reply.data[0]) {
404                                 rpc_printerror(rpc);
405                                 if (rpc->u.reply.rstatus) {
406                                         /* RPC failed, no verifier, data[0] */
407                                         return -9999;
408                                 }
409                                 if (rpc->u.reply.astatus) {
410                                         /* RPC couldn't decode parameters */
411                                         return -9998;
412                                 }
413                                 return -ntohl(rpc->u.reply.data[0]);
414                         } else {
415                                 memcpy(nfh, rpc->u.reply.data + 1, NFS_FHSIZE);
416                                 return 0;
417                         }
418                 }
419         }
420         return -1;
421 }
422
423 /**************************************************************************
424 NFS_READ - Read File on NFS Server
425 **************************************************************************/
426 static int nfs_read(struct sockaddr_in *server, char *fh, int offset, int len,
427                     int sport)
428 {
429         struct rpc_t buf, *rpc;
430         unsigned long id;
431         int retries;
432         long *p;
433
434         static int tokens=0;
435         /*
436          * Try to implement something similar to a window protocol in
437          * terms of response to losses. On successful receive, increment
438          * the number of tokens by 1 (cap at 256). On failure, halve it.
439          * When the number of tokens is >= 2, use a very short timeout.
440          */
441
442         id = rpc_id++;
443         buf.u.call.id = htonl(id);
444         buf.u.call.type = htonl(MSG_CALL);
445         buf.u.call.rpcvers = htonl(2);  /* use RPC version 2 */
446         buf.u.call.prog = htonl(PROG_NFS);
447         buf.u.call.vers = htonl(2);     /* nfsd is version 2 */
448         buf.u.call.proc = htonl(NFS_READ);
449         p = rpc_add_credentials((long *)buf.u.call.data);
450         memcpy(p, fh, NFS_FHSIZE);
451         p += NFS_FHSIZE / 4;
452         *p++ = htonl(offset);
453         *p++ = htonl(len);
454         *p++ = 0;               /* unused parameter */
455         for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
456                 long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
457                 if (tokens >= 2)
458                         timeout = TICKS_PER_SEC/2;
459
460                 udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
461                         (char *)p - (char *)&buf, &buf);
462                 if (await_reply(await_rpc, sport, &id, timeout)) {
463                         if (tokens < 256)
464                                 tokens++;
465                         rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
466                         if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
467                             rpc->u.reply.astatus || rpc->u.reply.data[0]) {
468                                 rpc_printerror(rpc);
469                                 if (rpc->u.reply.rstatus) {
470                                         /* RPC failed, no verifier, data[0] */
471                                         return -9999;
472                                 }
473                                 if (rpc->u.reply.astatus) {
474                                         /* RPC couldn't decode parameters */
475                                         return -9998;
476                                 }
477                                 return -ntohl(rpc->u.reply.data[0]);
478                         } else {
479                                 return 0;
480                         }
481                 } else
482                         tokens >>= 1;
483         }
484         return -1;
485 }
486
487 /**************************************************************************
488 NFS - Download extended BOOTP data, or kernel image from NFS server
489 **************************************************************************/
490 static int nfs ( char *url __unused,
491                  struct sockaddr_in *server,
492                  char *name,
493                  int ( * process ) ( unsigned char *data,
494                                      unsigned int blocknum,
495                                      unsigned int len, int eof ) ) {
496         static int recursion = 0;
497         int sport;
498         int err, namelen = strlen(name);
499         char dirname[300], *fname;
500         char dirfh[NFS_FHSIZE];         /* file handle of directory */
501         char filefh[NFS_FHSIZE];        /* file handle of kernel image */
502         unsigned int block;
503         int rlen, size, offs, len;
504         struct rpc_t *rpc;
505
506         sport = oport++;
507         if (oport > START_OPORT+OPORT_SWEEP) {
508                 oport = START_OPORT;
509         }
510
511         mount_server.sin_addr = nfs_server.sin_addr = server->sin_addr;
512         mount_server.sin_port = rpc_lookup(server, PROG_MOUNT, 1, sport);
513         if ( ! mount_server.sin_port ) {
514                 DBG ( "Cannot get mount port from %!:%d\n",
515                       server->sin_addr.s_addr, server->sin_port );
516                 return 0;
517         }
518         nfs_server.sin_port = rpc_lookup(server, PROG_NFS, 2, sport);
519         if ( ! mount_server.sin_port ) {
520                 DBG ( "Cannot get nfs port from %!:%d\n",
521                       server->sin_addr.s_addr, server->sin_port );
522                 return 0;
523         }
524
525         if ( name != dirname ) {
526                 memcpy(dirname, name, namelen + 1);
527         }
528         recursion = 0;
529 nfssymlink:
530         if ( recursion > NFS_MAXLINKDEPTH ) {
531                 DBG ( "\nRecursion: More than %d symlinks followed. Abort.\n",
532                       NFS_MAXLINKDEPTH );
533                 return  0;
534         }
535         recursion++;
536         fname = dirname + (namelen - 1);
537         while (fname >= dirname) {
538                 if (*fname == '/') {
539                         *fname = '\0';
540                         fname++;
541                         break;
542                 }
543                 fname--;
544         }
545         if (fname < dirname) {
546                 DBG("can't parse file name %s\n", name);
547                 return 0;
548         }
549
550         err = nfs_mount(&mount_server, dirname, dirfh, sport);
551         if (err) {
552                 DBG("mounting %s: ", dirname);
553                 nfs_printerror(err);
554                 /* just to be sure... */
555                 nfs_reset();
556                 return 0;
557         }
558
559         err = nfs_lookup(&nfs_server, dirfh, fname, filefh, sport);
560         if (err) {
561                 DBG("looking up %s: ", fname);
562                 nfs_printerror(err);
563                 nfs_reset();
564                 return 0;
565         }
566
567         offs = 0;
568         block = 1;      /* blocks are numbered starting from 1 */
569         size = -1;      /* will be set properly with the first reply */
570         len = NFS_READ_SIZE;    /* first request is always full size */
571         do {
572                 err = nfs_read(&nfs_server, filefh, offs, len, sport);
573                 if ((err <= -NFSERR_ISDIR)&&(err >= -NFSERR_INVAL) && (offs == 0)) {
574                         // An error occured. NFS servers tend to sending
575                         // errors 21 / 22 when symlink instead of real file
576                         // is requested. So check if it's a symlink!
577                         block = nfs_readlink(&nfs_server, dirfh, dirname,
578                                         filefh, sport);
579                         if ( 0 == block ) {
580                                 printf("\nLoading symlink:%s ..",dirname);
581                                 goto nfssymlink;
582                         }
583                         nfs_printerror(err);
584                         nfs_reset();
585                         return 0;
586                 }
587                 if (err) {
588                         printf("reading at offset %d: ", offs);
589                         nfs_printerror(err);
590                         nfs_reset();
591                         return 0;
592                 }
593
594                 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
595
596                 /* size must be found out early to allow EOF detection */
597                 if (size == -1) {
598                         size = ntohl(rpc->u.reply.data[6]);
599                 }
600                 rlen = ntohl(rpc->u.reply.data[18]);
601                 if (rlen > len) {
602                         rlen = len;     /* shouldn't happen...  */
603                 }
604
605                 err = process((char *)&rpc->u.reply.data[19], block, rlen,
606                         (offs+rlen == size));
607                 if (err <= 0) {
608                         nfs_reset();
609                         return err;
610                 }
611
612                 block++;
613                 offs += rlen;
614                 /* last request is done with matching requested read size */
615                 if (size-offs < NFS_READ_SIZE) {
616                         len = size-offs;
617                 }
618         } while (len != 0);
619         /* len == 0 means that all the file has been read */
620         return 1;
621 }
622
623 INIT_FN ( INIT_RPC, rpc_init, nfs_reset, nfs_reset );
624
625 static struct protocol nfs_protocol __protocol = {
626         "nfs", nfs
627 };