/* * 32 bit compatibility code for System V IPC * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1999 Arun Sharma * Copyright (C) 2000 VA Linux Co * Copyright (C) 2000 Don Dugger * Copyright (C) 2000 Hewlett-Packard Co. * Copyright (C) 2000 David Mosberger-Tang * Copyright (C) 2000 Gerhard Tonn (ton@de.ibm.com) * Copyright (C) 2000-2002 Andi Kleen, SuSE Labs (x86-64 port) * Copyright (C) 2000 Silicon Graphics, Inc. * Copyright (C) 2001 IBM * Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation * Copyright (C) 2004 Arnd Bergmann (arnd@arndb.de) * * This code is collected from the versions for sparc64, mips64, s390x, ia64, * ppc64 and x86_64, all of which are based on the original sparc64 version * by Jakub Jelinek. * */ #include #include #include #include #include #include #include #include #include #include #include "util.h" struct compat_msgbuf { compat_long_t mtype; char mtext[1]; }; struct compat_ipc_kludge { compat_uptr_t msgp; compat_long_t msgtyp; }; int get_compat_ipc64_perm(struct ipc64_perm *to, struct compat_ipc64_perm __user *from) { struct compat_ipc64_perm v; if (copy_from_user(&v, from, sizeof(v))) return -EFAULT; to->uid = v.uid; to->gid = v.gid; to->mode = v.mode; return 0; } int get_compat_ipc_perm(struct ipc64_perm *to, struct compat_ipc_perm __user *from) { struct compat_ipc_perm v; if (copy_from_user(&v, from, sizeof(v))) return -EFAULT; to->uid = v.uid; to->gid = v.gid; to->mode = v.mode; return 0; } void to_compat_ipc64_perm(struct compat_ipc64_perm *to, struct ipc64_perm *from) { to->key = from->key; to->uid = from->uid; to->gid = from->gid; to->cuid = from->cuid; to->cgid = from->cgid; to->mode = from->mode; to->seq = from->seq; } void to_compat_ipc_perm(struct compat_ipc_perm *to, struct ipc64_perm *from) { to->key = from->key; SET_UID(to->uid, from->uid); SET_GID(to->gid, from->gid); SET_UID(to->cuid, from->cuid); SET_GID(to->cgid, from->cgid); to->mode = from->mode; to->seq = from->seq; } static long compat_do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz) { struct compat_msgbuf __user *msgp = dest; size_t msgsz; if (put_user(msg->m_type, &msgp->mtype)) return -EFAULT; msgsz = (bufsz > msg->m_ts) ? msg->m_ts : bufsz; if (store_msg(msgp->mtext, msg, msgsz)) return -EFAULT; return msgsz; } #ifndef COMPAT_SHMLBA #define COMPAT_SHMLBA SHMLBA #endif #ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC COMPAT_SYSCALL_DEFINE6(ipc, u32, call, int, first, int, second, u32, third, compat_uptr_t, ptr, u32, fifth) { int version; u32 pad; version = call >> 16; /* hack for backward compatibility */ call &= 0xffff; switch (call) { case SEMOP: /* struct sembuf is the same on 32 and 64bit :)) */ return sys_semtimedop(first, compat_ptr(ptr), second, NULL); case SEMTIMEDOP: return compat_sys_semtimedop(first, compat_ptr(ptr), second, compat_ptr(fifth)); case SEMGET: return sys_semget(first, second, third); case SEMCTL: if (!ptr) return -EINVAL; if (get_user(pad, (u32 __user *) compat_ptr(ptr))) return -EFAULT; return compat_sys_semctl(first, second, third, pad); case MSGSND: { struct compat_msgbuf __user *up = compat_ptr(ptr); compat_long_t type; if (first < 0 || second < 0) return -EINVAL; if (get_user(type, &up->mtype)) return -EFAULT; return do_msgsnd(first, type, up->mtext, second, third); } case MSGRCV: { void __user *uptr = compat_ptr(ptr); if (first < 0 || second < 0) return -EINVAL; if (!version) { struct compat_ipc_kludge ipck; if (!uptr) return -EINVAL; if (copy_from_user(&ipck, uptr, sizeof(ipck))) return -EFAULT; uptr = compat_ptr(ipck.msgp); fifth = ipck.msgtyp; } return do_msgrcv(first, uptr, second, (s32)fifth, third, compat_do_msg_fill); } case MSGGET: return sys_msgget(first, second); case MSGCTL: return compat_sys_msgctl(first, second, compat_ptr(ptr)); case SHMAT: { int err; unsigned long raddr; if (version == 1) return -EINVAL; err = do_shmat(first, compat_ptr(ptr), second, &raddr, COMPAT_SHMLBA); if (err < 0) return err; return put_user(raddr, (compat_ulong_t *)compat_ptr(third)); } case SHMDT: return sys_shmdt(compat_ptr(ptr)); case SHMGET: return sys_shmget(first, (unsigned)second, third); case SHMCTL: return compat_sys_shmctl(first, second, compat_ptr(ptr)); } return -ENOSYS; } #endif COMPAT_SYSCALL_DEFINE4(msgsnd, int, msqid, compat_uptr_t, msgp, compat_ssize_t, msgsz, int, msgflg) { struct compat_msgbuf __user *up = compat_ptr(msgp); compat_long_t mtype; if (get_user(mtype, &up->mtype)) return -EFAULT; return do_msgsnd(msqid, mtype, up->mtext, (ssize_t)msgsz, msgflg); } COMPAT_SYSCALL_DEFINE5(msgrcv, int, msqid, compat_uptr_t, msgp, compat_ssize_t, msgsz, compat_long_t, msgtyp, int, msgflg) { return do_msgrcv(msqid, compat_ptr(msgp), (ssize_t)msgsz, (long)msgtyp, msgflg, compat_do_msg_fill); } COMPAT_SYSCALL_DEFINE3(shmat, int, shmid, compat_uptr_t, shmaddr, int, shmflg) { unsigned long ret; long err; err = do_shmat(shmid, compat_ptr(shmaddr), shmflg, &ret, COMPAT_SHMLBA); if (err) return err; force_successful_syscall_return(); return (long)ret; } COMPAT_SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsems, unsigned, nsops, const struct compat_timespec __user *, timeout) { struct timespec __user *ts64; if (compat_convert_timespec(&ts64, timeout)) return -EFAULT; return sys_semtimedop(semid, tsems, nsops, ts64); }