/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2000,2001,2002,2004 Free Software Foundation, Inc.
*
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/**************************************************************************
Author: Martin Renters
Date: Dec/93
**************************************************************************/
/* #define TFTP_DEBUG 1 */
#include <filesys.h>
#include <shared.h>
#include "grub.h"
#include "tftp.h"
#include "nic.h"
static int tftp_file_read_undi(const char *name,
int (*fnc)(unsigned char *, unsigned int, unsigned int, int));
static int tftp_dir_undi(char *dirname);
static void tftp_close_undi(void);
static int buf_fill_undi(int abort);
extern int use_bios_pxe;
static int retry;
static unsigned short oport = 0;
static int bcounter;
static int packetsize;
static int saved_filepos;
/**
* tftp_read
*
* Read file with _name_, data handled by _fnc_. In fact, grub never
* use it, we just use it to read dhcp config file.
*/
{
static int tftp_count = 0;
if (!udp) {
return 0;
}
return 0;
return 0;
tftp_count++; /* show progress */
if ((tftp_count % 1000) == 0)
printf(".");
return 1;
}
{
int rc;
if (use_bios_pxe)
retry = 0;
block = 0;
prevblock = 0;
bcounter = 0;
rx_qdrain();
/* Warning: the following assumes the layout of bootp_t.
But that's fixed by the IP, UDP and BOOTP specs. */
return (0);
for (;;)
{
long timeout;
#ifdef CONGESTED
#else
#endif
{
{ /* maybe initial request was lost */
return (0);
continue;
}
#ifdef CONGESTED
{ /* we resend our last ack */
#ifdef MDEBUG
printf("<REXMT>\n");
#endif
TFTP_MIN_PACKET, &tp);
continue;
}
#endif
break; /* timeout */
}
{
printf("TFTP error %d (%s)\n",
break;
}
if (prevblock) /* shouldn't happen */
continue; /* ignore it */
if (len > TFTP_MAX_PACKET)
goto noak;
e = p + len;
while (*p != '\0' && p < e) {
/* if (!strcasecmp("blksize", p)) { */
if (!grub_strcmp("blksize", p)) {
p += 8;
/* if ((packetsize = strtoul(p, &p, 10)) < */
goto noak;
while (p < e && *p) p++;
if (p < e)
p++;
}
else {
noak:
/*
* Warning: the following assumes the layout of bootp_t.
* But that's fixed by the IP, UDP and BOOTP specs.
*/
/*
* Normally bad form to omit the format string, but in this case
* the string we are copying from is fixed. sprintf is just being
* used as a strcpy and strlen.
*/
"RFC1782 error") + 1;
return (0);
}
}
if (p > e)
goto noak;
/* the packet does not get */
/* processed as data! */
}
continue; /* ignore it */
else {/* neither TFTP_OACK nor TFTP_DATA */
break;
}
/* Block order should be continuous */
}
/* Retransmission or OACK, don't process via callback
* and don't change the value of prevblock. */
continue;
}
retry = 0; /* It's the right place to zero the timer? */
return(rc);
printf("tftp download complete, but\n");
return (1);
}
}
return (0);
}
/* Fill the buffer by receiving the data via the TFTP protocol. */
static int
{
#ifdef TFTP_DEBUG
#endif
if (use_bios_pxe)
return (buf_fill_undi(abort));
{
long timeout;
#ifdef CONGESTED
#else
#endif
{
if (user_abort)
return 0;
{
/* Maybe initial request was lost. */
#ifdef TFTP_DEBUG
grub_printf ("Maybe initial request was lost.\n");
#endif
return 0;
continue;
}
#ifdef CONGESTED
{
/* We resend our last ack. */
# ifdef TFTP_DEBUG
grub_printf ("<REXMT>\n");
# endif
TFTP_MIN_PACKET, &tp);
continue;
}
#endif
/* Timeout. */
return 0;
}
{
grub_printf ("TFTP error %d (%s)\n",
return 0;
}
{
#ifdef TFTP_DEBUG
grub_printf ("OACK ");
#endif
/* Shouldn't happen. */
if (prevblock)
{
/* Ignore it. */
grub_printf ("%s:%d: warning: PREVBLOCK != 0 (0x%x)\n",
continue;
}
if (len > TFTP_MAX_PACKET)
goto noak;
e = p + len;
while (*p != '\000' && p < e)
{
if (! grub_strcmp ("blksize", p))
{
p += 8;
goto noak;
#ifdef TFTP_DEBUG
#endif
}
else if (! grub_strcmp ("tsize", p))
{
p += 6;
{
filemax = -1;
goto noak;
}
#ifdef TFTP_DEBUG
#endif
}
else
{
noak:
#ifdef TFTP_DEBUG
grub_printf ("NOAK\n");
#endif
"RFC1782 error")
+ 1);
return 0;
}
while (p < e && *p)
p++;
if (p < e)
p++;
}
if (p > e)
goto noak;
/* This ensures that the packet does not get processed as
data! */
}
{
#ifdef TFTP_DEBUG
grub_printf ("DATA ");
#endif
/* Shouldn't happen. */
if (len > packetsize)
{
/* Ignore it. */
grub_printf ("%s:%d: warning: LEN > PACKETSIZE (0x%x > 0x%x)\n",
continue;
}
}
else
/* Neither TFTP_OACK nor TFTP_DATA. */
break;
/* Block order should be continuous */
/* Should be continuous. */
#ifdef TFTP_DEBUG
grub_printf ("ACK\n");
#endif
/* Ack. */
if (abort)
{
buf_eof = 1;
break;
}
/* Retransmission or OACK. */
/* Don't process. */
continue;
/* Is it the right place to zero the timer? */
retry = 0;
/* In GRUB, this variable doesn't play any important role at all,
but use it for consistency with Etherboot. */
bcounter++;
/* Copy the downloaded data to the buffer. */
/* End of data. */
if (len < packetsize)
buf_eof = 1;
}
return 1;
}
/* Send the RRQ whose length is LEN. */
static int
send_rrq (void)
{
/* Initialize some variables. */
retry = 0;
block = 0;
prevblock = 0;
bcounter = 0;
buf_eof = 0;
buf_read = 0;
saved_filepos = 0;
rx_qdrain();
#ifdef TFTP_DEBUG
grub_printf ("send_rrq ()\n");
{
int i;
char *p;
if (p[i] >= ' ' && p[i] <= '~')
grub_putchar (p[i]);
else
grub_printf ("\\%x", (unsigned) p[i]);
grub_putchar ('\n');
}
#endif
/* Send the packet. */
}
/* Mount the network drive. If the drive is ready, return one, otherwise
return zero. */
int
tftp_mount (void)
{
/* Check if the current drive is the network drive. */
if (current_drive != NETWORK_DRIVE)
return 0;
/* If the drive is not initialized yet, abort. */
if (! network_ready)
return 0;
return 1;
}
/* Read up to SIZE bytes, returned in ADDR. */
int
{
/* How many bytes is read? */
int ret = 0;
#ifdef TFTP_DEBUG
#endif
if (use_bios_pxe)
if (filepos < saved_filepos)
{
/* Uggh.. FILEPOS has been moved backwards. So reopen the file. */
buf_read = 0;
buf_fill (1);
#ifdef TFTP_DEBUG
{
int i;
for (i = 0; i < TFTP_DEFAULTSIZE_PACKET; i++)
{
else
grub_putchar ('*');
}
grub_putchar ('\n');
}
#endif
if (! send_rrq ())
{
return 0;
}
}
while (size > 0)
{
/* If the length that can be copied from the buffer is over the
requested size, cut it down. */
if (amt > 0)
{
/* Copy the buffer to the supplied memory space. */
/* If the size of the empty space becomes small, move the unused
data forwards. */
{
}
}
else
{
/* Skip the whole buffer. */
buf_read = 0;
}
/* Read the data. */
{
return 0;
}
/* Sanity check. */
{
return 0;
}
}
return ret;
}
/* Check if the file DIRNAME really exists. Get the size and save it in
FILEMAX. */
int
{
int ch;
#ifdef TFTP_DEBUG
#endif
if (use_bios_pxe)
return (tftp_dir_undi(dirname));
/* In TFTP, there is no way to know what files exist. */
if (print_possibilities)
return 1;
/* Don't know the size yet. */
filemax = -1;
/* Construct the TFTP request packet. */
/* Terminate the filename. */
/* Make the request string (octet, blksize and tsize). */
"%s%coctet%cblksize%c%d%ctsize%c0",
dirname, 0, 0, 0, TFTP_MAX_PACKET, 0, 0)
/* Restore the original DIRNAME. */
/* Save the TFTP packet so that we can reopen the file later. */
if (! send_rrq ())
{
return 0;
}
/* Read the data. */
if (! buf_fill (0))
{
return 0;
}
if (filemax == -1)
{
/* The server doesn't support the "tsize" option, so we must read
the file twice... */
/* Zero the size of the file. */
filemax = 0;
do
{
/* Add the length of the downloaded data. */
/* Reset the offset. Just discard the contents of the buffer. */
buf_read = 0;
/* Read the data. */
if (! buf_fill (0))
{
return 0;
}
}
while (! buf_eof);
/* Maybe a few amounts of data remains. */
/* Retry the open instruction. */
goto reopen;
}
return 1;
}
/* Close the file. */
void
tftp_close (void)
{
#ifdef TFTP_DEBUG
grub_printf ("tftp_close ()\n");
#endif
if (use_bios_pxe) {
return;
}
buf_read = 0;
buf_fill (1);
}
/* tftp implementation using BIOS established PXE stack */
int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
{
int rc;
/* open tftp session */
return (0);
/* read blocks and invoke fnc for each block */
for (;;) {
if (rc == 0)
break;
break;
}
(void) eb_pxenv_tftp_close();
return (rc > 0 ? 1 : 0);
}
/* Fill the buffer by reading the data via the TFTP protocol. */
static int
{
int rc;
if (user_abort)
return 0;
if (abort) {
buf_eof = 1;
break;
}
return (0);
/* End of data. */
if (len < packetsize)
buf_eof = 1;
}
return 1;
}
static void
tftp_reopen_undi(void)
{
tftp_close();
buf_eof = 0;
buf_read = 0;
saved_filepos = 0;
}
/* Read up to SIZE bytes, returned in ADDR. */
static int
{
int ret = 0;
if (filepos < saved_filepos) {
/* Uggh.. FILEPOS has been moved backwards. reopen the file. */
}
while (size > 0) {
/* If the length that can be copied from the buffer is over
the requested size, cut it down. */
if (amt > 0) {
/* Copy the buffer to the supplied memory space. */
/* If the size of the empty space becomes small,
* move the unused data forwards.
*/
FSYS_BUFLEN / 2);
}
} else {
/* Skip the whole buffer. */
buf_read = 0;
}
/* Read the data. */
return 0;
}
/* Sanity check. */
return 0;
}
}
return ret;
}
static int
{
/* In TFTP, there is no way to know what files exist. */
if (print_possibilities)
return 1;
/* name may be space terminated */
saved_name = (char *)&saved_tp;
/* Restore the original dirname */
/* get the file size; must call before tftp_open */
/* open tftp session */
return (0);
buf_eof = 0;
buf_read = 0;
saved_filepos = 0;
if (rc == 0) {
/* Read the entire file to get filemax */
filemax = 0;
do {
/* Add the length of the downloaded data. */
buf_read = 0;
if (! buf_fill (0)) {
return 0;
}
} while (! buf_eof);
/* Maybe a few amounts of data remains. */
tftp_reopen_undi(); /* reopen file to read from beginning */
}
return (1);
}
static void
tftp_close_undi(void)
{
buf_read = 0;
buf_fill (1);
(void) eb_pxenv_tftp_close();
}