mirror of
https://git.savannah.gnu.org/git/grub.git
synced 2026-10-27 09:39:22 +00:00
228 lines
4.9 KiB
C
228 lines
4.9 KiB
C
#include "../stage2/filesys.h"
|
|
|
|
#include "netboot.h"
|
|
#include "netboot_config.h"
|
|
#include "nic.h"
|
|
|
|
#include "ip.h"
|
|
|
|
#if 0
|
|
|
|
#define BUFSIZE (20*TFTP_DEFAULTSIZE_PACKET)
|
|
static char buf[BUFSIZE];
|
|
|
|
#else
|
|
|
|
/* use GRUB's file system buffer */
|
|
#define BUFSIZE (32*1024)
|
|
#define buf ((char *)(FSYS_BUF))
|
|
|
|
#endif
|
|
|
|
static int buf_read = 0, buf_eof = 0;
|
|
static unsigned long buf_fileoff;
|
|
|
|
static int retry;
|
|
static unsigned short isocket = 2000;
|
|
static unsigned short osocket;
|
|
static unsigned short len, block, prevblock;
|
|
static struct tftp_t *tr;
|
|
static struct tftp_t tp;
|
|
static int packetsize = TFTP_DEFAULTSIZE_PACKET;
|
|
|
|
static int buf_fill(int abort);
|
|
|
|
int tftp_mount(void)
|
|
{
|
|
if (current_drive != 0x20) /* only mount network drives */
|
|
return 0;
|
|
|
|
if (!ip_init())
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* read "size" bytes from file position "filepos" to "addr" */
|
|
int tftp_read(char *addr, int size)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (filepos < buf_fileoff)
|
|
{
|
|
printf("tftp_read: can't read from filepos=%d, buf_fileoff=%d\n",
|
|
filepos, buf_fileoff);
|
|
errnum = ERR_BOOT_FAILURE;
|
|
return 0;
|
|
}
|
|
|
|
while (size > 0)
|
|
{
|
|
if (filepos < buf_fileoff + buf_read)
|
|
{
|
|
/* can copy stuff from buffer */
|
|
int tocopy = buf_fileoff + buf_read - filepos;
|
|
if (tocopy > size) tocopy = size;
|
|
bcopy(buf + (filepos - buf_fileoff), (void*) addr, tocopy);
|
|
size -= tocopy;
|
|
addr += tocopy;
|
|
filepos += tocopy;
|
|
ret += tocopy;
|
|
|
|
if (buf_eof && (filepos + size >= buf_fileoff + buf_read))
|
|
break; /* end of file */
|
|
|
|
continue;
|
|
}
|
|
else if ((filepos == buf_fileoff + buf_read) && buf_eof)
|
|
break; /* end of file */
|
|
|
|
if (buf_eof) /* filepos beyond end of file */
|
|
{
|
|
errnum = ERR_READ;
|
|
return 0;
|
|
}
|
|
|
|
/* move buffer contents forward by 1/2 buffer size */
|
|
bcopy(buf + BUFSIZE/2, buf, BUFSIZE/2);
|
|
buf_fileoff += BUFSIZE/2;
|
|
buf_read -= BUFSIZE/2;
|
|
|
|
if (! buf_fill(0)) /* read more data */
|
|
{
|
|
errnum = ERR_READ;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int tftp_dir(char *dirname)
|
|
{
|
|
char name[100], *np;
|
|
|
|
if (print_possibilities)
|
|
{
|
|
printf(" TFTP doesn't support listing the directory; Sorry!\n");
|
|
return 1;
|
|
}
|
|
|
|
/* open the file */
|
|
np = name;
|
|
while (dirname && *dirname && !isspace(*dirname))
|
|
*np++ = *dirname++;
|
|
*np = 0;
|
|
|
|
buf_eof = buf_read = buf_fileoff = 0;
|
|
|
|
retry = 0;
|
|
block = 0;
|
|
prevblock = 0;
|
|
packetsize = TFTP_DEFAULTSIZE_PACKET;
|
|
|
|
/* send tftp read request */
|
|
tp.opcode = htons(TFTP_RRQ);
|
|
len = 30 + sprintf((char *)tp.u.rrq, "%s%coctet%cblksize%c%d",
|
|
name, 0, 0, 0, TFTP_MAX_PACKET) + 1;
|
|
|
|
if (!udp_transmit(arptable[ARP_SERVER].ipaddr, ++isocket, TFTP,
|
|
len, (char *)&tp))
|
|
{
|
|
printf("tftp_dir: can't transmit TFTP read request\n");
|
|
errnum = ERR_BOOT_FAILURE;
|
|
return (0);
|
|
}
|
|
if (! buf_fill(0))
|
|
{
|
|
printf("tftp_dir: can't read from file\n");
|
|
errnum = ERR_FILE_NOT_FOUND;
|
|
return 0;
|
|
}
|
|
|
|
/* filemax = 234620; */
|
|
filemax = 16L*1024*1024; /* XXX 16MB is max filesize */
|
|
|
|
return 1;
|
|
}
|
|
|
|
#if 0
|
|
void tftp_close(void)
|
|
{
|
|
buf_read = 0;
|
|
buf_fill(1); /* abort. */
|
|
}
|
|
#endif
|
|
|
|
static int buf_fill(int abort)
|
|
{
|
|
while (!buf_eof && (buf_read + packetsize <= BUFSIZE))
|
|
{
|
|
/* read a packet from the network */
|
|
if (!await_reply(AWAIT_TFTP, isocket, NULL))
|
|
{
|
|
if (ip_abort)
|
|
return 0;
|
|
|
|
if (prevblock == 0 && retry++ < MAX_TFTP_RETRIES)
|
|
{ /* maybe initial request was lost */
|
|
rfc951_sleep(retry);
|
|
if (!udp_transmit(arptable[ARP_SERVER].ipaddr,
|
|
++isocket, TFTP, len, (char *)&tp))
|
|
return (0);
|
|
continue;
|
|
}
|
|
return 0; /* timeout on other blocks */
|
|
}
|
|
|
|
/* we got a packet */
|
|
|
|
tr = (struct tftp_t *)&nic.packet[ETHER_HDR_SIZE];
|
|
|
|
if (tr->opcode == ntohs(TFTP_ERROR)) /* error? */
|
|
{
|
|
printf("TFTP error %d (%s)\r\n",
|
|
ntohs(tr->u.err.errcode),
|
|
tr->u.err.errmsg);
|
|
return 0;
|
|
}
|
|
else if (tr->opcode == ntohs(TFTP_OACK))
|
|
{
|
|
continue; /* ignore */
|
|
}
|
|
else if (tr->opcode == ntohs(TFTP_DATA))
|
|
{
|
|
retry = MAX_TFTP_RETRIES; /*no more retries! */
|
|
len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
|
|
if (len > packetsize) /* shouldn't happen */
|
|
continue; /* ignore it */
|
|
block = ntohs(tp.u.ack.block = tr->u.data.block);
|
|
}
|
|
else /* neither TFTP_OACK nor TFTP_DATA */
|
|
return 0;
|
|
|
|
tp.opcode = abort ? htons(TFTP_ERROR) : htons(TFTP_ACK);
|
|
osocket = ntohs(tr->udp.src);
|
|
udp_transmit(arptable[ARP_SERVER].ipaddr, isocket,
|
|
osocket, TFTP_MIN_PACKET, (char *)&tp); /* ack */
|
|
|
|
if (abort)
|
|
{
|
|
buf_eof = 1;
|
|
break;
|
|
}
|
|
|
|
if (block <= prevblock) /* retransmission or OACK */
|
|
continue; /* don't process */
|
|
prevblock = block;
|
|
|
|
bcopy(tr->u.data.download, buf + buf_read, len);
|
|
buf_read += len;
|
|
|
|
if (len < packetsize) /* End of data */
|
|
buf_eof = 1;
|
|
}
|
|
|
|
return 1;
|
|
}
|