xref: /titanic_44/usr/src/grub/grub-0.97/netboot/fsys_tftp.c (revision 2d6b5ea734bb47d251c82670646fde46af15fd69)
1 /*
2  *  GRUB  --  GRand Unified Bootloader
3  *  Copyright (C) 2000,2001,2002,2004  Free Software Foundation, Inc.
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 /* Based on "src/main.c" in etherboot-4.5.8.  */
21 /**************************************************************************
22 ETHERBOOT -  BOOTP/TFTP Bootstrap Program
23 
24 Author: Martin Renters
25   Date: Dec/93
26 
27 **************************************************************************/
28 
29 /* #define TFTP_DEBUG	1 */
30 
31 #include <filesys.h>
32 #include <shared.h>
33 
34 #include "grub.h"
35 #include "tftp.h"
36 #include "nic.h"
37 
38 static int tftp_file_read_undi(const char *name,
39     int (*fnc)(unsigned char *, unsigned int, unsigned int, int));
40 static int tftp_read_undi(char *addr, int size);
41 static int tftp_dir_undi(char *dirname);
42 static void tftp_close_undi(void);
43 static int buf_fill_undi(int abort);
44 
45 extern int use_bios_pxe;
46 
47 static int retry;
48 static unsigned short iport = 2000;
49 static unsigned short oport = 0;
50 static unsigned short block, prevblock;
51 static int bcounter;
52 static struct tftp_t tp, saved_tp;
53 static int packetsize;
54 static int buf_eof, buf_read;
55 static int saved_filepos;
56 static unsigned short len, saved_len;
57 static char *buf, *saved_name;
58 
59 /**
60  * tftp_read
61  *
62  * Read file with _name_, data handled by _fnc_. In fact, grub never
63  * use it, we just use it to read dhcp config file.
64  */
65 static int await_tftp(int ival, void *ptr __unused,
66 		      unsigned short ptype __unused, struct iphdr *ip,
67 		      struct udphdr *udp)
68 {
69 	static int tftp_count = 0;
70 
71 	if (!udp) {
72 		return 0;
73 	}
74 	if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
75 		return 0;
76 	if (ntohs(udp->dest) != ival)
77 		return 0;
78 	tftp_count++;	/* show progress */
79 	if ((tftp_count % 1000) == 0)
80 		printf(".");
81 	return 1;
82 }
83 
84 int tftp_file_read(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
85 {
86 	struct tftpreq_t tp;
87 	struct tftp_t  *tr;
88 	int		rc;
89 
90 	if (use_bios_pxe)
91 		return (tftp_file_read_undi(name, fnc));
92 
93 	retry = 0;
94 	block = 0;
95 	prevblock = 0;
96 	bcounter = 0;
97 
98 
99 	rx_qdrain();
100 
101 	tp.opcode = htons(TFTP_RRQ);
102 	/* Warning: the following assumes the layout of bootp_t.
103 	   But that's fixed by the IP, UDP and BOOTP specs. */
104 	len = sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) +
105 		sprintf((char *)tp.u.rrq, "%s%coctet%cblksize%c%d",
106 		name, 0, 0, 0, TFTP_MAX_PACKET) + 1;
107 	if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
108 			  TFTP_PORT, len, &tp))
109 		return (0);
110 	for (;;)
111 	{
112 		long timeout;
113 #ifdef	CONGESTED
114 		timeout = rfc2131_sleep_interval(block?TFTP_REXMT: TIMEOUT, retry);
115 #else
116 		timeout = rfc2131_sleep_interval(TIMEOUT, retry);
117 #endif
118 		if (!await_reply(await_tftp, iport, NULL, timeout))
119 		{
120 			if (!block && retry++ < MAX_TFTP_RETRIES)
121 			{	/* maybe initial request was lost */
122 				if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
123 						  ++iport, TFTP_PORT, len, &tp))
124 					return (0);
125 				continue;
126 			}
127 #ifdef	CONGESTED
128 			if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
129 			{	/* we resend our last ack */
130 #ifdef	MDEBUG
131 				printf("<REXMT>\n");
132 #endif
133 				udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
134 					     iport, oport,
135 					     TFTP_MIN_PACKET, &tp);
136 				continue;
137 			}
138 #endif
139 			break;	/* timeout */
140 		}
141 		tr = (struct tftp_t *)&nic.packet[ETH_HLEN];
142 		if (tr->opcode == ntohs(TFTP_ERROR))
143 		{
144 			printf("TFTP error %d (%s)\n",
145 			       ntohs(tr->u.err.errcode),
146 			       tr->u.err.errmsg);
147 			break;
148 		}
149 
150 		if (tr->opcode == ntohs(TFTP_OACK)) {
151 			char *p = tr->u.oack.data, *e;
152 
153 			if (prevblock)		/* shouldn't happen */
154 				continue;	/* ignore it */
155 			len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2;
156 			if (len > TFTP_MAX_PACKET)
157 				goto noak;
158 			e = p + len;
159 			while (*p != '\0' && p < e) {
160 /* 				if (!strcasecmp("blksize", p)) { */
161 				if (!grub_strcmp("blksize", p)) {
162 					p += 8;
163 /* 					if ((packetsize = strtoul(p, &p, 10)) < */
164 					if ((packetsize = getdec(&p)) < TFTP_DEFAULTSIZE_PACKET)
165 						goto noak;
166 					while (p < e && *p) p++;
167 					if (p < e)
168 						p++;
169 				}
170 				else {
171 				noak:
172 					tp.opcode = htons(TFTP_ERROR);
173 					tp.u.err.errcode = 8;
174 /*
175  *	Warning: the following assumes the layout of bootp_t.
176  *	But that's fixed by the IP, UDP and BOOTP specs.
177  */
178 					len = sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) + sizeof(tp.u.err.errcode) +
179 /*
180  *	Normally bad form to omit the format string, but in this case
181  *	the string we are copying from is fixed. sprintf is just being
182  *	used as a strcpy and strlen.
183  */
184 						sprintf((char *)tp.u.err.errmsg,
185 						"RFC1782 error") + 1;
186 					udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
187 						     iport, ntohs(tr->udp.src),
188 						     len, &tp);
189 					return (0);
190 				}
191 			}
192 			if (p > e)
193 				goto noak;
194 			block = tp.u.ack.block = 0; /* this ensures, that */
195 						/* the packet does not get */
196 						/* processed as data! */
197 		}
198 		else if (tr->opcode == htons(TFTP_DATA)) {
199 			len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
200 			if (len > packetsize)	/* shouldn't happen */
201 				continue;	/* ignore it */
202 			block = ntohs(tp.u.ack.block = tr->u.data.block); }
203 		else {/* neither TFTP_OACK nor TFTP_DATA */
204 			break;
205 		}
206 
207 		if ((block || bcounter) && (block != (unsigned short)(prevblock+1))) {
208 			/* Block order should be continuous */
209 			tp.u.ack.block = htons(block = prevblock);
210 		}
211 		tp.opcode = htons(TFTP_ACK);
212 		oport = ntohs(tr->udp.src);
213 		udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, iport,
214 			     oport, TFTP_MIN_PACKET, &tp);	/* ack */
215 		if ((unsigned short)(block-prevblock) != 1) {
216 			/* Retransmission or OACK, don't process via callback
217 			 * and don't change the value of prevblock.  */
218 			continue;
219 		}
220 		prevblock = block;
221 		retry = 0;	/* It's the right place to zero the timer? */
222 		if ((rc = fnc(tr->u.data.download,
223 			      ++bcounter, len, len < packetsize)) <= 0)
224 			return(rc);
225 		if (len < packetsize) {	/* End of data --- fnc should not have returned */
226 			printf("tftp download complete, but\n");
227 			return (1);
228 		}
229 	}
230 	return (0);
231 }
232 
233 /* Fill the buffer by receiving the data via the TFTP protocol.  */
234 static int
235 buf_fill (int abort)
236 {
237 #ifdef TFTP_DEBUG
238   grub_printf ("buf_fill (%d)\n", abort);
239 #endif
240 
241   if (use_bios_pxe)
242 	return (buf_fill_undi(abort));
243 
244   while (! buf_eof && (buf_read + packetsize <= FSYS_BUFLEN))
245     {
246       struct tftp_t *tr;
247       long timeout;
248 
249 #ifdef CONGESTED
250       timeout = rfc2131_sleep_interval (block ? TFTP_REXMT : TIMEOUT, retry);
251 #else
252       timeout = rfc2131_sleep_interval (TIMEOUT, retry);
253 #endif
254 
255       if (! await_reply (await_tftp, iport, NULL, timeout))
256 	{
257 	  if (user_abort)
258 	    return 0;
259 
260 	  if (! block && retry++ < MAX_TFTP_RETRIES)
261 	    {
262 	      /* Maybe initial request was lost.  */
263 #ifdef TFTP_DEBUG
264 	      grub_printf ("Maybe initial request was lost.\n");
265 #endif
266 	      if (! udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
267 				  ++iport, TFTP_PORT, len, &tp))
268 		return 0;
269 
270 	      continue;
271 	    }
272 
273 #ifdef CONGESTED
274 	  if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
275 	    {
276 	      /* We resend our last ack.  */
277 # ifdef TFTP_DEBUG
278 	      grub_printf ("<REXMT>\n");
279 # endif
280 	      udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
281 			    iport, oport,
282 			    TFTP_MIN_PACKET, &tp);
283 	      continue;
284 	    }
285 #endif
286 	  /* Timeout.  */
287 	  return 0;
288 	}
289 
290       tr = (struct tftp_t *) &nic.packet[ETH_HLEN];
291       if (tr->opcode == ntohs (TFTP_ERROR))
292 	{
293 	  grub_printf ("TFTP error %d (%s)\n",
294 		       ntohs (tr->u.err.errcode),
295 		       tr->u.err.errmsg);
296 	  return 0;
297 	}
298 
299       if (tr->opcode == ntohs (TFTP_OACK))
300 	{
301 	  char *p = tr->u.oack.data, *e;
302 
303 #ifdef TFTP_DEBUG
304 	  grub_printf ("OACK ");
305 #endif
306 	  /* Shouldn't happen.  */
307 	  if (prevblock)
308 	    {
309 	      /* Ignore it.  */
310 	      grub_printf ("%s:%d: warning: PREVBLOCK != 0 (0x%x)\n",
311 			   __FILE__, __LINE__, prevblock);
312 	      continue;
313 	    }
314 
315 	  len = ntohs (tr->udp.len) - sizeof (struct udphdr) - 2;
316 	  if (len > TFTP_MAX_PACKET)
317 	    goto noak;
318 
319 	  e = p + len;
320 	  while (*p != '\000' && p < e)
321 	    {
322 	      if (! grub_strcmp ("blksize", p))
323 		{
324 		  p += 8;
325 		  if ((packetsize = getdec (&p)) < TFTP_DEFAULTSIZE_PACKET)
326 		    goto noak;
327 #ifdef TFTP_DEBUG
328 		  grub_printf ("blksize = %d\n", packetsize);
329 #endif
330 		}
331 	      else if (! grub_strcmp ("tsize", p))
332 		{
333 		  p += 6;
334 		  if ((filemax = getdec (&p)) < 0)
335 		    {
336 		      filemax = -1;
337 		      goto noak;
338 		    }
339 #ifdef TFTP_DEBUG
340 		  grub_printf ("tsize = %d\n", filemax);
341 #endif
342 		}
343 	      else
344 		{
345 		noak:
346 #ifdef TFTP_DEBUG
347 		  grub_printf ("NOAK\n");
348 #endif
349 		  tp.opcode = htons (TFTP_ERROR);
350 		  tp.u.err.errcode = 8;
351 		  len = (grub_sprintf ((char *) tp.u.err.errmsg,
352 				       "RFC1782 error")
353 			 + sizeof (tp.ip) + sizeof (tp.udp)
354 			 + sizeof (tp.opcode) + sizeof (tp.u.err.errcode)
355 			 + 1);
356 		  udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
357 				iport, ntohs (tr->udp.src),
358 				len, &tp);
359 		  return 0;
360 		}
361 
362 	      while (p < e && *p)
363 		p++;
364 
365 	      if (p < e)
366 		p++;
367 	    }
368 
369 	  if (p > e)
370 	    goto noak;
371 
372 	  /* This ensures that the packet does not get processed as
373 	     data!  */
374 	  block = tp.u.ack.block = 0;
375 	}
376       else if (tr->opcode == ntohs (TFTP_DATA))
377 	{
378 #ifdef TFTP_DEBUG
379 	  grub_printf ("DATA ");
380 #endif
381 	  len = ntohs (tr->udp.len) - sizeof (struct udphdr) - 4;
382 
383 	  /* Shouldn't happen.  */
384 	  if (len > packetsize)
385 	    {
386 	      /* Ignore it.  */
387 	      grub_printf ("%s:%d: warning: LEN > PACKETSIZE (0x%x > 0x%x)\n",
388 			   __FILE__, __LINE__, len, packetsize);
389 	      continue;
390 	    }
391 
392 	  block = ntohs (tp.u.ack.block = tr->u.data.block);
393 	}
394       else
395 	/* Neither TFTP_OACK nor TFTP_DATA.  */
396 	break;
397 
398       if ((block || bcounter) && (block != prevblock + (unsigned short) 1))
399 	/* Block order should be continuous */
400 	tp.u.ack.block = htons (block = prevblock);
401 
402       /* Should be continuous.  */
403       tp.opcode = abort ? htons (TFTP_ERROR) : htons (TFTP_ACK);
404       oport = ntohs (tr->udp.src);
405 
406 #ifdef TFTP_DEBUG
407       grub_printf ("ACK\n");
408 #endif
409       /* Ack.  */
410       udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr, iport,
411 		    oport, TFTP_MIN_PACKET, &tp);
412 
413       if (abort)
414 	{
415 	  buf_eof = 1;
416 	  break;
417 	}
418 
419       /* Retransmission or OACK.  */
420       if ((unsigned short) (block - prevblock) != 1)
421 	/* Don't process.  */
422 	continue;
423 
424       prevblock = block;
425       /* Is it the right place to zero the timer?  */
426       retry = 0;
427 
428       /* In GRUB, this variable doesn't play any important role at all,
429 	 but use it for consistency with Etherboot.  */
430       bcounter++;
431 
432       /* Copy the downloaded data to the buffer.  */
433       grub_memmove (buf + buf_read, tr->u.data.download, len);
434       buf_read += len;
435 
436       /* End of data.  */
437       if (len < packetsize)
438 	buf_eof = 1;
439     }
440 
441   return 1;
442 }
443 
444 /* Send the RRQ whose length is LEN.  */
445 static int
446 send_rrq (void)
447 {
448   /* Initialize some variables.  */
449   retry = 0;
450   block = 0;
451   prevblock = 0;
452   packetsize = TFTP_DEFAULTSIZE_PACKET;
453   bcounter = 0;
454 
455   buf = (char *) FSYS_BUF;
456   buf_eof = 0;
457   buf_read = 0;
458   saved_filepos = 0;
459 
460   rx_qdrain();
461 
462 #ifdef TFTP_DEBUG
463   grub_printf ("send_rrq ()\n");
464   {
465     int i;
466     char *p;
467 
468     for (i = 0, p = (char *) &tp; i < len; i++)
469       if (p[i] >= ' ' && p[i] <= '~')
470 	grub_putchar (p[i]);
471       else
472 	grub_printf ("\\%x", (unsigned) p[i]);
473 
474     grub_putchar ('\n');
475   }
476 #endif
477   /* Send the packet.  */
478   return udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
479 		       TFTP_PORT, len, &tp);
480 }
481 
482 /* Mount the network drive. If the drive is ready, return one, otherwise
483    return zero.  */
484 int
485 tftp_mount (void)
486 {
487   /* Check if the current drive is the network drive.  */
488   if (current_drive != NETWORK_DRIVE)
489     return 0;
490 
491   /* If the drive is not initialized yet, abort.  */
492   if (! network_ready)
493     return 0;
494 
495   return 1;
496 }
497 
498 /* Read up to SIZE bytes, returned in ADDR.  */
499 int
500 tftp_read (char *addr, int size)
501 {
502   /* How many bytes is read?  */
503   int ret = 0;
504 
505 #ifdef TFTP_DEBUG
506   grub_printf ("tftp_read (0x%x, %d)\n", (int) addr, size);
507 #endif
508 
509   if (use_bios_pxe)
510 	return (tftp_read_undi(addr, size));
511 
512   if (filepos < saved_filepos)
513     {
514       /* Uggh.. FILEPOS has been moved backwards. So reopen the file.  */
515       buf_read = 0;
516       buf_fill (1);
517       grub_memmove ((char *) &tp, (char *) &saved_tp, saved_len);
518       len = saved_len;
519 #ifdef TFTP_DEBUG
520       {
521 	int i;
522 	grub_printf ("opcode = 0x%x, rrq = ", (unsigned long) tp.opcode);
523 	for (i = 0; i < TFTP_DEFAULTSIZE_PACKET; i++)
524 	  {
525 	    if (tp.u.rrq[i] >= ' ' && tp.u.rrq[i] <= '~')
526 	      grub_putchar (tp.u.rrq[i]);
527 	    else
528 	      grub_putchar ('*');
529 	  }
530 	grub_putchar ('\n');
531       }
532 #endif
533 
534       if (! send_rrq ())
535 	{
536 	  errnum = ERR_WRITE;
537 	  return 0;
538 	}
539     }
540 
541   while (size > 0)
542     {
543       int amt = buf_read + saved_filepos - filepos;
544 
545       /* If the length that can be copied from the buffer is over the
546 	 requested size, cut it down.  */
547       if (amt > size)
548 	amt = size;
549 
550       if (amt > 0)
551 	{
552 	  /* Copy the buffer to the supplied memory space.  */
553 	  grub_memmove (addr, buf + filepos - saved_filepos, amt);
554 	  size -= amt;
555 	  addr += amt;
556 	  filepos += amt;
557 	  ret += amt;
558 
559 	  /* If the size of the empty space becomes small, move the unused
560 	     data forwards.  */
561 	  if (filepos - saved_filepos > FSYS_BUFLEN / 2)
562 	    {
563 	      grub_memmove (buf, buf + FSYS_BUFLEN / 2, FSYS_BUFLEN / 2);
564 	      buf_read -= FSYS_BUFLEN / 2;
565 	      saved_filepos += FSYS_BUFLEN / 2;
566 	    }
567 	}
568       else
569 	{
570 	  /* Skip the whole buffer.  */
571 	  saved_filepos += buf_read;
572 	  buf_read = 0;
573 	}
574 
575       /* Read the data.  */
576       if (size > 0 && ! buf_fill (0))
577 	{
578 	  errnum = ERR_READ;
579 	  return 0;
580 	}
581 
582       /* Sanity check.  */
583       if (size > 0 && buf_read == 0)
584 	{
585 	  errnum = ERR_READ;
586 	  return 0;
587 	}
588     }
589 
590   return ret;
591 }
592 
593 /* Check if the file DIRNAME really exists. Get the size and save it in
594    FILEMAX.  */
595 int
596 tftp_dir (char *dirname)
597 {
598   int ch;
599 
600 #ifdef TFTP_DEBUG
601   grub_printf ("tftp_dir (%s)\n", dirname);
602 #endif
603 
604   if (use_bios_pxe)
605 	return (tftp_dir_undi(dirname));
606 
607   /* In TFTP, there is no way to know what files exist.  */
608   if (print_possibilities)
609     return 1;
610 
611   /* Don't know the size yet.  */
612   filemax = -1;
613 
614  reopen:
615   /* Construct the TFTP request packet.  */
616   tp.opcode = htons (TFTP_RRQ);
617   /* Terminate the filename.  */
618   ch = nul_terminate (dirname);
619   /* Make the request string (octet, blksize and tsize).  */
620   len = (grub_sprintf ((char *) tp.u.rrq,
621 		       "%s%coctet%cblksize%c%d%ctsize%c0",
622 		       dirname, 0, 0, 0, TFTP_MAX_PACKET, 0, 0)
623 	 + sizeof (tp.ip) + sizeof (tp.udp) + sizeof (tp.opcode) + 1);
624   /* Restore the original DIRNAME.  */
625   dirname[grub_strlen (dirname)] = ch;
626   /* Save the TFTP packet so that we can reopen the file later.  */
627   grub_memmove ((char *) &saved_tp, (char *) &tp, len);
628   saved_len = len;
629   if (! send_rrq ())
630     {
631       errnum = ERR_WRITE;
632       return 0;
633     }
634 
635   /* Read the data.  */
636   if (! buf_fill (0))
637     {
638       errnum = ERR_FILE_NOT_FOUND;
639       return 0;
640     }
641 
642   if (filemax == -1)
643     {
644       /* The server doesn't support the "tsize" option, so we must read
645 	 the file twice...  */
646 
647       /* Zero the size of the file.  */
648       filemax = 0;
649       do
650 	{
651 	  /* Add the length of the downloaded data.  */
652 	  filemax += buf_read;
653 	  /* Reset the offset. Just discard the contents of the buffer.  */
654 	  buf_read = 0;
655 	  /* Read the data.  */
656 	  if (! buf_fill (0))
657 	    {
658 	      errnum = ERR_READ;
659 	      return 0;
660 	    }
661 	}
662       while (! buf_eof);
663 
664       /* Maybe a few amounts of data remains.  */
665       filemax += buf_read;
666 
667       /* Retry the open instruction.  */
668       goto reopen;
669     }
670 
671   return 1;
672 }
673 
674 /* Close the file.  */
675 void
676 tftp_close (void)
677 {
678 #ifdef TFTP_DEBUG
679   grub_printf ("tftp_close ()\n");
680 #endif
681 
682   if (use_bios_pxe) {
683 	tftp_close_undi();
684 	return;
685   }
686 
687   buf_read = 0;
688   buf_fill (1);
689 }
690 
691 /* tftp implementation using BIOS established PXE stack */
692 
693 static int tftp_file_read_undi(const char *name,
694     int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
695 {
696 	int rc;
697 	uint16_t len;
698 
699 	buf = (char *)&nic.packet;
700 	/* open tftp session */
701 	if (eb_pxenv_tftp_open(name, arptable[ARP_SERVER].ipaddr,
702 	    arptable[ARP_GATEWAY].ipaddr, &packetsize) == 0)
703 		return (0);
704 
705 	/* read blocks and invoke fnc for each block */
706 	for (;;) {
707 		rc = eb_pxenv_tftp_read(buf, &len);
708 		if (rc == 0)
709 			break;
710 		rc = fnc(buf, ++block, len, len < packetsize);
711 		if (rc <= 0 || len < packetsize)
712 			break;
713 	}
714 
715 	(void) eb_pxenv_tftp_close();
716 	return (rc > 0 ? 1 : 0);
717 }
718 
719 /* Fill the buffer by reading the data via the TFTP protocol.  */
720 static int
721 buf_fill_undi(int abort)
722 {
723 	int rc;
724 	uint8_t *tmpbuf;
725 
726 	while (! buf_eof && (buf_read + packetsize <= FSYS_BUFLEN)) {
727 		poll_interruptions();
728 		if (user_abort)
729 			return 0;
730 		if (abort) {
731 			buf_eof = 1;
732 			break;
733 		}
734 
735 		if (eb_pxenv_tftp_read(buf + buf_read, &len) == 0)
736 			return (0);
737 
738 		buf_read += len;
739 
740 		/* End of data.  */
741 		if (len < packetsize)
742 			buf_eof = 1;
743 	}
744 	return 1;
745 }
746 
747 static void
748 tftp_reopen_undi(void)
749 {
750 	tftp_close();
751 	(void) eb_pxenv_tftp_open(saved_name, arptable[ARP_SERVER].ipaddr,
752 	    arptable[ARP_GATEWAY].ipaddr, &packetsize);
753 
754 	buf_eof = 0;
755 	buf_read = 0;
756 	saved_filepos = 0;
757 }
758 
759 /* Read up to SIZE bytes, returned in ADDR.  */
760 static int
761 tftp_read_undi(char *addr, int size)
762 {
763 	int ret = 0;
764 
765 	if (filepos < saved_filepos) {
766 		/* Uggh.. FILEPOS has been moved backwards. reopen the file. */
767 		tftp_reopen_undi();
768 	}
769 
770 	while (size > 0) {
771 		int amt = buf_read + saved_filepos - filepos;
772 
773 		/* If the length that can be copied from the buffer is over
774 		   the requested size, cut it down. */
775 		if (amt > size)
776 			amt = size;
777 
778 		if (amt > 0) {
779 			/* Copy the buffer to the supplied memory space.  */
780 			grub_memmove (addr, buf + filepos - saved_filepos, amt);
781 			size -= amt;
782 			addr += amt;
783 			filepos += amt;
784 			ret += amt;
785 
786 			/* If the size of the empty space becomes small,
787 			 * move the unused data forwards.
788 			 */
789 			if (filepos - saved_filepos > FSYS_BUFLEN / 2) {
790 				grub_memmove (buf, buf + FSYS_BUFLEN / 2,
791 				    FSYS_BUFLEN / 2);
792 				buf_read -= FSYS_BUFLEN / 2;
793 				saved_filepos += FSYS_BUFLEN / 2;
794 			}
795 		} else {
796 			/* Skip the whole buffer.  */
797 			saved_filepos += buf_read;
798 			buf_read = 0;
799 		}
800 
801 		/* Read the data.  */
802 		if (size > 0 && ! buf_fill (0)) {
803 			errnum = ERR_READ;
804 			return 0;
805 		}
806 
807 		/* Sanity check.  */
808 		if (size > 0 && buf_read == 0) {
809 			errnum = ERR_READ;
810 			return 0;
811 		}
812 	}
813 
814 	return ret;
815 }
816 
817 static int
818 tftp_dir_undi(char *dirname)
819 {
820 	int rc, ch;
821 	uint16_t len;
822 
823 	/* In TFTP, there is no way to know what files exist.  */
824 	if (print_possibilities)
825 		return 1;
826 
827 	/* name may be space terminated */
828 	ch = nul_terminate(dirname);
829 	saved_name = (char *)&saved_tp;
830 	sprintf(saved_name, "%s", dirname);
831 
832   	/* Restore the original dirname */
833 	dirname[grub_strlen (dirname)] = ch;
834 
835 	/* get the file size; must call before tftp_open */
836 	rc = eb_pxenv_tftp_get_fsize(saved_name, arptable[ARP_SERVER].ipaddr,
837 	    arptable[ARP_GATEWAY].ipaddr, &filemax);
838 
839 	/* open tftp session */
840 	if (eb_pxenv_tftp_open(saved_name, arptable[ARP_SERVER].ipaddr,
841 	    arptable[ARP_GATEWAY].ipaddr, &packetsize) == 0)
842 		return (0);
843 
844 	buf = (char *) FSYS_BUF;
845 	buf_eof = 0;
846 	buf_read = 0;
847 	saved_filepos = 0;
848 
849 	if (rc == 0) {
850 		/* Read the entire file to get filemax */
851 		filemax = 0;
852 		do {
853 			/* Add the length of the downloaded data.  */
854 			filemax += buf_read;
855 			buf_read = 0;
856 			if (! buf_fill (0)) {
857 				errnum = ERR_READ;
858 				return 0;
859 			}
860 		} while (! buf_eof);
861 
862 		/* Maybe a few amounts of data remains.  */
863 		filemax += buf_read;
864 
865 		tftp_reopen_undi(); /* reopen file to read from beginning */
866 	}
867 
868 	return (1);
869 }
870 
871 static void
872 tftp_close_undi(void)
873 {
874 	buf_read = 0;
875 	buf_fill (1);
876 	(void) eb_pxenv_tftp_close();
877 }
878