xref: /titanic_41/usr/src/grub/grub-0.97/netboot/fsys_tftp.c (revision df05b9eea34242cff39427aa1782e012cd696979)
11b8adde7SWilliam Kucharski /*
21b8adde7SWilliam Kucharski  *  GRUB  --  GRand Unified Bootloader
31b8adde7SWilliam Kucharski  *  Copyright (C) 2000,2001,2002,2004  Free Software Foundation, Inc.
41b8adde7SWilliam Kucharski  *
51b8adde7SWilliam Kucharski  *  This program is free software; you can redistribute it and/or modify
61b8adde7SWilliam Kucharski  *  it under the terms of the GNU General Public License as published by
71b8adde7SWilliam Kucharski  *  the Free Software Foundation; either version 2 of the License, or
81b8adde7SWilliam Kucharski  *  (at your option) any later version.
91b8adde7SWilliam Kucharski  *
101b8adde7SWilliam Kucharski  *  This program is distributed in the hope that it will be useful,
111b8adde7SWilliam Kucharski  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
121b8adde7SWilliam Kucharski  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
131b8adde7SWilliam Kucharski  *  GNU General Public License for more details.
141b8adde7SWilliam Kucharski  *
151b8adde7SWilliam Kucharski  *  You should have received a copy of the GNU General Public License
161b8adde7SWilliam Kucharski  *  along with this program; if not, write to the Free Software
171b8adde7SWilliam Kucharski  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
181b8adde7SWilliam Kucharski  */
191b8adde7SWilliam Kucharski 
201b8adde7SWilliam Kucharski /* Based on "src/main.c" in etherboot-4.5.8.  */
211b8adde7SWilliam Kucharski /**************************************************************************
221b8adde7SWilliam Kucharski ETHERBOOT -  BOOTP/TFTP Bootstrap Program
231b8adde7SWilliam Kucharski 
241b8adde7SWilliam Kucharski Author: Martin Renters
251b8adde7SWilliam Kucharski   Date: Dec/93
261b8adde7SWilliam Kucharski 
271b8adde7SWilliam Kucharski **************************************************************************/
281b8adde7SWilliam Kucharski 
291b8adde7SWilliam Kucharski /* #define TFTP_DEBUG	1 */
301b8adde7SWilliam Kucharski 
311b8adde7SWilliam Kucharski #include <filesys.h>
321b8adde7SWilliam Kucharski #include <shared.h>
331b8adde7SWilliam Kucharski 
341b8adde7SWilliam Kucharski #include "grub.h"
351b8adde7SWilliam Kucharski #include "tftp.h"
361b8adde7SWilliam Kucharski #include "nic.h"
371b8adde7SWilliam Kucharski 
381b8adde7SWilliam Kucharski static int tftp_file_read_undi(const char *name,
391b8adde7SWilliam Kucharski     int (*fnc)(unsigned char *, unsigned int, unsigned int, int));
401b8adde7SWilliam Kucharski static int tftp_read_undi(char *addr, int size);
411b8adde7SWilliam Kucharski static int tftp_dir_undi(char *dirname);
421b8adde7SWilliam Kucharski static void tftp_close_undi(void);
431b8adde7SWilliam Kucharski static int buf_fill_undi(int abort);
441b8adde7SWilliam Kucharski 
451b8adde7SWilliam Kucharski extern int use_bios_pxe;
461b8adde7SWilliam Kucharski 
471b8adde7SWilliam Kucharski static int retry;
481b8adde7SWilliam Kucharski static unsigned short iport = 2000;
491b8adde7SWilliam Kucharski static unsigned short oport = 0;
501b8adde7SWilliam Kucharski static unsigned short block, prevblock;
511b8adde7SWilliam Kucharski static int bcounter;
521b8adde7SWilliam Kucharski static struct tftp_t tp, saved_tp;
531b8adde7SWilliam Kucharski static int packetsize;
541b8adde7SWilliam Kucharski static int buf_eof, buf_read;
551b8adde7SWilliam Kucharski static int saved_filepos;
561b8adde7SWilliam Kucharski static unsigned short len, saved_len;
571b8adde7SWilliam Kucharski static char *buf, *saved_name;
581b8adde7SWilliam Kucharski 
591b8adde7SWilliam Kucharski /**
601b8adde7SWilliam Kucharski  * tftp_read
611b8adde7SWilliam Kucharski  *
621b8adde7SWilliam Kucharski  * Read file with _name_, data handled by _fnc_. In fact, grub never
631b8adde7SWilliam Kucharski  * use it, we just use it to read dhcp config file.
641b8adde7SWilliam Kucharski  */
await_tftp(int ival,void * ptr __unused,unsigned short ptype __unused,struct iphdr * ip,struct udphdr * udp)651b8adde7SWilliam Kucharski static int await_tftp(int ival, void *ptr __unused,
661b8adde7SWilliam Kucharski 		      unsigned short ptype __unused, struct iphdr *ip,
671b8adde7SWilliam Kucharski 		      struct udphdr *udp)
681b8adde7SWilliam Kucharski {
691b8adde7SWilliam Kucharski 	static int tftp_count = 0;
701b8adde7SWilliam Kucharski 
711b8adde7SWilliam Kucharski 	if (!udp) {
721b8adde7SWilliam Kucharski 		return 0;
731b8adde7SWilliam Kucharski 	}
741b8adde7SWilliam Kucharski 	if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
751b8adde7SWilliam Kucharski 		return 0;
761b8adde7SWilliam Kucharski 	if (ntohs(udp->dest) != ival)
771b8adde7SWilliam Kucharski 		return 0;
781b8adde7SWilliam Kucharski 	tftp_count++;	/* show progress */
791b8adde7SWilliam Kucharski 	if ((tftp_count % 1000) == 0)
801b8adde7SWilliam Kucharski 		printf(".");
811b8adde7SWilliam Kucharski 	return 1;
821b8adde7SWilliam Kucharski }
831b8adde7SWilliam Kucharski 
tftp_file_read(const char * name,int (* fnc)(unsigned char *,unsigned int,unsigned int,int))841b8adde7SWilliam Kucharski int tftp_file_read(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
851b8adde7SWilliam Kucharski {
861b8adde7SWilliam Kucharski 	struct tftpreq_t tp;
871b8adde7SWilliam Kucharski 	struct tftp_t  *tr;
881b8adde7SWilliam Kucharski 	int		rc;
891b8adde7SWilliam Kucharski 
901b8adde7SWilliam Kucharski 	if (use_bios_pxe)
911b8adde7SWilliam Kucharski 		return (tftp_file_read_undi(name, fnc));
921b8adde7SWilliam Kucharski 
931b8adde7SWilliam Kucharski 	retry = 0;
941b8adde7SWilliam Kucharski 	block = 0;
951b8adde7SWilliam Kucharski 	prevblock = 0;
961b8adde7SWilliam Kucharski 	bcounter = 0;
971b8adde7SWilliam Kucharski 
981b8adde7SWilliam Kucharski 
991b8adde7SWilliam Kucharski 	rx_qdrain();
1001b8adde7SWilliam Kucharski 
1011b8adde7SWilliam Kucharski 	tp.opcode = htons(TFTP_RRQ);
1021b8adde7SWilliam Kucharski 	/* Warning: the following assumes the layout of bootp_t.
1031b8adde7SWilliam Kucharski 	   But that's fixed by the IP, UDP and BOOTP specs. */
1041b8adde7SWilliam Kucharski 	len = sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) +
1051b8adde7SWilliam Kucharski 		sprintf((char *)tp.u.rrq, "%s%coctet%cblksize%c%d",
1061b8adde7SWilliam Kucharski 		name, 0, 0, 0, TFTP_MAX_PACKET) + 1;
1071b8adde7SWilliam Kucharski 	if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
1081b8adde7SWilliam Kucharski 			  TFTP_PORT, len, &tp))
1091b8adde7SWilliam Kucharski 		return (0);
1101b8adde7SWilliam Kucharski 	for (;;)
1111b8adde7SWilliam Kucharski 	{
1121b8adde7SWilliam Kucharski 		long timeout;
1131b8adde7SWilliam Kucharski #ifdef	CONGESTED
1141b8adde7SWilliam Kucharski 		timeout = rfc2131_sleep_interval(block?TFTP_REXMT: TIMEOUT, retry);
1151b8adde7SWilliam Kucharski #else
1161b8adde7SWilliam Kucharski 		timeout = rfc2131_sleep_interval(TIMEOUT, retry);
1171b8adde7SWilliam Kucharski #endif
1181b8adde7SWilliam Kucharski 		if (!await_reply(await_tftp, iport, NULL, timeout))
1191b8adde7SWilliam Kucharski 		{
1201b8adde7SWilliam Kucharski 			if (!block && retry++ < MAX_TFTP_RETRIES)
1211b8adde7SWilliam Kucharski 			{	/* maybe initial request was lost */
1221b8adde7SWilliam Kucharski 				if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
1231b8adde7SWilliam Kucharski 						  ++iport, TFTP_PORT, len, &tp))
1241b8adde7SWilliam Kucharski 					return (0);
1251b8adde7SWilliam Kucharski 				continue;
1261b8adde7SWilliam Kucharski 			}
1271b8adde7SWilliam Kucharski #ifdef	CONGESTED
1281b8adde7SWilliam Kucharski 			if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
1291b8adde7SWilliam Kucharski 			{	/* we resend our last ack */
1301b8adde7SWilliam Kucharski #ifdef	MDEBUG
1311b8adde7SWilliam Kucharski 				printf("<REXMT>\n");
1321b8adde7SWilliam Kucharski #endif
1331b8adde7SWilliam Kucharski 				udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
1341b8adde7SWilliam Kucharski 					     iport, oport,
1351b8adde7SWilliam Kucharski 					     TFTP_MIN_PACKET, &tp);
1361b8adde7SWilliam Kucharski 				continue;
1371b8adde7SWilliam Kucharski 			}
1381b8adde7SWilliam Kucharski #endif
1391b8adde7SWilliam Kucharski 			break;	/* timeout */
1401b8adde7SWilliam Kucharski 		}
1411b8adde7SWilliam Kucharski 		tr = (struct tftp_t *)&nic.packet[ETH_HLEN];
1421b8adde7SWilliam Kucharski 		if (tr->opcode == ntohs(TFTP_ERROR))
1431b8adde7SWilliam Kucharski 		{
1441b8adde7SWilliam Kucharski 			printf("TFTP error %d (%s)\n",
1451b8adde7SWilliam Kucharski 			       ntohs(tr->u.err.errcode),
1461b8adde7SWilliam Kucharski 			       tr->u.err.errmsg);
1471b8adde7SWilliam Kucharski 			break;
1481b8adde7SWilliam Kucharski 		}
1491b8adde7SWilliam Kucharski 
1501b8adde7SWilliam Kucharski 		if (tr->opcode == ntohs(TFTP_OACK)) {
1511b8adde7SWilliam Kucharski 			char *p = tr->u.oack.data, *e;
1521b8adde7SWilliam Kucharski 
1531b8adde7SWilliam Kucharski 			if (prevblock)		/* shouldn't happen */
1541b8adde7SWilliam Kucharski 				continue;	/* ignore it */
1551b8adde7SWilliam Kucharski 			len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2;
1561b8adde7SWilliam Kucharski 			if (len > TFTP_MAX_PACKET)
1571b8adde7SWilliam Kucharski 				goto noak;
1581b8adde7SWilliam Kucharski 			e = p + len;
1591b8adde7SWilliam Kucharski 			while (*p != '\0' && p < e) {
1601b8adde7SWilliam Kucharski /* 				if (!strcasecmp("blksize", p)) { */
1611b8adde7SWilliam Kucharski 				if (!grub_strcmp("blksize", p)) {
1621b8adde7SWilliam Kucharski 					p += 8;
1631b8adde7SWilliam Kucharski /* 					if ((packetsize = strtoul(p, &p, 10)) < */
1641b8adde7SWilliam Kucharski 					if ((packetsize = getdec(&p)) < TFTP_DEFAULTSIZE_PACKET)
1651b8adde7SWilliam Kucharski 						goto noak;
1661b8adde7SWilliam Kucharski 					while (p < e && *p) p++;
1671b8adde7SWilliam Kucharski 					if (p < e)
1681b8adde7SWilliam Kucharski 						p++;
1691b8adde7SWilliam Kucharski 				}
1701b8adde7SWilliam Kucharski 				else {
1711b8adde7SWilliam Kucharski 				noak:
1721b8adde7SWilliam Kucharski 					tp.opcode = htons(TFTP_ERROR);
1731b8adde7SWilliam Kucharski 					tp.u.err.errcode = 8;
1741b8adde7SWilliam Kucharski /*
1751b8adde7SWilliam Kucharski  *	Warning: the following assumes the layout of bootp_t.
1761b8adde7SWilliam Kucharski  *	But that's fixed by the IP, UDP and BOOTP specs.
1771b8adde7SWilliam Kucharski  */
1781b8adde7SWilliam Kucharski 					len = sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) + sizeof(tp.u.err.errcode) +
1791b8adde7SWilliam Kucharski /*
1801b8adde7SWilliam Kucharski  *	Normally bad form to omit the format string, but in this case
1811b8adde7SWilliam Kucharski  *	the string we are copying from is fixed. sprintf is just being
1821b8adde7SWilliam Kucharski  *	used as a strcpy and strlen.
1831b8adde7SWilliam Kucharski  */
1841b8adde7SWilliam Kucharski 						sprintf((char *)tp.u.err.errmsg,
1851b8adde7SWilliam Kucharski 						"RFC1782 error") + 1;
1861b8adde7SWilliam Kucharski 					udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
1871b8adde7SWilliam Kucharski 						     iport, ntohs(tr->udp.src),
1881b8adde7SWilliam Kucharski 						     len, &tp);
1891b8adde7SWilliam Kucharski 					return (0);
1901b8adde7SWilliam Kucharski 				}
1911b8adde7SWilliam Kucharski 			}
1921b8adde7SWilliam Kucharski 			if (p > e)
1931b8adde7SWilliam Kucharski 				goto noak;
1941b8adde7SWilliam Kucharski 			block = tp.u.ack.block = 0; /* this ensures, that */
1951b8adde7SWilliam Kucharski 						/* the packet does not get */
1961b8adde7SWilliam Kucharski 						/* processed as data! */
1971b8adde7SWilliam Kucharski 		}
1981b8adde7SWilliam Kucharski 		else if (tr->opcode == htons(TFTP_DATA)) {
1991b8adde7SWilliam Kucharski 			len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
2001b8adde7SWilliam Kucharski 			if (len > packetsize)	/* shouldn't happen */
2011b8adde7SWilliam Kucharski 				continue;	/* ignore it */
2021b8adde7SWilliam Kucharski 			block = ntohs(tp.u.ack.block = tr->u.data.block); }
2031b8adde7SWilliam Kucharski 		else {/* neither TFTP_OACK nor TFTP_DATA */
2041b8adde7SWilliam Kucharski 			break;
2051b8adde7SWilliam Kucharski 		}
2061b8adde7SWilliam Kucharski 
2071b8adde7SWilliam Kucharski 		if ((block || bcounter) && (block != (unsigned short)(prevblock+1))) {
2081b8adde7SWilliam Kucharski 			/* Block order should be continuous */
2091b8adde7SWilliam Kucharski 			tp.u.ack.block = htons(block = prevblock);
2101b8adde7SWilliam Kucharski 		}
2111b8adde7SWilliam Kucharski 		tp.opcode = htons(TFTP_ACK);
2121b8adde7SWilliam Kucharski 		oport = ntohs(tr->udp.src);
2131b8adde7SWilliam Kucharski 		udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, iport,
2141b8adde7SWilliam Kucharski 			     oport, TFTP_MIN_PACKET, &tp);	/* ack */
2151b8adde7SWilliam Kucharski 		if ((unsigned short)(block-prevblock) != 1) {
2161b8adde7SWilliam Kucharski 			/* Retransmission or OACK, don't process via callback
2171b8adde7SWilliam Kucharski 			 * and don't change the value of prevblock.  */
2181b8adde7SWilliam Kucharski 			continue;
2191b8adde7SWilliam Kucharski 		}
2201b8adde7SWilliam Kucharski 		prevblock = block;
2211b8adde7SWilliam Kucharski 		retry = 0;	/* It's the right place to zero the timer? */
2221b8adde7SWilliam Kucharski 		if ((rc = fnc(tr->u.data.download,
2231b8adde7SWilliam Kucharski 			      ++bcounter, len, len < packetsize)) <= 0)
2241b8adde7SWilliam Kucharski 			return(rc);
2251b8adde7SWilliam Kucharski 		if (len < packetsize) {	/* End of data --- fnc should not have returned */
2261b8adde7SWilliam Kucharski 			printf("tftp download complete, but\n");
2271b8adde7SWilliam Kucharski 			return (1);
2281b8adde7SWilliam Kucharski 		}
2291b8adde7SWilliam Kucharski 	}
2301b8adde7SWilliam Kucharski 	return (0);
2311b8adde7SWilliam Kucharski }
2321b8adde7SWilliam Kucharski 
2331b8adde7SWilliam Kucharski /* Fill the buffer by receiving the data via the TFTP protocol.  */
2341b8adde7SWilliam Kucharski static int
buf_fill(int abort)2351b8adde7SWilliam Kucharski buf_fill (int abort)
2361b8adde7SWilliam Kucharski {
2371b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
2381b8adde7SWilliam Kucharski   grub_printf ("buf_fill (%d)\n", abort);
2391b8adde7SWilliam Kucharski #endif
2401b8adde7SWilliam Kucharski 
2411b8adde7SWilliam Kucharski   if (use_bios_pxe)
2421b8adde7SWilliam Kucharski 	return (buf_fill_undi(abort));
2431b8adde7SWilliam Kucharski 
2441b8adde7SWilliam Kucharski   while (! buf_eof && (buf_read + packetsize <= FSYS_BUFLEN))
2451b8adde7SWilliam Kucharski     {
2461b8adde7SWilliam Kucharski       struct tftp_t *tr;
2471b8adde7SWilliam Kucharski       long timeout;
2481b8adde7SWilliam Kucharski 
2491b8adde7SWilliam Kucharski #ifdef CONGESTED
2501b8adde7SWilliam Kucharski       timeout = rfc2131_sleep_interval (block ? TFTP_REXMT : TIMEOUT, retry);
2511b8adde7SWilliam Kucharski #else
2521b8adde7SWilliam Kucharski       timeout = rfc2131_sleep_interval (TIMEOUT, retry);
2531b8adde7SWilliam Kucharski #endif
2541b8adde7SWilliam Kucharski 
2551b8adde7SWilliam Kucharski       if (! await_reply (await_tftp, iport, NULL, timeout))
2561b8adde7SWilliam Kucharski 	{
2571b8adde7SWilliam Kucharski 	  if (user_abort)
2581b8adde7SWilliam Kucharski 	    return 0;
2591b8adde7SWilliam Kucharski 
2601b8adde7SWilliam Kucharski 	  if (! block && retry++ < MAX_TFTP_RETRIES)
2611b8adde7SWilliam Kucharski 	    {
2621b8adde7SWilliam Kucharski 	      /* Maybe initial request was lost.  */
2631b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
2641b8adde7SWilliam Kucharski 	      grub_printf ("Maybe initial request was lost.\n");
2651b8adde7SWilliam Kucharski #endif
2661b8adde7SWilliam Kucharski 	      if (! udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
2671b8adde7SWilliam Kucharski 				  ++iport, TFTP_PORT, len, &tp))
2681b8adde7SWilliam Kucharski 		return 0;
2691b8adde7SWilliam Kucharski 
2701b8adde7SWilliam Kucharski 	      continue;
2711b8adde7SWilliam Kucharski 	    }
2721b8adde7SWilliam Kucharski 
2731b8adde7SWilliam Kucharski #ifdef CONGESTED
2741b8adde7SWilliam Kucharski 	  if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
2751b8adde7SWilliam Kucharski 	    {
2761b8adde7SWilliam Kucharski 	      /* We resend our last ack.  */
2771b8adde7SWilliam Kucharski # ifdef TFTP_DEBUG
2781b8adde7SWilliam Kucharski 	      grub_printf ("<REXMT>\n");
2791b8adde7SWilliam Kucharski # endif
2801b8adde7SWilliam Kucharski 	      udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
2811b8adde7SWilliam Kucharski 			    iport, oport,
2821b8adde7SWilliam Kucharski 			    TFTP_MIN_PACKET, &tp);
2831b8adde7SWilliam Kucharski 	      continue;
2841b8adde7SWilliam Kucharski 	    }
2851b8adde7SWilliam Kucharski #endif
2861b8adde7SWilliam Kucharski 	  /* Timeout.  */
2871b8adde7SWilliam Kucharski 	  return 0;
2881b8adde7SWilliam Kucharski 	}
2891b8adde7SWilliam Kucharski 
2901b8adde7SWilliam Kucharski       tr = (struct tftp_t *) &nic.packet[ETH_HLEN];
2911b8adde7SWilliam Kucharski       if (tr->opcode == ntohs (TFTP_ERROR))
2921b8adde7SWilliam Kucharski 	{
2931b8adde7SWilliam Kucharski 	  grub_printf ("TFTP error %d (%s)\n",
2941b8adde7SWilliam Kucharski 		       ntohs (tr->u.err.errcode),
2951b8adde7SWilliam Kucharski 		       tr->u.err.errmsg);
2961b8adde7SWilliam Kucharski 	  return 0;
2971b8adde7SWilliam Kucharski 	}
2981b8adde7SWilliam Kucharski 
2991b8adde7SWilliam Kucharski       if (tr->opcode == ntohs (TFTP_OACK))
3001b8adde7SWilliam Kucharski 	{
3011b8adde7SWilliam Kucharski 	  char *p = tr->u.oack.data, *e;
3021b8adde7SWilliam Kucharski 
3031b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
3041b8adde7SWilliam Kucharski 	  grub_printf ("OACK ");
3051b8adde7SWilliam Kucharski #endif
3061b8adde7SWilliam Kucharski 	  /* Shouldn't happen.  */
3071b8adde7SWilliam Kucharski 	  if (prevblock)
3081b8adde7SWilliam Kucharski 	    {
3091b8adde7SWilliam Kucharski 	      /* Ignore it.  */
3101b8adde7SWilliam Kucharski 	      grub_printf ("%s:%d: warning: PREVBLOCK != 0 (0x%x)\n",
3111b8adde7SWilliam Kucharski 			   __FILE__, __LINE__, prevblock);
3121b8adde7SWilliam Kucharski 	      continue;
3131b8adde7SWilliam Kucharski 	    }
3141b8adde7SWilliam Kucharski 
3151b8adde7SWilliam Kucharski 	  len = ntohs (tr->udp.len) - sizeof (struct udphdr) - 2;
3161b8adde7SWilliam Kucharski 	  if (len > TFTP_MAX_PACKET)
3171b8adde7SWilliam Kucharski 	    goto noak;
3181b8adde7SWilliam Kucharski 
3191b8adde7SWilliam Kucharski 	  e = p + len;
3201b8adde7SWilliam Kucharski 	  while (*p != '\000' && p < e)
3211b8adde7SWilliam Kucharski 	    {
3221b8adde7SWilliam Kucharski 	      if (! grub_strcmp ("blksize", p))
3231b8adde7SWilliam Kucharski 		{
3241b8adde7SWilliam Kucharski 		  p += 8;
3251b8adde7SWilliam Kucharski 		  if ((packetsize = getdec (&p)) < TFTP_DEFAULTSIZE_PACKET)
3261b8adde7SWilliam Kucharski 		    goto noak;
3271b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
3281b8adde7SWilliam Kucharski 		  grub_printf ("blksize = %d\n", packetsize);
3291b8adde7SWilliam Kucharski #endif
3301b8adde7SWilliam Kucharski 		}
3311b8adde7SWilliam Kucharski 	      else if (! grub_strcmp ("tsize", p))
3321b8adde7SWilliam Kucharski 		{
3331b8adde7SWilliam Kucharski 		  p += 6;
3341b8adde7SWilliam Kucharski 		  if ((filemax = getdec (&p)) < 0)
3351b8adde7SWilliam Kucharski 		    {
3361b8adde7SWilliam Kucharski 		      filemax = -1;
3371b8adde7SWilliam Kucharski 		      goto noak;
3381b8adde7SWilliam Kucharski 		    }
3391b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
3401b8adde7SWilliam Kucharski 		  grub_printf ("tsize = %d\n", filemax);
3411b8adde7SWilliam Kucharski #endif
3421b8adde7SWilliam Kucharski 		}
3431b8adde7SWilliam Kucharski 	      else
3441b8adde7SWilliam Kucharski 		{
3451b8adde7SWilliam Kucharski 		noak:
3461b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
3471b8adde7SWilliam Kucharski 		  grub_printf ("NOAK\n");
3481b8adde7SWilliam Kucharski #endif
3491b8adde7SWilliam Kucharski 		  tp.opcode = htons (TFTP_ERROR);
3501b8adde7SWilliam Kucharski 		  tp.u.err.errcode = 8;
3511b8adde7SWilliam Kucharski 		  len = (grub_sprintf ((char *) tp.u.err.errmsg,
3521b8adde7SWilliam Kucharski 				       "RFC1782 error")
3531b8adde7SWilliam Kucharski 			 + sizeof (tp.ip) + sizeof (tp.udp)
3541b8adde7SWilliam Kucharski 			 + sizeof (tp.opcode) + sizeof (tp.u.err.errcode)
3551b8adde7SWilliam Kucharski 			 + 1);
3561b8adde7SWilliam Kucharski 		  udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
3571b8adde7SWilliam Kucharski 				iport, ntohs (tr->udp.src),
3581b8adde7SWilliam Kucharski 				len, &tp);
3591b8adde7SWilliam Kucharski 		  return 0;
3601b8adde7SWilliam Kucharski 		}
3611b8adde7SWilliam Kucharski 
3621b8adde7SWilliam Kucharski 	      while (p < e && *p)
3631b8adde7SWilliam Kucharski 		p++;
3641b8adde7SWilliam Kucharski 
3651b8adde7SWilliam Kucharski 	      if (p < e)
3661b8adde7SWilliam Kucharski 		p++;
3671b8adde7SWilliam Kucharski 	    }
3681b8adde7SWilliam Kucharski 
3691b8adde7SWilliam Kucharski 	  if (p > e)
3701b8adde7SWilliam Kucharski 	    goto noak;
3711b8adde7SWilliam Kucharski 
3721b8adde7SWilliam Kucharski 	  /* This ensures that the packet does not get processed as
3731b8adde7SWilliam Kucharski 	     data!  */
3741b8adde7SWilliam Kucharski 	  block = tp.u.ack.block = 0;
3751b8adde7SWilliam Kucharski 	}
3761b8adde7SWilliam Kucharski       else if (tr->opcode == ntohs (TFTP_DATA))
3771b8adde7SWilliam Kucharski 	{
3781b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
3791b8adde7SWilliam Kucharski 	  grub_printf ("DATA ");
3801b8adde7SWilliam Kucharski #endif
3811b8adde7SWilliam Kucharski 	  len = ntohs (tr->udp.len) - sizeof (struct udphdr) - 4;
3821b8adde7SWilliam Kucharski 
3831b8adde7SWilliam Kucharski 	  /* Shouldn't happen.  */
3841b8adde7SWilliam Kucharski 	  if (len > packetsize)
3851b8adde7SWilliam Kucharski 	    {
3861b8adde7SWilliam Kucharski 	      /* Ignore it.  */
3871b8adde7SWilliam Kucharski 	      grub_printf ("%s:%d: warning: LEN > PACKETSIZE (0x%x > 0x%x)\n",
3881b8adde7SWilliam Kucharski 			   __FILE__, __LINE__, len, packetsize);
3891b8adde7SWilliam Kucharski 	      continue;
3901b8adde7SWilliam Kucharski 	    }
3911b8adde7SWilliam Kucharski 
3921b8adde7SWilliam Kucharski 	  block = ntohs (tp.u.ack.block = tr->u.data.block);
3931b8adde7SWilliam Kucharski 	}
3941b8adde7SWilliam Kucharski       else
3951b8adde7SWilliam Kucharski 	/* Neither TFTP_OACK nor TFTP_DATA.  */
3961b8adde7SWilliam Kucharski 	break;
3971b8adde7SWilliam Kucharski 
398*df05b9eeSGuoli Shu       if ((block || bcounter) && (block != (unsigned short) (prevblock + 1)))
3991b8adde7SWilliam Kucharski 	/* Block order should be continuous */
4001b8adde7SWilliam Kucharski 	tp.u.ack.block = htons (block = prevblock);
4011b8adde7SWilliam Kucharski 
4021b8adde7SWilliam Kucharski       /* Should be continuous.  */
4031b8adde7SWilliam Kucharski       tp.opcode = abort ? htons (TFTP_ERROR) : htons (TFTP_ACK);
4041b8adde7SWilliam Kucharski       oport = ntohs (tr->udp.src);
4051b8adde7SWilliam Kucharski 
4061b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
4071b8adde7SWilliam Kucharski       grub_printf ("ACK\n");
4081b8adde7SWilliam Kucharski #endif
4091b8adde7SWilliam Kucharski       /* Ack.  */
4101b8adde7SWilliam Kucharski       udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr, iport,
4111b8adde7SWilliam Kucharski 		    oport, TFTP_MIN_PACKET, &tp);
4121b8adde7SWilliam Kucharski 
4131b8adde7SWilliam Kucharski       if (abort)
4141b8adde7SWilliam Kucharski 	{
4151b8adde7SWilliam Kucharski 	  buf_eof = 1;
4161b8adde7SWilliam Kucharski 	  break;
4171b8adde7SWilliam Kucharski 	}
4181b8adde7SWilliam Kucharski 
4191b8adde7SWilliam Kucharski       /* Retransmission or OACK.  */
4201b8adde7SWilliam Kucharski       if ((unsigned short) (block - prevblock) != 1)
4211b8adde7SWilliam Kucharski 	/* Don't process.  */
4221b8adde7SWilliam Kucharski 	continue;
4231b8adde7SWilliam Kucharski 
4241b8adde7SWilliam Kucharski       prevblock = block;
4251b8adde7SWilliam Kucharski       /* Is it the right place to zero the timer?  */
4261b8adde7SWilliam Kucharski       retry = 0;
4271b8adde7SWilliam Kucharski 
4281b8adde7SWilliam Kucharski       /* In GRUB, this variable doesn't play any important role at all,
4291b8adde7SWilliam Kucharski 	 but use it for consistency with Etherboot.  */
4301b8adde7SWilliam Kucharski       bcounter++;
4311b8adde7SWilliam Kucharski 
4321b8adde7SWilliam Kucharski       /* Copy the downloaded data to the buffer.  */
4331b8adde7SWilliam Kucharski       grub_memmove (buf + buf_read, tr->u.data.download, len);
4341b8adde7SWilliam Kucharski       buf_read += len;
4351b8adde7SWilliam Kucharski 
4361b8adde7SWilliam Kucharski       /* End of data.  */
4371b8adde7SWilliam Kucharski       if (len < packetsize)
4381b8adde7SWilliam Kucharski 	buf_eof = 1;
4391b8adde7SWilliam Kucharski     }
4401b8adde7SWilliam Kucharski 
4411b8adde7SWilliam Kucharski   return 1;
4421b8adde7SWilliam Kucharski }
4431b8adde7SWilliam Kucharski 
4441b8adde7SWilliam Kucharski /* Send the RRQ whose length is LEN.  */
4451b8adde7SWilliam Kucharski static int
send_rrq(void)4461b8adde7SWilliam Kucharski send_rrq (void)
4471b8adde7SWilliam Kucharski {
4481b8adde7SWilliam Kucharski   /* Initialize some variables.  */
4491b8adde7SWilliam Kucharski   retry = 0;
4501b8adde7SWilliam Kucharski   block = 0;
4511b8adde7SWilliam Kucharski   prevblock = 0;
4521b8adde7SWilliam Kucharski   packetsize = TFTP_DEFAULTSIZE_PACKET;
4531b8adde7SWilliam Kucharski   bcounter = 0;
4541b8adde7SWilliam Kucharski 
4551b8adde7SWilliam Kucharski   buf = (char *) FSYS_BUF;
4561b8adde7SWilliam Kucharski   buf_eof = 0;
4571b8adde7SWilliam Kucharski   buf_read = 0;
4581b8adde7SWilliam Kucharski   saved_filepos = 0;
4591b8adde7SWilliam Kucharski 
4601b8adde7SWilliam Kucharski   rx_qdrain();
4611b8adde7SWilliam Kucharski 
4621b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
4631b8adde7SWilliam Kucharski   grub_printf ("send_rrq ()\n");
4641b8adde7SWilliam Kucharski   {
4651b8adde7SWilliam Kucharski     int i;
4661b8adde7SWilliam Kucharski     char *p;
4671b8adde7SWilliam Kucharski 
4681b8adde7SWilliam Kucharski     for (i = 0, p = (char *) &tp; i < len; i++)
4691b8adde7SWilliam Kucharski       if (p[i] >= ' ' && p[i] <= '~')
4701b8adde7SWilliam Kucharski 	grub_putchar (p[i]);
4711b8adde7SWilliam Kucharski       else
4721b8adde7SWilliam Kucharski 	grub_printf ("\\%x", (unsigned) p[i]);
4731b8adde7SWilliam Kucharski 
4741b8adde7SWilliam Kucharski     grub_putchar ('\n');
4751b8adde7SWilliam Kucharski   }
4761b8adde7SWilliam Kucharski #endif
4771b8adde7SWilliam Kucharski   /* Send the packet.  */
4781b8adde7SWilliam Kucharski   return udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
4791b8adde7SWilliam Kucharski 		       TFTP_PORT, len, &tp);
4801b8adde7SWilliam Kucharski }
4811b8adde7SWilliam Kucharski 
4821b8adde7SWilliam Kucharski /* Mount the network drive. If the drive is ready, return one, otherwise
4831b8adde7SWilliam Kucharski    return zero.  */
4841b8adde7SWilliam Kucharski int
tftp_mount(void)4851b8adde7SWilliam Kucharski tftp_mount (void)
4861b8adde7SWilliam Kucharski {
4871b8adde7SWilliam Kucharski   /* Check if the current drive is the network drive.  */
4881b8adde7SWilliam Kucharski   if (current_drive != NETWORK_DRIVE)
4891b8adde7SWilliam Kucharski     return 0;
4901b8adde7SWilliam Kucharski 
4911b8adde7SWilliam Kucharski   /* If the drive is not initialized yet, abort.  */
4921b8adde7SWilliam Kucharski   if (! network_ready)
4931b8adde7SWilliam Kucharski     return 0;
4941b8adde7SWilliam Kucharski 
4951b8adde7SWilliam Kucharski   return 1;
4961b8adde7SWilliam Kucharski }
4971b8adde7SWilliam Kucharski 
4981b8adde7SWilliam Kucharski /* Read up to SIZE bytes, returned in ADDR.  */
4991b8adde7SWilliam Kucharski int
tftp_read(char * addr,int size)5001b8adde7SWilliam Kucharski tftp_read (char *addr, int size)
5011b8adde7SWilliam Kucharski {
5021b8adde7SWilliam Kucharski   /* How many bytes is read?  */
5031b8adde7SWilliam Kucharski   int ret = 0;
5041b8adde7SWilliam Kucharski 
5051b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
5061b8adde7SWilliam Kucharski   grub_printf ("tftp_read (0x%x, %d)\n", (int) addr, size);
5071b8adde7SWilliam Kucharski #endif
5081b8adde7SWilliam Kucharski 
5091b8adde7SWilliam Kucharski   if (use_bios_pxe)
5101b8adde7SWilliam Kucharski 	return (tftp_read_undi(addr, size));
5111b8adde7SWilliam Kucharski 
5121b8adde7SWilliam Kucharski   if (filepos < saved_filepos)
5131b8adde7SWilliam Kucharski     {
5141b8adde7SWilliam Kucharski       /* Uggh.. FILEPOS has been moved backwards. So reopen the file.  */
5151b8adde7SWilliam Kucharski       buf_read = 0;
5161b8adde7SWilliam Kucharski       buf_fill (1);
5171b8adde7SWilliam Kucharski       grub_memmove ((char *) &tp, (char *) &saved_tp, saved_len);
5181b8adde7SWilliam Kucharski       len = saved_len;
5191b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
5201b8adde7SWilliam Kucharski       {
5211b8adde7SWilliam Kucharski 	int i;
5221b8adde7SWilliam Kucharski 	grub_printf ("opcode = 0x%x, rrq = ", (unsigned long) tp.opcode);
5231b8adde7SWilliam Kucharski 	for (i = 0; i < TFTP_DEFAULTSIZE_PACKET; i++)
5241b8adde7SWilliam Kucharski 	  {
5251b8adde7SWilliam Kucharski 	    if (tp.u.rrq[i] >= ' ' && tp.u.rrq[i] <= '~')
5261b8adde7SWilliam Kucharski 	      grub_putchar (tp.u.rrq[i]);
5271b8adde7SWilliam Kucharski 	    else
5281b8adde7SWilliam Kucharski 	      grub_putchar ('*');
5291b8adde7SWilliam Kucharski 	  }
5301b8adde7SWilliam Kucharski 	grub_putchar ('\n');
5311b8adde7SWilliam Kucharski       }
5321b8adde7SWilliam Kucharski #endif
5331b8adde7SWilliam Kucharski 
5341b8adde7SWilliam Kucharski       if (! send_rrq ())
5351b8adde7SWilliam Kucharski 	{
5361b8adde7SWilliam Kucharski 	  errnum = ERR_WRITE;
5371b8adde7SWilliam Kucharski 	  return 0;
5381b8adde7SWilliam Kucharski 	}
5391b8adde7SWilliam Kucharski     }
5401b8adde7SWilliam Kucharski 
5411b8adde7SWilliam Kucharski   while (size > 0)
5421b8adde7SWilliam Kucharski     {
5431b8adde7SWilliam Kucharski       int amt = buf_read + saved_filepos - filepos;
5441b8adde7SWilliam Kucharski 
5451b8adde7SWilliam Kucharski       /* If the length that can be copied from the buffer is over the
5461b8adde7SWilliam Kucharski 	 requested size, cut it down.  */
5471b8adde7SWilliam Kucharski       if (amt > size)
5481b8adde7SWilliam Kucharski 	amt = size;
5491b8adde7SWilliam Kucharski 
5501b8adde7SWilliam Kucharski       if (amt > 0)
5511b8adde7SWilliam Kucharski 	{
5521b8adde7SWilliam Kucharski 	  /* Copy the buffer to the supplied memory space.  */
5531b8adde7SWilliam Kucharski 	  grub_memmove (addr, buf + filepos - saved_filepos, amt);
5541b8adde7SWilliam Kucharski 	  size -= amt;
5551b8adde7SWilliam Kucharski 	  addr += amt;
5561b8adde7SWilliam Kucharski 	  filepos += amt;
5571b8adde7SWilliam Kucharski 	  ret += amt;
5581b8adde7SWilliam Kucharski 
5591b8adde7SWilliam Kucharski 	  /* If the size of the empty space becomes small, move the unused
5601b8adde7SWilliam Kucharski 	     data forwards.  */
5611b8adde7SWilliam Kucharski 	  if (filepos - saved_filepos > FSYS_BUFLEN / 2)
5621b8adde7SWilliam Kucharski 	    {
5631b8adde7SWilliam Kucharski 	      grub_memmove (buf, buf + FSYS_BUFLEN / 2, FSYS_BUFLEN / 2);
5641b8adde7SWilliam Kucharski 	      buf_read -= FSYS_BUFLEN / 2;
5651b8adde7SWilliam Kucharski 	      saved_filepos += FSYS_BUFLEN / 2;
5661b8adde7SWilliam Kucharski 	    }
5671b8adde7SWilliam Kucharski 	}
5681b8adde7SWilliam Kucharski       else
5691b8adde7SWilliam Kucharski 	{
5701b8adde7SWilliam Kucharski 	  /* Skip the whole buffer.  */
5711b8adde7SWilliam Kucharski 	  saved_filepos += buf_read;
5721b8adde7SWilliam Kucharski 	  buf_read = 0;
5731b8adde7SWilliam Kucharski 	}
5741b8adde7SWilliam Kucharski 
5751b8adde7SWilliam Kucharski       /* Read the data.  */
5761b8adde7SWilliam Kucharski       if (size > 0 && ! buf_fill (0))
5771b8adde7SWilliam Kucharski 	{
5781b8adde7SWilliam Kucharski 	  errnum = ERR_READ;
5791b8adde7SWilliam Kucharski 	  return 0;
5801b8adde7SWilliam Kucharski 	}
5811b8adde7SWilliam Kucharski 
5821b8adde7SWilliam Kucharski       /* Sanity check.  */
5831b8adde7SWilliam Kucharski       if (size > 0 && buf_read == 0)
5841b8adde7SWilliam Kucharski 	{
5851b8adde7SWilliam Kucharski 	  errnum = ERR_READ;
5861b8adde7SWilliam Kucharski 	  return 0;
5871b8adde7SWilliam Kucharski 	}
5881b8adde7SWilliam Kucharski     }
5891b8adde7SWilliam Kucharski 
5901b8adde7SWilliam Kucharski   return ret;
5911b8adde7SWilliam Kucharski }
5921b8adde7SWilliam Kucharski 
5931b8adde7SWilliam Kucharski /* Check if the file DIRNAME really exists. Get the size and save it in
5941b8adde7SWilliam Kucharski    FILEMAX.  */
5951b8adde7SWilliam Kucharski int
tftp_dir(char * dirname)5961b8adde7SWilliam Kucharski tftp_dir (char *dirname)
5971b8adde7SWilliam Kucharski {
5981b8adde7SWilliam Kucharski   int ch;
5991b8adde7SWilliam Kucharski 
6001b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
6011b8adde7SWilliam Kucharski   grub_printf ("tftp_dir (%s)\n", dirname);
6021b8adde7SWilliam Kucharski #endif
6031b8adde7SWilliam Kucharski 
6041b8adde7SWilliam Kucharski   if (use_bios_pxe)
6051b8adde7SWilliam Kucharski 	return (tftp_dir_undi(dirname));
6061b8adde7SWilliam Kucharski 
6071b8adde7SWilliam Kucharski   /* In TFTP, there is no way to know what files exist.  */
6081b8adde7SWilliam Kucharski   if (print_possibilities)
6091b8adde7SWilliam Kucharski     return 1;
6101b8adde7SWilliam Kucharski 
6111b8adde7SWilliam Kucharski   /* Don't know the size yet.  */
6121b8adde7SWilliam Kucharski   filemax = -1;
6131b8adde7SWilliam Kucharski 
6141b8adde7SWilliam Kucharski  reopen:
6151b8adde7SWilliam Kucharski   /* Construct the TFTP request packet.  */
6161b8adde7SWilliam Kucharski   tp.opcode = htons (TFTP_RRQ);
6171b8adde7SWilliam Kucharski   /* Terminate the filename.  */
6181b8adde7SWilliam Kucharski   ch = nul_terminate (dirname);
6191b8adde7SWilliam Kucharski   /* Make the request string (octet, blksize and tsize).  */
6201b8adde7SWilliam Kucharski   len = (grub_sprintf ((char *) tp.u.rrq,
6211b8adde7SWilliam Kucharski 		       "%s%coctet%cblksize%c%d%ctsize%c0",
6221b8adde7SWilliam Kucharski 		       dirname, 0, 0, 0, TFTP_MAX_PACKET, 0, 0)
6231b8adde7SWilliam Kucharski 	 + sizeof (tp.ip) + sizeof (tp.udp) + sizeof (tp.opcode) + 1);
6241b8adde7SWilliam Kucharski   /* Restore the original DIRNAME.  */
6251b8adde7SWilliam Kucharski   dirname[grub_strlen (dirname)] = ch;
6261b8adde7SWilliam Kucharski   /* Save the TFTP packet so that we can reopen the file later.  */
6271b8adde7SWilliam Kucharski   grub_memmove ((char *) &saved_tp, (char *) &tp, len);
6281b8adde7SWilliam Kucharski   saved_len = len;
6291b8adde7SWilliam Kucharski   if (! send_rrq ())
6301b8adde7SWilliam Kucharski     {
6311b8adde7SWilliam Kucharski       errnum = ERR_WRITE;
6321b8adde7SWilliam Kucharski       return 0;
6331b8adde7SWilliam Kucharski     }
6341b8adde7SWilliam Kucharski 
6351b8adde7SWilliam Kucharski   /* Read the data.  */
6361b8adde7SWilliam Kucharski   if (! buf_fill (0))
6371b8adde7SWilliam Kucharski     {
6381b8adde7SWilliam Kucharski       errnum = ERR_FILE_NOT_FOUND;
6391b8adde7SWilliam Kucharski       return 0;
6401b8adde7SWilliam Kucharski     }
6411b8adde7SWilliam Kucharski 
6421b8adde7SWilliam Kucharski   if (filemax == -1)
6431b8adde7SWilliam Kucharski     {
6441b8adde7SWilliam Kucharski       /* The server doesn't support the "tsize" option, so we must read
6451b8adde7SWilliam Kucharski 	 the file twice...  */
6461b8adde7SWilliam Kucharski 
6471b8adde7SWilliam Kucharski       /* Zero the size of the file.  */
6481b8adde7SWilliam Kucharski       filemax = 0;
6491b8adde7SWilliam Kucharski       do
6501b8adde7SWilliam Kucharski 	{
6511b8adde7SWilliam Kucharski 	  /* Add the length of the downloaded data.  */
6521b8adde7SWilliam Kucharski 	  filemax += buf_read;
6531b8adde7SWilliam Kucharski 	  /* Reset the offset. Just discard the contents of the buffer.  */
6541b8adde7SWilliam Kucharski 	  buf_read = 0;
6551b8adde7SWilliam Kucharski 	  /* Read the data.  */
6561b8adde7SWilliam Kucharski 	  if (! buf_fill (0))
6571b8adde7SWilliam Kucharski 	    {
6581b8adde7SWilliam Kucharski 	      errnum = ERR_READ;
6591b8adde7SWilliam Kucharski 	      return 0;
6601b8adde7SWilliam Kucharski 	    }
6611b8adde7SWilliam Kucharski 	}
6621b8adde7SWilliam Kucharski       while (! buf_eof);
6631b8adde7SWilliam Kucharski 
6641b8adde7SWilliam Kucharski       /* Maybe a few amounts of data remains.  */
6651b8adde7SWilliam Kucharski       filemax += buf_read;
6661b8adde7SWilliam Kucharski 
6671b8adde7SWilliam Kucharski       /* Retry the open instruction.  */
6681b8adde7SWilliam Kucharski       goto reopen;
6691b8adde7SWilliam Kucharski     }
6701b8adde7SWilliam Kucharski 
6711b8adde7SWilliam Kucharski   return 1;
6721b8adde7SWilliam Kucharski }
6731b8adde7SWilliam Kucharski 
6741b8adde7SWilliam Kucharski /* Close the file.  */
6751b8adde7SWilliam Kucharski void
tftp_close(void)6761b8adde7SWilliam Kucharski tftp_close (void)
6771b8adde7SWilliam Kucharski {
6781b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
6791b8adde7SWilliam Kucharski   grub_printf ("tftp_close ()\n");
6801b8adde7SWilliam Kucharski #endif
6811b8adde7SWilliam Kucharski 
6821b8adde7SWilliam Kucharski   if (use_bios_pxe) {
6831b8adde7SWilliam Kucharski 	tftp_close_undi();
6841b8adde7SWilliam Kucharski 	return;
6851b8adde7SWilliam Kucharski   }
6861b8adde7SWilliam Kucharski 
6871b8adde7SWilliam Kucharski   buf_read = 0;
6881b8adde7SWilliam Kucharski   buf_fill (1);
6891b8adde7SWilliam Kucharski }
6901b8adde7SWilliam Kucharski 
6911b8adde7SWilliam Kucharski /* tftp implementation using BIOS established PXE stack */
6921b8adde7SWilliam Kucharski 
tftp_file_read_undi(const char * name,int (* fnc)(unsigned char *,unsigned int,unsigned int,int))6931b8adde7SWilliam Kucharski static int tftp_file_read_undi(const char *name,
6941b8adde7SWilliam Kucharski     int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
6951b8adde7SWilliam Kucharski {
6961b8adde7SWilliam Kucharski 	int rc;
6971b8adde7SWilliam Kucharski 	uint16_t len;
6981b8adde7SWilliam Kucharski 
6991b8adde7SWilliam Kucharski 	buf = (char *)&nic.packet;
7001b8adde7SWilliam Kucharski 	/* open tftp session */
7011b8adde7SWilliam Kucharski 	if (eb_pxenv_tftp_open(name, arptable[ARP_SERVER].ipaddr,
7021b8adde7SWilliam Kucharski 	    arptable[ARP_GATEWAY].ipaddr, &packetsize) == 0)
7031b8adde7SWilliam Kucharski 		return (0);
7041b8adde7SWilliam Kucharski 
7051b8adde7SWilliam Kucharski 	/* read blocks and invoke fnc for each block */
7061b8adde7SWilliam Kucharski 	for (;;) {
7071b8adde7SWilliam Kucharski 		rc = eb_pxenv_tftp_read(buf, &len);
7081b8adde7SWilliam Kucharski 		if (rc == 0)
7091b8adde7SWilliam Kucharski 			break;
7101b8adde7SWilliam Kucharski 		rc = fnc(buf, ++block, len, len < packetsize);
7111b8adde7SWilliam Kucharski 		if (rc <= 0 || len < packetsize)
7121b8adde7SWilliam Kucharski 			break;
7131b8adde7SWilliam Kucharski 	}
7141b8adde7SWilliam Kucharski 
7151b8adde7SWilliam Kucharski 	(void) eb_pxenv_tftp_close();
7161b8adde7SWilliam Kucharski 	return (rc > 0 ? 1 : 0);
7171b8adde7SWilliam Kucharski }
7181b8adde7SWilliam Kucharski 
7191b8adde7SWilliam Kucharski /* Fill the buffer by reading the data via the TFTP protocol.  */
7201b8adde7SWilliam Kucharski static int
buf_fill_undi(int abort)7211b8adde7SWilliam Kucharski buf_fill_undi(int abort)
7221b8adde7SWilliam Kucharski {
7231b8adde7SWilliam Kucharski 	int rc;
7241b8adde7SWilliam Kucharski 	uint8_t *tmpbuf;
7251b8adde7SWilliam Kucharski 
7261b8adde7SWilliam Kucharski 	while (! buf_eof && (buf_read + packetsize <= FSYS_BUFLEN)) {
7271b8adde7SWilliam Kucharski 		poll_interruptions();
7281b8adde7SWilliam Kucharski 		if (user_abort)
7291b8adde7SWilliam Kucharski 			return 0;
7301b8adde7SWilliam Kucharski 		if (abort) {
7311b8adde7SWilliam Kucharski 			buf_eof = 1;
7321b8adde7SWilliam Kucharski 			break;
7331b8adde7SWilliam Kucharski 		}
7341b8adde7SWilliam Kucharski 
7351b8adde7SWilliam Kucharski 		if (eb_pxenv_tftp_read(buf + buf_read, &len) == 0)
7361b8adde7SWilliam Kucharski 			return (0);
7371b8adde7SWilliam Kucharski 
7381b8adde7SWilliam Kucharski 		buf_read += len;
7391b8adde7SWilliam Kucharski 
7401b8adde7SWilliam Kucharski 		/* End of data.  */
7411b8adde7SWilliam Kucharski 		if (len < packetsize)
7421b8adde7SWilliam Kucharski 			buf_eof = 1;
7431b8adde7SWilliam Kucharski 	}
7441b8adde7SWilliam Kucharski 	return 1;
7451b8adde7SWilliam Kucharski }
7461b8adde7SWilliam Kucharski 
7471b8adde7SWilliam Kucharski static void
tftp_reopen_undi(void)7481b8adde7SWilliam Kucharski tftp_reopen_undi(void)
7491b8adde7SWilliam Kucharski {
7501b8adde7SWilliam Kucharski 	tftp_close();
7511b8adde7SWilliam Kucharski 	(void) eb_pxenv_tftp_open(saved_name, arptable[ARP_SERVER].ipaddr,
7521b8adde7SWilliam Kucharski 	    arptable[ARP_GATEWAY].ipaddr, &packetsize);
7531b8adde7SWilliam Kucharski 
7541b8adde7SWilliam Kucharski 	buf_eof = 0;
7551b8adde7SWilliam Kucharski 	buf_read = 0;
7561b8adde7SWilliam Kucharski 	saved_filepos = 0;
7571b8adde7SWilliam Kucharski }
7581b8adde7SWilliam Kucharski 
7591b8adde7SWilliam Kucharski /* Read up to SIZE bytes, returned in ADDR.  */
7601b8adde7SWilliam Kucharski static int
tftp_read_undi(char * addr,int size)7611b8adde7SWilliam Kucharski tftp_read_undi(char *addr, int size)
7621b8adde7SWilliam Kucharski {
7631b8adde7SWilliam Kucharski 	int ret = 0;
7641b8adde7SWilliam Kucharski 
7651b8adde7SWilliam Kucharski 	if (filepos < saved_filepos) {
7661b8adde7SWilliam Kucharski 		/* Uggh.. FILEPOS has been moved backwards. reopen the file. */
7671b8adde7SWilliam Kucharski 		tftp_reopen_undi();
7681b8adde7SWilliam Kucharski 	}
7691b8adde7SWilliam Kucharski 
7701b8adde7SWilliam Kucharski 	while (size > 0) {
7711b8adde7SWilliam Kucharski 		int amt = buf_read + saved_filepos - filepos;
7721b8adde7SWilliam Kucharski 
7731b8adde7SWilliam Kucharski 		/* If the length that can be copied from the buffer is over
7741b8adde7SWilliam Kucharski 		   the requested size, cut it down. */
7751b8adde7SWilliam Kucharski 		if (amt > size)
7761b8adde7SWilliam Kucharski 			amt = size;
7771b8adde7SWilliam Kucharski 
7781b8adde7SWilliam Kucharski 		if (amt > 0) {
7791b8adde7SWilliam Kucharski 			/* Copy the buffer to the supplied memory space.  */
7801b8adde7SWilliam Kucharski 			grub_memmove (addr, buf + filepos - saved_filepos, amt);
7811b8adde7SWilliam Kucharski 			size -= amt;
7821b8adde7SWilliam Kucharski 			addr += amt;
7831b8adde7SWilliam Kucharski 			filepos += amt;
7841b8adde7SWilliam Kucharski 			ret += amt;
7851b8adde7SWilliam Kucharski 
7861b8adde7SWilliam Kucharski 			/* If the size of the empty space becomes small,
7871b8adde7SWilliam Kucharski 			 * move the unused data forwards.
7881b8adde7SWilliam Kucharski 			 */
7891b8adde7SWilliam Kucharski 			if (filepos - saved_filepos > FSYS_BUFLEN / 2) {
7901b8adde7SWilliam Kucharski 				grub_memmove (buf, buf + FSYS_BUFLEN / 2,
7911b8adde7SWilliam Kucharski 				    FSYS_BUFLEN / 2);
7921b8adde7SWilliam Kucharski 				buf_read -= FSYS_BUFLEN / 2;
7931b8adde7SWilliam Kucharski 				saved_filepos += FSYS_BUFLEN / 2;
7941b8adde7SWilliam Kucharski 			}
7951b8adde7SWilliam Kucharski 		} else {
7961b8adde7SWilliam Kucharski 			/* Skip the whole buffer.  */
7971b8adde7SWilliam Kucharski 			saved_filepos += buf_read;
7981b8adde7SWilliam Kucharski 			buf_read = 0;
7991b8adde7SWilliam Kucharski 		}
8001b8adde7SWilliam Kucharski 
8011b8adde7SWilliam Kucharski 		/* Read the data.  */
8021b8adde7SWilliam Kucharski 		if (size > 0 && ! buf_fill (0)) {
8031b8adde7SWilliam Kucharski 			errnum = ERR_READ;
8041b8adde7SWilliam Kucharski 			return 0;
8051b8adde7SWilliam Kucharski 		}
8061b8adde7SWilliam Kucharski 
8071b8adde7SWilliam Kucharski 		/* Sanity check.  */
8081b8adde7SWilliam Kucharski 		if (size > 0 && buf_read == 0) {
8091b8adde7SWilliam Kucharski 			errnum = ERR_READ;
8101b8adde7SWilliam Kucharski 			return 0;
8111b8adde7SWilliam Kucharski 		}
8121b8adde7SWilliam Kucharski 	}
8131b8adde7SWilliam Kucharski 
8141b8adde7SWilliam Kucharski 	return ret;
8151b8adde7SWilliam Kucharski }
8161b8adde7SWilliam Kucharski 
8171b8adde7SWilliam Kucharski static int
tftp_dir_undi(char * dirname)8181b8adde7SWilliam Kucharski tftp_dir_undi(char *dirname)
8191b8adde7SWilliam Kucharski {
8201b8adde7SWilliam Kucharski 	int rc, ch;
8211b8adde7SWilliam Kucharski 	uint16_t len;
8221b8adde7SWilliam Kucharski 
8231b8adde7SWilliam Kucharski 	/* In TFTP, there is no way to know what files exist.  */
8241b8adde7SWilliam Kucharski 	if (print_possibilities)
8251b8adde7SWilliam Kucharski 		return 1;
8261b8adde7SWilliam Kucharski 
8271b8adde7SWilliam Kucharski 	/* name may be space terminated */
8281b8adde7SWilliam Kucharski 	ch = nul_terminate(dirname);
8291b8adde7SWilliam Kucharski 	saved_name = (char *)&saved_tp;
8301b8adde7SWilliam Kucharski 	sprintf(saved_name, "%s", dirname);
8311b8adde7SWilliam Kucharski 
8321b8adde7SWilliam Kucharski   	/* Restore the original dirname */
8331b8adde7SWilliam Kucharski 	dirname[grub_strlen (dirname)] = ch;
8341b8adde7SWilliam Kucharski 
8351b8adde7SWilliam Kucharski 	/* get the file size; must call before tftp_open */
8361b8adde7SWilliam Kucharski 	rc = eb_pxenv_tftp_get_fsize(saved_name, arptable[ARP_SERVER].ipaddr,
8371b8adde7SWilliam Kucharski 	    arptable[ARP_GATEWAY].ipaddr, &filemax);
8381b8adde7SWilliam Kucharski 
8391b8adde7SWilliam Kucharski 	/* open tftp session */
8401b8adde7SWilliam Kucharski 	if (eb_pxenv_tftp_open(saved_name, arptable[ARP_SERVER].ipaddr,
8411b8adde7SWilliam Kucharski 	    arptable[ARP_GATEWAY].ipaddr, &packetsize) == 0)
8421b8adde7SWilliam Kucharski 		return (0);
8431b8adde7SWilliam Kucharski 
8441b8adde7SWilliam Kucharski 	buf = (char *) FSYS_BUF;
8451b8adde7SWilliam Kucharski 	buf_eof = 0;
8461b8adde7SWilliam Kucharski 	buf_read = 0;
8471b8adde7SWilliam Kucharski 	saved_filepos = 0;
8481b8adde7SWilliam Kucharski 
8491b8adde7SWilliam Kucharski 	if (rc == 0) {
8501b8adde7SWilliam Kucharski 		/* Read the entire file to get filemax */
8511b8adde7SWilliam Kucharski 		filemax = 0;
8521b8adde7SWilliam Kucharski 		do {
8531b8adde7SWilliam Kucharski 			/* Add the length of the downloaded data.  */
8541b8adde7SWilliam Kucharski 			filemax += buf_read;
8551b8adde7SWilliam Kucharski 			buf_read = 0;
8561b8adde7SWilliam Kucharski 			if (! buf_fill (0)) {
8571b8adde7SWilliam Kucharski 				errnum = ERR_READ;
8581b8adde7SWilliam Kucharski 				return 0;
8591b8adde7SWilliam Kucharski 			}
8601b8adde7SWilliam Kucharski 		} while (! buf_eof);
8611b8adde7SWilliam Kucharski 
8621b8adde7SWilliam Kucharski 		/* Maybe a few amounts of data remains.  */
8631b8adde7SWilliam Kucharski 		filemax += buf_read;
8641b8adde7SWilliam Kucharski 
8651b8adde7SWilliam Kucharski 		tftp_reopen_undi(); /* reopen file to read from beginning */
8661b8adde7SWilliam Kucharski 	}
8671b8adde7SWilliam Kucharski 
8681b8adde7SWilliam Kucharski 	return (1);
8691b8adde7SWilliam Kucharski }
8701b8adde7SWilliam Kucharski 
8711b8adde7SWilliam Kucharski static void
tftp_close_undi(void)8721b8adde7SWilliam Kucharski tftp_close_undi(void)
8731b8adde7SWilliam Kucharski {
8741b8adde7SWilliam Kucharski 	buf_read = 0;
8751b8adde7SWilliam Kucharski 	buf_fill (1);
8761b8adde7SWilliam Kucharski 	(void) eb_pxenv_tftp_close();
8771b8adde7SWilliam Kucharski }
878