aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@linux.dev>2024-02-04 22:56:16 -0500
committerKent Overstreet <kent.overstreet@linux.dev>2024-03-13 18:39:13 -0400
commitf704f108af79c1ccbc1984cf0fd5e9f30102a718 (patch)
tree89e0550a19e22b0334872614c3d57bcfe9d90de5
parenta6777ca4ff237d097b3d6186283eb2d2f24071d1 (diff)
downloadvfs-f704f108af79c1ccbc1984cf0fd5e9f30102a718.tar.gz
bcachefs: thread_with_stdio: fix bch2_stdio_redirect_readline()
This fixes a bug where we'd return data without waiting for a newline, if data was present but a newline was not. Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
-rw-r--r--fs/bcachefs/thread_with_file.c33
1 files changed, 22 insertions, 11 deletions
diff --git a/fs/bcachefs/thread_with_file.c b/fs/bcachefs/thread_with_file.c
index eb8ab4c47a94b..830efb06ef0be 100644
--- a/fs/bcachefs/thread_with_file.c
+++ b/fs/bcachefs/thread_with_file.c
@@ -277,25 +277,36 @@ int bch2_stdio_redirect_read(struct stdio_redirect *stdio, char *ubuf, size_t le
int bch2_stdio_redirect_readline(struct stdio_redirect *stdio, char *ubuf, size_t len)
{
struct stdio_buf *buf = &stdio->input;
-
+ size_t copied = 0;
+ ssize_t ret = 0;
+again:
wait_event(buf->wait, stdio_redirect_has_input(stdio));
- if (stdio->done)
- return -1;
+ if (stdio->done) {
+ ret = -1;
+ goto out;
+ }
spin_lock(&buf->lock);
- int ret = min(len, buf->buf.nr);
- char *n = memchr(buf->buf.data, '\n', ret);
- if (!n)
- ret = min(ret, n + 1 - buf->buf.data);
- buf->buf.nr -= ret;
- memcpy(ubuf, buf->buf.data, ret);
+ size_t b = min(len, buf->buf.nr);
+ char *n = memchr(buf->buf.data, '\n', b);
+ if (n)
+ b = min_t(size_t, b, n + 1 - buf->buf.data);
+ buf->buf.nr -= b;
+ memcpy(ubuf, buf->buf.data, b);
memmove(buf->buf.data,
- buf->buf.data + ret,
+ buf->buf.data + b,
buf->buf.nr);
+ ubuf += b;
+ len -= b;
+ copied += b;
spin_unlock(&buf->lock);
wake_up(&buf->wait);
- return ret;
+
+ if (!n && len)
+ goto again;
+out:
+ return copied ?: ret;
}
__printf(3, 0)