This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / mm / usercopy.c
1 /*
2  * linux/mm/usercopy.c
3  *
4  * (C) Copyright 2003 Ingo Molnar
5  *
6  * Generic implementation of all the user-VM access functions, without
7  * relying on being able to access the VM directly.
8  */
9
10 #include <linux/module.h>
11 #include <linux/sched.h>
12 #include <linux/errno.h>
13 #include <linux/mm.h>
14 #include <linux/highmem.h>
15 #include <linux/pagemap.h>
16 #include <linux/smp_lock.h>
17 #include <linux/ptrace.h>
18 #include <linux/interrupt.h>
19
20 #include <asm/pgtable.h>
21 #include <asm/uaccess.h>
22 #include <asm/atomic_kmap.h>
23
24 /*
25  * Get kernel address of the user page and pin it.
26  */
27 static inline struct page *pin_page(unsigned long addr, int write)
28 {
29         struct mm_struct *mm = current->mm ? : &init_mm;
30         struct page *page = NULL;
31         int ret;
32
33         /*
34          * Do a quick atomic lookup first - this is the fastpath.
35          */
36 retry:
37         page = follow_page(mm, addr, write);
38         if (likely(page != NULL)) {
39                 if (!PageReserved(page))
40                         get_page(page);
41                 return page;
42         }
43
44         /*
45          * No luck - bad address or need to fault in the page:
46          */
47
48         /* Release the lock so get_user_pages can sleep */
49         spin_unlock(&mm->page_table_lock);
50
51         /*
52          * In the context of filemap_copy_from_user(), we are not allowed
53          * to sleep.  We must fail this usercopy attempt and allow
54          * filemap_copy_from_user() to recover: drop its atomic kmap and use
55          * a sleeping kmap instead.
56          */
57         if (in_atomic()) {
58                 spin_lock(&mm->page_table_lock);
59                 return NULL;
60         }
61
62         down_read(&mm->mmap_sem);
63         ret = get_user_pages(current, mm, addr, 1, write, 0, NULL, NULL);
64         up_read(&mm->mmap_sem);
65         spin_lock(&mm->page_table_lock);
66
67         if (ret <= 0)
68                 return NULL;
69
70         /*
71          * Go try the follow_page again.
72          */
73         goto retry;
74 }
75
76 static inline void unpin_page(struct page *page)
77 {
78         put_page(page);
79 }
80
81 /*
82  * Access another process' address space.
83  * Source/target buffer must be kernel space,
84  * Do not walk the page table directly, use get_user_pages
85  */
86 static int rw_vm(unsigned long addr, void *buf, int len, int write)
87 {
88         struct mm_struct *mm = current->mm ? : &init_mm;
89
90         if (!len)
91                 return 0;
92
93         spin_lock(&mm->page_table_lock);
94
95         /* ignore errors, just check how much was sucessfully transfered */
96         while (len) {
97                 struct page *page = NULL;
98                 int bytes, offset;
99                 void *maddr;
100
101                 page = pin_page(addr, write);
102                 if (!page)
103                         break;
104
105                 bytes = len;
106                 offset = addr & (PAGE_SIZE-1);
107                 if (bytes > PAGE_SIZE-offset)
108                         bytes = PAGE_SIZE-offset;
109
110                 maddr = kmap_atomic(page, KM_USER_COPY);
111
112 #define HANDLE_TYPE(type) \
113         case sizeof(type): *(type *)(maddr+offset) = *(type *)(buf); break;
114
115                 if (write) {
116                         switch (bytes) {
117                         HANDLE_TYPE(char);
118                         HANDLE_TYPE(int);
119                         HANDLE_TYPE(long long);
120                         default:
121                                 memcpy(maddr + offset, buf, bytes);
122                         }
123                 } else {
124 #undef HANDLE_TYPE
125 #define HANDLE_TYPE(type) \
126         case sizeof(type): *(type *)(buf) = *(type *)(maddr+offset); break;
127                         switch (bytes) {
128                         HANDLE_TYPE(char);
129                         HANDLE_TYPE(int);
130                         HANDLE_TYPE(long long);
131                         default:
132                                 memcpy(buf, maddr + offset, bytes);
133                         }
134 #undef HANDLE_TYPE
135                 }
136                 kunmap_atomic(maddr, KM_USER_COPY);
137                 unpin_page(page);
138                 len -= bytes;
139                 buf += bytes;
140                 addr += bytes;
141         }
142         spin_unlock(&mm->page_table_lock);
143
144         return len;
145 }
146
147 static int str_vm(unsigned long addr, void *buf0, int len, int copy)
148 {
149         struct mm_struct *mm = current->mm ? : &init_mm;
150         struct page *page;
151         void *buf = buf0;
152
153         if (!len)
154                 return len;
155
156         spin_lock(&mm->page_table_lock);
157
158         /* ignore errors, just check how much was sucessfully transfered */
159         while (len) {
160                 int bytes, offset, left, copied;
161                 char *maddr;
162
163                 page = pin_page(addr, copy == 2);
164                 if (!page) {
165                         spin_unlock(&mm->page_table_lock);
166                         return -EFAULT;
167                 }
168                 bytes = len;
169                 offset = addr & (PAGE_SIZE-1);
170                 if (bytes > PAGE_SIZE-offset)
171                         bytes = PAGE_SIZE-offset;
172
173                 maddr = kmap_atomic(page, KM_USER_COPY);
174                 if (copy == 2) {
175                         memset(maddr + offset, 0, bytes);
176                         copied = bytes;
177                         left = 0;
178                 } else if (copy == 1) {
179                         left = strncpy_count(buf, maddr + offset, bytes);
180                         copied = bytes - left;
181                 } else {
182                         copied = strnlen(maddr + offset, bytes);
183                         left = bytes - copied;
184                 }
185                 BUG_ON(bytes < 0 || copied < 0);
186                 kunmap_atomic(maddr, KM_USER_COPY);
187                 unpin_page(page);
188                 len -= copied;
189                 buf += copied;
190                 addr += copied;
191                 if (left)
192                         break;
193         }
194         spin_unlock(&mm->page_table_lock);
195
196         return len;
197 }
198
199 /*
200  * Copies memory from userspace (ptr) into kernelspace (val).
201  *
202  * returns # of bytes not copied.
203  */
204 int get_user_size(unsigned int size, void *val, const void *ptr)
205 {
206         int ret;
207
208         if (unlikely(segment_eq(get_fs(), KERNEL_DS)))
209                 ret = __direct_copy_from_user(val, ptr, size);
210         else
211                 ret = rw_vm((unsigned long)ptr, val, size, 0);
212         if (ret)
213                 /*
214                  * Zero the rest:
215                  */
216                 memset(val + size - ret, 0, ret);
217         return ret;
218 }
219
220 /*
221  * Copies memory from kernelspace (val) into userspace (ptr).
222  *
223  * returns # of bytes not copied.
224  */
225 int put_user_size(unsigned int size, const void *val, void *ptr)
226 {
227         if (unlikely(segment_eq(get_fs(), KERNEL_DS)))
228                 return __direct_copy_to_user(ptr, val, size);
229         else
230                 return rw_vm((unsigned long)ptr, (void *)val, size, 1);
231 }
232
233 int copy_str_fromuser_size(unsigned int size, void *val, const void *ptr)
234 {
235         int copied, left;
236
237         if (unlikely(segment_eq(get_fs(), KERNEL_DS))) {
238                 left = strncpy_count(val, ptr, size);
239                 copied = size - left;
240                 BUG_ON(copied < 0);
241
242                 return copied;
243         }
244         left = str_vm((unsigned long)ptr, val, size, 1);
245         if (left < 0)
246                 return left;
247         copied = size - left;
248         BUG_ON(copied < 0);
249
250         return copied;
251 }
252
253 int strlen_fromuser_size(unsigned int size, const void *ptr)
254 {
255         int copied, left;
256
257         if (unlikely(segment_eq(get_fs(), KERNEL_DS))) {
258                 copied = strnlen(ptr, size) + 1;
259                 BUG_ON(copied < 0);
260
261                 return copied;
262         }
263         left = str_vm((unsigned long)ptr, NULL, size, 0);
264         if (left < 0)
265                 return 0;
266         copied = size - left + 1;
267         BUG_ON(copied < 0);
268
269         return copied;
270 }
271
272 int zero_user_size(unsigned int size, void *ptr)
273 {
274         int left;
275
276         if (unlikely(segment_eq(get_fs(), KERNEL_DS))) {
277                 memset(ptr, 0, size);
278                 return 0;
279         }
280         left = str_vm((unsigned long)ptr, NULL, size, 2);
281         if (left < 0)
282                 return size;
283         return left;
284 }
285
286 EXPORT_SYMBOL(get_user_size);
287 EXPORT_SYMBOL(put_user_size);
288 EXPORT_SYMBOL(zero_user_size);
289 EXPORT_SYMBOL(copy_str_fromuser_size);
290 EXPORT_SYMBOL(strlen_fromuser_size);