VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[linux-2.6.git] / ipc / mqueue.c
index f7acbb5..c9ea777 100644 (file)
 #define CTL_MSGSIZEMAX         4
 
 /* default values */
-#define DFLT_QUEUESMAX 64      /* max number of message queues */
-#define DFLT_MSGMAX    40      /* max number of messages in each queue */
+#define DFLT_QUEUESMAX 256     /* max number of message queues */
+#define DFLT_MSGMAX    10      /* max number of messages in each queue */
 #define HARD_MSGMAX    (131072/sizeof(void*))
-#define DFLT_MSGSIZEMAX 16384  /* max message size */
+#define DFLT_MSGSIZEMAX 8192   /* max message size */
 
 #define NOTIFY_COOKIE_LEN      32
 
@@ -67,6 +67,7 @@ struct mqueue_inode_info {
 
        struct sigevent notify;
        pid_t notify_owner;
+       struct user_struct *user;       /* user who created, for accouting */
        struct sock *notify_sock;
        struct sk_buff *notify_cookie;
 
@@ -97,7 +98,8 @@ static inline struct mqueue_inode_info *MQUEUE_I(struct inode *inode)
        return container_of(inode, struct mqueue_inode_info, vfs_inode);
 }
 
-static struct inode *mqueue_get_inode(struct super_block *sb, int mode)
+static struct inode *mqueue_get_inode(struct super_block *sb, int mode,
+                                                       struct mq_attr *attr)
 {
        struct inode *inode;
 
@@ -113,6 +115,9 @@ static struct inode *mqueue_get_inode(struct super_block *sb, int mode)
 
                if (S_ISREG(mode)) {
                        struct mqueue_inode_info *info;
+                       struct task_struct *p = current;
+                       struct user_struct *u = p->user;
+                       unsigned long mq_bytes, mq_msg_tblsz;
 
                        inode->i_fop = &mqueue_file_operations;
                        inode->i_size = FILENT_SIZE;
@@ -122,17 +127,40 @@ static struct inode *mqueue_get_inode(struct super_block *sb, int mode)
                        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 = 0;
                        info->qsize = 0;
+                       info->user = NULL;      /* set when all is ok */
                        memset(&info->attr, 0, sizeof(info->attr));
                        info->attr.mq_maxmsg = DFLT_MSGMAX;
                        info->attr.mq_msgsize = DFLT_MSGSIZEMAX;
-                       info->messages = kmalloc(DFLT_MSGMAX * sizeof(struct msg_msg *), GFP_KERNEL);
+                       if (attr) {
+                               info->attr.mq_maxmsg = attr->mq_maxmsg;
+                               info->attr.mq_msgsize = attr->mq_msgsize;
+                       }
+                       mq_msg_tblsz = info->attr.mq_maxmsg * sizeof(struct msg_msg *);
+                       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->rlim[RLIMIT_MSGQUEUE].rlim_cur) {
+                               spin_unlock(&mq_lock);
+                               goto out_inode;
+                       }
+                       u->mq_bytes += mq_bytes;
+                       spin_unlock(&mq_lock);
+
+                       info->messages = kmalloc(mq_msg_tblsz, GFP_KERNEL);
                        if (!info->messages) {
-                               make_bad_inode(inode);
-                               iput(inode);
-                               inode = NULL;
+                               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)) {
                        inode->i_nlink++;
                        /* Some things misbehave if size == 0 on a directory */
@@ -142,6 +170,10 @@ static struct inode *mqueue_get_inode(struct super_block *sb, int mode)
                }
        }
        return inode;
+out_inode:
+       make_bad_inode(inode);
+       iput(inode);
+       return NULL;
 }
 
 static int mqueue_fill_super(struct super_block *sb, void *data, int silent)
@@ -153,7 +185,7 @@ static int mqueue_fill_super(struct super_block *sb, void *data, int silent)
        sb->s_magic = MQUEUE_MAGIC;
        sb->s_op = &mqueue_super_ops;
 
-       inode = mqueue_get_inode(sb, S_IFDIR | S_ISVTX | S_IRWXUGO);
+       inode = mqueue_get_inode(sb, S_IFDIR | S_ISVTX | S_IRWXUGO, NULL);
        if (!inode)
                return -ENOMEM;
 
