mqueue: apply mathematics distributivity on mq_bytes calculation
[safe/jmp/linux-2.6] / ipc / mqueue.c
index a3673a0..15eabf9 100644 (file)
 #define STATE_PENDING  1
 #define STATE_READY    2
 
 #define STATE_PENDING  1
 #define STATE_READY    2
 
-/*
- * Define the ranges various user-specified maximum values can
- * be set to.
- */
-#define MIN_MSGMAX     1               /* min value for msg_max */
-#define MAX_MSGMAX     HARD_MSGMAX     /* max value for msg_max */
-#define MIN_MSGSIZEMAX 128             /* min value for msgsize_max */
-#define MAX_MSGSIZEMAX (8192*128)      /* max value for msgsize_max */
-
 struct ext_wait_queue {                /* queue of sleeping tasks */
        struct task_struct *task;
        struct list_head list;
 struct ext_wait_queue {                /* queue of sleeping tasks */
        struct task_struct *task;
        struct list_head list;
@@ -85,10 +76,9 @@ struct mqueue_inode_info {
 
 static const struct inode_operations mqueue_dir_inode_operations;
 static const struct file_operations mqueue_file_operations;
 
 static const struct inode_operations mqueue_dir_inode_operations;
 static const struct file_operations mqueue_file_operations;
-static struct super_operations mqueue_super_ops;
+static const struct super_operations mqueue_super_ops;
 static void remove_notification(struct mqueue_inode_info *info);
 
 static void remove_notification(struct mqueue_inode_info *info);
 
-static spinlock_t mq_lock;
 static struct kmem_cache *mqueue_inode_cachep;
 
 static struct ctl_table_header * mq_sysctl_table;
 static struct kmem_cache *mqueue_inode_cachep;
 
 static struct ctl_table_header * mq_sysctl_table;
@@ -98,27 +88,30 @@ static inline struct mqueue_inode_info *MQUEUE_I(struct inode *inode)
        return container_of(inode, struct mqueue_inode_info, vfs_inode);
 }
 
        return container_of(inode, struct mqueue_inode_info, vfs_inode);
 }
 
-void mq_init_ns(struct ipc_namespace *ns)
+/*
+ * This routine should be called with the mq_lock held.
+ */
+static inline struct ipc_namespace *__get_ns_from_inode(struct inode *inode)
 {
 {
-       ns->mq_queues_count  = 0;
-       ns->mq_queues_max    = DFLT_QUEUESMAX;
-       ns->mq_msg_max       = DFLT_MSGMAX;
-       ns->mq_msgsize_max   = DFLT_MSGSIZEMAX;
-       ns->mq_mnt           = mntget(init_ipc_ns.mq_mnt);
+       return get_ipc_ns(inode->i_sb->s_fs_info);
 }
 
 }
 
-void mq_exit_ns(struct ipc_namespace *ns)
+static struct ipc_namespace *get_ns_from_inode(struct inode *inode)
 {
 {
-       /* will need to clear out ns->mq_mnt->mnt_sb->s_fs_info here */
-       mntput(ns->mq_mnt);
+       struct ipc_namespace *ns;
+
+       spin_lock(&mq_lock);
+       ns = __get_ns_from_inode(inode);
+       spin_unlock(&mq_lock);
+       return ns;
 }
 
 }
 
-static struct inode *mqueue_get_inode(struct super_block *sb, int mode,
-                                                       struct mq_attr *attr)
+static struct inode *mqueue_get_inode(struct super_block *sb,
+               struct ipc_namespace *ipc_ns, int mode,
+               struct mq_attr *attr)
 {
        struct user_struct *u = current_user();
        struct inode *inode;
 {
        struct user_struct *u = current_user();
        struct inode *inode;
-       struct ipc_namespace *ipc_ns = &init_ipc_ns;
 
        inode = new_inode(sb);
        if (inode) {
 
        inode = new_inode(sb);
        if (inode) {
@@ -141,7 +134,6 @@ 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);
                        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 */
                        info->notify_owner = NULL;
                        info->qsize = 0;
                        info->user = NULL;      /* set when all is ok */
@@ -153,6 +145,10 @@ static struct inode *mqueue_get_inode(struct super_block *sb, int mode,
                                info->attr.mq_msgsize = attr->mq_msgsize;
                        }
                        mq_msg_tblsz = info->attr.mq_maxmsg * sizeof(struct msg_msg *);
                                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));
 
                        mq_bytes = (mq_msg_tblsz +
                                (info->attr.mq_maxmsg * info->attr.mq_msgsize));
 
@@ -161,18 +157,12 @@ static struct inode *mqueue_get_inode(struct super_block *sb, int mode,
                            u->mq_bytes + mq_bytes >
                            p->signal->rlim[RLIMIT_MSGQUEUE].rlim_cur) {
                                spin_unlock(&mq_lock);
                            u->mq_bytes + mq_bytes >
                            p->signal->rlim[RLIMIT_MSGQUEUE].rlim_cur) {
                                spin_unlock(&mq_lock);
+                               kfree(info->messages);
                                goto out_inode;
                        }
                        u->mq_bytes += mq_bytes;
                        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) {
-                               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)) {
                        /* all is ok */
                        info->user = get_uid(u);
                } else if (S_ISDIR(mode)) {
@@ -193,30 +183,38 @@ out_inode:
 static int mqueue_fill_super(struct super_block *sb, void *data, int silent)
 {
        struct inode *inode;
 static int mqueue_fill_super(struct super_block *sb, void *data, int silent)
 {
        struct inode *inode;
+       struct ipc_namespace *ns = data;
+       int error = 0;
 
        sb->s_blocksize = PAGE_CACHE_SIZE;
        sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
        sb->s_magic = MQUEUE_MAGIC;
        sb->s_op = &mqueue_super_ops;
 
 
        sb->s_blocksize = PAGE_CACHE_SIZE;
        sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
        sb->s_magic = MQUEUE_MAGIC;
        sb->s_op = &mqueue_super_ops;
 
-       inode = mqueue_get_inode(sb, S_IFDIR | S_ISVTX | S_IRWXUGO, NULL);
-       if (!inode)
-               return -ENOMEM;
+       inode = mqueue_get_inode(sb, ns, S_IFDIR | S_ISVTX | S_IRWXUGO,
+                               NULL);
+       if (!inode) {
+               error = -ENOMEM;
+               goto out;
+       }
 
        sb->s_root = d_alloc_root(inode);
        if (!sb->s_root) {
                iput(inode);
 
        sb->s_root = d_alloc_root(inode);
        if (!sb->s_root) {
                iput(inode);
-               return -ENOMEM;
+               error = -ENOMEM;
        }
 
        }
 
-       return 0;
+out:
+       return error;
 }
 
 static int mqueue_get_sb(struct file_system_type *fs_type,
                         int flags, const char *dev_name,
                         void *data, struct vfsmount *mnt)
 {
 }
 
 static int mqueue_get_sb(struct file_system_type *fs_type,
                         int flags, const char *dev_name,
                         void *data, struct vfsmount *mnt)
 {
-       return get_sb_single(fs_type, flags, data, mqueue_fill_super, mnt);
+       if (!(flags & MS_KERNMOUNT))
+               data = current->nsproxy->ipc_ns;
+       return get_sb_ns(fs_type, flags, data, mqueue_fill_super, mnt);
 }
 
 static void init_once(void *foo)
 }
 
 static void init_once(void *foo)
@@ -247,12 +245,13 @@ static void mqueue_delete_inode(struct inode *inode)
        struct user_struct *user;
        unsigned long mq_bytes;
        int i;
        struct user_struct *user;
        unsigned long mq_bytes;
        int i;
-       struct ipc_namespace *ipc_ns = &init_ipc_ns;
+       struct ipc_namespace *ipc_ns;
 
        if (S_ISDIR(inode->i_mode)) {
                clear_inode(inode);
                return;
        }
 
        if (S_ISDIR(inode->i_mode)) {
                clear_inode(inode);
                return;
        }
+       ipc_ns = get_ns_from_inode(inode);
        info = MQUEUE_I(inode);
        spin_lock(&info->lock);
        for (i = 0; i < info->attr.mq_curmsgs; i++)
        info = MQUEUE_I(inode);
        spin_lock(&info->lock);
        for (i = 0; i < info->attr.mq_curmsgs; i++)
@@ -262,16 +261,26 @@ static void mqueue_delete_inode(struct inode *inode)
 
        clear_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);
                user->mq_bytes -= mq_bytes;
        user = info->user;
        if (user) {
                spin_lock(&mq_lock);
                user->mq_bytes -= mq_bytes;
-               ipc_ns->mq_queues_count--;
+               /*
+                * get_ns_from_inode() ensures that the
+                * (ipc_ns = sb->s_fs_info) is either a valid ipc_ns
+                * to which we now hold a reference, or it is NULL.
+                * We can't put it here under mq_lock, though.
+                */
+               if (ipc_ns)
+                       ipc_ns->mq_queues_count--;
                spin_unlock(&mq_lock);
                free_uid(user);
        }
                spin_unlock(&mq_lock);
                free_uid(user);
        }
