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