ALSA: usb-audio: add support for Akai MPD16
[safe/jmp/linux-2.6] / ipc / mqueue.c
index ee9d697..5108232 100644 (file)
@@ -32,7 +32,7 @@
 #include <linux/nsproxy.h>
 #include <linux/pid.h>
 #include <linux/ipc_namespace.h>
-#include <linux/ima.h>
+#include <linux/slab.h>
 
 #include <net/sock.h>
 #include "util.h"
@@ -135,7 +135,6 @@ static struct inode *mqueue_get_inode(struct super_block *sb,
                        init_waitqueue_head(&info->wait_q);
                        INIT_LIST_HEAD(&info->e_wait_q[0].list);
                        INIT_LIST_HEAD(&info->e_wait_q[1].list);
-                       info->messages = NULL;
                        info->notify_owner = NULL;
                        info->qsize = 0;
                        info->user = NULL;      /* set when all is ok */
@@ -147,26 +146,24 @@ static struct inode *mqueue_get_inode(struct super_block *sb,
                                info->attr.mq_msgsize = attr->mq_msgsize;
                        }
                        mq_msg_tblsz = info->attr.mq_maxmsg * sizeof(struct msg_msg *);
+                       info->messages = kmalloc(mq_msg_tblsz, GFP_KERNEL);
+                       if (!info->messages)
+                               goto out_inode;
+
                        mq_bytes = (mq_msg_tblsz +
                                (info->attr.mq_maxmsg * info->attr.mq_msgsize));
 
                        spin_lock(&mq_lock);
                        if (u->mq_bytes + mq_bytes < u->mq_bytes ||
                            u->mq_bytes + mq_bytes >
-                           p->signal->rlim[RLIMIT_MSGQUEUE].rlim_cur) {
+                           task_rlimit(p, RLIMIT_MSGQUEUE)) {
                                spin_unlock(&mq_lock);
+                               /* mqueue_delete_inode() releases info->messages */
                                goto out_inode;
                        }
                        u->mq_bytes += mq_bytes;
                        spin_unlock(&mq_lock);
 
-                       info->messages = kmalloc(mq_msg_tblsz, GFP_KERNEL);
-                       if (!info->messages) {
-                               spin_lock(&mq_lock);
-                               u->mq_bytes -= mq_bytes;
-                               spin_unlock(&mq_lock);
-                               goto out_inode;
-                       }
                        /* all is ok */
                        info->user = get_uid(u);
                } else if (S_ISDIR(mode)) {
@@ -188,7 +185,7 @@ static int mqueue_fill_super(struct super_block *sb, void *data, int silent)
 {
        struct inode *inode;
        struct ipc_namespace *ns = data;
-       int error = 0;
+       int error;
 
        sb->s_blocksize = PAGE_CACHE_SIZE;
        sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
@@ -206,7 +203,9 @@ static int mqueue_fill_super(struct super_block *sb, void *data, int silent)
        if (!sb->s_root) {
                iput(inode);
                error = -ENOMEM;
+               goto out;
        }
+       error = 0;
 
 out:
        return error;
@@ -265,8 +264,9 @@ static void mqueue_delete_inode(struct inode *inode)
 
        clear_inode(inode);
 
-       mq_bytes = (info->attr.mq_maxmsg * sizeof(struct msg_msg *) +
-                  (info->attr.mq_maxmsg * info->attr.mq_msgsize));
+       /* Total amount of bytes accounted for the mqueue */
+       mq_bytes = info->attr.mq_maxmsg * (sizeof(struct msg_msg *)
+           + info->attr.mq_msgsize);
        user = info->user;
        if (user) {
                spin_lock(&mq_lock);
@@ -429,7 +429,7 @@ static void wq_add(struct mqueue_inode_info *info, int sr,
  * sr: SEND or RECV
  */
 static int wq_sleep(struct mqueue_inode_info *info, int sr,
-                       long timeout, struct ext_wait_queue *ewp)
+                   ktime_t *timeout, struct ext_wait_queue *ewp)
 {
        int retval;
        signed long time;
@@ -440,7 +440,8 @@ static int wq_sleep(struct mqueue_inode_info *info, int sr,
                set_current_state(TASK_INTERRUPTIBLE);
 
                spin_unlock(&info->lock);
-               time = schedule_timeout(timeout);
+               time = schedule_hrtimeout_range_clock(timeout,
+                   HRTIMER_MODE_ABS, 0, CLOCK_REALTIME);
 
                while (ewp->state == STATE_PENDING)
                        cpu_relax();
@@ -552,31 +553,16 @@ static void __do_notify(struct mqueue_inode_info *info)
        wake_up(&info->wait_q);
 }
 
-static long prepare_timeout(struct timespec *p)
+static int prepare_timeout(const struct timespec __user *u_abs_timeout,
+                          ktime_t *expires, struct timespec *ts)
 {
-       struct timespec nowts;
-       long timeout;
-
-       if (p) {
-               if (unlikely(p->tv_nsec < 0 || p->tv_sec < 0
-                       || p->tv_nsec >= NSEC_PER_SEC))
-                       return -EINVAL;
-               nowts = CURRENT_TIME;
-               /* first subtract as jiffies can't be too big */
-               p->tv_sec -= nowts.tv_sec;
-               if (p->tv_nsec < nowts.tv_nsec) {
-                       p->tv_nsec += NSEC_PER_SEC;
-                       p->tv_sec--;
-               }
-               p->tv_nsec -= nowts.tv_nsec;
-               if (p->tv_sec < 0)
-                       return 0;
-
-               timeout = timespec_to_jiffies(p) + 1;
-       } else
-               return MAX_SCHEDULE_TIMEOUT;
+       if (copy_from_user(ts, u_abs_timeout, sizeof(struct timespec)))
+               return -EFAULT;
+       if (!timespec_valid(ts))
+               return -EINVAL;
 
-       return timeout;
+       *expires = timespec_to_ktime(*ts);
+       return 0;
 }
 
 static void remove_notification(struct mqueue_inode_info *info)
@@ -605,8 +591,8 @@ static int mq_attr_ok(struct ipc_namespace *ipc_ns, struct mq_attr *attr)
        /* check for overflow */
        if (attr->mq_msgsize > ULONG_MAX/attr->mq_maxmsg)
                return 0;
-       if ((unsigned long)(attr->mq_maxmsg * attr->mq_msgsize) +
-           (attr->mq_maxmsg * sizeof (struct msg_msg *)) <
+       if ((unsigned long)(attr->mq_maxmsg * (attr->mq_msgsize
+           + sizeof (struct msg_msg *))) <
            (unsigned long)(attr->mq_maxmsg * attr->mq_msgsize))
                return 0;
        return 1;
@@ -624,9 +610,10 @@ static struct file *do_create(struct ipc_namespace *ipc_ns, struct dentry *dir,
        int ret;
 
        if (attr) {
-               ret = -EINVAL;
-               if (!mq_attr_ok(ipc_ns, attr))
+               if (!mq_attr_ok(ipc_ns, attr)) {
+                       ret = -EINVAL;
                        goto out;
+               }
                /* store for use during create */
                dentry->d_fsdata = attr;
        }
@@ -660,24 +647,28 @@ out:
 static struct file *do_open(struct ipc_namespace *ipc_ns,
                                struct dentry *dentry, int oflag)
 {
+       int ret;
        const struct cred *cred = current_cred();
 
        static const int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE,
                                                  MAY_READ | MAY_WRITE };
 
        if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) {
-               dput(dentry);
-               mntput(ipc_ns->mq_mnt);
-               return ERR_PTR(-EINVAL);
+               ret = -EINVAL;
+               goto err;
        }
 
        if (inode_permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE])) {
-               dput(dentry);
-               mntput(ipc_ns->mq_mnt);
-               return ERR_PTR(-EACCES);
+               ret = -EACCES;
+               goto err;
        }
 
        return dentry_open(dentry, ipc_ns->mq_mnt, oflag, cred);
+
+err:
+       dput(dentry);
+       mntput(ipc_ns->mq_mnt);
+       return ERR_PTR(ret);
 }
 
 SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, mode_t, mode,
@@ -706,16 +697,17 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, mode_t, mode,
        dentry = lookup_one_len(name, ipc_ns->mq_mnt->mnt_root, strlen(name));
        if (IS_ERR(dentry)) {
                error = PTR_ERR(dentry);
-               goto out_err;
+               goto out_putfd;
        }
        mntget(ipc_ns->mq_mnt);
 
        if (oflag & O_CREAT) {
                if (dentry->d_inode) {  /* entry already exists */
                        audit_inode(name, dentry);
-                       error = -EEXIST;
-                       if (oflag & O_EXCL)
+                       if (oflag & O_EXCL) {
+                               error = -EEXIST;
                                goto out;
+                       }
                        filp = do_open(ipc_ns, dentry, oflag);
                } else {
                        filp = do_create(ipc_ns, ipc_ns->mq_mnt->mnt_root,
@@ -723,9 +715,10 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, mode_t, mode,
                                                u_attr ? &attr : NULL);
                }
        } else {
-               error = -ENOENT;
-               if (!dentry->d_inode)
+               if (!dentry->d_inode) {
+                       error = -ENOENT;
                        goto out;
+               }
                audit_inode(name, dentry);
                filp = do_open(ipc_ns, dentry, oflag);
        }
@@ -734,7 +727,6 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, mode_t, mode,
                error = PTR_ERR(filp);
                goto out_putfd;
        }
-       ima_counts_get(filp);
 
        fd_install(fd, filp);
        goto out_upsem;
@@ -744,7 +736,6 @@ out:
        mntput(ipc_ns->mq_mnt);
 out_putfd:
        put_unused_fd(fd);
-out_err:
        fd = error;
 out_upsem:
        mutex_unlock(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex);
@@ -857,36 +848,40 @@ SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr,
        struct ext_wait_queue *receiver;
        struct msg_msg *msg_ptr;
        struct mqueue_inode_info *info;
-       struct timespec ts, *p = NULL;
-       long timeout;
+       ktime_t expires, *timeout = NULL;
+       struct timespec ts;
        int ret;
 
        if (u_abs_timeout) {
-               if (copy_from_user(&ts, u_abs_timeout, 
-                                       sizeof(struct timespec)))
-                       return -EFAULT;
-               p = &ts;
+               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, p);
-       timeout = prepare_timeout(p);
+       audit_mq_sendrecv(mqdes, msg_len, msg_prio, timeout ? &ts : NULL);
 
-       ret = -EBADF;
        filp = fget(mqdes);
-       if (unlikely(!filp))
+       if (unlikely(!filp)) {
+               ret = -EBADF;
                goto out;
+       }
 
        inode = filp->f_path.dentry->d_inode;
-       if (unlikely(filp->f_op != &mqueue_file_operations))
+       if (unlikely(filp->f_op != &mqueue_file_operations)) {
+               ret = -EBADF;
                goto out_fput;
+       }
        info = MQUEUE_I(inode);
        audit_inode(NULL, filp->f_path.dentry);
 
-       if (unlikely(!(filp->f_mode & FMODE_WRITE)))
+       if (unlikely(!(filp->f_mode & FMODE_WRITE))) {
+               ret = -EBADF;
                goto out_fput;
+       }
 
        if (unlikely(msg_len > info->attr.mq_msgsize)) {
                ret = -EMSGSIZE;
@@ -909,9 +904,6 @@ SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr,
                if (filp->f_flags & O_NONBLOCK) {
                        spin_unlock(&info->lock);
                        ret = -EAGAIN;
-               } else if (unlikely(timeout < 0)) {
-                       spin_unlock(&info->lock);
-                       ret = timeout;
                } else {
                        wait.task = current;
                        wait.msg = (void *) msg_ptr;
@@ -944,38 +936,42 @@ 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)
 {
-       long timeout;
        ssize_t ret;
        struct msg_msg *msg_ptr;
        struct file *filp;
        struct inode *inode;
        struct mqueue_inode_info *info;
        struct ext_wait_queue wait;
-       struct timespec ts, *p = NULL;
+       ktime_t expires, *timeout = NULL;
+       struct timespec ts;
 
        if (u_abs_timeout) {
-               if (copy_from_user(&ts, u_abs_timeout, 
-                                       sizeof(struct timespec)))
-                       return -EFAULT;
-               p = &ts;
+               int res = prepare_timeout(u_abs_timeout, &expires, &ts);
+               if (res)
+                       return res;
+               timeout = &expires;
        }
 
-       audit_mq_sendrecv(mqdes, msg_len, 0, p);
-       timeout = prepare_timeout(p);
+       audit_mq_sendrecv(mqdes, msg_len, 0, timeout ? &ts : NULL);
 
-       ret = -EBADF;
        filp = fget(mqdes);
-       if (unlikely(!filp))
+       if (unlikely(!filp)) {
+               ret = -EBADF;
                goto out;
+       }
 
        inode = filp->f_path.dentry->d_inode;
-       if (unlikely(filp->f_op != &mqueue_file_operations))
+       if (unlikely(filp->f_op != &mqueue_file_operations)) {
+               ret = -EBADF;
                goto out_fput;
+       }
        info = MQUEUE_I(inode);
        audit_inode(NULL, filp->f_path.dentry);
 
-       if (unlikely(!(filp->f_mode & FMODE_READ)))
+       if (unlikely(!(filp->f_mode & FMODE_READ))) {
+               ret = -EBADF;
                goto out_fput;
+       }
 
        /* checks if buffer is big enough */
        if (unlikely(msg_len < info->attr.mq_msgsize)) {
@@ -988,11 +984,6 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr,
                if (filp->f_flags & O_NONBLOCK) {
                        spin_unlock(&info->lock);
                        ret = -EAGAIN;
-                       msg_ptr = NULL;
-               } else if (unlikely(timeout < 0)) {
-                       spin_unlock(&info->lock);
-                       ret = timeout;
-                       msg_ptr = NULL;
                } else {
                        wait.task = current;
                        wait.state = STATE_NONE;
@@ -1065,13 +1056,14 @@ SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes,
 
                        /* create the notify skb */
                        nc = alloc_skb(NOTIFY_COOKIE_LEN, GFP_KERNEL);
-                       ret = -ENOMEM;
-                       if (!nc)
+                       if (!nc) {
+                               ret = -ENOMEM;
                                goto out;
-                       ret = -EFAULT;
+                       }
                        if (copy_from_user(nc->data,
                                        notification.sigev_value.sival_ptr,
                                        NOTIFY_COOKIE_LEN)) {
+                               ret = -EFAULT;
                                goto out;
                        }
 
@@ -1080,9 +1072,10 @@ SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes,
                        /* and attach it to the socket */
 retry:
                        filp = fget(notification.sigev_signo);
-                       ret = -EBADF;
-                       if (!filp)
+                       if (!filp) {
+                               ret = -EBADF;
                                goto out;
+                       }
                        sock = netlink_getsockbyfilp(filp);
                        fput(filp);
                        if (IS_ERR(sock)) {
@@ -1094,7 +1087,7 @@ retry:
                        timeo = MAX_SCHEDULE_TIMEOUT;
                        ret = netlink_attachskb(sock, nc, &timeo, NULL);
                        if (ret == 1)
-                               goto retry;
+                               goto retry;
                        if (ret) {
                                sock = NULL;
                                nc = NULL;
@@ -1103,14 +1096,17 @@ retry:
                }
        }
 
-       ret = -EBADF;
        filp = fget(mqdes);
-       if (!filp)
+       if (!filp) {
+               ret = -EBADF;
                goto out;
+       }
 
        inode = filp->f_path.dentry->d_inode;
-       if (unlikely(filp->f_op != &mqueue_file_operations))
+       if (unlikely(filp->f_op != &mqueue_file_operations)) {
+               ret = -EBADF;
                goto out_fput;
+       }
        info = MQUEUE_I(inode);
 
        ret = 0;
@@ -1173,14 +1169,17 @@ SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes,
                        return -EINVAL;
        }
 
-       ret = -EBADF;
        filp = fget(mqdes);
-       if (!filp)
+       if (!filp) {
+               ret = -EBADF;
                goto out;
+       }
 
        inode = filp->f_path.dentry->d_inode;
-       if (unlikely(filp->f_op != &mqueue_file_operations))
+       if (unlikely(filp->f_op != &mqueue_file_operations)) {
+               ret = -EBADF;
                goto out_fput;
+       }
        info = MQUEUE_I(inode);
 
        spin_lock(&info->lock);
@@ -1274,7 +1273,7 @@ static int __init init_mqueue_fs(void)
        if (mqueue_inode_cachep == NULL)
                return -ENOMEM;
 
-       /* ignore failues - they are not fatal */
+       /* ignore failures - they are not fatal */
        mq_sysctl_table = mq_register_sysctl_table();
 
        error = register_filesystem(&mqueue_fs_type);