@@ -200,6 +232,8 @@ static void mqueue_destroy_inode(struct inode *inode)
 static void mqueue_delete_inode(struct inode *inode)
 {
        struct mqueue_inode_info *info;
+       struct user_struct *user;
+       unsigned long mq_bytes;
        int i;
 
        if (S_ISDIR(inode->i_mode)) {
@@ -215,10 +249,15 @@ static void mqueue_delete_inode(struct inode *inode)
 
        clear_inode(inode);
 
-       if (info->messages) {
+       mq_bytes = (info->attr.mq_maxmsg * sizeof(struct msg_msg *) +
+                  (info->attr.mq_maxmsg * info->attr.mq_msgsize));
+       user = info->user;
+       if (user) {
                spin_lock(&mq_lock);
+               user->mq_bytes -= mq_bytes;
                queues_count--;
                spin_unlock(&mq_lock);
+               free_uid(user);
        }
 }
 
@@ -226,6 +265,7 @@ static int mqueue_create(struct inode *dir, struct dentry *dentry,
                                int mode, struct nameidata *nd)
 {
        struct inode *inode;
+       struct mq_attr *attr = dentry->d_fsdata;
        int error;
 
        spin_lock(&mq_lock);
@@ -236,7 +276,7 @@ static int mqueue_create(struct inode *dir, struct dentry *dentry,
        queues_count++;
        spin_unlock(&mq_lock);
 
-       inode = mqueue_get_inode(dir->i_sb, mode);
+       inode = mqueue_get_inode(dir->i_sb, mode, attr);
        if (!inode) {
                error = -ENOMEM;
                spin_lock(&mq_lock);
@@ -528,6 +568,28 @@ static void remove_notification(struct mqueue_inode_info *info)
        info->notify_owner = 0;
 }
 
+static int mq_attr_ok(struct mq_attr *attr)
+{
+       if (attr->mq_maxmsg <= 0 || attr->mq_msgsize <= 0)
+               return 0;
+       if (capable(CAP_SYS_RESOURCE)) {
+               if (attr->mq_maxmsg > HARD_MSGMAX)
+                       return 0;
+       } else {
+               if (attr->mq_maxmsg > msg_max ||
+                               attr->mq_msgsize > msgsize_max)
+                       return 0;
+       }
+       /* 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 *)) <
+           (unsigned long)(attr->mq_maxmsg * attr->mq_msgsize))
+               return 0;
+       return 1;
+}
+
 /*
  * Invoked when creating a new queue via sys_mq_open
  */
@@ -535,48 +597,22 @@ static struct file *do_create(struct dentry *dir, struct dentry *dentry,
                        int oflag, mode_t mode, struct mq_attr __user *u_attr)
 {
        struct file *filp;
-       struct inode *inode;
-       struct mqueue_inode_info *info;
-       struct msg_msg **msgs = NULL;
        struct mq_attr attr;
        int ret;
 
        if (u_attr != NULL) {
                if (copy_from_user(&attr, u_attr, sizeof(attr)))
                        return ERR_PTR(-EFAULT);
-
-               if (attr.mq_maxmsg <= 0 || attr.mq_msgsize <= 0)
+               if (!mq_attr_ok(&attr))
                        return ERR_PTR(-EINVAL);
-               if (capable(CAP_SYS_RESOURCE)) {
-                       if (attr.mq_maxmsg > HARD_MSGMAX)
-                               return ERR_PTR(-EINVAL);
-               } else {
-                       if (attr.mq_maxmsg > msg_max ||
-                                       attr.mq_msgsize > msgsize_max)
-                               return ERR_PTR(-EINVAL);
-               }
-               msgs = kmalloc(attr.mq_maxmsg * sizeof(*msgs), GFP_KERNEL);
-               if (!msgs)
-                       return ERR_PTR(-ENOMEM);
-       } else {
-               msgs = NULL;
+               /* store for use during create */
+               dentry->d_fsdata = &attr;
        }
 
        ret = vfs_create(dir->d_inode, dentry, mode, NULL);
-       if (ret) {
-               kfree(msgs);
+       dentry->d_fsdata = NULL;
+       if (ret)
                return ERR_PTR(ret);
-       }
-
-       inode = dentry->d_inode;
-       info = MQUEUE_I(inode);
-
-       if (msgs) {
-               info->attr.mq_maxmsg = attr.mq_maxmsg;
-               info->attr.mq_msgsize = attr.mq_msgsize;
-               kfree(info->messages);
-               info->messages = msgs;
-       }
 
        filp = dentry_open(dentry, mqueue_mnt, oflag);
        if (!IS_ERR(filp))
@@ -791,7 +827,7 @@ asmlinkage long sys_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr,
 
        /* First try to allocate memory, before doing anything with
         * existing queues. */
-       msg_ptr = load_msg((void *)u_msg_ptr, msg_len);
+       msg_ptr = load_msg(u_msg_ptr, msg_len);
        if (unlikely(IS_ERR(msg_ptr))) {
                ret = PTR_ERR(msg_ptr);
                goto out_fput;