diff options
Diffstat (limited to 'kernel/ipc/compat_mq.c')
-rw-r--r-- | kernel/ipc/compat_mq.c | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/kernel/ipc/compat_mq.c b/kernel/ipc/compat_mq.c new file mode 100644 index 000000000..ef6f91cc4 --- /dev/null +++ b/kernel/ipc/compat_mq.c @@ -0,0 +1,138 @@ +/* + * ipc/compat_mq.c + * 32 bit emulation for POSIX message queue system calls + * + * Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author: Arnd Bergmann <arnd@arndb.de> + */ + +#include <linux/compat.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/mqueue.h> +#include <linux/syscalls.h> + +#include <linux/uaccess.h> + +struct compat_mq_attr { + compat_long_t mq_flags; /* message queue flags */ + compat_long_t mq_maxmsg; /* maximum number of messages */ + compat_long_t mq_msgsize; /* maximum message size */ + compat_long_t mq_curmsgs; /* number of messages currently queued */ + compat_long_t __reserved[4]; /* ignored for input, zeroed for output */ +}; + +static inline int get_compat_mq_attr(struct mq_attr *attr, + const struct compat_mq_attr __user *uattr) +{ + if (!access_ok(VERIFY_READ, uattr, sizeof *uattr)) + return -EFAULT; + + return __get_user(attr->mq_flags, &uattr->mq_flags) + | __get_user(attr->mq_maxmsg, &uattr->mq_maxmsg) + | __get_user(attr->mq_msgsize, &uattr->mq_msgsize) + | __get_user(attr->mq_curmsgs, &uattr->mq_curmsgs); +} + +static inline int put_compat_mq_attr(const struct mq_attr *attr, + struct compat_mq_attr __user *uattr) +{ + if (clear_user(uattr, sizeof *uattr)) + return -EFAULT; + + return __put_user(attr->mq_flags, &uattr->mq_flags) + | __put_user(attr->mq_maxmsg, &uattr->mq_maxmsg) + | __put_user(attr->mq_msgsize, &uattr->mq_msgsize) + | __put_user(attr->mq_curmsgs, &uattr->mq_curmsgs); +} + +COMPAT_SYSCALL_DEFINE4(mq_open, const char __user *, u_name, + int, oflag, compat_mode_t, mode, + struct compat_mq_attr __user *, u_attr) +{ + void __user *p = NULL; + if (u_attr && oflag & O_CREAT) { + struct mq_attr attr; + + memset(&attr, 0, sizeof(attr)); + + p = compat_alloc_user_space(sizeof(attr)); + if (get_compat_mq_attr(&attr, u_attr) || + copy_to_user(p, &attr, sizeof(attr))) + return -EFAULT; + } + return sys_mq_open(u_name, oflag, mode, p); +} + +COMPAT_SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, + const char __user *, u_msg_ptr, + compat_size_t, msg_len, unsigned int, msg_prio, + const struct compat_timespec __user *, u_abs_timeout) +{ + struct timespec __user *u_ts; + + if (compat_convert_timespec(&u_ts, u_abs_timeout)) + return -EFAULT; + + return sys_mq_timedsend(mqdes, u_msg_ptr, msg_len, + msg_prio, u_ts); +} + +COMPAT_SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, + char __user *, u_msg_ptr, + compat_size_t, msg_len, unsigned int __user *, u_msg_prio, + const struct compat_timespec __user *, u_abs_timeout) +{ + struct timespec __user *u_ts; + + if (compat_convert_timespec(&u_ts, u_abs_timeout)) + return -EFAULT; + + return sys_mq_timedreceive(mqdes, u_msg_ptr, msg_len, + u_msg_prio, u_ts); +} + +COMPAT_SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes, + const struct compat_sigevent __user *, u_notification) +{ + struct sigevent __user *p = NULL; + if (u_notification) { + struct sigevent n; + p = compat_alloc_user_space(sizeof(*p)); + if (get_compat_sigevent(&n, u_notification)) + return -EFAULT; + if (n.sigev_notify == SIGEV_THREAD) + n.sigev_value.sival_ptr = compat_ptr(n.sigev_value.sival_int); + if (copy_to_user(p, &n, sizeof(*p))) + return -EFAULT; + } + return sys_mq_notify(mqdes, p); +} + +COMPAT_SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes, + const struct compat_mq_attr __user *, u_mqstat, + struct compat_mq_attr __user *, u_omqstat) +{ + struct mq_attr mqstat; + struct mq_attr __user *p = compat_alloc_user_space(2 * sizeof(*p)); + long ret; + + memset(&mqstat, 0, sizeof(mqstat)); + + if (u_mqstat) { + if (get_compat_mq_attr(&mqstat, u_mqstat) || + copy_to_user(p, &mqstat, sizeof(mqstat))) + return -EFAULT; + } + ret = sys_mq_getsetattr(mqdes, + u_mqstat ? p : NULL, + u_omqstat ? p + 1 : NULL); + if (ret) + return ret; + if (u_omqstat) { + if (copy_from_user(&mqstat, p + 1, sizeof(mqstat)) || + put_compat_mq_attr(&mqstat, u_omqstat)) + return -EFAULT; + } + return 0; +} |