blob: 9cf75df9b2bb4753356ca9e3e5ae527b49329268 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/fs/compat.c
3 *
4 * Kernel compatibililty routines for e.g. 32 bit syscall support
5 * on 64 bit kernels.
6 *
7 * Copyright (C) 2002 Stephen Rothwell, IBM Corporation
8 * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com)
9 * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
10 * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs
11 * Copyright (C) 2003 Pavel Machek (pavel@suse.cz)
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License version 2 as
15 * published by the Free Software Foundation.
16 */
17
Milind Arun Choudhary022a1692007-05-08 00:29:02 -070018#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <linux/linkage.h>
20#include <linux/compat.h>
21#include <linux/errno.h>
22#include <linux/time.h>
23#include <linux/fs.h>
24#include <linux/fcntl.h>
25#include <linux/namei.h>
26#include <linux/file.h>
27#include <linux/vfs.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <linux/ioctl.h>
29#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/smb.h>
31#include <linux/smb_mount.h>
32#include <linux/ncp_mount.h>
David Howells9a9947b2005-04-18 10:54:51 -070033#include <linux/nfs4_mount.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034#include <linux/smp_lock.h>
35#include <linux/syscalls.h>
36#include <linux/ctype.h>
37#include <linux/module.h>
38#include <linux/dirent.h>
Robert Love0eeca282005-07-12 17:06:03 -040039#include <linux/fsnotify.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#include <linux/highuid.h>
41#include <linux/sunrpc/svc.h>
42#include <linux/nfsd/nfsd.h>
43#include <linux/nfsd/syscall.h>
44#include <linux/personality.h>
45#include <linux/rwsem.h>
Jay Lan8f0ab512006-09-30 23:28:59 -070046#include <linux/tsacct_kern.h>
Christoph Hellwig6272e262007-05-08 00:29:21 -070047#include <linux/security.h>
Al Viroa1f8e7f2006-10-19 16:08:53 -040048#include <linux/highmem.h>
Al Virobd01f842006-10-19 17:23:57 -040049#include <linux/poll.h>
David S. Miller4a805e82005-09-14 21:40:00 -070050#include <linux/mm.h>
Davide Libenzif6dfb4f2007-03-07 20:41:21 -080051#include <linux/eventpoll.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
Linus Torvalds1da177e2005-04-16 15:20:36 -070053#include <asm/uaccess.h>
54#include <asm/mmu_context.h>
55#include <asm/ioctls.h>
David Howells07f3f052006-09-30 20:52:18 +020056#include "internal.h"
David Woodhouse9f729492006-01-18 17:44:05 -080057
Andi Kleenbebfa102006-06-26 13:56:52 +020058int compat_log = 1;
59
60int compat_printk(const char *fmt, ...)
61{
62 va_list ap;
63 int ret;
64 if (!compat_log)
65 return 0;
66 va_start(ap, fmt);
67 ret = vprintk(fmt, ap);
68 va_end(ap);
69 return ret;
70}
71
Badari Pulavartyee0b3e62006-09-30 23:28:47 -070072#include "read_write.h"
73
Linus Torvalds1da177e2005-04-16 15:20:36 -070074/*
75 * Not all architectures have sys_utime, so implement this in terms
76 * of sys_utimes.
77 */
78asmlinkage long compat_sys_utime(char __user *filename, struct compat_utimbuf __user *t)
79{
Ulrich Drepper1c710c82007-05-08 00:33:25 -070080 struct timespec tv[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -070081
82 if (t) {
83 if (get_user(tv[0].tv_sec, &t->actime) ||
84 get_user(tv[1].tv_sec, &t->modtime))
85 return -EFAULT;
Ulrich Drepper1c710c82007-05-08 00:33:25 -070086 tv[0].tv_nsec = 0;
87 tv[1].tv_nsec = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 }
Ulrich Drepper1c710c82007-05-08 00:33:25 -070089 return do_utimes(AT_FDCWD, filename, t ? tv : NULL, 0);
90}
91
92asmlinkage long compat_sys_utimensat(unsigned int dfd, char __user *filename, struct compat_timespec __user *t, int flags)
93{
94 struct timespec tv[2];
95
96 if (t) {
97 if (get_compat_timespec(&tv[0], &t[0]) ||
98 get_compat_timespec(&tv[1], &t[1]))
99 return -EFAULT;
100
101 if ((tv[0].tv_nsec == UTIME_OMIT || tv[0].tv_nsec == UTIME_NOW)
102 && tv[0].tv_sec != 0)
103 return -EINVAL;
104 if ((tv[1].tv_nsec == UTIME_OMIT || tv[1].tv_nsec == UTIME_NOW)
105 && tv[1].tv_sec != 0)
106 return -EINVAL;
107
108 if (tv[0].tv_nsec == UTIME_OMIT && tv[1].tv_nsec == UTIME_OMIT)
109 return 0;
110 }
111 return do_utimes(dfd, filename, t ? tv : NULL, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112}
113
Stephen Rothwell9ad11ab2006-02-02 16:11:51 +1100114asmlinkage long compat_sys_futimesat(unsigned int dfd, char __user *filename, struct compat_timeval __user *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115{
Ulrich Drepper1c710c82007-05-08 00:33:25 -0700116 struct timespec tv[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117
Stephen Rothwell9ad11ab2006-02-02 16:11:51 +1100118 if (t) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 if (get_user(tv[0].tv_sec, &t[0].tv_sec) ||
Ulrich Drepper1c710c82007-05-08 00:33:25 -0700120 get_user(tv[0].tv_nsec, &t[0].tv_usec) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 get_user(tv[1].tv_sec, &t[1].tv_sec) ||
Ulrich Drepper1c710c82007-05-08 00:33:25 -0700122 get_user(tv[1].tv_nsec, &t[1].tv_usec))
Stephen Rothwell9ad11ab2006-02-02 16:11:51 +1100123 return -EFAULT;
Ulrich Drepper1c710c82007-05-08 00:33:25 -0700124 if (tv[0].tv_nsec >= 1000000 || tv[0].tv_nsec < 0 ||
125 tv[1].tv_nsec >= 1000000 || tv[1].tv_nsec < 0)
126 return -EINVAL;
127 tv[0].tv_nsec *= 1000;
128 tv[1].tv_nsec *= 1000;
Stephen Rothwell9ad11ab2006-02-02 16:11:51 +1100129 }
Ulrich Drepper1c710c82007-05-08 00:33:25 -0700130 return do_utimes(dfd, filename, t ? tv : NULL, 0);
Ulrich Drepper5590ff02006-01-18 17:43:53 -0800131}
132
133asmlinkage long compat_sys_utimes(char __user *filename, struct compat_timeval __user *t)
134{
135 return compat_sys_futimesat(AT_FDCWD, filename, t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136}
137
138asmlinkage long compat_sys_newstat(char __user * filename,
139 struct compat_stat __user *statbuf)
140{
141 struct kstat stat;
Ulrich Drepper5590ff02006-01-18 17:43:53 -0800142 int error = vfs_stat_fd(AT_FDCWD, filename, &stat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143
144 if (!error)
145 error = cp_compat_stat(&stat, statbuf);
146 return error;
147}
148
149asmlinkage long compat_sys_newlstat(char __user * filename,
150 struct compat_stat __user *statbuf)
151{
152 struct kstat stat;
Ulrich Drepper5590ff02006-01-18 17:43:53 -0800153 int error = vfs_lstat_fd(AT_FDCWD, filename, &stat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154
155 if (!error)
156 error = cp_compat_stat(&stat, statbuf);
157 return error;
158}
159
Kyle McMartin82d821d2006-03-24 03:18:20 -0800160#ifndef __ARCH_WANT_STAT64
Stephen Rothwell9ad11ab2006-02-02 16:11:51 +1100161asmlinkage long compat_sys_newfstatat(unsigned int dfd, char __user *filename,
Ulrich Drepper5590ff02006-01-18 17:43:53 -0800162 struct compat_stat __user *statbuf, int flag)
163{
164 struct kstat stat;
165 int error = -EINVAL;
166
167 if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0)
168 goto out;
169
170 if (flag & AT_SYMLINK_NOFOLLOW)
171 error = vfs_lstat_fd(dfd, filename, &stat);
172 else
173 error = vfs_stat_fd(dfd, filename, &stat);
174
175 if (!error)
176 error = cp_compat_stat(&stat, statbuf);
177
178out:
179 return error;
180}
Kyle McMartin82d821d2006-03-24 03:18:20 -0800181#endif
Ulrich Drepper5590ff02006-01-18 17:43:53 -0800182
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183asmlinkage long compat_sys_newfstat(unsigned int fd,
184 struct compat_stat __user * statbuf)
185{
186 struct kstat stat;
187 int error = vfs_fstat(fd, &stat);
188
189 if (!error)
190 error = cp_compat_stat(&stat, statbuf);
191 return error;
192}
193
194static int put_compat_statfs(struct compat_statfs __user *ubuf, struct kstatfs *kbuf)
195{
196
197 if (sizeof ubuf->f_blocks == 4) {
198 if ((kbuf->f_blocks | kbuf->f_bfree | kbuf->f_bavail) &
199 0xffffffff00000000ULL)
200 return -EOVERFLOW;
201 /* f_files and f_ffree may be -1; it's okay
202 * to stuff that into 32 bits */
203 if (kbuf->f_files != 0xffffffffffffffffULL
204 && (kbuf->f_files & 0xffffffff00000000ULL))
205 return -EOVERFLOW;
206 if (kbuf->f_ffree != 0xffffffffffffffffULL
207 && (kbuf->f_ffree & 0xffffffff00000000ULL))
208 return -EOVERFLOW;
209 }
210 if (!access_ok(VERIFY_WRITE, ubuf, sizeof(*ubuf)) ||
211 __put_user(kbuf->f_type, &ubuf->f_type) ||
212 __put_user(kbuf->f_bsize, &ubuf->f_bsize) ||
213 __put_user(kbuf->f_blocks, &ubuf->f_blocks) ||
214 __put_user(kbuf->f_bfree, &ubuf->f_bfree) ||
215 __put_user(kbuf->f_bavail, &ubuf->f_bavail) ||
216 __put_user(kbuf->f_files, &ubuf->f_files) ||
217 __put_user(kbuf->f_ffree, &ubuf->f_ffree) ||
218 __put_user(kbuf->f_namelen, &ubuf->f_namelen) ||
219 __put_user(kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) ||
220 __put_user(kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]) ||
221 __put_user(kbuf->f_frsize, &ubuf->f_frsize) ||
222 __put_user(0, &ubuf->f_spare[0]) ||
223 __put_user(0, &ubuf->f_spare[1]) ||
224 __put_user(0, &ubuf->f_spare[2]) ||
225 __put_user(0, &ubuf->f_spare[3]) ||
226 __put_user(0, &ubuf->f_spare[4]))
227 return -EFAULT;
228 return 0;
229}
230
231/*
232 * The following statfs calls are copies of code from fs/open.c and
233 * should be checked against those from time to time
234 */
235asmlinkage long compat_sys_statfs(const char __user *path, struct compat_statfs __user *buf)
236{
237 struct nameidata nd;
238 int error;
239
240 error = user_path_walk(path, &nd);
241 if (!error) {
242 struct kstatfs tmp;
David Howells726c3342006-06-23 02:02:58 -0700243 error = vfs_statfs(nd.dentry, &tmp);
David Gibson86e07ce2005-11-21 21:32:23 -0800244 if (!error)
245 error = put_compat_statfs(buf, &tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 path_release(&nd);
247 }
248 return error;
249}
250
251asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs __user *buf)
252{
253 struct file * file;
254 struct kstatfs tmp;
255 int error;
256
257 error = -EBADF;
258 file = fget(fd);
259 if (!file)
260 goto out;
Josef "Jeff" Sipek0f7fc9e2006-12-08 02:36:35 -0800261 error = vfs_statfs(file->f_path.dentry, &tmp);
David Gibson86e07ce2005-11-21 21:32:23 -0800262 if (!error)
263 error = put_compat_statfs(buf, &tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 fput(file);
265out:
266 return error;
267}
268
269static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstatfs *kbuf)
270{
271 if (sizeof ubuf->f_blocks == 4) {
272 if ((kbuf->f_blocks | kbuf->f_bfree | kbuf->f_bavail) &
273 0xffffffff00000000ULL)
274 return -EOVERFLOW;
275 /* f_files and f_ffree may be -1; it's okay
276 * to stuff that into 32 bits */
277 if (kbuf->f_files != 0xffffffffffffffffULL
278 && (kbuf->f_files & 0xffffffff00000000ULL))
279 return -EOVERFLOW;
280 if (kbuf->f_ffree != 0xffffffffffffffffULL
281 && (kbuf->f_ffree & 0xffffffff00000000ULL))
282 return -EOVERFLOW;
283 }
284 if (!access_ok(VERIFY_WRITE, ubuf, sizeof(*ubuf)) ||
285 __put_user(kbuf->f_type, &ubuf->f_type) ||
286 __put_user(kbuf->f_bsize, &ubuf->f_bsize) ||
287 __put_user(kbuf->f_blocks, &ubuf->f_blocks) ||
288 __put_user(kbuf->f_bfree, &ubuf->f_bfree) ||
289 __put_user(kbuf->f_bavail, &ubuf->f_bavail) ||
290 __put_user(kbuf->f_files, &ubuf->f_files) ||
291 __put_user(kbuf->f_ffree, &ubuf->f_ffree) ||
292 __put_user(kbuf->f_namelen, &ubuf->f_namelen) ||
293 __put_user(kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) ||
294 __put_user(kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]) ||
295 __put_user(kbuf->f_frsize, &ubuf->f_frsize))
296 return -EFAULT;
297 return 0;
298}
299
300asmlinkage long compat_sys_statfs64(const char __user *path, compat_size_t sz, struct compat_statfs64 __user *buf)
301{
302 struct nameidata nd;
303 int error;
304
305 if (sz != sizeof(*buf))
306 return -EINVAL;
307
308 error = user_path_walk(path, &nd);
309 if (!error) {
310 struct kstatfs tmp;
David Howells726c3342006-06-23 02:02:58 -0700311 error = vfs_statfs(nd.dentry, &tmp);
David Gibson86e07ce2005-11-21 21:32:23 -0800312 if (!error)
313 error = put_compat_statfs64(buf, &tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 path_release(&nd);
315 }
316 return error;
317}
318
319asmlinkage long compat_sys_fstatfs64(unsigned int fd, compat_size_t sz, struct compat_statfs64 __user *buf)
320{
321 struct file * file;
322 struct kstatfs tmp;
323 int error;
324
325 if (sz != sizeof(*buf))
326 return -EINVAL;
327
328 error = -EBADF;
329 file = fget(fd);
330 if (!file)
331 goto out;
Josef "Jeff" Sipek0f7fc9e2006-12-08 02:36:35 -0800332 error = vfs_statfs(file->f_path.dentry, &tmp);
David Gibson86e07ce2005-11-21 21:32:23 -0800333 if (!error)
334 error = put_compat_statfs64(buf, &tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 fput(file);
336out:
337 return error;
338}
339
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340static int get_compat_flock(struct flock *kfl, struct compat_flock __user *ufl)
341{
342 if (!access_ok(VERIFY_READ, ufl, sizeof(*ufl)) ||
343 __get_user(kfl->l_type, &ufl->l_type) ||
344 __get_user(kfl->l_whence, &ufl->l_whence) ||
345 __get_user(kfl->l_start, &ufl->l_start) ||
346 __get_user(kfl->l_len, &ufl->l_len) ||
347 __get_user(kfl->l_pid, &ufl->l_pid))
348 return -EFAULT;
349 return 0;
350}
351
352static int put_compat_flock(struct flock *kfl, struct compat_flock __user *ufl)
353{
354 if (!access_ok(VERIFY_WRITE, ufl, sizeof(*ufl)) ||
355 __put_user(kfl->l_type, &ufl->l_type) ||
356 __put_user(kfl->l_whence, &ufl->l_whence) ||
357 __put_user(kfl->l_start, &ufl->l_start) ||
358 __put_user(kfl->l_len, &ufl->l_len) ||
359 __put_user(kfl->l_pid, &ufl->l_pid))
360 return -EFAULT;
361 return 0;
362}
363
364#ifndef HAVE_ARCH_GET_COMPAT_FLOCK64
365static int get_compat_flock64(struct flock *kfl, struct compat_flock64 __user *ufl)
366{
367 if (!access_ok(VERIFY_READ, ufl, sizeof(*ufl)) ||
368 __get_user(kfl->l_type, &ufl->l_type) ||
369 __get_user(kfl->l_whence, &ufl->l_whence) ||
370 __get_user(kfl->l_start, &ufl->l_start) ||
371 __get_user(kfl->l_len, &ufl->l_len) ||
372 __get_user(kfl->l_pid, &ufl->l_pid))
373 return -EFAULT;
374 return 0;
375}
376#endif
377
378#ifndef HAVE_ARCH_PUT_COMPAT_FLOCK64
379static int put_compat_flock64(struct flock *kfl, struct compat_flock64 __user *ufl)
380{
381 if (!access_ok(VERIFY_WRITE, ufl, sizeof(*ufl)) ||
382 __put_user(kfl->l_type, &ufl->l_type) ||
383 __put_user(kfl->l_whence, &ufl->l_whence) ||
384 __put_user(kfl->l_start, &ufl->l_start) ||
385 __put_user(kfl->l_len, &ufl->l_len) ||
386 __put_user(kfl->l_pid, &ufl->l_pid))
387 return -EFAULT;
388 return 0;
389}
390#endif
391
392asmlinkage long compat_sys_fcntl64(unsigned int fd, unsigned int cmd,
393 unsigned long arg)
394{
395 mm_segment_t old_fs;
396 struct flock f;
397 long ret;
398
399 switch (cmd) {
400 case F_GETLK:
401 case F_SETLK:
402 case F_SETLKW:
403 ret = get_compat_flock(&f, compat_ptr(arg));
404 if (ret != 0)
405 break;
406 old_fs = get_fs();
407 set_fs(KERNEL_DS);
408 ret = sys_fcntl(fd, cmd, (unsigned long)&f);
409 set_fs(old_fs);
410 if (cmd == F_GETLK && ret == 0) {
NeilBrown2520f142006-01-08 01:02:40 -0800411 /* GETLK was successfule and we need to return the data...
412 * but it needs to fit in the compat structure.
413 * l_start shouldn't be too big, unless the original
414 * start + end is greater than COMPAT_OFF_T_MAX, in which
415 * case the app was asking for trouble, so we return
416 * -EOVERFLOW in that case.
417 * l_len could be too big, in which case we just truncate it,
418 * and only allow the app to see that part of the conflicting
419 * lock that might make sense to it anyway
420 */
421
422 if (f.l_start > COMPAT_OFF_T_MAX)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 ret = -EOVERFLOW;
NeilBrown2520f142006-01-08 01:02:40 -0800424 if (f.l_len > COMPAT_OFF_T_MAX)
425 f.l_len = COMPAT_OFF_T_MAX;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 if (ret == 0)
427 ret = put_compat_flock(&f, compat_ptr(arg));
428 }
429 break;
430
431 case F_GETLK64:
432 case F_SETLK64:
433 case F_SETLKW64:
434 ret = get_compat_flock64(&f, compat_ptr(arg));
435 if (ret != 0)
436 break;
437 old_fs = get_fs();
438 set_fs(KERNEL_DS);
439 ret = sys_fcntl(fd, (cmd == F_GETLK64) ? F_GETLK :
440 ((cmd == F_SETLK64) ? F_SETLK : F_SETLKW),
441 (unsigned long)&f);
442 set_fs(old_fs);
443 if (cmd == F_GETLK64 && ret == 0) {
NeilBrown2520f142006-01-08 01:02:40 -0800444 /* need to return lock information - see above for commentary */
445 if (f.l_start > COMPAT_LOFF_T_MAX)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 ret = -EOVERFLOW;
NeilBrown2520f142006-01-08 01:02:40 -0800447 if (f.l_len > COMPAT_LOFF_T_MAX)
448 f.l_len = COMPAT_LOFF_T_MAX;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 if (ret == 0)
450 ret = put_compat_flock64(&f, compat_ptr(arg));
451 }
452 break;
453
454 default:
455 ret = sys_fcntl(fd, cmd, arg);
456 break;
457 }
458 return ret;
459}
460
461asmlinkage long compat_sys_fcntl(unsigned int fd, unsigned int cmd,
462 unsigned long arg)
463{
464 if ((cmd == F_GETLK64) || (cmd == F_SETLK64) || (cmd == F_SETLKW64))
465 return -EINVAL;
466 return compat_sys_fcntl64(fd, cmd, arg);
467}
468
469asmlinkage long
470compat_sys_io_setup(unsigned nr_reqs, u32 __user *ctx32p)
471{
472 long ret;
473 aio_context_t ctx64;
474
475 mm_segment_t oldfs = get_fs();
476 if (unlikely(get_user(ctx64, ctx32p)))
477 return -EFAULT;
478
479 set_fs(KERNEL_DS);
480 /* The __user pointer cast is valid because of the set_fs() */
481 ret = sys_io_setup(nr_reqs, (aio_context_t __user *) &ctx64);
482 set_fs(oldfs);
483 /* truncating is ok because it's a user address */
484 if (!ret)
485 ret = put_user((u32) ctx64, ctx32p);
486 return ret;
487}
488
489asmlinkage long
490compat_sys_io_getevents(aio_context_t ctx_id,
491 unsigned long min_nr,
492 unsigned long nr,
493 struct io_event __user *events,
494 struct compat_timespec __user *timeout)
495{
496 long ret;
497 struct timespec t;
498 struct timespec __user *ut = NULL;
499
500 ret = -EFAULT;
501 if (unlikely(!access_ok(VERIFY_WRITE, events,
502 nr * sizeof(struct io_event))))
503 goto out;
504 if (timeout) {
505 if (get_compat_timespec(&t, timeout))
506 goto out;
507
508 ut = compat_alloc_user_space(sizeof(*ut));
509 if (copy_to_user(ut, &t, sizeof(t)) )
510 goto out;
511 }
512 ret = sys_io_getevents(ctx_id, min_nr, nr, events, ut);
513out:
514 return ret;
515}
516
517static inline long
518copy_iocb(long nr, u32 __user *ptr32, struct iocb __user * __user *ptr64)
519{
520 compat_uptr_t uptr;
521 int i;
522
523 for (i = 0; i < nr; ++i) {
524 if (get_user(uptr, ptr32 + i))
525 return -EFAULT;
526 if (put_user(compat_ptr(uptr), ptr64 + i))
527 return -EFAULT;
528 }
529 return 0;
530}
531
532#define MAX_AIO_SUBMITS (PAGE_SIZE/sizeof(struct iocb *))
533
534asmlinkage long
535compat_sys_io_submit(aio_context_t ctx_id, int nr, u32 __user *iocb)
536{
537 struct iocb __user * __user *iocb64;
538 long ret;
539
540 if (unlikely(nr < 0))
541 return -EINVAL;
542
543 if (nr > MAX_AIO_SUBMITS)
544 nr = MAX_AIO_SUBMITS;
545
546 iocb64 = compat_alloc_user_space(nr * sizeof(*iocb64));
547 ret = copy_iocb(nr, iocb, iocb64);
548 if (!ret)
549 ret = sys_io_submit(ctx_id, nr, iocb64);
550 return ret;
551}
552
553struct compat_ncp_mount_data {
554 compat_int_t version;
555 compat_uint_t ncp_fd;
Stephen Rothwell202e5972005-09-06 15:16:40 -0700556 __compat_uid_t mounted_uid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 compat_pid_t wdog_pid;
558 unsigned char mounted_vol[NCP_VOLNAME_LEN + 1];
559 compat_uint_t time_out;
560 compat_uint_t retry_count;
561 compat_uint_t flags;
Stephen Rothwell202e5972005-09-06 15:16:40 -0700562 __compat_uid_t uid;
563 __compat_gid_t gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 compat_mode_t file_mode;
565 compat_mode_t dir_mode;
566};
567
568struct compat_ncp_mount_data_v4 {
569 compat_int_t version;
570 compat_ulong_t flags;
571 compat_ulong_t mounted_uid;
572 compat_long_t wdog_pid;
573 compat_uint_t ncp_fd;
574 compat_uint_t time_out;
575 compat_uint_t retry_count;
576 compat_ulong_t uid;
577 compat_ulong_t gid;
578 compat_ulong_t file_mode;
579 compat_ulong_t dir_mode;
580};
581
582static void *do_ncp_super_data_conv(void *raw_data)
583{
584 int version = *(unsigned int *)raw_data;
585
586 if (version == 3) {
587 struct compat_ncp_mount_data *c_n = raw_data;
588 struct ncp_mount_data *n = raw_data;
589
590 n->dir_mode = c_n->dir_mode;
591 n->file_mode = c_n->file_mode;
592 n->gid = c_n->gid;
593 n->uid = c_n->uid;
594 memmove (n->mounted_vol, c_n->mounted_vol, (sizeof (c_n->mounted_vol) + 3 * sizeof (unsigned int)));
595 n->wdog_pid = c_n->wdog_pid;
596 n->mounted_uid = c_n->mounted_uid;
597 } else if (version == 4) {
598 struct compat_ncp_mount_data_v4 *c_n = raw_data;
599 struct ncp_mount_data_v4 *n = raw_data;
600
601 n->dir_mode = c_n->dir_mode;
602 n->file_mode = c_n->file_mode;
603 n->gid = c_n->gid;
604 n->uid = c_n->uid;
605 n->retry_count = c_n->retry_count;
606 n->time_out = c_n->time_out;
607 n->ncp_fd = c_n->ncp_fd;
608 n->wdog_pid = c_n->wdog_pid;
609 n->mounted_uid = c_n->mounted_uid;
610 n->flags = c_n->flags;
611 } else if (version != 5) {
612 return NULL;
613 }
614
615 return raw_data;
616}
617
618struct compat_smb_mount_data {
619 compat_int_t version;
Stephen Rothwell202e5972005-09-06 15:16:40 -0700620 __compat_uid_t mounted_uid;
621 __compat_uid_t uid;
622 __compat_gid_t gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 compat_mode_t file_mode;
624 compat_mode_t dir_mode;
625};
626
627static void *do_smb_super_data_conv(void *raw_data)
628{
629 struct smb_mount_data *s = raw_data;
630 struct compat_smb_mount_data *c_s = raw_data;
631
632 if (c_s->version != SMB_MOUNT_OLDVERSION)
633 goto out;
634 s->dir_mode = c_s->dir_mode;
635 s->file_mode = c_s->file_mode;
636 s->gid = c_s->gid;
637 s->uid = c_s->uid;
638 s->mounted_uid = c_s->mounted_uid;
639 out:
640 return raw_data;
641}
642
David Howells9a9947b2005-04-18 10:54:51 -0700643struct compat_nfs_string {
644 compat_uint_t len;
David Howells5fc3e622005-04-27 15:39:03 -0700645 compat_uptr_t data;
David Howells9a9947b2005-04-18 10:54:51 -0700646};
647
648static inline void compat_nfs_string(struct nfs_string *dst,
649 struct compat_nfs_string *src)
650{
651 dst->data = compat_ptr(src->data);
652 dst->len = src->len;
653}
654
655struct compat_nfs4_mount_data_v1 {
656 compat_int_t version;
657 compat_int_t flags;
658 compat_int_t rsize;
659 compat_int_t wsize;
660 compat_int_t timeo;
661 compat_int_t retrans;
662 compat_int_t acregmin;
663 compat_int_t acregmax;
664 compat_int_t acdirmin;
665 compat_int_t acdirmax;
666 struct compat_nfs_string client_addr;
667 struct compat_nfs_string mnt_path;
668 struct compat_nfs_string hostname;
669 compat_uint_t host_addrlen;
David Howells5fc3e622005-04-27 15:39:03 -0700670 compat_uptr_t host_addr;
David Howells9a9947b2005-04-18 10:54:51 -0700671 compat_int_t proto;
672 compat_int_t auth_flavourlen;
David Howells5fc3e622005-04-27 15:39:03 -0700673 compat_uptr_t auth_flavours;
David Howells9a9947b2005-04-18 10:54:51 -0700674};
675
676static int do_nfs4_super_data_conv(void *raw_data)
677{
678 int version = *(compat_uint_t *) raw_data;
679
680 if (version == 1) {
681 struct compat_nfs4_mount_data_v1 *raw = raw_data;
682 struct nfs4_mount_data *real = raw_data;
683
684 /* copy the fields backwards */
685 real->auth_flavours = compat_ptr(raw->auth_flavours);
686 real->auth_flavourlen = raw->auth_flavourlen;
687 real->proto = raw->proto;
688 real->host_addr = compat_ptr(raw->host_addr);
689 real->host_addrlen = raw->host_addrlen;
690 compat_nfs_string(&real->hostname, &raw->hostname);
691 compat_nfs_string(&real->mnt_path, &raw->mnt_path);
692 compat_nfs_string(&real->client_addr, &raw->client_addr);
693 real->acdirmax = raw->acdirmax;
694 real->acdirmin = raw->acdirmin;
695 real->acregmax = raw->acregmax;
696 real->acregmin = raw->acregmin;
697 real->retrans = raw->retrans;
698 real->timeo = raw->timeo;
699 real->wsize = raw->wsize;
700 real->rsize = raw->rsize;
701 real->flags = raw->flags;
702 real->version = raw->version;
703 }
704 else {
705 return -EINVAL;
706 }
707
708 return 0;
709}
710
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711#define SMBFS_NAME "smbfs"
712#define NCPFS_NAME "ncpfs"
David Howells9a9947b2005-04-18 10:54:51 -0700713#define NFS4_NAME "nfs4"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714
715asmlinkage long compat_sys_mount(char __user * dev_name, char __user * dir_name,
716 char __user * type, unsigned long flags,
717 void __user * data)
718{
719 unsigned long type_page;
720 unsigned long data_page;
721 unsigned long dev_page;
722 char *dir_page;
723 int retval;
724
725 retval = copy_mount_options (type, &type_page);
726 if (retval < 0)
727 goto out;
728
729 dir_page = getname(dir_name);
730 retval = PTR_ERR(dir_page);
731 if (IS_ERR(dir_page))
732 goto out1;
733
734 retval = copy_mount_options (dev_name, &dev_page);
735 if (retval < 0)
736 goto out2;
737
738 retval = copy_mount_options (data, &data_page);
739 if (retval < 0)
740 goto out3;
741
742 retval = -EINVAL;
743
Andrey Mirkin822191a2006-12-06 20:31:35 -0800744 if (type_page && data_page) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 if (!strcmp((char *)type_page, SMBFS_NAME)) {
746 do_smb_super_data_conv((void *)data_page);
747 } else if (!strcmp((char *)type_page, NCPFS_NAME)) {
748 do_ncp_super_data_conv((void *)data_page);
David Howells9a9947b2005-04-18 10:54:51 -0700749 } else if (!strcmp((char *)type_page, NFS4_NAME)) {
750 if (do_nfs4_super_data_conv((void *) data_page))
751 goto out4;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 }
753 }
754
755 lock_kernel();
756 retval = do_mount((char*)dev_page, dir_page, (char*)type_page,
757 flags, (void*)data_page);
758 unlock_kernel();
759
David Howells9a9947b2005-04-18 10:54:51 -0700760 out4:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 free_page(data_page);
762 out3:
763 free_page(dev_page);
764 out2:
765 putname(dir_page);
766 out1:
767 free_page(type_page);
768 out:
769 return retval;
770}
771
772#define NAME_OFFSET(de) ((int) ((de)->d_name - (char __user *) (de)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773
774struct compat_old_linux_dirent {
775 compat_ulong_t d_ino;
776 compat_ulong_t d_offset;
777 unsigned short d_namlen;
778 char d_name[1];
779};
780
781struct compat_readdir_callback {
782 struct compat_old_linux_dirent __user *dirent;
783 int result;
784};
785
786static int compat_fillonedir(void *__buf, const char *name, int namlen,
David Howellsafefdbb2006-10-03 01:13:46 -0700787 loff_t offset, u64 ino, unsigned int d_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788{
789 struct compat_readdir_callback *buf = __buf;
790 struct compat_old_linux_dirent __user *dirent;
David Howellsafefdbb2006-10-03 01:13:46 -0700791 compat_ulong_t d_ino;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792
793 if (buf->result)
794 return -EINVAL;
David Howellsafefdbb2006-10-03 01:13:46 -0700795 d_ino = ino;
796 if (sizeof(d_ino) < sizeof(ino) && d_ino != ino)
797 return -EOVERFLOW;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 buf->result++;
799 dirent = buf->dirent;
800 if (!access_ok(VERIFY_WRITE, dirent,
801 (unsigned long)(dirent->d_name + namlen + 1) -
802 (unsigned long)dirent))
803 goto efault;
David Howellsafefdbb2006-10-03 01:13:46 -0700804 if ( __put_user(d_ino, &dirent->d_ino) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 __put_user(offset, &dirent->d_offset) ||
806 __put_user(namlen, &dirent->d_namlen) ||
807 __copy_to_user(dirent->d_name, name, namlen) ||
808 __put_user(0, dirent->d_name + namlen))
809 goto efault;
810 return 0;
811efault:
812 buf->result = -EFAULT;
813 return -EFAULT;
814}
815
816asmlinkage long compat_sys_old_readdir(unsigned int fd,
817 struct compat_old_linux_dirent __user *dirent, unsigned int count)
818{
819 int error;
820 struct file *file;
821 struct compat_readdir_callback buf;
822
823 error = -EBADF;
824 file = fget(fd);
825 if (!file)
826 goto out;
827
828 buf.result = 0;
829 buf.dirent = dirent;
830
831 error = vfs_readdir(file, compat_fillonedir, &buf);
832 if (error >= 0)
833 error = buf.result;
834
835 fput(file);
836out:
837 return error;
838}
839
840struct compat_linux_dirent {
841 compat_ulong_t d_ino;
842 compat_ulong_t d_off;
843 unsigned short d_reclen;
844 char d_name[1];
845};
846
847struct compat_getdents_callback {
848 struct compat_linux_dirent __user *current_dir;
849 struct compat_linux_dirent __user *previous;
850 int count;
851 int error;
852};
853
854static int compat_filldir(void *__buf, const char *name, int namlen,
David Howellsafefdbb2006-10-03 01:13:46 -0700855 loff_t offset, u64 ino, unsigned int d_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856{
857 struct compat_linux_dirent __user * dirent;
858 struct compat_getdents_callback *buf = __buf;
David Howellsafefdbb2006-10-03 01:13:46 -0700859 compat_ulong_t d_ino;
Milind Arun Choudhary022a1692007-05-08 00:29:02 -0700860 int reclen = ALIGN(NAME_OFFSET(dirent) + namlen + 2, sizeof(compat_long_t));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861
862 buf->error = -EINVAL; /* only used if we fail.. */
863 if (reclen > buf->count)
864 return -EINVAL;
David Howellsafefdbb2006-10-03 01:13:46 -0700865 d_ino = ino;
866 if (sizeof(d_ino) < sizeof(ino) && d_ino != ino)
867 return -EOVERFLOW;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 dirent = buf->previous;
869 if (dirent) {
870 if (__put_user(offset, &dirent->d_off))
871 goto efault;
872 }
873 dirent = buf->current_dir;
David Howellsafefdbb2006-10-03 01:13:46 -0700874 if (__put_user(d_ino, &dirent->d_ino))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875 goto efault;
876 if (__put_user(reclen, &dirent->d_reclen))
877 goto efault;
878 if (copy_to_user(dirent->d_name, name, namlen))
879 goto efault;
880 if (__put_user(0, dirent->d_name + namlen))
881 goto efault;
882 if (__put_user(d_type, (char __user *) dirent + reclen - 1))
883 goto efault;
884 buf->previous = dirent;
885 dirent = (void __user *)dirent + reclen;
886 buf->current_dir = dirent;
887 buf->count -= reclen;
888 return 0;
889efault:
890 buf->error = -EFAULT;
891 return -EFAULT;
892}
893
894asmlinkage long compat_sys_getdents(unsigned int fd,
895 struct compat_linux_dirent __user *dirent, unsigned int count)
896{
897 struct file * file;
898 struct compat_linux_dirent __user * lastdirent;
899 struct compat_getdents_callback buf;
900 int error;
901
902 error = -EFAULT;
903 if (!access_ok(VERIFY_WRITE, dirent, count))
904 goto out;
905
906 error = -EBADF;
907 file = fget(fd);
908 if (!file)
909 goto out;
910
911 buf.current_dir = dirent;
912 buf.previous = NULL;
913 buf.count = count;
914 buf.error = 0;
915
916 error = vfs_readdir(file, compat_filldir, &buf);
917 if (error < 0)
918 goto out_putf;
919 error = buf.error;
920 lastdirent = buf.previous;
921 if (lastdirent) {
922 if (put_user(file->f_pos, &lastdirent->d_off))
923 error = -EFAULT;
924 else
925 error = count - buf.count;
926 }
927
928out_putf:
929 fput(file);
930out:
931 return error;
932}
933
934#ifndef __ARCH_OMIT_COMPAT_SYS_GETDENTS64
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935
936struct compat_getdents_callback64 {
937 struct linux_dirent64 __user *current_dir;
938 struct linux_dirent64 __user *previous;
939 int count;
940 int error;
941};
942
943static int compat_filldir64(void * __buf, const char * name, int namlen, loff_t offset,
David Howellsafefdbb2006-10-03 01:13:46 -0700944 u64 ino, unsigned int d_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945{
946 struct linux_dirent64 __user *dirent;
947 struct compat_getdents_callback64 *buf = __buf;
948 int jj = NAME_OFFSET(dirent);
Milind Arun Choudhary022a1692007-05-08 00:29:02 -0700949 int reclen = ALIGN(jj + namlen + 1, sizeof(u64));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 u64 off;
951
952 buf->error = -EINVAL; /* only used if we fail.. */
953 if (reclen > buf->count)
954 return -EINVAL;
955 dirent = buf->previous;
956
957 if (dirent) {
958 if (__put_user_unaligned(offset, &dirent->d_off))
959 goto efault;
960 }
961 dirent = buf->current_dir;
962 if (__put_user_unaligned(ino, &dirent->d_ino))
963 goto efault;
964 off = 0;
965 if (__put_user_unaligned(off, &dirent->d_off))
966 goto efault;
967 if (__put_user(reclen, &dirent->d_reclen))
968 goto efault;
969 if (__put_user(d_type, &dirent->d_type))
970 goto efault;
971 if (copy_to_user(dirent->d_name, name, namlen))
972 goto efault;
973 if (__put_user(0, dirent->d_name + namlen))
974 goto efault;
975 buf->previous = dirent;
976 dirent = (void __user *)dirent + reclen;
977 buf->current_dir = dirent;
978 buf->count -= reclen;
979 return 0;
980efault:
981 buf->error = -EFAULT;
982 return -EFAULT;
983}
984
985asmlinkage long compat_sys_getdents64(unsigned int fd,
986 struct linux_dirent64 __user * dirent, unsigned int count)
987{
988 struct file * file;
989 struct linux_dirent64 __user * lastdirent;
990 struct compat_getdents_callback64 buf;
991 int error;
992
993 error = -EFAULT;
994 if (!access_ok(VERIFY_WRITE, dirent, count))
995 goto out;
996
997 error = -EBADF;
998 file = fget(fd);
999 if (!file)
1000 goto out;
1001
1002 buf.current_dir = dirent;
1003 buf.previous = NULL;
1004 buf.count = count;
1005 buf.error = 0;
1006
1007 error = vfs_readdir(file, compat_filldir64, &buf);
1008 if (error < 0)
1009 goto out_putf;
1010 error = buf.error;
1011 lastdirent = buf.previous;
1012 if (lastdirent) {
1013 typeof(lastdirent->d_off) d_off = file->f_pos;
Heiko Carstens7116e992006-12-06 20:36:36 -08001014 error = -EFAULT;
1015 if (__put_user_unaligned(d_off, &lastdirent->d_off))
1016 goto out_putf;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 error = count - buf.count;
1018 }
1019
1020out_putf:
1021 fput(file);
1022out:
1023 return error;
1024}
1025#endif /* ! __ARCH_OMIT_COMPAT_SYS_GETDENTS64 */
1026
1027static ssize_t compat_do_readv_writev(int type, struct file *file,
1028 const struct compat_iovec __user *uvector,
1029 unsigned long nr_segs, loff_t *pos)
1030{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 compat_ssize_t tot_len;
1032 struct iovec iovstack[UIO_FASTIOV];
1033 struct iovec *iov=iovstack, *vector;
1034 ssize_t ret;
1035 int seg;
1036 io_fn_t fn;
1037 iov_fn_t fnv;
1038
1039 /*
1040 * SuS says "The readv() function *may* fail if the iovcnt argument
1041 * was less than or equal to 0, or greater than {IOV_MAX}. Linux has
1042 * traditionally returned zero for zero segments, so...
1043 */
1044 ret = 0;
1045 if (nr_segs == 0)
1046 goto out;
1047
1048 /*
1049 * First get the "struct iovec" from user memory and
1050 * verify all the pointers
1051 */
1052 ret = -EINVAL;
1053 if ((nr_segs > UIO_MAXIOV) || (nr_segs <= 0))
1054 goto out;
1055 if (!file->f_op)
1056 goto out;
1057 if (nr_segs > UIO_FASTIOV) {
1058 ret = -ENOMEM;
1059 iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
1060 if (!iov)
1061 goto out;
1062 }
1063 ret = -EFAULT;
1064 if (!access_ok(VERIFY_READ, uvector, nr_segs*sizeof(*uvector)))
1065 goto out;
1066
1067 /*
1068 * Single unix specification:
1069 * We should -EINVAL if an element length is not >= 0 and fitting an
1070 * ssize_t. The total length is fitting an ssize_t
1071 *
1072 * Be careful here because iov_len is a size_t not an ssize_t
1073 */
1074 tot_len = 0;
1075 vector = iov;
1076 ret = -EINVAL;
1077 for (seg = 0 ; seg < nr_segs; seg++) {
1078 compat_ssize_t tmp = tot_len;
1079 compat_ssize_t len;
1080 compat_uptr_t buf;
1081
1082 if (__get_user(len, &uvector->iov_len) ||
1083 __get_user(buf, &uvector->iov_base)) {
1084 ret = -EFAULT;
1085 goto out;
1086 }
1087 if (len < 0) /* size_t not fitting an compat_ssize_t .. */
1088 goto out;
1089 tot_len += len;
1090 if (tot_len < tmp) /* maths overflow on the compat_ssize_t */
1091 goto out;
1092 vector->iov_base = compat_ptr(buf);
1093 vector->iov_len = (compat_size_t) len;
1094 uvector++;
1095 vector++;
1096 }
1097 if (tot_len == 0) {
1098 ret = 0;
1099 goto out;
1100 }
1101
1102 ret = rw_verify_area(type, file, pos, tot_len);
Linus Torvaldse28cc712006-01-04 16:20:40 -08001103 if (ret < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 goto out;
1105
James Morrise7edf9c2006-04-26 02:45:03 -04001106 ret = security_file_permission(file, type == READ ? MAY_READ:MAY_WRITE);
1107 if (ret)
1108 goto out;
1109
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 fnv = NULL;
1111 if (type == READ) {
1112 fn = file->f_op->read;
Badari Pulavartyee0b3e62006-09-30 23:28:47 -07001113 fnv = file->f_op->aio_read;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 } else {
1115 fn = (io_fn_t)file->f_op->write;
Badari Pulavartyee0b3e62006-09-30 23:28:47 -07001116 fnv = file->f_op->aio_write;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 }
1118
Badari Pulavartyee0b3e62006-09-30 23:28:47 -07001119 if (fnv)
1120 ret = do_sync_readv_writev(file, iov, nr_segs, tot_len,
1121 pos, fnv);
1122 else
1123 ret = do_loop_readv_writev(file, iov, nr_segs, pos, fn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125out:
1126 if (iov != iovstack)
1127 kfree(iov);
Robert Love0eeca282005-07-12 17:06:03 -04001128 if ((ret + (type == READ)) > 0) {
Josef "Jeff" Sipek0f7fc9e2006-12-08 02:36:35 -08001129 struct dentry *dentry = file->f_path.dentry;
Robert Love0eeca282005-07-12 17:06:03 -04001130 if (type == READ)
1131 fsnotify_access(dentry);
1132 else
1133 fsnotify_modify(dentry);
1134 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 return ret;
1136}
1137
1138asmlinkage ssize_t
1139compat_sys_readv(unsigned long fd, const struct compat_iovec __user *vec, unsigned long vlen)
1140{
1141 struct file *file;
1142 ssize_t ret = -EBADF;
1143
1144 file = fget(fd);
1145 if (!file)
1146 return -EBADF;
1147
1148 if (!(file->f_mode & FMODE_READ))
1149 goto out;
1150
1151 ret = -EINVAL;
Badari Pulavartyee0b3e62006-09-30 23:28:47 -07001152 if (!file->f_op || (!file->f_op->aio_read && !file->f_op->read))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 goto out;
1154
1155 ret = compat_do_readv_writev(READ, file, vec, vlen, &file->f_pos);
1156
1157out:
1158 fput(file);
1159 return ret;
1160}
1161
1162asmlinkage ssize_t
1163compat_sys_writev(unsigned long fd, const struct compat_iovec __user *vec, unsigned long vlen)
1164{
1165 struct file *file;
1166 ssize_t ret = -EBADF;
1167
1168 file = fget(fd);
1169 if (!file)
1170 return -EBADF;
1171 if (!(file->f_mode & FMODE_WRITE))
1172 goto out;
1173
1174 ret = -EINVAL;
Badari Pulavartyee0b3e62006-09-30 23:28:47 -07001175 if (!file->f_op || (!file->f_op->aio_write && !file->f_op->write))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176 goto out;
1177
1178 ret = compat_do_readv_writev(WRITE, file, vec, vlen, &file->f_pos);
1179
1180out:
1181 fput(file);
1182 return ret;
1183}
1184
Andi Kleend2610202006-05-01 12:15:48 -07001185asmlinkage long
1186compat_sys_vmsplice(int fd, const struct compat_iovec __user *iov32,
1187 unsigned int nr_segs, unsigned int flags)
1188{
1189 unsigned i;
Al Viro90cbad62006-10-10 22:44:17 +01001190 struct iovec __user *iov;
Jens Axboe98232d52006-05-04 09:13:49 +02001191 if (nr_segs > UIO_MAXIOV)
Andi Kleend2610202006-05-01 12:15:48 -07001192 return -EINVAL;
1193 iov = compat_alloc_user_space(nr_segs * sizeof(struct iovec));
1194 for (i = 0; i < nr_segs; i++) {
1195 struct compat_iovec v;
1196 if (get_user(v.iov_base, &iov32[i].iov_base) ||
1197 get_user(v.iov_len, &iov32[i].iov_len) ||
1198 put_user(compat_ptr(v.iov_base), &iov[i].iov_base) ||
1199 put_user(v.iov_len, &iov[i].iov_len))
1200 return -EFAULT;
1201 }
1202 return sys_vmsplice(fd, iov, nr_segs, flags);
1203}
1204
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205/*
Miklos Szeredie922efc2005-09-06 15:18:25 -07001206 * Exactly like fs/open.c:sys_open(), except that it doesn't set the
1207 * O_LARGEFILE flag.
1208 */
1209asmlinkage long
1210compat_sys_open(const char __user *filename, int flags, int mode)
1211{
Ulrich Drepper5590ff02006-01-18 17:43:53 -08001212 return do_sys_open(AT_FDCWD, filename, flags, mode);
1213}
1214
1215/*
1216 * Exactly like fs/open.c:sys_openat(), except that it doesn't set the
1217 * O_LARGEFILE flag.
1218 */
1219asmlinkage long
Stephen Rothwell9ad11ab2006-02-02 16:11:51 +11001220compat_sys_openat(unsigned int dfd, const char __user *filename, int flags, int mode)
Ulrich Drepper5590ff02006-01-18 17:43:53 -08001221{
1222 return do_sys_open(dfd, filename, flags, mode);
Miklos Szeredie922efc2005-09-06 15:18:25 -07001223}
1224
1225/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226 * compat_count() counts the number of arguments/envelopes. It is basically
1227 * a copy of count() from fs/exec.c, except that it works with 32 bit argv
1228 * and envp pointers.
1229 */
1230static int compat_count(compat_uptr_t __user *argv, int max)
1231{
1232 int i = 0;
1233
1234 if (argv != NULL) {
1235 for (;;) {
1236 compat_uptr_t p;
1237
1238 if (get_user(p, argv))
1239 return -EFAULT;
1240 if (!p)
1241 break;
1242 argv++;
1243 if(++i > max)
1244 return -E2BIG;
1245 }
1246 }
1247 return i;
1248}
1249
1250/*
1251 * compat_copy_strings() is basically a copy of copy_strings() from fs/exec.c
1252 * except that it works with 32 bit argv and envp pointers.
1253 */
1254static int compat_copy_strings(int argc, compat_uptr_t __user *argv,
1255 struct linux_binprm *bprm)
1256{
1257 struct page *kmapped_page = NULL;
1258 char *kaddr = NULL;
1259 int ret;
1260
1261 while (argc-- > 0) {
1262 compat_uptr_t str;
1263 int len;
1264 unsigned long pos;
1265
1266 if (get_user(str, argv+argc) ||
1267 !(len = strnlen_user(compat_ptr(str), bprm->p))) {
1268 ret = -EFAULT;
1269 goto out;
1270 }
1271
1272 if (bprm->p < len) {
1273 ret = -E2BIG;
1274 goto out;
1275 }
1276
1277 bprm->p -= len;
1278 /* XXX: add architecture specific overflow check here. */
1279 pos = bprm->p;
1280
1281 while (len > 0) {
1282 int i, new, err;
1283 int offset, bytes_to_copy;
1284 struct page *page;
1285
1286 offset = pos % PAGE_SIZE;
1287 i = pos/PAGE_SIZE;
1288 page = bprm->page[i];
1289 new = 0;
1290 if (!page) {
1291 page = alloc_page(GFP_HIGHUSER);
1292 bprm->page[i] = page;
1293 if (!page) {
1294 ret = -ENOMEM;
1295 goto out;
1296 }
1297 new = 1;
1298 }
1299
1300 if (page != kmapped_page) {
1301 if (kmapped_page)
1302 kunmap(kmapped_page);
1303 kmapped_page = page;
1304 kaddr = kmap(kmapped_page);
1305 }
1306 if (new && offset)
1307 memset(kaddr, 0, offset);
1308 bytes_to_copy = PAGE_SIZE - offset;
1309 if (bytes_to_copy > len) {
1310 bytes_to_copy = len;
1311 if (new)
1312 memset(kaddr+offset+len, 0,
1313 PAGE_SIZE-offset-len);
1314 }
1315 err = copy_from_user(kaddr+offset, compat_ptr(str),
1316 bytes_to_copy);
1317 if (err) {
1318 ret = -EFAULT;
1319 goto out;
1320 }
1321
1322 pos += bytes_to_copy;
1323 str += bytes_to_copy;
1324 len -= bytes_to_copy;
1325 }
1326 }
1327 ret = 0;
1328out:
1329 if (kmapped_page)
1330 kunmap(kmapped_page);
1331 return ret;
1332}
1333
1334#ifdef CONFIG_MMU
1335
1336#define free_arg_pages(bprm) do { } while (0)
1337
1338#else
1339
1340static inline void free_arg_pages(struct linux_binprm *bprm)
1341{
1342 int i;
1343
1344 for (i = 0; i < MAX_ARG_PAGES; i++) {
1345 if (bprm->page[i])
1346 __free_page(bprm->page[i]);
1347 bprm->page[i] = NULL;
1348 }
1349}
1350
1351#endif /* CONFIG_MMU */
1352
1353/*
1354 * compat_do_execve() is mostly a copy of do_execve(), with the exception
1355 * that it processes 32 bit argv and envp pointers.
1356 */
1357int compat_do_execve(char * filename,
1358 compat_uptr_t __user *argv,
1359 compat_uptr_t __user *envp,
1360 struct pt_regs * regs)
1361{
1362 struct linux_binprm *bprm;
1363 struct file *file;
1364 int retval;
1365 int i;
1366
1367 retval = -ENOMEM;
Oliver Neukum11b0b5a2006-03-25 03:08:13 -08001368 bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 if (!bprm)
1370 goto out_ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371
1372 file = open_exec(filename);
1373 retval = PTR_ERR(file);
1374 if (IS_ERR(file))
1375 goto out_kfree;
1376
1377 sched_exec();
1378
1379 bprm->p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
1380 bprm->file = file;
1381 bprm->filename = filename;
1382 bprm->interp = filename;
1383 bprm->mm = mm_alloc();
1384 retval = -ENOMEM;
1385 if (!bprm->mm)
1386 goto out_file;
1387
1388 retval = init_new_context(current, bprm->mm);
1389 if (retval < 0)
1390 goto out_mm;
1391
1392 bprm->argc = compat_count(argv, bprm->p / sizeof(compat_uptr_t));
1393 if ((retval = bprm->argc) < 0)
1394 goto out_mm;
1395
1396 bprm->envc = compat_count(envp, bprm->p / sizeof(compat_uptr_t));
1397 if ((retval = bprm->envc) < 0)
1398 goto out_mm;
1399
1400 retval = security_bprm_alloc(bprm);
1401 if (retval)
1402 goto out;
1403
1404 retval = prepare_binprm(bprm);
1405 if (retval < 0)
1406 goto out;
1407
1408 retval = copy_strings_kernel(1, &bprm->filename, bprm);
1409 if (retval < 0)
1410 goto out;
1411
1412 bprm->exec = bprm->p;
1413 retval = compat_copy_strings(bprm->envc, envp, bprm);
1414 if (retval < 0)
1415 goto out;
1416
1417 retval = compat_copy_strings(bprm->argc, argv, bprm);
1418 if (retval < 0)
1419 goto out;
1420
1421 retval = search_binary_handler(bprm, regs);
1422 if (retval >= 0) {
1423 free_arg_pages(bprm);
1424
1425 /* execve success */
1426 security_bprm_free(bprm);
David S. Miller4a805e82005-09-14 21:40:00 -07001427 acct_update_integrals(current);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001428 kfree(bprm);
1429 return retval;
1430 }
1431
1432out:
1433 /* Something went wrong, return the inode and free the argument pages*/
1434 for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
1435 struct page * page = bprm->page[i];
1436 if (page)
1437 __free_page(page);
1438 }
1439
1440 if (bprm->security)
1441 security_bprm_free(bprm);
1442
1443out_mm:
1444 if (bprm->mm)
1445 mmdrop(bprm->mm);
1446
1447out_file:
1448 if (bprm->file) {
1449 allow_write_access(bprm->file);
1450 fput(bprm->file);
1451 }
1452
1453out_kfree:
1454 kfree(bprm);
1455
1456out_ret:
1457 return retval;
1458}
1459
1460#define __COMPAT_NFDBITS (8 * sizeof(compat_ulong_t))
1461
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462/*
1463 * Ooo, nasty. We need here to frob 32-bit unsigned longs to
1464 * 64-bit unsigned longs.
1465 */
Arjan van de Ven858119e2006-01-14 13:20:43 -08001466static
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467int compat_get_fd_set(unsigned long nr, compat_ulong_t __user *ufdset,
1468 unsigned long *fdset)
1469{
Milind Arun Choudhary022a1692007-05-08 00:29:02 -07001470 nr = DIV_ROUND_UP(nr, __COMPAT_NFDBITS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471 if (ufdset) {
1472 unsigned long odd;
1473
1474 if (!access_ok(VERIFY_WRITE, ufdset, nr*sizeof(compat_ulong_t)))
1475 return -EFAULT;
1476
1477 odd = nr & 1UL;
1478 nr &= ~1UL;
1479 while (nr) {
1480 unsigned long h, l;
Heiko Carstens7116e992006-12-06 20:36:36 -08001481 if (__get_user(l, ufdset) || __get_user(h, ufdset+1))
1482 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483 ufdset += 2;
1484 *fdset++ = h << 32 | l;
1485 nr -= 2;
1486 }
Heiko Carstens7116e992006-12-06 20:36:36 -08001487 if (odd && __get_user(*fdset, ufdset))
1488 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 } else {
1490 /* Tricky, must clear full unsigned long in the
1491 * kernel fdset at the end, this makes sure that
1492 * actually happens.
1493 */
1494 memset(fdset, 0, ((nr + 1) & ~1)*sizeof(compat_ulong_t));
1495 }
1496 return 0;
1497}
1498
Arjan van de Ven858119e2006-01-14 13:20:43 -08001499static
Heiko Carstens7116e992006-12-06 20:36:36 -08001500int compat_set_fd_set(unsigned long nr, compat_ulong_t __user *ufdset,
1501 unsigned long *fdset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502{
1503 unsigned long odd;
Milind Arun Choudhary022a1692007-05-08 00:29:02 -07001504 nr = DIV_ROUND_UP(nr, __COMPAT_NFDBITS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505
1506 if (!ufdset)
Heiko Carstens7116e992006-12-06 20:36:36 -08001507 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508
1509 odd = nr & 1UL;
1510 nr &= ~1UL;
1511 while (nr) {
1512 unsigned long h, l;
1513 l = *fdset++;
1514 h = l >> 32;
Heiko Carstens7116e992006-12-06 20:36:36 -08001515 if (__put_user(l, ufdset) || __put_user(h, ufdset+1))
1516 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 ufdset += 2;
1518 nr -= 2;
1519 }
Heiko Carstens7116e992006-12-06 20:36:36 -08001520 if (odd && __put_user(*fdset, ufdset))
1521 return -EFAULT;
1522 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523}
1524
1525
1526/*
1527 * This is a virtual copy of sys_select from fs/select.c and probably
1528 * should be compared to it from time to time
1529 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530
1531/*
1532 * We can actually return ERESTARTSYS instead of EINTR, but I'd
1533 * like to be certain this leads to no problems. So I return
1534 * EINTR just for safety.
1535 *
1536 * Update: ERESTARTSYS breaks at least the xview clock binary, so
1537 * I'm trying ERESTARTNOHAND which restart only when you want to.
1538 */
1539#define MAX_SELECT_SECONDS \
1540 ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1)
1541
David Woodhouse9f729492006-01-18 17:44:05 -08001542int compat_core_sys_select(int n, compat_ulong_t __user *inp,
1543 compat_ulong_t __user *outp, compat_ulong_t __user *exp, s64 *timeout)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544{
1545 fd_set_bits fds;
1546 char *bits;
Vadim Lobanovbbea9f62006-12-10 02:21:12 -08001547 int size, max_fds, ret = -EINVAL;
Linus Torvaldsa4531ed2005-09-09 15:10:52 -07001548 struct fdtable *fdt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550 if (n < 0)
1551 goto out_nofds;
1552
Vadim Lobanovbbea9f62006-12-10 02:21:12 -08001553 /* max_fds can increase, so grab it once to avoid race */
Linus Torvaldsac5b8b62005-09-09 15:42:34 -07001554 rcu_read_lock();
Linus Torvaldsa4531ed2005-09-09 15:10:52 -07001555 fdt = files_fdtable(current->files);
Vadim Lobanovbbea9f62006-12-10 02:21:12 -08001556 max_fds = fdt->max_fds;
Linus Torvaldsac5b8b62005-09-09 15:42:34 -07001557 rcu_read_unlock();
Vadim Lobanovbbea9f62006-12-10 02:21:12 -08001558 if (n > max_fds)
1559 n = max_fds;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560
1561 /*
1562 * We need 6 bitmaps (in/out/ex for both incoming and outgoing),
1563 * since we used fdset we need to allocate memory in units of
1564 * long-words.
1565 */
1566 ret = -ENOMEM;
1567 size = FDS_BYTES(n);
Vadim Lobanov68c34312006-03-28 01:56:35 -08001568 bits = kmalloc(6 * size, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 if (!bits)
1570 goto out_nofds;
1571 fds.in = (unsigned long *) bits;
1572 fds.out = (unsigned long *) (bits + size);
1573 fds.ex = (unsigned long *) (bits + 2*size);
1574 fds.res_in = (unsigned long *) (bits + 3*size);
1575 fds.res_out = (unsigned long *) (bits + 4*size);
1576 fds.res_ex = (unsigned long *) (bits + 5*size);
1577
1578 if ((ret = compat_get_fd_set(n, inp, fds.in)) ||
1579 (ret = compat_get_fd_set(n, outp, fds.out)) ||
1580 (ret = compat_get_fd_set(n, exp, fds.ex)))
1581 goto out;
1582 zero_fd_set(n, fds.res_in);
1583 zero_fd_set(n, fds.res_out);
1584 zero_fd_set(n, fds.res_ex);
1585
David Woodhouse9f729492006-01-18 17:44:05 -08001586 ret = do_select(n, &fds, timeout);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587
1588 if (ret < 0)
1589 goto out;
1590 if (!ret) {
1591 ret = -ERESTARTNOHAND;
1592 if (signal_pending(current))
1593 goto out;
1594 ret = 0;
1595 }
1596
Heiko Carstens7116e992006-12-06 20:36:36 -08001597 if (compat_set_fd_set(n, inp, fds.res_in) ||
1598 compat_set_fd_set(n, outp, fds.res_out) ||
1599 compat_set_fd_set(n, exp, fds.res_ex))
1600 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601out:
Vadim Lobanov68c34312006-03-28 01:56:35 -08001602 kfree(bits);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001603out_nofds:
1604 return ret;
1605}
1606
David Woodhouse9f729492006-01-18 17:44:05 -08001607asmlinkage long compat_sys_select(int n, compat_ulong_t __user *inp,
1608 compat_ulong_t __user *outp, compat_ulong_t __user *exp,
1609 struct compat_timeval __user *tvp)
1610{
1611 s64 timeout = -1;
1612 struct compat_timeval tv;
1613 int ret;
1614
1615 if (tvp) {
1616 if (copy_from_user(&tv, tvp, sizeof(tv)))
1617 return -EFAULT;
1618
1619 if (tv.tv_sec < 0 || tv.tv_usec < 0)
1620 return -EINVAL;
1621
1622 /* Cast to u64 to make GCC stop complaining */
1623 if ((u64)tv.tv_sec >= (u64)MAX_INT64_SECONDS)
1624 timeout = -1; /* infinite */
1625 else {
Milind Arun Choudhary022a1692007-05-08 00:29:02 -07001626 timeout = DIV_ROUND_UP(tv.tv_usec, 1000000/HZ);
David Woodhouse9f729492006-01-18 17:44:05 -08001627 timeout += tv.tv_sec * HZ;
1628 }
1629 }
1630
1631 ret = compat_core_sys_select(n, inp, outp, exp, &timeout);
1632
1633 if (tvp) {
Andrew Morton643a6542006-02-11 17:55:52 -08001634 struct compat_timeval rtv;
1635
David Woodhouse9f729492006-01-18 17:44:05 -08001636 if (current->personality & STICKY_TIMEOUTS)
1637 goto sticky;
Andrew Morton643a6542006-02-11 17:55:52 -08001638 rtv.tv_usec = jiffies_to_usecs(do_div((*(u64*)&timeout), HZ));
1639 rtv.tv_sec = timeout;
Andrew Morton74910e62006-02-17 13:52:58 -08001640 if (compat_timeval_compare(&rtv, &tv) >= 0)
Andrew Morton643a6542006-02-11 17:55:52 -08001641 rtv = tv;
1642 if (copy_to_user(tvp, &rtv, sizeof(rtv))) {
David Woodhouse9f729492006-01-18 17:44:05 -08001643sticky:
1644 /*
1645 * If an application puts its timeval in read-only
1646 * memory, we don't want the Linux-specific update to
1647 * the timeval to cause a fault after the select has
1648 * completed successfully. However, because we're not
1649 * updating the timeval, we can't restart the system
1650 * call.
1651 */
1652 if (ret == -ERESTARTNOHAND)
1653 ret = -EINTR;
1654 }
1655 }
1656
1657 return ret;
1658}
1659
1660#ifdef TIF_RESTORE_SIGMASK
1661asmlinkage long compat_sys_pselect7(int n, compat_ulong_t __user *inp,
1662 compat_ulong_t __user *outp, compat_ulong_t __user *exp,
1663 struct compat_timespec __user *tsp, compat_sigset_t __user *sigmask,
1664 compat_size_t sigsetsize)
1665{
1666 compat_sigset_t ss32;
1667 sigset_t ksigmask, sigsaved;
Andrew Mortoncb82a6c2006-02-01 03:04:49 -08001668 s64 timeout = MAX_SCHEDULE_TIMEOUT;
David Woodhouse9f729492006-01-18 17:44:05 -08001669 struct compat_timespec ts;
1670 int ret;
1671
1672 if (tsp) {
1673 if (copy_from_user(&ts, tsp, sizeof(ts)))
1674 return -EFAULT;
1675
1676 if (ts.tv_sec < 0 || ts.tv_nsec < 0)
1677 return -EINVAL;
1678 }
1679
1680 if (sigmask) {
1681 if (sigsetsize != sizeof(compat_sigset_t))
1682 return -EINVAL;
1683 if (copy_from_user(&ss32, sigmask, sizeof(ss32)))
1684 return -EFAULT;
1685 sigset_from_compat(&ksigmask, &ss32);
1686
1687 sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP));
1688 sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
1689 }
1690
1691 do {
1692 if (tsp) {
1693 if ((unsigned long)ts.tv_sec < MAX_SELECT_SECONDS) {
Milind Arun Choudhary022a1692007-05-08 00:29:02 -07001694 timeout = DIV_ROUND_UP(ts.tv_nsec, 1000000000/HZ);
David Woodhouse9f729492006-01-18 17:44:05 -08001695 timeout += ts.tv_sec * (unsigned long)HZ;
1696 ts.tv_sec = 0;
1697 ts.tv_nsec = 0;
1698 } else {
1699 ts.tv_sec -= MAX_SELECT_SECONDS;
1700 timeout = MAX_SELECT_SECONDS * HZ;
1701 }
1702 }
1703
1704 ret = compat_core_sys_select(n, inp, outp, exp, &timeout);
1705
1706 } while (!ret && !timeout && tsp && (ts.tv_sec || ts.tv_nsec));
1707
Heiko Carstens87c2b7c2006-11-02 22:06:58 -08001708 if (tsp) {
Andrew Morton643a6542006-02-11 17:55:52 -08001709 struct compat_timespec rts;
1710
Heiko Carstens87c2b7c2006-11-02 22:06:58 -08001711 if (current->personality & STICKY_TIMEOUTS)
1712 goto sticky;
1713
Andrew Morton643a6542006-02-11 17:55:52 -08001714 rts.tv_sec = timeout / HZ;
1715 rts.tv_nsec = (timeout % HZ) * (NSEC_PER_SEC/HZ);
1716 if (rts.tv_nsec >= NSEC_PER_SEC) {
1717 rts.tv_sec++;
1718 rts.tv_nsec -= NSEC_PER_SEC;
David Woodhouse9f729492006-01-18 17:44:05 -08001719 }
Andrew Morton74910e62006-02-17 13:52:58 -08001720 if (compat_timespec_compare(&rts, &ts) >= 0)
Andrew Morton643a6542006-02-11 17:55:52 -08001721 rts = ts;
Heiko Carstens87c2b7c2006-11-02 22:06:58 -08001722 if (copy_to_user(tsp, &rts, sizeof(rts))) {
1723sticky:
1724 /*
1725 * If an application puts its timeval in read-only
1726 * memory, we don't want the Linux-specific update to
1727 * the timeval to cause a fault after the select has
1728 * completed successfully. However, because we're not
1729 * updating the timeval, we can't restart the system
1730 * call.
1731 */
1732 if (ret == -ERESTARTNOHAND)
1733 ret = -EINTR;
1734 }
David Woodhouse9f729492006-01-18 17:44:05 -08001735 }
1736
1737 if (ret == -ERESTARTNOHAND) {
1738 /*
1739 * Don't restore the signal mask yet. Let do_signal() deliver
1740 * the signal on the way back to userspace, before the signal
1741 * mask is restored.
1742 */
1743 if (sigmask) {
1744 memcpy(&current->saved_sigmask, &sigsaved,
1745 sizeof(sigsaved));
1746 set_thread_flag(TIF_RESTORE_SIGMASK);
1747 }
1748 } else if (sigmask)
1749 sigprocmask(SIG_SETMASK, &sigsaved, NULL);
1750
1751 return ret;
1752}
1753
1754asmlinkage long compat_sys_pselect6(int n, compat_ulong_t __user *inp,
1755 compat_ulong_t __user *outp, compat_ulong_t __user *exp,
1756 struct compat_timespec __user *tsp, void __user *sig)
1757{
1758 compat_size_t sigsetsize = 0;
1759 compat_uptr_t up = 0;
1760
1761 if (sig) {
1762 if (!access_ok(VERIFY_READ, sig,
1763 sizeof(compat_uptr_t)+sizeof(compat_size_t)) ||
1764 __get_user(up, (compat_uptr_t __user *)sig) ||
1765 __get_user(sigsetsize,
1766 (compat_size_t __user *)(sig+sizeof(up))))
1767 return -EFAULT;
1768 }
1769 return compat_sys_pselect7(n, inp, outp, exp, tsp, compat_ptr(up),
1770 sigsetsize);
1771}
1772
1773asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds,
1774 unsigned int nfds, struct compat_timespec __user *tsp,
1775 const compat_sigset_t __user *sigmask, compat_size_t sigsetsize)
1776{
1777 compat_sigset_t ss32;
1778 sigset_t ksigmask, sigsaved;
1779 struct compat_timespec ts;
1780 s64 timeout = -1;
1781 int ret;
1782
1783 if (tsp) {
1784 if (copy_from_user(&ts, tsp, sizeof(ts)))
1785 return -EFAULT;
1786
1787 /* We assume that ts.tv_sec is always lower than
1788 the number of seconds that can be expressed in
1789 an s64. Otherwise the compiler bitches at us */
Milind Arun Choudhary022a1692007-05-08 00:29:02 -07001790 timeout = DIV_ROUND_UP(ts.tv_nsec, 1000000000/HZ);
David Woodhouse9f729492006-01-18 17:44:05 -08001791 timeout += ts.tv_sec * HZ;
1792 }
1793
1794 if (sigmask) {
Alexey Dobriyan3835a9b2006-05-15 09:44:27 -07001795 if (sigsetsize != sizeof(compat_sigset_t))
David Woodhouse9f729492006-01-18 17:44:05 -08001796 return -EINVAL;
1797 if (copy_from_user(&ss32, sigmask, sizeof(ss32)))
1798 return -EFAULT;
1799 sigset_from_compat(&ksigmask, &ss32);
1800
1801 sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP));
1802 sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
1803 }
1804
1805 ret = do_sys_poll(ufds, nfds, &timeout);
1806
1807 /* We can restart this syscall, usually */
1808 if (ret == -EINTR) {
1809 /*
1810 * Don't restore the signal mask yet. Let do_signal() deliver
1811 * the signal on the way back to userspace, before the signal
1812 * mask is restored.
1813 */
1814 if (sigmask) {
1815 memcpy(&current->saved_sigmask, &sigsaved,
1816 sizeof(sigsaved));
1817 set_thread_flag(TIF_RESTORE_SIGMASK);
1818 }
1819 ret = -ERESTARTNOHAND;
1820 } else if (sigmask)
1821 sigprocmask(SIG_SETMASK, &sigsaved, NULL);
1822
1823 if (tsp && timeout >= 0) {
Andrew Morton643a6542006-02-11 17:55:52 -08001824 struct compat_timespec rts;
1825
David Woodhouse9f729492006-01-18 17:44:05 -08001826 if (current->personality & STICKY_TIMEOUTS)
1827 goto sticky;
1828 /* Yes, we know it's actually an s64, but it's also positive. */
Andrew Morton643a6542006-02-11 17:55:52 -08001829 rts.tv_nsec = jiffies_to_usecs(do_div((*(u64*)&timeout), HZ)) *
1830 1000;
1831 rts.tv_sec = timeout;
Andrew Morton74910e62006-02-17 13:52:58 -08001832 if (compat_timespec_compare(&rts, &ts) >= 0)
Andrew Morton643a6542006-02-11 17:55:52 -08001833 rts = ts;
1834 if (copy_to_user(tsp, &rts, sizeof(rts))) {
David Woodhouse9f729492006-01-18 17:44:05 -08001835sticky:
1836 /*
1837 * If an application puts its timeval in read-only
1838 * memory, we don't want the Linux-specific update to
1839 * the timeval to cause a fault after the select has
1840 * completed successfully. However, because we're not
1841 * updating the timeval, we can't restart the system
1842 * call.
1843 */
1844 if (ret == -ERESTARTNOHAND && timeout >= 0)
1845 ret = -EINTR;
1846 }
1847 }
1848
1849 return ret;
1850}
1851#endif /* TIF_RESTORE_SIGMASK */
1852
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853#if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)
1854/* Stuff for NFS server syscalls... */
1855struct compat_nfsctl_svc {
1856 u16 svc32_port;
1857 s32 svc32_nthreads;
1858};
1859
1860struct compat_nfsctl_client {
1861 s8 cl32_ident[NFSCLNT_IDMAX+1];
1862 s32 cl32_naddr;
1863 struct in_addr cl32_addrlist[NFSCLNT_ADDRMAX];
1864 s32 cl32_fhkeytype;
1865 s32 cl32_fhkeylen;
1866 u8 cl32_fhkey[NFSCLNT_KEYMAX];
1867};
1868
1869struct compat_nfsctl_export {
1870 char ex32_client[NFSCLNT_IDMAX+1];
1871 char ex32_path[NFS_MAXPATHLEN+1];
1872 compat_dev_t ex32_dev;
1873 compat_ino_t ex32_ino;
1874 compat_int_t ex32_flags;
Stephen Rothwell202e5972005-09-06 15:16:40 -07001875 __compat_uid_t ex32_anon_uid;
1876 __compat_gid_t ex32_anon_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877};
1878
1879struct compat_nfsctl_fdparm {
1880 struct sockaddr gd32_addr;
1881 s8 gd32_path[NFS_MAXPATHLEN+1];
1882 compat_int_t gd32_version;
1883};
1884
1885struct compat_nfsctl_fsparm {
1886 struct sockaddr gd32_addr;
1887 s8 gd32_path[NFS_MAXPATHLEN+1];
1888 compat_int_t gd32_maxlen;
1889};
1890
1891struct compat_nfsctl_arg {
1892 compat_int_t ca32_version; /* safeguard */
1893 union {
1894 struct compat_nfsctl_svc u32_svc;
1895 struct compat_nfsctl_client u32_client;
1896 struct compat_nfsctl_export u32_export;
1897 struct compat_nfsctl_fdparm u32_getfd;
1898 struct compat_nfsctl_fsparm u32_getfs;
1899 } u;
1900#define ca32_svc u.u32_svc
1901#define ca32_client u.u32_client
1902#define ca32_export u.u32_export
1903#define ca32_getfd u.u32_getfd
1904#define ca32_getfs u.u32_getfs
1905};
1906
1907union compat_nfsctl_res {
1908 __u8 cr32_getfh[NFS_FHSIZE];
1909 struct knfsd_fh cr32_getfs;
1910};
1911
Lin Feng Shend64b1c82006-05-20 14:59:49 -07001912static int compat_nfs_svc_trans(struct nfsctl_arg *karg,
1913 struct compat_nfsctl_arg __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914{
Lin Feng Shend64b1c82006-05-20 14:59:49 -07001915 if (!access_ok(VERIFY_READ, &arg->ca32_svc, sizeof(arg->ca32_svc)) ||
1916 get_user(karg->ca_version, &arg->ca32_version) ||
1917 __get_user(karg->ca_svc.svc_port, &arg->ca32_svc.svc32_port) ||
1918 __get_user(karg->ca_svc.svc_nthreads,
1919 &arg->ca32_svc.svc32_nthreads))
1920 return -EFAULT;
1921 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922}
1923
Lin Feng Shend64b1c82006-05-20 14:59:49 -07001924static int compat_nfs_clnt_trans(struct nfsctl_arg *karg,
1925 struct compat_nfsctl_arg __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926{
Lin Feng Shend64b1c82006-05-20 14:59:49 -07001927 if (!access_ok(VERIFY_READ, &arg->ca32_client,
1928 sizeof(arg->ca32_client)) ||
1929 get_user(karg->ca_version, &arg->ca32_version) ||
1930 __copy_from_user(&karg->ca_client.cl_ident[0],
1931 &arg->ca32_client.cl32_ident[0],
1932 NFSCLNT_IDMAX) ||
1933 __get_user(karg->ca_client.cl_naddr,
1934 &arg->ca32_client.cl32_naddr) ||
1935 __copy_from_user(&karg->ca_client.cl_addrlist[0],
1936 &arg->ca32_client.cl32_addrlist[0],
1937 (sizeof(struct in_addr) * NFSCLNT_ADDRMAX)) ||
1938 __get_user(karg->ca_client.cl_fhkeytype,
1939 &arg->ca32_client.cl32_fhkeytype) ||
1940 __get_user(karg->ca_client.cl_fhkeylen,
1941 &arg->ca32_client.cl32_fhkeylen) ||
1942 __copy_from_user(&karg->ca_client.cl_fhkey[0],
1943 &arg->ca32_client.cl32_fhkey[0],
1944 NFSCLNT_KEYMAX))
1945 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946
Lin Feng Shend64b1c82006-05-20 14:59:49 -07001947 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948}
1949
Lin Feng Shend64b1c82006-05-20 14:59:49 -07001950static int compat_nfs_exp_trans(struct nfsctl_arg *karg,
1951 struct compat_nfsctl_arg __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952{
Lin Feng Shend64b1c82006-05-20 14:59:49 -07001953 if (!access_ok(VERIFY_READ, &arg->ca32_export,
1954 sizeof(arg->ca32_export)) ||
1955 get_user(karg->ca_version, &arg->ca32_version) ||
1956 __copy_from_user(&karg->ca_export.ex_client[0],
1957 &arg->ca32_export.ex32_client[0],
1958 NFSCLNT_IDMAX) ||
1959 __copy_from_user(&karg->ca_export.ex_path[0],
1960 &arg->ca32_export.ex32_path[0],
1961 NFS_MAXPATHLEN) ||
1962 __get_user(karg->ca_export.ex_dev,
1963 &arg->ca32_export.ex32_dev) ||
1964 __get_user(karg->ca_export.ex_ino,
1965 &arg->ca32_export.ex32_ino) ||
1966 __get_user(karg->ca_export.ex_flags,
1967 &arg->ca32_export.ex32_flags) ||
1968 __get_user(karg->ca_export.ex_anon_uid,
1969 &arg->ca32_export.ex32_anon_uid) ||
1970 __get_user(karg->ca_export.ex_anon_gid,
1971 &arg->ca32_export.ex32_anon_gid))
1972 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001973 SET_UID(karg->ca_export.ex_anon_uid, karg->ca_export.ex_anon_uid);
1974 SET_GID(karg->ca_export.ex_anon_gid, karg->ca_export.ex_anon_gid);
1975
Lin Feng Shend64b1c82006-05-20 14:59:49 -07001976 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977}
1978
Lin Feng Shend64b1c82006-05-20 14:59:49 -07001979static int compat_nfs_getfd_trans(struct nfsctl_arg *karg,
1980 struct compat_nfsctl_arg __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001981{
Lin Feng Shend64b1c82006-05-20 14:59:49 -07001982 if (!access_ok(VERIFY_READ, &arg->ca32_getfd,
1983 sizeof(arg->ca32_getfd)) ||
1984 get_user(karg->ca_version, &arg->ca32_version) ||
1985 __copy_from_user(&karg->ca_getfd.gd_addr,
1986 &arg->ca32_getfd.gd32_addr,
1987 (sizeof(struct sockaddr))) ||
1988 __copy_from_user(&karg->ca_getfd.gd_path,
1989 &arg->ca32_getfd.gd32_path,
1990 (NFS_MAXPATHLEN+1)) ||
1991 __get_user(karg->ca_getfd.gd_version,
1992 &arg->ca32_getfd.gd32_version))
1993 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994
Lin Feng Shend64b1c82006-05-20 14:59:49 -07001995 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996}
1997
Lin Feng Shend64b1c82006-05-20 14:59:49 -07001998static int compat_nfs_getfs_trans(struct nfsctl_arg *karg,
1999 struct compat_nfsctl_arg __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002000{
Lin Feng Shend64b1c82006-05-20 14:59:49 -07002001 if (!access_ok(VERIFY_READ,&arg->ca32_getfs,sizeof(arg->ca32_getfs)) ||
2002 get_user(karg->ca_version, &arg->ca32_version) ||
2003 __copy_from_user(&karg->ca_getfs.gd_addr,
2004 &arg->ca32_getfs.gd32_addr,
2005 (sizeof(struct sockaddr))) ||
2006 __copy_from_user(&karg->ca_getfs.gd_path,
2007 &arg->ca32_getfs.gd32_path,
2008 (NFS_MAXPATHLEN+1)) ||
2009 __get_user(karg->ca_getfs.gd_maxlen,
2010 &arg->ca32_getfs.gd32_maxlen))
2011 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012
Lin Feng Shend64b1c82006-05-20 14:59:49 -07002013 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014}
2015
2016/* This really doesn't need translations, we are only passing
2017 * back a union which contains opaque nfs file handle data.
2018 */
Lin Feng Shend64b1c82006-05-20 14:59:49 -07002019static int compat_nfs_getfh_res_trans(union nfsctl_res *kres,
2020 union compat_nfsctl_res __user *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021{
2022 int err;
2023
2024 err = copy_to_user(res, kres, sizeof(*res));
2025
2026 return (err) ? -EFAULT : 0;
2027}
2028
Lin Feng Shend64b1c82006-05-20 14:59:49 -07002029asmlinkage long compat_sys_nfsservctl(int cmd,
2030 struct compat_nfsctl_arg __user *arg,
2031 union compat_nfsctl_res __user *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032{
2033 struct nfsctl_arg *karg;
2034 union nfsctl_res *kres;
2035 mm_segment_t oldfs;
2036 int err;
2037
2038 karg = kmalloc(sizeof(*karg), GFP_USER);
2039 kres = kmalloc(sizeof(*kres), GFP_USER);
2040 if(!karg || !kres) {
2041 err = -ENOMEM;
2042 goto done;
2043 }
2044
2045 switch(cmd) {
2046 case NFSCTL_SVC:
2047 err = compat_nfs_svc_trans(karg, arg);
2048 break;
2049
2050 case NFSCTL_ADDCLIENT:
2051 err = compat_nfs_clnt_trans(karg, arg);
2052 break;
2053
2054 case NFSCTL_DELCLIENT:
2055 err = compat_nfs_clnt_trans(karg, arg);
2056 break;
2057
2058 case NFSCTL_EXPORT:
2059 case NFSCTL_UNEXPORT:
2060 err = compat_nfs_exp_trans(karg, arg);
2061 break;
2062
2063 case NFSCTL_GETFD:
2064 err = compat_nfs_getfd_trans(karg, arg);
2065 break;
2066
2067 case NFSCTL_GETFS:
2068 err = compat_nfs_getfs_trans(karg, arg);
2069 break;
2070
2071 default:
2072 err = -EINVAL;
Peter Staubach57070d02006-03-25 03:08:04 -08002073 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074 }
2075
Peter Staubach57070d02006-03-25 03:08:04 -08002076 if (err)
2077 goto done;
2078
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079 oldfs = get_fs();
2080 set_fs(KERNEL_DS);
2081 /* The __user pointer casts are valid because of the set_fs() */
2082 err = sys_nfsservctl(cmd, (void __user *) karg, (void __user *) kres);
2083 set_fs(oldfs);
2084
2085 if (err)
2086 goto done;
2087
2088 if((cmd == NFSCTL_GETFD) ||
2089 (cmd == NFSCTL_GETFS))
2090 err = compat_nfs_getfh_res_trans(kres, res);
2091
2092done:
2093 kfree(karg);
2094 kfree(kres);
2095 return err;
2096}
2097#else /* !NFSD */
2098long asmlinkage compat_sys_nfsservctl(int cmd, void *notused, void *notused2)
2099{
2100 return sys_ni_syscall();
2101}
2102#endif
Davide Libenzif6dfb4f2007-03-07 20:41:21 -08002103
2104#ifdef CONFIG_EPOLL
2105
2106#ifdef CONFIG_HAS_COMPAT_EPOLL_EVENT
2107asmlinkage long compat_sys_epoll_ctl(int epfd, int op, int fd,
2108 struct compat_epoll_event __user *event)
2109{
2110 long err = 0;
2111 struct compat_epoll_event user;
2112 struct epoll_event __user *kernel = NULL;
2113
2114 if (event) {
2115 if (copy_from_user(&user, event, sizeof(user)))
2116 return -EFAULT;
2117 kernel = compat_alloc_user_space(sizeof(struct epoll_event));
2118 err |= __put_user(user.events, &kernel->events);
2119 err |= __put_user(user.data, &kernel->data);
2120 }
2121
2122 return err ? err : sys_epoll_ctl(epfd, op, fd, kernel);
2123}
2124
2125
2126asmlinkage long compat_sys_epoll_wait(int epfd,
2127 struct compat_epoll_event __user *events,
2128 int maxevents, int timeout)
2129{
2130 long i, ret, err = 0;
2131 struct epoll_event __user *kbuf;
2132 struct epoll_event ev;
2133
2134 if ((maxevents <= 0) ||
2135 (maxevents > (INT_MAX / sizeof(struct epoll_event))))
2136 return -EINVAL;
2137 kbuf = compat_alloc_user_space(sizeof(struct epoll_event) * maxevents);
2138 ret = sys_epoll_wait(epfd, kbuf, maxevents, timeout);
2139 for (i = 0; i < ret; i++) {
2140 err |= __get_user(ev.events, &kbuf[i].events);
2141 err |= __get_user(ev.data, &kbuf[i].data);
2142 err |= __put_user(ev.events, &events->events);
2143 err |= __put_user_unaligned(ev.data, &events->data);
2144 events++;
2145 }
2146
2147 return err ? -EFAULT: ret;
2148}
2149#endif /* CONFIG_HAS_COMPAT_EPOLL_EVENT */
2150
2151#ifdef TIF_RESTORE_SIGMASK
2152asmlinkage long compat_sys_epoll_pwait(int epfd,
2153 struct compat_epoll_event __user *events,
2154 int maxevents, int timeout,
2155 const compat_sigset_t __user *sigmask,
2156 compat_size_t sigsetsize)
2157{
2158 long err;
2159 compat_sigset_t csigmask;
2160 sigset_t ksigmask, sigsaved;
2161
2162 /*
2163 * If the caller wants a certain signal mask to be set during the wait,
2164 * we apply it here.
2165 */
2166 if (sigmask) {
2167 if (sigsetsize != sizeof(compat_sigset_t))
2168 return -EINVAL;
2169 if (copy_from_user(&csigmask, sigmask, sizeof(csigmask)))
2170 return -EFAULT;
2171 sigset_from_compat(&ksigmask, &csigmask);
2172 sigdelsetmask(&ksigmask, sigmask(SIGKILL) | sigmask(SIGSTOP));
2173 sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
2174 }
2175
2176#ifdef CONFIG_HAS_COMPAT_EPOLL_EVENT
2177 err = compat_sys_epoll_wait(epfd, events, maxevents, timeout);
2178#else
2179 err = sys_epoll_wait(epfd, events, maxevents, timeout);
2180#endif
2181
2182 /*
2183 * If we changed the signal mask, we need to restore the original one.
2184 * In case we've got a signal while waiting, we do not restore the
2185 * signal mask yet, and we allow do_signal() to deliver the signal on
2186 * the way back to userspace, before the signal mask is restored.
2187 */
2188 if (sigmask) {
2189 if (err == -EINTR) {
2190 memcpy(&current->saved_sigmask, &sigsaved,
2191 sizeof(sigsaved));
2192 set_thread_flag(TIF_RESTORE_SIGMASK);
2193 } else
2194 sigprocmask(SIG_SETMASK, &sigsaved, NULL);
2195 }
2196
2197 return err;
2198}
2199#endif /* TIF_RESTORE_SIGMASK */
2200
2201#endif /* CONFIG_EPOLL */