| /* | |
| * linux/ipc/msgutil.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 <linux/msg.h> | |
| #include <linux/ipc_namespace.h> | |
| #include <linux/utsname.h> | |
| #include <linux/proc_ns.h> | |
| #include <linux/uaccess.h> | |
| #include "util.h" | |
| DEFINE_SPINLOCK(mq_lock); | |
| /* | |
| * The next 2 defines are here bc this is the only file | |
| * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE | |
| * and not CONFIG_IPC_NS. | |
| */ | |
| struct ipc_namespace init_ipc_ns = { | |
| .count = ATOMIC_INIT(1), | |
| .user_ns = &init_user_ns, | |
| .ns.inum = PROC_IPC_INIT_INO, | |
| #ifdef CONFIG_IPC_NS | |
| .ns.ops = &ipcns_operations, | |
| #endif | |
| }; | |
| atomic_t nr_ipc_ns = ATOMIC_INIT(1); | |
| struct msg_msgseg { | |
| struct msg_msgseg *next; | |
| /* the next part of the message follows immediately */ | |
| }; | |
| #define DATALEN_MSG ((size_t)PAGE_SIZE-sizeof(struct msg_msg)) | |
| #define DATALEN_SEG ((size_t)PAGE_SIZE-sizeof(struct msg_msgseg)) | |
| static struct msg_msg *alloc_msg(size_t len) | |
| { | |
| struct msg_msg *msg; | |
| struct msg_msgseg **pseg; | |
| size_t alen; | |
| alen = min(len, DATALEN_MSG); | |
| msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL); | |
| if (msg == NULL) | |
| return NULL; | |
| msg->next = NULL; | |
| msg->security = NULL; | |
| len -= alen; | |
| pseg = &msg->next; | |
| while (len > 0) { | |
| struct msg_msgseg *seg; | |
| alen = min(len, DATALEN_SEG); | |
| seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL); | |
| if (seg == NULL) | |
| goto out_err; | |
| *pseg = seg; | |
| seg->next = NULL; | |
| pseg = &seg->next; | |
| len -= alen; | |
| } | |
| return msg; | |
| out_err: | |
| free_msg(msg); | |
| return NULL; | |
| } | |
| struct msg_msg *load_msg(const void __user *src, size_t len) | |
| { | |
| struct msg_msg *msg; | |
| struct msg_msgseg *seg; | |
| int err = -EFAULT; | |
| size_t alen; | |
| msg = alloc_msg(len); | |
| if (msg == NULL) | |
| return ERR_PTR(-ENOMEM); | |
| alen = min(len, DATALEN_MSG); | |
| if (copy_from_user(msg + 1, src, alen)) | |
| goto out_err; | |
| for (seg = msg->next; seg != NULL; seg = seg->next) { | |
| len -= alen; | |
| src = (char __user *)src + alen; | |
| alen = min(len, DATALEN_SEG); | |
| if (copy_from_user(seg + 1, src, alen)) | |
| goto out_err; | |
| } | |
| err = security_msg_msg_alloc(msg); | |
| if (err) | |
| goto out_err; | |
| return msg; | |
| out_err: | |
| free_msg(msg); | |
| return ERR_PTR(err); | |
| } | |
| #ifdef CONFIG_CHECKPOINT_RESTORE | |
| struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst) | |
| { | |
| struct msg_msgseg *dst_pseg, *src_pseg; | |
| size_t len = src->m_ts; | |
| size_t alen; | |
| if (src->m_ts > dst->m_ts) | |
| return ERR_PTR(-EINVAL); | |
| alen = min(len, DATALEN_MSG); | |
| memcpy(dst + 1, src + 1, alen); | |
| for (dst_pseg = dst->next, src_pseg = src->next; | |
| src_pseg != NULL; | |
| dst_pseg = dst_pseg->next, src_pseg = src_pseg->next) { | |
| len -= alen; | |
| alen = min(len, DATALEN_SEG); | |
| memcpy(dst_pseg + 1, src_pseg + 1, alen); | |
| } | |
| dst->m_type = src->m_type; | |
| dst->m_ts = src->m_ts; | |
| return dst; | |
| } | |
| #else | |
| struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst) | |
| { | |
| return ERR_PTR(-ENOSYS); | |
| } | |
| #endif | |
| int store_msg(void __user *dest, struct msg_msg *msg, size_t len) | |
| { | |
| size_t alen; | |
| struct msg_msgseg *seg; | |
| alen = min(len, DATALEN_MSG); | |
| if (copy_to_user(dest, msg + 1, alen)) | |
| return -1; | |
| for (seg = msg->next; seg != NULL; seg = seg->next) { | |
| len -= alen; | |
| dest = (char __user *)dest + alen; | |
| alen = min(len, DATALEN_SEG); | |
| if (copy_to_user(dest, seg + 1, alen)) | |
| return -1; | |
| } | |
| 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; | |
| } | |
| } |