1
2
3
4
5
6
7
8
9
10#include <linux/slab.h>
11#include <linux/pagemap.h>
12#include <linux/mm.h>
13#include <linux/mman.h>
14#include <linux/syscalls.h>
15
16#include <asm/uaccess.h>
17#include <asm/pgtable.h>
18
19
20
21
22
23
24
25static unsigned char mincore_page(struct vm_area_struct * vma,
26 unsigned long pgoff)
27{
28 unsigned char present = 0;
29 struct address_space * as = vma->vm_file->f_mapping;
30 struct page * page;
31
32 page = find_get_page(as, pgoff);
33 if (page) {
34 present = PageUptodate(page);
35 page_cache_release(page);
36 }
37
38 return present;
39}
40
41
42
43
44
45
46static long do_mincore(unsigned long addr, unsigned char *vec, unsigned long pages)
47{
48 unsigned long i, nr, pgoff;
49 struct vm_area_struct *vma = find_vma(current->mm, addr);
50
51
52
53
54
55 if (!vma || addr < vma->vm_start)
56 return -ENOMEM;
57
58
59
60
61
62
63
64
65
66
67 if (!vma->vm_file)
68 return -ENOMEM;
69
70
71
72
73
74 nr = (vma->vm_end - addr) >> PAGE_SHIFT;
75 if (nr > pages)
76 nr = pages;
77
78 pgoff = (addr - vma->vm_start) >> PAGE_SHIFT;
79 pgoff += vma->vm_pgoff;
80
81
82 for (i = 0 ; i < nr; i++, pgoff++)
83 vec[i] = mincore_page(vma, pgoff);
84
85 return nr;
86}
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112asmlinkage long sys_mincore(unsigned long start, size_t len,
113 unsigned char __user * vec)
114{
115 long retval;
116 unsigned long pages;
117 unsigned char *tmp;
118
119
120 if (start & ~PAGE_CACHE_MASK)
121 return -EINVAL;
122
123
124 if (!access_ok(VERIFY_READ, (void __user *) start, len))
125 return -ENOMEM;
126
127
128 pages = len >> PAGE_SHIFT;
129 pages += (len & ~PAGE_MASK) != 0;
130
131 if (!access_ok(VERIFY_WRITE, vec, pages))
132 return -EFAULT;
133
134 tmp = (void *) __get_free_page(GFP_USER);
135 if (!tmp)
136 return -EAGAIN;
137
138 retval = 0;
139 while (pages) {
140
141
142
143
144 down_read(¤t->mm->mmap_sem);
145 retval = do_mincore(start, tmp, min(pages, PAGE_SIZE));
146 up_read(¤t->mm->mmap_sem);
147
148 if (retval <= 0)
149 break;
150 if (copy_to_user(vec, tmp, retval)) {
151 retval = -EFAULT;
152 break;
153 }
154 pages -= retval;
155 vec += retval;
156 start += retval << PAGE_SHIFT;
157 retval = 0;
158 }
159 free_page((unsigned long) tmp);
160 return retval;
161}
162