ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / kernel / compat.c
1 /*
2  *  linux/kernel/compat.c
3  *
4  *  Kernel compatibililty routines for e.g. 32 bit syscall support
5  *  on 64 bit kernels.
6  *
7  *  Copyright (C) 2002-2003 Stephen Rothwell, IBM Corporation
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License version 2 as
11  *  published by the Free Software Foundation.
12  */
13
14 #include <linux/linkage.h>
15 #include <linux/compat.h>
16 #include <linux/errno.h>
17 #include <linux/time.h>
18 #include <linux/signal.h>
19 #include <linux/sched.h>        /* for MAX_SCHEDULE_TIMEOUT */
20 #include <linux/futex.h>        /* for FUTEX_WAIT */
21 #include <linux/syscalls.h>
22 #include <linux/unistd.h>
23
24 #include <asm/uaccess.h>
25
26 int get_compat_timespec(struct timespec *ts, const struct compat_timespec *cts)
27 {
28         return (verify_area(VERIFY_READ, cts, sizeof(*cts)) ||
29                         __get_user(ts->tv_sec, &cts->tv_sec) ||
30                         __get_user(ts->tv_nsec, &cts->tv_nsec)) ? -EFAULT : 0;
31 }
32
33 int put_compat_timespec(const struct timespec *ts, struct compat_timespec *cts)
34 {
35         return (verify_area(VERIFY_WRITE, cts, sizeof(*cts)) ||
36                         __put_user(ts->tv_sec, &cts->tv_sec) ||
37                         __put_user(ts->tv_nsec, &cts->tv_nsec)) ? -EFAULT : 0;
38 }
39
40 static long compat_nanosleep_restart(struct restart_block *restart)
41 {
42         unsigned long expire = restart->arg0, now = jiffies;
43         struct compat_timespec *rmtp;
44
45         /* Did it expire while we handled signals? */
46         if (!time_after(expire, now))
47                 return 0;
48
49         current->state = TASK_INTERRUPTIBLE;
50         expire = schedule_timeout(expire - now);
51         if (expire == 0)
52                 return 0;
53
54         rmtp = (struct compat_timespec *)restart->arg1;
55         if (rmtp) {
56                 struct compat_timespec ct;
57                 struct timespec t;
58
59                 jiffies_to_timespec(expire, &t);
60                 ct.tv_sec = t.tv_sec;
61                 ct.tv_nsec = t.tv_nsec;
62                 if (copy_to_user(rmtp, &ct, sizeof(ct)))
63                         return -EFAULT;
64         }
65         /* The 'restart' block is already filled in */
66         return -ERESTART_RESTARTBLOCK;
67 }
68
69 asmlinkage long compat_sys_nanosleep(struct compat_timespec *rqtp,
70                 struct compat_timespec *rmtp)
71 {
72         struct timespec t;
73         struct restart_block *restart;
74         unsigned long expire;
75
76         if (get_compat_timespec(&t, rqtp))
77                 return -EFAULT;
78
79         if ((t.tv_nsec >= 1000000000L) || (t.tv_nsec < 0) || (t.tv_sec < 0))
80                 return -EINVAL;
81
82         expire = timespec_to_jiffies(&t) + (t.tv_sec || t.tv_nsec);
83         current->state = TASK_INTERRUPTIBLE;
84         expire = schedule_timeout(expire);
85         if (expire == 0)
86                 return 0;
87
88         if (rmtp) {
89                 jiffies_to_timespec(expire, &t);
90                 if (put_compat_timespec(&t, rmtp))
91                         return -EFAULT;
92         }
93         restart = &current_thread_info()->restart_block;
94         restart->fn = compat_nanosleep_restart;
95         restart->arg0 = jiffies + expire;
96         restart->arg1 = (unsigned long) rmtp;
97         return -ERESTART_RESTARTBLOCK;
98 }
99
100 static inline long get_compat_itimerval(struct itimerval *o,
101                 struct compat_itimerval *i)
102 {
103         return (!access_ok(VERIFY_READ, i, sizeof(*i)) ||
104                 (__get_user(o->it_interval.tv_sec, &i->it_interval.tv_sec) |
105                  __get_user(o->it_interval.tv_usec, &i->it_interval.tv_usec) |
106                  __get_user(o->it_value.tv_sec, &i->it_value.tv_sec) |
107                  __get_user(o->it_value.tv_usec, &i->it_value.tv_usec)));
108 }
109
110 static inline long put_compat_itimerval(struct compat_itimerval *o,
111                 struct itimerval *i)
112 {
113         return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) ||
114                 (__put_user(i->it_interval.tv_sec, &o->it_interval.tv_sec) |
115                  __put_user(i->it_interval.tv_usec, &o->it_interval.tv_usec) |
116                  __put_user(i->it_value.tv_sec, &o->it_value.tv_sec) |
117                  __put_user(i->it_value.tv_usec, &o->it_value.tv_usec)));
118 }
119
120 asmlinkage long compat_sys_getitimer(int which, struct compat_itimerval *it)
121 {
122         struct itimerval kit;
123         int error;
124
125         error = do_getitimer(which, &kit);
126         if (!error && put_compat_itimerval(it, &kit))
127                 error = -EFAULT;
128         return error;
129 }
130
131 asmlinkage long compat_sys_setitimer(int which, struct compat_itimerval *in,
132                 struct compat_itimerval *out)
133 {
134         struct itimerval kin, kout;
135         int error;
136
137         if (in) {
138                 if (get_compat_itimerval(&kin, in))
139                         return -EFAULT;
140         } else
141                 memset(&kin, 0, sizeof(kin));
142
143         error = do_setitimer(which, &kin, out ? &kout : NULL);
144         if (error || !out)
145                 return error;
146         if (put_compat_itimerval(out, &kout))
147                 return -EFAULT;
148         return 0;
149 }
150
151 asmlinkage long compat_sys_times(struct compat_tms *tbuf)
152 {
153         /*
154          *      In the SMP world we might just be unlucky and have one of
155          *      the times increment as we use it. Since the value is an
156          *      atomically safe type this is just fine. Conceptually its
157          *      as if the syscall took an instant longer to occur.
158          */
159         if (tbuf) {
160                 struct compat_tms tmp;
161                 tmp.tms_utime = compat_jiffies_to_clock_t(current->utime);
162                 tmp.tms_stime = compat_jiffies_to_clock_t(current->stime);
163                 tmp.tms_cutime = compat_jiffies_to_clock_t(current->cutime);
164                 tmp.tms_cstime = compat_jiffies_to_clock_t(current->cstime);
165                 if (copy_to_user(tbuf, &tmp, sizeof(tmp)))
166                         return -EFAULT;
167         }
168         return compat_jiffies_to_clock_t(jiffies);
169 }
170
171 /*
172  * Assumption: old_sigset_t and compat_old_sigset_t are both
173  * types that can be passed to put_user()/get_user().
174  */
175
176 asmlinkage long compat_sys_sigpending(compat_old_sigset_t *set)
177 {
178         old_sigset_t s;
179         long ret;
180         mm_segment_t old_fs = get_fs();
181
182         set_fs(KERNEL_DS);
183         ret = sys_sigpending(&s);
184         set_fs(old_fs);
185         if (ret == 0)
186                 ret = put_user(s, set);
187         return ret;
188 }
189
190 asmlinkage long compat_sys_sigprocmask(int how, compat_old_sigset_t *set,
191                 compat_old_sigset_t *oset)
192 {
193         old_sigset_t s;
194         long ret;
195         mm_segment_t old_fs;
196
197         if (set && get_user(s, set))
198                 return -EFAULT;
199         old_fs = get_fs();
200         set_fs(KERNEL_DS);
201         ret = sys_sigprocmask(how, set ? &s : NULL, oset ? &s : NULL);
202         set_fs(old_fs);
203         if (ret == 0)
204                 if (oset)
205                         ret = put_user(s, oset);
206         return ret;
207 }
208
209 #ifdef CONFIG_FUTEX
210 asmlinkage long compat_sys_futex(u32 *uaddr, int op, int val,
211                 struct compat_timespec *utime, u32 *uaddr2)
212 {
213         struct timespec t;
214         unsigned long timeout = MAX_SCHEDULE_TIMEOUT;
215         int val2 = 0;
216
217         if ((op == FUTEX_WAIT) && utime) {
218                 if (get_compat_timespec(&t, utime))
219                         return -EFAULT;
220                 timeout = timespec_to_jiffies(&t) + 1;
221         }
222         if (op == FUTEX_REQUEUE)
223                 val2 = (int) (long) utime;
224
225         return do_futex((unsigned long)uaddr, op, val, timeout,
226                         (unsigned long)uaddr2, val2);
227 }
228 #endif
229
230 asmlinkage long compat_sys_setrlimit(unsigned int resource, struct compat_rlimit *rlim)
231 {
232         struct rlimit r;
233         int ret;
234         mm_segment_t old_fs = get_fs ();
235
236         if (resource >= RLIM_NLIMITS) 
237                 return -EINVAL; 
238
239         if (!access_ok(VERIFY_READ, rlim, sizeof(*rlim)) ||
240             __get_user(r.rlim_cur, &rlim->rlim_cur) ||
241             __get_user(r.rlim_max, &rlim->rlim_max))
242                 return -EFAULT;
243
244         if (r.rlim_cur == COMPAT_RLIM_INFINITY)
245                 r.rlim_cur = RLIM_INFINITY;
246         if (r.rlim_max == COMPAT_RLIM_INFINITY)
247                 r.rlim_max = RLIM_INFINITY;
248         set_fs(KERNEL_DS);
249         ret = sys_setrlimit(resource, &r);
250         set_fs(old_fs);
251         return ret;
252 }
253
254 #ifdef COMPAT_RLIM_OLD_INFINITY
255
256 asmlinkage long compat_sys_old_getrlimit(unsigned int resource, struct compat_rlimit *rlim)
257 {
258         struct rlimit r;
259         int ret;
260         mm_segment_t old_fs = get_fs();
261
262         set_fs(KERNEL_DS);
263         ret = sys_old_getrlimit(resource, &r);
264         set_fs(old_fs);
265
266         if (!ret) {
267                 if (r.rlim_cur > COMPAT_RLIM_OLD_INFINITY)
268                         r.rlim_cur = COMPAT_RLIM_INFINITY;
269                 if (r.rlim_max > COMPAT_RLIM_OLD_INFINITY)
270                         r.rlim_max = COMPAT_RLIM_INFINITY;
271
272                 if (!access_ok(VERIFY_WRITE, rlim, sizeof(*rlim)) ||
273                     __put_user(r.rlim_cur, &rlim->rlim_cur) ||
274                     __put_user(r.rlim_max, &rlim->rlim_max))
275                         return -EFAULT;
276         }
277         return ret;
278 }
279
280 #endif
281
282 asmlinkage long compat_sys_getrlimit (unsigned int resource, struct compat_rlimit *rlim)
283 {
284         struct rlimit r;
285         int ret;
286         mm_segment_t old_fs = get_fs();
287
288         set_fs(KERNEL_DS);
289         ret = sys_getrlimit(resource, &r);
290         set_fs(old_fs);
291         if (!ret) {
292                 if (r.rlim_cur > COMPAT_RLIM_INFINITY)
293                         r.rlim_cur = COMPAT_RLIM_INFINITY;
294                 if (r.rlim_max > COMPAT_RLIM_INFINITY)
295                         r.rlim_max = COMPAT_RLIM_INFINITY;
296
297                 if (!access_ok(VERIFY_WRITE, rlim, sizeof(*rlim)) ||
298                     __put_user(r.rlim_cur, &rlim->rlim_cur) ||
299                     __put_user(r.rlim_max, &rlim->rlim_max))
300                         return -EFAULT;
301         }
302         return ret;
303 }
304
305 static long put_compat_rusage (struct compat_rusage *ru, struct rusage *r)
306 {
307         if (!access_ok(VERIFY_WRITE, ru, sizeof(*ru)) ||
308             __put_user(r->ru_utime.tv_sec, &ru->ru_utime.tv_sec) ||
309             __put_user(r->ru_utime.tv_usec, &ru->ru_utime.tv_usec) ||
310             __put_user(r->ru_stime.tv_sec, &ru->ru_stime.tv_sec) ||
311             __put_user(r->ru_stime.tv_usec, &ru->ru_stime.tv_usec) ||
312             __put_user(r->ru_maxrss, &ru->ru_maxrss) ||
313             __put_user(r->ru_ixrss, &ru->ru_ixrss) ||
314             __put_user(r->ru_idrss, &ru->ru_idrss) ||
315             __put_user(r->ru_isrss, &ru->ru_isrss) ||
316             __put_user(r->ru_minflt, &ru->ru_minflt) ||
317             __put_user(r->ru_majflt, &ru->ru_majflt) ||
318             __put_user(r->ru_nswap, &ru->ru_nswap) ||
319             __put_user(r->ru_inblock, &ru->ru_inblock) ||
320             __put_user(r->ru_oublock, &ru->ru_oublock) ||
321             __put_user(r->ru_msgsnd, &ru->ru_msgsnd) ||
322             __put_user(r->ru_msgrcv, &ru->ru_msgrcv) ||
323             __put_user(r->ru_nsignals, &ru->ru_nsignals) ||
324             __put_user(r->ru_nvcsw, &ru->ru_nvcsw) ||
325             __put_user(r->ru_nivcsw, &ru->ru_nivcsw))
326                 return -EFAULT;
327         return 0;
328 }
329
330 asmlinkage long compat_sys_getrusage(int who, struct compat_rusage *ru)
331 {
332         struct rusage r;
333         int ret;
334         mm_segment_t old_fs = get_fs();
335
336         set_fs(KERNEL_DS);
337         ret = sys_getrusage(who, &r);
338         set_fs(old_fs);
339
340         if (ret)
341                 return ret;
342
343         if (put_compat_rusage(ru, &r))
344                 return -EFAULT;
345
346         return 0;
347 }
348
349 asmlinkage long
350 compat_sys_wait4(compat_pid_t pid, compat_uint_t * stat_addr, int options,
351         struct compat_rusage *ru)
352 {
353         if (!ru) {
354                 return sys_wait4(pid, stat_addr, options, NULL);
355         } else {
356                 struct rusage r;
357                 int ret;
358                 unsigned int status;
359                 mm_segment_t old_fs = get_fs();
360
361                 set_fs (KERNEL_DS);
362                 ret = sys_wait4(pid, stat_addr ? &status : NULL, options, &r);
363                 set_fs (old_fs);
364
365                 if (ret > 0) {
366                         if (put_compat_rusage(ru, &r)) 
367                                 return -EFAULT;
368                         if (stat_addr && put_user(status, stat_addr))
369                                 return -EFAULT;
370                 }
371                 return ret;
372         }
373 }
374
375 asmlinkage long compat_sys_sched_setaffinity(compat_pid_t pid, 
376                                              unsigned int len,
377                                              compat_ulong_t *user_mask_ptr)
378 {
379         unsigned long kernel_mask;
380         mm_segment_t old_fs;
381         int ret;
382
383         if (get_user(kernel_mask, user_mask_ptr))
384                 return -EFAULT;
385
386         old_fs = get_fs();
387         set_fs(KERNEL_DS);
388         ret = sys_sched_setaffinity(pid,
389                                     sizeof(kernel_mask),
390                                     &kernel_mask);
391         set_fs(old_fs);
392
393         return ret;
394 }
395
396 asmlinkage int compat_sys_sched_getaffinity(compat_pid_t pid, unsigned int len,
397                                             compat_ulong_t *user_mask_ptr)
398 {
399         unsigned long kernel_mask;
400         mm_segment_t old_fs;
401         int ret;
402
403         old_fs = get_fs();
404         set_fs(KERNEL_DS);
405         ret = sys_sched_getaffinity(pid,
406                                     sizeof(kernel_mask),
407                                     &kernel_mask);
408         set_fs(old_fs);
409
410         if (ret > 0) {
411                 ret = sizeof(compat_ulong_t);
412                 if (put_user(kernel_mask, user_mask_ptr))
413                         return -EFAULT;
414         }
415
416         return ret;
417 }
418
419 static int get_compat_itimerspec(struct itimerspec *dst, 
420                                  struct compat_itimerspec *src)
421
422         if (get_compat_timespec(&dst->it_interval, &src->it_interval) ||
423             get_compat_timespec(&dst->it_value, &src->it_value))
424                 return -EFAULT;
425         return 0;
426
427
428 static int put_compat_itimerspec(struct compat_itimerspec *dst, 
429                                  struct itimerspec *src)
430
431         if (put_compat_timespec(&src->it_interval, &dst->it_interval) ||
432             put_compat_timespec(&src->it_value, &dst->it_value))
433                 return -EFAULT;
434         return 0;
435
436
437 long compat_timer_settime(timer_t timer_id, int flags, 
438                           struct compat_itimerspec *new, 
439                           struct compat_itimerspec *old)
440
441         long err;
442         mm_segment_t oldfs;
443         struct itimerspec newts, oldts;
444
445         if (!new)
446                 return -EINVAL;
447         if (get_compat_itimerspec(&newts, new))
448                 return -EFAULT; 
449         oldfs = get_fs();
450         set_fs(KERNEL_DS);
451         err = sys_timer_settime(timer_id, flags, &newts, &oldts);
452         set_fs(oldfs); 
453         if (!err && old && put_compat_itimerspec(old, &oldts))
454                 return -EFAULT;
455         return err;
456
457
458 long compat_timer_gettime(timer_t timer_id, struct compat_itimerspec *setting)
459
460         long err;
461         mm_segment_t oldfs;
462         struct itimerspec ts; 
463         oldfs = get_fs();
464         set_fs(KERNEL_DS);
465         err = sys_timer_gettime(timer_id, &ts); 
466         set_fs(oldfs); 
467         if (!err && put_compat_itimerspec(setting, &ts))
468                 return -EFAULT;
469         return err;
470
471
472 long compat_clock_settime(clockid_t which_clock,  struct compat_timespec *tp)
473 {
474         long err;
475         mm_segment_t oldfs;
476         struct timespec ts; 
477         if (get_compat_timespec(&ts, tp))
478                 return -EFAULT; 
479         oldfs = get_fs();
480         set_fs(KERNEL_DS);      
481         err = sys_clock_settime(which_clock, &ts); 
482         set_fs(oldfs);
483         return err;
484
485
486 long compat_clock_gettime(clockid_t which_clock,  struct compat_timespec *tp)
487 {
488         long err;
489         mm_segment_t oldfs;
490         struct timespec ts; 
491         oldfs = get_fs();
492         set_fs(KERNEL_DS);
493         err = sys_clock_gettime(which_clock, &ts); 
494         set_fs(oldfs);
495         if (!err && put_compat_timespec(&ts, tp))
496                 return -EFAULT; 
497         return err;
498
499
500 long compat_clock_getres(clockid_t which_clock,  struct compat_timespec *tp)
501 {
502         long err;
503         mm_segment_t oldfs;
504         struct timespec ts; 
505         oldfs = get_fs();
506         set_fs(KERNEL_DS);
507         err = sys_clock_getres(which_clock, &ts); 
508         set_fs(oldfs);
509         if (!err && put_compat_timespec(&ts, tp))
510                 return -EFAULT; 
511         return err;
512
513
514 long compat_clock_nanosleep(clockid_t which_clock, int flags,
515                             struct compat_timespec __user *rqtp,
516                             struct compat_timespec __user *rmtp)
517 {
518         long err;
519         mm_segment_t oldfs;
520         struct timespec in, out; 
521         if (get_compat_timespec(&in, rqtp)) 
522                 return -EFAULT;
523         oldfs = get_fs();
524         set_fs(KERNEL_DS);      
525         err = sys_clock_nanosleep(which_clock, flags, &in, &out);  
526         set_fs(oldfs);
527         if ((err == -ERESTART_RESTARTBLOCK) && rmtp &&
528             put_compat_timespec(&out, rmtp))
529                 return -EFAULT;
530         return err;     
531
532
533 /* timer_create is architecture specific because it needs sigevent conversion */
534