lkml.org 
[lkml]   [2007]   [Jan]   [29]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[patch 6/9] mm: be sure to trim blocks
    Date
    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: Nick Piggin <npiggin@suse.de>

    Index: linux-2.6/mm/filemap.c
    ===================================================================
    --- linux-2.6.orig/mm/filemap.c
    +++ linux-2.6/mm/filemap.c
    @@ -1911,22 +1911,9 @@ generic_file_buffered_write(struct kiocb
    }

    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);
    @@ -1935,40 +1922,53 @@ generic_file_buffered_write(struct kiocb
    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))
    + goto fs_write_aop_error;
    + if (unlikely(copied != bytes)) {
    + status = -EFAULT;
    + goto fs_write_aop_error;
    }
    - if (likely(copied > 0)) {
    - if (!status)
    - status = copied;
    + if (unlikely(status > 0)) /* filesystem did partial write */
    + copied = status;

    - if (status >= 0) {
    - written += status;
    - count -= status;
    - pos += status;
    - buf += status;
    - if (unlikely(nr_segs > 1)) {
    - filemap_set_next_iovec(&cur_iov,
    - &iov_offset, status);
    - if (count)
    - buf = cur_iov->iov_base +
    - iov_offset;
    - } else {
    - iov_offset += status;
    - }
    + if (likely(copied > 0)) {
    + written += copied;
    + count -= copied;
    + pos += copied;
    + buf += copied;
    + if (unlikely(nr_segs > 1)) {
    + filemap_set_next_iovec(&cur_iov,
    + &iov_offset, copied);
    + if (count)
    + buf = cur_iov->iov_base + iov_offset;
    + } else {
    + 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;

    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/
    \
     
     \ /
      Last update: 2007-01-29 11:39    [W:3.988 / U:0.008 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site