2005-04-17 02:20:36 +04:00
/*
* linux / ipc / msg . c
2006-07-30 14:04:11 +04:00
* Copyright ( C ) 1992 Krishna Balasubramanian
2005-04-17 02:20:36 +04:00
*
* Removed all the remaining kerneld mess
* Catch the - EFAULT stuff properly
* Use GFP_KERNEL for messages as in 1.2
* Fixed up the unchecked user space derefs
* Copyright ( C ) 1998 Alan Cox & Andi Kleen
*
* / proc / sysvipc / msg support ( c ) 1999 Dragos Acostachioaie < dragos @ iname . com >
*
* mostly rewritten , threaded and wake - one semantics added
* MSGMAX limit removed , sysctl ' s added
2006-01-15 04:43:54 +03:00
* ( c ) 1999 Manfred Spraul < manfred @ colorfullife . com >
2006-04-03 01:07:33 +04:00
*
* support for audit of ipc object properties and permission changes
* Dustin Kirkland < dustin . kirkland @ us . ibm . com >
2006-10-02 13:18:21 +04:00
*
* namespaces support
* OpenVZ , SWsoft Inc .
* Pavel Emelianov < xemul @ openvz . org >
2005-04-17 02:20:36 +04:00
*/
2006-01-11 23:17:46 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/slab.h>
# include <linux/msg.h>
# include <linux/spinlock.h>
# include <linux/init.h>
# include <linux/proc_fs.h>
# include <linux/list.h>
# include <linux/security.h>
# include <linux/sched.h>
# include <linux/syscalls.h>
# include <linux/audit.h>
2005-09-07 02:17:10 +04:00
# include <linux/seq_file.h>
2007-10-19 10:40:54 +04:00
# include <linux/rwsem.h>
2006-10-02 13:18:21 +04:00
# include <linux/nsproxy.h>
2006-03-26 13:37:17 +04:00
2005-04-17 02:20:36 +04:00
# include <asm/current.h>
# include <asm/uaccess.h>
# include "util.h"
2006-07-30 14:04:11 +04:00
/*
* one msg_receiver structure for each sleeping receiver :
*/
2005-04-17 02:20:36 +04:00
struct msg_receiver {
2006-07-30 14:04:11 +04:00
struct list_head r_list ;
struct task_struct * r_tsk ;
2005-04-17 02:20:36 +04:00
2006-07-30 14:04:11 +04:00
int r_mode ;
long r_msgtype ;
long r_maxsize ;
2005-04-17 02:20:36 +04:00
2006-11-04 20:55:00 +03:00
struct msg_msg * volatile r_msg ;
2005-04-17 02:20:36 +04:00
} ;
/* one msg_sender for each sleeping sender */
struct msg_sender {
2006-07-30 14:04:11 +04:00
struct list_head list ;
struct task_struct * tsk ;
2005-04-17 02:20:36 +04:00
} ;
# define SEARCH_ANY 1
# define SEARCH_EQUAL 2
# define SEARCH_NOTEQUAL 3
# define SEARCH_LESSEQUAL 4
2006-10-02 13:18:21 +04:00
static struct ipc_ids init_msg_ids ;
2005-04-17 02:20:36 +04:00
2006-10-02 13:18:21 +04:00
# define msg_ids(ns) (*((ns)->ids[IPC_MSG_IDS]))
2005-04-17 02:20:36 +04:00
2006-10-02 13:18:21 +04:00
# define msg_unlock(msq) ipc_unlock(&(msq)->q_perm)
2007-10-19 10:40:55 +04:00
# define msg_buildid(id, seq) ipc_buildid(id, seq)
2006-10-02 13:18:21 +04:00
2007-10-19 10:40:48 +04:00
static void freeque ( struct ipc_namespace * , struct msg_queue * ) ;
2007-10-19 10:40:49 +04:00
static int newque ( struct ipc_namespace * , struct ipc_params * ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PROC_FS
2005-09-07 02:17:10 +04:00
static int sysvipc_msg_proc_show ( struct seq_file * s , void * it ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-07-16 10:40:58 +04:00
static void __msg_init_ns ( struct ipc_namespace * ns , struct ipc_ids * ids )
2006-10-02 13:18:21 +04:00
{
ns - > ids [ IPC_MSG_IDS ] = ids ;
ns - > msg_ctlmax = MSGMAX ;
ns - > msg_ctlmnb = MSGMNB ;
ns - > msg_ctlmni = MSGMNI ;
2007-10-19 10:40:56 +04:00
atomic_set ( & ns - > msg_bytes , 0 ) ;
atomic_set ( & ns - > msg_hdrs , 0 ) ;
2007-10-19 10:40:48 +04:00
ipc_init_ids ( ids ) ;
2006-10-02 13:18:21 +04:00
}
int msg_init_ns ( struct ipc_namespace * ns )
{
struct ipc_ids * ids ;
ids = kmalloc ( sizeof ( struct ipc_ids ) , GFP_KERNEL ) ;
if ( ids = = NULL )
return - ENOMEM ;
__msg_init_ns ( ns , ids ) ;
return 0 ;
}
void msg_exit_ns ( struct ipc_namespace * ns )
{
struct msg_queue * msq ;
2007-10-19 10:40:48 +04:00
int next_id ;
int total , in_use ;
2006-10-02 13:18:21 +04:00
2007-10-19 10:40:54 +04:00
down_write ( & msg_ids ( ns ) . rw_mutex ) ;
2007-10-19 10:40:48 +04:00
in_use = msg_ids ( ns ) . in_use ;
for ( total = 0 , next_id = 0 ; total < in_use ; next_id + + ) {
msq = idr_find ( & msg_ids ( ns ) . ipcs_idr , next_id ) ;
2006-10-02 13:18:21 +04:00
if ( msq = = NULL )
continue ;
2007-10-19 10:40:48 +04:00
ipc_lock_by_ptr ( & msq - > q_perm ) ;
freeque ( ns , msq ) ;
total + + ;
2006-10-02 13:18:21 +04:00
}
2007-10-19 10:40:54 +04:00
up_write ( & msg_ids ( ns ) . rw_mutex ) ;
2006-10-02 13:18:21 +04:00
kfree ( ns - > ids [ IPC_MSG_IDS ] ) ;
ns - > ids [ IPC_MSG_IDS ] = NULL ;
}
2006-07-30 14:04:11 +04:00
void __init msg_init ( void )
2005-04-17 02:20:36 +04:00
{
2006-10-02 13:18:21 +04:00
__msg_init_ns ( & init_ipc_ns , & init_msg_ids ) ;
2005-09-07 02:17:10 +04:00
ipc_init_proc_interface ( " sysvipc/msg " ,
" key msqid perms cbytes qnum lspid lrpid uid gid cuid cgid stime rtime ctime \n " ,
2006-10-02 13:18:21 +04:00
IPC_MSG_IDS , sysvipc_msg_proc_show ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-19 10:40:54 +04:00
/*
* This routine is called in the paths where the rw_mutex is held to protect
* access to the idr tree .
*/
static inline struct msg_queue * msg_lock_check_down ( struct ipc_namespace * ns ,
int id )
{
struct kern_ipc_perm * ipcp = ipc_lock_check_down ( & msg_ids ( ns ) , id ) ;
return container_of ( ipcp , struct msg_queue , q_perm ) ;
}
/*
* msg_lock_ ( check_ ) routines are called in the paths where the rw_mutex
* is not held .
*/
2007-10-19 10:40:51 +04:00
static inline struct msg_queue * msg_lock ( struct ipc_namespace * ns , int id )
{
2007-10-19 10:40:51 +04:00
struct kern_ipc_perm * ipcp = ipc_lock ( & msg_ids ( ns ) , id ) ;
return container_of ( ipcp , struct msg_queue , q_perm ) ;
2007-10-19 10:40:51 +04:00
}
static inline struct msg_queue * msg_lock_check ( struct ipc_namespace * ns ,
int id )
{
2007-10-19 10:40:51 +04:00
struct kern_ipc_perm * ipcp = ipc_lock_check ( & msg_ids ( ns ) , id ) ;
return container_of ( ipcp , struct msg_queue , q_perm ) ;
2007-10-19 10:40:51 +04:00
}
2007-10-19 10:40:48 +04:00
static inline void msg_rmid ( struct ipc_namespace * ns , struct msg_queue * s )
{
ipc_rmid ( & msg_ids ( ns ) , & s - > q_perm ) ;
}
2007-10-19 10:40:53 +04:00
/**
* newque - Create a new msg queue
* @ ns : namespace
* @ params : ptr to the structure that contains the key and msgflg
*
2007-10-19 10:40:54 +04:00
* Called with msg_ids . rw_mutex held ( writer )
2007-10-19 10:40:53 +04:00
*/
2007-10-19 10:40:49 +04:00
static int newque ( struct ipc_namespace * ns , struct ipc_params * params )
2005-04-17 02:20:36 +04:00
{
struct msg_queue * msq ;
2006-07-30 14:04:11 +04:00
int id , retval ;
2007-10-19 10:40:49 +04:00
key_t key = params - > key ;
int msgflg = params - > flg ;
2005-04-17 02:20:36 +04:00
2006-07-30 14:04:11 +04:00
msq = ipc_rcu_alloc ( sizeof ( * msq ) ) ;
if ( ! msq )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
2006-07-30 14:04:11 +04:00
msq - > q_perm . mode = msgflg & S_IRWXUGO ;
2005-04-17 02:20:36 +04:00
msq - > q_perm . key = key ;
msq - > q_perm . security = NULL ;
retval = security_msg_queue_alloc ( msq ) ;
if ( retval ) {
ipc_rcu_putref ( msq ) ;
return retval ;
}
2007-10-19 10:40:48 +04:00
/*
* ipc_addid ( ) locks msq
*/
2006-10-02 13:18:21 +04:00
id = ipc_addid ( & msg_ids ( ns ) , & msq - > q_perm , ns - > msg_ctlmni ) ;
2007-10-19 10:40:57 +04:00
if ( id < 0 ) {
2005-04-17 02:20:36 +04:00
security_msg_queue_free ( msq ) ;
ipc_rcu_putref ( msq ) ;
2007-10-19 10:40:57 +04:00
return id ;
2005-04-17 02:20:36 +04:00
}
2007-10-19 10:40:55 +04:00
msq - > q_perm . id = msg_buildid ( id , msq - > q_perm . seq ) ;
2005-04-17 02:20:36 +04:00
msq - > q_stime = msq - > q_rtime = 0 ;
msq - > q_ctime = get_seconds ( ) ;
msq - > q_cbytes = msq - > q_qnum = 0 ;
2006-10-02 13:18:21 +04:00
msq - > q_qbytes = ns - > msg_ctlmnb ;
2005-04-17 02:20:36 +04:00
msq - > q_lspid = msq - > q_lrpid = 0 ;
INIT_LIST_HEAD ( & msq - > q_messages ) ;
INIT_LIST_HEAD ( & msq - > q_receivers ) ;
INIT_LIST_HEAD ( & msq - > q_senders ) ;
2007-10-19 10:40:48 +04:00
2005-04-17 02:20:36 +04:00
msg_unlock ( msq ) ;
2007-10-19 10:40:48 +04:00
return msq - > q_perm . id ;
2005-04-17 02:20:36 +04:00
}
2006-07-30 14:04:11 +04:00
static inline void ss_add ( struct msg_queue * msq , struct msg_sender * mss )
2005-04-17 02:20:36 +04:00
{
2006-07-30 14:04:11 +04:00
mss - > tsk = current ;
current - > state = TASK_INTERRUPTIBLE ;
list_add_tail ( & mss - > list , & msq - > q_senders ) ;
2005-04-17 02:20:36 +04:00
}
2006-07-30 14:04:11 +04:00
static inline void ss_del ( struct msg_sender * mss )
2005-04-17 02:20:36 +04:00
{
2006-07-30 14:04:11 +04:00
if ( mss - > list . next ! = NULL )
2005-04-17 02:20:36 +04:00
list_del ( & mss - > list ) ;
}
2006-07-30 14:04:11 +04:00
static void ss_wakeup ( struct list_head * h , int kill )
2005-04-17 02:20:36 +04:00
{
struct list_head * tmp ;
tmp = h - > next ;
while ( tmp ! = h ) {
2006-07-30 14:04:11 +04:00
struct msg_sender * mss ;
mss = list_entry ( tmp , struct msg_sender , list ) ;
2005-04-17 02:20:36 +04:00
tmp = tmp - > next ;
2006-07-30 14:04:11 +04:00
if ( kill )
mss - > list . next = NULL ;
2005-04-17 02:20:36 +04:00
wake_up_process ( mss - > tsk ) ;
}
}
2006-07-30 14:04:11 +04:00
static void expunge_all ( struct msg_queue * msq , int res )
2005-04-17 02:20:36 +04:00
{
struct list_head * tmp ;
tmp = msq - > q_receivers . next ;
while ( tmp ! = & msq - > q_receivers ) {
2006-07-30 14:04:11 +04:00
struct msg_receiver * msr ;
msr = list_entry ( tmp , struct msg_receiver , r_list ) ;
2005-04-17 02:20:36 +04:00
tmp = tmp - > next ;
msr - > r_msg = NULL ;
wake_up_process ( msr - > r_tsk ) ;
smp_mb ( ) ;
msr - > r_msg = ERR_PTR ( res ) ;
}
}
2006-07-30 14:04:11 +04:00
/*
* freeque ( ) wakes up waiters on the sender and receiver waiting queue ,
2007-10-19 10:40:53 +04:00
* removes the message queue from message queue ID IDR , and cleans up all the
* messages associated with this queue .
2005-04-17 02:20:36 +04:00
*
2007-10-19 10:40:54 +04:00
* msg_ids . rw_mutex ( writer ) and the spinlock for this message queue are held
* before freeque ( ) is called . msg_ids . rw_mutex remains locked on exit .
2005-04-17 02:20:36 +04:00
*/
2007-10-19 10:40:48 +04:00
static void freeque ( struct ipc_namespace * ns , struct msg_queue * msq )
2005-04-17 02:20:36 +04:00
{
struct list_head * tmp ;
2006-07-30 14:04:11 +04:00
expunge_all ( msq , - EIDRM ) ;
ss_wakeup ( & msq - > q_senders , 1 ) ;
2007-10-19 10:40:48 +04:00
msg_rmid ( ns , msq ) ;
2005-04-17 02:20:36 +04:00
msg_unlock ( msq ) ;
2006-07-30 14:04:11 +04:00
2005-04-17 02:20:36 +04:00
tmp = msq - > q_messages . next ;
2006-07-30 14:04:11 +04:00
while ( tmp ! = & msq - > q_messages ) {
struct msg_msg * msg = list_entry ( tmp , struct msg_msg , m_list ) ;
2005-04-17 02:20:36 +04:00
tmp = tmp - > next ;
2007-10-19 10:40:56 +04:00
atomic_dec ( & ns - > msg_hdrs ) ;
2005-04-17 02:20:36 +04:00
free_msg ( msg ) ;
}
2007-10-19 10:40:56 +04:00
atomic_sub ( msq - > q_cbytes , & ns - > msg_bytes ) ;
2005-04-17 02:20:36 +04:00
security_msg_queue_free ( msq ) ;
ipc_rcu_putref ( msq ) ;
}
2007-10-19 10:40:53 +04:00
/*
2007-10-19 10:40:54 +04:00
* Called with msg_ids . rw_mutex and ipcp locked .
2007-10-19 10:40:53 +04:00
*/
2007-10-19 10:40:51 +04:00
static inline int msg_security ( struct kern_ipc_perm * ipcp , int msgflg )
2007-10-19 10:40:49 +04:00
{
2007-10-19 10:40:51 +04:00
struct msg_queue * msq = container_of ( ipcp , struct msg_queue , q_perm ) ;
return security_msg_queue_associate ( msq , msgflg ) ;
2007-10-19 10:40:49 +04:00
}
2006-07-30 14:04:11 +04:00
asmlinkage long sys_msgget ( key_t key , int msgflg )
2005-04-17 02:20:36 +04:00
{
2006-10-02 13:18:21 +04:00
struct ipc_namespace * ns ;
2007-10-19 10:40:49 +04:00
struct ipc_ops msg_ops ;
struct ipc_params msg_params ;
2006-10-02 13:18:21 +04:00
ns = current - > nsproxy - > ipc_ns ;
2007-10-19 10:40:48 +04:00
2007-10-19 10:40:49 +04:00
msg_ops . getnew = newque ;
msg_ops . associate = msg_security ;
msg_ops . more_checks = NULL ;
msg_params . key = key ;
msg_params . flg = msgflg ;
2006-07-30 14:04:11 +04:00
2007-10-19 10:40:49 +04:00
return ipcget ( ns , & msg_ids ( ns ) , & msg_ops , & msg_params ) ;
2005-04-17 02:20:36 +04:00
}
2006-07-30 14:04:11 +04:00
static inline unsigned long
copy_msqid_to_user ( void __user * buf , struct msqid64_ds * in , int version )
2005-04-17 02:20:36 +04:00
{
switch ( version ) {
case IPC_64 :
2006-07-30 14:04:11 +04:00
return copy_to_user ( buf , in , sizeof ( * in ) ) ;
2005-04-17 02:20:36 +04:00
case IPC_OLD :
2006-07-30 14:04:11 +04:00
{
2005-04-17 02:20:36 +04:00
struct msqid_ds out ;
2006-07-30 14:04:11 +04:00
memset ( & out , 0 , sizeof ( out ) ) ;
2005-04-17 02:20:36 +04:00
ipc64_perm_to_ipc_perm ( & in - > msg_perm , & out . msg_perm ) ;
out . msg_stime = in - > msg_stime ;
out . msg_rtime = in - > msg_rtime ;
out . msg_ctime = in - > msg_ctime ;
2006-07-30 14:04:11 +04:00
if ( in - > msg_cbytes > USHRT_MAX )
2005-04-17 02:20:36 +04:00
out . msg_cbytes = USHRT_MAX ;
else
out . msg_cbytes = in - > msg_cbytes ;
out . msg_lcbytes = in - > msg_cbytes ;
2006-07-30 14:04:11 +04:00
if ( in - > msg_qnum > USHRT_MAX )
2005-04-17 02:20:36 +04:00
out . msg_qnum = USHRT_MAX ;
else
out . msg_qnum = in - > msg_qnum ;
2006-07-30 14:04:11 +04:00
if ( in - > msg_qbytes > USHRT_MAX )
2005-04-17 02:20:36 +04:00
out . msg_qbytes = USHRT_MAX ;
else
out . msg_qbytes = in - > msg_qbytes ;
out . msg_lqbytes = in - > msg_qbytes ;
out . msg_lspid = in - > msg_lspid ;
out . msg_lrpid = in - > msg_lrpid ;
2006-07-30 14:04:11 +04:00
return copy_to_user ( buf , & out , sizeof ( out ) ) ;
}
2005-04-17 02:20:36 +04:00
default :
return - EINVAL ;
}
}
struct msq_setbuf {
unsigned long qbytes ;
uid_t uid ;
gid_t gid ;
mode_t mode ;
} ;
2006-07-30 14:04:11 +04:00
static inline unsigned long
copy_msqid_from_user ( struct msq_setbuf * out , void __user * buf , int version )
2005-04-17 02:20:36 +04:00
{
switch ( version ) {
case IPC_64 :
2006-07-30 14:04:11 +04:00
{
2005-04-17 02:20:36 +04:00
struct msqid64_ds tbuf ;
2006-07-30 14:04:11 +04:00
if ( copy_from_user ( & tbuf , buf , sizeof ( tbuf ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
out - > qbytes = tbuf . msg_qbytes ;
out - > uid = tbuf . msg_perm . uid ;
out - > gid = tbuf . msg_perm . gid ;
out - > mode = tbuf . msg_perm . mode ;
return 0 ;
2006-07-30 14:04:11 +04:00
}
2005-04-17 02:20:36 +04:00
case IPC_OLD :
2006-07-30 14:04:11 +04:00
{
2005-04-17 02:20:36 +04:00
struct msqid_ds tbuf_old ;
2006-07-30 14:04:11 +04:00
if ( copy_from_user ( & tbuf_old , buf , sizeof ( tbuf_old ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
out - > uid = tbuf_old . msg_perm . uid ;
out - > gid = tbuf_old . msg_perm . gid ;
out - > mode = tbuf_old . msg_perm . mode ;
2006-07-30 14:04:11 +04:00
if ( tbuf_old . msg_qbytes = = 0 )
2005-04-17 02:20:36 +04:00
out - > qbytes = tbuf_old . msg_lqbytes ;
else
out - > qbytes = tbuf_old . msg_qbytes ;
return 0 ;
2006-07-30 14:04:11 +04:00
}
2005-04-17 02:20:36 +04:00
default :
return - EINVAL ;
}
}
2006-07-30 14:04:11 +04:00
asmlinkage long sys_msgctl ( int msqid , int cmd , struct msqid_ds __user * buf )
2005-04-17 02:20:36 +04:00
{
struct kern_ipc_perm * ipcp ;
2007-07-17 13:40:59 +04:00
struct msq_setbuf uninitialized_var ( setbuf ) ;
2006-07-30 14:04:11 +04:00
struct msg_queue * msq ;
int err , version ;
2006-10-02 13:18:21 +04:00
struct ipc_namespace * ns ;
2006-07-30 14:04:11 +04:00
2005-04-17 02:20:36 +04:00
if ( msqid < 0 | | cmd < 0 )
return - EINVAL ;
version = ipc_parse_version ( & cmd ) ;
2006-10-02 13:18:21 +04:00
ns = current - > nsproxy - > ipc_ns ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
2006-07-30 14:04:11 +04:00
case IPC_INFO :
case MSG_INFO :
{
2005-04-17 02:20:36 +04:00
struct msginfo msginfo ;
int max_id ;
2006-07-30 14:04:11 +04:00
2005-04-17 02:20:36 +04:00
if ( ! buf )
return - EFAULT ;
2006-07-30 14:04:11 +04:00
/*
* We must not return kernel stack data .
2005-04-17 02:20:36 +04:00
* due to padding , it ' s not enough
* to set all member fields .
*/
err = security_msg_queue_msgctl ( NULL , cmd ) ;
if ( err )
return err ;
2006-07-30 14:04:11 +04:00
memset ( & msginfo , 0 , sizeof ( msginfo ) ) ;
2006-10-02 13:18:21 +04:00
msginfo . msgmni = ns - > msg_ctlmni ;
msginfo . msgmax = ns - > msg_ctlmax ;
msginfo . msgmnb = ns - > msg_ctlmnb ;
2005-04-17 02:20:36 +04:00
msginfo . msgssz = MSGSSZ ;
msginfo . msgseg = MSGSEG ;
2007-10-19 10:40:54 +04:00
down_read ( & msg_ids ( ns ) . rw_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( cmd = = MSG_INFO ) {
2006-10-02 13:18:21 +04:00
msginfo . msgpool = msg_ids ( ns ) . in_use ;
2007-10-19 10:40:56 +04:00
msginfo . msgmap = atomic_read ( & ns - > msg_hdrs ) ;
msginfo . msgtql = atomic_read ( & ns - > msg_bytes ) ;
2005-04-17 02:20:36 +04:00
} else {
msginfo . msgmap = MSGMAP ;
msginfo . msgpool = MSGPOOL ;
msginfo . msgtql = MSGTQL ;
}
2007-10-19 10:40:48 +04:00
max_id = ipc_get_maxid ( & msg_ids ( ns ) ) ;
2007-10-19 10:40:54 +04:00
up_read ( & msg_ids ( ns ) . rw_mutex ) ;
2006-07-30 14:04:11 +04:00
if ( copy_to_user ( buf , & msginfo , sizeof ( struct msginfo ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2006-07-30 14:04:11 +04:00
return ( max_id < 0 ) ? 0 : max_id ;
2005-04-17 02:20:36 +04:00
}
2007-10-19 10:40:48 +04:00
case MSG_STAT : /* msqid is an index rather than a msg queue id */
2005-04-17 02:20:36 +04:00
case IPC_STAT :
{
struct msqid64_ds tbuf ;
int success_return ;
2006-07-30 14:04:11 +04:00
2005-04-17 02:20:36 +04:00
if ( ! buf )
return - EFAULT ;
2006-07-30 14:04:11 +04:00
if ( cmd = = MSG_STAT ) {
2007-10-19 10:40:51 +04:00
msq = msg_lock ( ns , msqid ) ;
if ( IS_ERR ( msq ) )
return PTR_ERR ( msq ) ;
2007-10-19 10:40:48 +04:00
success_return = msq - > q_perm . id ;
2005-04-17 02:20:36 +04:00
} else {
2007-10-19 10:40:51 +04:00
msq = msg_lock_check ( ns , msqid ) ;
if ( IS_ERR ( msq ) )
return PTR_ERR ( msq ) ;
2005-04-17 02:20:36 +04:00
success_return = 0 ;
}
err = - EACCES ;
2006-07-30 14:04:11 +04:00
if ( ipcperms ( & msq - > q_perm , S_IRUGO ) )
2005-04-17 02:20:36 +04:00
goto out_unlock ;
err = security_msg_queue_msgctl ( msq , cmd ) ;
if ( err )
goto out_unlock ;
2007-10-19 10:40:51 +04:00
memset ( & tbuf , 0 , sizeof ( tbuf ) ) ;
2005-04-17 02:20:36 +04:00
kernel_to_ipc64_perm ( & msq - > q_perm , & tbuf . msg_perm ) ;
tbuf . msg_stime = msq - > q_stime ;
tbuf . msg_rtime = msq - > q_rtime ;
tbuf . msg_ctime = msq - > q_ctime ;
tbuf . msg_cbytes = msq - > q_cbytes ;
tbuf . msg_qnum = msq - > q_qnum ;
tbuf . msg_qbytes = msq - > q_qbytes ;
tbuf . msg_lspid = msq - > q_lspid ;
tbuf . msg_lrpid = msq - > q_lrpid ;
msg_unlock ( msq ) ;
if ( copy_msqid_to_user ( buf , & tbuf , version ) )
return - EFAULT ;
return success_return ;
}
case IPC_SET :
if ( ! buf )
return - EFAULT ;
2006-07-30 14:04:11 +04:00
if ( copy_msqid_from_user ( & setbuf , buf , version ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
break ;
case IPC_RMID :
break ;
default :
return - EINVAL ;
}
2007-10-19 10:40:54 +04:00
down_write ( & msg_ids ( ns ) . rw_mutex ) ;
msq = msg_lock_check_down ( ns , msqid ) ;
2007-10-19 10:40:51 +04:00
if ( IS_ERR ( msq ) ) {
err = PTR_ERR ( msq ) ;
2005-04-17 02:20:36 +04:00
goto out_up ;
2007-10-19 10:40:51 +04:00
}
2005-04-17 02:20:36 +04:00
ipcp = & msq - > q_perm ;
2006-04-03 01:07:33 +04:00
err = audit_ipc_obj ( ipcp ) ;
if ( err )
goto out_unlock_up ;
2007-07-17 13:40:59 +04:00
if ( cmd = = IPC_SET ) {
2006-07-30 14:04:11 +04:00
err = audit_ipc_set_perm ( setbuf . qbytes , setbuf . uid , setbuf . gid ,
setbuf . mode ) ;
2006-05-17 06:03:48 +04:00
if ( err )
goto out_unlock_up ;
}
2006-04-03 01:07:33 +04:00
2005-04-17 02:20:36 +04:00
err = - EPERM ;
2006-07-30 14:04:11 +04:00
if ( current - > euid ! = ipcp - > cuid & &
2005-04-17 02:20:36 +04:00
current - > euid ! = ipcp - > uid & & ! capable ( CAP_SYS_ADMIN ) )
2006-07-30 14:04:11 +04:00
/* We _could_ check for CAP_CHOWN above, but we don't */
2005-04-17 02:20:36 +04:00
goto out_unlock_up ;
err = security_msg_queue_msgctl ( msq , cmd ) ;
if ( err )
goto out_unlock_up ;
switch ( cmd ) {
case IPC_SET :
{
err = - EPERM ;
2006-10-02 13:18:21 +04:00
if ( setbuf . qbytes > ns - > msg_ctlmnb & & ! capable ( CAP_SYS_RESOURCE ) )
2005-04-17 02:20:36 +04:00
goto out_unlock_up ;
msq - > q_qbytes = setbuf . qbytes ;
ipcp - > uid = setbuf . uid ;
ipcp - > gid = setbuf . gid ;
2006-07-30 14:04:11 +04:00
ipcp - > mode = ( ipcp - > mode & ~ S_IRWXUGO ) |
( S_IRWXUGO & setbuf . mode ) ;
2005-04-17 02:20:36 +04:00
msq - > q_ctime = get_seconds ( ) ;
/* sleeping receivers might be excluded by
* stricter permissions .
*/
2006-07-30 14:04:11 +04:00
expunge_all ( msq , - EAGAIN ) ;
2005-04-17 02:20:36 +04:00
/* sleeping senders might be able to send
* due to a larger queue size .
*/
2006-07-30 14:04:11 +04:00
ss_wakeup ( & msq - > q_senders , 0 ) ;
2005-04-17 02:20:36 +04:00
msg_unlock ( msq ) ;
break ;
}
case IPC_RMID :
2007-10-19 10:40:48 +04:00
freeque ( ns , msq ) ;
2005-04-17 02:20:36 +04:00
break ;
}
err = 0 ;
out_up :
2007-10-19 10:40:54 +04:00
up_write ( & msg_ids ( ns ) . rw_mutex ) ;
2005-04-17 02:20:36 +04:00
return err ;
out_unlock_up :
msg_unlock ( msq ) ;
goto out_up ;
out_unlock :
msg_unlock ( msq ) ;
return err ;
}
2006-07-30 14:04:11 +04:00
static int testmsg ( struct msg_msg * msg , long type , int mode )
2005-04-17 02:20:36 +04:00
{
switch ( mode )
{
case SEARCH_ANY :
return 1 ;
case SEARCH_LESSEQUAL :
2006-07-30 14:04:11 +04:00
if ( msg - > m_type < = type )
2005-04-17 02:20:36 +04:00
return 1 ;
break ;
case SEARCH_EQUAL :
2006-07-30 14:04:11 +04:00
if ( msg - > m_type = = type )
2005-04-17 02:20:36 +04:00
return 1 ;
break ;
case SEARCH_NOTEQUAL :
2006-07-30 14:04:11 +04:00
if ( msg - > m_type ! = type )
2005-04-17 02:20:36 +04:00
return 1 ;
break ;
}
return 0 ;
}
2006-07-30 14:04:11 +04:00
static inline int pipelined_send ( struct msg_queue * msq , struct msg_msg * msg )
2005-04-17 02:20:36 +04:00
{
2006-07-30 14:04:11 +04:00
struct list_head * tmp ;
2005-04-17 02:20:36 +04:00
tmp = msq - > q_receivers . next ;
while ( tmp ! = & msq - > q_receivers ) {
2006-07-30 14:04:11 +04:00
struct msg_receiver * msr ;
msr = list_entry ( tmp , struct msg_receiver , r_list ) ;
2005-04-17 02:20:36 +04:00
tmp = tmp - > next ;
2006-07-30 14:04:11 +04:00
if ( testmsg ( msg , msr - > r_msgtype , msr - > r_mode ) & &
! security_msg_queue_msgrcv ( msq , msg , msr - > r_tsk ,
msr - > r_msgtype , msr - > r_mode ) ) {
2005-04-17 02:20:36 +04:00
list_del ( & msr - > r_list ) ;
2006-07-30 14:04:11 +04:00
if ( msr - > r_maxsize < msg - > m_ts ) {
2005-04-17 02:20:36 +04:00
msr - > r_msg = NULL ;
wake_up_process ( msr - > r_tsk ) ;
smp_mb ( ) ;
msr - > r_msg = ERR_PTR ( - E2BIG ) ;
} else {
msr - > r_msg = NULL ;
2007-10-19 10:40:14 +04:00
msq - > q_lrpid = task_pid_vnr ( msr - > r_tsk ) ;
2005-04-17 02:20:36 +04:00
msq - > q_rtime = get_seconds ( ) ;
wake_up_process ( msr - > r_tsk ) ;
smp_mb ( ) ;
msr - > r_msg = msg ;
2006-07-30 14:04:11 +04:00
2005-04-17 02:20:36 +04:00
return 1 ;
}
}
}
return 0 ;
}
2006-12-07 07:37:48 +03:00
long do_msgsnd ( int msqid , long mtype , void __user * mtext ,
size_t msgsz , int msgflg )
2005-04-17 02:20:36 +04:00
{
struct msg_queue * msq ;
struct msg_msg * msg ;
int err ;
2006-10-02 13:18:21 +04:00
struct ipc_namespace * ns ;
ns = current - > nsproxy - > ipc_ns ;
2006-07-30 14:04:11 +04:00
2006-10-02 13:18:21 +04:00
if ( msgsz > ns - > msg_ctlmax | | ( long ) msgsz < 0 | | msqid < 0 )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
if ( mtype < 1 )
return - EINVAL ;
2006-12-07 07:37:48 +03:00
msg = load_msg ( mtext , msgsz ) ;
2006-07-30 14:04:11 +04:00
if ( IS_ERR ( msg ) )
2005-04-17 02:20:36 +04:00
return PTR_ERR ( msg ) ;
msg - > m_type = mtype ;
msg - > m_ts = msgsz ;
2007-10-19 10:40:51 +04:00
msq = msg_lock_check ( ns , msqid ) ;
if ( IS_ERR ( msq ) ) {
err = PTR_ERR ( msq ) ;
2005-04-17 02:20:36 +04:00
goto out_free ;
2007-10-19 10:40:51 +04:00
}
2005-04-17 02:20:36 +04:00
for ( ; ; ) {
struct msg_sender s ;
2006-07-30 14:04:11 +04:00
err = - EACCES ;
2005-04-17 02:20:36 +04:00
if ( ipcperms ( & msq - > q_perm , S_IWUGO ) )
goto out_unlock_free ;
err = security_msg_queue_msgsnd ( msq , msg , msgflg ) ;
if ( err )
goto out_unlock_free ;
2006-07-30 14:04:11 +04:00
if ( msgsz + msq - > q_cbytes < = msq - > q_qbytes & &
2005-04-17 02:20:36 +04:00
1 + msq - > q_qnum < = msq - > q_qbytes ) {
break ;
}
/* queue full, wait: */
2006-07-30 14:04:11 +04:00
if ( msgflg & IPC_NOWAIT ) {
err = - EAGAIN ;
2005-04-17 02:20:36 +04:00
goto out_unlock_free ;
}
ss_add ( msq , & s ) ;
ipc_rcu_getref ( msq ) ;
msg_unlock ( msq ) ;
schedule ( ) ;
ipc_lock_by_ptr ( & msq - > q_perm ) ;
ipc_rcu_putref ( msq ) ;
if ( msq - > q_perm . deleted ) {
err = - EIDRM ;
goto out_unlock_free ;
}
ss_del ( & s ) ;
2006-07-30 14:04:11 +04:00
2005-04-17 02:20:36 +04:00
if ( signal_pending ( current ) ) {
2006-07-30 14:04:11 +04:00
err = - ERESTARTNOHAND ;
2005-04-17 02:20:36 +04:00
goto out_unlock_free ;
}
}
2007-10-19 10:40:14 +04:00
msq - > q_lspid = task_tgid_vnr ( current ) ;
2005-04-17 02:20:36 +04:00
msq - > q_stime = get_seconds ( ) ;
2006-07-30 14:04:11 +04:00
if ( ! pipelined_send ( msq , msg ) ) {
2005-04-17 02:20:36 +04:00
/* noone is waiting for this message, enqueue it */
2006-07-30 14:04:11 +04:00
list_add_tail ( & msg - > m_list , & msq - > q_messages ) ;
2005-04-17 02:20:36 +04:00
msq - > q_cbytes + = msgsz ;
msq - > q_qnum + + ;
2007-10-19 10:40:56 +04:00
atomic_add ( msgsz , & ns - > msg_bytes ) ;
atomic_inc ( & ns - > msg_hdrs ) ;
2005-04-17 02:20:36 +04:00
}
2006-07-30 14:04:11 +04:00
2005-04-17 02:20:36 +04:00
err = 0 ;
msg = NULL ;
out_unlock_free :
msg_unlock ( msq ) ;
out_free :
2006-07-30 14:04:11 +04:00
if ( msg ! = NULL )
2005-04-17 02:20:36 +04:00
free_msg ( msg ) ;
return err ;
}
2006-12-07 07:37:48 +03:00
asmlinkage long
sys_msgsnd ( int msqid , struct msgbuf __user * msgp , size_t msgsz , int msgflg )
{
long mtype ;
if ( get_user ( mtype , & msgp - > mtype ) )
return - EFAULT ;
return do_msgsnd ( msqid , mtype , msgp - > mtext , msgsz , msgflg ) ;
}
2006-07-30 14:04:11 +04:00
static inline int convert_mode ( long * msgtyp , int msgflg )
2005-04-17 02:20:36 +04:00
{
2006-07-30 14:04:11 +04:00
/*
2005-04-17 02:20:36 +04:00
* find message of correct type .
* msgtyp = 0 = > get first .
* msgtyp > 0 = > get first message of matching type .
2006-07-30 14:04:11 +04:00
* msgtyp < 0 = > get message with least type must be < abs ( msgtype ) .
2005-04-17 02:20:36 +04:00
*/
2006-07-30 14:04:11 +04:00
if ( * msgtyp = = 0 )
2005-04-17 02:20:36 +04:00
return SEARCH_ANY ;
2006-07-30 14:04:11 +04:00
if ( * msgtyp < 0 ) {
* msgtyp = - * msgtyp ;
2005-04-17 02:20:36 +04:00
return SEARCH_LESSEQUAL ;
}
2006-07-30 14:04:11 +04:00
if ( msgflg & MSG_EXCEPT )
2005-04-17 02:20:36 +04:00
return SEARCH_NOTEQUAL ;
return SEARCH_EQUAL ;
}
2006-12-07 07:37:48 +03:00
long do_msgrcv ( int msqid , long * pmtype , void __user * mtext ,
size_t msgsz , long msgtyp , int msgflg )
2005-04-17 02:20:36 +04:00
{
struct msg_queue * msq ;
struct msg_msg * msg ;
int mode ;
2006-10-02 13:18:21 +04:00
struct ipc_namespace * ns ;
2005-04-17 02:20:36 +04:00
if ( msqid < 0 | | ( long ) msgsz < 0 )
return - EINVAL ;
2006-07-30 14:04:11 +04:00
mode = convert_mode ( & msgtyp , msgflg ) ;
2006-10-02 13:18:21 +04:00
ns = current - > nsproxy - > ipc_ns ;
2005-04-17 02:20:36 +04:00
2007-10-19 10:40:51 +04:00
msq = msg_lock_check ( ns , msqid ) ;
if ( IS_ERR ( msq ) )
return PTR_ERR ( msq ) ;
2005-04-17 02:20:36 +04:00
for ( ; ; ) {
struct msg_receiver msr_d ;
2006-07-30 14:04:11 +04:00
struct list_head * tmp ;
2005-04-17 02:20:36 +04:00
msg = ERR_PTR ( - EACCES ) ;
2006-07-30 14:04:11 +04:00
if ( ipcperms ( & msq - > q_perm , S_IRUGO ) )
2005-04-17 02:20:36 +04:00
goto out_unlock ;
msg = ERR_PTR ( - EAGAIN ) ;
tmp = msq - > q_messages . next ;
while ( tmp ! = & msq - > q_messages ) {
struct msg_msg * walk_msg ;
2006-07-30 14:04:11 +04:00
walk_msg = list_entry ( tmp , struct msg_msg , m_list ) ;
if ( testmsg ( walk_msg , msgtyp , mode ) & &
! security_msg_queue_msgrcv ( msq , walk_msg , current ,
msgtyp , mode ) ) {
2005-04-17 02:20:36 +04:00
msg = walk_msg ;
2006-07-30 14:04:11 +04:00
if ( mode = = SEARCH_LESSEQUAL & &
walk_msg - > m_type ! = 1 ) {
msg = walk_msg ;
msgtyp = walk_msg - > m_type - 1 ;
2005-04-17 02:20:36 +04:00
} else {
2006-07-30 14:04:11 +04:00
msg = walk_msg ;
2005-04-17 02:20:36 +04:00
break ;
}
}
tmp = tmp - > next ;
}
2006-07-30 14:04:11 +04:00
if ( ! IS_ERR ( msg ) ) {
/*
* Found a suitable message .
* Unlink it from the queue .
*/
2005-04-17 02:20:36 +04:00
if ( ( msgsz < msg - > m_ts ) & & ! ( msgflg & MSG_NOERROR ) ) {
msg = ERR_PTR ( - E2BIG ) ;
goto out_unlock ;
}
list_del ( & msg - > m_list ) ;
msq - > q_qnum - - ;
msq - > q_rtime = get_seconds ( ) ;
2007-10-19 10:40:14 +04:00
msq - > q_lrpid = task_tgid_vnr ( current ) ;
2005-04-17 02:20:36 +04:00
msq - > q_cbytes - = msg - > m_ts ;
2007-10-19 10:40:56 +04:00
atomic_sub ( msg - > m_ts , & ns - > msg_bytes ) ;
atomic_dec ( & ns - > msg_hdrs ) ;
2006-07-30 14:04:11 +04:00
ss_wakeup ( & msq - > q_senders , 0 ) ;
2005-04-17 02:20:36 +04:00
msg_unlock ( msq ) ;
break ;
}
/* No message waiting. Wait for a message */
if ( msgflg & IPC_NOWAIT ) {
msg = ERR_PTR ( - ENOMSG ) ;
goto out_unlock ;
}
2006-07-30 14:04:11 +04:00
list_add_tail ( & msr_d . r_list , & msq - > q_receivers ) ;
2005-04-17 02:20:36 +04:00
msr_d . r_tsk = current ;
msr_d . r_msgtype = msgtyp ;
msr_d . r_mode = mode ;
2006-07-30 14:04:11 +04:00
if ( msgflg & MSG_NOERROR )
2005-04-17 02:20:36 +04:00
msr_d . r_maxsize = INT_MAX ;
2006-07-30 14:04:11 +04:00
else
2005-04-17 02:20:36 +04:00
msr_d . r_maxsize = msgsz ;
msr_d . r_msg = ERR_PTR ( - EAGAIN ) ;
current - > state = TASK_INTERRUPTIBLE ;
msg_unlock ( msq ) ;
schedule ( ) ;
/* Lockless receive, part 1:
* Disable preemption . We don ' t hold a reference to the queue
* and getting a reference would defeat the idea of a lockless
* operation , thus the code relies on rcu to guarantee the
* existance of msq :
* Prior to destruction , expunge_all ( - EIRDM ) changes r_msg .
* Thus if r_msg is - EAGAIN , then the queue not yet destroyed .
* rcu_read_lock ( ) prevents preemption between reading r_msg
* and the spin_lock ( ) inside ipc_lock_by_ptr ( ) .
*/
rcu_read_lock ( ) ;
/* Lockless receive, part 2:
* Wait until pipelined_send or expunge_all are outside of
* wake_up_process ( ) . There is a race with exit ( ) , see
* ipc / mqueue . c for the details .
*/
2006-07-30 14:04:11 +04:00
msg = ( struct msg_msg * ) msr_d . r_msg ;
2005-04-17 02:20:36 +04:00
while ( msg = = NULL ) {
cpu_relax ( ) ;
2006-07-30 14:04:11 +04:00
msg = ( struct msg_msg * ) msr_d . r_msg ;
2005-04-17 02:20:36 +04:00
}
/* Lockless receive, part 3:
* If there is a message or an error then accept it without
* locking .
*/
2006-07-30 14:04:11 +04:00
if ( msg ! = ERR_PTR ( - EAGAIN ) ) {
2005-04-17 02:20:36 +04:00
rcu_read_unlock ( ) ;
break ;
}
/* Lockless receive, part 3:
* Acquire the queue spinlock .
*/
ipc_lock_by_ptr ( & msq - > q_perm ) ;
rcu_read_unlock ( ) ;
/* Lockless receive, part 4:
* Repeat test after acquiring the spinlock .
*/
msg = ( struct msg_msg * ) msr_d . r_msg ;
2006-07-30 14:04:11 +04:00
if ( msg ! = ERR_PTR ( - EAGAIN ) )
2005-04-17 02:20:36 +04:00
goto out_unlock ;
list_del ( & msr_d . r_list ) ;
if ( signal_pending ( current ) ) {
msg = ERR_PTR ( - ERESTARTNOHAND ) ;
out_unlock :
msg_unlock ( msq ) ;
break ;
}
}
if ( IS_ERR ( msg ) )
2006-07-30 14:04:11 +04:00
return PTR_ERR ( msg ) ;
2005-04-17 02:20:36 +04:00
msgsz = ( msgsz > msg - > m_ts ) ? msg - > m_ts : msgsz ;
2006-12-07 07:37:48 +03:00
* pmtype = msg - > m_type ;
if ( store_msg ( mtext , msg , msgsz ) )
2006-07-30 14:04:11 +04:00
msgsz = - EFAULT ;
2006-12-07 07:37:48 +03:00
2005-04-17 02:20:36 +04:00
free_msg ( msg ) ;
2006-07-30 14:04:11 +04:00
2005-04-17 02:20:36 +04:00
return msgsz ;
}
2006-12-07 07:37:48 +03:00
asmlinkage long sys_msgrcv ( int msqid , struct msgbuf __user * msgp , size_t msgsz ,
long msgtyp , int msgflg )
{
long err , mtype ;
err = do_msgrcv ( msqid , & mtype , msgp - > mtext , msgsz , msgtyp , msgflg ) ;
if ( err < 0 )
goto out ;
if ( put_user ( mtype , & msgp - > mtype ) )
err = - EFAULT ;
out :
return err ;
}
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PROC_FS
2005-09-07 02:17:10 +04:00
static int sysvipc_msg_proc_show ( struct seq_file * s , void * it )
2005-04-17 02:20:36 +04:00
{
2005-09-07 02:17:10 +04:00
struct msg_queue * msq = it ;
return seq_printf ( s ,
2006-07-30 14:04:11 +04:00
" %10d %10d %4o %10lu %10lu %5u %5u %5u %5u %5u %5u %10lu %10lu %10lu \n " ,
msq - > q_perm . key ,
2007-10-19 10:40:48 +04:00
msq - > q_perm . id ,
2006-07-30 14:04:11 +04:00
msq - > q_perm . mode ,
msq - > q_cbytes ,
msq - > q_qnum ,
msq - > q_lspid ,
msq - > q_lrpid ,
msq - > q_perm . uid ,
msq - > q_perm . gid ,
msq - > q_perm . cuid ,
msq - > q_perm . cgid ,
msq - > q_stime ,
msq - > q_rtime ,
msq - > q_ctime ) ;
2005-04-17 02:20:36 +04:00
}
# endif