Commit 7d8761ba authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull seq_file fix from Al Viro:
 "This fixes a regression introduced in this cycle wrt iov_iter based
  variant for reading a seq_file"

* 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  fix return values of seq_read_iter()
parents cd796ed3 4bbf439b
Loading
Loading
Loading
Loading
+27 −30
Original line number Diff line number Diff line
@@ -168,12 +168,14 @@ EXPORT_SYMBOL(seq_read);
ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
	struct seq_file *m = iocb->ki_filp->private_data;
	size_t size = iov_iter_count(iter);
	size_t copied = 0;
	size_t n;
	void *p;
	int err = 0;

	if (!iov_iter_count(iter))
		return 0;

	mutex_lock(&m->lock);

	/*
@@ -206,36 +208,34 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter)
		if (!m->buf)
			goto Enomem;
	}
	/* if not empty - flush it first */
	// something left in the buffer - copy it out first
	if (m->count) {
		n = min(m->count, size);
		if (copy_to_iter(m->buf + m->from, n, iter) != n)
			goto Efault;
		n = copy_to_iter(m->buf + m->from, m->count, iter);
		m->count -= n;
		m->from += n;
		size -= n;
		copied += n;
		if (!size)
		if (m->count)	// hadn't managed to copy everything
			goto Done;
	}
	/* we need at least one record in buffer */
	// get a non-empty record in the buffer
	m->from = 0;
	p = m->op->start(m, &m->index);
	while (1) {
		err = PTR_ERR(p);
		if (!p || IS_ERR(p))
		if (!p || IS_ERR(p))	// EOF or an error
			break;
		err = m->op->show(m, p);
		if (err < 0)
		if (err < 0)		// hard error
			break;
		if (unlikely(err))
		if (unlikely(err))	// ->show() says "skip it"
			m->count = 0;
		if (unlikely(!m->count)) {
		if (unlikely(!m->count)) { // empty record
			p = m->op->next(m, p, &m->index);
			continue;
		}
		if (m->count < m->size)
		if (!seq_has_overflowed(m)) // got it
			goto Fill;
		// need a bigger buffer
		m->op->stop(m, p);
		kvfree(m->buf);
		m->count = 0;
@@ -244,11 +244,14 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter)
			goto Enomem;
		p = m->op->start(m, &m->index);
	}
	// EOF or an error
	m->op->stop(m, p);
	m->count = 0;
	goto Done;
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) {
		size_t offs = m->count;
		loff_t pos = m->index;
@@ -259,30 +262,27 @@ Fill:
					    m->op->next);
			m->index++;
		}
		if (!p || IS_ERR(p)) {
			err = PTR_ERR(p);
		if (!p || IS_ERR(p))	// no next record for us
			break;
		}
		if (m->count >= size)
		if (m->count >= iov_iter_count(iter))
			break;
		err = m->op->show(m, p);
		if (seq_has_overflowed(m) || err) {
		if (err > 0) {		// ->show() says "skip it"
			m->count = offs;
		} else if (err || seq_has_overflowed(m)) {
			m->count = offs;
			if (likely(err <= 0))
			break;
		}
	}
	m->op->stop(m, p);
	n = min(m->count, size);
	if (copy_to_iter(m->buf, n, iter) != n)
		goto Efault;
	n = copy_to_iter(m->buf, m->count, iter);
	copied += n;
	m->count -= n;
	m->from = n;
Done:
	if (!copied)
		copied = err;
	else {
	if (unlikely(!copied)) {
		copied = m->count ? -EFAULT : err;
	} else {
		iocb->ki_pos += copied;
		m->read_pos += copied;
	}
@@ -291,9 +291,6 @@ Done:
Enomem:
	err = -ENOMEM;
	goto Done;
Efault:
	err = -EFAULT;
	goto Done;
}
EXPORT_SYMBOL(seq_read_iter);