Blame view
net/nfs.c
23.8 KB
cbd8a35c6
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
/* * NFS support driver - based on etherboot and U-BOOT's tftp.c * * Masami Komiya <mkomiya@sonare.it> 2004 * */ /* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read: * large portions are copied verbatim) as distributed in OSKit 0.97. A few * changes were necessary to adapt the code to Etherboot and to fix several * inconsistencies. Also the RPC message preparation is done "by hand" to * avoid adding netsprintf() which I find hard to understand and use. */ /* NOTE 2: Etherboot does not care about things beyond the kernel image, so * it loads the kernel image off the boot server (ARP_SERVER) and does not * access the client root disk (root-path in dhcpd.conf), which would use * ARP_ROOTSERVER. The root disk is something the operating system we are * about to load needs to use. This is different from the OSKit 0.97 logic. */ /* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14 * If a symlink is encountered, it is followed as far as possible (recursion * possible, maximum 16 steps). There is no clearing of ".."'s inside the * path, so please DON'T DO THAT. thx. */ |
b0baca982
|
24 25 26 |
/* NOTE 4: NFSv3 support added by Guillaume GARDET, 2016-June-20. * NFSv2 is still used by default. But if server does not support NFSv2, then * NFSv3 is used, if available on NFS server. */ |
cbd8a35c6
|
27 28 |
#include <common.h> #include <command.h> |
0ee48252b
|
29 |
#include <flash.h> |
8e8ccfe1a
|
30 |
#include <image.h> |
cbd8a35c6
|
31 32 |
#include <net.h> #include <malloc.h> |
55d5fd9a8
|
33 |
#include <mapmem.h> |
cbd8a35c6
|
34 35 |
#include "nfs.h" #include "bootp.h" |
1045315df
|
36 |
#include <time.h> |
cbd8a35c6
|
37 |
|
cbd8a35c6
|
38 |
#define HASHES_PER_LINE 65 /* Number of "loading" hashes per line */ |
fe891ecf4
|
39 |
#define NFS_RETRY_COUNT 30 |
48a3e999c
|
40 41 42 43 44 |
#ifndef CONFIG_NFS_TIMEOUT # define NFS_TIMEOUT 2000UL #else # define NFS_TIMEOUT CONFIG_NFS_TIMEOUT #endif |
cbd8a35c6
|
45 |
|
fa84fa708
|
46 47 |
#define NFS_RPC_ERR 1 #define NFS_RPC_DROP 124 |
c9f6c91b4
|
48 49 |
static int fs_mounted; static unsigned long rpc_id; |
cbd8a35c6
|
50 51 |
static int nfs_offset = -1; static int nfs_len; |
fa84fa708
|
52 |
static ulong nfs_timeout = NFS_TIMEOUT; |
cbd8a35c6
|
53 |
|
b0baca982
|
54 |
static char dirfh[NFS_FHSIZE]; /* NFSv2 / NFSv3 file handle of directory */ |
5280c7691
|
55 56 |
static char filefh[NFS3_FHSIZE]; /* NFSv2 / NFSv3 file handle */ static int filefh3_length; /* (variable) length of filefh when NFSv3 */ |
cbd8a35c6
|
57 |
|
22f6e99d5
|
58 |
static enum net_loop_state nfs_download_state; |
049a95a77
|
59 |
static struct in_addr nfs_server_ip; |
68c76a3a3
|
60 61 62 63 64 |
static int nfs_server_mount_port; static int nfs_server_port; static int nfs_our_port; static int nfs_timeout_count; static int nfs_state; |
cbd8a35c6
|
65 66 67 68 69 70 71 |
#define STATE_PRCLOOKUP_PROG_MOUNT_REQ 1 #define STATE_PRCLOOKUP_PROG_NFS_REQ 2 #define STATE_MOUNT_REQ 3 #define STATE_UMOUNT_REQ 4 #define STATE_LOOKUP_REQ 5 #define STATE_READ_REQ 6 #define STATE_READLINK_REQ 7 |
cbd8a35c6
|
72 73 74 |
static char *nfs_filename; static char *nfs_path; static char nfs_path_buff[2048]; |
b0baca982
|
75 76 77 |
#define NFSV2_FLAG 1 #define NFSV3_FLAG 1 << 1 static char supported_nfs_versions = NFSV2_FLAG | NFSV3_FLAG; |
68c76a3a3
|
78 |
static inline int store_block(uchar *src, unsigned offset, unsigned len) |
cbd8a35c6
|
79 |
{ |
a084f7da8
|
80 |
ulong newsize = offset + len; |
6d0f6bcf3
|
81 |
#ifdef CONFIG_SYS_DIRECT_FLASH_NFS |
cbd8a35c6
|
82 |
int i, rc = 0; |
c9f6c91b4
|
83 |
for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { |
cbd8a35c6
|
84 |
/* start address in flash? */ |
bb872dd93
|
85 |
if (image_load_addr + offset >= flash_info[i].start[0]) { |
cbd8a35c6
|
86 87 88 89 90 91 |
rc = 1; break; } } if (rc) { /* Flash is destination for this packet */ |
bb872dd93
|
92 93 |
rc = flash_write((uchar *)src, (ulong)image_load_addr + offset, len); |
cbd8a35c6
|
94 |
if (rc) { |
c9f6c91b4
|
95 |
flash_perror(rc); |
23a7a32d4
|
96 |
return -1; |
cbd8a35c6
|
97 98 |
} } else |
6d0f6bcf3
|
99 |
#endif /* CONFIG_SYS_DIRECT_FLASH_NFS */ |
cbd8a35c6
|
100 |
{ |
bb872dd93
|
101 |
void *ptr = map_sysmem(image_load_addr + offset, len); |
55d5fd9a8
|
102 103 104 |
memcpy(ptr, src, len); unmap_sysmem(ptr); |
cbd8a35c6
|
105 |
} |
a084f7da8
|
106 |
|
1411157d8
|
107 108 |
if (net_boot_file_size < (offset + len)) net_boot_file_size = newsize; |
23a7a32d4
|
109 |
return 0; |
cbd8a35c6
|
110 |
} |
68c76a3a3
|
111 |
static char *basename(char *path) |
cbd8a35c6
|
112 113 114 115 116 117 118 119 120 121 122 123 124 |
{ char *fname; fname = path + strlen(path) - 1; while (fname >= path) { if (*fname == '/') { fname++; break; } fname--; } return fname; } |
68c76a3a3
|
125 |
static char *dirname(char *path) |
cbd8a35c6
|
126 127 |
{ char *fname; |
c9f6c91b4
|
128 |
fname = basename(path); |
cbd8a35c6
|
129 130 131 132 133 134 |
--fname; *fname = '\0'; return path; } /************************************************************************** |
cbd8a35c6
|
135 136 |
RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries **************************************************************************/ |
e4ead4a21
|
137 |
static uint32_t *rpc_add_credentials(uint32_t *p) |
cbd8a35c6
|
138 |
{ |
cbd8a35c6
|
139 140 141 142 143 144 145 146 |
/* Here's the executive summary on authentication requirements of the * various NFS server implementations: Linux accepts both AUTH_NONE * and AUTH_UNIX authentication (also accepts an empty hostname field * in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX * scheme). To be safe, use AUTH_UNIX and pass the hostname if we have * it (if the BOOTP/DHCP reply didn't give one, just use an empty * hostname). */ |
cbd8a35c6
|
147 148 |
/* Provide an AUTH_UNIX credential. */ *p++ = htonl(1); /* AUTH_UNIX */ |
1ff65d440
|
149 150 151 |
*p++ = htonl(20); /* auth length */ *p++ = 0; /* stamp */ *p++ = 0; /* hostname string */ |
cbd8a35c6
|
152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
*p++ = 0; /* uid */ *p++ = 0; /* gid */ *p++ = 0; /* auxiliary gid list */ /* Provide an AUTH_NONE verifier. */ *p++ = 0; /* AUTH_NONE */ *p++ = 0; /* auth length */ return p; } /************************************************************************** RPC_LOOKUP - Lookup RPC Port numbers **************************************************************************/ |
a73588fe4
|
166 |
static void rpc_req(int rpc_prog, int rpc_proc, uint32_t *data, int datalen) |
cbd8a35c6
|
167 |
{ |
a73588fe4
|
168 |
struct rpc_t rpc_pkt; |
cbd8a35c6
|
169 |
unsigned long id; |
a73588fe4
|
170 |
uint32_t *p; |
cbd8a35c6
|
171 172 |
int pktlen; int sport; |
c3f9d4939
|
173 |
id = ++rpc_id; |
a73588fe4
|
174 175 176 177 |
rpc_pkt.u.call.id = htonl(id); rpc_pkt.u.call.type = htonl(MSG_CALL); rpc_pkt.u.call.rpcvers = htonl(2); /* use RPC version 2 */ rpc_pkt.u.call.prog = htonl(rpc_prog); |
b0baca982
|
178 179 180 |
switch (rpc_prog) { case PROG_NFS: if (supported_nfs_versions & NFSV2_FLAG) |
a73588fe4
|
181 |
rpc_pkt.u.call.vers = htonl(2); /* NFS v2 */ |
b0baca982
|
182 |
else /* NFSV3_FLAG */ |
a73588fe4
|
183 |
rpc_pkt.u.call.vers = htonl(3); /* NFS v3 */ |
b0baca982
|
184 185 186 187 |
break; case PROG_PORTMAP: case PROG_MOUNT: default: |
a73588fe4
|
188 |
rpc_pkt.u.call.vers = htonl(2); /* portmapper is version 2 */ |
b0baca982
|
189 |
} |
a73588fe4
|
190 |
rpc_pkt.u.call.proc = htonl(rpc_proc); |
15eea9a1a
|
191 |
p = rpc_pkt.u.call.data; |
a73588fe4
|
192 193 |
if (datalen) |
15eea9a1a
|
194 |
memcpy(p, data, datalen * sizeof(uint32_t)); |
a73588fe4
|
195 196 |
pktlen = (char *)p + datalen * sizeof(uint32_t) - (char *)&rpc_pkt; |
cbd8a35c6
|
197 |
|
a73588fe4
|
198 199 |
memcpy((char *)net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE, &rpc_pkt.u.data[0], pktlen); |
cbd8a35c6
|
200 201 202 203 |
if (rpc_prog == PROG_PORTMAP) sport = SUNRPC_PORT; else if (rpc_prog == PROG_MOUNT) |
68c76a3a3
|
204 |
sport = nfs_server_mount_port; |
cbd8a35c6
|
205 |
else |
68c76a3a3
|
206 |
sport = nfs_server_port; |
cbd8a35c6
|
207 |
|
1203fccee
|
208 |
net_send_udp_packet(net_server_ethaddr, nfs_server_ip, sport, |
68c76a3a3
|
209 |
nfs_our_port, pktlen); |
cbd8a35c6
|
210 211 212 213 214 |
} /************************************************************************** RPC_LOOKUP - Lookup RPC Port numbers **************************************************************************/ |
68c76a3a3
|
215 |
static void rpc_lookup_req(int prog, int ver) |
cbd8a35c6
|
216 |
{ |
a73588fe4
|
217 |
uint32_t data[16]; |
cbd8a35c6
|
218 219 220 221 222 223 224 |
data[0] = 0; data[1] = 0; /* auth credential */ data[2] = 0; data[3] = 0; /* auth verifier */ data[4] = htonl(prog); data[5] = htonl(ver); data[6] = htonl(17); /* IP_UDP */ data[7] = 0; |
a73588fe4
|
225 |
rpc_req(PROG_PORTMAP, PORTMAP_GETPORT, data, 8); |
cbd8a35c6
|
226 227 228 229 230 |
} /************************************************************************** NFS_MOUNT - Mount an NFS Filesystem **************************************************************************/ |
68c76a3a3
|
231 |
static void nfs_mount_req(char *path) |
cbd8a35c6
|
232 |
{ |
a73588fe4
|
233 |
uint32_t data[1024]; |
cbd8a35c6
|
234 235 236 |
uint32_t *p; int len; int pathlen; |
c9f6c91b4
|
237 |
pathlen = strlen(path); |
cbd8a35c6
|
238 |
|
a73588fe4
|
239 |
p = &(data[0]); |
e4ead4a21
|
240 |
p = rpc_add_credentials(p); |
cbd8a35c6
|
241 242 |
*p++ = htonl(pathlen); |
c9f6c91b4
|
243 244 245 |
if (pathlen & 3) *(p + pathlen / 4) = 0; memcpy(p, path, pathlen); |
cbd8a35c6
|
246 |
p += (pathlen + 3) / 4; |
a73588fe4
|
247 |
len = (uint32_t *)p - (uint32_t *)&(data[0]); |
cbd8a35c6
|
248 |
|
a73588fe4
|
249 |
rpc_req(PROG_MOUNT, MOUNT_ADDENTRY, data, len); |
cbd8a35c6
|
250 251 252 253 254 |
} /************************************************************************** NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server **************************************************************************/ |
68c76a3a3
|
255 |
static void nfs_umountall_req(void) |
cbd8a35c6
|
256 |
{ |
a73588fe4
|
257 |
uint32_t data[1024]; |
cbd8a35c6
|
258 259 |
uint32_t *p; int len; |
68c76a3a3
|
260 |
if ((nfs_server_mount_port == -1) || (!fs_mounted)) |
cbd8a35c6
|
261 262 |
/* Nothing mounted, nothing to umount */ return; |
cbd8a35c6
|
263 |
|
a73588fe4
|
264 |
p = &(data[0]); |
e4ead4a21
|
265 |
p = rpc_add_credentials(p); |
cbd8a35c6
|
266 |
|
a73588fe4
|
267 |
len = (uint32_t *)p - (uint32_t *)&(data[0]); |
cbd8a35c6
|
268 |
|
a73588fe4
|
269 |
rpc_req(PROG_MOUNT, MOUNT_UMOUNTALL, data, len); |
cbd8a35c6
|
270 271 272 273 274 275 276 277 278 |
} /*************************************************************************** * NFS_READLINK (AH 2003-07-14) * This procedure is called when read of the first block fails - * this probably happens when it's a directory or a symlink * In case of successful readlink(), the dirname is manipulated, * so that inside the nfs() function a recursion can be done. **************************************************************************/ |
68c76a3a3
|
279 |
static void nfs_readlink_req(void) |
cbd8a35c6
|
280 |
{ |
a73588fe4
|
281 |
uint32_t data[1024]; |
cbd8a35c6
|
282 283 |
uint32_t *p; int len; |
a73588fe4
|
284 |
p = &(data[0]); |
e4ead4a21
|
285 |
p = rpc_add_credentials(p); |
cbd8a35c6
|
286 |
|
b0baca982
|
287 288 289 290 291 |
if (supported_nfs_versions & NFSV2_FLAG) { memcpy(p, filefh, NFS_FHSIZE); p += (NFS_FHSIZE / 4); } else { /* NFSV3_FLAG */ *p++ = htonl(filefh3_length); |
5280c7691
|
292 |
memcpy(p, filefh, filefh3_length); |
b0baca982
|
293 294 |
p += (filefh3_length / 4); } |
cbd8a35c6
|
295 |
|
a73588fe4
|
296 |
len = (uint32_t *)p - (uint32_t *)&(data[0]); |
cbd8a35c6
|
297 |
|
a73588fe4
|
298 |
rpc_req(PROG_NFS, NFS_READLINK, data, len); |
cbd8a35c6
|
299 300 301 302 303 |
} /************************************************************************** NFS_LOOKUP - Lookup Pathname **************************************************************************/ |
68c76a3a3
|
304 |
static void nfs_lookup_req(char *fname) |
cbd8a35c6
|
305 |
{ |
a73588fe4
|
306 |
uint32_t data[1024]; |
cbd8a35c6
|
307 308 309 |
uint32_t *p; int len; int fnamelen; |
c9f6c91b4
|
310 |
fnamelen = strlen(fname); |
cbd8a35c6
|
311 |
|
a73588fe4
|
312 |
p = &(data[0]); |
e4ead4a21
|
313 |
p = rpc_add_credentials(p); |
cbd8a35c6
|
314 |
|
b0baca982
|
315 316 317 318 319 320 321 322 |
if (supported_nfs_versions & NFSV2_FLAG) { memcpy(p, dirfh, NFS_FHSIZE); p += (NFS_FHSIZE / 4); *p++ = htonl(fnamelen); if (fnamelen & 3) *(p + fnamelen / 4) = 0; memcpy(p, fname, fnamelen); p += (fnamelen + 3) / 4; |
a73588fe4
|
323 |
len = (uint32_t *)p - (uint32_t *)&(data[0]); |
b0baca982
|
324 |
|
a73588fe4
|
325 |
rpc_req(PROG_NFS, NFS_LOOKUP, data, len); |
b0baca982
|
326 327 328 329 330 331 332 333 334 |
} else { /* NFSV3_FLAG */ *p++ = htonl(NFS_FHSIZE); /* Dir handle length */ memcpy(p, dirfh, NFS_FHSIZE); p += (NFS_FHSIZE / 4); *p++ = htonl(fnamelen); if (fnamelen & 3) *(p + fnamelen / 4) = 0; memcpy(p, fname, fnamelen); p += (fnamelen + 3) / 4; |
a73588fe4
|
335 |
len = (uint32_t *)p - (uint32_t *)&(data[0]); |
b0baca982
|
336 |
|
a73588fe4
|
337 |
rpc_req(PROG_NFS, NFS3PROC_LOOKUP, data, len); |
b0baca982
|
338 |
} |
cbd8a35c6
|
339 340 341 342 343 |
} /************************************************************************** NFS_READ - Read File on NFS Server **************************************************************************/ |
68c76a3a3
|
344 |
static void nfs_read_req(int offset, int readlen) |
cbd8a35c6
|
345 |
{ |
a73588fe4
|
346 |
uint32_t data[1024]; |
cbd8a35c6
|
347 348 |
uint32_t *p; int len; |
a73588fe4
|
349 |
p = &(data[0]); |
e4ead4a21
|
350 |
p = rpc_add_credentials(p); |
cbd8a35c6
|
351 |
|
b0baca982
|
352 353 354 355 356 357 358 359 |
if (supported_nfs_versions & NFSV2_FLAG) { memcpy(p, filefh, NFS_FHSIZE); p += (NFS_FHSIZE / 4); *p++ = htonl(offset); *p++ = htonl(readlen); *p++ = 0; } else { /* NFSV3_FLAG */ *p++ = htonl(filefh3_length); |
5280c7691
|
360 |
memcpy(p, filefh, filefh3_length); |
b0baca982
|
361 362 363 364 365 366 |
p += (filefh3_length / 4); *p++ = htonl(0); /* offset is 64-bit long, so fill with 0 */ *p++ = htonl(offset); *p++ = htonl(readlen); *p++ = 0; } |
cbd8a35c6
|
367 |
|
a73588fe4
|
368 |
len = (uint32_t *)p - (uint32_t *)&(data[0]); |
cbd8a35c6
|
369 |
|
a73588fe4
|
370 |
rpc_req(PROG_NFS, NFS_READ, data, len); |
cbd8a35c6
|
371 372 373 374 375 |
} /************************************************************************** RPC request dispatcher **************************************************************************/ |
68c76a3a3
|
376 |
static void nfs_send(void) |
cbd8a35c6
|
377 |
{ |
0ebf04c60
|
378 379 |
debug("%s ", __func__); |
cbd8a35c6
|
380 |
|
68c76a3a3
|
381 |
switch (nfs_state) { |
cbd8a35c6
|
382 |
case STATE_PRCLOOKUP_PROG_MOUNT_REQ: |
b0baca982
|
383 384 385 386 |
if (supported_nfs_versions & NFSV2_FLAG) rpc_lookup_req(PROG_MOUNT, 1); else /* NFSV3_FLAG */ rpc_lookup_req(PROG_MOUNT, 3); |
cbd8a35c6
|
387 388 |
break; case STATE_PRCLOOKUP_PROG_NFS_REQ: |
b0baca982
|
389 390 391 392 |
if (supported_nfs_versions & NFSV2_FLAG) rpc_lookup_req(PROG_NFS, 2); else /* NFSV3_FLAG */ rpc_lookup_req(PROG_NFS, 3); |
cbd8a35c6
|
393 394 |
break; case STATE_MOUNT_REQ: |
c9f6c91b4
|
395 |
nfs_mount_req(nfs_path); |
cbd8a35c6
|
396 397 |
break; case STATE_UMOUNT_REQ: |
c9f6c91b4
|
398 |
nfs_umountall_req(); |
cbd8a35c6
|
399 400 |
break; case STATE_LOOKUP_REQ: |
c9f6c91b4
|
401 |
nfs_lookup_req(nfs_filename); |
cbd8a35c6
|
402 403 |
break; case STATE_READ_REQ: |
c9f6c91b4
|
404 |
nfs_read_req(nfs_offset, nfs_len); |
cbd8a35c6
|
405 406 |
break; case STATE_READLINK_REQ: |
c9f6c91b4
|
407 |
nfs_readlink_req(); |
cbd8a35c6
|
408 409 410 411 412 413 414 |
break; } } /************************************************************************** Handlers for the reply from server **************************************************************************/ |
68c76a3a3
|
415 |
static int rpc_lookup_reply(int prog, uchar *pkt, unsigned len) |
cbd8a35c6
|
416 417 |
{ struct rpc_t rpc_pkt; |
0517cc45e
|
418 |
memcpy(&rpc_pkt.u.data[0], pkt, len); |
cbd8a35c6
|
419 |
|
0ebf04c60
|
420 421 |
debug("%s ", __func__); |
cbd8a35c6
|
422 |
|
fa84fa708
|
423 424 425 426 |
if (ntohl(rpc_pkt.u.reply.id) > rpc_id) return -NFS_RPC_ERR; else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) return -NFS_RPC_DROP; |
c3f9d4939
|
427 |
|
cbd8a35c6
|
428 429 |
if (rpc_pkt.u.reply.rstatus || rpc_pkt.u.reply.verifier || |
c9f6c91b4
|
430 |
rpc_pkt.u.reply.astatus) |
c3f9d4939
|
431 |
return -1; |
cbd8a35c6
|
432 433 434 |
switch (prog) { case PROG_MOUNT: |
68c76a3a3
|
435 |
nfs_server_mount_port = ntohl(rpc_pkt.u.reply.data[0]); |
cbd8a35c6
|
436 437 |
break; case PROG_NFS: |
68c76a3a3
|
438 |
nfs_server_port = ntohl(rpc_pkt.u.reply.data[0]); |
cbd8a35c6
|
439 440 441 442 443 |
break; } return 0; } |
68c76a3a3
|
444 |
static int nfs_mount_reply(uchar *pkt, unsigned len) |
cbd8a35c6
|
445 446 |
{ struct rpc_t rpc_pkt; |
0ebf04c60
|
447 448 |
debug("%s ", __func__); |
cbd8a35c6
|
449 |
|
0517cc45e
|
450 |
memcpy(&rpc_pkt.u.data[0], pkt, len); |
cbd8a35c6
|
451 |
|
fa84fa708
|
452 453 454 455 |
if (ntohl(rpc_pkt.u.reply.id) > rpc_id) return -NFS_RPC_ERR; else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) return -NFS_RPC_DROP; |
c3f9d4939
|
456 |
|
cbd8a35c6
|
457 458 459 |
if (rpc_pkt.u.reply.rstatus || rpc_pkt.u.reply.verifier || rpc_pkt.u.reply.astatus || |
c9f6c91b4
|
460 |
rpc_pkt.u.reply.data[0]) |
cbd8a35c6
|
461 |
return -1; |
cbd8a35c6
|
462 463 |
fs_mounted = 1; |
b0baca982
|
464 |
/* NFSv2 and NFSv3 use same structure */ |
c9f6c91b4
|
465 |
memcpy(dirfh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE); |
cbd8a35c6
|
466 467 468 |
return 0; } |
68c76a3a3
|
469 |
static int nfs_umountall_reply(uchar *pkt, unsigned len) |
cbd8a35c6
|
470 471 |
{ struct rpc_t rpc_pkt; |
0ebf04c60
|
472 473 |
debug("%s ", __func__); |
cbd8a35c6
|
474 |
|
0517cc45e
|
475 |
memcpy(&rpc_pkt.u.data[0], pkt, len); |
cbd8a35c6
|
476 |
|
fa84fa708
|
477 478 479 480 |
if (ntohl(rpc_pkt.u.reply.id) > rpc_id) return -NFS_RPC_ERR; else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) return -NFS_RPC_DROP; |
c3f9d4939
|
481 |
|
cbd8a35c6
|
482 483 |
if (rpc_pkt.u.reply.rstatus || rpc_pkt.u.reply.verifier || |
c9f6c91b4
|
484 |
rpc_pkt.u.reply.astatus) |
cbd8a35c6
|
485 |
return -1; |
cbd8a35c6
|
486 487 |
fs_mounted = 0; |
c9f6c91b4
|
488 |
memset(dirfh, 0, sizeof(dirfh)); |
cbd8a35c6
|
489 490 491 |
return 0; } |
68c76a3a3
|
492 |
static int nfs_lookup_reply(uchar *pkt, unsigned len) |
cbd8a35c6
|
493 494 |
{ struct rpc_t rpc_pkt; |
0ebf04c60
|
495 496 |
debug("%s ", __func__); |
cbd8a35c6
|
497 |
|
0517cc45e
|
498 |
memcpy(&rpc_pkt.u.data[0], pkt, len); |
cbd8a35c6
|
499 |
|
fa84fa708
|
500 501 502 503 |
if (ntohl(rpc_pkt.u.reply.id) > rpc_id) return -NFS_RPC_ERR; else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) return -NFS_RPC_DROP; |
c3f9d4939
|
504 |
|
cbd8a35c6
|
505 506 507 |
if (rpc_pkt.u.reply.rstatus || rpc_pkt.u.reply.verifier || rpc_pkt.u.reply.astatus || |
69fd0d413
|
508 509 |
rpc_pkt.u.reply.data[0]) { switch (ntohl(rpc_pkt.u.reply.astatus)) { |
b0baca982
|
510 |
case NFS_RPC_SUCCESS: /* Not an error */ |
69fd0d413
|
511 |
break; |
b0baca982
|
512 513 514 515 516 |
case NFS_RPC_PROG_MISMATCH: /* Remote can't support NFS version */ switch (ntohl(rpc_pkt.u.reply.data[0])) { /* Minimal supported NFS version */ case 3: |
faecf84ab
|
517 518 |
debug("*** Warning: NFS version not supported: Requested: V%d, accepted: min V%d - max V%d ", |
347a90159
|
519 520 |
(supported_nfs_versions & NFSV2_FLAG) ? 2 : 3, |
b0baca982
|
521 522 523 524 525 526 527 528 529 |
ntohl(rpc_pkt.u.reply.data[0]), ntohl(rpc_pkt.u.reply.data[1])); debug("Will retry with NFSv3 "); /* Clear NFSV2_FLAG from supported versions */ supported_nfs_versions &= ~NFSV2_FLAG; return -NFS_RPC_PROG_MISMATCH; case 4: default: |
d89ff2df3
|
530 531 532 533 |
puts("*** ERROR: NFS version not supported"); debug(": Requested: V%d, accepted: min V%d - max V%d ", (supported_nfs_versions & NFSV2_FLAG) ? |
347a90159
|
534 |
2 : 3, |
d89ff2df3
|
535 536 537 538 |
ntohl(rpc_pkt.u.reply.data[0]), ntohl(rpc_pkt.u.reply.data[1])); puts(" "); |
b0baca982
|
539 |
} |
69fd0d413
|
540 |
break; |
b0baca982
|
541 542 543 544 |
case NFS_RPC_PROG_UNAVAIL: case NFS_RPC_PROC_UNAVAIL: case NFS_RPC_GARBAGE_ARGS: case NFS_RPC_SYSTEM_ERR: |
69fd0d413
|
545 |
default: /* Unknown error on 'accept state' flag */ |
d89ff2df3
|
546 547 548 |
debug("*** ERROR: accept state error (%d) ", ntohl(rpc_pkt.u.reply.astatus)); |
69fd0d413
|
549 550 |
break; } |
cbd8a35c6
|
551 |
return -1; |
69fd0d413
|
552 |
} |
cbd8a35c6
|
553 |
|
b0baca982
|
554 |
if (supported_nfs_versions & NFSV2_FLAG) { |
5d14ee4e5
|
555 556 |
if (((uchar *)&(rpc_pkt.u.reply.data[0]) - (uchar *)(&rpc_pkt) + NFS_FHSIZE) > len) return -NFS_RPC_DROP; |
b0baca982
|
557 558 559 560 561 |
memcpy(filefh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE); } else { /* NFSV3_FLAG */ filefh3_length = ntohl(rpc_pkt.u.reply.data[1]); if (filefh3_length > NFS3_FHSIZE) filefh3_length = NFS3_FHSIZE; |
5d14ee4e5
|
562 563 |
if (((uchar *)&(rpc_pkt.u.reply.data[0]) - (uchar *)(&rpc_pkt) + filefh3_length) > len) return -NFS_RPC_DROP; |
5280c7691
|
564 |
memcpy(filefh, rpc_pkt.u.reply.data + 2, filefh3_length); |
b0baca982
|
565 |
} |
cbd8a35c6
|
566 567 568 |
return 0; } |
051ed9af8
|
569 570 |
static int nfs3_get_attributes_offset(uint32_t *data) { |
15eea9a1a
|
571 |
if (data[1]) { |
051ed9af8
|
572 |
/* 'attributes_follow' flag is TRUE, |
c629c45f3
|
573 |
* so we have attributes on 21 dwords */ |
051ed9af8
|
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 |
/* Skip unused values : type; 32 bits value, mode; 32 bits value, nlink; 32 bits value, uid; 32 bits value, gid; 32 bits value, size; 64 bits value, used; 64 bits value, rdev; 64 bits value, fsid; 64 bits value, fileid; 64 bits value, atime; 64 bits value, mtime; 64 bits value, ctime; 64 bits value, */ return 22; } else { /* 'attributes_follow' flag is FALSE, * so we don't have any attributes */ return 1; } } |
68c76a3a3
|
596 |
static int nfs_readlink_reply(uchar *pkt, unsigned len) |
cbd8a35c6
|
597 598 599 |
{ struct rpc_t rpc_pkt; int rlen; |
051ed9af8
|
600 |
int nfsv3_data_offset = 0; |
cbd8a35c6
|
601 |
|
0ebf04c60
|
602 603 |
debug("%s ", __func__); |
cbd8a35c6
|
604 |
|
c9f6c91b4
|
605 |
memcpy((unsigned char *)&rpc_pkt, pkt, len); |
cbd8a35c6
|
606 |
|
fa84fa708
|
607 608 609 610 |
if (ntohl(rpc_pkt.u.reply.id) > rpc_id) return -NFS_RPC_ERR; else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) return -NFS_RPC_DROP; |
c3f9d4939
|
611 |
|
cbd8a35c6
|
612 613 614 |
if (rpc_pkt.u.reply.rstatus || rpc_pkt.u.reply.verifier || rpc_pkt.u.reply.astatus || |
c9f6c91b4
|
615 |
rpc_pkt.u.reply.data[0]) |
cbd8a35c6
|
616 |
return -1; |
cbd8a35c6
|
617 |
|
051ed9af8
|
618 619 620 621 |
if (!(supported_nfs_versions & NFSV2_FLAG)) { /* NFSV3_FLAG */ nfsv3_data_offset = nfs3_get_attributes_offset(rpc_pkt.u.reply.data); } |
b0baca982
|
622 |
|
051ed9af8
|
623 624 |
/* new path length */ rlen = ntohl(rpc_pkt.u.reply.data[1 + nfsv3_data_offset]); |
cf3a4f1e8
|
625 626 |
if (((uchar *)&(rpc_pkt.u.reply.data[0]) - (uchar *)(&rpc_pkt) + rlen) > len) return -NFS_RPC_DROP; |
051ed9af8
|
627 628 629 630 631 632 633 634 635 636 637 638 639 640 |
if (*((char *)&(rpc_pkt.u.reply.data[2 + nfsv3_data_offset])) != '/') { int pathlen; strcat(nfs_path, "/"); pathlen = strlen(nfs_path); memcpy(nfs_path + pathlen, (uchar *)&(rpc_pkt.u.reply.data[2 + nfsv3_data_offset]), rlen); nfs_path[pathlen + rlen] = 0; } else { memcpy(nfs_path, (uchar *)&(rpc_pkt.u.reply.data[2 + nfsv3_data_offset]), rlen); nfs_path[rlen] = 0; |
cbd8a35c6
|
641 642 643 |
} return 0; } |
68c76a3a3
|
644 |
static int nfs_read_reply(uchar *pkt, unsigned len) |
cbd8a35c6
|
645 646 647 |
{ struct rpc_t rpc_pkt; int rlen; |
b0baca982
|
648 |
uchar *data_ptr; |
cbd8a35c6
|
649 |
|
0ebf04c60
|
650 651 |
debug("%s ", __func__); |
cbd8a35c6
|
652 |
|
0517cc45e
|
653 |
memcpy(&rpc_pkt.u.data[0], pkt, sizeof(rpc_pkt.u.reply)); |
cbd8a35c6
|
654 |
|
fa84fa708
|
655 656 657 658 |
if (ntohl(rpc_pkt.u.reply.id) > rpc_id) return -NFS_RPC_ERR; else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) return -NFS_RPC_DROP; |
c3f9d4939
|
659 |
|
cbd8a35c6
|
660 661 662 663 |
if (rpc_pkt.u.reply.rstatus || rpc_pkt.u.reply.verifier || rpc_pkt.u.reply.astatus || rpc_pkt.u.reply.data[0]) { |
c9f6c91b4
|
664 |
if (rpc_pkt.u.reply.rstatus) |
cbd8a35c6
|
665 |
return -9999; |
c9f6c91b4
|
666 |
if (rpc_pkt.u.reply.astatus) |
cbd8a35c6
|
667 |
return -9999; |
c9f6c91b4
|
668 |
return -ntohl(rpc_pkt.u.reply.data[0]); |
cbd8a35c6
|
669 |
} |
c9f6c91b4
|
670 671 672 673 674 675 |
if ((nfs_offset != 0) && !((nfs_offset) % (NFS_READ_SIZE / 2 * 10 * HASHES_PER_LINE))) puts(" \t "); if (!(nfs_offset % ((NFS_READ_SIZE / 2) * 10))) putc('#'); |
cbd8a35c6
|
676 |
|
b0baca982
|
677 678 679 680 |
if (supported_nfs_versions & NFSV2_FLAG) { rlen = ntohl(rpc_pkt.u.reply.data[18]); data_ptr = (uchar *)&(rpc_pkt.u.reply.data[19]); } else { /* NFSV3_FLAG */ |
051ed9af8
|
681 682 683 684 685 686 687 688 689 690 691 |
int nfsv3_data_offset = nfs3_get_attributes_offset(rpc_pkt.u.reply.data); /* count value */ rlen = ntohl(rpc_pkt.u.reply.data[1 + nfsv3_data_offset]); /* Skip unused values : EOF: 32 bits value, data_size: 32 bits value, */ data_ptr = (uchar *) &(rpc_pkt.u.reply.data[4 + nfsv3_data_offset]); |
b0baca982
|
692 |
} |
aa207cf3a
|
693 694 |
if (((uchar *)&(rpc_pkt.u.reply.data[0]) - (uchar *)(&rpc_pkt) + rlen) > len) return -9999; |
b0baca982
|
695 696 |
if (store_block(data_ptr, nfs_offset, rlen)) return -9999; |
cbd8a35c6
|
697 698 699 700 701 702 703 |
return rlen; } /************************************************************************** Interfaces of U-BOOT **************************************************************************/ |
68c76a3a3
|
704 |
static void nfs_timeout_handler(void) |
a5725fabc
|
705 |
{ |
68c76a3a3
|
706 |
if (++nfs_timeout_count > NFS_RETRY_COUNT) { |
c9f6c91b4
|
707 708 709 |
puts(" Retry count exceeded; starting again "); |
bc0571fc1
|
710 |
net_start_again(); |
aabb8cb08
|
711 712 |
} else { puts("T "); |
bc0571fc1
|
713 714 715 |
net_set_timeout_handler(nfs_timeout + NFS_TIMEOUT * nfs_timeout_count, nfs_timeout_handler); |
68c76a3a3
|
716 |
nfs_send(); |
fe891ecf4
|
717 |
} |
a5725fabc
|
718 |
} |
049a95a77
|
719 720 |
static void nfs_handler(uchar *pkt, unsigned dest, struct in_addr sip, unsigned src, unsigned len) |
cbd8a35c6
|
721 722 |
{ int rlen; |
fa84fa708
|
723 |
int reply; |
cbd8a35c6
|
724 |
|
0ebf04c60
|
725 726 |
debug("%s ", __func__); |
cbd8a35c6
|
727 |
|
741a8a08e
|
728 729 |
if (len > sizeof(struct rpc_t)) return; |
68c76a3a3
|
730 |
if (dest != nfs_our_port) |
c9f6c91b4
|
731 |
return; |
cbd8a35c6
|
732 |
|
68c76a3a3
|
733 |
switch (nfs_state) { |
cbd8a35c6
|
734 |
case STATE_PRCLOOKUP_PROG_MOUNT_REQ: |
fa84fa708
|
735 736 |
if (rpc_lookup_reply(PROG_MOUNT, pkt, len) == -NFS_RPC_DROP) break; |
68c76a3a3
|
737 738 |
nfs_state = STATE_PRCLOOKUP_PROG_NFS_REQ; nfs_send(); |
cbd8a35c6
|
739 740 741 |
break; case STATE_PRCLOOKUP_PROG_NFS_REQ: |
fa84fa708
|
742 743 |
if (rpc_lookup_reply(PROG_NFS, pkt, len) == -NFS_RPC_DROP) break; |
68c76a3a3
|
744 745 |
nfs_state = STATE_MOUNT_REQ; nfs_send(); |
cbd8a35c6
|
746 747 748 |
break; case STATE_MOUNT_REQ: |
fa84fa708
|
749 |
reply = nfs_mount_reply(pkt, len); |
68c76a3a3
|
750 |
if (reply == -NFS_RPC_DROP) { |
fa84fa708
|
751 |
break; |
68c76a3a3
|
752 |
} else if (reply == -NFS_RPC_ERR) { |
c9f6c91b4
|
753 754 |
puts("*** ERROR: Cannot mount "); |
cbd8a35c6
|
755 |
/* just to be sure... */ |
68c76a3a3
|
756 757 |
nfs_state = STATE_UMOUNT_REQ; nfs_send(); |
cbd8a35c6
|
758 |
} else { |
68c76a3a3
|
759 760 |
nfs_state = STATE_LOOKUP_REQ; nfs_send(); |
cbd8a35c6
|
761 762 763 764 |
} break; case STATE_UMOUNT_REQ: |
fa84fa708
|
765 |
reply = nfs_umountall_reply(pkt, len); |
68c76a3a3
|
766 |
if (reply == -NFS_RPC_DROP) { |
fa84fa708
|
767 |
break; |
68c76a3a3
|
768 |
} else if (reply == -NFS_RPC_ERR) { |
d89ff2df3
|
769 770 |
debug("*** ERROR: Cannot umount "); |
22f6e99d5
|
771 |
net_set_state(NETLOOP_FAIL); |
cbd8a35c6
|
772 |
} else { |
c9f6c91b4
|
773 774 775 |
puts(" done "); |
22f6e99d5
|
776 |
net_set_state(nfs_download_state); |
cbd8a35c6
|
777 778 779 780 |
} break; case STATE_LOOKUP_REQ: |
fa84fa708
|
781 |
reply = nfs_lookup_reply(pkt, len); |
68c76a3a3
|
782 |
if (reply == -NFS_RPC_DROP) { |
fa84fa708
|
783 |
break; |
68c76a3a3
|
784 |
} else if (reply == -NFS_RPC_ERR) { |
c9f6c91b4
|
785 786 |
puts("*** ERROR: File lookup fail "); |
68c76a3a3
|
787 788 |
nfs_state = STATE_UMOUNT_REQ; nfs_send(); |
347a90159
|
789 790 |
} else if (reply == -NFS_RPC_PROG_MISMATCH && supported_nfs_versions != 0) { |
b0baca982
|
791 792 793 794 795 796 |
/* umount */ nfs_state = STATE_UMOUNT_REQ; nfs_send(); /* And retry with another supported version */ nfs_state = STATE_PRCLOOKUP_PROG_MOUNT_REQ; nfs_send(); |
cbd8a35c6
|
797 |
} else { |
68c76a3a3
|
798 |
nfs_state = STATE_READ_REQ; |
cbd8a35c6
|
799 800 |
nfs_offset = 0; nfs_len = NFS_READ_SIZE; |
68c76a3a3
|
801 |
nfs_send(); |
cbd8a35c6
|
802 803 804 805 |
} break; case STATE_READLINK_REQ: |
fa84fa708
|
806 |
reply = nfs_readlink_reply(pkt, len); |
68c76a3a3
|
807 |
if (reply == -NFS_RPC_DROP) { |
fa84fa708
|
808 |
break; |
68c76a3a3
|
809 |
} else if (reply == -NFS_RPC_ERR) { |
c9f6c91b4
|
810 811 |
puts("*** ERROR: Symlink fail "); |
68c76a3a3
|
812 813 |
nfs_state = STATE_UMOUNT_REQ; nfs_send(); |
cbd8a35c6
|
814 |
} else { |
0ebf04c60
|
815 816 |
debug("Symlink --> %s ", nfs_path); |
c9f6c91b4
|
817 818 |
nfs_filename = basename(nfs_path); nfs_path = dirname(nfs_path); |
cbd8a35c6
|
819 |
|
68c76a3a3
|
820 821 |
nfs_state = STATE_MOUNT_REQ; nfs_send(); |
cbd8a35c6
|
822 823 824 825 |
} break; case STATE_READ_REQ: |
c9f6c91b4
|
826 |
rlen = nfs_read_reply(pkt, len); |
d48d40a09
|
827 828 |
if (rlen == -NFS_RPC_DROP) break; |
bc0571fc1
|
829 |
net_set_timeout_handler(nfs_timeout, nfs_timeout_handler); |
cbd8a35c6
|
830 831 |
if (rlen > 0) { nfs_offset += rlen; |
68c76a3a3
|
832 |
nfs_send(); |
c9f6c91b4
|
833 |
} else if ((rlen == -NFSERR_ISDIR) || (rlen == -NFSERR_INVAL)) { |
cbd8a35c6
|
834 |
/* symbolic link */ |
68c76a3a3
|
835 836 |
nfs_state = STATE_READLINK_REQ; nfs_send(); |
cbd8a35c6
|
837 |
} else { |
c9f6c91b4
|
838 |
if (!rlen) |
22f6e99d5
|
839 |
nfs_download_state = NETLOOP_SUCCESS; |
b0baca982
|
840 |
if (rlen < 0) |
d89ff2df3
|
841 842 |
debug("NFS READ error (%d) ", rlen); |
68c76a3a3
|
843 844 |
nfs_state = STATE_UMOUNT_REQ; nfs_send(); |
cbd8a35c6
|
845 846 847 848 |
} break; } } |
cbd8a35c6
|
849 |
|
68c76a3a3
|
850 |
void nfs_start(void) |
cbd8a35c6
|
851 |
{ |
0ebf04c60
|
852 853 |
debug("%s ", __func__); |
22f6e99d5
|
854 |
nfs_download_state = NETLOOP_FAIL; |
cbd8a35c6
|
855 |
|
049a95a77
|
856 |
nfs_server_ip = net_server_ip; |
cbd8a35c6
|
857 858 859 |
nfs_path = (char *)nfs_path_buff; if (nfs_path == NULL) { |
22f6e99d5
|
860 |
net_set_state(NETLOOP_FAIL); |
faecf84ab
|
861 862 |
printf("*** ERROR: Fail allocate memory "); |
cbd8a35c6
|
863 864 |
return; } |
6ab128309
|
865 866 |
if (!net_parse_bootfile(&nfs_server_ip, nfs_path, sizeof(nfs_path_buff))) { |
f8b26c7ad
|
867 |
sprintf(nfs_path, "/nfsroot/%02X%02X%02X%02X.img", |
049a95a77
|
868 869 870 871 |
net_ip.s_addr & 0xFF, (net_ip.s_addr >> 8) & 0xFF, (net_ip.s_addr >> 16) & 0xFF, (net_ip.s_addr >> 24) & 0xFF); |
cbd8a35c6
|
872 |
|
faecf84ab
|
873 874 875 |
printf("*** Warning: no boot file name; using '%s' ", nfs_path); |
cbd8a35c6
|
876 |
} |
c9f6c91b4
|
877 878 |
nfs_filename = basename(nfs_path); nfs_path = dirname(nfs_path); |
cbd8a35c6
|
879 |
|
faecf84ab
|
880 881 |
printf("Using %s device ", eth_get_name()); |
cbd8a35c6
|
882 |
|
faecf84ab
|
883 884 |
printf("File transfer via NFS from server %pI4; our IP address is %pI4", &nfs_server_ip, &net_ip); |
cbd8a35c6
|
885 886 |
/* Check if we need to send across this subnet */ |
049a95a77
|
887 888 889 |
if (net_gateway.s_addr && net_netmask.s_addr) { struct in_addr our_net; struct in_addr server_net; |
cbd8a35c6
|
890 |
|
049a95a77
|
891 |
our_net.s_addr = net_ip.s_addr & net_netmask.s_addr; |
347e32b01
|
892 |
server_net.s_addr = nfs_server_ip.s_addr & net_netmask.s_addr; |
049a95a77
|
893 |
if (our_net.s_addr != server_net.s_addr) |
faecf84ab
|
894 895 |
printf("; sending through gateway %pI4", &net_gateway); |
cbd8a35c6
|
896 |
} |
faecf84ab
|
897 898 |
printf(" Filename '%s/%s'.", nfs_path, nfs_filename); |
cbd8a35c6
|
899 |
|
1411157d8
|
900 |
if (net_boot_file_expected_size_in_blocks) { |
faecf84ab
|
901 902 |
printf(" Size is 0x%x Bytes = ", net_boot_file_expected_size_in_blocks << 9); |
1411157d8
|
903 |
print_size(net_boot_file_expected_size_in_blocks << 9, ""); |
cbd8a35c6
|
904 |
} |
bb872dd93
|
905 906 907 |
printf(" Load address: 0x%lx Loading: *\b", image_load_addr); |
cbd8a35c6
|
908 |
|
bc0571fc1
|
909 |
net_set_timeout_handler(nfs_timeout, nfs_timeout_handler); |
049a95a77
|
910 |
net_set_udp_handler(nfs_handler); |
cbd8a35c6
|
911 |
|
68c76a3a3
|
912 913 |
nfs_timeout_count = 0; nfs_state = STATE_PRCLOOKUP_PROG_MOUNT_REQ; |
cbd8a35c6
|
914 |
|
68c76a3a3
|
915 |
/*nfs_our_port = 4096 + (get_ticks() % 3072);*/ |
cbd8a35c6
|
916 |
/*FIX ME !!!*/ |
68c76a3a3
|
917 |
nfs_our_port = 1000; |
cbd8a35c6
|
918 919 |
/* zero out server ether in case the server ip has changed */ |
0adb5b761
|
920 |
memset(net_server_ethaddr, 0, 6); |
cbd8a35c6
|
921 |
|
68c76a3a3
|
922 |
nfs_send(); |
cbd8a35c6
|
923 |
} |