patch-2.4.22 linux-2.4.22/arch/mips64/kernel/linux32.c
Next file: linux-2.4.22/arch/mips64/kernel/mips64_ksyms.c
Previous file: linux-2.4.22/arch/mips64/kernel/irq_cpu.c
Back to the patch index
Back to the overall index
- Lines: 1481
- Date:
2003-08-25 04:44:40.000000000 -0700
- Orig file:
linux-2.4.21/arch/mips64/kernel/linux32.c
- Orig date:
2002-11-28 15:53:10.000000000 -0800
diff -urN linux-2.4.21/arch/mips64/kernel/linux32.c linux-2.4.22/arch/mips64/kernel/linux32.c
@@ -13,7 +13,6 @@
#include <linux/highuid.h>
#include <linux/dirent.h>
#include <linux/resource.h>
-#include <linux/filter.h>
#include <linux/highmem.h>
#include <linux/time.h>
#include <linux/poll.h>
@@ -32,6 +31,7 @@
#include <linux/dnotify.h>
#include <linux/module.h>
#include <net/sock.h>
+#include <net/scm.h>
#include <asm/uaccess.h>
#include <asm/mman.h>
@@ -179,7 +179,32 @@
return err;
}
-asmlinkage int sys_mmap2(void) {return 0;}
+asmlinkage unsigned long
+sys32_mmap2(unsigned long addr, size_t len, unsigned long prot,
+ unsigned long flags, unsigned long fd, unsigned long pgoff)
+{
+ struct file * file = NULL;
+ unsigned long error;
+
+ error = -EINVAL;
+ if (!(flags & MAP_ANONYMOUS)) {
+ error = -EBADF;
+ file = fget(fd);
+ if (!file)
+ goto out;
+ }
+ flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+
+ down_write(¤t->mm->mmap_sem);
+ error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
+ up_write(¤t->mm->mmap_sem);
+ if (file)
+ fput(file);
+
+out:
+ return error;
+}
+
asmlinkage long sys_truncate(const char * path, unsigned long length);
@@ -241,15 +266,15 @@
if (argv != NULL) {
for (;;) {
- u32 p;
- /* egcs is stupid */
- if (!access_ok(VERIFY_READ, argv, sizeof (u32)))
- return -EFAULT;
- __get_user(p,argv);
+ u32 p; int error;
+
+ error = get_user(p,argv);
+ if (error)
+ return error;
if (!p)
break;
argv++;
- if(++i > max)
+ if (++i > max)
return -E2BIG;
}
}
@@ -309,7 +334,6 @@
}
err = copy_from_user(kaddr + offset, (char *)A(str),
bytes_to_copy);
- flush_page_to_ram(page);
kunmap(page);
if (err)
@@ -416,26 +440,22 @@
#else
static int nargs(unsigned int arg, char **ap)
{
- char *ptr;
- int n, ret;
+ unsigned int addr;
+ int n, err;
if (!arg)
return 0;
n = 0;
do {
- /* egcs is stupid */
- if (!access_ok(VERIFY_READ, arg, sizeof (unsigned int)))
- return -EFAULT;
- if (IS_ERR(ret = __get_user((long)ptr,(int *)A(arg))))
- return ret;
- if (ap) /* no access_ok needed, we allocated */
- if (IS_ERR(ret = __put_user(ptr, ap++)))
- return ret;
+ err = get_user(addr, (unsigned int *)A(arg));
+ if (err)
+ return err;
+ if (ap)
+ *ap++ = (char *) A(addr);
arg += sizeof(unsigned int);
n++;
- } while (ptr);
-
+ } while (addr);
return n - 1;
}
@@ -451,19 +471,20 @@
char * filename;
na = nargs(argv, NULL);
- if (IS_ERR(na))
- return(na);
+ if (na < 0)
+ return na;
ne = nargs(envp, NULL);
- if (IS_ERR(ne))
- return(ne);
+ if (ne < 0)
+ return ne;
len = (na + ne + 2) * sizeof(*av);
+
/*
* kmalloc won't work because the `sys_exec' code will attempt
* to do a `get_user' on the arg list and `get_user' will fail
* on a kernel address (simplifies `get_user'). Instead we
* do an mmap to get a user address. Note that since a successful
* `execve' frees all current memory we only have to do an
- * `munmap' if the `execve' failes.
+ * `munmap' if the `execve' fails.
*/
down_write(¤t->mm->mmap_sem);
av = (char **) do_mmap_pgoff(0, 0, len, PROT_READ | PROT_WRITE,
@@ -473,13 +494,15 @@
if (IS_ERR(av))
return (long) av;
ae = av + na + 1;
- if (IS_ERR(r = __put_user(0, (av + na))))
- goto out;
- if (IS_ERR(r = __put_user(0, (ae + ne))))
+ r = __put_user(0, (av + na));
+ r |= __put_user(0, (ae + ne));
+ if (r)
goto out;
- if (IS_ERR(r = nargs(argv, av)))
+ r = nargs(argv, av);
+ if (r < 0)
goto out;
- if (IS_ERR(r = nargs(envp, ae)))
+ r = nargs(envp, ae);
+ if (r < 0)
goto out;
filename = getname((char *) (long)regs.regs[4]);
r = PTR_ERR(filename);
@@ -488,10 +511,10 @@
r = do_execve(filename, av, ae, ®s);
putname(filename);
- if (IS_ERR(r))
+ if (r)
out:
sys_munmap((unsigned long)av, len);
- return(r);
+ return r ;
}
#endif
@@ -586,7 +609,10 @@
{
int err;
- err = put_user (r->ru_utime.tv_sec, &ru->ru_utime.tv_sec);
+ if (verify_area(VERIFY_WRITE, ru, sizeof *ru))
+ return -EFAULT;
+
+ err = __put_user (r->ru_utime.tv_sec, &ru->ru_utime.tv_sec);
err |= __put_user (r->ru_utime.tv_usec, &ru->ru_utime.tv_usec);
err |= __put_user (r->ru_stime.tv_sec, &ru->ru_stime.tv_sec);
err |= __put_user (r->ru_stime.tv_usec, &ru->ru_stime.tv_usec);
@@ -604,6 +630,7 @@
err |= __put_user (r->ru_nsignals, &ru->ru_nsignals);
err |= __put_user (r->ru_nvcsw, &ru->ru_nvcsw);
err |= __put_user (r->ru_nivcsw, &ru->ru_nivcsw);
+
return err;
}
@@ -635,6 +662,52 @@
return sys32_wait4(pid, stat_addr, options, NULL);
}
+struct sysinfo32 {
+ s32 uptime;
+ u32 loads[3];
+ u32 totalram;
+ u32 freeram;
+ u32 sharedram;
+ u32 bufferram;
+ u32 totalswap;
+ u32 freeswap;
+ u16 procs;
+ u32 totalhigh;
+ u32 freehigh;
+ u32 mem_unit;
+ char _f[8];
+};
+
+extern asmlinkage int sys_sysinfo(struct sysinfo *info);
+
+asmlinkage int sys32_sysinfo(struct sysinfo32 *info)
+{
+ struct sysinfo s;
+ int ret, err;
+ mm_segment_t old_fs = get_fs ();
+
+ set_fs (KERNEL_DS);
+ ret = sys_sysinfo(&s);
+ set_fs (old_fs);
+ err = put_user (s.uptime, &info->uptime);
+ err |= __put_user (s.loads[0], &info->loads[0]);
+ err |= __put_user (s.loads[1], &info->loads[1]);
+ err |= __put_user (s.loads[2], &info->loads[2]);
+ err |= __put_user (s.totalram, &info->totalram);
+ err |= __put_user (s.freeram, &info->freeram);
+ err |= __put_user (s.sharedram, &info->sharedram);
+ err |= __put_user (s.bufferram, &info->bufferram);
+ err |= __put_user (s.totalswap, &info->totalswap);
+ err |= __put_user (s.freeswap, &info->freeswap);
+ err |= __put_user (s.procs, &info->procs);
+ err |= __put_user (s.totalhigh, &info->totalhigh);
+ err |= __put_user (s.freehigh, &info->freehigh);
+ err |= __put_user (s.mem_unit, &info->mem_unit);
+ if (err)
+ return -EFAULT;
+ return ret;
+}
+
#define RLIM_INFINITY32 0x7fffffff
#define RESOURCE32(x) ((x > RLIM_INFINITY32) ? RLIM_INFINITY32 : x)
@@ -725,12 +798,17 @@
int ret;
struct statfs s;
mm_segment_t old_fs = get_fs();
-
- set_fs (KERNEL_DS);
- ret = sys_statfs((const char *)path, &s);
- set_fs (old_fs);
- if (put_statfs(buf, &s))
- return -EFAULT;
+ char *pth;
+
+ pth = getname (path);
+ ret = PTR_ERR(pth);
+ if (!IS_ERR(pth)) {
+ set_fs (KERNEL_DS);
+ ret = sys_statfs((const char *)path, &s);
+ set_fs (old_fs);
+ if (!ret && put_statfs(buf, &s))
+ return -EFAULT;
+ }
return ret;
}
@@ -751,6 +829,38 @@
return ret;
}
+#ifdef __MIPSEB__
+asmlinkage long sys32_truncate64(const char * path, unsigned long __dummy,
+ int length_hi, int length_lo)
+#endif
+#ifdef __MIPSEL__
+asmlinkage long sys32_truncate64(const char * path, unsigned long __dummy,
+ int length_lo, int length_hi)
+#endif
+{
+ loff_t length;
+
+ length = ((unsigned long) length_hi << 32) | (unsigned int) length_lo;
+
+ return sys_truncate(path, length);
+}
+
+#ifdef __MIPSEB__
+asmlinkage long sys32_ftruncate64(unsigned int fd, unsigned long __dummy,
+ int length_hi, int length_lo)
+#endif
+#ifdef __MIPSEL__
+asmlinkage long sys32_ftruncate64(unsigned int fd, unsigned long __dummy,
+ int length_lo, int length_hi)
+#endif
+{
+ loff_t length;
+
+ length = ((unsigned long) length_hi << 32) | (unsigned int) length_lo;
+
+ return sys_ftruncate(fd, length);
+}
+
extern asmlinkage int
sys_getrusage(int who, struct rusage *ru);
@@ -843,25 +953,6 @@
return -EFAULT;
return 0;
-
-}
-asmlinkage unsigned long
-sys32_alarm(unsigned int seconds)
-{
- struct itimerval it_new, it_old;
- unsigned int oldalarm;
-
- it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0;
- it_new.it_value.tv_sec = seconds;
- it_new.it_value.tv_usec = 0;
- do_setitimer(ITIMER_REAL, &it_new, &it_old);
- oldalarm = it_old.it_value.tv_sec;
- /* ehhh.. We can't return 0 if we have an alarm pending.. */
- /* And we'd better return too much than too little anyway */
- if (it_old.it_value.tv_usec)
- oldalarm++;
-
- return oldalarm;
}
/* Translations due to time_t size differences. Which affects all
@@ -1572,6 +1663,15 @@
unsigned short sem_nsems; /* no. of semaphores in array */
};
+struct semid64_ds32 {
+ struct ipc64_perm32 sem_perm;
+ __kernel_time_t32 sem_otime;
+ __kernel_time_t32 sem_ctime;
+ unsigned int sem_nsems;
+ unsigned int __unused1;
+ unsigned int __unused2;
+};
+
struct msqid_ds32
{
struct ipc_perm32 msg_perm;
@@ -1617,6 +1717,19 @@
unsigned short shm_nattch;
};
+struct shmid64_ds32 {
+ struct ipc64_perm32 shm_perm;
+ __kernel_size_t32 shm_segsz;
+ __kernel_time_t32 shm_atime;
+ __kernel_time_t32 shm_dtime;
+ __kernel_time_t32 shm_ctime;
+ __kernel_pid_t32 shm_cpid;
+ __kernel_pid_t32 shm_lpid;
+ unsigned int shm_nattch;
+ unsigned int __unused1;
+ unsigned int __unused2;
+};
+
struct ipc_kludge32 {
u32 msgp;
s32 msgtyp;
@@ -1629,7 +1742,6 @@
u32 pad;
int err, err2;
struct semid64_ds s;
- struct semid_ds32 *usp;
mm_segment_t old_fs;
if (!uptr)
@@ -1642,7 +1754,6 @@
else
fourth.__pad = (void *)A(pad);
switch (third & ~IPC_64) {
-
case IPC_INFO:
case IPC_RMID:
case IPC_SET:
@@ -1659,34 +1770,60 @@
case IPC_STAT:
case SEM_STAT:
- usp = (struct semid_ds32 *)A(pad);
fourth.__pad = &s;
old_fs = get_fs ();
set_fs (KERNEL_DS);
err = sys_semctl (first, second, third, fourth);
set_fs (old_fs);
- err2 = put_user(s.sem_perm.key, &usp->sem_perm.key);
- err2 |= __put_user(s.sem_perm.uid, &usp->sem_perm.uid);
- err2 |= __put_user(s.sem_perm.gid, &usp->sem_perm.gid);
- err2 |= __put_user(s.sem_perm.cuid,
- &usp->sem_perm.cuid);
- err2 |= __put_user (s.sem_perm.cgid,
- &usp->sem_perm.cgid);
- err2 |= __put_user (s.sem_perm.mode,
- &usp->sem_perm.mode);
- err2 |= __put_user (s.sem_perm.seq, &usp->sem_perm.seq);
- err2 |= __put_user (s.sem_otime, &usp->sem_otime);
- err2 |= __put_user (s.sem_ctime, &usp->sem_ctime);
- err2 |= __put_user (s.sem_nsems, &usp->sem_nsems);
+
+ if (third & IPC_64) {
+ struct semid64_ds32 *usp64 = (struct semid64_ds32 *) A(pad);
+
+ if (!access_ok(VERIFY_WRITE, usp64, sizeof(*usp64))) {
+ err = -EFAULT;
+ break;
+ }
+ err2 = __put_user(s.sem_perm.key, &usp64->sem_perm.key);
+ err2 |= __put_user(s.sem_perm.uid, &usp64->sem_perm.uid);
+ err2 |= __put_user(s.sem_perm.gid, &usp64->sem_perm.gid);
+ err2 |= __put_user(s.sem_perm.cuid, &usp64->sem_perm.cuid);
+ err2 |= __put_user(s.sem_perm.cgid, &usp64->sem_perm.cgid);
+ err2 |= __put_user(s.sem_perm.mode, &usp64->sem_perm.mode);
+ err2 |= __put_user(s.sem_perm.seq, &usp64->sem_perm.seq);
+ err2 |= __put_user(s.sem_otime, &usp64->sem_otime);
+ err2 |= __put_user(s.sem_ctime, &usp64->sem_ctime);
+ err2 |= __put_user(s.sem_nsems, &usp64->sem_nsems);
+ } else {
+ struct semid_ds32 *usp32 = (struct semid_ds32 *) A(pad);
+
+ if (!access_ok(VERIFY_WRITE, usp32, sizeof(*usp32))) {
+ err = -EFAULT;
+ break;
+ }
+ err2 = __put_user(s.sem_perm.key, &usp32->sem_perm.key);
+ err2 |= __put_user(s.sem_perm.uid, &usp32->sem_perm.uid);
+ err2 |= __put_user(s.sem_perm.gid, &usp32->sem_perm.gid);
+ err2 |= __put_user(s.sem_perm.cuid, &usp32->sem_perm.cuid);
+ err2 |= __put_user(s.sem_perm.cgid, &usp32->sem_perm.cgid);
+ err2 |= __put_user(s.sem_perm.mode, &usp32->sem_perm.mode);
+ err2 |= __put_user(s.sem_perm.seq, &usp32->sem_perm.seq);
+ err2 |= __put_user(s.sem_otime, &usp32->sem_otime);
+ err2 |= __put_user(s.sem_ctime, &usp32->sem_ctime);
+ err2 |= __put_user(s.sem_nsems, &usp32->sem_nsems);
+ }
if (err2)
err = -EFAULT;
break;
+ default:
+ err = - EINVAL;
+ break;
}
return err;
}
+static int
do_sys32_msgsnd (int first, int second, int third, void *uptr)
{
struct msgbuf32 *up = (struct msgbuf32 *)uptr;
@@ -1886,7 +2023,8 @@
int err = -EFAULT, err2;
struct shmid_ds s;
struct shmid64_ds s64;
- struct shmid_ds32 *up = (struct shmid_ds32 *)uptr;
+ struct shmid_ds32 *up32 = (struct shmid_ds32 *)uptr;
+ struct shmid64_ds32 *up64 = (struct shmid64_ds32 *)uptr;
mm_segment_t old_fs;
struct shm_info32 {
int used_ids;
@@ -1895,18 +2033,24 @@
} *uip = (struct shm_info32 *)uptr;
struct shm_info si;
- switch (second) {
-
+ switch (second & ~IPC_64) {
case IPC_INFO:
+ second = IPC_INFO; /* So that we don't have to translate it */
case IPC_RMID:
case SHM_LOCK:
case SHM_UNLOCK:
err = sys_shmctl (first, second, (struct shmid_ds *)uptr);
break;
case IPC_SET:
- err = get_user (s.shm_perm.uid, &up->shm_perm.uid);
- err |= __get_user (s.shm_perm.gid, &up->shm_perm.gid);
- err |= __get_user (s.shm_perm.mode, &up->shm_perm.mode);
+ if (second & IPC_64) {
+ err = get_user(s.shm_perm.uid, &up64->shm_perm.uid);
+ err |= get_user(s.shm_perm.gid, &up64->shm_perm.gid);
+ err |= get_user(s.shm_perm.mode, &up64->shm_perm.mode);
+ } else {
+ err = get_user(s.shm_perm.uid, &up32->shm_perm.uid);
+ err |= get_user(s.shm_perm.gid, &up32->shm_perm.gid);
+ err |= get_user(s.shm_perm.mode, &up32->shm_perm.mode);
+ }
if (err)
break;
old_fs = get_fs ();
@@ -1923,23 +2067,45 @@
set_fs (old_fs);
if (err < 0)
break;
- err2 = put_user (s64.shm_perm.key, &up->shm_perm.key);
- err2 |= __put_user (s64.shm_perm.uid, &up->shm_perm.uid);
- err2 |= __put_user (s64.shm_perm.gid, &up->shm_perm.gid);
- err2 |= __put_user (s64.shm_perm.cuid,
- &up->shm_perm.cuid);
- err2 |= __put_user (s64.shm_perm.cgid,
- &up->shm_perm.cgid);
- err2 |= __put_user (s64.shm_perm.mode,
- &up->shm_perm.mode);
- err2 |= __put_user (s64.shm_perm.seq, &up->shm_perm.seq);
- err2 |= __put_user (s64.shm_atime, &up->shm_atime);
- err2 |= __put_user (s64.shm_dtime, &up->shm_dtime);
- err2 |= __put_user (s64.shm_ctime, &up->shm_ctime);
- err2 |= __put_user (s64.shm_segsz, &up->shm_segsz);
- err2 |= __put_user (s64.shm_nattch, &up->shm_nattch);
- err2 |= __put_user (s64.shm_cpid, &up->shm_cpid);
- err2 |= __put_user (s64.shm_lpid, &up->shm_lpid);
+ if (second & IPC_64) {
+ if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) {
+ err = -EFAULT;
+ break;
+ }
+ err2 = __put_user(s64.shm_perm.key, &up64->shm_perm.key);
+ err2 |= __put_user(s64.shm_perm.uid, &up64->shm_perm.uid);
+ err2 |= __put_user(s64.shm_perm.gid, &up64->shm_perm.gid);
+ err2 |= __put_user(s64.shm_perm.cuid, &up64->shm_perm.cuid);
+ err2 |= __put_user(s64.shm_perm.cgid, &up64->shm_perm.cgid);
+ err2 |= __put_user(s64.shm_perm.mode, &up64->shm_perm.mode);
+ err2 |= __put_user(s64.shm_perm.seq, &up64->shm_perm.seq);
+ err2 |= __put_user(s64.shm_atime, &up64->shm_atime);
+ err2 |= __put_user(s64.shm_dtime, &up64->shm_dtime);
+ err2 |= __put_user(s64.shm_ctime, &up64->shm_ctime);
+ err2 |= __put_user(s64.shm_segsz, &up64->shm_segsz);
+ err2 |= __put_user(s64.shm_nattch, &up64->shm_nattch);
+ err2 |= __put_user(s64.shm_cpid, &up64->shm_cpid);
+ err2 |= __put_user(s64.shm_lpid, &up64->shm_lpid);
+ } else {
+ if (!access_ok(VERIFY_WRITE, up32, sizeof(*up32))) {
+ err = -EFAULT;
+ break;
+ }
+ err2 = __put_user(s64.shm_perm.key, &up32->shm_perm.key);
+ err2 |= __put_user(s64.shm_perm.uid, &up32->shm_perm.uid);
+ err2 |= __put_user(s64.shm_perm.gid, &up32->shm_perm.gid);
+ err2 |= __put_user(s64.shm_perm.cuid, &up32->shm_perm.cuid);
+ err2 |= __put_user(s64.shm_perm.cgid, &up32->shm_perm.cgid);
+ err2 |= __put_user(s64.shm_perm.mode, &up32->shm_perm.mode);
+ err2 |= __put_user(s64.shm_perm.seq, &up32->shm_perm.seq);
+ err2 |= __put_user(s64.shm_atime, &up32->shm_atime);
+ err2 |= __put_user(s64.shm_dtime, &up32->shm_dtime);
+ err2 |= __put_user(s64.shm_ctime, &up32->shm_ctime);
+ err2 |= __put_user(s64.shm_segsz, &up32->shm_segsz);
+ err2 |= __put_user(s64.shm_nattch, &up32->shm_nattch);
+ err2 |= __put_user(s64.shm_cpid, &up32->shm_cpid);
+ err2 |= __put_user(s64.shm_lpid, &up32->shm_lpid);
+ }
if (err2)
err = -EFAULT;
break;
@@ -1963,7 +2129,11 @@
err = -EFAULT;
break;
+ default:
+ err = -ENOSYS;
+ break;
}
+
return err;
}
@@ -2037,83 +2207,55 @@
unsigned int __unused[4];
};
-asmlinkage long sys32_sysctl(struct sysctl_args32 *uargs32)
-{
- struct __sysctl_args kargs;
- struct sysctl_args32 kargs32;
- mm_segment_t old_fs;
- int name[CTL_MAXNAME];
- size_t oldlen[1];
- int err, ret;
-
- ret = -EFAULT;
-
- memset(&kargs, 0, sizeof (kargs));
-
- err = get_user(kargs32.name, &uargs32->name);
- err |= __get_user(kargs32.nlen, &uargs32->nlen);
- err |= __get_user(kargs32.oldval, &uargs32->oldval);
- err |= __get_user(kargs32.oldlenp, &uargs32->oldlenp);
- err |= __get_user(kargs32.newval, &uargs32->newval);
- err |= __get_user(kargs32.newlen, &uargs32->newlen);
- if (err)
- goto out;
+#ifdef CONFIG_SYSCTL
- if (kargs32.nlen == 0 || kargs32.nlen >= CTL_MAXNAME) {
- ret = -ENOTDIR;
- goto out;
- }
+asmlinkage long sys32_sysctl(struct sysctl_args32 *args)
+{
+ struct sysctl_args32 tmp;
+ int error;
+ size_t oldlen, *oldlenp = NULL;
+ unsigned long addr = (((long)&args->__unused[0]) + 7) & ~7;
- kargs.name = name;
- kargs.nlen = kargs32.nlen;
- if (copy_from_user(kargs.name, (int *)A(kargs32.name),
- kargs32.nlen * sizeof(name) / sizeof(name[0])))
- goto out;
+ if (copy_from_user(&tmp, args, sizeof(tmp)))
+ return -EFAULT;
- if (kargs32.oldval) {
- if (!kargs32.oldlenp || get_user(oldlen[0],
- (int *)A(kargs32.oldlenp)))
+ if (tmp.oldval && tmp.oldlenp) {
+ /* Duh, this is ugly and might not work if sysctl_args
+ is in read-only memory, but do_sysctl does indirectly
+ a lot of uaccess in both directions and we'd have to
+ basically copy the whole sysctl.c here, and
+ glibc's __sysctl uses rw memory for the structure
+ anyway. */
+ if (get_user(oldlen, (u32 *)A(tmp.oldlenp)) ||
+ put_user(oldlen, (size_t *)addr))
return -EFAULT;
- kargs.oldlenp = oldlen;
- kargs.oldval = kmalloc(oldlen[0], GFP_KERNEL);
- if (!kargs.oldval) {
- ret = -ENOMEM;
- goto out;
- }
+ oldlenp = (size_t *)addr;
}
- if (kargs32.newval && kargs32.newlen) {
- kargs.newval = kmalloc(kargs32.newlen, GFP_KERNEL);
- if (!kargs.newval) {
- ret = -ENOMEM;
- goto out;
+ lock_kernel();
+ error = do_sysctl((int *)A(tmp.name), tmp.nlen, (void *)A(tmp.oldval),
+ oldlenp, (void *)A(tmp.newval), tmp.newlen);
+ unlock_kernel();
+ if (oldlenp) {
+ if (!error) {
+ if (get_user(oldlen, (size_t *)addr) ||
+ put_user(oldlen, (u32 *)A(tmp.oldlenp)))
+ error = -EFAULT;
}
- if (copy_from_user(kargs.newval, (int *)A(kargs32.newval),
- kargs32.newlen))
- goto out;
+ copy_to_user(args->__unused, tmp.__unused, sizeof(tmp.__unused));
}
+ return error;
+}
- old_fs = get_fs(); set_fs (KERNEL_DS);
- ret = sys_sysctl(&kargs);
- set_fs (old_fs);
-
- if (ret)
- goto out;
+#else /* CONFIG_SYSCTL */
- if (kargs.oldval) {
- if (put_user(oldlen[0], (int *)A(kargs32.oldlenp)) ||
- copy_to_user((int *)A(kargs32.oldval), kargs.oldval,
- oldlen[0]))
- ret = -EFAULT;
- }
-out:
- if (kargs.oldval)
- kfree(kargs.oldval);
- if (kargs.newval)
- kfree(kargs.newval);
- return ret;
+asmlinkage long sys32_sysctl(struct sysctl_args32 *args)
+{
+ return -ENOSYS;
}
+#endif /* CONFIG_SYSCTL */
+
asmlinkage long sys32_newuname(struct new_utsname * name)
{
int ret = 0;
@@ -2219,7 +2361,7 @@
/*
* Declare the 32-bit version of the msghdr
*/
-
+
struct msghdr32 {
unsigned int msg_name; /* Socket name */
int msg_namelen; /* Length of name */
@@ -2230,74 +2372,132 @@
unsigned msg_flags;
};
-static inline int
-shape_msg(struct msghdr *mp, struct msghdr32 *mp32)
+struct cmsghdr32 {
+ __kernel_size_t32 cmsg_len;
+ int cmsg_level;
+ int cmsg_type;
+};
+
+/* Bleech... */
+#define __CMSG32_NXTHDR(ctl, len, cmsg, cmsglen) __cmsg32_nxthdr((ctl),(len),(cmsg),(cmsglen))
+#define CMSG32_NXTHDR(mhdr, cmsg, cmsglen) cmsg32_nxthdr((mhdr), (cmsg), (cmsglen))
+
+#define CMSG32_ALIGN(len) ( ((len)+sizeof(int)-1) & ~(sizeof(int)-1) )
+
+#define CMSG32_DATA(cmsg) ((void *)((char *)(cmsg) + CMSG32_ALIGN(sizeof(struct cmsghdr32))))
+#define CMSG32_SPACE(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + CMSG32_ALIGN(len))
+#define CMSG32_LEN(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + (len))
+
+#define __CMSG32_FIRSTHDR(ctl,len) ((len) >= sizeof(struct cmsghdr32) ? \
+ (struct cmsghdr32 *)(ctl) : \
+ (struct cmsghdr32 *)NULL)
+#define CMSG32_FIRSTHDR(msg) __CMSG32_FIRSTHDR((msg)->msg_control, (msg)->msg_controllen)
+
+__inline__ struct cmsghdr32 *__cmsg32_nxthdr(void *__ctl, __kernel_size_t __size,
+ struct cmsghdr32 *__cmsg, int __cmsg_len)
{
- int ret;
- unsigned int i;
+ struct cmsghdr32 * __ptr;
- if (!access_ok(VERIFY_READ, mp32, sizeof(*mp32)))
- return(-EFAULT);
- ret = __get_user(i, &mp32->msg_name);
- mp->msg_name = (void *)A(i);
- ret |= __get_user(mp->msg_namelen, &mp32->msg_namelen);
- ret |= __get_user(i, &mp32->msg_iov);
- mp->msg_iov = (struct iovec *)A(i);
- ret |= __get_user(mp->msg_iovlen, &mp32->msg_iovlen);
- ret |= __get_user(i, &mp32->msg_control);
- mp->msg_control = (void *)A(i);
- ret |= __get_user(mp->msg_controllen, &mp32->msg_controllen);
- ret |= __get_user(mp->msg_flags, &mp32->msg_flags);
- return(ret ? -EFAULT : 0);
+ __ptr = (struct cmsghdr32 *)(((unsigned char *) __cmsg) +
+ CMSG32_ALIGN(__cmsg_len));
+ if ((unsigned long)((char*)(__ptr+1) - (char *) __ctl) > __size)
+ return NULL;
+
+ return __ptr;
}
-/*
- * Verify & re-shape IA32 iovec. The caller must ensure that the
- * iovec is big enough to hold the re-shaped message iovec.
- *
- * Save time not doing verify_area. copy_*_user will make this work
- * in any case.
- *
- * Don't need to check the total size for overflow (cf net/core/iovec.c),
- * 32-bit sizes can't overflow a 64-bit count.
- */
+__inline__ struct cmsghdr32 *cmsg32_nxthdr (struct msghdr *__msg,
+ struct cmsghdr32 *__cmsg,
+ int __cmsg_len)
+{
+ return __cmsg32_nxthdr(__msg->msg_control, __msg->msg_controllen,
+ __cmsg, __cmsg_len);
+}
-static inline int
-verify_iovec32(struct msghdr *m, struct iovec *iov, char *address, int mode)
+static inline int iov_from_user32_to_kern(struct iovec *kiov,
+ struct iovec32 *uiov32,
+ int niov)
{
- int size, err, ct;
- struct iovec32 *iov32;
+ int tot_len = 0;
- if(m->msg_namelen)
- {
- if(mode==VERIFY_READ)
- {
- err=move_addr_to_kernel(m->msg_name, m->msg_namelen, address);
- if(err<0)
- goto out;
+ while(niov > 0) {
+ u32 len, buf;
+
+ if(get_user(len, &uiov32->iov_len) ||
+ get_user(buf, &uiov32->iov_base)) {
+ tot_len = -EFAULT;
+ break;
}
+ tot_len += len;
+ kiov->iov_base = (void *)AA(buf);
+ kiov->iov_len = (__kernel_size_t) len;
+ uiov32++;
+ kiov++;
+ niov--;
+ }
+ return tot_len;
+}
- m->msg_name = address;
- } else
- m->msg_name = NULL;
+static inline int msghdr_from_user32_to_kern(struct msghdr *kmsg,
+ struct msghdr32 *umsg)
+{
+ u32 tmp1, tmp2, tmp3;
+ int err;
- err = -EFAULT;
- size = m->msg_iovlen * sizeof(struct iovec32);
- if (copy_from_user(iov, m->msg_iov, size))
- goto out;
- m->msg_iov=iov;
+ err = get_user(tmp1, &umsg->msg_name);
+ err |= __get_user(tmp2, &umsg->msg_iov);
+ err |= __get_user(tmp3, &umsg->msg_control);
+ if (err)
+ return -EFAULT;
- err = 0;
- iov32 = (struct iovec32 *)iov;
- for (ct = m->msg_iovlen; ct-- > 0; ) {
- iov[ct].iov_len = (__kernel_size_t)iov32[ct].iov_len;
- iov[ct].iov_base = (void *) A(iov32[ct].iov_base);
- err += iov[ct].iov_len;
- }
-out:
+ kmsg->msg_name = (void *)AA(tmp1);
+ kmsg->msg_iov = (struct iovec *)AA(tmp2);
+ kmsg->msg_control = (void *)AA(tmp3);
+
+ err = get_user(kmsg->msg_namelen, &umsg->msg_namelen);
+ err |= get_user(kmsg->msg_iovlen, &umsg->msg_iovlen);
+ err |= get_user(kmsg->msg_controllen, &umsg->msg_controllen);
+ err |= get_user(kmsg->msg_flags, &umsg->msg_flags);
+
return err;
}
+/* I've named the args so it is easy to tell whose space the pointers are in. */
+static int verify_iovec32(struct msghdr *kern_msg, struct iovec *kern_iov,
+ char *kern_address, int mode)
+{
+ int tot_len;
+
+ if(kern_msg->msg_namelen) {
+ if(mode==VERIFY_READ) {
+ int err = move_addr_to_kernel(kern_msg->msg_name,
+ kern_msg->msg_namelen,
+ kern_address);
+ if(err < 0)
+ return err;
+ }
+ kern_msg->msg_name = kern_address;
+ } else
+ kern_msg->msg_name = NULL;
+
+ if(kern_msg->msg_iovlen > UIO_FASTIOV) {
+ kern_iov = kmalloc(kern_msg->msg_iovlen * sizeof(struct iovec),
+ GFP_KERNEL);
+ if(!kern_iov)
+ return -ENOMEM;
+ }
+
+ tot_len = iov_from_user32_to_kern(kern_iov,
+ (struct iovec32 *)kern_msg->msg_iov,
+ kern_msg->msg_iovlen);
+ if(tot_len >= 0)
+ kern_msg->msg_iov = kern_iov;
+ else if(kern_msg->msg_iovlen > UIO_FASTIOV)
+ kfree(kern_iov);
+
+ return tot_len;
+}
+
extern __inline__ void
sockfd_put(struct socket *sock)
{
@@ -2305,177 +2505,406 @@
}
/* XXX This really belongs in some header file... -DaveM */
-#define MAX_SOCK_ADDR 128 /* 108 for Unix domain -
+#define MAX_SOCK_ADDR 128 /* 108 for Unix domain -
16 for IP, 16 for IPX,
24 for IPv6,
about 80 for AX.25 */
extern struct socket *sockfd_lookup(int fd, int *err);
-/*
- * BSD sendmsg interface
+/* There is a lot of hair here because the alignment rules (and
+ * thus placement) of cmsg headers and length are different for
+ * 32-bit apps. -DaveM
*/
-
-int sys32_sendmsg(int fd, struct msghdr32 *msg, unsigned flags)
+static int cmsghdr_from_user32_to_kern(struct msghdr *kmsg,
+ unsigned char *stackbuf, int stackbuf_size)
{
- struct socket *sock;
- char address[MAX_SOCK_ADDR];
- struct iovec iovstack[UIO_FASTIOV], *iov = iovstack;
- unsigned char ctl[sizeof(struct cmsghdr) + 20]; /* 20 is size of ipv6_pktinfo */
- unsigned char *ctl_buf = ctl;
- struct msghdr msg_sys;
- int err, ctl_len, iov_size, total_len;
+ struct cmsghdr32 *ucmsg;
+ struct cmsghdr *kcmsg, *kcmsg_base;
+ __kernel_size_t32 ucmlen;
+ __kernel_size_t kcmlen, tmp;
+
+ kcmlen = 0;
+ kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf;
+ ucmsg = CMSG32_FIRSTHDR(kmsg);
+ while(ucmsg != NULL) {
+ if(get_user(ucmlen, &ucmsg->cmsg_len))
+ return -EFAULT;
- err = -EFAULT;
- if (shape_msg(&msg_sys, msg))
- goto out;
+ /* Catch bogons. */
+ if(CMSG32_ALIGN(ucmlen) <
+ CMSG32_ALIGN(sizeof(struct cmsghdr32)))
+ return -ENOBUFS;
+ if((unsigned long)(((char *)ucmsg - (char *)kmsg->msg_control)
+ + ucmlen) > kmsg->msg_controllen)
+ return -EINVAL;
+
+ tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) +
+ CMSG_ALIGN(sizeof(struct cmsghdr)));
+ kcmlen += tmp;
+ ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen);
+ }
+ if(kcmlen == 0)
+ return -EINVAL;
- sock = sockfd_lookup(fd, &err);
- if (!sock)
- goto out;
+ /* The kcmlen holds the 64-bit version of the control length.
+ * It may not be modified as we do not stick it into the kmsg
+ * until we have successfully copied over all of the data
+ * from the user.
+ */
+ if(kcmlen > stackbuf_size)
+ kcmsg_base = kcmsg = kmalloc(kcmlen, GFP_KERNEL);
+ if(kcmsg == NULL)
+ return -ENOBUFS;
+
+ /* Now copy them over neatly. */
+ memset(kcmsg, 0, kcmlen);
+ ucmsg = CMSG32_FIRSTHDR(kmsg);
+ while(ucmsg != NULL) {
+ __get_user(ucmlen, &ucmsg->cmsg_len);
+ tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) +
+ CMSG_ALIGN(sizeof(struct cmsghdr)));
+ kcmsg->cmsg_len = tmp;
+ __get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level);
+ __get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type);
+
+ /* Copy over the data. */
+ if(copy_from_user(CMSG_DATA(kcmsg),
+ CMSG32_DATA(ucmsg),
+ (ucmlen - CMSG32_ALIGN(sizeof(*ucmsg)))))
+ goto out_free_efault;
+
+ /* Advance. */
+ kcmsg = (struct cmsghdr *)((char *)kcmsg + CMSG_ALIGN(tmp));
+ ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen);
+ }
+
+ /* Ok, looks like we made it. Hook it up and return success. */
+ kmsg->msg_control = kcmsg_base;
+ kmsg->msg_controllen = kcmlen;
+ return 0;
- /* do not move before msg_sys is valid */
- err = -EINVAL;
- if (msg_sys.msg_iovlen > UIO_MAXIOV)
- goto out_put;
+out_free_efault:
+ if(kcmsg_base != (struct cmsghdr *)stackbuf)
+ kfree(kcmsg_base);
+ return -EFAULT;
+}
- /* Check whether to allocate the iovec area*/
- err = -ENOMEM;
- iov_size = msg_sys.msg_iovlen * sizeof(struct iovec32);
- if (msg_sys.msg_iovlen > UIO_FASTIOV) {
- iov = sock_kmalloc(sock->sk, iov_size, GFP_KERNEL);
- if (!iov)
- goto out_put;
+static void put_cmsg32(struct msghdr *kmsg, int level, int type,
+ int len, void *data)
+{
+ struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control;
+ struct cmsghdr32 cmhdr;
+ int cmlen = CMSG32_LEN(len);
+
+ if(cm == NULL || kmsg->msg_controllen < sizeof(*cm)) {
+ kmsg->msg_flags |= MSG_CTRUNC;
+ return;
}
- /* This will also move the address data into kernel space */
- err = verify_iovec32(&msg_sys, iov, address, VERIFY_READ);
- if (err < 0)
- goto out_freeiov;
- total_len = err;
+ if(kmsg->msg_controllen < cmlen) {
+ kmsg->msg_flags |= MSG_CTRUNC;
+ cmlen = kmsg->msg_controllen;
+ }
+ cmhdr.cmsg_level = level;
+ cmhdr.cmsg_type = type;
+ cmhdr.cmsg_len = cmlen;
- err = -ENOBUFS;
+ if(copy_to_user(cm, &cmhdr, sizeof cmhdr))
+ return;
+ if(copy_to_user(CMSG32_DATA(cm), data, cmlen - sizeof(struct cmsghdr32)))
+ return;
+ cmlen = CMSG32_SPACE(len);
+ kmsg->msg_control += cmlen;
+ kmsg->msg_controllen -= cmlen;
+}
+
+static void scm_detach_fds32(struct msghdr *kmsg, struct scm_cookie *scm)
+{
+ struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control;
+ int fdmax = (kmsg->msg_controllen - sizeof(struct cmsghdr32)) / sizeof(int);
+ int fdnum = scm->fp->count;
+ struct file **fp = scm->fp->fp;
+ int *cmfptr;
+ int err = 0, i;
+
+ if (fdnum < fdmax)
+ fdmax = fdnum;
+
+ for (i = 0, cmfptr = (int *) CMSG32_DATA(cm); i < fdmax; i++, cmfptr++) {
+ int new_fd;
+ err = get_unused_fd();
+ if (err < 0)
+ break;
+ new_fd = err;
+ err = put_user(new_fd, cmfptr);
+ if (err) {
+ put_unused_fd(new_fd);
+ break;
+ }
+ /* Bump the usage count and install the file. */
+ get_file(fp[i]);
+ fd_install(new_fd, fp[i]);
+ }
- if (msg_sys.msg_controllen > INT_MAX)
- goto out_freeiov;
- ctl_len = msg_sys.msg_controllen;
- if (ctl_len)
- {
- if (ctl_len > sizeof(ctl))
- {
- err = -ENOBUFS;
- ctl_buf = sock_kmalloc(sock->sk, ctl_len, GFP_KERNEL);
- if (ctl_buf == NULL)
- goto out_freeiov;
+ if (i > 0) {
+ int cmlen = CMSG32_LEN(i * sizeof(int));
+ if (!err)
+ err = put_user(SOL_SOCKET, &cm->cmsg_level);
+ if (!err)
+ err = put_user(SCM_RIGHTS, &cm->cmsg_type);
+ if (!err)
+ err = put_user(cmlen, &cm->cmsg_len);
+ if (!err) {
+ cmlen = CMSG32_SPACE(i * sizeof(int));
+ kmsg->msg_control += cmlen;
+ kmsg->msg_controllen -= cmlen;
}
- err = -EFAULT;
- if (copy_from_user(ctl_buf, msg_sys.msg_control, ctl_len))
- goto out_freectl;
- msg_sys.msg_control = ctl_buf;
- }
- msg_sys.msg_flags = flags;
-
- if (sock->file->f_flags & O_NONBLOCK)
- msg_sys.msg_flags |= MSG_DONTWAIT;
- err = sock_sendmsg(sock, &msg_sys, total_len);
-
-out_freectl:
- if (ctl_buf != ctl)
- sock_kfree_s(sock->sk, ctl_buf, ctl_len);
-out_freeiov:
- if (iov != iovstack)
- sock_kfree_s(sock->sk, iov, iov_size);
-out_put:
- sockfd_put(sock);
-out:
- return err;
+ }
+ if (i < fdnum)
+ kmsg->msg_flags |= MSG_CTRUNC;
+
+ /*
+ * All of the files that fit in the message have had their
+ * usage counts incremented, so we just free the list.
+ */
+ __scm_destroy(scm);
}
-/*
- * BSD recvmsg interface
+/* In these cases we (currently) can just copy to data over verbatim
+ * because all CMSGs created by the kernel have well defined types which
+ * have the same layout in both the 32-bit and 64-bit API. One must add
+ * some special cased conversions here if we start sending control messages
+ * with incompatible types.
+ *
+ * SCM_RIGHTS and SCM_CREDENTIALS are done by hand in recvmsg32 right after
+ * we do our work. The remaining cases are:
+ *
+ * SOL_IP IP_PKTINFO struct in_pktinfo 32-bit clean
+ * IP_TTL int 32-bit clean
+ * IP_TOS __u8 32-bit clean
+ * IP_RECVOPTS variable length 32-bit clean
+ * IP_RETOPTS variable length 32-bit clean
+ * (these last two are clean because the types are defined
+ * by the IPv4 protocol)
+ * IP_RECVERR struct sock_extended_err +
+ * struct sockaddr_in 32-bit clean
+ * SOL_IPV6 IPV6_RECVERR struct sock_extended_err +
+ * struct sockaddr_in6 32-bit clean
+ * IPV6_PKTINFO struct in6_pktinfo 32-bit clean
+ * IPV6_HOPLIMIT int 32-bit clean
+ * IPV6_FLOWINFO u32 32-bit clean
+ * IPV6_HOPOPTS ipv6 hop exthdr 32-bit clean
+ * IPV6_DSTOPTS ipv6 dst exthdr(s) 32-bit clean
+ * IPV6_RTHDR ipv6 routing exthdr 32-bit clean
+ * IPV6_AUTHHDR ipv6 auth exthdr 32-bit clean
*/
-
-int
-sys32_recvmsg (int fd, struct msghdr32 *msg, unsigned int flags)
+static void cmsg32_recvmsg_fixup(struct msghdr *kmsg, unsigned long orig_cmsg_uptr)
{
- struct socket *sock;
- struct iovec iovstack[UIO_FASTIOV];
- struct iovec *iov=iovstack;
- struct msghdr msg_sys;
- unsigned long cmsg_ptr;
- int err, iov_size, total_len, len;
+ unsigned char *workbuf, *wp;
+ unsigned long bufsz, space_avail;
+ struct cmsghdr *ucmsg;
+
+ bufsz = ((unsigned long)kmsg->msg_control) - orig_cmsg_uptr;
+ space_avail = kmsg->msg_controllen + bufsz;
+ wp = workbuf = kmalloc(bufsz, GFP_KERNEL);
+ if(workbuf == NULL)
+ goto fail;
+
+ /* To make this more sane we assume the kernel sends back properly
+ * formatted control messages. Because of how the kernel will truncate
+ * the cmsg_len for MSG_TRUNC cases, we need not check that case either.
+ */
+ ucmsg = (struct cmsghdr *) orig_cmsg_uptr;
+ while(((unsigned long)ucmsg) <=
+ (((unsigned long)kmsg->msg_control) - sizeof(struct cmsghdr))) {
+ struct cmsghdr32 *kcmsg32 = (struct cmsghdr32 *) wp;
+ int clen64, clen32;
+
+ /* UCMSG is the 64-bit format CMSG entry in user-space.
+ * KCMSG32 is within the kernel space temporary buffer
+ * we use to convert into a 32-bit style CMSG.
+ */
+ __get_user(kcmsg32->cmsg_len, &ucmsg->cmsg_len);
+ __get_user(kcmsg32->cmsg_level, &ucmsg->cmsg_level);
+ __get_user(kcmsg32->cmsg_type, &ucmsg->cmsg_type);
+
+ clen64 = kcmsg32->cmsg_len;
+ copy_from_user(CMSG32_DATA(kcmsg32), CMSG_DATA(ucmsg),
+ clen64 - CMSG_ALIGN(sizeof(*ucmsg)));
+ clen32 = ((clen64 - CMSG_ALIGN(sizeof(*ucmsg))) +
+ CMSG32_ALIGN(sizeof(struct cmsghdr32)));
+ kcmsg32->cmsg_len = clen32;
+
+ ucmsg = (struct cmsghdr *) (((char *)ucmsg) + CMSG_ALIGN(clen64));
+ wp = (((char *)kcmsg32) + CMSG32_ALIGN(clen32));
+ }
+
+ /* Copy back fixed up data, and adjust pointers. */
+ bufsz = (wp - workbuf);
+ copy_to_user((void *)orig_cmsg_uptr, workbuf, bufsz);
+
+ kmsg->msg_control = (struct cmsghdr *)
+ (((char *)orig_cmsg_uptr) + bufsz);
+ kmsg->msg_controllen = space_avail - bufsz;
- /* kernel mode address */
- char addr[MAX_SOCK_ADDR];
+ kfree(workbuf);
+ return;
- /* user mode address pointers */
- struct sockaddr *uaddr;
- int *uaddr_len;
+fail:
+ /* If we leave the 64-bit format CMSG chunks in there,
+ * the application could get confused and crash. So to
+ * ensure greater recovery, we report no CMSGs.
+ */
+ kmsg->msg_controllen += bufsz;
+ kmsg->msg_control = (void *) orig_cmsg_uptr;
+}
- err=-EFAULT;
- if (shape_msg(&msg_sys, msg))
- goto out;
+asmlinkage int sys32_sendmsg(int fd, struct msghdr32 *user_msg, unsigned user_flags)
+{
+ struct socket *sock;
+ char address[MAX_SOCK_ADDR];
+ struct iovec iov[UIO_FASTIOV];
+ unsigned char ctl[sizeof(struct cmsghdr) + 20];
+ unsigned char *ctl_buf = ctl;
+ struct msghdr kern_msg;
+ int err, total_len;
- sock = sockfd_lookup(fd, &err);
- if (!sock)
+ if(msghdr_from_user32_to_kern(&kern_msg, user_msg))
+ return -EFAULT;
+ if(kern_msg.msg_iovlen > UIO_MAXIOV)
+ return -EINVAL;
+ err = verify_iovec32(&kern_msg, iov, address, VERIFY_READ);
+ if (err < 0)
goto out;
+ total_len = err;
- err = -EINVAL;
- if (msg_sys.msg_iovlen > UIO_MAXIOV)
- goto out_put;
+ if(kern_msg.msg_controllen) {
+ err = cmsghdr_from_user32_to_kern(&kern_msg, ctl, sizeof(ctl));
+ if(err)
+ goto out_freeiov;
+ ctl_buf = kern_msg.msg_control;
+ }
+ kern_msg.msg_flags = user_flags;
- /* Check whether to allocate the iovec area*/
- err = -ENOMEM;
- iov_size = msg_sys.msg_iovlen * sizeof(struct iovec);
- if (msg_sys.msg_iovlen > UIO_FASTIOV) {
- iov = sock_kmalloc(sock->sk, iov_size, GFP_KERNEL);
- if (!iov)
- goto out_put;
+ sock = sockfd_lookup(fd, &err);
+ if (sock != NULL) {
+ if (sock->file->f_flags & O_NONBLOCK)
+ kern_msg.msg_flags |= MSG_DONTWAIT;
+ err = sock_sendmsg(sock, &kern_msg, total_len);
+ sockfd_put(sock);
}
- /*
- * Save the user-mode address (verify_iovec will change the
- * kernel msghdr to use the kernel address space)
- */
+ /* N.B. Use kfree here, as kern_msg.msg_controllen might change? */
+ if(ctl_buf != ctl)
+ kfree(ctl_buf);
+out_freeiov:
+ if(kern_msg.msg_iov != iov)
+ kfree(kern_msg.msg_iov);
+out:
+ return err;
+}
- uaddr = msg_sys.msg_name;
- uaddr_len = &msg->msg_namelen;
- err = verify_iovec32(&msg_sys, iov, addr, VERIFY_WRITE);
- if (err < 0)
- goto out_freeiov;
- total_len=err;
+asmlinkage int sys32_recvmsg(int fd, struct msghdr32 *user_msg, unsigned int user_flags)
+{
+ struct iovec iovstack[UIO_FASTIOV];
+ struct msghdr kern_msg;
+ char addr[MAX_SOCK_ADDR];
+ struct socket *sock;
+ struct iovec *iov = iovstack;
+ struct sockaddr *uaddr;
+ int *uaddr_len;
+ unsigned long cmsg_ptr;
+ int err, total_len, len = 0;
- cmsg_ptr = (unsigned long)msg_sys.msg_control;
- msg_sys.msg_flags = 0;
+ if(msghdr_from_user32_to_kern(&kern_msg, user_msg))
+ return -EFAULT;
+ if(kern_msg.msg_iovlen > UIO_MAXIOV)
+ return -EINVAL;
- if (sock->file->f_flags & O_NONBLOCK)
- flags |= MSG_DONTWAIT;
- err = sock_recvmsg(sock, &msg_sys, total_len, flags);
+ uaddr = kern_msg.msg_name;
+ uaddr_len = &user_msg->msg_namelen;
+ err = verify_iovec32(&kern_msg, iov, addr, VERIFY_WRITE);
if (err < 0)
- goto out_freeiov;
- len = err;
+ goto out;
+ total_len = err;
- if (uaddr != NULL) {
- err = move_addr_to_user(addr, msg_sys.msg_namelen, uaddr, uaddr_len);
- if (err < 0)
- goto out_freeiov;
+ cmsg_ptr = (unsigned long) kern_msg.msg_control;
+ kern_msg.msg_flags = 0;
+
+ sock = sockfd_lookup(fd, &err);
+ if (sock != NULL) {
+ struct scm_cookie scm;
+
+ if (sock->file->f_flags & O_NONBLOCK)
+ user_flags |= MSG_DONTWAIT;
+ memset(&scm, 0, sizeof(scm));
+ err = sock->ops->recvmsg(sock, &kern_msg, total_len,
+ user_flags, &scm);
+ if(err >= 0) {
+ len = err;
+ if(!kern_msg.msg_control) {
+ if(sock->passcred || scm.fp)
+ kern_msg.msg_flags |= MSG_CTRUNC;
+ if(scm.fp)
+ __scm_destroy(&scm);
+ } else {
+ /* If recvmsg processing itself placed some
+ * control messages into user space, it's is
+ * using 64-bit CMSG processing, so we need
+ * to fix it up before we tack on more stuff.
+ */
+ if((unsigned long) kern_msg.msg_control != cmsg_ptr)
+ cmsg32_recvmsg_fixup(&kern_msg, cmsg_ptr);
+
+ /* Wheee... */
+ if(sock->passcred)
+ put_cmsg32(&kern_msg,
+ SOL_SOCKET, SCM_CREDENTIALS,
+ sizeof(scm.creds), &scm.creds);
+ if(scm.fp != NULL)
+ scm_detach_fds32(&kern_msg, &scm);
+ }
+ }
+ sockfd_put(sock);
}
- err = __put_user(msg_sys.msg_flags, &msg->msg_flags);
- if (err)
- goto out_freeiov;
- err = __put_user((unsigned long)msg_sys.msg_control-cmsg_ptr,
- &msg->msg_controllen);
- if (err)
- goto out_freeiov;
- err = len;
-out_freeiov:
- if (iov != iovstack)
- sock_kfree_s(sock->sk, iov, iov_size);
-out_put:
- sockfd_put(sock);
+ if(uaddr != NULL && kern_msg.msg_namelen && err >= 0)
+ err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len);
+ if(cmsg_ptr != 0 && err >= 0) {
+ unsigned long ucmsg_ptr = ((unsigned long)kern_msg.msg_control);
+ __kernel_size_t32 uclen = (__kernel_size_t32) (ucmsg_ptr - cmsg_ptr);
+ err |= __put_user(uclen, &user_msg->msg_controllen);
+ }
+ if(err >= 0)
+ err = __put_user(kern_msg.msg_flags, &user_msg->msg_flags);
+ if(kern_msg.msg_iov != iov)
+ kfree(kern_msg.msg_iov);
out:
- return err;
+ if(err < 0)
+ return err;
+ return len;
+}
+
+extern asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
+
+asmlinkage int sys32_sendfile(int out_fd, int in_fd, __kernel_off_t32 *offset, s32 count)
+{
+ mm_segment_t old_fs = get_fs();
+ int ret;
+ off_t of;
+
+ if (offset && get_user(of, offset))
+ return -EFAULT;
+
+ set_fs(KERNEL_DS);
+ ret = sys_sendfile(out_fd, in_fd, offset ? &of : NULL, count);
+ set_fs(old_fs);
+
+ if (offset && put_user(of, offset))
+ return -EFAULT;
+
+ return ret;
}
asmlinkage ssize_t sys_readahead(int fd, loff_t offset, size_t count);
@@ -2520,7 +2949,37 @@
return i;
}
-#else
+#else /* CONFIG_MODULES */
+
+asmlinkage unsigned long
+sys32_create_module(const char *name_user, size_t size)
+{
+ return -ENOSYS;
+}
+
+asmlinkage int
+sys32_init_module(const char *name_user, struct module *mod_user)
+{
+ return -ENOSYS;
+}
+
+asmlinkage int
+sys32_delete_module(const char *name_user)
+{
+ return -ENOSYS;
+}
+
+asmlinkage int
+sys32_query_module(const char *name_user, int which, char *buf, size_t bufsize,
+ size_t *ret)
+{
+ /* Let the program know about the new interface. Not that
+ it'll do them much good. */
+ if (which == 0)
+ return 0;
+
+ return -ENOSYS;
+}
asmlinkage long
sys32_get_kernel_syms(struct kernel_sym *table)
@@ -2528,4 +2987,4 @@
return -ENOSYS;
}
-#endif
+#endif /* CONFIG_MODULES */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)