xref: /onnv-gate/usr/src/grub/grub-0.97/netboot/fsys_tftp.c (revision 10708:c68a5b74788e)
18044SWilliam.Kucharski@Sun.COM /*
28044SWilliam.Kucharski@Sun.COM  *  GRUB  --  GRand Unified Bootloader
38044SWilliam.Kucharski@Sun.COM  *  Copyright (C) 2000,2001,2002,2004  Free Software Foundation, Inc.
48044SWilliam.Kucharski@Sun.COM  *
58044SWilliam.Kucharski@Sun.COM  *  This program is free software; you can redistribute it and/or modify
68044SWilliam.Kucharski@Sun.COM  *  it under the terms of the GNU General Public License as published by
78044SWilliam.Kucharski@Sun.COM  *  the Free Software Foundation; either version 2 of the License, or
88044SWilliam.Kucharski@Sun.COM  *  (at your option) any later version.
98044SWilliam.Kucharski@Sun.COM  *
108044SWilliam.Kucharski@Sun.COM  *  This program is distributed in the hope that it will be useful,
118044SWilliam.Kucharski@Sun.COM  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
128044SWilliam.Kucharski@Sun.COM  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
138044SWilliam.Kucharski@Sun.COM  *  GNU General Public License for more details.
148044SWilliam.Kucharski@Sun.COM  *
158044SWilliam.Kucharski@Sun.COM  *  You should have received a copy of the GNU General Public License
168044SWilliam.Kucharski@Sun.COM  *  along with this program; if not, write to the Free Software
178044SWilliam.Kucharski@Sun.COM  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
188044SWilliam.Kucharski@Sun.COM  */
198044SWilliam.Kucharski@Sun.COM 
208044SWilliam.Kucharski@Sun.COM /* Based on "src/main.c" in etherboot-4.5.8.  */
218044SWilliam.Kucharski@Sun.COM /**************************************************************************
228044SWilliam.Kucharski@Sun.COM ETHERBOOT -  BOOTP/TFTP Bootstrap Program
238044SWilliam.Kucharski@Sun.COM 
248044SWilliam.Kucharski@Sun.COM Author: Martin Renters
258044SWilliam.Kucharski@Sun.COM   Date: Dec/93
268044SWilliam.Kucharski@Sun.COM 
278044SWilliam.Kucharski@Sun.COM **************************************************************************/
288044SWilliam.Kucharski@Sun.COM 
298044SWilliam.Kucharski@Sun.COM /* #define TFTP_DEBUG	1 */
308044SWilliam.Kucharski@Sun.COM 
318044SWilliam.Kucharski@Sun.COM #include <filesys.h>
328044SWilliam.Kucharski@Sun.COM #include <shared.h>
338044SWilliam.Kucharski@Sun.COM 
348044SWilliam.Kucharski@Sun.COM #include "grub.h"
358044SWilliam.Kucharski@Sun.COM #include "tftp.h"
368044SWilliam.Kucharski@Sun.COM #include "nic.h"
378044SWilliam.Kucharski@Sun.COM 
388044SWilliam.Kucharski@Sun.COM static int tftp_file_read_undi(const char *name,
398044SWilliam.Kucharski@Sun.COM     int (*fnc)(unsigned char *, unsigned int, unsigned int, int));
408044SWilliam.Kucharski@Sun.COM static int tftp_read_undi(char *addr, int size);
418044SWilliam.Kucharski@Sun.COM static int tftp_dir_undi(char *dirname);
428044SWilliam.Kucharski@Sun.COM static void tftp_close_undi(void);
438044SWilliam.Kucharski@Sun.COM static int buf_fill_undi(int abort);
448044SWilliam.Kucharski@Sun.COM 
458044SWilliam.Kucharski@Sun.COM extern int use_bios_pxe;
468044SWilliam.Kucharski@Sun.COM 
478044SWilliam.Kucharski@Sun.COM static int retry;
488044SWilliam.Kucharski@Sun.COM static unsigned short iport = 2000;
498044SWilliam.Kucharski@Sun.COM static unsigned short oport = 0;
508044SWilliam.Kucharski@Sun.COM static unsigned short block, prevblock;
518044SWilliam.Kucharski@Sun.COM static int bcounter;
528044SWilliam.Kucharski@Sun.COM static struct tftp_t tp, saved_tp;
538044SWilliam.Kucharski@Sun.COM static int packetsize;
548044SWilliam.Kucharski@Sun.COM static int buf_eof, buf_read;
558044SWilliam.Kucharski@Sun.COM static int saved_filepos;
568044SWilliam.Kucharski@Sun.COM static unsigned short len, saved_len;
578044SWilliam.Kucharski@Sun.COM static char *buf, *saved_name;
588044SWilliam.Kucharski@Sun.COM 
598044SWilliam.Kucharski@Sun.COM /**
608044SWilliam.Kucharski@Sun.COM  * tftp_read
618044SWilliam.Kucharski@Sun.COM  *
628044SWilliam.Kucharski@Sun.COM  * Read file with _name_, data handled by _fnc_. In fact, grub never
638044SWilliam.Kucharski@Sun.COM  * use it, we just use it to read dhcp config file.
648044SWilliam.Kucharski@Sun.COM  */
await_tftp(int ival,void * ptr __unused,unsigned short ptype __unused,struct iphdr * ip,struct udphdr * udp)658044SWilliam.Kucharski@Sun.COM static int await_tftp(int ival, void *ptr __unused,
668044SWilliam.Kucharski@Sun.COM 		      unsigned short ptype __unused, struct iphdr *ip,
678044SWilliam.Kucharski@Sun.COM 		      struct udphdr *udp)
688044SWilliam.Kucharski@Sun.COM {
698044SWilliam.Kucharski@Sun.COM 	static int tftp_count = 0;
708044SWilliam.Kucharski@Sun.COM 
718044SWilliam.Kucharski@Sun.COM 	if (!udp) {
728044SWilliam.Kucharski@Sun.COM 		return 0;
738044SWilliam.Kucharski@Sun.COM 	}
748044SWilliam.Kucharski@Sun.COM 	if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
758044SWilliam.Kucharski@Sun.COM 		return 0;
768044SWilliam.Kucharski@Sun.COM 	if (ntohs(udp->dest) != ival)
778044SWilliam.Kucharski@Sun.COM 		return 0;
788044SWilliam.Kucharski@Sun.COM 	tftp_count++;	/* show progress */
798044SWilliam.Kucharski@Sun.COM 	if ((tftp_count % 1000) == 0)
808044SWilliam.Kucharski@Sun.COM 		printf(".");
818044SWilliam.Kucharski@Sun.COM 	return 1;
828044SWilliam.Kucharski@Sun.COM }
838044SWilliam.Kucharski@Sun.COM 
tftp_file_read(const char * name,int (* fnc)(unsigned char *,unsigned int,unsigned int,int))848044SWilliam.Kucharski@Sun.COM int tftp_file_read(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
858044SWilliam.Kucharski@Sun.COM {
868044SWilliam.Kucharski@Sun.COM 	struct tftpreq_t tp;
878044SWilliam.Kucharski@Sun.COM 	struct tftp_t  *tr;
888044SWilliam.Kucharski@Sun.COM 	int		rc;
898044SWilliam.Kucharski@Sun.COM 
908044SWilliam.Kucharski@Sun.COM 	if (use_bios_pxe)
918044SWilliam.Kucharski@Sun.COM 		return (tftp_file_read_undi(name, fnc));
928044SWilliam.Kucharski@Sun.COM 
938044SWilliam.Kucharski@Sun.COM 	retry = 0;
948044SWilliam.Kucharski@Sun.COM 	block = 0;
958044SWilliam.Kucharski@Sun.COM 	prevblock = 0;
968044SWilliam.Kucharski@Sun.COM 	bcounter = 0;
978044SWilliam.Kucharski@Sun.COM 
988044SWilliam.Kucharski@Sun.COM 
998044SWilliam.Kucharski@Sun.COM 	rx_qdrain();
1008044SWilliam.Kucharski@Sun.COM 
1018044SWilliam.Kucharski@Sun.COM 	tp.opcode = htons(TFTP_RRQ);
1028044SWilliam.Kucharski@Sun.COM 	/* Warning: the following assumes the layout of bootp_t.
1038044SWilliam.Kucharski@Sun.COM 	   But that's fixed by the IP, UDP and BOOTP specs. */
1048044SWilliam.Kucharski@Sun.COM 	len = sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) +
1058044SWilliam.Kucharski@Sun.COM 		sprintf((char *)tp.u.rrq, "%s%coctet%cblksize%c%d",
1068044SWilliam.Kucharski@Sun.COM 		name, 0, 0, 0, TFTP_MAX_PACKET) + 1;
1078044SWilliam.Kucharski@Sun.COM 	if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
1088044SWilliam.Kucharski@Sun.COM 			  TFTP_PORT, len, &tp))
1098044SWilliam.Kucharski@Sun.COM 		return (0);
1108044SWilliam.Kucharski@Sun.COM 	for (;;)
1118044SWilliam.Kucharski@Sun.COM 	{
1128044SWilliam.Kucharski@Sun.COM 		long timeout;
1138044SWilliam.Kucharski@Sun.COM #ifdef	CONGESTED
1148044SWilliam.Kucharski@Sun.COM 		timeout = rfc2131_sleep_interval(block?TFTP_REXMT: TIMEOUT, retry);
1158044SWilliam.Kucharski@Sun.COM #else
1168044SWilliam.Kucharski@Sun.COM 		timeout = rfc2131_sleep_interval(TIMEOUT, retry);
1178044SWilliam.Kucharski@Sun.COM #endif
1188044SWilliam.Kucharski@Sun.COM 		if (!await_reply(await_tftp, iport, NULL, timeout))
1198044SWilliam.Kucharski@Sun.COM 		{
1208044SWilliam.Kucharski@Sun.COM 			if (!block && retry++ < MAX_TFTP_RETRIES)
1218044SWilliam.Kucharski@Sun.COM 			{	/* maybe initial request was lost */
1228044SWilliam.Kucharski@Sun.COM 				if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
1238044SWilliam.Kucharski@Sun.COM 						  ++iport, TFTP_PORT, len, &tp))
1248044SWilliam.Kucharski@Sun.COM 					return (0);
1258044SWilliam.Kucharski@Sun.COM 				continue;
1268044SWilliam.Kucharski@Sun.COM 			}
1278044SWilliam.Kucharski@Sun.COM #ifdef	CONGESTED
1288044SWilliam.Kucharski@Sun.COM 			if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
1298044SWilliam.Kucharski@Sun.COM 			{	/* we resend our last ack */
1308044SWilliam.Kucharski@Sun.COM #ifdef	MDEBUG
1318044SWilliam.Kucharski@Sun.COM 				printf("<REXMT>\n");
1328044SWilliam.Kucharski@Sun.COM #endif
1338044SWilliam.Kucharski@Sun.COM 				udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
1348044SWilliam.Kucharski@Sun.COM 					     iport, oport,
1358044SWilliam.Kucharski@Sun.COM 					     TFTP_MIN_PACKET, &tp);
1368044SWilliam.Kucharski@Sun.COM 				continue;
1378044SWilliam.Kucharski@Sun.COM 			}
1388044SWilliam.Kucharski@Sun.COM #endif
1398044SWilliam.Kucharski@Sun.COM 			break;	/* timeout */
1408044SWilliam.Kucharski@Sun.COM 		}
1418044SWilliam.Kucharski@Sun.COM 		tr = (struct tftp_t *)&nic.packet[ETH_HLEN];
1428044SWilliam.Kucharski@Sun.COM 		if (tr->opcode == ntohs(TFTP_ERROR))
1438044SWilliam.Kucharski@Sun.COM 		{
1448044SWilliam.Kucharski@Sun.COM 			printf("TFTP error %d (%s)\n",
1458044SWilliam.Kucharski@Sun.COM 			       ntohs(tr->u.err.errcode),
1468044SWilliam.Kucharski@Sun.COM 			       tr->u.err.errmsg);
1478044SWilliam.Kucharski@Sun.COM 			break;
1488044SWilliam.Kucharski@Sun.COM 		}
1498044SWilliam.Kucharski@Sun.COM 
1508044SWilliam.Kucharski@Sun.COM 		if (tr->opcode == ntohs(TFTP_OACK)) {
1518044SWilliam.Kucharski@Sun.COM 			char *p = tr->u.oack.data, *e;
1528044SWilliam.Kucharski@Sun.COM 
1538044SWilliam.Kucharski@Sun.COM 			if (prevblock)		/* shouldn't happen */
1548044SWilliam.Kucharski@Sun.COM 				continue;	/* ignore it */
1558044SWilliam.Kucharski@Sun.COM 			len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2;
1568044SWilliam.Kucharski@Sun.COM 			if (len > TFTP_MAX_PACKET)
1578044SWilliam.Kucharski@Sun.COM 				goto noak;
1588044SWilliam.Kucharski@Sun.COM 			e = p + len;
1598044SWilliam.Kucharski@Sun.COM 			while (*p != '\0' && p < e) {
1608044SWilliam.Kucharski@Sun.COM /* 				if (!strcasecmp("blksize", p)) { */
1618044SWilliam.Kucharski@Sun.COM 				if (!grub_strcmp("blksize", p)) {
1628044SWilliam.Kucharski@Sun.COM 					p += 8;
1638044SWilliam.Kucharski@Sun.COM /* 					if ((packetsize = strtoul(p, &p, 10)) < */
1648044SWilliam.Kucharski@Sun.COM 					if ((packetsize = getdec(&p)) < TFTP_DEFAULTSIZE_PACKET)
1658044SWilliam.Kucharski@Sun.COM 						goto noak;
1668044SWilliam.Kucharski@Sun.COM 					while (p < e && *p) p++;
1678044SWilliam.Kucharski@Sun.COM 					if (p < e)
1688044SWilliam.Kucharski@Sun.COM 						p++;
1698044SWilliam.Kucharski@Sun.COM 				}
1708044SWilliam.Kucharski@Sun.COM 				else {
1718044SWilliam.Kucharski@Sun.COM 				noak:
1728044SWilliam.Kucharski@Sun.COM 					tp.opcode = htons(TFTP_ERROR);
1738044SWilliam.Kucharski@Sun.COM 					tp.u.err.errcode = 8;
1748044SWilliam.Kucharski@Sun.COM /*
1758044SWilliam.Kucharski@Sun.COM  *	Warning: the following assumes the layout of bootp_t.
1768044SWilliam.Kucharski@Sun.COM  *	But that's fixed by the IP, UDP and BOOTP specs.
1778044SWilliam.Kucharski@Sun.COM  */
1788044SWilliam.Kucharski@Sun.COM 					len = sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) + sizeof(tp.u.err.errcode) +
1798044SWilliam.Kucharski@Sun.COM /*
1808044SWilliam.Kucharski@Sun.COM  *	Normally bad form to omit the format string, but in this case
1818044SWilliam.Kucharski@Sun.COM  *	the string we are copying from is fixed. sprintf is just being
1828044SWilliam.Kucharski@Sun.COM  *	used as a strcpy and strlen.
1838044SWilliam.Kucharski@Sun.COM  */
1848044SWilliam.Kucharski@Sun.COM 						sprintf((char *)tp.u.err.errmsg,
1858044SWilliam.Kucharski@Sun.COM 						"RFC1782 error") + 1;
1868044SWilliam.Kucharski@Sun.COM 					udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
1878044SWilliam.Kucharski@Sun.COM 						     iport, ntohs(tr->udp.src),
1888044SWilliam.Kucharski@Sun.COM 						     len, &tp);
1898044SWilliam.Kucharski@Sun.COM 					return (0);
1908044SWilliam.Kucharski@Sun.COM 				}
1918044SWilliam.Kucharski@Sun.COM 			}
1928044SWilliam.Kucharski@Sun.COM 			if (p > e)
1938044SWilliam.Kucharski@Sun.COM 				goto noak;
1948044SWilliam.Kucharski@Sun.COM 			block = tp.u.ack.block = 0; /* this ensures, that */
1958044SWilliam.Kucharski@Sun.COM 						/* the packet does not get */
1968044SWilliam.Kucharski@Sun.COM 						/* processed as data! */
1978044SWilliam.Kucharski@Sun.COM 		}
1988044SWilliam.Kucharski@Sun.COM 		else if (tr->opcode == htons(TFTP_DATA)) {
1998044SWilliam.Kucharski@Sun.COM 			len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
2008044SWilliam.Kucharski@Sun.COM 			if (len > packetsize)	/* shouldn't happen */
2018044SWilliam.Kucharski@Sun.COM 				continue;	/* ignore it */
2028044SWilliam.Kucharski@Sun.COM 			block = ntohs(tp.u.ack.block = tr->u.data.block); }
2038044SWilliam.Kucharski@Sun.COM 		else {/* neither TFTP_OACK nor TFTP_DATA */
2048044SWilliam.Kucharski@Sun.COM 			break;
2058044SWilliam.Kucharski@Sun.COM 		}
2068044SWilliam.Kucharski@Sun.COM 
2078044SWilliam.Kucharski@Sun.COM 		if ((block || bcounter) && (block != (unsigned short)(prevblock+1))) {
2088044SWilliam.Kucharski@Sun.COM 			/* Block order should be continuous */
2098044SWilliam.Kucharski@Sun.COM 			tp.u.ack.block = htons(block = prevblock);
2108044SWilliam.Kucharski@Sun.COM 		}
2118044SWilliam.Kucharski@Sun.COM 		tp.opcode = htons(TFTP_ACK);
2128044SWilliam.Kucharski@Sun.COM 		oport = ntohs(tr->udp.src);
2138044SWilliam.Kucharski@Sun.COM 		udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, iport,
2148044SWilliam.Kucharski@Sun.COM 			     oport, TFTP_MIN_PACKET, &tp);	/* ack */
2158044SWilliam.Kucharski@Sun.COM 		if ((unsigned short)(block-prevblock) != 1) {
2168044SWilliam.Kucharski@Sun.COM 			/* Retransmission or OACK, don't process via callback
2178044SWilliam.Kucharski@Sun.COM 			 * and don't change the value of prevblock.  */
2188044SWilliam.Kucharski@Sun.COM 			continue;
2198044SWilliam.Kucharski@Sun.COM 		}
2208044SWilliam.Kucharski@Sun.COM 		prevblock = block;
2218044SWilliam.Kucharski@Sun.COM 		retry = 0;	/* It's the right place to zero the timer? */
2228044SWilliam.Kucharski@Sun.COM 		if ((rc = fnc(tr->u.data.download,
2238044SWilliam.Kucharski@Sun.COM 			      ++bcounter, len, len < packetsize)) <= 0)
2248044SWilliam.Kucharski@Sun.COM 			return(rc);
2258044SWilliam.Kucharski@Sun.COM 		if (len < packetsize) {	/* End of data --- fnc should not have returned */
2268044SWilliam.Kucharski@Sun.COM 			printf("tftp download complete, but\n");
2278044SWilliam.Kucharski@Sun.COM 			return (1);
2288044SWilliam.Kucharski@Sun.COM 		}
2298044SWilliam.Kucharski@Sun.COM 	}
2308044SWilliam.Kucharski@Sun.COM 	return (0);
2318044SWilliam.Kucharski@Sun.COM }
2328044SWilliam.Kucharski@Sun.COM 
2338044SWilliam.Kucharski@Sun.COM /* Fill the buffer by receiving the data via the TFTP protocol.  */
2348044SWilliam.Kucharski@Sun.COM static int
buf_fill(int abort)2358044SWilliam.Kucharski@Sun.COM buf_fill (int abort)
2368044SWilliam.Kucharski@Sun.COM {
2378044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
2388044SWilliam.Kucharski@Sun.COM   grub_printf ("buf_fill (%d)\n", abort);
2398044SWilliam.Kucharski@Sun.COM #endif
2408044SWilliam.Kucharski@Sun.COM 
2418044SWilliam.Kucharski@Sun.COM   if (use_bios_pxe)
2428044SWilliam.Kucharski@Sun.COM 	return (buf_fill_undi(abort));
2438044SWilliam.Kucharski@Sun.COM 
2448044SWilliam.Kucharski@Sun.COM   while (! buf_eof && (buf_read + packetsize <= FSYS_BUFLEN))
2458044SWilliam.Kucharski@Sun.COM     {
2468044SWilliam.Kucharski@Sun.COM       struct tftp_t *tr;
2478044SWilliam.Kucharski@Sun.COM       long timeout;
2488044SWilliam.Kucharski@Sun.COM 
2498044SWilliam.Kucharski@Sun.COM #ifdef CONGESTED
2508044SWilliam.Kucharski@Sun.COM       timeout = rfc2131_sleep_interval (block ? TFTP_REXMT : TIMEOUT, retry);
2518044SWilliam.Kucharski@Sun.COM #else
2528044SWilliam.Kucharski@Sun.COM       timeout = rfc2131_sleep_interval (TIMEOUT, retry);
2538044SWilliam.Kucharski@Sun.COM #endif
2548044SWilliam.Kucharski@Sun.COM 
2558044SWilliam.Kucharski@Sun.COM       if (! await_reply (await_tftp, iport, NULL, timeout))
2568044SWilliam.Kucharski@Sun.COM 	{
2578044SWilliam.Kucharski@Sun.COM 	  if (user_abort)
2588044SWilliam.Kucharski@Sun.COM 	    return 0;
2598044SWilliam.Kucharski@Sun.COM 
2608044SWilliam.Kucharski@Sun.COM 	  if (! block && retry++ < MAX_TFTP_RETRIES)
2618044SWilliam.Kucharski@Sun.COM 	    {
2628044SWilliam.Kucharski@Sun.COM 	      /* Maybe initial request was lost.  */
2638044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
2648044SWilliam.Kucharski@Sun.COM 	      grub_printf ("Maybe initial request was lost.\n");
2658044SWilliam.Kucharski@Sun.COM #endif
2668044SWilliam.Kucharski@Sun.COM 	      if (! udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
2678044SWilliam.Kucharski@Sun.COM 				  ++iport, TFTP_PORT, len, &tp))
2688044SWilliam.Kucharski@Sun.COM 		return 0;
2698044SWilliam.Kucharski@Sun.COM 
2708044SWilliam.Kucharski@Sun.COM 	      continue;
2718044SWilliam.Kucharski@Sun.COM 	    }
2728044SWilliam.Kucharski@Sun.COM 
2738044SWilliam.Kucharski@Sun.COM #ifdef CONGESTED
2748044SWilliam.Kucharski@Sun.COM 	  if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
2758044SWilliam.Kucharski@Sun.COM 	    {
2768044SWilliam.Kucharski@Sun.COM 	      /* We resend our last ack.  */
2778044SWilliam.Kucharski@Sun.COM # ifdef TFTP_DEBUG
2788044SWilliam.Kucharski@Sun.COM 	      grub_printf ("<REXMT>\n");
2798044SWilliam.Kucharski@Sun.COM # endif
2808044SWilliam.Kucharski@Sun.COM 	      udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
2818044SWilliam.Kucharski@Sun.COM 			    iport, oport,
2828044SWilliam.Kucharski@Sun.COM 			    TFTP_MIN_PACKET, &tp);
2838044SWilliam.Kucharski@Sun.COM 	      continue;
2848044SWilliam.Kucharski@Sun.COM 	    }
2858044SWilliam.Kucharski@Sun.COM #endif
2868044SWilliam.Kucharski@Sun.COM 	  /* Timeout.  */
2878044SWilliam.Kucharski@Sun.COM 	  return 0;
2888044SWilliam.Kucharski@Sun.COM 	}
2898044SWilliam.Kucharski@Sun.COM 
2908044SWilliam.Kucharski@Sun.COM       tr = (struct tftp_t *) &nic.packet[ETH_HLEN];
2918044SWilliam.Kucharski@Sun.COM       if (tr->opcode == ntohs (TFTP_ERROR))
2928044SWilliam.Kucharski@Sun.COM 	{
2938044SWilliam.Kucharski@Sun.COM 	  grub_printf ("TFTP error %d (%s)\n",
2948044SWilliam.Kucharski@Sun.COM 		       ntohs (tr->u.err.errcode),
2958044SWilliam.Kucharski@Sun.COM 		       tr->u.err.errmsg);
2968044SWilliam.Kucharski@Sun.COM 	  return 0;
2978044SWilliam.Kucharski@Sun.COM 	}
2988044SWilliam.Kucharski@Sun.COM 
2998044SWilliam.Kucharski@Sun.COM       if (tr->opcode == ntohs (TFTP_OACK))
3008044SWilliam.Kucharski@Sun.COM 	{
3018044SWilliam.Kucharski@Sun.COM 	  char *p = tr->u.oack.data, *e;
3028044SWilliam.Kucharski@Sun.COM 
3038044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
3048044SWilliam.Kucharski@Sun.COM 	  grub_printf ("OACK ");
3058044SWilliam.Kucharski@Sun.COM #endif
3068044SWilliam.Kucharski@Sun.COM 	  /* Shouldn't happen.  */
3078044SWilliam.Kucharski@Sun.COM 	  if (prevblock)
3088044SWilliam.Kucharski@Sun.COM 	    {
3098044SWilliam.Kucharski@Sun.COM 	      /* Ignore it.  */
3108044SWilliam.Kucharski@Sun.COM 	      grub_printf ("%s:%d: warning: PREVBLOCK != 0 (0x%x)\n",
3118044SWilliam.Kucharski@Sun.COM 			   __FILE__, __LINE__, prevblock);
3128044SWilliam.Kucharski@Sun.COM 	      continue;
3138044SWilliam.Kucharski@Sun.COM 	    }
3148044SWilliam.Kucharski@Sun.COM 
3158044SWilliam.Kucharski@Sun.COM 	  len = ntohs (tr->udp.len) - sizeof (struct udphdr) - 2;
3168044SWilliam.Kucharski@Sun.COM 	  if (len > TFTP_MAX_PACKET)
3178044SWilliam.Kucharski@Sun.COM 	    goto noak;
3188044SWilliam.Kucharski@Sun.COM 
3198044SWilliam.Kucharski@Sun.COM 	  e = p + len;
3208044SWilliam.Kucharski@Sun.COM 	  while (*p != '\000' && p < e)
3218044SWilliam.Kucharski@Sun.COM 	    {
3228044SWilliam.Kucharski@Sun.COM 	      if (! grub_strcmp ("blksize", p))
3238044SWilliam.Kucharski@Sun.COM 		{
3248044SWilliam.Kucharski@Sun.COM 		  p += 8;
3258044SWilliam.Kucharski@Sun.COM 		  if ((packetsize = getdec (&p)) < TFTP_DEFAULTSIZE_PACKET)
3268044SWilliam.Kucharski@Sun.COM 		    goto noak;
3278044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
3288044SWilliam.Kucharski@Sun.COM 		  grub_printf ("blksize = %d\n", packetsize);
3298044SWilliam.Kucharski@Sun.COM #endif
3308044SWilliam.Kucharski@Sun.COM 		}
3318044SWilliam.Kucharski@Sun.COM 	      else if (! grub_strcmp ("tsize", p))
3328044SWilliam.Kucharski@Sun.COM 		{
3338044SWilliam.Kucharski@Sun.COM 		  p += 6;
3348044SWilliam.Kucharski@Sun.COM 		  if ((filemax = getdec (&p)) < 0)
3358044SWilliam.Kucharski@Sun.COM 		    {
3368044SWilliam.Kucharski@Sun.COM 		      filemax = -1;
3378044SWilliam.Kucharski@Sun.COM 		      goto noak;
3388044SWilliam.Kucharski@Sun.COM 		    }
3398044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
3408044SWilliam.Kucharski@Sun.COM 		  grub_printf ("tsize = %d\n", filemax);
3418044SWilliam.Kucharski@Sun.COM #endif
3428044SWilliam.Kucharski@Sun.COM 		}
3438044SWilliam.Kucharski@Sun.COM 	      else
3448044SWilliam.Kucharski@Sun.COM 		{
3458044SWilliam.Kucharski@Sun.COM 		noak:
3468044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
3478044SWilliam.Kucharski@Sun.COM 		  grub_printf ("NOAK\n");
3488044SWilliam.Kucharski@Sun.COM #endif
3498044SWilliam.Kucharski@Sun.COM 		  tp.opcode = htons (TFTP_ERROR);
3508044SWilliam.Kucharski@Sun.COM 		  tp.u.err.errcode = 8;
3518044SWilliam.Kucharski@Sun.COM 		  len = (grub_sprintf ((char *) tp.u.err.errmsg,
3528044SWilliam.Kucharski@Sun.COM 				       "RFC1782 error")
3538044SWilliam.Kucharski@Sun.COM 			 + sizeof (tp.ip) + sizeof (tp.udp)
3548044SWilliam.Kucharski@Sun.COM 			 + sizeof (tp.opcode) + sizeof (tp.u.err.errcode)
3558044SWilliam.Kucharski@Sun.COM 			 + 1);
3568044SWilliam.Kucharski@Sun.COM 		  udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
3578044SWilliam.Kucharski@Sun.COM 				iport, ntohs (tr->udp.src),
3588044SWilliam.Kucharski@Sun.COM 				len, &tp);
3598044SWilliam.Kucharski@Sun.COM 		  return 0;
3608044SWilliam.Kucharski@Sun.COM 		}
3618044SWilliam.Kucharski@Sun.COM 
3628044SWilliam.Kucharski@Sun.COM 	      while (p < e && *p)
3638044SWilliam.Kucharski@Sun.COM 		p++;
3648044SWilliam.Kucharski@Sun.COM 
3658044SWilliam.Kucharski@Sun.COM 	      if (p < e)
3668044SWilliam.Kucharski@Sun.COM 		p++;
3678044SWilliam.Kucharski@Sun.COM 	    }
3688044SWilliam.Kucharski@Sun.COM 
3698044SWilliam.Kucharski@Sun.COM 	  if (p > e)
3708044SWilliam.Kucharski@Sun.COM 	    goto noak;
3718044SWilliam.Kucharski@Sun.COM 
3728044SWilliam.Kucharski@Sun.COM 	  /* This ensures that the packet does not get processed as
3738044SWilliam.Kucharski@Sun.COM 	     data!  */
3748044SWilliam.Kucharski@Sun.COM 	  block = tp.u.ack.block = 0;
3758044SWilliam.Kucharski@Sun.COM 	}
3768044SWilliam.Kucharski@Sun.COM       else if (tr->opcode == ntohs (TFTP_DATA))
3778044SWilliam.Kucharski@Sun.COM 	{
3788044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
3798044SWilliam.Kucharski@Sun.COM 	  grub_printf ("DATA ");
3808044SWilliam.Kucharski@Sun.COM #endif
3818044SWilliam.Kucharski@Sun.COM 	  len = ntohs (tr->udp.len) - sizeof (struct udphdr) - 4;
3828044SWilliam.Kucharski@Sun.COM 
3838044SWilliam.Kucharski@Sun.COM 	  /* Shouldn't happen.  */
3848044SWilliam.Kucharski@Sun.COM 	  if (len > packetsize)
3858044SWilliam.Kucharski@Sun.COM 	    {
3868044SWilliam.Kucharski@Sun.COM 	      /* Ignore it.  */
3878044SWilliam.Kucharski@Sun.COM 	      grub_printf ("%s:%d: warning: LEN > PACKETSIZE (0x%x > 0x%x)\n",
3888044SWilliam.Kucharski@Sun.COM 			   __FILE__, __LINE__, len, packetsize);
3898044SWilliam.Kucharski@Sun.COM 	      continue;
3908044SWilliam.Kucharski@Sun.COM 	    }
3918044SWilliam.Kucharski@Sun.COM 
3928044SWilliam.Kucharski@Sun.COM 	  block = ntohs (tp.u.ack.block = tr->u.data.block);
3938044SWilliam.Kucharski@Sun.COM 	}
3948044SWilliam.Kucharski@Sun.COM       else
3958044SWilliam.Kucharski@Sun.COM 	/* Neither TFTP_OACK nor TFTP_DATA.  */
3968044SWilliam.Kucharski@Sun.COM 	break;
3978044SWilliam.Kucharski@Sun.COM 
398*10708SKerry.Shu@Sun.COM       if ((block || bcounter) && (block != (unsigned short) (prevblock + 1)))
3998044SWilliam.Kucharski@Sun.COM 	/* Block order should be continuous */
4008044SWilliam.Kucharski@Sun.COM 	tp.u.ack.block = htons (block = prevblock);
4018044SWilliam.Kucharski@Sun.COM 
4028044SWilliam.Kucharski@Sun.COM       /* Should be continuous.  */
4038044SWilliam.Kucharski@Sun.COM       tp.opcode = abort ? htons (TFTP_ERROR) : htons (TFTP_ACK);
4048044SWilliam.Kucharski@Sun.COM       oport = ntohs (tr->udp.src);
4058044SWilliam.Kucharski@Sun.COM 
4068044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
4078044SWilliam.Kucharski@Sun.COM       grub_printf ("ACK\n");
4088044SWilliam.Kucharski@Sun.COM #endif
4098044SWilliam.Kucharski@Sun.COM       /* Ack.  */
4108044SWilliam.Kucharski@Sun.COM       udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr, iport,
4118044SWilliam.Kucharski@Sun.COM 		    oport, TFTP_MIN_PACKET, &tp);
4128044SWilliam.Kucharski@Sun.COM 
4138044SWilliam.Kucharski@Sun.COM       if (abort)
4148044SWilliam.Kucharski@Sun.COM 	{
4158044SWilliam.Kucharski@Sun.COM 	  buf_eof = 1;
4168044SWilliam.Kucharski@Sun.COM 	  break;
4178044SWilliam.Kucharski@Sun.COM 	}
4188044SWilliam.Kucharski@Sun.COM 
4198044SWilliam.Kucharski@Sun.COM       /* Retransmission or OACK.  */
4208044SWilliam.Kucharski@Sun.COM       if ((unsigned short) (block - prevblock) != 1)
4218044SWilliam.Kucharski@Sun.COM 	/* Don't process.  */
4228044SWilliam.Kucharski@Sun.COM 	continue;
4238044SWilliam.Kucharski@Sun.COM 
4248044SWilliam.Kucharski@Sun.COM       prevblock = block;
4258044SWilliam.Kucharski@Sun.COM       /* Is it the right place to zero the timer?  */
4268044SWilliam.Kucharski@Sun.COM       retry = 0;
4278044SWilliam.Kucharski@Sun.COM 
4288044SWilliam.Kucharski@Sun.COM       /* In GRUB, this variable doesn't play any important role at all,
4298044SWilliam.Kucharski@Sun.COM 	 but use it for consistency with Etherboot.  */
4308044SWilliam.Kucharski@Sun.COM       bcounter++;
4318044SWilliam.Kucharski@Sun.COM 
4328044SWilliam.Kucharski@Sun.COM       /* Copy the downloaded data to the buffer.  */
4338044SWilliam.Kucharski@Sun.COM       grub_memmove (buf + buf_read, tr->u.data.download, len);
4348044SWilliam.Kucharski@Sun.COM       buf_read += len;
4358044SWilliam.Kucharski@Sun.COM 
4368044SWilliam.Kucharski@Sun.COM       /* End of data.  */
4378044SWilliam.Kucharski@Sun.COM       if (len < packetsize)
4388044SWilliam.Kucharski@Sun.COM 	buf_eof = 1;
4398044SWilliam.Kucharski@Sun.COM     }
4408044SWilliam.Kucharski@Sun.COM 
4418044SWilliam.Kucharski@Sun.COM   return 1;
4428044SWilliam.Kucharski@Sun.COM }
4438044SWilliam.Kucharski@Sun.COM 
4448044SWilliam.Kucharski@Sun.COM /* Send the RRQ whose length is LEN.  */
4458044SWilliam.Kucharski@Sun.COM static int
send_rrq(void)4468044SWilliam.Kucharski@Sun.COM send_rrq (void)
4478044SWilliam.Kucharski@Sun.COM {
4488044SWilliam.Kucharski@Sun.COM   /* Initialize some variables.  */
4498044SWilliam.Kucharski@Sun.COM   retry = 0;
4508044SWilliam.Kucharski@Sun.COM   block = 0;
4518044SWilliam.Kucharski@Sun.COM   prevblock = 0;
4528044SWilliam.Kucharski@Sun.COM   packetsize = TFTP_DEFAULTSIZE_PACKET;
4538044SWilliam.Kucharski@Sun.COM   bcounter = 0;
4548044SWilliam.Kucharski@Sun.COM 
4558044SWilliam.Kucharski@Sun.COM   buf = (char *) FSYS_BUF;
4568044SWilliam.Kucharski@Sun.COM   buf_eof = 0;
4578044SWilliam.Kucharski@Sun.COM   buf_read = 0;
4588044SWilliam.Kucharski@Sun.COM   saved_filepos = 0;
4598044SWilliam.Kucharski@Sun.COM 
4608044SWilliam.Kucharski@Sun.COM   rx_qdrain();
4618044SWilliam.Kucharski@Sun.COM 
4628044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
4638044SWilliam.Kucharski@Sun.COM   grub_printf ("send_rrq ()\n");
4648044SWilliam.Kucharski@Sun.COM   {
4658044SWilliam.Kucharski@Sun.COM     int i;
4668044SWilliam.Kucharski@Sun.COM     char *p;
4678044SWilliam.Kucharski@Sun.COM 
4688044SWilliam.Kucharski@Sun.COM     for (i = 0, p = (char *) &tp; i < len; i++)
4698044SWilliam.Kucharski@Sun.COM       if (p[i] >= ' ' && p[i] <= '~')
4708044SWilliam.Kucharski@Sun.COM 	grub_putchar (p[i]);
4718044SWilliam.Kucharski@Sun.COM       else
4728044SWilliam.Kucharski@Sun.COM 	grub_printf ("\\%x", (unsigned) p[i]);
4738044SWilliam.Kucharski@Sun.COM 
4748044SWilliam.Kucharski@Sun.COM     grub_putchar ('\n');
4758044SWilliam.Kucharski@Sun.COM   }
4768044SWilliam.Kucharski@Sun.COM #endif
4778044SWilliam.Kucharski@Sun.COM   /* Send the packet.  */
4788044SWilliam.Kucharski@Sun.COM   return udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
4798044SWilliam.Kucharski@Sun.COM 		       TFTP_PORT, len, &tp);
4808044SWilliam.Kucharski@Sun.COM }
4818044SWilliam.Kucharski@Sun.COM 
4828044SWilliam.Kucharski@Sun.COM /* Mount the network drive. If the drive is ready, return one, otherwise
4838044SWilliam.Kucharski@Sun.COM    return zero.  */
4848044SWilliam.Kucharski@Sun.COM int
tftp_mount(void)4858044SWilliam.Kucharski@Sun.COM tftp_mount (void)
4868044SWilliam.Kucharski@Sun.COM {
4878044SWilliam.Kucharski@Sun.COM   /* Check if the current drive is the network drive.  */
4888044SWilliam.Kucharski@Sun.COM   if (current_drive != NETWORK_DRIVE)
4898044SWilliam.Kucharski@Sun.COM     return 0;
4908044SWilliam.Kucharski@Sun.COM 
4918044SWilliam.Kucharski@Sun.COM   /* If the drive is not initialized yet, abort.  */
4928044SWilliam.Kucharski@Sun.COM   if (! network_ready)
4938044SWilliam.Kucharski@Sun.COM     return 0;
4948044SWilliam.Kucharski@Sun.COM 
4958044SWilliam.Kucharski@Sun.COM   return 1;
4968044SWilliam.Kucharski@Sun.COM }
4978044SWilliam.Kucharski@Sun.COM 
4988044SWilliam.Kucharski@Sun.COM /* Read up to SIZE bytes, returned in ADDR.  */
4998044SWilliam.Kucharski@Sun.COM int
tftp_read(char * addr,int size)5008044SWilliam.Kucharski@Sun.COM tftp_read (char *addr, int size)
5018044SWilliam.Kucharski@Sun.COM {
5028044SWilliam.Kucharski@Sun.COM   /* How many bytes is read?  */
5038044SWilliam.Kucharski@Sun.COM   int ret = 0;
5048044SWilliam.Kucharski@Sun.COM 
5058044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
5068044SWilliam.Kucharski@Sun.COM   grub_printf ("tftp_read (0x%x, %d)\n", (int) addr, size);
5078044SWilliam.Kucharski@Sun.COM #endif
5088044SWilliam.Kucharski@Sun.COM 
5098044SWilliam.Kucharski@Sun.COM   if (use_bios_pxe)
5108044SWilliam.Kucharski@Sun.COM 	return (tftp_read_undi(addr, size));
5118044SWilliam.Kucharski@Sun.COM 
5128044SWilliam.Kucharski@Sun.COM   if (filepos < saved_filepos)
5138044SWilliam.Kucharski@Sun.COM     {
5148044SWilliam.Kucharski@Sun.COM       /* Uggh.. FILEPOS has been moved backwards. So reopen the file.  */
5158044SWilliam.Kucharski@Sun.COM       buf_read = 0;
5168044SWilliam.Kucharski@Sun.COM       buf_fill (1);
5178044SWilliam.Kucharski@Sun.COM       grub_memmove ((char *) &tp, (char *) &saved_tp, saved_len);
5188044SWilliam.Kucharski@Sun.COM       len = saved_len;
5198044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
5208044SWilliam.Kucharski@Sun.COM       {
5218044SWilliam.Kucharski@Sun.COM 	int i;
5228044SWilliam.Kucharski@Sun.COM 	grub_printf ("opcode = 0x%x, rrq = ", (unsigned long) tp.opcode);
5238044SWilliam.Kucharski@Sun.COM 	for (i = 0; i < TFTP_DEFAULTSIZE_PACKET; i++)
5248044SWilliam.Kucharski@Sun.COM 	  {
5258044SWilliam.Kucharski@Sun.COM 	    if (tp.u.rrq[i] >= ' ' && tp.u.rrq[i] <= '~')
5268044SWilliam.Kucharski@Sun.COM 	      grub_putchar (tp.u.rrq[i]);
5278044SWilliam.Kucharski@Sun.COM 	    else
5288044SWilliam.Kucharski@Sun.COM 	      grub_putchar ('*');
5298044SWilliam.Kucharski@Sun.COM 	  }
5308044SWilliam.Kucharski@Sun.COM 	grub_putchar ('\n');
5318044SWilliam.Kucharski@Sun.COM       }
5328044SWilliam.Kucharski@Sun.COM #endif
5338044SWilliam.Kucharski@Sun.COM 
5348044SWilliam.Kucharski@Sun.COM       if (! send_rrq ())
5358044SWilliam.Kucharski@Sun.COM 	{
5368044SWilliam.Kucharski@Sun.COM 	  errnum = ERR_WRITE;
5378044SWilliam.Kucharski@Sun.COM 	  return 0;
5388044SWilliam.Kucharski@Sun.COM 	}
5398044SWilliam.Kucharski@Sun.COM     }
5408044SWilliam.Kucharski@Sun.COM 
5418044SWilliam.Kucharski@Sun.COM   while (size > 0)
5428044SWilliam.Kucharski@Sun.COM     {
5438044SWilliam.Kucharski@Sun.COM       int amt = buf_read + saved_filepos - filepos;
5448044SWilliam.Kucharski@Sun.COM 
5458044SWilliam.Kucharski@Sun.COM       /* If the length that can be copied from the buffer is over the
5468044SWilliam.Kucharski@Sun.COM 	 requested size, cut it down.  */
5478044SWilliam.Kucharski@Sun.COM       if (amt > size)
5488044SWilliam.Kucharski@Sun.COM 	amt = size;
5498044SWilliam.Kucharski@Sun.COM 
5508044SWilliam.Kucharski@Sun.COM       if (amt > 0)
5518044SWilliam.Kucharski@Sun.COM 	{
5528044SWilliam.Kucharski@Sun.COM 	  /* Copy the buffer to the supplied memory space.  */
5538044SWilliam.Kucharski@Sun.COM 	  grub_memmove (addr, buf + filepos - saved_filepos, amt);
5548044SWilliam.Kucharski@Sun.COM 	  size -= amt;
5558044SWilliam.Kucharski@Sun.COM 	  addr += amt;
5568044SWilliam.Kucharski@Sun.COM 	  filepos += amt;
5578044SWilliam.Kucharski@Sun.COM 	  ret += amt;
5588044SWilliam.Kucharski@Sun.COM 
5598044SWilliam.Kucharski@Sun.COM 	  /* If the size of the empty space becomes small, move the unused
5608044SWilliam.Kucharski@Sun.COM 	     data forwards.  */
5618044SWilliam.Kucharski@Sun.COM 	  if (filepos - saved_filepos > FSYS_BUFLEN / 2)
5628044SWilliam.Kucharski@Sun.COM 	    {
5638044SWilliam.Kucharski@Sun.COM 	      grub_memmove (buf, buf + FSYS_BUFLEN / 2, FSYS_BUFLEN / 2);
5648044SWilliam.Kucharski@Sun.COM 	      buf_read -= FSYS_BUFLEN / 2;
5658044SWilliam.Kucharski@Sun.COM 	      saved_filepos += FSYS_BUFLEN / 2;
5668044SWilliam.Kucharski@Sun.COM 	    }
5678044SWilliam.Kucharski@Sun.COM 	}
5688044SWilliam.Kucharski@Sun.COM       else
5698044SWilliam.Kucharski@Sun.COM 	{
5708044SWilliam.Kucharski@Sun.COM 	  /* Skip the whole buffer.  */
5718044SWilliam.Kucharski@Sun.COM 	  saved_filepos += buf_read;
5728044SWilliam.Kucharski@Sun.COM 	  buf_read = 0;
5738044SWilliam.Kucharski@Sun.COM 	}
5748044SWilliam.Kucharski@Sun.COM 
5758044SWilliam.Kucharski@Sun.COM       /* Read the data.  */
5768044SWilliam.Kucharski@Sun.COM       if (size > 0 && ! buf_fill (0))
5778044SWilliam.Kucharski@Sun.COM 	{
5788044SWilliam.Kucharski@Sun.COM 	  errnum = ERR_READ;
5798044SWilliam.Kucharski@Sun.COM 	  return 0;
5808044SWilliam.Kucharski@Sun.COM 	}
5818044SWilliam.Kucharski@Sun.COM 
5828044SWilliam.Kucharski@Sun.COM       /* Sanity check.  */
5838044SWilliam.Kucharski@Sun.COM       if (size > 0 && buf_read == 0)
5848044SWilliam.Kucharski@Sun.COM 	{
5858044SWilliam.Kucharski@Sun.COM 	  errnum = ERR_READ;
5868044SWilliam.Kucharski@Sun.COM 	  return 0;
5878044SWilliam.Kucharski@Sun.COM 	}
5888044SWilliam.Kucharski@Sun.COM     }
5898044SWilliam.Kucharski@Sun.COM 
5908044SWilliam.Kucharski@Sun.COM   return ret;
5918044SWilliam.Kucharski@Sun.COM }
5928044SWilliam.Kucharski@Sun.COM 
5938044SWilliam.Kucharski@Sun.COM /* Check if the file DIRNAME really exists. Get the size and save it in
5948044SWilliam.Kucharski@Sun.COM    FILEMAX.  */
5958044SWilliam.Kucharski@Sun.COM int
tftp_dir(char * dirname)5968044SWilliam.Kucharski@Sun.COM tftp_dir (char *dirname)
5978044SWilliam.Kucharski@Sun.COM {
5988044SWilliam.Kucharski@Sun.COM   int ch;
5998044SWilliam.Kucharski@Sun.COM 
6008044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
6018044SWilliam.Kucharski@Sun.COM   grub_printf ("tftp_dir (%s)\n", dirname);
6028044SWilliam.Kucharski@Sun.COM #endif
6038044SWilliam.Kucharski@Sun.COM 
6048044SWilliam.Kucharski@Sun.COM   if (use_bios_pxe)
6058044SWilliam.Kucharski@Sun.COM 	return (tftp_dir_undi(dirname));
6068044SWilliam.Kucharski@Sun.COM 
6078044SWilliam.Kucharski@Sun.COM   /* In TFTP, there is no way to know what files exist.  */
6088044SWilliam.Kucharski@Sun.COM   if (print_possibilities)
6098044SWilliam.Kucharski@Sun.COM     return 1;
6108044SWilliam.Kucharski@Sun.COM 
6118044SWilliam.Kucharski@Sun.COM   /* Don't know the size yet.  */
6128044SWilliam.Kucharski@Sun.COM   filemax = -1;
6138044SWilliam.Kucharski@Sun.COM 
6148044SWilliam.Kucharski@Sun.COM  reopen:
6158044SWilliam.Kucharski@Sun.COM   /* Construct the TFTP request packet.  */
6168044SWilliam.Kucharski@Sun.COM   tp.opcode = htons (TFTP_RRQ);
6178044SWilliam.Kucharski@Sun.COM   /* Terminate the filename.  */
6188044SWilliam.Kucharski@Sun.COM   ch = nul_terminate (dirname);
6198044SWilliam.Kucharski@Sun.COM   /* Make the request string (octet, blksize and tsize).  */
6208044SWilliam.Kucharski@Sun.COM   len = (grub_sprintf ((char *) tp.u.rrq,
6218044SWilliam.Kucharski@Sun.COM 		       "%s%coctet%cblksize%c%d%ctsize%c0",
6228044SWilliam.Kucharski@Sun.COM 		       dirname, 0, 0, 0, TFTP_MAX_PACKET, 0, 0)
6238044SWilliam.Kucharski@Sun.COM 	 + sizeof (tp.ip) + sizeof (tp.udp) + sizeof (tp.opcode) + 1);
6248044SWilliam.Kucharski@Sun.COM   /* Restore the original DIRNAME.  */
6258044SWilliam.Kucharski@Sun.COM   dirname[grub_strlen (dirname)] = ch;
6268044SWilliam.Kucharski@Sun.COM   /* Save the TFTP packet so that we can reopen the file later.  */
6278044SWilliam.Kucharski@Sun.COM   grub_memmove ((char *) &saved_tp, (char *) &tp, len);
6288044SWilliam.Kucharski@Sun.COM   saved_len = len;
6298044SWilliam.Kucharski@Sun.COM   if (! send_rrq ())
6308044SWilliam.Kucharski@Sun.COM     {
6318044SWilliam.Kucharski@Sun.COM       errnum = ERR_WRITE;
6328044SWilliam.Kucharski@Sun.COM       return 0;
6338044SWilliam.Kucharski@Sun.COM     }
6348044SWilliam.Kucharski@Sun.COM 
6358044SWilliam.Kucharski@Sun.COM   /* Read the data.  */
6368044SWilliam.Kucharski@Sun.COM   if (! buf_fill (0))
6378044SWilliam.Kucharski@Sun.COM     {
6388044SWilliam.Kucharski@Sun.COM       errnum = ERR_FILE_NOT_FOUND;
6398044SWilliam.Kucharski@Sun.COM       return 0;
6408044SWilliam.Kucharski@Sun.COM     }
6418044SWilliam.Kucharski@Sun.COM 
6428044SWilliam.Kucharski@Sun.COM   if (filemax == -1)
6438044SWilliam.Kucharski@Sun.COM     {
6448044SWilliam.Kucharski@Sun.COM       /* The server doesn't support the "tsize" option, so we must read
6458044SWilliam.Kucharski@Sun.COM 	 the file twice...  */
6468044SWilliam.Kucharski@Sun.COM 
6478044SWilliam.Kucharski@Sun.COM       /* Zero the size of the file.  */
6488044SWilliam.Kucharski@Sun.COM       filemax = 0;
6498044SWilliam.Kucharski@Sun.COM       do
6508044SWilliam.Kucharski@Sun.COM 	{
6518044SWilliam.Kucharski@Sun.COM 	  /* Add the length of the downloaded data.  */
6528044SWilliam.Kucharski@Sun.COM 	  filemax += buf_read;
6538044SWilliam.Kucharski@Sun.COM 	  /* Reset the offset. Just discard the contents of the buffer.  */
6548044SWilliam.Kucharski@Sun.COM 	  buf_read = 0;
6558044SWilliam.Kucharski@Sun.COM 	  /* Read the data.  */
6568044SWilliam.Kucharski@Sun.COM 	  if (! buf_fill (0))
6578044SWilliam.Kucharski@Sun.COM 	    {
6588044SWilliam.Kucharski@Sun.COM 	      errnum = ERR_READ;
6598044SWilliam.Kucharski@Sun.COM 	      return 0;
6608044SWilliam.Kucharski@Sun.COM 	    }
6618044SWilliam.Kucharski@Sun.COM 	}
6628044SWilliam.Kucharski@Sun.COM       while (! buf_eof);
6638044SWilliam.Kucharski@Sun.COM 
6648044SWilliam.Kucharski@Sun.COM       /* Maybe a few amounts of data remains.  */
6658044SWilliam.Kucharski@Sun.COM       filemax += buf_read;
6668044SWilliam.Kucharski@Sun.COM 
6678044SWilliam.Kucharski@Sun.COM       /* Retry the open instruction.  */
6688044SWilliam.Kucharski@Sun.COM       goto reopen;
6698044SWilliam.Kucharski@Sun.COM     }
6708044SWilliam.Kucharski@Sun.COM 
6718044SWilliam.Kucharski@Sun.COM   return 1;
6728044SWilliam.Kucharski@Sun.COM }
6738044SWilliam.Kucharski@Sun.COM 
6748044SWilliam.Kucharski@Sun.COM /* Close the file.  */
6758044SWilliam.Kucharski@Sun.COM void
tftp_close(void)6768044SWilliam.Kucharski@Sun.COM tftp_close (void)
6778044SWilliam.Kucharski@Sun.COM {
6788044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
6798044SWilliam.Kucharski@Sun.COM   grub_printf ("tftp_close ()\n");
6808044SWilliam.Kucharski@Sun.COM #endif
6818044SWilliam.Kucharski@Sun.COM 
6828044SWilliam.Kucharski@Sun.COM   if (use_bios_pxe) {
6838044SWilliam.Kucharski@Sun.COM 	tftp_close_undi();
6848044SWilliam.Kucharski@Sun.COM 	return;
6858044SWilliam.Kucharski@Sun.COM   }
6868044SWilliam.Kucharski@Sun.COM 
6878044SWilliam.Kucharski@Sun.COM   buf_read = 0;
6888044SWilliam.Kucharski@Sun.COM   buf_fill (1);
6898044SWilliam.Kucharski@Sun.COM }
6908044SWilliam.Kucharski@Sun.COM 
6918044SWilliam.Kucharski@Sun.COM /* tftp implementation using BIOS established PXE stack */
6928044SWilliam.Kucharski@Sun.COM 
tftp_file_read_undi(const char * name,int (* fnc)(unsigned char *,unsigned int,unsigned int,int))6938044SWilliam.Kucharski@Sun.COM static int tftp_file_read_undi(const char *name,
6948044SWilliam.Kucharski@Sun.COM     int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
6958044SWilliam.Kucharski@Sun.COM {
6968044SWilliam.Kucharski@Sun.COM 	int rc;
6978044SWilliam.Kucharski@Sun.COM 	uint16_t len;
6988044SWilliam.Kucharski@Sun.COM 
6998044SWilliam.Kucharski@Sun.COM 	buf = (char *)&nic.packet;
7008044SWilliam.Kucharski@Sun.COM 	/* open tftp session */
7018044SWilliam.Kucharski@Sun.COM 	if (eb_pxenv_tftp_open(name, arptable[ARP_SERVER].ipaddr,
7028044SWilliam.Kucharski@Sun.COM 	    arptable[ARP_GATEWAY].ipaddr, &packetsize) == 0)
7038044SWilliam.Kucharski@Sun.COM 		return (0);
7048044SWilliam.Kucharski@Sun.COM 
7058044SWilliam.Kucharski@Sun.COM 	/* read blocks and invoke fnc for each block */
7068044SWilliam.Kucharski@Sun.COM 	for (;;) {
7078044SWilliam.Kucharski@Sun.COM 		rc = eb_pxenv_tftp_read(buf, &len);
7088044SWilliam.Kucharski@Sun.COM 		if (rc == 0)
7098044SWilliam.Kucharski@Sun.COM 			break;
7108044SWilliam.Kucharski@Sun.COM 		rc = fnc(buf, ++block, len, len < packetsize);
7118044SWilliam.Kucharski@Sun.COM 		if (rc <= 0 || len < packetsize)
7128044SWilliam.Kucharski@Sun.COM 			break;
7138044SWilliam.Kucharski@Sun.COM 	}
7148044SWilliam.Kucharski@Sun.COM 
7158044SWilliam.Kucharski@Sun.COM 	(void) eb_pxenv_tftp_close();
7168044SWilliam.Kucharski@Sun.COM 	return (rc > 0 ? 1 : 0);
7178044SWilliam.Kucharski@Sun.COM }
7188044SWilliam.Kucharski@Sun.COM 
7198044SWilliam.Kucharski@Sun.COM /* Fill the buffer by reading the data via the TFTP protocol.  */
7208044SWilliam.Kucharski@Sun.COM static int
buf_fill_undi(int abort)7218044SWilliam.Kucharski@Sun.COM buf_fill_undi(int abort)
7228044SWilliam.Kucharski@Sun.COM {
7238044SWilliam.Kucharski@Sun.COM 	int rc;
7248044SWilliam.Kucharski@Sun.COM 	uint8_t *tmpbuf;
7258044SWilliam.Kucharski@Sun.COM 
7268044SWilliam.Kucharski@Sun.COM 	while (! buf_eof && (buf_read + packetsize <= FSYS_BUFLEN)) {
7278044SWilliam.Kucharski@Sun.COM 		poll_interruptions();
7288044SWilliam.Kucharski@Sun.COM 		if (user_abort)
7298044SWilliam.Kucharski@Sun.COM 			return 0;
7308044SWilliam.Kucharski@Sun.COM 		if (abort) {
7318044SWilliam.Kucharski@Sun.COM 			buf_eof = 1;
7328044SWilliam.Kucharski@Sun.COM 			break;
7338044SWilliam.Kucharski@Sun.COM 		}
7348044SWilliam.Kucharski@Sun.COM 
7358044SWilliam.Kucharski@Sun.COM 		if (eb_pxenv_tftp_read(buf + buf_read, &len) == 0)
7368044SWilliam.Kucharski@Sun.COM 			return (0);
7378044SWilliam.Kucharski@Sun.COM 
7388044SWilliam.Kucharski@Sun.COM 		buf_read += len;
7398044SWilliam.Kucharski@Sun.COM 
7408044SWilliam.Kucharski@Sun.COM 		/* End of data.  */
7418044SWilliam.Kucharski@Sun.COM 		if (len < packetsize)
7428044SWilliam.Kucharski@Sun.COM 			buf_eof = 1;
7438044SWilliam.Kucharski@Sun.COM 	}
7448044SWilliam.Kucharski@Sun.COM 	return 1;
7458044SWilliam.Kucharski@Sun.COM }
7468044SWilliam.Kucharski@Sun.COM 
7478044SWilliam.Kucharski@Sun.COM static void
tftp_reopen_undi(void)7488044SWilliam.Kucharski@Sun.COM tftp_reopen_undi(void)
7498044SWilliam.Kucharski@Sun.COM {
7508044SWilliam.Kucharski@Sun.COM 	tftp_close();
7518044SWilliam.Kucharski@Sun.COM 	(void) eb_pxenv_tftp_open(saved_name, arptable[ARP_SERVER].ipaddr,
7528044SWilliam.Kucharski@Sun.COM 	    arptable[ARP_GATEWAY].ipaddr, &packetsize);
7538044SWilliam.Kucharski@Sun.COM 
7548044SWilliam.Kucharski@Sun.COM 	buf_eof = 0;
7558044SWilliam.Kucharski@Sun.COM 	buf_read = 0;
7568044SWilliam.Kucharski@Sun.COM 	saved_filepos = 0;
7578044SWilliam.Kucharski@Sun.COM }
7588044SWilliam.Kucharski@Sun.COM 
7598044SWilliam.Kucharski@Sun.COM /* Read up to SIZE bytes, returned in ADDR.  */
7608044SWilliam.Kucharski@Sun.COM static int
tftp_read_undi(char * addr,int size)7618044SWilliam.Kucharski@Sun.COM tftp_read_undi(char *addr, int size)
7628044SWilliam.Kucharski@Sun.COM {
7638044SWilliam.Kucharski@Sun.COM 	int ret = 0;
7648044SWilliam.Kucharski@Sun.COM 
7658044SWilliam.Kucharski@Sun.COM 	if (filepos < saved_filepos) {
7668044SWilliam.Kucharski@Sun.COM 		/* Uggh.. FILEPOS has been moved backwards. reopen the file. */
7678044SWilliam.Kucharski@Sun.COM 		tftp_reopen_undi();
7688044SWilliam.Kucharski@Sun.COM 	}
7698044SWilliam.Kucharski@Sun.COM 
7708044SWilliam.Kucharski@Sun.COM 	while (size > 0) {
7718044SWilliam.Kucharski@Sun.COM 		int amt = buf_read + saved_filepos - filepos;
7728044SWilliam.Kucharski@Sun.COM 
7738044SWilliam.Kucharski@Sun.COM 		/* If the length that can be copied from the buffer is over
7748044SWilliam.Kucharski@Sun.COM 		   the requested size, cut it down. */
7758044SWilliam.Kucharski@Sun.COM 		if (amt > size)
7768044SWilliam.Kucharski@Sun.COM 			amt = size;
7778044SWilliam.Kucharski@Sun.COM 
7788044SWilliam.Kucharski@Sun.COM 		if (amt > 0) {
7798044SWilliam.Kucharski@Sun.COM 			/* Copy the buffer to the supplied memory space.  */
7808044SWilliam.Kucharski@Sun.COM 			grub_memmove (addr, buf + filepos - saved_filepos, amt);
7818044SWilliam.Kucharski@Sun.COM 			size -= amt;
7828044SWilliam.Kucharski@Sun.COM 			addr += amt;
7838044SWilliam.Kucharski@Sun.COM 			filepos += amt;
7848044SWilliam.Kucharski@Sun.COM 			ret += amt;
7858044SWilliam.Kucharski@Sun.COM 
7868044SWilliam.Kucharski@Sun.COM 			/* If the size of the empty space becomes small,
7878044SWilliam.Kucharski@Sun.COM 			 * move the unused data forwards.
7888044SWilliam.Kucharski@Sun.COM 			 */
7898044SWilliam.Kucharski@Sun.COM 			if (filepos - saved_filepos > FSYS_BUFLEN / 2) {
7908044SWilliam.Kucharski@Sun.COM 				grub_memmove (buf, buf + FSYS_BUFLEN / 2,
7918044SWilliam.Kucharski@Sun.COM 				    FSYS_BUFLEN / 2);
7928044SWilliam.Kucharski@Sun.COM 				buf_read -= FSYS_BUFLEN / 2;
7938044SWilliam.Kucharski@Sun.COM 				saved_filepos += FSYS_BUFLEN / 2;
7948044SWilliam.Kucharski@Sun.COM 			}
7958044SWilliam.Kucharski@Sun.COM 		} else {
7968044SWilliam.Kucharski@Sun.COM 			/* Skip the whole buffer.  */
7978044SWilliam.Kucharski@Sun.COM 			saved_filepos += buf_read;
7988044SWilliam.Kucharski@Sun.COM 			buf_read = 0;
7998044SWilliam.Kucharski@Sun.COM 		}
8008044SWilliam.Kucharski@Sun.COM 
8018044SWilliam.Kucharski@Sun.COM 		/* Read the data.  */
8028044SWilliam.Kucharski@Sun.COM 		if (size > 0 && ! buf_fill (0)) {
8038044SWilliam.Kucharski@Sun.COM 			errnum = ERR_READ;
8048044SWilliam.Kucharski@Sun.COM 			return 0;
8058044SWilliam.Kucharski@Sun.COM 		}
8068044SWilliam.Kucharski@Sun.COM 
8078044SWilliam.Kucharski@Sun.COM 		/* Sanity check.  */
8088044SWilliam.Kucharski@Sun.COM 		if (size > 0 && buf_read == 0) {
8098044SWilliam.Kucharski@Sun.COM 			errnum = ERR_READ;
8108044SWilliam.Kucharski@Sun.COM 			return 0;
8118044SWilliam.Kucharski@Sun.COM 		}
8128044SWilliam.Kucharski@Sun.COM 	}
8138044SWilliam.Kucharski@Sun.COM 
8148044SWilliam.Kucharski@Sun.COM 	return ret;
8158044SWilliam.Kucharski@Sun.COM }
8168044SWilliam.Kucharski@Sun.COM 
8178044SWilliam.Kucharski@Sun.COM static int
tftp_dir_undi(char * dirname)8188044SWilliam.Kucharski@Sun.COM tftp_dir_undi(char *dirname)
8198044SWilliam.Kucharski@Sun.COM {
8208044SWilliam.Kucharski@Sun.COM 	int rc, ch;
8218044SWilliam.Kucharski@Sun.COM 	uint16_t len;
8228044SWilliam.Kucharski@Sun.COM 
8238044SWilliam.Kucharski@Sun.COM 	/* In TFTP, there is no way to know what files exist.  */
8248044SWilliam.Kucharski@Sun.COM 	if (print_possibilities)
8258044SWilliam.Kucharski@Sun.COM 		return 1;
8268044SWilliam.Kucharski@Sun.COM 
8278044SWilliam.Kucharski@Sun.COM 	/* name may be space terminated */
8288044SWilliam.Kucharski@Sun.COM 	ch = nul_terminate(dirname);
8298044SWilliam.Kucharski@Sun.COM 	saved_name = (char *)&saved_tp;
8308044SWilliam.Kucharski@Sun.COM 	sprintf(saved_name, "%s", dirname);
8318044SWilliam.Kucharski@Sun.COM 
8328044SWilliam.Kucharski@Sun.COM   	/* Restore the original dirname */
8338044SWilliam.Kucharski@Sun.COM 	dirname[grub_strlen (dirname)] = ch;
8348044SWilliam.Kucharski@Sun.COM 
8358044SWilliam.Kucharski@Sun.COM 	/* get the file size; must call before tftp_open */
8368044SWilliam.Kucharski@Sun.COM 	rc = eb_pxenv_tftp_get_fsize(saved_name, arptable[ARP_SERVER].ipaddr,
8378044SWilliam.Kucharski@Sun.COM 	    arptable[ARP_GATEWAY].ipaddr, &filemax);
8388044SWilliam.Kucharski@Sun.COM 
8398044SWilliam.Kucharski@Sun.COM 	/* open tftp session */
8408044SWilliam.Kucharski@Sun.COM 	if (eb_pxenv_tftp_open(saved_name, arptable[ARP_SERVER].ipaddr,
8418044SWilliam.Kucharski@Sun.COM 	    arptable[ARP_GATEWAY].ipaddr, &packetsize) == 0)
8428044SWilliam.Kucharski@Sun.COM 		return (0);
8438044SWilliam.Kucharski@Sun.COM 
8448044SWilliam.Kucharski@Sun.COM 	buf = (char *) FSYS_BUF;
8458044SWilliam.Kucharski@Sun.COM 	buf_eof = 0;
8468044SWilliam.Kucharski@Sun.COM 	buf_read = 0;
8478044SWilliam.Kucharski@Sun.COM 	saved_filepos = 0;
8488044SWilliam.Kucharski@Sun.COM 
8498044SWilliam.Kucharski@Sun.COM 	if (rc == 0) {
8508044SWilliam.Kucharski@Sun.COM 		/* Read the entire file to get filemax */
8518044SWilliam.Kucharski@Sun.COM 		filemax = 0;
8528044SWilliam.Kucharski@Sun.COM 		do {
8538044SWilliam.Kucharski@Sun.COM 			/* Add the length of the downloaded data.  */
8548044SWilliam.Kucharski@Sun.COM 			filemax += buf_read;
8558044SWilliam.Kucharski@Sun.COM 			buf_read = 0;
8568044SWilliam.Kucharski@Sun.COM 			if (! buf_fill (0)) {
8578044SWilliam.Kucharski@Sun.COM 				errnum = ERR_READ;
8588044SWilliam.Kucharski@Sun.COM 				return 0;
8598044SWilliam.Kucharski@Sun.COM 			}
8608044SWilliam.Kucharski@Sun.COM 		} while (! buf_eof);
8618044SWilliam.Kucharski@Sun.COM 
8628044SWilliam.Kucharski@Sun.COM 		/* Maybe a few amounts of data remains.  */
8638044SWilliam.Kucharski@Sun.COM 		filemax += buf_read;
8648044SWilliam.Kucharski@Sun.COM 
8658044SWilliam.Kucharski@Sun.COM 		tftp_reopen_undi(); /* reopen file to read from beginning */
8668044SWilliam.Kucharski@Sun.COM 	}
8678044SWilliam.Kucharski@Sun.COM 
8688044SWilliam.Kucharski@Sun.COM 	return (1);
8698044SWilliam.Kucharski@Sun.COM }
8708044SWilliam.Kucharski@Sun.COM 
8718044SWilliam.Kucharski@Sun.COM static void
tftp_close_undi(void)8728044SWilliam.Kucharski@Sun.COM tftp_close_undi(void)
8738044SWilliam.Kucharski@Sun.COM {
8748044SWilliam.Kucharski@Sun.COM 	buf_read = 0;
8758044SWilliam.Kucharski@Sun.COM 	buf_fill (1);
8768044SWilliam.Kucharski@Sun.COM 	(void) eb_pxenv_tftp_close();
8778044SWilliam.Kucharski@Sun.COM }
878