ext4: fix off-by-in in loop termination in ext4_find_unwritten_pgoff()
[sfrench/cifs-2.6.git] / fs / ext4 / file.c
index cefa9835f275d9b062ae9b13765ea743edb53f64..2b00bf84c05bca35f13eb671b56ac2cdb17048f4 100644 (file)
@@ -257,6 +257,7 @@ static int ext4_dax_huge_fault(struct vm_fault *vmf,
                enum page_entry_size pe_size)
 {
        int result;
+       handle_t *handle = NULL;
        struct inode *inode = file_inode(vmf->vma->vm_file);
        struct super_block *sb = inode->i_sb;
        bool write = vmf->flags & FAULT_FLAG_WRITE;
@@ -264,12 +265,24 @@ static int ext4_dax_huge_fault(struct vm_fault *vmf,
        if (write) {
                sb_start_pagefault(sb);
                file_update_time(vmf->vma->vm_file);
+               down_read(&EXT4_I(inode)->i_mmap_sem);
+               handle = ext4_journal_start_sb(sb, EXT4_HT_WRITE_PAGE,
+                                              EXT4_DATA_TRANS_BLOCKS(sb));
+       } else {
+               down_read(&EXT4_I(inode)->i_mmap_sem);
        }
-       down_read(&EXT4_I(inode)->i_mmap_sem);
-       result = dax_iomap_fault(vmf, pe_size, &ext4_iomap_ops);
-       up_read(&EXT4_I(inode)->i_mmap_sem);
-       if (write)
+       if (!IS_ERR(handle))
+               result = dax_iomap_fault(vmf, pe_size, &ext4_iomap_ops);
+       else
+               result = VM_FAULT_SIGBUS;
+       if (write) {
+               if (!IS_ERR(handle))
+                       ext4_journal_stop(handle);
+               up_read(&EXT4_I(inode)->i_mmap_sem);
                sb_end_pagefault(sb);
+       } else {
+               up_read(&EXT4_I(inode)->i_mmap_sem);
+       }
 
        return result;
 }
@@ -461,7 +474,7 @@ static int ext4_find_unwritten_pgoff(struct inode *inode,
        endoff = (loff_t)end_blk << blkbits;
 
        index = startoff >> PAGE_SHIFT;
-       end = endoff >> PAGE_SHIFT;
+       end = (endoff - 1) >> PAGE_SHIFT;
 
        pagevec_init(&pvec, 0);
        do {
@@ -471,47 +484,27 @@ static int ext4_find_unwritten_pgoff(struct inode *inode,
                num = min_t(pgoff_t, end - index, PAGEVEC_SIZE);
                nr_pages = pagevec_lookup(&pvec, inode->i_mapping, index,
                                          (pgoff_t)num);
-               if (nr_pages == 0) {
-                       if (whence == SEEK_DATA)
-                               break;
-
-                       BUG_ON(whence != SEEK_HOLE);
-                       /*
-                        * If this is the first time to go into the loop and
-                        * offset is not beyond the end offset, it will be a
-                        * hole at this offset
-                        */
-                       if (lastoff == startoff || lastoff < endoff)
-                               found = 1;
+               if (nr_pages == 0)
                        break;
-               }
-
-               /*
-                * If this is the first time to go into the loop and
-                * offset is smaller than the first page offset, it will be a
-                * hole at this offset.
-                */
-               if (lastoff == startoff && whence == SEEK_HOLE &&
-                   lastoff < page_offset(pvec.pages[0])) {
-                       found = 1;
-                       break;
-               }
 
                for (i = 0; i < nr_pages; i++) {
                        struct page *page = pvec.pages[i];
                        struct buffer_head *bh, *head;
 
                        /*
-                        * If the current offset is not beyond the end of given
-                        * range, it will be a hole.
+                        * If current offset is smaller than the page offset,
+                        * there is a hole at this offset.
                         */
-                       if (lastoff < endoff && whence == SEEK_HOLE &&
-                           page->index > end) {
+                       if (whence == SEEK_HOLE && lastoff < endoff &&
+                           lastoff < page_offset(pvec.pages[i])) {
                                found = 1;
                                *offset = lastoff;
                                goto out;
                        }
 
+                       if (page->index > end)
+                               goto out;
+
                        lock_page(page);
 
                        if (unlikely(page->mapping != inode->i_mapping)) {
@@ -551,20 +544,18 @@ static int ext4_find_unwritten_pgoff(struct inode *inode,
                        unlock_page(page);
                }
 
-               /*
-                * The no. of pages is less than our desired, that would be a
-                * hole in there.
-                */
-               if (nr_pages < num && whence == SEEK_HOLE) {
-                       found = 1;
-                       *offset = lastoff;
+               /* The no. of pages is less than our desired, we are done. */
+               if (nr_pages < num)
                        break;
-               }
 
                index = pvec.pages[i - 1]->index + 1;
                pagevec_release(&pvec);
        } while (index <= end);
 
+       if (whence == SEEK_HOLE && lastoff < endoff) {
+               found = 1;
+               *offset = lastoff;
+       }
 out:
        pagevec_release(&pvec);
        return found;