Import 2.3.7pre9
[davej-history.git] / fs / proc / mem.c
blobdf04473833fe75b219860d15d6de0cc65046ef48
1 /*
2 * linux/fs/proc/mem.c
4 * Copyright (C) 1991, 1992 Linus Torvalds
5 */
7 #include <linux/types.h>
8 #include <linux/errno.h>
9 #include <linux/sched.h>
10 #include <linux/kernel.h>
11 #include <linux/mm.h>
12 #include <linux/proc_fs.h>
14 #include <asm/page.h>
15 #include <asm/uaccess.h>
16 #include <asm/io.h>
17 #include <asm/pgtable.h>
20 * mem_write isn't really a good idea right now. It needs
21 * to check a lot more: if the process we try to write to
22 * dies in the middle right now, mem_write will overwrite
23 * kernel memory.. This disables it altogether.
25 #define mem_write NULL
27 static int check_range(struct mm_struct * mm, unsigned long addr, int count)
29 struct vm_area_struct *vma;
30 int retval;
32 vma = find_vma(mm, addr);
33 if (!vma)
34 return -EACCES;
35 if (vma->vm_start > addr)
36 return -EACCES;
37 if (!(vma->vm_flags & VM_READ))
38 return -EACCES;
39 while ((retval = vma->vm_end - addr) < count) {
40 struct vm_area_struct *next = vma->vm_next;
41 if (!next)
42 break;
43 if (vma->vm_end != next->vm_start)
44 break;
45 if (!(next->vm_flags & VM_READ))
46 break;
47 vma = next;
49 if (retval > count)
50 retval = count;
51 return retval;
54 static struct task_struct * get_task(int pid)
56 struct task_struct * tsk = current;
58 if (pid != tsk->pid) {
59 tsk = find_task_by_pid(pid);
61 /* Allow accesses only under the same circumstances
62 * that we would allow ptrace to work.
64 if (tsk) {
65 if (!(tsk->flags & PF_PTRACED)
66 || tsk->state != TASK_STOPPED
67 || tsk->p_pptr != current)
68 tsk = NULL;
71 return tsk;
74 static ssize_t mem_read(struct file * file, char * buf,
75 size_t count, loff_t *ppos)
77 struct inode * inode = file->f_dentry->d_inode;
78 pgd_t *page_dir;
79 pmd_t *page_middle;
80 pte_t pte;
81 char * page;
82 struct task_struct * tsk;
83 unsigned long addr;
84 char *tmp;
85 ssize_t scount, i;
87 read_lock(&tasklist_lock);
88 tsk = get_task(inode->i_ino >> 16);
89 read_unlock(&tasklist_lock); /* FIXME: This should really be done only afetr not using tsk any more!!! */
90 if (!tsk)
91 return -ESRCH;
92 addr = *ppos;
93 scount = check_range(tsk->mm, addr, count);
94 if (scount < 0)
95 return scount;
96 tmp = buf;
97 while (scount > 0) {
98 if (signal_pending(current))
99 break;
100 page_dir = pgd_offset(tsk->mm,addr);
101 if (pgd_none(*page_dir))
102 break;
103 if (pgd_bad(*page_dir)) {
104 printk("Bad page dir entry %08lx\n", pgd_val(*page_dir));
105 pgd_clear(page_dir);
106 break;
108 page_middle = pmd_offset(page_dir,addr);
109 if (pmd_none(*page_middle))
110 break;
111 if (pmd_bad(*page_middle)) {
112 printk("Bad page middle entry %08lx\n", pmd_val(*page_middle));
113 pmd_clear(page_middle);
114 break;
116 pte = *pte_offset(page_middle,addr);
117 if (!pte_present(pte))
118 break;
119 page = (char *) pte_page(pte) + (addr & ~PAGE_MASK);
120 i = PAGE_SIZE-(addr & ~PAGE_MASK);
121 if (i > scount)
122 i = scount;
123 copy_to_user(tmp, page, i);
124 addr += i;
125 tmp += i;
126 scount -= i;
128 *ppos = addr;
129 return tmp-buf;
132 #ifndef mem_write
134 static ssize_t mem_write(struct file * file, char * buf,
135 size_t count, loff_t *ppos)
137 struct inode * inode = file->f_dentry->d_inode;
138 pgd_t *page_dir;
139 pmd_t *page_middle;
140 pte_t pte;
141 char * page;
142 struct task_struct * tsk;
143 unsigned long addr;
144 char *tmp;
145 long i;
147 addr = *ppos;
148 tsk = get_task(inode->i_ino >> 16);
149 if (!tsk)
150 return -ESRCH;
151 tmp = buf;
152 while (count > 0) {
153 if (signal_pending(current))
154 break;
155 page_dir = pgd_offset(tsk,addr);
156 if (pgd_none(*page_dir))
157 break;
158 if (pgd_bad(*page_dir)) {
159 printk("Bad page dir entry %08lx\n", pgd_val(*page_dir));
160 pgd_clear(page_dir);
161 break;
163 page_middle = pmd_offset(page_dir,addr);
164 if (pmd_none(*page_middle))
165 break;
166 if (pmd_bad(*page_middle)) {
167 printk("Bad page middle entry %08lx\n", pmd_val(*page_middle));
168 pmd_clear(page_middle);
169 break;
171 pte = *pte_offset(page_middle,addr);
172 if (!pte_present(pte))
173 break;
174 if (!pte_write(pte))
175 break;
176 page = (char *) pte_page(pte) + (addr & ~PAGE_MASK);
177 i = PAGE_SIZE-(addr & ~PAGE_MASK);
178 if (i > count)
179 i = count;
180 copy_from_user(page, tmp, i);
181 addr += i;
182 tmp += i;
183 count -= i;
185 *ppos = addr;
186 if (tmp != buf)
187 return tmp-buf;
188 if (signal_pending(current))
189 return -ERESTARTSYS;
190 return 0;
193 #endif
195 static long long mem_lseek(struct file * file, long long offset, int orig)
197 switch (orig) {
198 case 0:
199 file->f_pos = offset;
200 return file->f_pos;
201 case 1:
202 file->f_pos += offset;
203 return file->f_pos;
204 default:
205 return -EINVAL;
210 * This isn't really reliable by any means..
212 int mem_mmap(struct file * file, struct vm_area_struct * vma)
214 struct task_struct *tsk;
215 pgd_t *src_dir, *dest_dir;
216 pmd_t *src_middle, *dest_middle;
217 pte_t *src_table, *dest_table;
218 unsigned long stmp, dtmp, mapnr;
219 struct vm_area_struct *src_vma = NULL;
220 struct inode *inode = file->f_dentry->d_inode;
222 /* Get the source's task information */
224 tsk = get_task(inode->i_ino >> 16);
226 if (!tsk)
227 return -ESRCH;
229 /* Ensure that we have a valid source area. (Has to be mmap'ed and
230 have valid page information.) We can't map shared memory at the
231 moment because working out the vm_area_struct & nattach stuff isn't
232 worth it. */
234 src_vma = tsk->mm->mmap;
235 stmp = vma->vm_offset;
236 while (stmp < vma->vm_offset + (vma->vm_end - vma->vm_start)) {
237 while (src_vma && stmp > src_vma->vm_end)
238 src_vma = src_vma->vm_next;
239 if (!src_vma || (src_vma->vm_flags & VM_SHM))
240 return -EINVAL;
242 src_dir = pgd_offset(tsk->mm, stmp);
243 if (pgd_none(*src_dir))
244 return -EINVAL;
245 if (pgd_bad(*src_dir)) {
246 printk("Bad source page dir entry %08lx\n", pgd_val(*src_dir));
247 return -EINVAL;
249 src_middle = pmd_offset(src_dir, stmp);
250 if (pmd_none(*src_middle))
251 return -EINVAL;
252 if (pmd_bad(*src_middle)) {
253 printk("Bad source page middle entry %08lx\n", pmd_val(*src_middle));
254 return -EINVAL;
256 src_table = pte_offset(src_middle, stmp);
257 if (pte_none(*src_table))
258 return -EINVAL;
260 if (stmp < src_vma->vm_start) {
261 if (!(src_vma->vm_flags & VM_GROWSDOWN))
262 return -EINVAL;
263 if (src_vma->vm_end - stmp > current->rlim[RLIMIT_STACK].rlim_cur)
264 return -EINVAL;
266 stmp += PAGE_SIZE;
269 src_vma = tsk->mm->mmap;
270 stmp = vma->vm_offset;
271 dtmp = vma->vm_start;
273 flush_cache_range(vma->vm_mm, vma->vm_start, vma->vm_end);
274 flush_cache_range(src_vma->vm_mm, src_vma->vm_start, src_vma->vm_end);
275 while (dtmp < vma->vm_end) {
276 while (src_vma && stmp > src_vma->vm_end)
277 src_vma = src_vma->vm_next;
279 src_dir = pgd_offset(tsk->mm, stmp);
280 src_middle = pmd_offset(src_dir, stmp);
281 src_table = pte_offset(src_middle, stmp);
283 dest_dir = pgd_offset(current->mm, dtmp);
284 dest_middle = pmd_alloc(dest_dir, dtmp);
285 if (!dest_middle)
286 return -ENOMEM;
287 dest_table = pte_alloc(dest_middle, dtmp);
288 if (!dest_table)
289 return -ENOMEM;
291 if (!pte_present(*src_table))
292 handle_mm_fault(tsk, src_vma, stmp, 1);
294 if ((vma->vm_flags & VM_WRITE) && !pte_write(*src_table))
295 handle_mm_fault(tsk, src_vma, stmp, 1);
297 set_pte(src_table, pte_mkdirty(*src_table));
298 set_pte(dest_table, *src_table);
299 mapnr = MAP_NR(pte_page(*src_table));
300 if (mapnr < max_mapnr)
301 get_page(mem_map + MAP_NR(pte_page(*src_table)));
303 stmp += PAGE_SIZE;
304 dtmp += PAGE_SIZE;
307 flush_tlb_range(vma->vm_mm, vma->vm_start, vma->vm_end);
308 flush_tlb_range(src_vma->vm_mm, src_vma->vm_start, src_vma->vm_end);
309 return 0;
312 static struct file_operations proc_mem_operations = {
313 mem_lseek,
314 mem_read,
315 mem_write,
316 NULL, /* mem_readdir */
317 NULL, /* mem_poll */
318 NULL, /* mem_ioctl */
319 mem_mmap, /* mmap */
320 NULL, /* no special open code */
321 NULL, /* flush */
322 NULL, /* no special release code */
323 NULL /* can't fsync */
326 struct inode_operations proc_mem_inode_operations = {
327 &proc_mem_operations, /* default base directory file-ops */
328 NULL, /* create */
329 NULL, /* lookup */
330 NULL, /* link */
331 NULL, /* unlink */
332 NULL, /* symlink */
333 NULL, /* mkdir */
334 NULL, /* rmdir */
335 NULL, /* mknod */
336 NULL, /* rename */
337 NULL, /* readlink */
338 NULL, /* follow_link */
339 NULL, /* readpage */
340 NULL, /* writepage */
341 NULL, /* bmap */
342 NULL, /* truncate */
343 proc_permission /* permission */