Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 64649a58 authored by Nick Piggin's avatar Nick Piggin Committed by Linus Torvalds
Browse files

mm: trim more holes



If prepare_write fails with AOP_TRUNCATED_PAGE, or if commit_write fails, then
we may have failed the write operation despite prepare_write having
instantiated blocks past i_size.  Fix this, and consolidate the trimming into
one place.

Signed-off-by: default avatarNick Piggin <npiggin@suse.de>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 5fe17237
Loading
Loading
Loading
Loading
+40 −40
Original line number Diff line number Diff line
@@ -1895,22 +1895,9 @@ generic_file_buffered_write(struct kiocb *iocb, const struct iovec *iov,
		}

		status = a_ops->prepare_write(file, page, offset, offset+bytes);
		if (unlikely(status)) {
			loff_t isize = i_size_read(inode);
		if (unlikely(status))
			goto fs_write_aop_error;

			if (status != AOP_TRUNCATED_PAGE)
				unlock_page(page);
			page_cache_release(page);
			if (status == AOP_TRUNCATED_PAGE)
				continue;
			/*
			 * prepare_write() may have instantiated a few blocks
			 * outside i_size.  Trim these off again.
			 */
			if (pos + bytes > isize)
				vmtruncate(inode, isize);
			break;
		}
		if (likely(nr_segs == 1))
			copied = filemap_copy_from_user(page, offset,
							buf, bytes);
@@ -1919,40 +1906,53 @@ generic_file_buffered_write(struct kiocb *iocb, const struct iovec *iov,
						cur_iov, iov_offset, bytes);
		flush_dcache_page(page);
		status = a_ops->commit_write(file, page, offset, offset+bytes);
		if (status == AOP_TRUNCATED_PAGE) {
			page_cache_release(page);
			continue;
		if (unlikely(status < 0 || status == AOP_TRUNCATED_PAGE))
			goto fs_write_aop_error;
		if (unlikely(copied != bytes)) {
			status = -EFAULT;
			goto fs_write_aop_error;
		}
		if (unlikely(status > 0)) /* filesystem did partial write */
			copied = status;

		if (likely(copied > 0)) {
			if (!status)
				status = copied;

			if (status >= 0) {
				written += status;
				count -= status;
				pos += status;
				buf += status;
			written += copied;
			count -= copied;
			pos += copied;
			buf += copied;
			if (unlikely(nr_segs > 1)) {
				filemap_set_next_iovec(&cur_iov,
							&iov_offset, status);
						&iov_offset, copied);
				if (count)
						buf = cur_iov->iov_base +
							iov_offset;
					buf = cur_iov->iov_base + iov_offset;
			} else {
					iov_offset += status;
				}
				iov_offset += copied;
			}
		}
		if (unlikely(copied != bytes))
			if (status >= 0)
				status = -EFAULT;
		unlock_page(page);
		mark_page_accessed(page);
		page_cache_release(page);
		if (status < 0)
			break;
		balance_dirty_pages_ratelimited(mapping);
		cond_resched();
		continue;

fs_write_aop_error:
		if (status != AOP_TRUNCATED_PAGE)
			unlock_page(page);
		page_cache_release(page);

		/*
		 * prepare_write() may have instantiated a few blocks
		 * outside i_size.  Trim these off again. Don't need
		 * i_size_read because we hold i_mutex.
		 */
		if (pos + bytes > inode->i_size)
			vmtruncate(inode, inode->i_size);
		if (status == AOP_TRUNCATED_PAGE)
			continue;
		else
			break;

	} while (count);
	*ppos = pos;