diff options
Diffstat (limited to 'ipc/msgutil.c')
| -rw-r--r-- | ipc/msgutil.c | 127 | 
1 files changed, 127 insertions, 0 deletions
diff --git a/ipc/msgutil.c b/ipc/msgutil.c new file mode 100644 index 00000000000..66cfb87646e --- /dev/null +++ b/ipc/msgutil.c @@ -0,0 +1,127 @@ +/* + * linux/ipc/util.c + * Copyright (C) 1999, 2004 Manfred Spraul + * + * This file is released under GNU General Public Licence version 2 or + * (at your option) any later version. + * + * See the file COPYING for more details. + */ + +#include <linux/spinlock.h> +#include <linux/init.h> +#include <linux/security.h> +#include <linux/slab.h> +#include <linux/ipc.h> +#include <asm/uaccess.h> + +#include "util.h" + +struct msg_msgseg { +	struct msg_msgseg* next; +	/* the next part of the message follows immediately */ +}; + +#define DATALEN_MSG	(PAGE_SIZE-sizeof(struct msg_msg)) +#define DATALEN_SEG	(PAGE_SIZE-sizeof(struct msg_msgseg)) + +struct msg_msg *load_msg(const void __user *src, int len) +{ +	struct msg_msg *msg; +	struct msg_msgseg **pseg; +	int err; +	int alen; + +	alen = len; +	if (alen > DATALEN_MSG) +		alen = DATALEN_MSG; + +	msg = (struct msg_msg *)kmalloc(sizeof(*msg) + alen, GFP_KERNEL); +	if (msg == NULL) +		return ERR_PTR(-ENOMEM); + +	msg->next = NULL; +	msg->security = NULL; + +	if (copy_from_user(msg + 1, src, alen)) { +		err = -EFAULT; +		goto out_err; +	} + +	len -= alen; +	src = ((char __user *)src) + alen; +	pseg = &msg->next; +	while (len > 0) { +		struct msg_msgseg *seg; +		alen = len; +		if (alen > DATALEN_SEG) +			alen = DATALEN_SEG; +		seg = (struct msg_msgseg *)kmalloc(sizeof(*seg) + alen, +						 GFP_KERNEL); +		if (seg == NULL) { +			err = -ENOMEM; +			goto out_err; +		} +		*pseg = seg; +		seg->next = NULL; +		if (copy_from_user(seg + 1, src, alen)) { +			err = -EFAULT; +			goto out_err; +		} +		pseg = &seg->next; +		len -= alen; +		src = ((char __user *)src) + alen; +	} + +	err = security_msg_msg_alloc(msg); +	if (err) +		goto out_err; + +	return msg; + +out_err: +	free_msg(msg); +	return ERR_PTR(err); +} + +int store_msg(void __user *dest, struct msg_msg *msg, int len) +{ +	int alen; +	struct msg_msgseg *seg; + +	alen = len; +	if (alen > DATALEN_MSG) +		alen = DATALEN_MSG; +	if (copy_to_user(dest, msg + 1, alen)) +		return -1; + +	len -= alen; +	dest = ((char __user *)dest) + alen; +	seg = msg->next; +	while (len > 0) { +		alen = len; +		if (alen > DATALEN_SEG) +			alen = DATALEN_SEG; +		if (copy_to_user(dest, seg + 1, alen)) +			return -1; +		len -= alen; +		dest = ((char __user *)dest) + alen; +		seg = seg->next; +	} +	return 0; +} + +void free_msg(struct msg_msg *msg) +{ +	struct msg_msgseg *seg; + +	security_msg_msg_free(msg); + +	seg = msg->next; +	kfree(msg); +	while (seg != NULL) { +		struct msg_msgseg *tmp = seg->next; +		kfree(seg); +		seg = tmp; +	} +}  |