You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

nfs.c 19KB

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