+       if (ipc_ns)
+               put_ipc_ns(ipc_ns);
 }
 
 static int mqueue_create(struct inode *dir, struct dentry *dentry,
 }
 
 static int mqueue_create(struct inode *dir, struct dentry *dentry,
@@ -280,9 +289,14 @@ static int mqueue_create(struct inode *dir, struct dentry *dentry,
        struct inode *inode;
        struct mq_attr *attr = dentry->d_fsdata;
        int error;
        struct inode *inode;
        struct mq_attr *attr = dentry->d_fsdata;
        int error;
-       struct ipc_namespace *ipc_ns = &init_ipc_ns;
+       struct ipc_namespace *ipc_ns;
 
        spin_lock(&mq_lock);
 
        spin_lock(&mq_lock);
+       ipc_ns = __get_ns_from_inode(dir);
+       if (!ipc_ns) {
+               error = -EACCES;
+               goto out_unlock;
+       }
        if (ipc_ns->mq_queues_count >= ipc_ns->mq_queues_max &&
                        !capable(CAP_SYS_RESOURCE)) {
                error = -ENOSPC;
        if (ipc_ns->mq_queues_count >= ipc_ns->mq_queues_max &&
                        !capable(CAP_SYS_RESOURCE)) {
                error = -ENOSPC;
@@ -291,7 +305,7 @@ static int mqueue_create(struct inode *dir, struct dentry *dentry,
        ipc_ns->mq_queues_count++;
        spin_unlock(&mq_lock);
 
        ipc_ns->mq_queues_count++;
        spin_unlock(&mq_lock);
 
-       inode = mqueue_get_inode(dir->i_sb, mode, attr);
+       inode = mqueue_get_inode(dir->i_sb, ipc_ns, mode, attr);
        if (!inode) {
                error = -ENOMEM;
                spin_lock(&mq_lock);
        if (!inode) {
                error = -ENOMEM;
                spin_lock(&mq_lock);
@@ -299,6 +313,7 @@ static int mqueue_create(struct inode *dir, struct dentry *dentry,
                goto out_unlock;
        }
 
                goto out_unlock;
        }
 
+       put_ipc_ns(ipc_ns);
        dir->i_size += DIRENT_SIZE;
        dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME;
 
        dir->i_size += DIRENT_SIZE;
        dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME;
 
@@ -307,6 +322,8 @@ static int mqueue_create(struct inode *dir, struct dentry *dentry,
        return 0;
 out_unlock:
        spin_unlock(&mq_lock);
        return 0;
 out_unlock:
        spin_unlock(&mq_lock);
+       if (ipc_ns)
+               put_ipc_ns(ipc_ns);
        return error;
 }
 
        return error;
 }
 
@@ -585,8 +602,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;
        /* 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;
            (unsigned long)(attr->mq_maxmsg * attr->mq_msgsize))
                return 0;
        return 1;
@@ -668,7 +685,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, mode_t, mode,
        char *name;
        struct mq_attr attr;
        int fd, error;
        char *name;
        struct mq_attr attr;
        int fd, error;
-       struct ipc_namespace *ipc_ns = &init_ipc_ns;
+       struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
 
        if (u_attr && copy_from_user(&attr, u_attr, sizeof(struct mq_attr)))
                return -EFAULT;
 
        if (u_attr && copy_from_user(&attr, u_attr, sizeof(struct mq_attr)))
                return -EFAULT;
@@ -686,7 +703,7 @@ 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);
        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);
 
        }
        mntget(ipc_ns->mq_mnt);
 
@@ -723,7 +740,6 @@ out:
        mntput(ipc_ns->mq_mnt);
 out_putfd:
        put_unused_fd(fd);
        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);
        fd = error;
 out_upsem:
        mutex_unlock(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex);
@@ -738,7 +754,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
        char *name;
        struct dentry *dentry;
        struct inode *inode = NULL;
        char *name;
        struct dentry *dentry;
        struct inode *inode = NULL;
-       struct ipc_namespace *ipc_ns = &init_ipc_ns;
+       struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
 
        name = getname(u_name);
        if (IS_ERR(name))
 
        name = getname(u_name);
        if (IS_ERR(name))
@@ -1203,7 +1219,7 @@ static const struct file_operations mqueue_file_operations = {
        .read = mqueue_read_file,
 };
 
        .read = mqueue_read_file,
 };
 
-static struct super_operations mqueue_super_ops = {
+static const struct super_operations mqueue_super_ops = {
        .alloc_inode = mqueue_alloc_inode,
        .destroy_inode = mqueue_destroy_inode,
        .statfs = simple_statfs,
        .alloc_inode = mqueue_alloc_inode,
        .destroy_inode = mqueue_destroy_inode,
        .statfs = simple_statfs,
@@ -1217,59 +1233,31 @@ static struct file_system_type mqueue_fs_type = {
        .kill_sb = kill_litter_super,
 };
 
        .kill_sb = kill_litter_super,
 };
 
-static int msg_max_limit_min = MIN_MSGMAX;
-static int msg_max_limit_max = MAX_MSGMAX;
-
-static int msg_maxsize_limit_min = MIN_MSGSIZEMAX;
-static int msg_maxsize_limit_max = MAX_MSGSIZEMAX;
-
-static ctl_table mq_sysctls[] = {
-       {
-               .procname       = "queues_max",
-               .data           = &init_ipc_ns.mq_queues_max,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = &proc_dointvec,
-       },
-       {
-               .procname       = "msg_max",
-               .data           = &init_ipc_ns.mq_msg_max,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = &proc_dointvec_minmax,
-               .extra1         = &msg_max_limit_min,
-               .extra2         = &msg_max_limit_max,
-       },
-       {
-               .procname       = "msgsize_max",
-               .data           = &init_ipc_ns.mq_msgsize_max,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = &proc_dointvec_minmax,
-               .extra1         = &msg_maxsize_limit_min,
-               .extra2         = &msg_maxsize_limit_max,
-       },
-       { .ctl_name = 0 }
-};
+int mq_init_ns(struct ipc_namespace *ns)
+{
+       ns->mq_queues_count  = 0;
+       ns->mq_queues_max    = DFLT_QUEUESMAX;
+       ns->mq_msg_max       = DFLT_MSGMAX;
+       ns->mq_msgsize_max   = DFLT_MSGSIZEMAX;
 
 
-static ctl_table mq_sysctl_dir[] = {
-       {
-               .procname       = "mqueue",
-               .mode           = 0555,
-               .child          = mq_sysctls,
-       },
-       { .ctl_name = 0 }
-};
+       ns->mq_mnt = kern_mount_data(&mqueue_fs_type, ns);
+       if (IS_ERR(ns->mq_mnt)) {
+               int err = PTR_ERR(ns->mq_mnt);
+               ns->mq_mnt = NULL;
+               return err;
+       }
+       return 0;
+}
 
 
-static ctl_table mq_sysctl_root[] = {
-       {
-               .ctl_name       = CTL_FS,
-               .procname       = "fs",
-               .mode           = 0555,
-               .child          = mq_sysctl_dir,
-       },
-       { .ctl_name = 0 }
-};
+void mq_clear_sbinfo(struct ipc_namespace *ns)
+{
+       ns->mq_mnt->mnt_sb->s_fs_info = NULL;
+}
+
+void mq_put_mnt(struct ipc_namespace *ns)
+{
+       mntput(ns->mq_mnt);
+}
 
 static int __init init_mqueue_fs(void)
 {
 
 static int __init init_mqueue_fs(void)
 {
@@ -1282,21 +1270,20 @@ static int __init init_mqueue_fs(void)
                return -ENOMEM;
 
        /* ignore failues - they are not fatal */
                return -ENOMEM;
 
        /* ignore failues - they are not fatal */
-       mq_sysctl_table = register_sysctl_table(mq_sysctl_root);
+       mq_sysctl_table = mq_register_sysctl_table();
 
        error = register_filesystem(&mqueue_fs_type);
        if (error)
                goto out_sysctl;
 
 
        error = register_filesystem(&mqueue_fs_type);
        if (error)
                goto out_sysctl;
 
-       init_ipc_ns.mq_mnt = kern_mount(&mqueue_fs_type);
+       spin_lock_init(&mq_lock);
+
+       init_ipc_ns.mq_mnt = kern_mount_data(&mqueue_fs_type, &init_ipc_ns);
        if (IS_ERR(init_ipc_ns.mq_mnt)) {
                error = PTR_ERR(init_ipc_ns.mq_mnt);
                goto out_filesystem;
        }
 
        if (IS_ERR(init_ipc_ns.mq_mnt)) {
                error = PTR_ERR(init_ipc_ns.mq_mnt);
                goto out_filesystem;
        }
 
-       /* internal initialization - not common for vfs */
-       spin_lock_init(&mq_lock);
-
        return 0;
 
 out_filesystem:
        return 0;
 
 out_filesystem: