Merge git://git.infradead.org/mtd-2.6
[pandora-kernel.git] / ipc / msgutil.c
1 /*
2  * linux/ipc/msgutil.c
3  * Copyright (C) 1999, 2004 Manfred Spraul
4  *
5  * This file is released under GNU General Public Licence version 2 or
6  * (at your option) any later version.
7  *
8  * See the file COPYING for more details.
9  */
10
11 #include <linux/spinlock.h>
12 #include <linux/init.h>
13 #include <linux/security.h>
14 #include <linux/slab.h>
15 #include <linux/ipc.h>
16 #include <linux/ipc_namespace.h>
17 #include <asm/uaccess.h>
18
19 #include "util.h"
20
21 DEFINE_SPINLOCK(mq_lock);
22
23 /*
24  * The next 2 defines are here bc this is the only file
25  * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE
26  * and not CONFIG_IPC_NS.
27  */
28 struct ipc_namespace init_ipc_ns = {
29         .count          = ATOMIC_INIT(1),
30         .user_ns = &init_user_ns,
31 };
32
33 atomic_t nr_ipc_ns = ATOMIC_INIT(1);
34
35 struct msg_msgseg {
36         struct msg_msgseg* next;
37         /* the next part of the message follows immediately */
38 };
39
40 #define DATALEN_MSG     (PAGE_SIZE-sizeof(struct msg_msg))
41 #define DATALEN_SEG     (PAGE_SIZE-sizeof(struct msg_msgseg))
42
43 struct msg_msg *load_msg(const void __user *src, int len)
44 {
45         struct msg_msg *msg;
46         struct msg_msgseg **pseg;
47         int err;
48         int alen;
49
50         alen = len;
51         if (alen > DATALEN_MSG)
52                 alen = DATALEN_MSG;
53
54         msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL);
55         if (msg == NULL)
56                 return ERR_PTR(-ENOMEM);
57
58         msg->next = NULL;
59         msg->security = NULL;
60
61         if (copy_from_user(msg + 1, src, alen)) {
62                 err = -EFAULT;
63                 goto out_err;
64         }
65
66         len -= alen;
67         src = ((char __user *)src) + alen;
68         pseg = &msg->next;
69         while (len > 0) {
70                 struct msg_msgseg *seg;
71                 alen = len;
72                 if (alen > DATALEN_SEG)
73                         alen = DATALEN_SEG;
74                 seg = kmalloc(sizeof(*seg) + alen,
75                                                  GFP_KERNEL);
76                 if (seg == NULL) {
77                         err = -ENOMEM;
78                         goto out_err;
79                 }
80                 *pseg = seg;
81                 seg->next = NULL;
82                 if (copy_from_user(seg + 1, src, alen)) {
83                         err = -EFAULT;
84                         goto out_err;
85                 }
86                 pseg = &seg->next;
87                 len -= alen;
88                 src = ((char __user *)src) + alen;
89         }
90
91         err = security_msg_msg_alloc(msg);
92         if (err)
93                 goto out_err;
94
95         return msg;
96
97 out_err:
98         free_msg(msg);
99         return ERR_PTR(err);
100 }
101
102 int store_msg(void __user *dest, struct msg_msg *msg, int len)
103 {
104         int alen;
105         struct msg_msgseg *seg;
106
107         alen = len;
108         if (alen > DATALEN_MSG)
109                 alen = DATALEN_MSG;
110         if (copy_to_user(dest, msg + 1, alen))
111                 return -1;
112
113         len -= alen;
114         dest = ((char __user *)dest) + alen;
115         seg = msg->next;
116         while (len > 0) {
117                 alen = len;
118                 if (alen > DATALEN_SEG)
119                         alen = DATALEN_SEG;
120                 if (copy_to_user(dest, seg + 1, alen))
121                         return -1;
122                 len -= alen;
123                 dest = ((char __user *)dest) + alen;
124                 seg = seg->next;
125         }
126         return 0;
127 }
128
129 void free_msg(struct msg_msg *msg)
130 {
131         struct msg_msgseg *seg;
132
133         security_msg_msg_free(msg);
134
135         seg = msg->next;
136         kfree(msg);
137         while (seg != NULL) {
138                 struct msg_msgseg *tmp = seg->next;
139                 kfree(seg);
140                 seg = tmp;
141         }
142 }