+++ /dev/null
-/*
- * 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;
-}
}
static int prepare_timeout(const struct timespec __user *u_abs_timeout,
- ktime_t *expires, struct timespec *ts)
+ struct timespec *ts)
{
if (copy_from_user(ts, u_abs_timeout, sizeof(struct timespec)))
return -EFAULT;
if (!timespec_valid(ts))
return -EINVAL;
-
- *expires = timespec_to_ktime(*ts);
return 0;
}
return dentry_open(path, oflag, current_cred());
}
-SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
- struct mq_attr __user *, u_attr)
+static int do_mq_open(const char __user *u_name, int oflag, umode_t mode,
+ struct mq_attr *attr)
{
struct path path;
struct file *filp;
struct filename *name;
- struct mq_attr attr;
int fd, error;
struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
struct vfsmount *mnt = ipc_ns->mq_mnt;
struct dentry *root = mnt->mnt_root;
int ro;
- if (u_attr && copy_from_user(&attr, u_attr, sizeof(struct mq_attr)))
- return -EFAULT;
-
- audit_mq_open(oflag, mode, u_attr ? &attr : NULL);
+ audit_mq_open(oflag, mode, attr);
if (IS_ERR(name = getname(u_name)))
return PTR_ERR(name);
goto out;
}
audit_inode_parent_hidden(name, root);
- filp = do_create(ipc_ns, d_inode(root),
- &path, oflag, mode,
- u_attr ? &attr : NULL);
+ filp = do_create(ipc_ns, d_inode(root), &path,
+ oflag, mode, attr);
}
} else {
if (d_really_is_negative(path.dentry)) {
return fd;
}
+SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
+ struct mq_attr __user *, u_attr)
+{
+ struct mq_attr attr;
+ if (u_attr && copy_from_user(&attr, u_attr, sizeof(struct mq_attr)))
+ return -EFAULT;
+
+ return do_mq_open(u_name, oflag, mode, u_attr ? &attr : NULL);
+}
+
SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
{
int err;
sender->state = STATE_READY;
}
-SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr,
- size_t, msg_len, unsigned int, msg_prio,
- const struct timespec __user *, u_abs_timeout)
+static int do_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr,
+ size_t msg_len, unsigned int msg_prio,
+ struct timespec *ts)
{
struct fd f;
struct inode *inode;
struct msg_msg *msg_ptr;
struct mqueue_inode_info *info;
ktime_t expires, *timeout = NULL;
- struct timespec ts;
struct posix_msg_tree_node *new_leaf = NULL;
int ret = 0;
DEFINE_WAKE_Q(wake_q);
- if (u_abs_timeout) {
- int res = prepare_timeout(u_abs_timeout, &expires, &ts);
- if (res)
- return res;
- timeout = &expires;
- }
-
if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX))
return -EINVAL;
- audit_mq_sendrecv(mqdes, msg_len, msg_prio, timeout ? &ts : NULL);
+ if (ts) {
+ expires = timespec_to_ktime(*ts);
+ timeout = &expires;
+ }
+
+ audit_mq_sendrecv(mqdes, msg_len, msg_prio, ts);
f = fdget(mqdes);
if (unlikely(!f.file)) {
return ret;
}
-SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr,
- size_t, msg_len, unsigned int __user *, u_msg_prio,
- const struct timespec __user *, u_abs_timeout)
+static int do_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr,
+ size_t msg_len, unsigned int __user *u_msg_prio,
+ struct timespec *ts)
{
ssize_t ret;
struct msg_msg *msg_ptr;
struct mqueue_inode_info *info;
struct ext_wait_queue wait;
ktime_t expires, *timeout = NULL;
- struct timespec ts;
struct posix_msg_tree_node *new_leaf = NULL;
- if (u_abs_timeout) {
- int res = prepare_timeout(u_abs_timeout, &expires, &ts);
- if (res)
- return res;
+ if (ts) {
+ expires = timespec_to_ktime(*ts);
timeout = &expires;
}
- audit_mq_sendrecv(mqdes, msg_len, 0, timeout ? &ts : NULL);
+ audit_mq_sendrecv(mqdes, msg_len, 0, ts);
f = fdget(mqdes);
if (unlikely(!f.file)) {
return ret;
}
+SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr,
+ size_t, msg_len, unsigned int, msg_prio,
+ const struct timespec __user *, u_abs_timeout)
+{
+ struct timespec ts, *p = NULL;
+ if (u_abs_timeout) {
+ int res = prepare_timeout(u_abs_timeout, &ts);
+ if (res)
+ return res;
+ p = &ts;
+ }
+ return do_mq_timedsend(mqdes, u_msg_ptr, msg_len, msg_prio, p);
+}
+
+SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr,
+ size_t, msg_len, unsigned int __user *, u_msg_prio,
+ const struct timespec __user *, u_abs_timeout)
+{
+ struct timespec ts, *p = NULL;
+ if (u_abs_timeout) {
+ int res = prepare_timeout(u_abs_timeout, &ts);
+ if (res)
+ return res;
+ p = &ts;
+ }
+ return do_mq_timedreceive(mqdes, u_msg_ptr, msg_len, u_msg_prio, p);
+}
+
/*
* Notes: the case when user wants us to deregister (with NULL as pointer)
* and he isn't currently owner of notification, will be silently discarded.
* It isn't explicitly defined in the POSIX.
*/
-SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes,
- const struct sigevent __user *, u_notification)
+static int do_mq_notify(mqd_t mqdes, const struct sigevent *notification)
{
int ret;
struct fd f;
struct sock *sock;
struct inode *inode;
- struct sigevent notification;
struct mqueue_inode_info *info;
struct sk_buff *nc;
- if (u_notification) {
- if (copy_from_user(¬ification, u_notification,
- sizeof(struct sigevent)))
- return -EFAULT;
- }
-
- audit_mq_notify(mqdes, u_notification ? ¬ification : NULL);
+ audit_mq_notify(mqdes, notification);
nc = NULL;
sock = NULL;
- if (u_notification != NULL) {
- if (unlikely(notification.sigev_notify != SIGEV_NONE &&
- notification.sigev_notify != SIGEV_SIGNAL &&
- notification.sigev_notify != SIGEV_THREAD))
+ if (notification != NULL) {
+ if (unlikely(notification->sigev_notify != SIGEV_NONE &&
+ notification->sigev_notify != SIGEV_SIGNAL &&
+ notification->sigev_notify != SIGEV_THREAD))
return -EINVAL;
- if (notification.sigev_notify == SIGEV_SIGNAL &&
- !valid_signal(notification.sigev_signo)) {
+ if (notification->sigev_notify == SIGEV_SIGNAL &&
+ !valid_signal(notification->sigev_signo)) {
return -EINVAL;
}
- if (notification.sigev_notify == SIGEV_THREAD) {
+ if (notification->sigev_notify == SIGEV_THREAD) {
long timeo;
/* create the notify skb */
goto out;
}
if (copy_from_user(nc->data,
- notification.sigev_value.sival_ptr,
+ notification->sigev_value.sival_ptr,
NOTIFY_COOKIE_LEN)) {
ret = -EFAULT;
goto out;
skb_put(nc, NOTIFY_COOKIE_LEN);
/* and attach it to the socket */
retry:
- f = fdget(notification.sigev_signo);
+ f = fdget(notification->sigev_signo);
if (!f.file) {
ret = -EBADF;
goto out;
ret = 0;
spin_lock(&info->lock);
- if (u_notification == NULL) {
+ if (notification == NULL) {
if (info->notify_owner == task_tgid(current)) {
remove_notification(info);
inode->i_atime = inode->i_ctime = current_time(inode);
} else if (info->notify_owner != NULL) {
ret = -EBUSY;
} else {
- switch (notification.sigev_notify) {
+ switch (notification->sigev_notify) {
case SIGEV_NONE:
info->notify.sigev_notify = SIGEV_NONE;
break;
info->notify.sigev_notify = SIGEV_THREAD;
break;
case SIGEV_SIGNAL:
- info->notify.sigev_signo = notification.sigev_signo;
- info->notify.sigev_value = notification.sigev_value;
+ info->notify.sigev_signo = notification->sigev_signo;
+ info->notify.sigev_value = notification->sigev_value;
info->notify.sigev_notify = SIGEV_SIGNAL;
break;
}
return ret;
}
-SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes,
- const struct mq_attr __user *, u_mqstat,
- struct mq_attr __user *, u_omqstat)
+SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes,
+ const struct sigevent __user *, u_notification)
+{
+ struct sigevent n, *p = NULL;
+ if (u_notification) {
+ if (copy_from_user(&n, u_notification, sizeof(struct sigevent)))
+ return -EFAULT;
+ p = &n;
+ }
+ return do_mq_notify(mqdes, p);
+}
+
+static int do_mq_getsetattr(int mqdes, struct mq_attr *new, struct mq_attr *old)
{
- int ret;
- struct mq_attr mqstat, omqstat;
struct fd f;
struct inode *inode;
struct mqueue_inode_info *info;
- if (u_mqstat != NULL) {
- if (copy_from_user(&mqstat, u_mqstat, sizeof(struct mq_attr)))
- return -EFAULT;
- if (mqstat.mq_flags & (~O_NONBLOCK))
- return -EINVAL;
- }
+ if (new && (new->mq_flags & (~O_NONBLOCK)))
+ return -EINVAL;
f = fdget(mqdes);
- if (!f.file) {
- ret = -EBADF;
- goto out;
- }
+ if (!f.file)
+ return -EBADF;
- inode = file_inode(f.file);
if (unlikely(f.file->f_op != &mqueue_file_operations)) {
- ret = -EBADF;
- goto out_fput;
+ fdput(f);
+ return -EBADF;
}
+
+ inode = file_inode(f.file);
info = MQUEUE_I(inode);
spin_lock(&info->lock);
- omqstat = info->attr;
- omqstat.mq_flags = f.file->f_flags & O_NONBLOCK;
- if (u_mqstat) {
- audit_mq_getsetattr(mqdes, &mqstat);
+ if (old) {
+ *old = info->attr;
+ old->mq_flags = f.file->f_flags & O_NONBLOCK;
+ }
+ if (new) {
+ audit_mq_getsetattr(mqdes, new);
spin_lock(&f.file->f_lock);
- if (mqstat.mq_flags & O_NONBLOCK)
+ if (new->mq_flags & O_NONBLOCK)
f.file->f_flags |= O_NONBLOCK;
else
f.file->f_flags &= ~O_NONBLOCK;
}
spin_unlock(&info->lock);
+ fdput(f);
+ return 0;
+}
- ret = 0;
- if (u_omqstat != NULL && copy_to_user(u_omqstat, &omqstat,
- sizeof(struct mq_attr)))
- ret = -EFAULT;
+SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes,
+ const struct mq_attr __user *, u_mqstat,
+ struct mq_attr __user *, u_omqstat)
+{
+ int ret;
+ struct mq_attr mqstat, omqstat;
+ struct mq_attr *new = NULL, *old = NULL;
-out_fput:
- fdput(f);
-out:
- return ret;
+ if (u_mqstat) {
+ new = &mqstat;
+ if (copy_from_user(new, u_mqstat, sizeof(struct mq_attr)))
+ return -EFAULT;
+ }
+ if (u_omqstat)
+ old = &omqstat;
+
+ ret = do_mq_getsetattr(mqdes, new, old);
+ if (ret || !old)
+ return ret;
+
+ if (copy_to_user(u_omqstat, old, sizeof(struct mq_attr)))
+ return -EFAULT;
+ return 0;
+}
+
+#ifdef CONFIG_COMPAT
+
+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)
+{
+ struct compat_mq_attr v;
+
+ if (copy_from_user(&v, uattr, sizeof(*uattr)))
+ return -EFAULT;
+
+ memset(attr, 0, sizeof(*attr));
+ attr->mq_flags = v.mq_flags;
+ attr->mq_maxmsg = v.mq_maxmsg;
+ attr->mq_msgsize = v.mq_msgsize;
+ attr->mq_curmsgs = v.mq_curmsgs;
+ return 0;
+}
+
+static inline int put_compat_mq_attr(const struct mq_attr *attr,
+ struct compat_mq_attr __user *uattr)
+{
+ struct compat_mq_attr v;
+
+ memset(&v, 0, sizeof(v));
+ v.mq_flags = attr->mq_flags;
+ v.mq_maxmsg = attr->mq_maxmsg;
+ v.mq_msgsize = attr->mq_msgsize;
+ v.mq_curmsgs = attr->mq_curmsgs;
+ if (copy_to_user(uattr, &v, sizeof(*uattr)))
+ return -EFAULT;
+ return 0;
+}
+
+COMPAT_SYSCALL_DEFINE4(mq_open, const char __user *, u_name,
+ int, oflag, compat_mode_t, mode,
+ struct compat_mq_attr __user *, u_attr)
+{
+ struct mq_attr attr, *p = NULL;
+ if (u_attr && oflag & O_CREAT) {
+ p = &attr;
+ if (get_compat_mq_attr(&attr, u_attr))
+ return -EFAULT;
+ }
+ return do_mq_open(u_name, oflag, mode, p);
+}
+
+static int compat_prepare_timeout(const struct compat_timespec __user *p,
+ struct timespec *ts)
+{
+ if (compat_get_timespec(ts, p))
+ return -EFAULT;
+ if (!timespec_valid(ts))
+ return -EINVAL;
+ return 0;
+}
+
+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 ts, *p = NULL;
+ if (u_abs_timeout) {
+ int res = compat_prepare_timeout(u_abs_timeout, &ts);
+ if (res)
+ return res;
+ p = &ts;
+ }
+ return do_mq_timedsend(mqdes, u_msg_ptr, msg_len, msg_prio, p);
+}
+
+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 ts, *p = NULL;
+ if (u_abs_timeout) {
+ int res = compat_prepare_timeout(u_abs_timeout, &ts);
+ if (res)
+ return res;
+ p = &ts;
+ }
+ return do_mq_timedreceive(mqdes, u_msg_ptr, msg_len, u_msg_prio, p);
+}
+
+COMPAT_SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes,
+ const struct compat_sigevent __user *, u_notification)
+{
+ struct sigevent n, *p = NULL;
+ if (u_notification) {
+ 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);
+ p = &n;
+ }
+ return do_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)
+{
+ int ret;
+ struct mq_attr mqstat, omqstat;
+ struct mq_attr *new = NULL, *old = NULL;
+
+ if (u_mqstat) {
+ new = &mqstat;
+ if (get_compat_mq_attr(new, u_mqstat))
+ return -EFAULT;
+ }
+ if (u_omqstat)
+ old = &omqstat;
+
+ ret = do_mq_getsetattr(mqdes, new, old);
+ if (ret || !old)
+ return ret;
+
+ if (put_compat_mq_attr(old, u_omqstat))
+ return -EFAULT;
+ return 0;
}
+#endif
static const struct inode_operations mqueue_dir_inode_operations = {
.lookup = simple_lookup,