rx: strip padding from last block. closes bug 501.
No binary (build failed)
—
(log yok)
| @@ -1,15 +1,12 @@ | |
| /* vi: set sw=4 ts=4: */ | |
| - | /*------------------------------------------------------------------------- |
| - | * Filename: xmodem.c |
| + | /* |
| * Copyright: Copyright (C) 2001, Hewlett-Packard Company | |
| * Author: Christopher Hoover <[email protected]> | |
| * Description: xmodem functionality for uploading of kernels | |
| * and the like | |
| * Created at: Thu Dec 20 01:58:08 PST 2001 | |
| - | *-----------------------------------------------------------------------*/ |
| - | /* |
| - | * xmodem.c: xmodem functionality for uploading of kernels and |
| - | * the like |
| + | * |
| + | * xmodem functionality for uploading of kernels and the like |
| * | |
| * Copyright (C) 2001 Hewlett-Packard Laboratories | |
| * | |
| @@ -26,6 +23,7 @@ | |
| #define ACK 0x06 | |
| #define NAK 0x15 | |
| #define BS 0x08 | |
| + | #define PAD 0x1A |
| /* | |
| Cf: | |
| @@ -44,69 +42,93 @@ Cf: | |
| static int read_byte(unsigned timeout) | |
| { | |
| - | char buf[1]; |
| + | unsigned char buf; |
| int n; | |
| alarm(timeout); | |
| /* NOT safe_read! We want ALRM to interrupt us */ | |
| - | n = read(read_fd, buf, 1); |
| + | n = read(read_fd, &buf, 1); |
| alarm(0); | |
| if (n == 1) | |
| - | return (unsigned char)buf[0]; |
| + | return buf; |
| return -1; | |
| } | |
| static int receive(/*int read_fd, */int file_fd) | |
| { | |
| unsigned char blockBuf[1024]; | |
| + | unsigned blockLength = 0; |
| unsigned errors = 0; | |
| unsigned wantBlockNo = 1; | |
| unsigned length = 0; | |
| int do_crc = 1; | |
| - | char nak = 'C'; |
| + | char reply_char; |
| unsigned timeout = TIMEOUT_LONG; | |
| /* Flush pending input */ | |
| tcflush(read_fd, TCIFLUSH); | |
| /* Ask for CRC; if we get errors, we will go with checksum */ | |
| - | full_write(write_fd, &nak, 1); |
| + | reply_char = 'C'; |
| + | full_write(write_fd, &reply_char, 1); |
| for (;;) { | |
| int blockBegin; | |
| int blockNo, blockNoOnesCompl; | |
| - | int blockLength; |
| - | int cksum_crc; /* cksum OR crc */ |
| + | int cksum_or_crc; |
| int expected; | |
| - | int i,j; |
| + | int i, j; |
| blockBegin = read_byte(timeout); | |
| if (blockBegin < 0) | |
| goto timeout; | |
| + | /* If last block, remove padding */ |
| + | if (blockBegin == EOT) { |
| + | /* Data blocks can be padded with ^Z characters */ |
| + | /* This code tries to detect and remove them */ |
| + | if (blockLength >= 3 |
| + | && blockBuf[blockLength - 1] == PAD |
| + | && blockBuf[blockLength - 2] == PAD |
| + | && blockBuf[blockLength - 3] == PAD |
| + | ) { |
| + | while (blockLength |
| + | && blockBuf[blockLength - 1] == PAD |
| + | ) { |
| + | blockLength--; |
| + | } |
| + | } |
| + | } |
| + | /* Write previously received block */ |
| + | if (blockLength) { |
| + | errno = 0; |
| + | if (full_write(file_fd, blockBuf, blockLength) != blockLength) { |
| + | bb_perror_msg("can't write to file"); |
| + | goto fatal; |
| + | } |
| + | } |
| + | |
| timeout = TIMEOUT; | |
| - | nak = NAK; |
| + | reply_char = NAK; |
| switch (blockBegin) { | |
| case SOH: | |
| case STX: | |
| break; | |
| - | |
| case EOT: | |
| - | nak = ACK; |
| - | full_write(write_fd, &nak, 1); |
| + | reply_char = ACK; |
| + | full_write(write_fd, &reply_char, 1); |
| return length; | |
| - | |
| default: | |
| goto error; | |
| } | |
| - | /* block no */ |
| + | /* Block no */ |
| blockNo = read_byte(TIMEOUT); | |
| if (blockNo < 0) | |
| goto timeout; | |
| - | /* block no one's compliment */ |
| + | /* Block no, in one's complement form */ |
| blockNoOnesCompl = read_byte(TIMEOUT); | |
| if (blockNoOnesCompl < 0) | |
| goto timeout; | |
| @@ -126,15 +148,15 @@ static int receive(/*int read_fd, */int file_fd) | |
| } | |
| if (do_crc) { | |
| - | cksum_crc = read_byte(TIMEOUT); |
| - | if (cksum_crc < 0) |
| + | cksum_or_crc = read_byte(TIMEOUT); |
| + | if (cksum_or_crc < 0) |
| goto timeout; | |
| - | cksum_crc = (cksum_crc << 8) | read_byte(TIMEOUT); |
| - | if (cksum_crc < 0) |
| + | cksum_or_crc = (cksum_or_crc << 8) | read_byte(TIMEOUT); |
| + | if (cksum_or_crc < 0) |
| goto timeout; | |
| } else { | |
| - | cksum_crc = read_byte(TIMEOUT); |
| - | if (cksum_crc < 0) |
| + | cksum_or_crc = read_byte(TIMEOUT); |
| + | if (cksum_or_crc < 0) |
| goto timeout; | |
| } | |
| @@ -155,9 +177,9 @@ static int receive(/*int read_fd, */int file_fd) | |
| expected = expected ^ blockBuf[i] << 8; | |
| for (j = 0; j < 8; j++) { | |
| if (expected & 0x8000) | |
| - | expected = expected << 1 ^ 0x1021; |
| + | expected = (expected << 1) ^ 0x1021; |
| else | |
| - | expected = expected << 1; |
| + | expected = (expected << 1); |
| } | |
| } | |
| expected &= 0xffff; | |
| @@ -166,25 +188,19 @@ static int receive(/*int read_fd, */int file_fd) | |
| expected += blockBuf[i]; | |
| expected &= 0xff; | |
| } | |
| - | if (cksum_crc != expected) { |
| + | if (cksum_or_crc != expected) { |
| bb_error_msg(do_crc ? "crc error, expected 0x%04x, got 0x%04x" | |
| : "checksum error, expected 0x%02x, got 0x%02x", | |
| - | expected, cksum_crc); |
| + | expected, cksum_or_crc); |
| goto error; | |
| } | |
| wantBlockNo++; | |
| length += blockLength; | |
| - | |
| - | errno = 0; |
| - | if (full_write(file_fd, blockBuf, blockLength) != blockLength) { |
| - | bb_perror_msg("can't write to file"); |
| - | goto fatal; |
| - | } |
| next: | |
| errors = 0; | |
| - | nak = ACK; |
| - | full_write(write_fd, &nak, 1); |
| + | reply_char = ACK; |
| + | full_write(write_fd, &reply_char, 1); |
| continue; | |
| error: | |
| timeout: | |
| @@ -192,9 +208,9 @@ static int receive(/*int read_fd, */int file_fd) | |
| if (errors == MAXERRORS) { | |
| /* Abort */ | |
| - | /* if were asking for crc, try again w/o crc */ |
| - | if (nak == 'C') { |
| - | nak = NAK; |
| + | /* If were asking for crc, try again w/o crc */ |
| + | if (reply_char == 'C') { |
| + | reply_char = NAK; |
| errors = 0; | |
| do_crc = 0; | |
| goto timeout; | |
| @@ -209,7 +225,7 @@ static int receive(/*int read_fd, */int file_fd) | |
| /* Flush pending input */ | |
| tcflush(read_fd, TCIFLUSH); | |
| - | full_write(write_fd, &nak, 1); |
| + | full_write(write_fd, &reply_char, 1); |
| } /* for (;;) */ | |
| } | |
| @@ -0,0 +1,27 @@ | |
| + | #!/bin/sh |
| + | # Copyright 2009 by Denys Vlasenko |
| + | # Licensed under GPL v2, see file LICENSE for details. |
| + | |
| + | . testing.sh |
| + | |
| + | # testing "test name" "options" "expected result" "file input" "stdin" |
| + | |
| + | # Simple one-block file transfer |
| + | # rx => 'C' |
| + | # rx <= SOH <blockno> <255-blockno> <128 byte padded with x1A> <crc> <crc> |
| + | # rx => ACK |
| + | # rx <= EOT |
| + | # rx => ACK |
| + | testing "rx" \ |
| + | "rx rx.OUTFILE | hexdump -vC && cat rx.OUTFILE" \ |
| + | "\ |
| + | 00000000 43 06 06 |C..|\n\ |
| + | 00000003\n\ |
| + | ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????" \ |
| + | "" "\1\1\376\ |
| + | ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????\ |
| + | \x1A\x1A\x1A\x1A\x1A\x4B\xB0\4" |
| + | |
| + | rm -f rx.OUTFILE 2>/dev/null |
| + | |
| + | exit $FAILCOUNT |