Newer
Older
goto out;
buf.current_dir = dirent;
buf.previous = NULL;
buf.count = count;
buf.error = 0;
error = vfs_readdir(file, compat_filldir64, &buf);
if (error < 0)
goto out_putf;
error = buf.error;
lastdirent = buf.previous;
if (lastdirent) {
typeof(lastdirent->d_off) d_off = file->f_pos;
error = -EFAULT;
if (__put_user_unaligned(d_off, &lastdirent->d_off))
goto out_putf;
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
error = count - buf.count;
}
out_putf:
fput(file);
out:
return error;
}
#endif /* ! __ARCH_OMIT_COMPAT_SYS_GETDENTS64 */
static ssize_t compat_do_readv_writev(int type, struct file *file,
const struct compat_iovec __user *uvector,
unsigned long nr_segs, loff_t *pos)
{
compat_ssize_t tot_len;
struct iovec iovstack[UIO_FASTIOV];
struct iovec *iov=iovstack, *vector;
ssize_t ret;
int seg;
io_fn_t fn;
iov_fn_t fnv;
/*
* SuS says "The readv() function *may* fail if the iovcnt argument
* was less than or equal to 0, or greater than {IOV_MAX}. Linux has
* traditionally returned zero for zero segments, so...
*/
ret = 0;
if (nr_segs == 0)
goto out;
/*
* First get the "struct iovec" from user memory and
* verify all the pointers
*/
ret = -EINVAL;
if ((nr_segs > UIO_MAXIOV) || (nr_segs <= 0))
goto out;
if (!file->f_op)
goto out;
if (nr_segs > UIO_FASTIOV) {
ret = -ENOMEM;
iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
if (!iov)
goto out;
}
ret = -EFAULT;
if (!access_ok(VERIFY_READ, uvector, nr_segs*sizeof(*uvector)))
goto out;
/*
* Single unix specification:
* We should -EINVAL if an element length is not >= 0 and fitting an
* ssize_t. The total length is fitting an ssize_t
*
* Be careful here because iov_len is a size_t not an ssize_t
*/
tot_len = 0;
vector = iov;
ret = -EINVAL;
for (seg = 0 ; seg < nr_segs; seg++) {
compat_ssize_t tmp = tot_len;
compat_ssize_t len;
compat_uptr_t buf;
if (__get_user(len, &uvector->iov_len) ||
__get_user(buf, &uvector->iov_base)) {
ret = -EFAULT;
goto out;
}
if (len < 0) /* size_t not fitting an compat_ssize_t .. */
goto out;
tot_len += len;
if (tot_len < tmp) /* maths overflow on the compat_ssize_t */
goto out;
vector->iov_base = compat_ptr(buf);
vector->iov_len = (compat_size_t) len;
uvector++;
vector++;
}
if (tot_len == 0) {
ret = 0;
goto out;
}
ret = rw_verify_area(type, file, pos, tot_len);
goto out;
fnv = NULL;
if (type == READ) {
fn = file->f_op->read;
fnv = file->f_op->aio_read;
fnv = file->f_op->aio_write;
if (fnv)
ret = do_sync_readv_writev(file, iov, nr_segs, tot_len,
pos, fnv);
else
ret = do_loop_readv_writev(file, iov, nr_segs, pos, fn);
struct dentry *dentry = file->f_path.dentry;
if (type == READ)
fsnotify_access(dentry);
else
fsnotify_modify(dentry);
}
return ret;
}
asmlinkage ssize_t
compat_sys_readv(unsigned long fd, const struct compat_iovec __user *vec, unsigned long vlen)
{
struct file *file;
ssize_t ret = -EBADF;
file = fget(fd);
if (!file)
return -EBADF;
if (!(file->f_mode & FMODE_READ))
goto out;
ret = -EINVAL;
if (!file->f_op || (!file->f_op->aio_read && !file->f_op->read))
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
goto out;
ret = compat_do_readv_writev(READ, file, vec, vlen, &file->f_pos);
out:
fput(file);
return ret;
}
asmlinkage ssize_t
compat_sys_writev(unsigned long fd, const struct compat_iovec __user *vec, unsigned long vlen)
{
struct file *file;
ssize_t ret = -EBADF;
file = fget(fd);
if (!file)
return -EBADF;
if (!(file->f_mode & FMODE_WRITE))
goto out;
ret = -EINVAL;
if (!file->f_op || (!file->f_op->aio_write && !file->f_op->write))
goto out;
ret = compat_do_readv_writev(WRITE, file, vec, vlen, &file->f_pos);
out:
fput(file);
return ret;
}
asmlinkage long
compat_sys_vmsplice(int fd, const struct compat_iovec __user *iov32,
unsigned int nr_segs, unsigned int flags)
{
unsigned i;
if (nr_segs > UIO_MAXIOV)
return -EINVAL;
iov = compat_alloc_user_space(nr_segs * sizeof(struct iovec));
for (i = 0; i < nr_segs; i++) {
struct compat_iovec v;
if (get_user(v.iov_base, &iov32[i].iov_base) ||
get_user(v.iov_len, &iov32[i].iov_len) ||
put_user(compat_ptr(v.iov_base), &iov[i].iov_base) ||
put_user(v.iov_len, &iov[i].iov_len))
return -EFAULT;
}
return sys_vmsplice(fd, iov, nr_segs, flags);
}
/*
* Exactly like fs/open.c:sys_open(), except that it doesn't set the
* O_LARGEFILE flag.
*/
asmlinkage long
compat_sys_open(const char __user *filename, int flags, int mode)
{
return do_sys_open(AT_FDCWD, filename, flags, mode);
}
/*
* Exactly like fs/open.c:sys_openat(), except that it doesn't set the
* O_LARGEFILE flag.
*/
asmlinkage long
compat_sys_openat(unsigned int dfd, const char __user *filename, int flags, int mode)
{
return do_sys_open(dfd, filename, flags, mode);
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
/*
* compat_count() counts the number of arguments/envelopes. It is basically
* a copy of count() from fs/exec.c, except that it works with 32 bit argv
* and envp pointers.
*/
static int compat_count(compat_uptr_t __user *argv, int max)
{
int i = 0;
if (argv != NULL) {
for (;;) {
compat_uptr_t p;
if (get_user(p, argv))
return -EFAULT;
if (!p)
break;
argv++;
if(++i > max)
return -E2BIG;
}
}
return i;
}
/*
* compat_copy_strings() is basically a copy of copy_strings() from fs/exec.c
* except that it works with 32 bit argv and envp pointers.
*/
static int compat_copy_strings(int argc, compat_uptr_t __user *argv,
struct linux_binprm *bprm)
{
struct page *kmapped_page = NULL;
char *kaddr = NULL;
int ret;
while (argc-- > 0) {
compat_uptr_t str;
int len;
unsigned long pos;
if (get_user(str, argv+argc) ||
!(len = strnlen_user(compat_ptr(str), MAX_ARG_STRLEN))) {
/* We're going to work our way backwords. */
while (len > 0) {
int offset, bytes_to_copy;
offset = pos % PAGE_SIZE;
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
if (offset == 0)
offset = PAGE_SIZE;
bytes_to_copy = offset;
if (bytes_to_copy > len)
bytes_to_copy = len;
offset -= bytes_to_copy;
pos -= bytes_to_copy;
str -= bytes_to_copy;
len -= bytes_to_copy;
if (!kmapped_page || kpos != (pos & PAGE_MASK)) {
struct page *page;
#ifdef CONFIG_STACK_GROWSUP
ret = expand_stack_downwards(bprm->vma, pos);
if (ret < 0) {
/* We've exceed the stack rlimit. */
ret = -E2BIG;
goto out;
}
#endif
ret = get_user_pages(current, bprm->mm, pos,
1, 1, 1, &page, NULL);
if (ret <= 0) {
/* We've exceed the stack rlimit. */
ret = -E2BIG;
if (kmapped_page) {
flush_kernel_dcache_page(kmapped_page);
kmapped_page = page;
kaddr = kmap(kmapped_page);
kpos = pos & PAGE_MASK;
flush_cache_page(bprm->vma, kpos,
page_to_pfn(kmapped_page));
if (copy_from_user(kaddr+offset, compat_ptr(str),
bytes_to_copy)) {
ret = -EFAULT;
goto out;
}
}
}
ret = 0;
out:
if (kmapped_page) {
flush_kernel_dcache_page(kmapped_page);
}
/*
* compat_do_execve() is mostly a copy of do_execve(), with the exception
* that it processes 32 bit argv and envp pointers.
*/
int compat_do_execve(char * filename,
compat_uptr_t __user *argv,
compat_uptr_t __user *envp,
struct pt_regs * regs)
{
struct linux_binprm *bprm;
struct file *file;
int retval;
retval = -ENOMEM;
bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
if (!bprm)
goto out_ret;
file = open_exec(filename);
retval = PTR_ERR(file);
if (IS_ERR(file))
goto out_kfree;
sched_exec();
bprm->file = file;
bprm->filename = filename;
bprm->interp = filename;
retval = bprm_mm_init(bprm);
if (retval)
goto out_file;
bprm->argc = compat_count(argv, MAX_ARG_STRINGS);
if ((retval = bprm->argc) < 0)
goto out_mm;
bprm->envc = compat_count(envp, MAX_ARG_STRINGS);
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
if ((retval = bprm->envc) < 0)
goto out_mm;
retval = security_bprm_alloc(bprm);
if (retval)
goto out;
retval = prepare_binprm(bprm);
if (retval < 0)
goto out;
retval = copy_strings_kernel(1, &bprm->filename, bprm);
if (retval < 0)
goto out;
bprm->exec = bprm->p;
retval = compat_copy_strings(bprm->envc, envp, bprm);
if (retval < 0)
goto out;
retval = compat_copy_strings(bprm->argc, argv, bprm);
if (retval < 0)
goto out;
retval = search_binary_handler(bprm, regs);
if (retval >= 0) {
/* execve success */
security_bprm_free(bprm);
kfree(bprm);
return retval;
}
out:
if (bprm->security)
security_bprm_free(bprm);
out_mm:
if (bprm->mm)
out_file:
if (bprm->file) {
allow_write_access(bprm->file);
fput(bprm->file);
}
out_kfree:
kfree(bprm);
out_ret:
return retval;
}
#define __COMPAT_NFDBITS (8 * sizeof(compat_ulong_t))
/*
* Ooo, nasty. We need here to frob 32-bit unsigned longs to
* 64-bit unsigned longs.
*/
int compat_get_fd_set(unsigned long nr, compat_ulong_t __user *ufdset,
unsigned long *fdset)
{
nr = DIV_ROUND_UP(nr, __COMPAT_NFDBITS);
if (ufdset) {
unsigned long odd;
if (!access_ok(VERIFY_WRITE, ufdset, nr*sizeof(compat_ulong_t)))
return -EFAULT;
odd = nr & 1UL;
nr &= ~1UL;
while (nr) {
unsigned long h, l;
if (__get_user(l, ufdset) || __get_user(h, ufdset+1))
return -EFAULT;
ufdset += 2;
*fdset++ = h << 32 | l;
nr -= 2;
}
if (odd && __get_user(*fdset, ufdset))
return -EFAULT;
} else {
/* Tricky, must clear full unsigned long in the
* kernel fdset at the end, this makes sure that
* actually happens.
*/
memset(fdset, 0, ((nr + 1) & ~1)*sizeof(compat_ulong_t));
}
return 0;
}
int compat_set_fd_set(unsigned long nr, compat_ulong_t __user *ufdset,
unsigned long *fdset)
nr = DIV_ROUND_UP(nr, __COMPAT_NFDBITS);
odd = nr & 1UL;
nr &= ~1UL;
while (nr) {
unsigned long h, l;
l = *fdset++;
h = l >> 32;
if (__put_user(l, ufdset) || __put_user(h, ufdset+1))
return -EFAULT;
if (odd && __put_user(*fdset, ufdset))
return -EFAULT;
return 0;
}
/*
* This is a virtual copy of sys_select from fs/select.c and probably
* should be compared to it from time to time
*/
/*
* We can actually return ERESTARTSYS instead of EINTR, but I'd
* like to be certain this leads to no problems. So I return
* EINTR just for safety.
*
* Update: ERESTARTSYS breaks at least the xview clock binary, so
* I'm trying ERESTARTNOHAND which restart only when you want to.
*/
#define MAX_SELECT_SECONDS \
((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1)
int compat_core_sys_select(int n, compat_ulong_t __user *inp,
compat_ulong_t __user *outp, compat_ulong_t __user *exp, s64 *timeout)
Badari Pulavarty
committed
void *bits;
int size, max_fds, ret = -EINVAL;
struct fdtable *fdt;
Badari Pulavarty
committed
long stack_fds[SELECT_STACK_ALLOC/sizeof(long)];
/* max_fds can increase, so grab it once to avoid race */
fdt = files_fdtable(current->files);
max_fds = fdt->max_fds;
if (n > max_fds)
n = max_fds;
/*
* We need 6 bitmaps (in/out/ex for both incoming and outgoing),
* since we used fdset we need to allocate memory in units of
* long-words.
*/
size = FDS_BYTES(n);
Badari Pulavarty
committed
bits = stack_fds;
if (size > sizeof(stack_fds) / 6) {
bits = kmalloc(6 * size, GFP_KERNEL);
ret = -ENOMEM;
if (!bits)
goto out_nofds;
}
fds.in = (unsigned long *) bits;
fds.out = (unsigned long *) (bits + size);
fds.ex = (unsigned long *) (bits + 2*size);
fds.res_in = (unsigned long *) (bits + 3*size);
fds.res_out = (unsigned long *) (bits + 4*size);
fds.res_ex = (unsigned long *) (bits + 5*size);
if ((ret = compat_get_fd_set(n, inp, fds.in)) ||
(ret = compat_get_fd_set(n, outp, fds.out)) ||
(ret = compat_get_fd_set(n, exp, fds.ex)))
goto out;
zero_fd_set(n, fds.res_in);
zero_fd_set(n, fds.res_out);
zero_fd_set(n, fds.res_ex);
ret = do_select(n, &fds, timeout);
if (ret < 0)
goto out;
if (!ret) {
ret = -ERESTARTNOHAND;
if (signal_pending(current))
goto out;
ret = 0;
}
if (compat_set_fd_set(n, inp, fds.res_in) ||
compat_set_fd_set(n, outp, fds.res_out) ||
compat_set_fd_set(n, exp, fds.res_ex))
ret = -EFAULT;
Badari Pulavarty
committed
if (bits != stack_fds)
kfree(bits);
asmlinkage long compat_sys_select(int n, compat_ulong_t __user *inp,
compat_ulong_t __user *outp, compat_ulong_t __user *exp,
struct compat_timeval __user *tvp)
{
s64 timeout = -1;
struct compat_timeval tv;
int ret;
if (tvp) {
if (copy_from_user(&tv, tvp, sizeof(tv)))
return -EFAULT;
if (tv.tv_sec < 0 || tv.tv_usec < 0)
return -EINVAL;
/* Cast to u64 to make GCC stop complaining */
if ((u64)tv.tv_sec >= (u64)MAX_INT64_SECONDS)
timeout = -1; /* infinite */
else {
timeout = DIV_ROUND_UP(tv.tv_usec, 1000000/HZ);
timeout += tv.tv_sec * HZ;
}
}
ret = compat_core_sys_select(n, inp, outp, exp, &timeout);
if (tvp) {
if (current->personality & STICKY_TIMEOUTS)
goto sticky;
rtv.tv_usec = jiffies_to_usecs(do_div((*(u64*)&timeout), HZ));
rtv.tv_sec = timeout;
if (compat_timeval_compare(&rtv, &tv) >= 0)
rtv = tv;
if (copy_to_user(tvp, &rtv, sizeof(rtv))) {
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
sticky:
/*
* If an application puts its timeval in read-only
* memory, we don't want the Linux-specific update to
* the timeval to cause a fault after the select has
* completed successfully. However, because we're not
* updating the timeval, we can't restart the system
* call.
*/
if (ret == -ERESTARTNOHAND)
ret = -EINTR;
}
}
return ret;
}
#ifdef TIF_RESTORE_SIGMASK
asmlinkage long compat_sys_pselect7(int n, compat_ulong_t __user *inp,
compat_ulong_t __user *outp, compat_ulong_t __user *exp,
struct compat_timespec __user *tsp, compat_sigset_t __user *sigmask,
compat_size_t sigsetsize)
{
compat_sigset_t ss32;
sigset_t ksigmask, sigsaved;
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
struct compat_timespec ts;
int ret;
if (tsp) {
if (copy_from_user(&ts, tsp, sizeof(ts)))
return -EFAULT;
if (ts.tv_sec < 0 || ts.tv_nsec < 0)
return -EINVAL;
}
if (sigmask) {
if (sigsetsize != sizeof(compat_sigset_t))
return -EINVAL;
if (copy_from_user(&ss32, sigmask, sizeof(ss32)))
return -EFAULT;
sigset_from_compat(&ksigmask, &ss32);
sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP));
sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
}
do {
if (tsp) {
if ((unsigned long)ts.tv_sec < MAX_SELECT_SECONDS) {
timeout = DIV_ROUND_UP(ts.tv_nsec, 1000000000/HZ);
timeout += ts.tv_sec * (unsigned long)HZ;
ts.tv_sec = 0;
ts.tv_nsec = 0;
} else {
ts.tv_sec -= MAX_SELECT_SECONDS;
timeout = MAX_SELECT_SECONDS * HZ;
}
}
ret = compat_core_sys_select(n, inp, outp, exp, &timeout);
} while (!ret && !timeout && tsp && (ts.tv_sec || ts.tv_nsec));
if (tsp) {
if (current->personality & STICKY_TIMEOUTS)
goto sticky;
rts.tv_sec = timeout / HZ;
rts.tv_nsec = (timeout % HZ) * (NSEC_PER_SEC/HZ);
if (rts.tv_nsec >= NSEC_PER_SEC) {
rts.tv_sec++;
rts.tv_nsec -= NSEC_PER_SEC;
if (compat_timespec_compare(&rts, &ts) >= 0)
if (copy_to_user(tsp, &rts, sizeof(rts))) {
sticky:
/*
* If an application puts its timeval in read-only
* memory, we don't want the Linux-specific update to
* the timeval to cause a fault after the select has
* completed successfully. However, because we're not
* updating the timeval, we can't restart the system
* call.
*/
if (ret == -ERESTARTNOHAND)
ret = -EINTR;
}
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
}
if (ret == -ERESTARTNOHAND) {
/*
* Don't restore the signal mask yet. Let do_signal() deliver
* the signal on the way back to userspace, before the signal
* mask is restored.
*/
if (sigmask) {
memcpy(¤t->saved_sigmask, &sigsaved,
sizeof(sigsaved));
set_thread_flag(TIF_RESTORE_SIGMASK);
}
} else if (sigmask)
sigprocmask(SIG_SETMASK, &sigsaved, NULL);
return ret;
}
asmlinkage long compat_sys_pselect6(int n, compat_ulong_t __user *inp,
compat_ulong_t __user *outp, compat_ulong_t __user *exp,
struct compat_timespec __user *tsp, void __user *sig)
{
compat_size_t sigsetsize = 0;
compat_uptr_t up = 0;
if (sig) {
if (!access_ok(VERIFY_READ, sig,
sizeof(compat_uptr_t)+sizeof(compat_size_t)) ||
__get_user(up, (compat_uptr_t __user *)sig) ||
__get_user(sigsetsize,
(compat_size_t __user *)(sig+sizeof(up))))
return -EFAULT;
}
return compat_sys_pselect7(n, inp, outp, exp, tsp, compat_ptr(up),
sigsetsize);
}
asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds,
unsigned int nfds, struct compat_timespec __user *tsp,
const compat_sigset_t __user *sigmask, compat_size_t sigsetsize)
{
compat_sigset_t ss32;
sigset_t ksigmask, sigsaved;
struct compat_timespec ts;
s64 timeout = -1;
int ret;
if (tsp) {
if (copy_from_user(&ts, tsp, sizeof(ts)))
return -EFAULT;
/* We assume that ts.tv_sec is always lower than
the number of seconds that can be expressed in
an s64. Otherwise the compiler bitches at us */
timeout = DIV_ROUND_UP(ts.tv_nsec, 1000000000/HZ);
timeout += ts.tv_sec * HZ;
}
if (sigmask) {
if (sigsetsize != sizeof(compat_sigset_t))
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
return -EINVAL;
if (copy_from_user(&ss32, sigmask, sizeof(ss32)))
return -EFAULT;
sigset_from_compat(&ksigmask, &ss32);
sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP));
sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
}
ret = do_sys_poll(ufds, nfds, &timeout);
/* We can restart this syscall, usually */
if (ret == -EINTR) {
/*
* Don't restore the signal mask yet. Let do_signal() deliver
* the signal on the way back to userspace, before the signal
* mask is restored.
*/
if (sigmask) {
memcpy(¤t->saved_sigmask, &sigsaved,
sizeof(sigsaved));
set_thread_flag(TIF_RESTORE_SIGMASK);
}
ret = -ERESTARTNOHAND;
} else if (sigmask)
sigprocmask(SIG_SETMASK, &sigsaved, NULL);
if (tsp && timeout >= 0) {
if (current->personality & STICKY_TIMEOUTS)
goto sticky;
/* Yes, we know it's actually an s64, but it's also positive. */
rts.tv_nsec = jiffies_to_usecs(do_div((*(u64*)&timeout), HZ)) *
1000;
rts.tv_sec = timeout;
if (compat_timespec_compare(&rts, &ts) >= 0)
rts = ts;
if (copy_to_user(tsp, &rts, sizeof(rts))) {
sticky:
/*
* If an application puts its timeval in read-only
* memory, we don't want the Linux-specific update to
* the timeval to cause a fault after the select has
* completed successfully. However, because we're not
* updating the timeval, we can't restart the system
* call.
*/
if (ret == -ERESTARTNOHAND && timeout >= 0)
ret = -EINTR;
}
}
return ret;
}
#endif /* TIF_RESTORE_SIGMASK */
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
#if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)
/* Stuff for NFS server syscalls... */
struct compat_nfsctl_svc {
u16 svc32_port;
s32 svc32_nthreads;
};
struct compat_nfsctl_client {
s8 cl32_ident[NFSCLNT_IDMAX+1];
s32 cl32_naddr;
struct in_addr cl32_addrlist[NFSCLNT_ADDRMAX];
s32 cl32_fhkeytype;
s32 cl32_fhkeylen;
u8 cl32_fhkey[NFSCLNT_KEYMAX];
};
struct compat_nfsctl_export {
char ex32_client[NFSCLNT_IDMAX+1];
char ex32_path[NFS_MAXPATHLEN+1];
compat_dev_t ex32_dev;
compat_ino_t ex32_ino;
compat_int_t ex32_flags;
__compat_uid_t ex32_anon_uid;
__compat_gid_t ex32_anon_gid;
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
};
struct compat_nfsctl_fdparm {
struct sockaddr gd32_addr;
s8 gd32_path[NFS_MAXPATHLEN+1];
compat_int_t gd32_version;
};
struct compat_nfsctl_fsparm {
struct sockaddr gd32_addr;
s8 gd32_path[NFS_MAXPATHLEN+1];
compat_int_t gd32_maxlen;
};
struct compat_nfsctl_arg {
compat_int_t ca32_version; /* safeguard */
union {
struct compat_nfsctl_svc u32_svc;
struct compat_nfsctl_client u32_client;
struct compat_nfsctl_export u32_export;
struct compat_nfsctl_fdparm u32_getfd;
struct compat_nfsctl_fsparm u32_getfs;
} u;
#define ca32_svc u.u32_svc
#define ca32_client u.u32_client
#define ca32_export u.u32_export
#define ca32_getfd u.u32_getfd
#define ca32_getfs u.u32_getfs
};
union compat_nfsctl_res {
__u8 cr32_getfh[NFS_FHSIZE];
struct knfsd_fh cr32_getfs;
};
static int compat_nfs_svc_trans(struct nfsctl_arg *karg,
struct compat_nfsctl_arg __user *arg)
if (!access_ok(VERIFY_READ, &arg->ca32_svc, sizeof(arg->ca32_svc)) ||
get_user(karg->ca_version, &arg->ca32_version) ||
__get_user(karg->ca_svc.svc_port, &arg->ca32_svc.svc32_port) ||
__get_user(karg->ca_svc.svc_nthreads,
&arg->ca32_svc.svc32_nthreads))
return -EFAULT;
return 0;
static int compat_nfs_clnt_trans(struct nfsctl_arg *karg,
struct compat_nfsctl_arg __user *arg)
if (!access_ok(VERIFY_READ, &arg->ca32_client,
sizeof(arg->ca32_client)) ||
get_user(karg->ca_version, &arg->ca32_version) ||
__copy_from_user(&karg->ca_client.cl_ident[0],
&arg->ca32_client.cl32_ident[0],
NFSCLNT_IDMAX) ||
__get_user(karg->ca_client.cl_naddr,
&arg->ca32_client.cl32_naddr) ||
__copy_from_user(&karg->ca_client.cl_addrlist[0],
&arg->ca32_client.cl32_addrlist[0],
(sizeof(struct in_addr) * NFSCLNT_ADDRMAX)) ||
__get_user(karg->ca_client.cl_fhkeytype,
&arg->ca32_client.cl32_fhkeytype) ||
__get_user(karg->ca_client.cl_fhkeylen,
&arg->ca32_client.cl32_fhkeylen) ||
__copy_from_user(&karg->ca_client.cl_fhkey[0],
&arg->ca32_client.cl32_fhkey[0],
NFSCLNT_KEYMAX))
return -EFAULT;
return 0;
static int compat_nfs_exp_trans(struct nfsctl_arg *karg,
struct compat_nfsctl_arg __user *arg)
if (!access_ok(VERIFY_READ, &arg->ca32_export,
sizeof(arg->ca32_export)) ||
get_user(karg->ca_version, &arg->ca32_version) ||
__copy_from_user(&karg->ca_export.ex_client[0],
&arg->ca32_export.ex32_client[0],
NFSCLNT_IDMAX) ||
__copy_from_user(&karg->ca_export.ex_path[0],
&arg->ca32_export.ex32_path[0],
NFS_MAXPATHLEN) ||
__get_user(karg->ca_export.ex_dev,
&arg->ca32_export.ex32_dev) ||
__get_user(karg->ca_export.ex_ino,
&arg->ca32_export.ex32_ino) ||
__get_user(karg->ca_export.ex_flags,
&arg->ca32_export.ex32_flags) ||
__get_user(karg->ca_export.ex_anon_uid,
&arg->ca32_export.ex32_anon_uid) ||
__get_user(karg->ca_export.ex_anon_gid,
&arg->ca32_export.ex32_anon_gid))
return -EFAULT;
SET_UID(karg->ca_export.ex_anon_uid, karg->ca_export.ex_anon_uid);
SET_GID(karg->ca_export.ex_anon_gid, karg->ca_export.ex_anon_gid);
return 0;
static int compat_nfs_getfd_trans(struct nfsctl_arg *karg,
struct compat_nfsctl_arg __user *arg)
if (!access_ok(VERIFY_READ, &arg->ca32_getfd,
sizeof(arg->ca32_getfd)) ||
get_user(karg->ca_version, &arg->ca32_version) ||
__copy_from_user(&karg->ca_getfd.gd_addr,
&arg->ca32_getfd.gd32_addr,
(sizeof(struct sockaddr))) ||
__copy_from_user(&karg->ca_getfd.gd_path,
&arg->ca32_getfd.gd32_path,
(NFS_MAXPATHLEN+1)) ||
__get_user(karg->ca_getfd.gd_version,
&arg->ca32_getfd.gd32_version))
return -EFAULT;
return 0;
static int compat_nfs_getfs_trans(struct nfsctl_arg *karg,
struct compat_nfsctl_arg __user *arg)
if (!access_ok(VERIFY_READ,&arg->ca32_getfs,sizeof(arg->ca32_getfs)) ||
get_user(karg->ca_version, &arg->ca32_version) ||
__copy_from_user(&karg->ca_getfs.gd_addr,
&arg->ca32_getfs.gd32_addr,
(sizeof(struct sockaddr))) ||
__copy_from_user(&karg->ca_getfs.gd_path,
&arg->ca32_getfs.gd32_path,
(NFS_MAXPATHLEN+1)) ||
__get_user(karg->ca_getfs.gd_maxlen,
&arg->ca32_getfs.gd32_maxlen))
return -EFAULT;
return 0;
}
/* This really doesn't need translations, we are only passing
* back a union which contains opaque nfs file handle data.
*/
static int compat_nfs_getfh_res_trans(union nfsctl_res *kres,
union compat_nfsctl_res __user *res)