|  | #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; | 
|  | } |