diff --git a/fs/fincore.c b/fs/fincore.c index f329fe4..d1a5d65 100644 --- a/fs/fincore.c +++ b/fs/fincore.c @@ -11,19 +11,34 @@ #include #include #include +#include #include #include -static unsigned char fincore_page(struct address_space *mapping, pgoff_t pgoff) +static void fincore_pages(struct address_space *mapping, pgoff_t pgoff, + unsigned vec_count, unsigned char *vec) { - unsigned char present = 0; - struct page *page = find_get_page(mapping, pgoff); - if (page) { - present = PageUptodate(page); - page_cache_release(page); - } + struct pagevec pvec; + unsigned vec_i = 0; + + pagevec_init(&pvec, 0); + + while (vec_i < vec_count) { + unsigned pvec_max = min(vec_count - vec_i, (unsigned) PAGEVEC_SIZE); + unsigned pvec_i = 0; + + pagevec_lookup(&pvec, mapping, pgoff, pvec_max); - return present; + for (; vec_i < vec_count && pvec_i < pvec_max; vec_i++, pgoff++) { + if (pvec_i >= pagevec_count(&pvec) || + pvec.pages[pvec_i]->index != pgoff) + vec[vec_i] = 0; + else + vec[vec_i] = PageUptodate(pvec.pages[pvec_i++]); + } + + pagevec_release(&pvec); + } } /* @@ -64,9 +79,8 @@ SYSCALL_DEFINE4(fincore, unsigned int, fd, unsigned char __user *, vec, loff_t file_nbytes; pgoff_t file_npages; unsigned char *kernel_vec = NULL; - unsigned char kernel_vec_small[64]; + unsigned char kernel_vec_small[64]; /* 64 is fairly arbitrary */ unsigned kernel_vec_count; - int i; /* Check the start address: needs to be page-aligned.. */ if (start & ~PAGE_CACHE_MASK) @@ -100,16 +114,19 @@ SYSCALL_DEFINE4(fincore, unsigned int, fd, unsigned char __user *, vec, while (pgoff < pgend) { /* * Do at most kernel_vec_count entries per iteration, due to - * the limited buffer size. + * the limited buffer size and the possibility that an loff_t + * can store a larger value than an unsigned. */ - for (i = 0; pgoff < pgend && i < kernel_vec_count; pgoff++, i++) - kernel_vec[i] = fincore_page(filp->f_mapping, pgoff); + unsigned len = min((uint64_t) (pgend - pgoff), + (uint64_t) kernel_vec_count); + fincore_pages(filp->f_mapping, pgoff, len, kernel_vec); - if (copy_to_user(vec, kernel_vec, i)) { + if (copy_to_user(vec, kernel_vec, len)) { retval = -EFAULT; break; } - vec += i; + pgoff += len; + vec += len; if (signal_pending(current)) { retval = -EINTR;