diff options
author | Hugh Dickins <hugh@veritas.com> | 2004-07-28 08:58:01 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-07-28 08:58:01 -0700 |
commit | 5136181d5b8bbb2c94d163a6d3362bfea9fdd99d (patch) | |
tree | 76cb2ce062ca4010a369e06e0c4260b4eed1d72c /mm | |
parent | ddd443e4d88c6e39bc3a6d249a980ccc438544cb (diff) | |
download | history-5136181d5b8bbb2c94d163a6d3362bfea9fdd99d.tar.gz |
[PATCH] install_page vs. vmtruncate
BK is still missing one piece for Oleg's install_page/vmtruncate races.
Oleg didn't explicitly ACK this, but I think he did implicitly: Oleg?
The previous patch to install_page, returning an error if !page_mapping
once page_table_lock is held, is not enough to guard against vmtruncate.
When unmap_mapping_range already did this vma, but truncate_inode_pages has
not yet done this page, page->mapping will still be set, but we must now
refrain from inserting the page into the page table.
Could check truncate_count, but that would need caller to read and pass it
down. Instead, recheck page->index against i_size, which is updated before
unmap_mapping_range. Better check page->mapping too: not really necessary,
but it's accidental that index is left when mapping is reset.
Also, callers are expecting -EINVAL for beyond end of file, not -EAGAIN.
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'mm')
-rw-r--r-- | mm/fremap.c | 8 |
1 files changed, 6 insertions, 2 deletions
diff --git a/mm/fremap.c b/mm/fremap.c index f5c8b64ffb711f..dc64dd9dbe0bec 100644 --- a/mm/fremap.c +++ b/mm/fremap.c @@ -55,6 +55,8 @@ static inline void zap_pte(struct mm_struct *mm, struct vm_area_struct *vma, int install_page(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long addr, struct page *page, pgprot_t prot) { + struct inode *inode; + pgoff_t size; int err = -ENOMEM; pte_t *pte; pgd_t *pgd; @@ -76,8 +78,10 @@ int install_page(struct mm_struct *mm, struct vm_area_struct *vma, * This page may have been truncated. Tell the * caller about it. */ - err = -EAGAIN; - if (!page_mapping(page)) + err = -EINVAL; + inode = vma->vm_file->f_mapping->host; + size = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + if (!page->mapping || page->index >= size) goto err_unlock; zap_pte(mm, vma, addr, pte); |