/*
* POSIX message queues filesystem for Linux.
*
* Copyright (C) 2003,2004 Krzysztof Benedyczak (golbi@mat.uni.torun.pl)
* Michal Wronski (michal.wronski@gmail.com)
*
* Spinlocks: Mohamed Abbas (abbas.mohamed@intel.com)
* Lockless receive & send, fd based notify:
* Manfred Spraul (manfred@colorfullife.com)
*
* Audit: George Wilson (ltcgcw@us.ibm.com)
*
* This file is released under the GPL.
*/
#include <linux/capability.h>
#include <linux/init.h>
#include <linux/pagemap.h>
#include <linux/file.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/sysctl.h>
#include <linux/poll.h>
#include <linux/mqueue.h>
#include <linux/msg.h>
#include <linux/skbuff.h>
#include <linux/vmalloc.h>
#include <linux/netlink.h>
#include <linux/syscalls.h>
#include <linux/audit.h>
#include <linux/signal.h>
#include <linux/mutex.h>
#include <linux/nsproxy.h>
#include <linux/pid.h>
#include <linux/ipc_namespace.h>
#include <linux/user_namespace.h>
#include <linux/slab.h>
#include <linux/sched/wake_q.h>
#include <linux/sched/signal.h>
#include <linux/sched/user.h>
#include <net/sock.h>
#include "util.h"
#define MQUEUE_MAGIC 0x19800202
#define DIRENT_SIZE 20
#define FILENT_SIZE 80
#define SEND 0
#define RECV 1
#define STATE_NONE 0
#define STATE_READY 1
struct posix_msg_tree_node {
struct rb_node rb_node;
struct list_head msg_list;
int priority;
};
struct ext_wait_queue { /* queue of sleeping tasks */
struct task_struct *task;
struct list_head list;
struct msg_msg *msg; /* ptr of loaded message */
int state; /* one of STATE_* values */
};
struct mqueue_inode_info {
spinlock_t lock;
struct inode vfs_inode;
wait_queue_head_t wait_q;
struct rb_root msg_tree;
struct posix_msg_tree_node *node_cache;
struct mq_attr attr;
struct sigevent notify;
struct pid *notify_owner;
struct user_namespace *notify_user_ns;
struct user_struct *user; /* user who created, for accounting */
struct sock *notify_sock;
struct sk_buff *notify_cookie;
/* for tasks waiting for free space and messages, respectively */
struct ext_wait_queue e_wait_q[2];
unsigned long qsize; /* size of queue in memory (sum of all msgs) */
};
static const struct inode_operations mqueue_dir_inode_operations;
static const struct file_operations mqueue_file_operations;
static const struct super_operations mqueue_super_ops;
static void remove_notification(struct mqueue_inode_info *info);
static struct kmem_cache *mqueue_inode_cachep;
static struct ctl_table_header *mq_sysctl_table;
static inline struct mqueue_inode_info *MQUEUE_I(struct inode *inode)
{
return container_of(inode, struct mqueue_inode_info, vfs_inode);
}
/*
* This routine should be called with the mq_lock held.
*/
static inline struct ipc_namespace *__get_ns_from_inode(struct inode *inode)
{
return get_ipc_ns(inode->i_sb->s_fs_info);
}
static struct ipc_namespace *get_ns_from_inode(struct inode *inode)
{
struct ipc_namespace *ns;
spin_lock(&mq_lock);
ns = __get_ns_from_inode(inode);
spin_unlock(&mq_lock);
return ns;
}
/* Auxiliary functions to manipulate messages' list */
static int msg_insert(struct msg_msg *msg, struct mqueue_inode_info *info)
{
struct rb_node **p, *parent = NULL;
struct posix_msg_tree_node *leaf;
p = &info->msg_tree.rb_node;
while (*p) {
parent = *p;
leaf = rb_entry(parent, struct posix_msg_tree_node, rb_node);
if (likely(leaf->priority == msg->m_type))
goto insert_msg;
else if (msg->m_type < leaf->priority)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
if (info->node_cache) {
leaf = info->node_cache;
info->node_cache = NULL;
} else {
le
|