diff options
Diffstat (limited to 'fs/compat.c')
-rw-r--r-- | fs/compat.c | 161 |
1 files changed, 91 insertions, 70 deletions
diff --git a/fs/compat.c b/fs/compat.c index 4b6ed03cc478..e6d5d70cf3cf 100644 --- a/fs/compat.c +++ b/fs/compat.c | |||
@@ -8,13 +8,14 @@ | |||
8 | * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) | 8 | * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) |
9 | * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) | 9 | * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) |
10 | * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs | 10 | * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs |
11 | * Copyright (C) 2003 Pavel Machek (pavel@suse.cz) | 11 | * Copyright (C) 2003 Pavel Machek (pavel@ucw.cz) |
12 | * | 12 | * |
13 | * This program is free software; you can redistribute it and/or modify | 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 | 14 | * it under the terms of the GNU General Public License version 2 as |
15 | * published by the Free Software Foundation. | 15 | * published by the Free Software Foundation. |
16 | */ | 16 | */ |
17 | 17 | ||
18 | #include <linux/stddef.h> | ||
18 | #include <linux/kernel.h> | 19 | #include <linux/kernel.h> |
19 | #include <linux/linkage.h> | 20 | #include <linux/linkage.h> |
20 | #include <linux/compat.h> | 21 | #include <linux/compat.h> |
@@ -266,7 +267,7 @@ asmlinkage long compat_sys_statfs(const char __user *pathname, struct compat_sta | |||
266 | error = user_path(pathname, &path); | 267 | error = user_path(pathname, &path); |
267 | if (!error) { | 268 | if (!error) { |
268 | struct kstatfs tmp; | 269 | struct kstatfs tmp; |
269 | error = vfs_statfs(path.dentry, &tmp); | 270 | error = vfs_statfs(&path, &tmp); |
270 | if (!error) | 271 | if (!error) |
271 | error = put_compat_statfs(buf, &tmp); | 272 | error = put_compat_statfs(buf, &tmp); |
272 | path_put(&path); | 273 | path_put(&path); |
@@ -284,7 +285,7 @@ asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs __user | |||
284 | file = fget(fd); | 285 | file = fget(fd); |
285 | if (!file) | 286 | if (!file) |
286 | goto out; | 287 | goto out; |
287 | error = vfs_statfs(file->f_path.dentry, &tmp); | 288 | error = vfs_statfs(&file->f_path, &tmp); |
288 | if (!error) | 289 | if (!error) |
289 | error = put_compat_statfs(buf, &tmp); | 290 | error = put_compat_statfs(buf, &tmp); |
290 | fput(file); | 291 | fput(file); |
@@ -334,7 +335,7 @@ asmlinkage long compat_sys_statfs64(const char __user *pathname, compat_size_t s | |||
334 | error = user_path(pathname, &path); | 335 | error = user_path(pathname, &path); |
335 | if (!error) { | 336 | if (!error) { |
336 | struct kstatfs tmp; | 337 | struct kstatfs tmp; |
337 | error = vfs_statfs(path.dentry, &tmp); | 338 | error = vfs_statfs(&path, &tmp); |
338 | if (!error) | 339 | if (!error) |
339 | error = put_compat_statfs64(buf, &tmp); | 340 | error = put_compat_statfs64(buf, &tmp); |
340 | path_put(&path); | 341 | path_put(&path); |
@@ -355,7 +356,7 @@ asmlinkage long compat_sys_fstatfs64(unsigned int fd, compat_size_t sz, struct c | |||
355 | file = fget(fd); | 356 | file = fget(fd); |
356 | if (!file) | 357 | if (!file) |
357 | goto out; | 358 | goto out; |
358 | error = vfs_statfs(file->f_path.dentry, &tmp); | 359 | error = vfs_statfs(&file->f_path, &tmp); |
359 | if (!error) | 360 | if (!error) |
360 | error = put_compat_statfs64(buf, &tmp); | 361 | error = put_compat_statfs64(buf, &tmp); |
361 | fput(file); | 362 | fput(file); |
@@ -378,7 +379,7 @@ asmlinkage long compat_sys_ustat(unsigned dev, struct compat_ustat __user *u) | |||
378 | sb = user_get_super(new_decode_dev(dev)); | 379 | sb = user_get_super(new_decode_dev(dev)); |
379 | if (!sb) | 380 | if (!sb) |
380 | return -EINVAL; | 381 | return -EINVAL; |
381 | err = vfs_statfs(sb->s_root, &sbuf); | 382 | err = statfs_by_dentry(sb->s_root, &sbuf); |
382 | drop_super(sb); | 383 | drop_super(sb); |
383 | if (err) | 384 | if (err) |
384 | return err; | 385 | return err; |
@@ -568,6 +569,79 @@ out: | |||
568 | return ret; | 569 | return ret; |
569 | } | 570 | } |
570 | 571 | ||
572 | /* A write operation does a read from user space and vice versa */ | ||
573 | #define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ) | ||
574 | |||
575 | ssize_t compat_rw_copy_check_uvector(int type, | ||
576 | const struct compat_iovec __user *uvector, unsigned long nr_segs, | ||
577 | unsigned long fast_segs, struct iovec *fast_pointer, | ||
578 | struct iovec **ret_pointer) | ||
579 | { | ||
580 | compat_ssize_t tot_len; | ||
581 | struct iovec *iov = *ret_pointer = fast_pointer; | ||
582 | ssize_t ret = 0; | ||
583 | int seg; | ||
584 | |||
585 | /* | ||
586 | * SuS says "The readv() function *may* fail if the iovcnt argument | ||
587 | * was less than or equal to 0, or greater than {IOV_MAX}. Linux has | ||
588 | * traditionally returned zero for zero segments, so... | ||
589 | */ | ||
590 | if (nr_segs == 0) | ||
591 | goto out; | ||
592 | |||
593 | ret = -EINVAL; | ||
594 | if (nr_segs > UIO_MAXIOV || nr_segs < 0) | ||
595 | goto out; | ||
596 | if (nr_segs > fast_segs) { | ||
597 | ret = -ENOMEM; | ||
598 | iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL); | ||
599 | if (iov == NULL) { | ||
600 | *ret_pointer = fast_pointer; | ||
601 | goto out; | ||
602 | } | ||
603 | } | ||
604 | *ret_pointer = iov; | ||
605 | |||
606 | /* | ||
607 | * Single unix specification: | ||
608 | * We should -EINVAL if an element length is not >= 0 and fitting an | ||
609 | * ssize_t. The total length is fitting an ssize_t | ||
610 | * | ||
611 | * Be careful here because iov_len is a size_t not an ssize_t | ||
612 | */ | ||
613 | tot_len = 0; | ||
614 | ret = -EINVAL; | ||
615 | for (seg = 0; seg < nr_segs; seg++) { | ||
616 | compat_ssize_t tmp = tot_len; | ||
617 | compat_uptr_t buf; | ||
618 | compat_ssize_t len; | ||
619 | |||
620 | if (__get_user(len, &uvector->iov_len) || | ||
621 | __get_user(buf, &uvector->iov_base)) { | ||
622 | ret = -EFAULT; | ||
623 | goto out; | ||
624 | } | ||
625 | if (len < 0) /* size_t not fitting in compat_ssize_t .. */ | ||
626 | goto out; | ||
627 | tot_len += len; | ||
628 | if (tot_len < tmp) /* maths overflow on the compat_ssize_t */ | ||
629 | goto out; | ||
630 | if (!access_ok(vrfy_dir(type), compat_ptr(buf), len)) { | ||
631 | ret = -EFAULT; | ||
632 | goto out; | ||
633 | } | ||
634 | iov->iov_base = compat_ptr(buf); | ||
635 | iov->iov_len = (compat_size_t) len; | ||
636 | uvector++; | ||
637 | iov++; | ||
638 | } | ||
639 | ret = tot_len; | ||
640 | |||
641 | out: | ||
642 | return ret; | ||
643 | } | ||
644 | |||
571 | static inline long | 645 | static inline long |
572 | copy_iocb(long nr, u32 __user *ptr32, struct iocb __user * __user *ptr64) | 646 | copy_iocb(long nr, u32 __user *ptr32, struct iocb __user * __user *ptr64) |
573 | { | 647 | { |
@@ -600,7 +674,7 @@ compat_sys_io_submit(aio_context_t ctx_id, int nr, u32 __user *iocb) | |||
600 | iocb64 = compat_alloc_user_space(nr * sizeof(*iocb64)); | 674 | iocb64 = compat_alloc_user_space(nr * sizeof(*iocb64)); |
601 | ret = copy_iocb(nr, iocb, iocb64); | 675 | ret = copy_iocb(nr, iocb, iocb64); |
602 | if (!ret) | 676 | if (!ret) |
603 | ret = sys_io_submit(ctx_id, nr, iocb64); | 677 | ret = do_io_submit(ctx_id, nr, iocb64, 1); |
604 | return ret; | 678 | return ret; |
605 | } | 679 | } |
606 | 680 | ||
@@ -818,8 +892,6 @@ asmlinkage long compat_sys_mount(char __user * dev_name, char __user * dir_name, | |||
818 | return retval; | 892 | return retval; |
819 | } | 893 | } |
820 | 894 | ||
821 | #define NAME_OFFSET(de) ((int) ((de)->d_name - (char __user *) (de))) | ||
822 | |||
823 | struct compat_old_linux_dirent { | 895 | struct compat_old_linux_dirent { |
824 | compat_ulong_t d_ino; | 896 | compat_ulong_t d_ino; |
825 | compat_ulong_t d_offset; | 897 | compat_ulong_t d_offset; |
@@ -908,7 +980,8 @@ static int compat_filldir(void *__buf, const char *name, int namlen, | |||
908 | struct compat_linux_dirent __user * dirent; | 980 | struct compat_linux_dirent __user * dirent; |
909 | struct compat_getdents_callback *buf = __buf; | 981 | struct compat_getdents_callback *buf = __buf; |
910 | compat_ulong_t d_ino; | 982 | compat_ulong_t d_ino; |
911 | int reclen = ALIGN(NAME_OFFSET(dirent) + namlen + 2, sizeof(compat_long_t)); | 983 | int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) + |
984 | namlen + 2, sizeof(compat_long_t)); | ||
912 | 985 | ||
913 | buf->error = -EINVAL; /* only used if we fail.. */ | 986 | buf->error = -EINVAL; /* only used if we fail.. */ |
914 | if (reclen > buf->count) | 987 | if (reclen > buf->count) |
@@ -995,8 +1068,8 @@ static int compat_filldir64(void * __buf, const char * name, int namlen, loff_t | |||
995 | { | 1068 | { |
996 | struct linux_dirent64 __user *dirent; | 1069 | struct linux_dirent64 __user *dirent; |
997 | struct compat_getdents_callback64 *buf = __buf; | 1070 | struct compat_getdents_callback64 *buf = __buf; |
998 | int jj = NAME_OFFSET(dirent); | 1071 | int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1, |
999 | int reclen = ALIGN(jj + namlen + 1, sizeof(u64)); | 1072 | sizeof(u64)); |
1000 | u64 off; | 1073 | u64 off; |
1001 | 1074 | ||
1002 | buf->error = -EINVAL; /* only used if we fail.. */ | 1075 | buf->error = -EINVAL; /* only used if we fail.. */ |
@@ -1077,70 +1150,21 @@ static ssize_t compat_do_readv_writev(int type, struct file *file, | |||
1077 | { | 1150 | { |
1078 | compat_ssize_t tot_len; | 1151 | compat_ssize_t tot_len; |
1079 | struct iovec iovstack[UIO_FASTIOV]; | 1152 | struct iovec iovstack[UIO_FASTIOV]; |
1080 | struct iovec *iov=iovstack, *vector; | 1153 | struct iovec *iov; |
1081 | ssize_t ret; | 1154 | ssize_t ret; |
1082 | int seg; | ||
1083 | io_fn_t fn; | 1155 | io_fn_t fn; |
1084 | iov_fn_t fnv; | 1156 | iov_fn_t fnv; |
1085 | 1157 | ||
1086 | /* | ||
1087 | * SuS says "The readv() function *may* fail if the iovcnt argument | ||
1088 | * was less than or equal to 0, or greater than {IOV_MAX}. Linux has | ||
1089 | * traditionally returned zero for zero segments, so... | ||
1090 | */ | ||
1091 | ret = 0; | ||
1092 | if (nr_segs == 0) | ||
1093 | goto out; | ||
1094 | |||
1095 | /* | ||
1096 | * First get the "struct iovec" from user memory and | ||
1097 | * verify all the pointers | ||
1098 | */ | ||
1099 | ret = -EINVAL; | 1158 | ret = -EINVAL; |
1100 | if ((nr_segs > UIO_MAXIOV) || (nr_segs <= 0)) | ||
1101 | goto out; | ||
1102 | if (!file->f_op) | 1159 | if (!file->f_op) |
1103 | goto out; | 1160 | goto out; |
1104 | if (nr_segs > UIO_FASTIOV) { | 1161 | |
1105 | ret = -ENOMEM; | ||
1106 | iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL); | ||
1107 | if (!iov) | ||
1108 | goto out; | ||
1109 | } | ||
1110 | ret = -EFAULT; | 1162 | ret = -EFAULT; |
1111 | if (!access_ok(VERIFY_READ, uvector, nr_segs*sizeof(*uvector))) | 1163 | if (!access_ok(VERIFY_READ, uvector, nr_segs*sizeof(*uvector))) |
1112 | goto out; | 1164 | goto out; |
1113 | 1165 | ||
1114 | /* | 1166 | tot_len = compat_rw_copy_check_uvector(type, uvector, nr_segs, |
1115 | * Single unix specification: | 1167 | UIO_FASTIOV, iovstack, &iov); |
1116 | * We should -EINVAL if an element length is not >= 0 and fitting an | ||
1117 | * ssize_t. The total length is fitting an ssize_t | ||
1118 | * | ||
1119 | * Be careful here because iov_len is a size_t not an ssize_t | ||
1120 | */ | ||
1121 | tot_len = 0; | ||
1122 | vector = iov; | ||
1123 | ret = -EINVAL; | ||
1124 | for (seg = 0 ; seg < nr_segs; seg++) { | ||
1125 | compat_ssize_t tmp = tot_len; | ||
1126 | compat_ssize_t len; | ||
1127 | compat_uptr_t buf; | ||
1128 | |||
1129 | if (__get_user(len, &uvector->iov_len) || | ||
1130 | __get_user(buf, &uvector->iov_base)) { | ||
1131 | ret = -EFAULT; | ||
1132 | goto out; | ||
1133 | } | ||
1134 | if (len < 0) /* size_t not fitting an compat_ssize_t .. */ | ||
1135 | goto out; | ||
1136 | tot_len += len; | ||
1137 | if (tot_len < tmp) /* maths overflow on the compat_ssize_t */ | ||
1138 | goto out; | ||
1139 | vector->iov_base = compat_ptr(buf); | ||
1140 | vector->iov_len = (compat_size_t) len; | ||
1141 | uvector++; | ||
1142 | vector++; | ||
1143 | } | ||
1144 | if (tot_len == 0) { | 1168 | if (tot_len == 0) { |
1145 | ret = 0; | 1169 | ret = 0; |
1146 | goto out; | 1170 | goto out; |
@@ -1169,11 +1193,10 @@ out: | |||
1169 | if (iov != iovstack) | 1193 | if (iov != iovstack) |
1170 | kfree(iov); | 1194 | kfree(iov); |
1171 | if ((ret + (type == READ)) > 0) { | 1195 | if ((ret + (type == READ)) > 0) { |
1172 | struct dentry *dentry = file->f_path.dentry; | ||
1173 | if (type == READ) | 1196 | if (type == READ) |
1174 | fsnotify_access(dentry); | 1197 | fsnotify_access(file); |
1175 | else | 1198 | else |
1176 | fsnotify_modify(dentry); | 1199 | fsnotify_modify(file); |
1177 | } | 1200 | } |
1178 | return ret; | 1201 | return ret; |
1179 | } | 1202 | } |
@@ -1531,8 +1554,6 @@ int compat_do_execve(char * filename, | |||
1531 | if (retval < 0) | 1554 | if (retval < 0) |
1532 | goto out; | 1555 | goto out; |
1533 | 1556 | ||
1534 | current->stack_start = current->mm->start_stack; | ||
1535 | |||
1536 | /* execve succeeded */ | 1557 | /* execve succeeded */ |
1537 | current->fs->in_exec = 0; | 1558 | current->fs->in_exec = 0; |
1538 | current->in_execve = 0; | 1559 | current->in_execve = 0; |