Commit d6afe27b authored by Roman Zippel's avatar Roman Zippel Committed by Linus Torvalds

[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: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 87591451
...@@ -770,10 +770,8 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) ...@@ -770,10 +770,8 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
} }
if (c == '\n') { if (c == '\n') {
if (L_ECHO(tty) || L_ECHONL(tty)) { if (L_ECHO(tty) || L_ECHONL(tty)) {
if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { if (tty->read_cnt >= N_TTY_BUF_SIZE-1)
put_char('\a', tty); put_char('\a', tty);
return;
}
opost('\n', tty); opost('\n', tty);
} }
goto handle_newline; goto handle_newline;
...@@ -790,10 +788,8 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) ...@@ -790,10 +788,8 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
* XXX are EOL_CHAR and EOL2_CHAR echoed?!? * XXX are EOL_CHAR and EOL2_CHAR echoed?!?
*/ */
if (L_ECHO(tty)) { if (L_ECHO(tty)) {
if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { if (tty->read_cnt >= N_TTY_BUF_SIZE-1)
put_char('\a', tty); put_char('\a', tty);
return;
}
/* Record the column of first canon char. */ /* Record the column of first canon char. */
if (tty->canon_head == tty->read_head) if (tty->canon_head == tty->read_head)
tty->canon_column = tty->column; tty->canon_column = tty->column;
...@@ -862,12 +858,9 @@ static int n_tty_receive_room(struct tty_struct *tty) ...@@ -862,12 +858,9 @@ static int n_tty_receive_room(struct tty_struct *tty)
* that erase characters will be handled. Other excess * that erase characters will be handled. Other excess
* characters will be beeped. * characters will be beeped.
*/ */
if (tty->icanon && !tty->canon_data) if (left <= 0)
return N_TTY_BUF_SIZE; left = tty->icanon && !tty->canon_data;
return left;
if (left > 0)
return left;
return 0;
} }
/** /**
...@@ -1473,13 +1466,17 @@ static ssize_t write_chan(struct tty_struct * tty, struct file * file, ...@@ -1473,13 +1466,17 @@ static ssize_t write_chan(struct tty_struct * tty, struct file * file,
if (tty->driver->flush_chars) if (tty->driver->flush_chars)
tty->driver->flush_chars(tty); tty->driver->flush_chars(tty);
} else { } else {
c = tty->driver->write(tty, b, nr); while (nr > 0) {
if (c < 0) { c = tty->driver->write(tty, b, nr);
retval = c; if (c < 0) {
goto break_out; retval = c;
goto break_out;
}
if (!c)
break;
b += c;
nr -= c;
} }
b += c;
nr -= c;
} }
if (!nr) if (!nr)
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment