summaryrefslogtreecommitdiff
path: root/Kernel
diff options
context:
space:
mode:
authorSahan Fernando <sahan.h.fernando@gmail.com>2021-05-18 19:06:33 +1000
committerAndreas Kling <kling@serenityos.org>2021-05-18 16:47:26 +0200
commitc3b670c09213107ce52990e1d897aa3a68ad5851 (patch)
tree78e711e52028432991f12380f54ff07890ebc2c6 /Kernel
parentfe2d4d85c429d972e17038c9e78795c2a2ead2ff (diff)
downloadserenity-c3b670c09213107ce52990e1d897aa3a68ad5851.zip
Kernel: Acknowledge partial writes from TTYs
Fixes a bug where TTY::write will attempt to write into the underlying device but will not acknowledge the result of that write, instead assuming that the write fully completed.
Diffstat (limited to 'Kernel')
-rw-r--r--Kernel/TTY/TTY.cpp25
1 files changed, 23 insertions, 2 deletions
diff --git a/Kernel/TTY/TTY.cpp b/Kernel/TTY/TTY.cpp
index 353ad45582..ce7c21a678 100644
--- a/Kernel/TTY/TTY.cpp
+++ b/Kernel/TTY/TTY.cpp
@@ -97,8 +97,29 @@ KResultOr<size_t> TTY::write(FileDescription&, u64, const UserOrKernelBuffer& bu
}
modified_data[i + extra_chars] = ch;
}
- on_tty_write(UserOrKernelBuffer::for_kernel_buffer(modified_data), buffer_bytes + extra_chars);
- return buffer_bytes;
+ ssize_t bytes_written = on_tty_write(UserOrKernelBuffer::for_kernel_buffer(modified_data), buffer_bytes + extra_chars);
+ VERIFY(bytes_written != 0);
+ if (bytes_written < 0 || !(m_termios.c_oflag & OPOST) || !(m_termios.c_oflag & ONLCR))
+ return bytes_written;
+ if ((size_t)bytes_written == buffer_bytes + extra_chars)
+ return (ssize_t)buffer_bytes;
+
+ // Degenerate case where we converted some newlines and encountered a partial write
+
+ // Calculate where in the input buffer the last character would have been
+ size_t pos_data = 0;
+ for (ssize_t pos_modified_data = 0; pos_modified_data < bytes_written; ++pos_data) {
+ if (data[pos_data] == '\n')
+ pos_modified_data += 2;
+ else
+ pos_modified_data += 1;
+
+ // Handle case where the '\r' got written but not the '\n'
+ // FIXME: Our strategy is to retry writing both. We should really be queuing a write for the corresponding '\n'
+ if (pos_modified_data > bytes_written)
+ --pos_data;
+ }
+ return (ssize_t)pos_data;
});
}