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 != (unsigned short) (prevblock + 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