xref: /openbmc/u-boot/net/nfs.c (revision a57d45db90c8de2959b4484cc8f6ba81219a2269)
1cbd8a35cSwdenk /*
2cbd8a35cSwdenk  * NFS support driver - based on etherboot and U-BOOT's tftp.c
3cbd8a35cSwdenk  *
4cbd8a35cSwdenk  * Masami Komiya <mkomiya@sonare.it> 2004
5cbd8a35cSwdenk  *
6cbd8a35cSwdenk  */
7cbd8a35cSwdenk 
8cbd8a35cSwdenk /* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read:
9cbd8a35cSwdenk  * large portions are copied verbatim) as distributed in OSKit 0.97.  A few
10cbd8a35cSwdenk  * changes were necessary to adapt the code to Etherboot and to fix several
11cbd8a35cSwdenk  * inconsistencies.  Also the RPC message preparation is done "by hand" to
12cbd8a35cSwdenk  * avoid adding netsprintf() which I find hard to understand and use.  */
13cbd8a35cSwdenk 
14cbd8a35cSwdenk /* NOTE 2: Etherboot does not care about things beyond the kernel image, so
15cbd8a35cSwdenk  * it loads the kernel image off the boot server (ARP_SERVER) and does not
16cbd8a35cSwdenk  * access the client root disk (root-path in dhcpd.conf), which would use
17cbd8a35cSwdenk  * ARP_ROOTSERVER.  The root disk is something the operating system we are
18cbd8a35cSwdenk  * about to load needs to use.	This is different from the OSKit 0.97 logic.  */
19cbd8a35cSwdenk 
20cbd8a35cSwdenk /* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14
21cbd8a35cSwdenk  * If a symlink is encountered, it is followed as far as possible (recursion
22cbd8a35cSwdenk  * possible, maximum 16 steps). There is no clearing of ".."'s inside the
23cbd8a35cSwdenk  * path, so please DON'T DO THAT. thx. */
24cbd8a35cSwdenk 
25b0baca98SGuillaume GARDET /* NOTE 4: NFSv3 support added by Guillaume GARDET, 2016-June-20.
26b0baca98SGuillaume GARDET  * NFSv2 is still used by default. But if server does not support NFSv2, then
27b0baca98SGuillaume GARDET  * NFSv3 is used, if available on NFS server. */
28b0baca98SGuillaume GARDET 
29cbd8a35cSwdenk #include <common.h>
30cbd8a35cSwdenk #include <command.h>
31cbd8a35cSwdenk #include <net.h>
32cbd8a35cSwdenk #include <malloc.h>
3355d5fd9aSJoe Hershberger #include <mapmem.h>
34cbd8a35cSwdenk #include "nfs.h"
35cbd8a35cSwdenk #include "bootp.h"
36cbd8a35cSwdenk 
37cbd8a35cSwdenk #define HASHES_PER_LINE 65	/* Number of "loading" hashes per line	*/
38fe891ecfSHiroshi Ito #define NFS_RETRY_COUNT 30
3948a3e999STetsuyuki Kobayashi #ifndef CONFIG_NFS_TIMEOUT
4049f3bdbbSBartlomiej Sieka # define NFS_TIMEOUT 2000UL
4148a3e999STetsuyuki Kobayashi #else
4248a3e999STetsuyuki Kobayashi # define NFS_TIMEOUT CONFIG_NFS_TIMEOUT
4348a3e999STetsuyuki Kobayashi #endif
44cbd8a35cSwdenk 
45fa84fa70SMatthias Brugger #define NFS_RPC_ERR	1
46fa84fa70SMatthias Brugger #define NFS_RPC_DROP	124
47fa84fa70SMatthias Brugger 
48c9f6c91bSJoe Hershberger static int fs_mounted;
49c9f6c91bSJoe Hershberger static unsigned long rpc_id;
50cbd8a35cSwdenk static int nfs_offset = -1;
51cbd8a35cSwdenk static int nfs_len;
52fa84fa70SMatthias Brugger static ulong nfs_timeout = NFS_TIMEOUT;
53cbd8a35cSwdenk 
54b0baca98SGuillaume GARDET static char dirfh[NFS_FHSIZE];	/* NFSv2 / NFSv3 file handle of directory */
555280c769SJoe Hershberger static char filefh[NFS3_FHSIZE]; /* NFSv2 / NFSv3 file handle */
565280c769SJoe Hershberger static int filefh3_length;	/* (variable) length of filefh when NFSv3 */
57cbd8a35cSwdenk 
5822f6e99dSJoe Hershberger static enum net_loop_state nfs_download_state;
59049a95a7SJoe Hershberger static struct in_addr nfs_server_ip;
6068c76a3aSJoe Hershberger static int nfs_server_mount_port;
6168c76a3aSJoe Hershberger static int nfs_server_port;
6268c76a3aSJoe Hershberger static int nfs_our_port;
6368c76a3aSJoe Hershberger static int nfs_timeout_count;
6468c76a3aSJoe Hershberger static int nfs_state;
65cbd8a35cSwdenk #define STATE_PRCLOOKUP_PROG_MOUNT_REQ	1
66cbd8a35cSwdenk #define STATE_PRCLOOKUP_PROG_NFS_REQ	2
67cbd8a35cSwdenk #define STATE_MOUNT_REQ			3
68cbd8a35cSwdenk #define STATE_UMOUNT_REQ		4
69cbd8a35cSwdenk #define STATE_LOOKUP_REQ		5
70cbd8a35cSwdenk #define STATE_READ_REQ			6
71cbd8a35cSwdenk #define STATE_READLINK_REQ		7
72cbd8a35cSwdenk 
73cbd8a35cSwdenk static char *nfs_filename;
74cbd8a35cSwdenk static char *nfs_path;
75cbd8a35cSwdenk static char nfs_path_buff[2048];
76cbd8a35cSwdenk 
77b0baca98SGuillaume GARDET #define NFSV2_FLAG 1
78b0baca98SGuillaume GARDET #define NFSV3_FLAG 1 << 1
79b0baca98SGuillaume GARDET static char supported_nfs_versions = NFSV2_FLAG | NFSV3_FLAG;
80b0baca98SGuillaume GARDET 
store_block(uchar * src,unsigned offset,unsigned len)8168c76a3aSJoe Hershberger static inline int store_block(uchar *src, unsigned offset, unsigned len)
82cbd8a35cSwdenk {
83a084f7daSwdenk 	ulong newsize = offset + len;
846d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_DIRECT_FLASH_NFS
85cbd8a35cSwdenk 	int i, rc = 0;
86cbd8a35cSwdenk 
876d0f6bcfSJean-Christophe PLAGNIOL-VILLARD 	for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
88cbd8a35cSwdenk 		/* start address in flash? */
89cbd8a35cSwdenk 		if (load_addr + offset >= flash_info[i].start[0]) {
90cbd8a35cSwdenk 			rc = 1;
91cbd8a35cSwdenk 			break;
92cbd8a35cSwdenk 		}
93cbd8a35cSwdenk 	}
94cbd8a35cSwdenk 
95cbd8a35cSwdenk 	if (rc) { /* Flash is destination for this packet */
96cbd8a35cSwdenk 		rc = flash_write((uchar *)src, (ulong)(load_addr+offset), len);
97cbd8a35cSwdenk 		if (rc) {
98cbd8a35cSwdenk 			flash_perror(rc);
9923a7a32dSWolfgang Denk 			return -1;
100cbd8a35cSwdenk 		}
101cbd8a35cSwdenk 	} else
1026d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #endif /* CONFIG_SYS_DIRECT_FLASH_NFS */
103cbd8a35cSwdenk 	{
10455d5fd9aSJoe Hershberger 		void *ptr = map_sysmem(load_addr + offset, len);
10555d5fd9aSJoe Hershberger 
10655d5fd9aSJoe Hershberger 		memcpy(ptr, src, len);
10755d5fd9aSJoe Hershberger 		unmap_sysmem(ptr);
108cbd8a35cSwdenk 	}
109a084f7daSwdenk 
1101411157dSJoe Hershberger 	if (net_boot_file_size < (offset + len))
1111411157dSJoe Hershberger 		net_boot_file_size = newsize;
11223a7a32dSWolfgang Denk 	return 0;
113cbd8a35cSwdenk }
114cbd8a35cSwdenk 
basename(char * path)11568c76a3aSJoe Hershberger static char *basename(char *path)
116cbd8a35cSwdenk {
117cbd8a35cSwdenk 	char *fname;
118cbd8a35cSwdenk 
119cbd8a35cSwdenk 	fname = path + strlen(path) - 1;
120cbd8a35cSwdenk 	while (fname >= path) {
121cbd8a35cSwdenk 		if (*fname == '/') {
122cbd8a35cSwdenk 			fname++;
123cbd8a35cSwdenk 			break;
124cbd8a35cSwdenk 		}
125cbd8a35cSwdenk 		fname--;
126cbd8a35cSwdenk 	}
127cbd8a35cSwdenk 	return fname;
128cbd8a35cSwdenk }
129cbd8a35cSwdenk 
dirname(char * path)13068c76a3aSJoe Hershberger static char *dirname(char *path)
131cbd8a35cSwdenk {
132cbd8a35cSwdenk 	char *fname;
133cbd8a35cSwdenk 
134cbd8a35cSwdenk 	fname = basename(path);
135cbd8a35cSwdenk 	--fname;
136cbd8a35cSwdenk 	*fname = '\0';
137cbd8a35cSwdenk 	return path;
138cbd8a35cSwdenk }
139cbd8a35cSwdenk 
140cbd8a35cSwdenk /**************************************************************************
141cbd8a35cSwdenk RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
142cbd8a35cSwdenk **************************************************************************/
rpc_add_credentials(uint32_t * p)143e4ead4a2SRalf Hubert static uint32_t *rpc_add_credentials(uint32_t *p)
144cbd8a35cSwdenk {
145cbd8a35cSwdenk 	/* Here's the executive summary on authentication requirements of the
146cbd8a35cSwdenk 	 * various NFS server implementations:	Linux accepts both AUTH_NONE
147cbd8a35cSwdenk 	 * and AUTH_UNIX authentication (also accepts an empty hostname field
148cbd8a35cSwdenk 	 * in the AUTH_UNIX scheme).  *BSD refuses AUTH_NONE, but accepts
149cbd8a35cSwdenk 	 * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
150cbd8a35cSwdenk 	 * scheme).  To be safe, use AUTH_UNIX and pass the hostname if we have
151cbd8a35cSwdenk 	 * it (if the BOOTP/DHCP reply didn't give one, just use an empty
152cbd8a35cSwdenk 	 * hostname).  */
153cbd8a35cSwdenk 
154cbd8a35cSwdenk 	/* Provide an AUTH_UNIX credential.  */
155cbd8a35cSwdenk 	*p++ = htonl(1);		/* AUTH_UNIX */
1561ff65d44SJoe Hershberger 	*p++ = htonl(20);		/* auth length */
1571ff65d44SJoe Hershberger 	*p++ = 0;			/* stamp */
1581ff65d44SJoe Hershberger 	*p++ = 0;			/* hostname string */
159cbd8a35cSwdenk 	*p++ = 0;			/* uid */
160cbd8a35cSwdenk 	*p++ = 0;			/* gid */
161cbd8a35cSwdenk 	*p++ = 0;			/* auxiliary gid list */
162cbd8a35cSwdenk 
163cbd8a35cSwdenk 	/* Provide an AUTH_NONE verifier.  */
164cbd8a35cSwdenk 	*p++ = 0;			/* AUTH_NONE */
165cbd8a35cSwdenk 	*p++ = 0;			/* auth length */
166cbd8a35cSwdenk 
167cbd8a35cSwdenk 	return p;
168cbd8a35cSwdenk }
169cbd8a35cSwdenk 
170cbd8a35cSwdenk /**************************************************************************
171cbd8a35cSwdenk RPC_LOOKUP - Lookup RPC Port numbers
172cbd8a35cSwdenk **************************************************************************/
rpc_req(int rpc_prog,int rpc_proc,uint32_t * data,int datalen)173a73588feSJoe Hershberger static void rpc_req(int rpc_prog, int rpc_proc, uint32_t *data, int datalen)
174cbd8a35cSwdenk {
175a73588feSJoe Hershberger 	struct rpc_t rpc_pkt;
176cbd8a35cSwdenk 	unsigned long id;
177a73588feSJoe Hershberger 	uint32_t *p;
178cbd8a35cSwdenk 	int pktlen;
179cbd8a35cSwdenk 	int sport;
180cbd8a35cSwdenk 
181c3f9d493Swdenk 	id = ++rpc_id;
182a73588feSJoe Hershberger 	rpc_pkt.u.call.id = htonl(id);
183a73588feSJoe Hershberger 	rpc_pkt.u.call.type = htonl(MSG_CALL);
184a73588feSJoe Hershberger 	rpc_pkt.u.call.rpcvers = htonl(2);	/* use RPC version 2 */
185a73588feSJoe Hershberger 	rpc_pkt.u.call.prog = htonl(rpc_prog);
186b0baca98SGuillaume GARDET 	switch (rpc_prog) {
187b0baca98SGuillaume GARDET 	case PROG_NFS:
188b0baca98SGuillaume GARDET 		if (supported_nfs_versions & NFSV2_FLAG)
189a73588feSJoe Hershberger 			rpc_pkt.u.call.vers = htonl(2);	/* NFS v2 */
190b0baca98SGuillaume GARDET 		else /* NFSV3_FLAG */
191a73588feSJoe Hershberger 			rpc_pkt.u.call.vers = htonl(3);	/* NFS v3 */
192b0baca98SGuillaume GARDET 		break;
193b0baca98SGuillaume GARDET 	case PROG_PORTMAP:
194b0baca98SGuillaume GARDET 	case PROG_MOUNT:
195b0baca98SGuillaume GARDET 	default:
196a73588feSJoe Hershberger 		rpc_pkt.u.call.vers = htonl(2);	/* portmapper is version 2 */
197b0baca98SGuillaume GARDET 	}
198a73588feSJoe Hershberger 	rpc_pkt.u.call.proc = htonl(rpc_proc);
199a73588feSJoe Hershberger 	p = (uint32_t *)&(rpc_pkt.u.call.data);
200cbd8a35cSwdenk 
201a73588feSJoe Hershberger 	if (datalen)
202a73588feSJoe Hershberger 		memcpy((char *)p, (char *)data, datalen*sizeof(uint32_t));
203a73588feSJoe Hershberger 
204a73588feSJoe Hershberger 	pktlen = (char *)p + datalen * sizeof(uint32_t) - (char *)&rpc_pkt;
205a73588feSJoe Hershberger 
206a73588feSJoe Hershberger 	memcpy((char *)net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE,
207a73588feSJoe Hershberger 	       &rpc_pkt.u.data[0], pktlen);
208cbd8a35cSwdenk 
209cbd8a35cSwdenk 	if (rpc_prog == PROG_PORTMAP)
210cbd8a35cSwdenk 		sport = SUNRPC_PORT;
211cbd8a35cSwdenk 	else if (rpc_prog == PROG_MOUNT)
21268c76a3aSJoe Hershberger 		sport = nfs_server_mount_port;
213cbd8a35cSwdenk 	else
21468c76a3aSJoe Hershberger 		sport = nfs_server_port;
215cbd8a35cSwdenk 
2161203fcceSJoe Hershberger 	net_send_udp_packet(net_server_ethaddr, nfs_server_ip, sport,
21768c76a3aSJoe Hershberger 			    nfs_our_port, pktlen);
218cbd8a35cSwdenk }
219cbd8a35cSwdenk 
220cbd8a35cSwdenk /**************************************************************************
221cbd8a35cSwdenk RPC_LOOKUP - Lookup RPC Port numbers
222cbd8a35cSwdenk **************************************************************************/
rpc_lookup_req(int prog,int ver)22368c76a3aSJoe Hershberger static void rpc_lookup_req(int prog, int ver)
224cbd8a35cSwdenk {
225a73588feSJoe Hershberger 	uint32_t data[16];
226cbd8a35cSwdenk 
227cbd8a35cSwdenk 	data[0] = 0; data[1] = 0;	/* auth credential */
228cbd8a35cSwdenk 	data[2] = 0; data[3] = 0;	/* auth verifier */
229cbd8a35cSwdenk 	data[4] = htonl(prog);
230cbd8a35cSwdenk 	data[5] = htonl(ver);
231cbd8a35cSwdenk 	data[6] = htonl(17);	/* IP_UDP */
232cbd8a35cSwdenk 	data[7] = 0;
233a73588feSJoe Hershberger 	rpc_req(PROG_PORTMAP, PORTMAP_GETPORT, data, 8);
234cbd8a35cSwdenk }
235cbd8a35cSwdenk 
236cbd8a35cSwdenk /**************************************************************************
237cbd8a35cSwdenk NFS_MOUNT - Mount an NFS Filesystem
238cbd8a35cSwdenk **************************************************************************/
nfs_mount_req(char * path)23968c76a3aSJoe Hershberger static void nfs_mount_req(char *path)
240cbd8a35cSwdenk {
241a73588feSJoe Hershberger 	uint32_t data[1024];
242cbd8a35cSwdenk 	uint32_t *p;
243cbd8a35cSwdenk 	int len;
244cbd8a35cSwdenk 	int pathlen;
245cbd8a35cSwdenk 
246cbd8a35cSwdenk 	pathlen = strlen(path);
247cbd8a35cSwdenk 
248a73588feSJoe Hershberger 	p = &(data[0]);
249e4ead4a2SRalf Hubert 	p = rpc_add_credentials(p);
250cbd8a35cSwdenk 
251cbd8a35cSwdenk 	*p++ = htonl(pathlen);
252c9f6c91bSJoe Hershberger 	if (pathlen & 3)
253c9f6c91bSJoe Hershberger 		*(p + pathlen / 4) = 0;
254cbd8a35cSwdenk 	memcpy(p, path, pathlen);
255cbd8a35cSwdenk 	p += (pathlen + 3) / 4;
256cbd8a35cSwdenk 
257a73588feSJoe Hershberger 	len = (uint32_t *)p - (uint32_t *)&(data[0]);
258cbd8a35cSwdenk 
259a73588feSJoe Hershberger 	rpc_req(PROG_MOUNT, MOUNT_ADDENTRY, data, len);
260cbd8a35cSwdenk }
261cbd8a35cSwdenk 
262cbd8a35cSwdenk /**************************************************************************
263cbd8a35cSwdenk NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
264cbd8a35cSwdenk **************************************************************************/
nfs_umountall_req(void)26568c76a3aSJoe Hershberger static void nfs_umountall_req(void)
266cbd8a35cSwdenk {
267a73588feSJoe Hershberger 	uint32_t data[1024];
268cbd8a35cSwdenk 	uint32_t *p;
269cbd8a35cSwdenk 	int len;
270cbd8a35cSwdenk 
27168c76a3aSJoe Hershberger 	if ((nfs_server_mount_port == -1) || (!fs_mounted))
272cbd8a35cSwdenk 		/* Nothing mounted, nothing to umount */
273cbd8a35cSwdenk 		return;
274cbd8a35cSwdenk 
275a73588feSJoe Hershberger 	p = &(data[0]);
276e4ead4a2SRalf Hubert 	p = rpc_add_credentials(p);
277cbd8a35cSwdenk 
278a73588feSJoe Hershberger 	len = (uint32_t *)p - (uint32_t *)&(data[0]);
279cbd8a35cSwdenk 
280a73588feSJoe Hershberger 	rpc_req(PROG_MOUNT, MOUNT_UMOUNTALL, data, len);
281cbd8a35cSwdenk }
282cbd8a35cSwdenk 
283cbd8a35cSwdenk /***************************************************************************
284cbd8a35cSwdenk  * NFS_READLINK (AH 2003-07-14)
285cbd8a35cSwdenk  * This procedure is called when read of the first block fails -
286cbd8a35cSwdenk  * this probably happens when it's a directory or a symlink
287cbd8a35cSwdenk  * In case of successful readlink(), the dirname is manipulated,
288cbd8a35cSwdenk  * so that inside the nfs() function a recursion can be done.
289cbd8a35cSwdenk  **************************************************************************/
nfs_readlink_req(void)29068c76a3aSJoe Hershberger static void nfs_readlink_req(void)
291cbd8a35cSwdenk {
292a73588feSJoe Hershberger 	uint32_t data[1024];
293cbd8a35cSwdenk 	uint32_t *p;
294cbd8a35cSwdenk 	int len;
295cbd8a35cSwdenk 
296a73588feSJoe Hershberger 	p = &(data[0]);
297e4ead4a2SRalf Hubert 	p = rpc_add_credentials(p);
298cbd8a35cSwdenk 
299b0baca98SGuillaume GARDET 	if (supported_nfs_versions & NFSV2_FLAG) {
300cbd8a35cSwdenk 		memcpy(p, filefh, NFS_FHSIZE);
301cbd8a35cSwdenk 		p += (NFS_FHSIZE / 4);
302b0baca98SGuillaume GARDET 	} else { /* NFSV3_FLAG */
303b0baca98SGuillaume GARDET 		*p++ = htonl(filefh3_length);
3045280c769SJoe Hershberger 		memcpy(p, filefh, filefh3_length);
305b0baca98SGuillaume GARDET 		p += (filefh3_length / 4);
306b0baca98SGuillaume GARDET 	}
307cbd8a35cSwdenk 
308a73588feSJoe Hershberger 	len = (uint32_t *)p - (uint32_t *)&(data[0]);
309cbd8a35cSwdenk 
310a73588feSJoe Hershberger 	rpc_req(PROG_NFS, NFS_READLINK, data, len);
311cbd8a35cSwdenk }
312cbd8a35cSwdenk 
313cbd8a35cSwdenk /**************************************************************************
314cbd8a35cSwdenk NFS_LOOKUP - Lookup Pathname
315cbd8a35cSwdenk **************************************************************************/
nfs_lookup_req(char * fname)31668c76a3aSJoe Hershberger static void nfs_lookup_req(char *fname)
317cbd8a35cSwdenk {
318a73588feSJoe Hershberger 	uint32_t data[1024];
319cbd8a35cSwdenk 	uint32_t *p;
320cbd8a35cSwdenk 	int len;
321cbd8a35cSwdenk 	int fnamelen;
322cbd8a35cSwdenk 
323cbd8a35cSwdenk 	fnamelen = strlen(fname);
324cbd8a35cSwdenk 
325a73588feSJoe Hershberger 	p = &(data[0]);
326e4ead4a2SRalf Hubert 	p = rpc_add_credentials(p);
327cbd8a35cSwdenk 
328b0baca98SGuillaume GARDET 	if (supported_nfs_versions & NFSV2_FLAG) {
329cbd8a35cSwdenk 		memcpy(p, dirfh, NFS_FHSIZE);
330cbd8a35cSwdenk 		p += (NFS_FHSIZE / 4);
331cbd8a35cSwdenk 		*p++ = htonl(fnamelen);
332c9f6c91bSJoe Hershberger 		if (fnamelen & 3)
333c9f6c91bSJoe Hershberger 			*(p + fnamelen / 4) = 0;
334cbd8a35cSwdenk 		memcpy(p, fname, fnamelen);
335cbd8a35cSwdenk 		p += (fnamelen + 3) / 4;
336cbd8a35cSwdenk 
337a73588feSJoe Hershberger 		len = (uint32_t *)p - (uint32_t *)&(data[0]);
338cbd8a35cSwdenk 
339a73588feSJoe Hershberger 		rpc_req(PROG_NFS, NFS_LOOKUP, data, len);
340b0baca98SGuillaume GARDET 	} else {  /* NFSV3_FLAG */
341b0baca98SGuillaume GARDET 		*p++ = htonl(NFS_FHSIZE);	/* Dir handle length */
342b0baca98SGuillaume GARDET 		memcpy(p, dirfh, NFS_FHSIZE);
343b0baca98SGuillaume GARDET 		p += (NFS_FHSIZE / 4);
344b0baca98SGuillaume GARDET 		*p++ = htonl(fnamelen);
345b0baca98SGuillaume GARDET 		if (fnamelen & 3)
346b0baca98SGuillaume GARDET 			*(p + fnamelen / 4) = 0;
347b0baca98SGuillaume GARDET 		memcpy(p, fname, fnamelen);
348b0baca98SGuillaume GARDET 		p += (fnamelen + 3) / 4;
349b0baca98SGuillaume GARDET 
350a73588feSJoe Hershberger 		len = (uint32_t *)p - (uint32_t *)&(data[0]);
351b0baca98SGuillaume GARDET 
352a73588feSJoe Hershberger 		rpc_req(PROG_NFS, NFS3PROC_LOOKUP, data, len);
353b0baca98SGuillaume GARDET 	}
354cbd8a35cSwdenk }
355cbd8a35cSwdenk 
356cbd8a35cSwdenk /**************************************************************************
357cbd8a35cSwdenk NFS_READ - Read File on NFS Server
358cbd8a35cSwdenk **************************************************************************/
nfs_read_req(int offset,int readlen)35968c76a3aSJoe Hershberger static void nfs_read_req(int offset, int readlen)
360cbd8a35cSwdenk {
361a73588feSJoe Hershberger 	uint32_t data[1024];
362cbd8a35cSwdenk 	uint32_t *p;
363cbd8a35cSwdenk 	int len;
364cbd8a35cSwdenk 
365a73588feSJoe Hershberger 	p = &(data[0]);
366e4ead4a2SRalf Hubert 	p = rpc_add_credentials(p);
367cbd8a35cSwdenk 
368b0baca98SGuillaume GARDET 	if (supported_nfs_versions & NFSV2_FLAG) {
369cbd8a35cSwdenk 		memcpy(p, filefh, NFS_FHSIZE);
370cbd8a35cSwdenk 		p += (NFS_FHSIZE / 4);
371cbd8a35cSwdenk 		*p++ = htonl(offset);
372cbd8a35cSwdenk 		*p++ = htonl(readlen);
373cbd8a35cSwdenk 		*p++ = 0;
374b0baca98SGuillaume GARDET 	} else { /* NFSV3_FLAG */
375b0baca98SGuillaume GARDET 		*p++ = htonl(filefh3_length);
3765280c769SJoe Hershberger 		memcpy(p, filefh, filefh3_length);
377b0baca98SGuillaume GARDET 		p += (filefh3_length / 4);
378b0baca98SGuillaume GARDET 		*p++ = htonl(0); /* offset is 64-bit long, so fill with 0 */
379b0baca98SGuillaume GARDET 		*p++ = htonl(offset);
380b0baca98SGuillaume GARDET 		*p++ = htonl(readlen);
381b0baca98SGuillaume GARDET 		*p++ = 0;
382b0baca98SGuillaume GARDET 	}
383cbd8a35cSwdenk 
384a73588feSJoe Hershberger 	len = (uint32_t *)p - (uint32_t *)&(data[0]);
385cbd8a35cSwdenk 
386a73588feSJoe Hershberger 	rpc_req(PROG_NFS, NFS_READ, data, len);
387cbd8a35cSwdenk }
388cbd8a35cSwdenk 
389cbd8a35cSwdenk /**************************************************************************
390cbd8a35cSwdenk RPC request dispatcher
391cbd8a35cSwdenk **************************************************************************/
nfs_send(void)39268c76a3aSJoe Hershberger static void nfs_send(void)
393cbd8a35cSwdenk {
3940ebf04c6SRobin Getz 	debug("%s\n", __func__);
395cbd8a35cSwdenk 
39668c76a3aSJoe Hershberger 	switch (nfs_state) {
397cbd8a35cSwdenk 	case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
398b0baca98SGuillaume GARDET 		if (supported_nfs_versions & NFSV2_FLAG)
399cbd8a35cSwdenk 			rpc_lookup_req(PROG_MOUNT, 1);
400b0baca98SGuillaume GARDET 		else  /* NFSV3_FLAG */
401b0baca98SGuillaume GARDET 			rpc_lookup_req(PROG_MOUNT, 3);
402cbd8a35cSwdenk 		break;
403cbd8a35cSwdenk 	case STATE_PRCLOOKUP_PROG_NFS_REQ:
404b0baca98SGuillaume GARDET 		if (supported_nfs_versions & NFSV2_FLAG)
405cbd8a35cSwdenk 			rpc_lookup_req(PROG_NFS, 2);
406b0baca98SGuillaume GARDET 		else  /* NFSV3_FLAG */
407b0baca98SGuillaume GARDET 			rpc_lookup_req(PROG_NFS, 3);
408cbd8a35cSwdenk 		break;
409cbd8a35cSwdenk 	case STATE_MOUNT_REQ:
410cbd8a35cSwdenk 		nfs_mount_req(nfs_path);
411cbd8a35cSwdenk 		break;
412cbd8a35cSwdenk 	case STATE_UMOUNT_REQ:
413cbd8a35cSwdenk 		nfs_umountall_req();
414cbd8a35cSwdenk 		break;
415cbd8a35cSwdenk 	case STATE_LOOKUP_REQ:
416cbd8a35cSwdenk 		nfs_lookup_req(nfs_filename);
417cbd8a35cSwdenk 		break;
418cbd8a35cSwdenk 	case STATE_READ_REQ:
419cbd8a35cSwdenk 		nfs_read_req(nfs_offset, nfs_len);
420cbd8a35cSwdenk 		break;
421cbd8a35cSwdenk 	case STATE_READLINK_REQ:
422cbd8a35cSwdenk 		nfs_readlink_req();
423cbd8a35cSwdenk 		break;
424cbd8a35cSwdenk 	}
425cbd8a35cSwdenk }
426cbd8a35cSwdenk 
427cbd8a35cSwdenk /**************************************************************************
428cbd8a35cSwdenk Handlers for the reply from server
429cbd8a35cSwdenk **************************************************************************/
430cbd8a35cSwdenk 
rpc_lookup_reply(int prog,uchar * pkt,unsigned len)43168c76a3aSJoe Hershberger static int rpc_lookup_reply(int prog, uchar *pkt, unsigned len)
432cbd8a35cSwdenk {
433cbd8a35cSwdenk 	struct rpc_t rpc_pkt;
434cbd8a35cSwdenk 
4350517cc45SJoe Hershberger 	memcpy(&rpc_pkt.u.data[0], pkt, len);
436cbd8a35cSwdenk 
4370ebf04c6SRobin Getz 	debug("%s\n", __func__);
438cbd8a35cSwdenk 
439fa84fa70SMatthias Brugger 	if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
440fa84fa70SMatthias Brugger 		return -NFS_RPC_ERR;
441fa84fa70SMatthias Brugger 	else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
442fa84fa70SMatthias Brugger 		return -NFS_RPC_DROP;
443c3f9d493Swdenk 
444cbd8a35cSwdenk 	if (rpc_pkt.u.reply.rstatus  ||
445cbd8a35cSwdenk 	    rpc_pkt.u.reply.verifier ||
446c9f6c91bSJoe Hershberger 	    rpc_pkt.u.reply.astatus)
447c3f9d493Swdenk 		return -1;
448cbd8a35cSwdenk 
449cbd8a35cSwdenk 	switch (prog) {
450cbd8a35cSwdenk 	case PROG_MOUNT:
45168c76a3aSJoe Hershberger 		nfs_server_mount_port = ntohl(rpc_pkt.u.reply.data[0]);
452cbd8a35cSwdenk 		break;
453cbd8a35cSwdenk 	case PROG_NFS:
45468c76a3aSJoe Hershberger 		nfs_server_port = ntohl(rpc_pkt.u.reply.data[0]);
455cbd8a35cSwdenk 		break;
456cbd8a35cSwdenk 	}
457cbd8a35cSwdenk 
458cbd8a35cSwdenk 	return 0;
459cbd8a35cSwdenk }
460cbd8a35cSwdenk 
nfs_mount_reply(uchar * pkt,unsigned len)46168c76a3aSJoe Hershberger static int nfs_mount_reply(uchar *pkt, unsigned len)
462cbd8a35cSwdenk {
463cbd8a35cSwdenk 	struct rpc_t rpc_pkt;
464cbd8a35cSwdenk 
4650ebf04c6SRobin Getz 	debug("%s\n", __func__);
466cbd8a35cSwdenk 
4670517cc45SJoe Hershberger 	memcpy(&rpc_pkt.u.data[0], pkt, len);
468cbd8a35cSwdenk 
469fa84fa70SMatthias Brugger 	if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
470fa84fa70SMatthias Brugger 		return -NFS_RPC_ERR;
471fa84fa70SMatthias Brugger 	else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
472fa84fa70SMatthias Brugger 		return -NFS_RPC_DROP;
473c3f9d493Swdenk 
474cbd8a35cSwdenk 	if (rpc_pkt.u.reply.rstatus  ||
475cbd8a35cSwdenk 	    rpc_pkt.u.reply.verifier ||
476cbd8a35cSwdenk 	    rpc_pkt.u.reply.astatus  ||
477c9f6c91bSJoe Hershberger 	    rpc_pkt.u.reply.data[0])
478cbd8a35cSwdenk 		return -1;
479cbd8a35cSwdenk 
480cbd8a35cSwdenk 	fs_mounted = 1;
481b0baca98SGuillaume GARDET 	/*  NFSv2 and NFSv3 use same structure */
482cbd8a35cSwdenk 	memcpy(dirfh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
483cbd8a35cSwdenk 
484cbd8a35cSwdenk 	return 0;
485cbd8a35cSwdenk }
486cbd8a35cSwdenk 
nfs_umountall_reply(uchar * pkt,unsigned len)48768c76a3aSJoe Hershberger static int nfs_umountall_reply(uchar *pkt, unsigned len)
488cbd8a35cSwdenk {
489cbd8a35cSwdenk 	struct rpc_t rpc_pkt;
490cbd8a35cSwdenk 
4910ebf04c6SRobin Getz 	debug("%s\n", __func__);
492cbd8a35cSwdenk 
4930517cc45SJoe Hershberger 	memcpy(&rpc_pkt.u.data[0], pkt, len);
494cbd8a35cSwdenk 
495fa84fa70SMatthias Brugger 	if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
496fa84fa70SMatthias Brugger 		return -NFS_RPC_ERR;
497fa84fa70SMatthias Brugger 	else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
498fa84fa70SMatthias Brugger 		return -NFS_RPC_DROP;
499c3f9d493Swdenk 
500cbd8a35cSwdenk 	if (rpc_pkt.u.reply.rstatus  ||
501cbd8a35cSwdenk 	    rpc_pkt.u.reply.verifier ||
502c9f6c91bSJoe Hershberger 	    rpc_pkt.u.reply.astatus)
503cbd8a35cSwdenk 		return -1;
504cbd8a35cSwdenk 
505cbd8a35cSwdenk 	fs_mounted = 0;
506cbd8a35cSwdenk 	memset(dirfh, 0, sizeof(dirfh));
507cbd8a35cSwdenk 
508cbd8a35cSwdenk 	return 0;
509cbd8a35cSwdenk }
510cbd8a35cSwdenk 
nfs_lookup_reply(uchar * pkt,unsigned len)51168c76a3aSJoe Hershberger static int nfs_lookup_reply(uchar *pkt, unsigned len)
512cbd8a35cSwdenk {
513cbd8a35cSwdenk 	struct rpc_t rpc_pkt;
514cbd8a35cSwdenk 
5150ebf04c6SRobin Getz 	debug("%s\n", __func__);
516cbd8a35cSwdenk 
5170517cc45SJoe Hershberger 	memcpy(&rpc_pkt.u.data[0], pkt, len);
518cbd8a35cSwdenk 
519fa84fa70SMatthias Brugger 	if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
520fa84fa70SMatthias Brugger 		return -NFS_RPC_ERR;
521fa84fa70SMatthias Brugger 	else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
522fa84fa70SMatthias Brugger 		return -NFS_RPC_DROP;
523c3f9d493Swdenk 
524cbd8a35cSwdenk 	if (rpc_pkt.u.reply.rstatus  ||
525cbd8a35cSwdenk 	    rpc_pkt.u.reply.verifier ||
526cbd8a35cSwdenk 	    rpc_pkt.u.reply.astatus  ||
52769fd0d41SGuillaume GARDET 	    rpc_pkt.u.reply.data[0]) {
52869fd0d41SGuillaume GARDET 		switch (ntohl(rpc_pkt.u.reply.astatus)) {
529b0baca98SGuillaume GARDET 		case NFS_RPC_SUCCESS: /* Not an error */
53069fd0d41SGuillaume GARDET 			break;
531b0baca98SGuillaume GARDET 		case NFS_RPC_PROG_MISMATCH:
532b0baca98SGuillaume GARDET 			/* Remote can't support NFS version */
533b0baca98SGuillaume GARDET 			switch (ntohl(rpc_pkt.u.reply.data[0])) {
534b0baca98SGuillaume GARDET 			/* Minimal supported NFS version */
535b0baca98SGuillaume GARDET 			case 3:
536faecf84aSJoe Hershberger 				debug("*** Warning: NFS version not supported: Requested: V%d, accepted: min V%d - max V%d\n",
537347a9015SJoe Hershberger 				      (supported_nfs_versions & NFSV2_FLAG) ?
538347a9015SJoe Hershberger 						2 : 3,
53969fd0d41SGuillaume GARDET 				      ntohl(rpc_pkt.u.reply.data[0]),
54069fd0d41SGuillaume GARDET 				      ntohl(rpc_pkt.u.reply.data[1]));
541b0baca98SGuillaume GARDET 				debug("Will retry with NFSv3\n");
542b0baca98SGuillaume GARDET 				/* Clear NFSV2_FLAG from supported versions */
543b0baca98SGuillaume GARDET 				supported_nfs_versions &= ~NFSV2_FLAG;
544b0baca98SGuillaume GARDET 				return -NFS_RPC_PROG_MISMATCH;
545b0baca98SGuillaume GARDET 			case 4:
546b0baca98SGuillaume GARDET 			default:
547d89ff2dfSJoe Hershberger 				puts("*** ERROR: NFS version not supported");
548d89ff2dfSJoe Hershberger 				debug(": Requested: V%d, accepted: min V%d - max V%d\n",
549347a9015SJoe Hershberger 				      (supported_nfs_versions & NFSV2_FLAG) ?
550347a9015SJoe Hershberger 						2 : 3,
551b0baca98SGuillaume GARDET 				      ntohl(rpc_pkt.u.reply.data[0]),
552b0baca98SGuillaume GARDET 				      ntohl(rpc_pkt.u.reply.data[1]));
553d89ff2dfSJoe Hershberger 				puts("\n");
554b0baca98SGuillaume GARDET 			}
55569fd0d41SGuillaume GARDET 			break;
556b0baca98SGuillaume GARDET 		case NFS_RPC_PROG_UNAVAIL:
557b0baca98SGuillaume GARDET 		case NFS_RPC_PROC_UNAVAIL:
558b0baca98SGuillaume GARDET 		case NFS_RPC_GARBAGE_ARGS:
559b0baca98SGuillaume GARDET 		case NFS_RPC_SYSTEM_ERR:
56069fd0d41SGuillaume GARDET 		default: /* Unknown error on 'accept state' flag */
561d89ff2dfSJoe Hershberger 			debug("*** ERROR: accept state error (%d)\n",
56269fd0d41SGuillaume GARDET 			      ntohl(rpc_pkt.u.reply.astatus));
56369fd0d41SGuillaume GARDET 			break;
56469fd0d41SGuillaume GARDET 		}
565cbd8a35cSwdenk 		return -1;
56669fd0d41SGuillaume GARDET 	}
567cbd8a35cSwdenk 
568b0baca98SGuillaume GARDET 	if (supported_nfs_versions & NFSV2_FLAG) {
569cbd8a35cSwdenk 		memcpy(filefh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
570b0baca98SGuillaume GARDET 	} else {  /* NFSV3_FLAG */
571b0baca98SGuillaume GARDET 		filefh3_length = ntohl(rpc_pkt.u.reply.data[1]);
572b0baca98SGuillaume GARDET 		if (filefh3_length > NFS3_FHSIZE)
573b0baca98SGuillaume GARDET 			filefh3_length  = NFS3_FHSIZE;
5745280c769SJoe Hershberger 		memcpy(filefh, rpc_pkt.u.reply.data + 2, filefh3_length);
575b0baca98SGuillaume GARDET 	}
576cbd8a35cSwdenk 
577cbd8a35cSwdenk 	return 0;
578cbd8a35cSwdenk }
579cbd8a35cSwdenk 
nfs3_get_attributes_offset(uint32_t * data)580051ed9afSJoe Hershberger static int nfs3_get_attributes_offset(uint32_t *data)
581cbd8a35cSwdenk {
582051ed9afSJoe Hershberger 	if (ntohl(data[1]) != 0) {
583b0baca98SGuillaume GARDET 		/* 'attributes_follow' flag is TRUE,
584c629c45fSJoe Hershberger 		 * so we have attributes on 21 dwords */
585b0baca98SGuillaume GARDET 		/* Skip unused values :
586b0baca98SGuillaume GARDET 			type;	32 bits value,
587b0baca98SGuillaume GARDET 			mode;	32 bits value,
588b0baca98SGuillaume GARDET 			nlink;	32 bits value,
589b0baca98SGuillaume GARDET 			uid;	32 bits value,
590b0baca98SGuillaume GARDET 			gid;	32 bits value,
591b0baca98SGuillaume GARDET 			size;	64 bits value,
592b0baca98SGuillaume GARDET 			used;	64 bits value,
593b0baca98SGuillaume GARDET 			rdev;	64 bits value,
594b0baca98SGuillaume GARDET 			fsid;	64 bits value,
595b0baca98SGuillaume GARDET 			fileid;	64 bits value,
596b0baca98SGuillaume GARDET 			atime;	64 bits value,
597b0baca98SGuillaume GARDET 			mtime;	64 bits value,
598b0baca98SGuillaume GARDET 			ctime;	64 bits value,
599b0baca98SGuillaume GARDET 		*/
600051ed9afSJoe Hershberger 		return 22;
601b0baca98SGuillaume GARDET 	} else {
602b0baca98SGuillaume GARDET 		/* 'attributes_follow' flag is FALSE,
603b0baca98SGuillaume GARDET 		 * so we don't have any attributes */
604051ed9afSJoe Hershberger 		return 1;
605051ed9afSJoe Hershberger 	}
606051ed9afSJoe Hershberger }
607051ed9afSJoe Hershberger 
nfs_readlink_reply(uchar * pkt,unsigned len)608051ed9afSJoe Hershberger static int nfs_readlink_reply(uchar *pkt, unsigned len)
609051ed9afSJoe Hershberger {
610051ed9afSJoe Hershberger 	struct rpc_t rpc_pkt;
611051ed9afSJoe Hershberger 	int rlen;
612051ed9afSJoe Hershberger 	int nfsv3_data_offset = 0;
613051ed9afSJoe Hershberger 
614051ed9afSJoe Hershberger 	debug("%s\n", __func__);
615051ed9afSJoe Hershberger 
616051ed9afSJoe Hershberger 	memcpy((unsigned char *)&rpc_pkt, pkt, len);
617051ed9afSJoe Hershberger 
618051ed9afSJoe Hershberger 	if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
619051ed9afSJoe Hershberger 		return -NFS_RPC_ERR;
620051ed9afSJoe Hershberger 	else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
621051ed9afSJoe Hershberger 		return -NFS_RPC_DROP;
622051ed9afSJoe Hershberger 
623051ed9afSJoe Hershberger 	if (rpc_pkt.u.reply.rstatus  ||
624051ed9afSJoe Hershberger 	    rpc_pkt.u.reply.verifier ||
625051ed9afSJoe Hershberger 	    rpc_pkt.u.reply.astatus  ||
626051ed9afSJoe Hershberger 	    rpc_pkt.u.reply.data[0])
627051ed9afSJoe Hershberger 		return -1;
628051ed9afSJoe Hershberger 
629051ed9afSJoe Hershberger 	if (!(supported_nfs_versions & NFSV2_FLAG)) { /* NFSV3_FLAG */
630051ed9afSJoe Hershberger 		nfsv3_data_offset =
631051ed9afSJoe Hershberger 			nfs3_get_attributes_offset(rpc_pkt.u.reply.data);
632b0baca98SGuillaume GARDET 	}
633b0baca98SGuillaume GARDET 
634b0baca98SGuillaume GARDET 	/* new path length */
635b0baca98SGuillaume GARDET 	rlen = ntohl(rpc_pkt.u.reply.data[1 + nfsv3_data_offset]);
636b0baca98SGuillaume GARDET 
637b0baca98SGuillaume GARDET 	if (*((char *)&(rpc_pkt.u.reply.data[2 + nfsv3_data_offset])) != '/') {
638b0baca98SGuillaume GARDET 		int pathlen;
639051ed9afSJoe Hershberger 
640b0baca98SGuillaume GARDET 		strcat(nfs_path, "/");
641b0baca98SGuillaume GARDET 		pathlen = strlen(nfs_path);
642b0baca98SGuillaume GARDET 		memcpy(nfs_path + pathlen,
643b0baca98SGuillaume GARDET 		       (uchar *)&(rpc_pkt.u.reply.data[2 + nfsv3_data_offset]),
644b0baca98SGuillaume GARDET 		       rlen);
645b0baca98SGuillaume GARDET 		nfs_path[pathlen + rlen] = 0;
646b0baca98SGuillaume GARDET 	} else {
647b0baca98SGuillaume GARDET 		memcpy(nfs_path,
648b0baca98SGuillaume GARDET 		       (uchar *)&(rpc_pkt.u.reply.data[2 + nfsv3_data_offset]),
649b0baca98SGuillaume GARDET 		       rlen);
650b0baca98SGuillaume GARDET 		nfs_path[rlen] = 0;
651b0baca98SGuillaume GARDET 	}
652cbd8a35cSwdenk 	return 0;
653cbd8a35cSwdenk }
654cbd8a35cSwdenk 
nfs_read_reply(uchar * pkt,unsigned len)65568c76a3aSJoe Hershberger static int nfs_read_reply(uchar *pkt, unsigned len)
656cbd8a35cSwdenk {
657cbd8a35cSwdenk 	struct rpc_t rpc_pkt;
658cbd8a35cSwdenk 	int rlen;
659b0baca98SGuillaume GARDET 	uchar *data_ptr;
660cbd8a35cSwdenk 
6610ebf04c6SRobin Getz 	debug("%s\n", __func__);
662cbd8a35cSwdenk 
6630517cc45SJoe Hershberger 	memcpy(&rpc_pkt.u.data[0], pkt, sizeof(rpc_pkt.u.reply));
664cbd8a35cSwdenk 
665fa84fa70SMatthias Brugger 	if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
666fa84fa70SMatthias Brugger 		return -NFS_RPC_ERR;
667fa84fa70SMatthias Brugger 	else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
668fa84fa70SMatthias Brugger 		return -NFS_RPC_DROP;
669c3f9d493Swdenk 
670cbd8a35cSwdenk 	if (rpc_pkt.u.reply.rstatus  ||
671cbd8a35cSwdenk 	    rpc_pkt.u.reply.verifier ||
672cbd8a35cSwdenk 	    rpc_pkt.u.reply.astatus  ||
673cbd8a35cSwdenk 	    rpc_pkt.u.reply.data[0]) {
674c9f6c91bSJoe Hershberger 		if (rpc_pkt.u.reply.rstatus)
675cbd8a35cSwdenk 			return -9999;
676c9f6c91bSJoe Hershberger 		if (rpc_pkt.u.reply.astatus)
677cbd8a35cSwdenk 			return -9999;
678c9f6c91bSJoe Hershberger 		return -ntohl(rpc_pkt.u.reply.data[0]);
679cbd8a35cSwdenk 	}
680cbd8a35cSwdenk 
681c9f6c91bSJoe Hershberger 	if ((nfs_offset != 0) && !((nfs_offset) %
682c9f6c91bSJoe Hershberger 			(NFS_READ_SIZE / 2 * 10 * HASHES_PER_LINE)))
683cbd8a35cSwdenk 		puts("\n\t ");
684c9f6c91bSJoe Hershberger 	if (!(nfs_offset % ((NFS_READ_SIZE / 2) * 10)))
685cbd8a35cSwdenk 		putc('#');
686cbd8a35cSwdenk 
687b0baca98SGuillaume GARDET 	if (supported_nfs_versions & NFSV2_FLAG) {
688cbd8a35cSwdenk 		rlen = ntohl(rpc_pkt.u.reply.data[18]);
689b0baca98SGuillaume GARDET 		data_ptr = (uchar *)&(rpc_pkt.u.reply.data[19]);
690b0baca98SGuillaume GARDET 	} else {  /* NFSV3_FLAG */
691051ed9afSJoe Hershberger 		int nfsv3_data_offset =
692051ed9afSJoe Hershberger 			nfs3_get_attributes_offset(rpc_pkt.u.reply.data);
693051ed9afSJoe Hershberger 
694051ed9afSJoe Hershberger 		/* count value */
695051ed9afSJoe Hershberger 		rlen = ntohl(rpc_pkt.u.reply.data[1 + nfsv3_data_offset]);
696b0baca98SGuillaume GARDET 		/* Skip unused values :
697b0baca98SGuillaume GARDET 			EOF:		32 bits value,
698b0baca98SGuillaume GARDET 			data_size:	32 bits value,
699b0baca98SGuillaume GARDET 		*/
700051ed9afSJoe Hershberger 		data_ptr = (uchar *)
701051ed9afSJoe Hershberger 			&(rpc_pkt.u.reply.data[4 + nfsv3_data_offset]);
702b0baca98SGuillaume GARDET 	}
703b0baca98SGuillaume GARDET 
704b0baca98SGuillaume GARDET 	if (store_block(data_ptr, nfs_offset, rlen))
70523a7a32dSWolfgang Denk 			return -9999;
706cbd8a35cSwdenk 
707cbd8a35cSwdenk 	return rlen;
708cbd8a35cSwdenk }
709cbd8a35cSwdenk 
710cbd8a35cSwdenk /**************************************************************************
711cbd8a35cSwdenk Interfaces of U-BOOT
712cbd8a35cSwdenk **************************************************************************/
nfs_timeout_handler(void)71368c76a3aSJoe Hershberger static void nfs_timeout_handler(void)
714a5725fabSwdenk {
71568c76a3aSJoe Hershberger 	if (++nfs_timeout_count > NFS_RETRY_COUNT) {
716aabb8cb0SEvan Samanas 		puts("\nRetry count exceeded; starting again\n");
717bc0571fcSJoe Hershberger 		net_start_again();
718aabb8cb0SEvan Samanas 	} else {
719aabb8cb0SEvan Samanas 		puts("T ");
720bc0571fcSJoe Hershberger 		net_set_timeout_handler(nfs_timeout +
721bc0571fcSJoe Hershberger 					NFS_TIMEOUT * nfs_timeout_count,
72268c76a3aSJoe Hershberger 					nfs_timeout_handler);
72368c76a3aSJoe Hershberger 		nfs_send();
724fe891ecfSHiroshi Ito 	}
725a5725fabSwdenk }
726a5725fabSwdenk 
nfs_handler(uchar * pkt,unsigned dest,struct in_addr sip,unsigned src,unsigned len)727049a95a7SJoe Hershberger static void nfs_handler(uchar *pkt, unsigned dest, struct in_addr sip,
728049a95a7SJoe Hershberger 			unsigned src, unsigned len)
729cbd8a35cSwdenk {
730cbd8a35cSwdenk 	int rlen;
731fa84fa70SMatthias Brugger 	int reply;
732cbd8a35cSwdenk 
7330ebf04c6SRobin Getz 	debug("%s\n", __func__);
734cbd8a35cSwdenk 
73568c76a3aSJoe Hershberger 	if (dest != nfs_our_port)
736c9f6c91bSJoe Hershberger 		return;
737cbd8a35cSwdenk 
73868c76a3aSJoe Hershberger 	switch (nfs_state) {
739cbd8a35cSwdenk 	case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
740fa84fa70SMatthias Brugger 		if (rpc_lookup_reply(PROG_MOUNT, pkt, len) == -NFS_RPC_DROP)
741fa84fa70SMatthias Brugger 			break;
74268c76a3aSJoe Hershberger 		nfs_state = STATE_PRCLOOKUP_PROG_NFS_REQ;
74368c76a3aSJoe Hershberger 		nfs_send();
744cbd8a35cSwdenk 		break;
745cbd8a35cSwdenk 
746cbd8a35cSwdenk 	case STATE_PRCLOOKUP_PROG_NFS_REQ:
747fa84fa70SMatthias Brugger 		if (rpc_lookup_reply(PROG_NFS, pkt, len) == -NFS_RPC_DROP)
748fa84fa70SMatthias Brugger 			break;
74968c76a3aSJoe Hershberger 		nfs_state = STATE_MOUNT_REQ;
75068c76a3aSJoe Hershberger 		nfs_send();
751cbd8a35cSwdenk 		break;
752cbd8a35cSwdenk 
753cbd8a35cSwdenk 	case STATE_MOUNT_REQ:
754fa84fa70SMatthias Brugger 		reply = nfs_mount_reply(pkt, len);
75568c76a3aSJoe Hershberger 		if (reply == -NFS_RPC_DROP) {
756fa84fa70SMatthias Brugger 			break;
75768c76a3aSJoe Hershberger 		} else if (reply == -NFS_RPC_ERR) {
758cbd8a35cSwdenk 			puts("*** ERROR: Cannot mount\n");
759cbd8a35cSwdenk 			/* just to be sure... */
76068c76a3aSJoe Hershberger 			nfs_state = STATE_UMOUNT_REQ;
76168c76a3aSJoe Hershberger 			nfs_send();
762cbd8a35cSwdenk 		} else {
76368c76a3aSJoe Hershberger 			nfs_state = STATE_LOOKUP_REQ;
76468c76a3aSJoe Hershberger 			nfs_send();
765cbd8a35cSwdenk 		}
766cbd8a35cSwdenk 		break;
767cbd8a35cSwdenk 
768cbd8a35cSwdenk 	case STATE_UMOUNT_REQ:
769fa84fa70SMatthias Brugger 		reply = nfs_umountall_reply(pkt, len);
77068c76a3aSJoe Hershberger 		if (reply == -NFS_RPC_DROP) {
771fa84fa70SMatthias Brugger 			break;
77268c76a3aSJoe Hershberger 		} else if (reply == -NFS_RPC_ERR) {
773d89ff2dfSJoe Hershberger 			debug("*** ERROR: Cannot umount\n");
77422f6e99dSJoe Hershberger 			net_set_state(NETLOOP_FAIL);
775cbd8a35cSwdenk 		} else {
776a084f7daSwdenk 			puts("\ndone\n");
77722f6e99dSJoe Hershberger 			net_set_state(nfs_download_state);
778cbd8a35cSwdenk 		}
779cbd8a35cSwdenk 		break;
780cbd8a35cSwdenk 
781cbd8a35cSwdenk 	case STATE_LOOKUP_REQ:
782fa84fa70SMatthias Brugger 		reply = nfs_lookup_reply(pkt, len);
78368c76a3aSJoe Hershberger 		if (reply == -NFS_RPC_DROP) {
784fa84fa70SMatthias Brugger 			break;
78568c76a3aSJoe Hershberger 		} else if (reply == -NFS_RPC_ERR) {
786cbd8a35cSwdenk 			puts("*** ERROR: File lookup fail\n");
78768c76a3aSJoe Hershberger 			nfs_state = STATE_UMOUNT_REQ;
78868c76a3aSJoe Hershberger 			nfs_send();
789347a9015SJoe Hershberger 		} else if (reply == -NFS_RPC_PROG_MISMATCH &&
790347a9015SJoe Hershberger 			   supported_nfs_versions != 0) {
791b0baca98SGuillaume GARDET 			/* umount */
792b0baca98SGuillaume GARDET 			nfs_state = STATE_UMOUNT_REQ;
793b0baca98SGuillaume GARDET 			nfs_send();
794b0baca98SGuillaume GARDET 			/* And retry with another supported version */
795b0baca98SGuillaume GARDET 			nfs_state = STATE_PRCLOOKUP_PROG_MOUNT_REQ;
796b0baca98SGuillaume GARDET 			nfs_send();
797cbd8a35cSwdenk 		} else {
79868c76a3aSJoe Hershberger 			nfs_state = STATE_READ_REQ;
799cbd8a35cSwdenk 			nfs_offset = 0;
800cbd8a35cSwdenk 			nfs_len = NFS_READ_SIZE;
80168c76a3aSJoe Hershberger 			nfs_send();
802cbd8a35cSwdenk 		}
803cbd8a35cSwdenk 		break;
804cbd8a35cSwdenk 
805cbd8a35cSwdenk 	case STATE_READLINK_REQ:
806fa84fa70SMatthias Brugger 		reply = nfs_readlink_reply(pkt, len);
80768c76a3aSJoe Hershberger 		if (reply == -NFS_RPC_DROP) {
808fa84fa70SMatthias Brugger 			break;
80968c76a3aSJoe Hershberger 		} else if (reply == -NFS_RPC_ERR) {
810cbd8a35cSwdenk 			puts("*** ERROR: Symlink fail\n");
81168c76a3aSJoe Hershberger 			nfs_state = STATE_UMOUNT_REQ;
81268c76a3aSJoe Hershberger 			nfs_send();
813cbd8a35cSwdenk 		} else {
8140ebf04c6SRobin Getz 			debug("Symlink --> %s\n", nfs_path);
815cbd8a35cSwdenk 			nfs_filename = basename(nfs_path);
816cbd8a35cSwdenk 			nfs_path     = dirname(nfs_path);
817cbd8a35cSwdenk 
81868c76a3aSJoe Hershberger 			nfs_state = STATE_MOUNT_REQ;
81968c76a3aSJoe Hershberger 			nfs_send();
820cbd8a35cSwdenk 		}
821cbd8a35cSwdenk 		break;
822cbd8a35cSwdenk 
823cbd8a35cSwdenk 	case STATE_READ_REQ:
824cbd8a35cSwdenk 		rlen = nfs_read_reply(pkt, len);
825d48d40a0SVasily Khoruzhick 		if (rlen == -NFS_RPC_DROP)
826d48d40a0SVasily Khoruzhick 			break;
827bc0571fcSJoe Hershberger 		net_set_timeout_handler(nfs_timeout, nfs_timeout_handler);
828cbd8a35cSwdenk 		if (rlen > 0) {
829cbd8a35cSwdenk 			nfs_offset += rlen;
83068c76a3aSJoe Hershberger 			nfs_send();
831c9f6c91bSJoe Hershberger 		} else if ((rlen == -NFSERR_ISDIR) || (rlen == -NFSERR_INVAL)) {
832cbd8a35cSwdenk 			/* symbolic link */
83368c76a3aSJoe Hershberger 			nfs_state = STATE_READLINK_REQ;
83468c76a3aSJoe Hershberger 			nfs_send();
835cbd8a35cSwdenk 		} else {
836c9f6c91bSJoe Hershberger 			if (!rlen)
83722f6e99dSJoe Hershberger 				nfs_download_state = NETLOOP_SUCCESS;
838b0baca98SGuillaume GARDET 			if (rlen < 0)
839d89ff2dfSJoe Hershberger 				debug("NFS READ error (%d)\n", rlen);
84068c76a3aSJoe Hershberger 			nfs_state = STATE_UMOUNT_REQ;
84168c76a3aSJoe Hershberger 			nfs_send();
842cbd8a35cSwdenk 		}
843cbd8a35cSwdenk 		break;
844cbd8a35cSwdenk 	}
845cbd8a35cSwdenk }
846cbd8a35cSwdenk 
847cbd8a35cSwdenk 
nfs_start(void)84868c76a3aSJoe Hershberger void nfs_start(void)
849cbd8a35cSwdenk {
8500ebf04c6SRobin Getz 	debug("%s\n", __func__);
85122f6e99dSJoe Hershberger 	nfs_download_state = NETLOOP_FAIL;
852cbd8a35cSwdenk 
853049a95a7SJoe Hershberger 	nfs_server_ip = net_server_ip;
854cbd8a35cSwdenk 	nfs_path = (char *)nfs_path_buff;
855cbd8a35cSwdenk 
856cbd8a35cSwdenk 	if (nfs_path == NULL) {
85722f6e99dSJoe Hershberger 		net_set_state(NETLOOP_FAIL);
858faecf84aSJoe Hershberger 		printf("*** ERROR: Fail allocate memory\n");
859cbd8a35cSwdenk 		return;
860cbd8a35cSwdenk 	}
861cbd8a35cSwdenk 
862*6ab12830SJoe Hershberger 	if (!net_parse_bootfile(&nfs_server_ip, nfs_path,
863*6ab12830SJoe Hershberger 				sizeof(nfs_path_buff))) {
864f8b26c7aSJoe Hershberger 		sprintf(nfs_path, "/nfsroot/%02X%02X%02X%02X.img",
865049a95a7SJoe Hershberger 			net_ip.s_addr & 0xFF,
866049a95a7SJoe Hershberger 			(net_ip.s_addr >>  8) & 0xFF,
867049a95a7SJoe Hershberger 			(net_ip.s_addr >> 16) & 0xFF,
868049a95a7SJoe Hershberger 			(net_ip.s_addr >> 24) & 0xFF);
869cbd8a35cSwdenk 
870faecf84aSJoe Hershberger 		printf("*** Warning: no boot file name; using '%s'\n",
871cbd8a35cSwdenk 		       nfs_path);
872cbd8a35cSwdenk 	}
873cbd8a35cSwdenk 
874cbd8a35cSwdenk 	nfs_filename = basename(nfs_path);
875cbd8a35cSwdenk 	nfs_path     = dirname(nfs_path);
876cbd8a35cSwdenk 
877faecf84aSJoe Hershberger 	printf("Using %s device\n", eth_get_name());
878cbd8a35cSwdenk 
879faecf84aSJoe Hershberger 	printf("File transfer via NFS from server %pI4; our IP address is %pI4",
880049a95a7SJoe Hershberger 	       &nfs_server_ip, &net_ip);
881cbd8a35cSwdenk 
882cbd8a35cSwdenk 	/* Check if we need to send across this subnet */
883049a95a7SJoe Hershberger 	if (net_gateway.s_addr && net_netmask.s_addr) {
884049a95a7SJoe Hershberger 		struct in_addr our_net;
885049a95a7SJoe Hershberger 		struct in_addr server_net;
886cbd8a35cSwdenk 
887049a95a7SJoe Hershberger 		our_net.s_addr = net_ip.s_addr & net_netmask.s_addr;
888347e32b0SJoe Hershberger 		server_net.s_addr = nfs_server_ip.s_addr & net_netmask.s_addr;
889049a95a7SJoe Hershberger 		if (our_net.s_addr != server_net.s_addr)
890faecf84aSJoe Hershberger 			printf("; sending through gateway %pI4",
891049a95a7SJoe Hershberger 			       &net_gateway);
892cbd8a35cSwdenk 	}
893faecf84aSJoe Hershberger 	printf("\nFilename '%s/%s'.", nfs_path, nfs_filename);
894cbd8a35cSwdenk 
8951411157dSJoe Hershberger 	if (net_boot_file_expected_size_in_blocks) {
896faecf84aSJoe Hershberger 		printf(" Size is 0x%x Bytes = ",
8971411157dSJoe Hershberger 		       net_boot_file_expected_size_in_blocks << 9);
8981411157dSJoe Hershberger 		print_size(net_boot_file_expected_size_in_blocks << 9, "");
899cbd8a35cSwdenk 	}
900faecf84aSJoe Hershberger 	printf("\nLoad address: 0x%lx\nLoading: *\b", load_addr);
901cbd8a35cSwdenk 
902bc0571fcSJoe Hershberger 	net_set_timeout_handler(nfs_timeout, nfs_timeout_handler);
903049a95a7SJoe Hershberger 	net_set_udp_handler(nfs_handler);
904cbd8a35cSwdenk 
90568c76a3aSJoe Hershberger 	nfs_timeout_count = 0;
90668c76a3aSJoe Hershberger 	nfs_state = STATE_PRCLOOKUP_PROG_MOUNT_REQ;
907cbd8a35cSwdenk 
90868c76a3aSJoe Hershberger 	/*nfs_our_port = 4096 + (get_ticks() % 3072);*/
909cbd8a35cSwdenk 	/*FIX ME !!!*/
91068c76a3aSJoe Hershberger 	nfs_our_port = 1000;
911cbd8a35cSwdenk 
912cbd8a35cSwdenk 	/* zero out server ether in case the server ip has changed */
9130adb5b76SJoe Hershberger 	memset(net_server_ethaddr, 0, 6);
914cbd8a35cSwdenk 
91568c76a3aSJoe Hershberger 	nfs_send();
916cbd8a35cSwdenk }
917