Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] tty output lossage fix

The patch fixes a few corner cases around tty line editing with
very long input lines:

- n_tty_receive_char(): don't simply drop eol characters,
otherwise canon_data isn't increased and the reader isn't woken
up.

- n_tty_receive_room(): If there is no newline pending and the
edit buffer is full, allow only a single character to be written
(until eol is found and the line is flushed), so characters from
the next line aren't dropped.

- write_chan(): if an incomplete line was written, continue
writing until write() returns 0, otherwise it might not write
the eol character to flush the line and the writer goes to sleep
without ever being woken up.

BTW the core problem is that part of this should be handled in the
receive_buf path, but for this it has to return the number of
written characters, as the amount of written characters may not be
the same as the amount of characters going into the write buffer,
so the receive_room() usage in pty_write() is not really reliable.

Alan said:

The problem looks valid. The behaviour of 'traditional unix' appears to
be the following

If you exceed the line limit then beep and drop the character
Always allow EOL to complete a canonical line input
Always do signal/control processing if enabled

Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by

Roman Zippel and committed by
Linus Torvalds
d6afe27b 87591451

+15 -18
+15 -18
drivers/char/n_tty.c
··· 770 770 } 771 771 if (c == '\n') { 772 772 if (L_ECHO(tty) || L_ECHONL(tty)) { 773 - if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { 773 + if (tty->read_cnt >= N_TTY_BUF_SIZE-1) 774 774 put_char('\a', tty); 775 - return; 776 - } 777 775 opost('\n', tty); 778 776 } 779 777 goto handle_newline; ··· 788 790 * XXX are EOL_CHAR and EOL2_CHAR echoed?!? 789 791 */ 790 792 if (L_ECHO(tty)) { 791 - if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { 793 + if (tty->read_cnt >= N_TTY_BUF_SIZE-1) 792 794 put_char('\a', tty); 793 - return; 794 - } 795 795 /* Record the column of first canon char. */ 796 796 if (tty->canon_head == tty->read_head) 797 797 tty->canon_column = tty->column; ··· 858 862 * that erase characters will be handled. Other excess 859 863 * characters will be beeped. 860 864 */ 861 - if (tty->icanon && !tty->canon_data) 862 - return N_TTY_BUF_SIZE; 863 - 864 - if (left > 0) 865 - return left; 866 - return 0; 865 + if (left <= 0) 866 + left = tty->icanon && !tty->canon_data; 867 + return left; 867 868 } 868 869 869 870 /** ··· 1466 1473 if (tty->driver->flush_chars) 1467 1474 tty->driver->flush_chars(tty); 1468 1475 } else { 1469 - c = tty->driver->write(tty, b, nr); 1470 - if (c < 0) { 1471 - retval = c; 1472 - goto break_out; 1476 + while (nr > 0) { 1477 + c = tty->driver->write(tty, b, nr); 1478 + if (c < 0) { 1479 + retval = c; 1480 + goto break_out; 1481 + } 1482 + if (!c) 1483 + break; 1484 + b += c; 1485 + nr -= c; 1473 1486 } 1474 - b += c; 1475 - nr -= c; 1476 1487 } 1477 1488 if (!nr) 1478 1489 break;