blob: c82c215693d7c9c54ae93dfa90d7f162085900d5 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Uwe Zeisbergerf30c2262006-10-03 23:01:26 +02002 * linux/ipc/msgutil.c
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 * 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 <asm/uaccess.h>
17
18#include "util.h"
19
20struct msg_msgseg {
21 struct msg_msgseg* next;
22 /* the next part of the message follows immediately */
23};
24
25#define DATALEN_MSG (PAGE_SIZE-sizeof(struct msg_msg))
26#define DATALEN_SEG (PAGE_SIZE-sizeof(struct msg_msgseg))
27
28struct msg_msg *load_msg(const void __user *src, int len)
29{
30 struct msg_msg *msg;
31 struct msg_msgseg **pseg;
32 int err;
33 int alen;
34
35 alen = len;
36 if (alen > DATALEN_MSG)
37 alen = DATALEN_MSG;
38
Robert P. J. Day5cbded52006-12-13 00:35:56 -080039 msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -070040 if (msg == NULL)
41 return ERR_PTR(-ENOMEM);
42
43 msg->next = NULL;
44 msg->security = NULL;
45
46 if (copy_from_user(msg + 1, src, alen)) {
47 err = -EFAULT;
48 goto out_err;
49 }
50
51 len -= alen;
52 src = ((char __user *)src) + alen;
53 pseg = &msg->next;
54 while (len > 0) {
55 struct msg_msgseg *seg;
56 alen = len;
57 if (alen > DATALEN_SEG)
58 alen = DATALEN_SEG;
Robert P. J. Day5cbded52006-12-13 00:35:56 -080059 seg = kmalloc(sizeof(*seg) + alen,
Linus Torvalds1da177e2005-04-16 15:20:36 -070060 GFP_KERNEL);
61 if (seg == NULL) {
62 err = -ENOMEM;
63 goto out_err;
64 }
65 *pseg = seg;
66 seg->next = NULL;
67 if (copy_from_user(seg + 1, src, alen)) {
68 err = -EFAULT;
69 goto out_err;
70 }
71 pseg = &seg->next;
72 len -= alen;
73 src = ((char __user *)src) + alen;
74 }
75
76 err = security_msg_msg_alloc(msg);
77 if (err)
78 goto out_err;
79
80 return msg;
81
82out_err:
83 free_msg(msg);
84 return ERR_PTR(err);
85}
86
87int store_msg(void __user *dest, struct msg_msg *msg, int len)
88{
89 int alen;
90 struct msg_msgseg *seg;
91
92 alen = len;
93 if (alen > DATALEN_MSG)
94 alen = DATALEN_MSG;
95 if (copy_to_user(dest, msg + 1, alen))
96 return -1;
97
98 len -= alen;
99 dest = ((char __user *)dest) + alen;
100 seg = msg->next;
101 while (len > 0) {
102 alen = len;
103 if (alen > DATALEN_SEG)
104 alen = DATALEN_SEG;
105 if (copy_to_user(dest, seg + 1, alen))
106 return -1;
107 len -= alen;
108 dest = ((char __user *)dest) + alen;
109 seg = seg->next;
110 }
111 return 0;
112}
113
114void free_msg(struct msg_msg *msg)
115{
116 struct msg_msgseg *seg;
117
118 security_msg_msg_free(msg);
119
120 seg = msg->next;
121 kfree(msg);
122 while (seg != NULL) {
123 struct msg_msgseg *tmp = seg->next;
124 kfree(seg);
125 seg = tmp;
126 }
127}