fix return values of seq_read_iter()

Unlike ->read(), ->read_iter() instances *must* return the amount
of data they'd left in iterator.  For ->read() returning less than
it has actually copied is a QoI issue; read(fd, unmapped_page - 5, 8)
is allowed to fill all 5 bytes of destination and return 4; it's
not nice to caller, but POSIX allows pretty much anything in such
situation, up to and including a SIGSEGV.

generic_file_splice_read() uses pipe-backed iterator as destination;
there a short copy comes from pipe being full, not from running into
an un{mapped,writable} page in the middle of destination as we
have for iovec-backed iterators read(2) uses.  And there we rely
upon the ->read_iter() reporting the actual amount it has left
in destination.

Conversion of a ->read() instance into ->read_iter() has to watch
out for that.  If you really need an "all or nothing" kind of
behaviour somewhere, you need to do iov_iter_revert() to prune
the partial copy.

In case of seq_read_iter() we can handle short copy just fine;
the data is in m->buf and next call will fetch it from there.

Fixes: d4d50710a8 (seq_file: add seq_read_iter)
Tested-by: Nathan Chancellor <natechancellor@gmail.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Al Viro 2020-11-12 14:40:37 -05:00
parent d4d50710a8
commit 4bbf439b09

View file

@ -168,12 +168,14 @@ EXPORT_SYMBOL(seq_read);
ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter) ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{ {
struct seq_file *m = iocb->ki_filp->private_data; struct seq_file *m = iocb->ki_filp->private_data;
size_t size = iov_iter_count(iter);
size_t copied = 0; size_t copied = 0;
size_t n; size_t n;
void *p; void *p;
int err = 0; int err = 0;
if (!iov_iter_count(iter))
return 0;
mutex_lock(&m->lock); mutex_lock(&m->lock);
/* /*
@ -206,36 +208,34 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter)
if (!m->buf) if (!m->buf)
goto Enomem; goto Enomem;
} }
/* if not empty - flush it first */ // something left in the buffer - copy it out first
if (m->count) { if (m->count) {
n = min(m->count, size); n = copy_to_iter(m->buf + m->from, m->count, iter);
if (copy_to_iter(m->buf + m->from, n, iter) != n)
goto Efault;
m->count -= n; m->count -= n;
m->from += n; m->from += n;
size -= n;
copied += n; copied += n;
if (!size) if (m->count) // hadn't managed to copy everything
goto Done; goto Done;
} }
/* we need at least one record in buffer */ // get a non-empty record in the buffer
m->from = 0; m->from = 0;
p = m->op->start(m, &m->index); p = m->op->start(m, &m->index);
while (1) { while (1) {
err = PTR_ERR(p); err = PTR_ERR(p);
if (!p || IS_ERR(p)) if (!p || IS_ERR(p)) // EOF or an error
break; break;
err = m->op->show(m, p); err = m->op->show(m, p);
if (err < 0) if (err < 0) // hard error
break; break;
if (unlikely(err)) if (unlikely(err)) // ->show() says "skip it"
m->count = 0; m->count = 0;
if (unlikely(!m->count)) { if (unlikely(!m->count)) { // empty record
p = m->op->next(m, p, &m->index); p = m->op->next(m, p, &m->index);
continue; continue;
} }
if (m->count < m->size) if (!seq_has_overflowed(m)) // got it
goto Fill; goto Fill;
// need a bigger buffer
m->op->stop(m, p); m->op->stop(m, p);
kvfree(m->buf); kvfree(m->buf);
m->count = 0; m->count = 0;
@ -244,11 +244,14 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter)
goto Enomem; goto Enomem;
p = m->op->start(m, &m->index); p = m->op->start(m, &m->index);
} }
// EOF or an error
m->op->stop(m, p); m->op->stop(m, p);
m->count = 0; m->count = 0;
goto Done; goto Done;
Fill: Fill:
/* they want more? let's try to get some more */ // one non-empty record is in the buffer; if they want more,
// try to fit more in, but in any case we need to advance
// the iterator once for every record shown.
while (1) { while (1) {
size_t offs = m->count; size_t offs = m->count;
loff_t pos = m->index; loff_t pos = m->index;
@ -259,30 +262,27 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter)
m->op->next); m->op->next);
m->index++; m->index++;
} }
if (!p || IS_ERR(p)) { if (!p || IS_ERR(p)) // no next record for us
err = PTR_ERR(p);
break; break;
} if (m->count >= iov_iter_count(iter))
if (m->count >= size)
break; break;
err = m->op->show(m, p); err = m->op->show(m, p);
if (seq_has_overflowed(m) || err) { if (err > 0) { // ->show() says "skip it"
m->count = offs; m->count = offs;
if (likely(err <= 0)) } else if (err || seq_has_overflowed(m)) {
break; m->count = offs;
break;
} }
} }
m->op->stop(m, p); m->op->stop(m, p);
n = min(m->count, size); n = copy_to_iter(m->buf, m->count, iter);
if (copy_to_iter(m->buf, n, iter) != n)
goto Efault;
copied += n; copied += n;
m->count -= n; m->count -= n;
m->from = n; m->from = n;
Done: Done:
if (!copied) if (unlikely(!copied)) {
copied = err; copied = m->count ? -EFAULT : err;
else { } else {
iocb->ki_pos += copied; iocb->ki_pos += copied;
m->read_pos += copied; m->read_pos += copied;
} }
@ -291,9 +291,6 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter)
Enomem: Enomem:
err = -ENOMEM; err = -ENOMEM;
goto Done; goto Done;
Efault:
err = -EFAULT;
goto Done;
} }
EXPORT_SYMBOL(seq_read_iter); EXPORT_SYMBOL(seq_read_iter);