OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [net/] [unix/] [af_unix.c] - Diff between revs 1275 and 1765

Only display areas with differences | Details | Blame | View Log

Rev 1275 Rev 1765
/*
/*
 * NET4:        Implementation of BSD Unix domain sockets.
 * NET4:        Implementation of BSD Unix domain sockets.
 *
 *
 * Authors:     Alan Cox, <alan.cox@linux.org>
 * Authors:     Alan Cox, <alan.cox@linux.org>
 *
 *
 *              This program is free software; you can redistribute it and/or
 *              This program is free software; you can redistribute it and/or
 *              modify it under the terms of the GNU General Public License
 *              modify it under the terms of the GNU General Public License
 *              as published by the Free Software Foundation; either version
 *              as published by the Free Software Foundation; either version
 *              2 of the License, or (at your option) any later version.
 *              2 of the License, or (at your option) any later version.
 *
 *
 * Version:     $Id: af_unix.c,v 1.1.1.1 2004-04-15 01:16:03 phoenix Exp $
 * Version:     $Id: af_unix.c,v 1.1.1.1 2004-04-15 01:16:03 phoenix Exp $
 *
 *
 * Fixes:
 * Fixes:
 *              Linus Torvalds  :       Assorted bug cures.
 *              Linus Torvalds  :       Assorted bug cures.
 *              Niibe Yutaka    :       async I/O support.
 *              Niibe Yutaka    :       async I/O support.
 *              Carsten Paeth   :       PF_UNIX check, address fixes.
 *              Carsten Paeth   :       PF_UNIX check, address fixes.
 *              Alan Cox        :       Limit size of allocated blocks.
 *              Alan Cox        :       Limit size of allocated blocks.
 *              Alan Cox        :       Fixed the stupid socketpair bug.
 *              Alan Cox        :       Fixed the stupid socketpair bug.
 *              Alan Cox        :       BSD compatibility fine tuning.
 *              Alan Cox        :       BSD compatibility fine tuning.
 *              Alan Cox        :       Fixed a bug in connect when interrupted.
 *              Alan Cox        :       Fixed a bug in connect when interrupted.
 *              Alan Cox        :       Sorted out a proper draft version of
 *              Alan Cox        :       Sorted out a proper draft version of
 *                                      file descriptor passing hacked up from
 *                                      file descriptor passing hacked up from
 *                                      Mike Shaver's work.
 *                                      Mike Shaver's work.
 *              Marty Leisner   :       Fixes to fd passing
 *              Marty Leisner   :       Fixes to fd passing
 *              Nick Nevin      :       recvmsg bugfix.
 *              Nick Nevin      :       recvmsg bugfix.
 *              Alan Cox        :       Started proper garbage collector
 *              Alan Cox        :       Started proper garbage collector
 *              Heiko EiBfeldt  :       Missing verify_area check
 *              Heiko EiBfeldt  :       Missing verify_area check
 *              Alan Cox        :       Started POSIXisms
 *              Alan Cox        :       Started POSIXisms
 *              Andreas Schwab  :       Replace inode by dentry for proper
 *              Andreas Schwab  :       Replace inode by dentry for proper
 *                                      reference counting
 *                                      reference counting
 *              Kirk Petersen   :       Made this a module
 *              Kirk Petersen   :       Made this a module
 *          Christoph Rohland   :       Elegant non-blocking accept/connect algorithm.
 *          Christoph Rohland   :       Elegant non-blocking accept/connect algorithm.
 *                                      Lots of bug fixes.
 *                                      Lots of bug fixes.
 *           Alexey Kuznetosv   :       Repaired (I hope) bugs introduces
 *           Alexey Kuznetosv   :       Repaired (I hope) bugs introduces
 *                                      by above two patches.
 *                                      by above two patches.
 *           Andrea Arcangeli   :       If possible we block in connect(2)
 *           Andrea Arcangeli   :       If possible we block in connect(2)
 *                                      if the max backlog of the listen socket
 *                                      if the max backlog of the listen socket
 *                                      is been reached. This won't break
 *                                      is been reached. This won't break
 *                                      old apps and it will avoid huge amount
 *                                      old apps and it will avoid huge amount
 *                                      of socks hashed (this for unix_gc()
 *                                      of socks hashed (this for unix_gc()
 *                                      performances reasons).
 *                                      performances reasons).
 *                                      Security fix that limits the max
 *                                      Security fix that limits the max
 *                                      number of socks to 2*max_files and
 *                                      number of socks to 2*max_files and
 *                                      the number of skb queueable in the
 *                                      the number of skb queueable in the
 *                                      dgram receiver.
 *                                      dgram receiver.
 *              Artur Skawina   :       Hash function optimizations
 *              Artur Skawina   :       Hash function optimizations
 *           Alexey Kuznetsov   :       Full scale SMP. Lot of bugs are introduced 8)
 *           Alexey Kuznetsov   :       Full scale SMP. Lot of bugs are introduced 8)
 *            Malcolm Beattie   :       Set peercred for socketpair
 *            Malcolm Beattie   :       Set peercred for socketpair
 *           Michal Ostrowski   :       Module initialization cleanup.
 *           Michal Ostrowski   :       Module initialization cleanup.
 *
 *
 *
 *
 * Known differences from reference BSD that was tested:
 * Known differences from reference BSD that was tested:
 *
 *
 *      [TO FIX]
 *      [TO FIX]
 *      ECONNREFUSED is not returned from one end of a connected() socket to the
 *      ECONNREFUSED is not returned from one end of a connected() socket to the
 *              other the moment one end closes.
 *              other the moment one end closes.
 *      fstat() doesn't return st_dev=NODEV, and give the blksize as high water mark
 *      fstat() doesn't return st_dev=NODEV, and give the blksize as high water mark
 *              and a fake inode identifier (nor the BSD first socket fstat twice bug).
 *              and a fake inode identifier (nor the BSD first socket fstat twice bug).
 *      [NOT TO FIX]
 *      [NOT TO FIX]
 *      accept() returns a path name even if the connecting socket has closed
 *      accept() returns a path name even if the connecting socket has closed
 *              in the meantime (BSD loses the path and gives up).
 *              in the meantime (BSD loses the path and gives up).
 *      accept() returns 0 length path for an unbound connector. BSD returns 16
 *      accept() returns 0 length path for an unbound connector. BSD returns 16
 *              and a null first byte in the path (but not for gethost/peername - BSD bug ??)
 *              and a null first byte in the path (but not for gethost/peername - BSD bug ??)
 *      socketpair(...SOCK_RAW..) doesn't panic the kernel.
 *      socketpair(...SOCK_RAW..) doesn't panic the kernel.
 *      BSD af_unix apparently has connect forgetting to block properly.
 *      BSD af_unix apparently has connect forgetting to block properly.
 *              (need to check this with the POSIX spec in detail)
 *              (need to check this with the POSIX spec in detail)
 *
 *
 * Differences from 2.0.0-11-... (ANK)
 * Differences from 2.0.0-11-... (ANK)
 *      Bug fixes and improvements.
 *      Bug fixes and improvements.
 *              - client shutdown killed server socket.
 *              - client shutdown killed server socket.
 *              - removed all useless cli/sti pairs.
 *              - removed all useless cli/sti pairs.
 *
 *
 *      Semantic changes/extensions.
 *      Semantic changes/extensions.
 *              - generic control message passing.
 *              - generic control message passing.
 *              - SCM_CREDENTIALS control message.
 *              - SCM_CREDENTIALS control message.
 *              - "Abstract" (not FS based) socket bindings.
 *              - "Abstract" (not FS based) socket bindings.
 *                Abstract names are sequences of bytes (not zero terminated)
 *                Abstract names are sequences of bytes (not zero terminated)
 *                started by 0, so that this name space does not intersect
 *                started by 0, so that this name space does not intersect
 *                with BSD names.
 *                with BSD names.
 */
 */
 
 
#include <linux/module.h>
#include <linux/module.h>
#include <linux/config.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/major.h>
#include <linux/signal.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/stat.h>
#include <linux/socket.h>
#include <linux/socket.h>
#include <linux/un.h>
#include <linux/un.h>
#include <linux/fcntl.h>
#include <linux/fcntl.h>
#include <linux/termios.h>
#include <linux/termios.h>
#include <linux/sockios.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <linux/net.h>
#include <linux/in.h>
#include <linux/in.h>
#include <linux/fs.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <asm/uaccess.h>
#include <linux/skbuff.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/netdevice.h>
#include <net/sock.h>
#include <net/sock.h>
#include <linux/tcp.h>
#include <linux/tcp.h>
#include <net/af_unix.h>
#include <net/af_unix.h>
#include <linux/proc_fs.h>
#include <linux/proc_fs.h>
#include <net/scm.h>
#include <net/scm.h>
#include <linux/init.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/poll.h>
#include <linux/smp_lock.h>
#include <linux/smp_lock.h>
#include <linux/rtnetlink.h>
#include <linux/rtnetlink.h>
 
 
#include <asm/checksum.h>
#include <asm/checksum.h>
 
 
int sysctl_unix_max_dgram_qlen = 10;
int sysctl_unix_max_dgram_qlen = 10;
 
 
unix_socket *unix_socket_table[UNIX_HASH_SIZE+1];
unix_socket *unix_socket_table[UNIX_HASH_SIZE+1];
rwlock_t unix_table_lock = RW_LOCK_UNLOCKED;
rwlock_t unix_table_lock = RW_LOCK_UNLOCKED;
static atomic_t unix_nr_socks = ATOMIC_INIT(0);
static atomic_t unix_nr_socks = ATOMIC_INIT(0);
 
 
#define unix_sockets_unbound    (unix_socket_table[UNIX_HASH_SIZE])
#define unix_sockets_unbound    (unix_socket_table[UNIX_HASH_SIZE])
 
 
#define UNIX_ABSTRACT(sk)       ((sk)->protinfo.af_unix.addr->hash!=UNIX_HASH_SIZE)
#define UNIX_ABSTRACT(sk)       ((sk)->protinfo.af_unix.addr->hash!=UNIX_HASH_SIZE)
 
 
/*
/*
 *  SMP locking strategy:
 *  SMP locking strategy:
 *    hash table is protected with rwlock unix_table_lock
 *    hash table is protected with rwlock unix_table_lock
 *    each socket state is protected by separate rwlock.
 *    each socket state is protected by separate rwlock.
 */
 */
 
 
static inline unsigned unix_hash_fold(unsigned hash)
static inline unsigned unix_hash_fold(unsigned hash)
{
{
        hash ^= hash>>16;
        hash ^= hash>>16;
        hash ^= hash>>8;
        hash ^= hash>>8;
        return hash&(UNIX_HASH_SIZE-1);
        return hash&(UNIX_HASH_SIZE-1);
}
}
 
 
#define unix_peer(sk) ((sk)->pair)
#define unix_peer(sk) ((sk)->pair)
 
 
static inline int unix_our_peer(unix_socket *sk, unix_socket *osk)
static inline int unix_our_peer(unix_socket *sk, unix_socket *osk)
{
{
        return unix_peer(osk) == sk;
        return unix_peer(osk) == sk;
}
}
 
 
static inline int unix_may_send(unix_socket *sk, unix_socket *osk)
static inline int unix_may_send(unix_socket *sk, unix_socket *osk)
{
{
        return (unix_peer(osk) == NULL || unix_our_peer(sk, osk));
        return (unix_peer(osk) == NULL || unix_our_peer(sk, osk));
}
}
 
 
static inline unix_socket * unix_peer_get(unix_socket *s)
static inline unix_socket * unix_peer_get(unix_socket *s)
{
{
        unix_socket *peer;
        unix_socket *peer;
 
 
        unix_state_rlock(s);
        unix_state_rlock(s);
        peer = unix_peer(s);
        peer = unix_peer(s);
        if (peer)
        if (peer)
                sock_hold(peer);
                sock_hold(peer);
        unix_state_runlock(s);
        unix_state_runlock(s);
        return peer;
        return peer;
}
}
 
 
extern inline void unix_release_addr(struct unix_address *addr)
extern inline void unix_release_addr(struct unix_address *addr)
{
{
        if (atomic_dec_and_test(&addr->refcnt))
        if (atomic_dec_and_test(&addr->refcnt))
                kfree(addr);
                kfree(addr);
}
}
 
 
/*
/*
 *      Check unix socket name:
 *      Check unix socket name:
 *              - should be not zero length.
 *              - should be not zero length.
 *              - if started by not zero, should be NULL terminated (FS object)
 *              - if started by not zero, should be NULL terminated (FS object)
 *              - if started by zero, it is abstract name.
 *              - if started by zero, it is abstract name.
 */
 */
 
 
static int unix_mkname(struct sockaddr_un * sunaddr, int len, unsigned *hashp)
static int unix_mkname(struct sockaddr_un * sunaddr, int len, unsigned *hashp)
{
{
        if (len <= sizeof(short) || len > sizeof(*sunaddr))
        if (len <= sizeof(short) || len > sizeof(*sunaddr))
                return -EINVAL;
                return -EINVAL;
        if (!sunaddr || sunaddr->sun_family != AF_UNIX)
        if (!sunaddr || sunaddr->sun_family != AF_UNIX)
                return -EINVAL;
                return -EINVAL;
        if (sunaddr->sun_path[0])
        if (sunaddr->sun_path[0])
        {
        {
                /*
                /*
                 *      This may look like an off by one error but it is
                 *      This may look like an off by one error but it is
                 *      a bit more subtle. 108 is the longest valid AF_UNIX
                 *      a bit more subtle. 108 is the longest valid AF_UNIX
                 *      path for a binding. sun_path[108] doesn't as such
                 *      path for a binding. sun_path[108] doesn't as such
                 *      exist. However in kernel space we are guaranteed that
                 *      exist. However in kernel space we are guaranteed that
                 *      it is a valid memory location in our kernel
                 *      it is a valid memory location in our kernel
                 *      address buffer.
                 *      address buffer.
                 */
                 */
                if (len > sizeof(*sunaddr))
                if (len > sizeof(*sunaddr))
                        len = sizeof(*sunaddr);
                        len = sizeof(*sunaddr);
                ((char *)sunaddr)[len]=0;
                ((char *)sunaddr)[len]=0;
                len = strlen(sunaddr->sun_path)+1+sizeof(short);
                len = strlen(sunaddr->sun_path)+1+sizeof(short);
                return len;
                return len;
        }
        }
 
 
        *hashp = unix_hash_fold(csum_partial((char*)sunaddr, len, 0));
        *hashp = unix_hash_fold(csum_partial((char*)sunaddr, len, 0));
        return len;
        return len;
}
}
 
 
static void __unix_remove_socket(unix_socket *sk)
static void __unix_remove_socket(unix_socket *sk)
{
{
        unix_socket **list = sk->protinfo.af_unix.list;
        unix_socket **list = sk->protinfo.af_unix.list;
        if (list) {
        if (list) {
                if (sk->next)
                if (sk->next)
                        sk->next->prev = sk->prev;
                        sk->next->prev = sk->prev;
                if (sk->prev)
                if (sk->prev)
                        sk->prev->next = sk->next;
                        sk->prev->next = sk->next;
                if (*list == sk)
                if (*list == sk)
                        *list = sk->next;
                        *list = sk->next;
                sk->protinfo.af_unix.list = NULL;
                sk->protinfo.af_unix.list = NULL;
                sk->prev = NULL;
                sk->prev = NULL;
                sk->next = NULL;
                sk->next = NULL;
                __sock_put(sk);
                __sock_put(sk);
        }
        }
}
}
 
 
static void __unix_insert_socket(unix_socket **list, unix_socket *sk)
static void __unix_insert_socket(unix_socket **list, unix_socket *sk)
{
{
        BUG_TRAP(sk->protinfo.af_unix.list==NULL);
        BUG_TRAP(sk->protinfo.af_unix.list==NULL);
 
 
        sk->protinfo.af_unix.list = list;
        sk->protinfo.af_unix.list = list;
        sk->prev = NULL;
        sk->prev = NULL;
        sk->next = *list;
        sk->next = *list;
        if (*list)
        if (*list)
                (*list)->prev = sk;
                (*list)->prev = sk;
        *list=sk;
        *list=sk;
        sock_hold(sk);
        sock_hold(sk);
}
}
 
 
static inline void unix_remove_socket(unix_socket *sk)
static inline void unix_remove_socket(unix_socket *sk)
{
{
        write_lock(&unix_table_lock);
        write_lock(&unix_table_lock);
        __unix_remove_socket(sk);
        __unix_remove_socket(sk);
        write_unlock(&unix_table_lock);
        write_unlock(&unix_table_lock);
}
}
 
 
static inline void unix_insert_socket(unix_socket **list, unix_socket *sk)
static inline void unix_insert_socket(unix_socket **list, unix_socket *sk)
{
{
        write_lock(&unix_table_lock);
        write_lock(&unix_table_lock);
        __unix_insert_socket(list, sk);
        __unix_insert_socket(list, sk);
        write_unlock(&unix_table_lock);
        write_unlock(&unix_table_lock);
}
}
 
 
static unix_socket *__unix_find_socket_byname(struct sockaddr_un *sunname,
static unix_socket *__unix_find_socket_byname(struct sockaddr_un *sunname,
                                              int len, int type, unsigned hash)
                                              int len, int type, unsigned hash)
{
{
        unix_socket *s;
        unix_socket *s;
 
 
        for (s=unix_socket_table[hash^type]; s; s=s->next) {
        for (s=unix_socket_table[hash^type]; s; s=s->next) {
                if(s->protinfo.af_unix.addr->len==len &&
                if(s->protinfo.af_unix.addr->len==len &&
                   memcmp(s->protinfo.af_unix.addr->name, sunname, len) == 0)
                   memcmp(s->protinfo.af_unix.addr->name, sunname, len) == 0)
                        return s;
                        return s;
        }
        }
        return NULL;
        return NULL;
}
}
 
 
static inline unix_socket *
static inline unix_socket *
unix_find_socket_byname(struct sockaddr_un *sunname,
unix_find_socket_byname(struct sockaddr_un *sunname,
                        int len, int type, unsigned hash)
                        int len, int type, unsigned hash)
{
{
        unix_socket *s;
        unix_socket *s;
 
 
        read_lock(&unix_table_lock);
        read_lock(&unix_table_lock);
        s = __unix_find_socket_byname(sunname, len, type, hash);
        s = __unix_find_socket_byname(sunname, len, type, hash);
        if (s)
        if (s)
                sock_hold(s);
                sock_hold(s);
        read_unlock(&unix_table_lock);
        read_unlock(&unix_table_lock);
        return s;
        return s;
}
}
 
 
static unix_socket *unix_find_socket_byinode(struct inode *i)
static unix_socket *unix_find_socket_byinode(struct inode *i)
{
{
        unix_socket *s;
        unix_socket *s;
 
 
        read_lock(&unix_table_lock);
        read_lock(&unix_table_lock);
        for (s=unix_socket_table[i->i_ino & (UNIX_HASH_SIZE-1)]; s; s=s->next)
        for (s=unix_socket_table[i->i_ino & (UNIX_HASH_SIZE-1)]; s; s=s->next)
        {
        {
                struct dentry *dentry = s->protinfo.af_unix.dentry;
                struct dentry *dentry = s->protinfo.af_unix.dentry;
 
 
                if(dentry && dentry->d_inode == i)
                if(dentry && dentry->d_inode == i)
                {
                {
                        sock_hold(s);
                        sock_hold(s);
                        break;
                        break;
                }
                }
        }
        }
        read_unlock(&unix_table_lock);
        read_unlock(&unix_table_lock);
        return s;
        return s;
}
}
 
 
static inline int unix_writable(struct sock *sk)
static inline int unix_writable(struct sock *sk)
{
{
        return ((atomic_read(&sk->wmem_alloc)<<2) <= sk->sndbuf);
        return ((atomic_read(&sk->wmem_alloc)<<2) <= sk->sndbuf);
}
}
 
 
static void unix_write_space(struct sock *sk)
static void unix_write_space(struct sock *sk)
{
{
        read_lock(&sk->callback_lock);
        read_lock(&sk->callback_lock);
        if (unix_writable(sk)) {
        if (unix_writable(sk)) {
                if (sk->sleep && waitqueue_active(sk->sleep))
                if (sk->sleep && waitqueue_active(sk->sleep))
                        wake_up_interruptible(sk->sleep);
                        wake_up_interruptible(sk->sleep);
                sk_wake_async(sk, 2, POLL_OUT);
                sk_wake_async(sk, 2, POLL_OUT);
        }
        }
        read_unlock(&sk->callback_lock);
        read_unlock(&sk->callback_lock);
}
}
 
 
/* When dgram socket disconnects (or changes its peer), we clear its receive
/* When dgram socket disconnects (or changes its peer), we clear its receive
 * queue of packets arrived from previous peer. First, it allows to do
 * queue of packets arrived from previous peer. First, it allows to do
 * flow control based only on wmem_alloc; second, sk connected to peer
 * flow control based only on wmem_alloc; second, sk connected to peer
 * may receive messages only from that peer. */
 * may receive messages only from that peer. */
static void unix_dgram_disconnected(struct sock *sk, struct sock *other)
static void unix_dgram_disconnected(struct sock *sk, struct sock *other)
{
{
        if (skb_queue_len(&sk->receive_queue)) {
        if (skb_queue_len(&sk->receive_queue)) {
                skb_queue_purge(&sk->receive_queue);
                skb_queue_purge(&sk->receive_queue);
                wake_up_interruptible_all(&sk->protinfo.af_unix.peer_wait);
                wake_up_interruptible_all(&sk->protinfo.af_unix.peer_wait);
 
 
                /* If one link of bidirectional dgram pipe is disconnected,
                /* If one link of bidirectional dgram pipe is disconnected,
                 * we signal error. Messages are lost. Do not make this,
                 * we signal error. Messages are lost. Do not make this,
                 * when peer was not connected to us.
                 * when peer was not connected to us.
                 */
                 */
                if (!other->dead && unix_peer(other) == sk) {
                if (!other->dead && unix_peer(other) == sk) {
                        other->err = ECONNRESET;
                        other->err = ECONNRESET;
                        other->error_report(other);
                        other->error_report(other);
                }
                }
        }
        }
}
}
 
 
static void unix_sock_destructor(struct sock *sk)
static void unix_sock_destructor(struct sock *sk)
{
{
        skb_queue_purge(&sk->receive_queue);
        skb_queue_purge(&sk->receive_queue);
 
 
        BUG_TRAP(atomic_read(&sk->wmem_alloc) == 0);
        BUG_TRAP(atomic_read(&sk->wmem_alloc) == 0);
        BUG_TRAP(sk->protinfo.af_unix.list==NULL);
        BUG_TRAP(sk->protinfo.af_unix.list==NULL);
        BUG_TRAP(sk->socket==NULL);
        BUG_TRAP(sk->socket==NULL);
        if (sk->dead==0) {
        if (sk->dead==0) {
                printk("Attempt to release alive unix socket: %p\n", sk);
                printk("Attempt to release alive unix socket: %p\n", sk);
                return;
                return;
        }
        }
 
 
        if (sk->protinfo.af_unix.addr)
        if (sk->protinfo.af_unix.addr)
                unix_release_addr(sk->protinfo.af_unix.addr);
                unix_release_addr(sk->protinfo.af_unix.addr);
 
 
        atomic_dec(&unix_nr_socks);
        atomic_dec(&unix_nr_socks);
#ifdef UNIX_REFCNT_DEBUG
#ifdef UNIX_REFCNT_DEBUG
        printk(KERN_DEBUG "UNIX %p is destroyed, %d are still alive.\n", sk, atomic_read(&unix_nr_socks));
        printk(KERN_DEBUG "UNIX %p is destroyed, %d are still alive.\n", sk, atomic_read(&unix_nr_socks));
#endif
#endif
        MOD_DEC_USE_COUNT;
        MOD_DEC_USE_COUNT;
}
}
 
 
static int unix_release_sock (unix_socket *sk, int embrion)
static int unix_release_sock (unix_socket *sk, int embrion)
{
{
        struct dentry *dentry;
        struct dentry *dentry;
        struct vfsmount *mnt;
        struct vfsmount *mnt;
        unix_socket *skpair;
        unix_socket *skpair;
        struct sk_buff *skb;
        struct sk_buff *skb;
        int state;
        int state;
 
 
        unix_remove_socket(sk);
        unix_remove_socket(sk);
 
 
        /* Clear state */
        /* Clear state */
        unix_state_wlock(sk);
        unix_state_wlock(sk);
        sock_orphan(sk);
        sock_orphan(sk);
        sk->shutdown = SHUTDOWN_MASK;
        sk->shutdown = SHUTDOWN_MASK;
        dentry = sk->protinfo.af_unix.dentry;
        dentry = sk->protinfo.af_unix.dentry;
        sk->protinfo.af_unix.dentry=NULL;
        sk->protinfo.af_unix.dentry=NULL;
        mnt = sk->protinfo.af_unix.mnt;
        mnt = sk->protinfo.af_unix.mnt;
        sk->protinfo.af_unix.mnt=NULL;
        sk->protinfo.af_unix.mnt=NULL;
        state = sk->state;
        state = sk->state;
        sk->state = TCP_CLOSE;
        sk->state = TCP_CLOSE;
        unix_state_wunlock(sk);
        unix_state_wunlock(sk);
 
 
        wake_up_interruptible_all(&sk->protinfo.af_unix.peer_wait);
        wake_up_interruptible_all(&sk->protinfo.af_unix.peer_wait);
 
 
        skpair=unix_peer(sk);
        skpair=unix_peer(sk);
 
 
        if (skpair!=NULL) {
        if (skpair!=NULL) {
                if (sk->type==SOCK_STREAM) {
                if (sk->type==SOCK_STREAM) {
                        unix_state_wlock(skpair);
                        unix_state_wlock(skpair);
                        skpair->shutdown=SHUTDOWN_MASK; /* No more writes*/
                        skpair->shutdown=SHUTDOWN_MASK; /* No more writes*/
                        if (!skb_queue_empty(&sk->receive_queue) || embrion)
                        if (!skb_queue_empty(&sk->receive_queue) || embrion)
                                skpair->err = ECONNRESET;
                                skpair->err = ECONNRESET;
                        unix_state_wunlock(skpair);
                        unix_state_wunlock(skpair);
                        skpair->state_change(skpair);
                        skpair->state_change(skpair);
                        read_lock(&skpair->callback_lock);
                        read_lock(&skpair->callback_lock);
                        sk_wake_async(skpair,1,POLL_HUP);
                        sk_wake_async(skpair,1,POLL_HUP);
                        read_unlock(&skpair->callback_lock);
                        read_unlock(&skpair->callback_lock);
                }
                }
                sock_put(skpair); /* It may now die */
                sock_put(skpair); /* It may now die */
                unix_peer(sk) = NULL;
                unix_peer(sk) = NULL;
        }
        }
 
 
        /* Try to flush out this socket. Throw out buffers at least */
        /* Try to flush out this socket. Throw out buffers at least */
 
 
        while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
        while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
        {
        {
                if (state==TCP_LISTEN)
                if (state==TCP_LISTEN)
                        unix_release_sock(skb->sk, 1);
                        unix_release_sock(skb->sk, 1);
                /* passed fds are erased in the kfree_skb hook        */
                /* passed fds are erased in the kfree_skb hook        */
                kfree_skb(skb);
                kfree_skb(skb);
        }
        }
 
 
        if (dentry) {
        if (dentry) {
                dput(dentry);
                dput(dentry);
                mntput(mnt);
                mntput(mnt);
        }
        }
 
 
        sock_put(sk);
        sock_put(sk);
 
 
        /* ---- Socket is dead now and most probably destroyed ---- */
        /* ---- Socket is dead now and most probably destroyed ---- */
 
 
        /*
        /*
         * Fixme: BSD difference: In BSD all sockets connected to use get
         * Fixme: BSD difference: In BSD all sockets connected to use get
         *        ECONNRESET and we die on the spot. In Linux we behave
         *        ECONNRESET and we die on the spot. In Linux we behave
         *        like files and pipes do and wait for the last
         *        like files and pipes do and wait for the last
         *        dereference.
         *        dereference.
         *
         *
         * Can't we simply set sock->err?
         * Can't we simply set sock->err?
         *
         *
         *        What the above comment does talk about? --ANK(980817)
         *        What the above comment does talk about? --ANK(980817)
         */
         */
 
 
        if (atomic_read(&unix_tot_inflight))
        if (atomic_read(&unix_tot_inflight))
                unix_gc();              /* Garbage collect fds */
                unix_gc();              /* Garbage collect fds */
 
 
        return 0;
        return 0;
}
}
 
 
static int unix_listen(struct socket *sock, int backlog)
static int unix_listen(struct socket *sock, int backlog)
{
{
        int err;
        int err;
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
 
 
        err = -EOPNOTSUPP;
        err = -EOPNOTSUPP;
        if (sock->type!=SOCK_STREAM)
        if (sock->type!=SOCK_STREAM)
                goto out;                       /* Only stream sockets accept */
                goto out;                       /* Only stream sockets accept */
        err = -EINVAL;
        err = -EINVAL;
        if (!sk->protinfo.af_unix.addr)
        if (!sk->protinfo.af_unix.addr)
                goto out;                       /* No listens on an unbound socket */
                goto out;                       /* No listens on an unbound socket */
        unix_state_wlock(sk);
        unix_state_wlock(sk);
        if (sk->state != TCP_CLOSE && sk->state != TCP_LISTEN)
        if (sk->state != TCP_CLOSE && sk->state != TCP_LISTEN)
                goto out_unlock;
                goto out_unlock;
        if (backlog > sk->max_ack_backlog)
        if (backlog > sk->max_ack_backlog)
                wake_up_interruptible_all(&sk->protinfo.af_unix.peer_wait);
                wake_up_interruptible_all(&sk->protinfo.af_unix.peer_wait);
        sk->max_ack_backlog=backlog;
        sk->max_ack_backlog=backlog;
        sk->state=TCP_LISTEN;
        sk->state=TCP_LISTEN;
        /* set credentials so connect can copy them */
        /* set credentials so connect can copy them */
        sk->peercred.pid = current->pid;
        sk->peercred.pid = current->pid;
        sk->peercred.uid = current->euid;
        sk->peercred.uid = current->euid;
        sk->peercred.gid = current->egid;
        sk->peercred.gid = current->egid;
        err = 0;
        err = 0;
 
 
out_unlock:
out_unlock:
        unix_state_wunlock(sk);
        unix_state_wunlock(sk);
out:
out:
        return err;
        return err;
}
}
 
 
extern struct proto_ops unix_stream_ops;
extern struct proto_ops unix_stream_ops;
extern struct proto_ops unix_dgram_ops;
extern struct proto_ops unix_dgram_ops;
 
 
static struct sock * unix_create1(struct socket *sock)
static struct sock * unix_create1(struct socket *sock)
{
{
        struct sock *sk;
        struct sock *sk;
 
 
        if (atomic_read(&unix_nr_socks) >= 2*files_stat.max_files)
        if (atomic_read(&unix_nr_socks) >= 2*files_stat.max_files)
                return NULL;
                return NULL;
 
 
        MOD_INC_USE_COUNT;
        MOD_INC_USE_COUNT;
        sk = sk_alloc(PF_UNIX, GFP_KERNEL, 1);
        sk = sk_alloc(PF_UNIX, GFP_KERNEL, 1);
        if (!sk) {
        if (!sk) {
                MOD_DEC_USE_COUNT;
                MOD_DEC_USE_COUNT;
                return NULL;
                return NULL;
        }
        }
 
 
        atomic_inc(&unix_nr_socks);
        atomic_inc(&unix_nr_socks);
 
 
        sock_init_data(sock,sk);
        sock_init_data(sock,sk);
 
 
        sk->write_space         =       unix_write_space;
        sk->write_space         =       unix_write_space;
 
 
        sk->max_ack_backlog = sysctl_unix_max_dgram_qlen;
        sk->max_ack_backlog = sysctl_unix_max_dgram_qlen;
        sk->destruct = unix_sock_destructor;
        sk->destruct = unix_sock_destructor;
        sk->protinfo.af_unix.dentry=NULL;
        sk->protinfo.af_unix.dentry=NULL;
        sk->protinfo.af_unix.mnt=NULL;
        sk->protinfo.af_unix.mnt=NULL;
        sk->protinfo.af_unix.lock = RW_LOCK_UNLOCKED;
        sk->protinfo.af_unix.lock = RW_LOCK_UNLOCKED;
        atomic_set(&sk->protinfo.af_unix.inflight, sock ? 0 : -1);
        atomic_set(&sk->protinfo.af_unix.inflight, sock ? 0 : -1);
        init_MUTEX(&sk->protinfo.af_unix.readsem);/* single task reading lock */
        init_MUTEX(&sk->protinfo.af_unix.readsem);/* single task reading lock */
        init_waitqueue_head(&sk->protinfo.af_unix.peer_wait);
        init_waitqueue_head(&sk->protinfo.af_unix.peer_wait);
        sk->protinfo.af_unix.list=NULL;
        sk->protinfo.af_unix.list=NULL;
        unix_insert_socket(&unix_sockets_unbound, sk);
        unix_insert_socket(&unix_sockets_unbound, sk);
 
 
        return sk;
        return sk;
}
}
 
 
static int unix_create(struct socket *sock, int protocol)
static int unix_create(struct socket *sock, int protocol)
{
{
        if (protocol && protocol != PF_UNIX)
        if (protocol && protocol != PF_UNIX)
                return -EPROTONOSUPPORT;
                return -EPROTONOSUPPORT;
 
 
        sock->state = SS_UNCONNECTED;
        sock->state = SS_UNCONNECTED;
 
 
        switch (sock->type) {
        switch (sock->type) {
        case SOCK_STREAM:
        case SOCK_STREAM:
                sock->ops = &unix_stream_ops;
                sock->ops = &unix_stream_ops;
                break;
                break;
                /*
                /*
                 *      Believe it or not BSD has AF_UNIX, SOCK_RAW though
                 *      Believe it or not BSD has AF_UNIX, SOCK_RAW though
                 *      nothing uses it.
                 *      nothing uses it.
                 */
                 */
        case SOCK_RAW:
        case SOCK_RAW:
                sock->type=SOCK_DGRAM;
                sock->type=SOCK_DGRAM;
        case SOCK_DGRAM:
        case SOCK_DGRAM:
                sock->ops = &unix_dgram_ops;
                sock->ops = &unix_dgram_ops;
                break;
                break;
        default:
        default:
                return -ESOCKTNOSUPPORT;
                return -ESOCKTNOSUPPORT;
        }
        }
 
 
        return unix_create1(sock) ? 0 : -ENOMEM;
        return unix_create1(sock) ? 0 : -ENOMEM;
}
}
 
 
static int unix_release(struct socket *sock)
static int unix_release(struct socket *sock)
{
{
        unix_socket *sk = sock->sk;
        unix_socket *sk = sock->sk;
 
 
        if (!sk)
        if (!sk)
                return 0;
                return 0;
 
 
        sock->sk = NULL;
        sock->sk = NULL;
 
 
        return unix_release_sock (sk, 0);
        return unix_release_sock (sk, 0);
}
}
 
 
static int unix_autobind(struct socket *sock)
static int unix_autobind(struct socket *sock)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        static u32 ordernum = 1;
        static u32 ordernum = 1;
        struct unix_address * addr;
        struct unix_address * addr;
        int err;
        int err;
 
 
        down(&sk->protinfo.af_unix.readsem);
        down(&sk->protinfo.af_unix.readsem);
 
 
        err = 0;
        err = 0;
        if (sk->protinfo.af_unix.addr)
        if (sk->protinfo.af_unix.addr)
                goto out;
                goto out;
 
 
        err = -ENOMEM;
        err = -ENOMEM;
        addr = kmalloc(sizeof(*addr) + sizeof(short) + 16, GFP_KERNEL);
        addr = kmalloc(sizeof(*addr) + sizeof(short) + 16, GFP_KERNEL);
        if (!addr)
        if (!addr)
                goto out;
                goto out;
 
 
        memset(addr, 0, sizeof(*addr) + sizeof(short) + 16);
        memset(addr, 0, sizeof(*addr) + sizeof(short) + 16);
        addr->name->sun_family = AF_UNIX;
        addr->name->sun_family = AF_UNIX;
        atomic_set(&addr->refcnt, 1);
        atomic_set(&addr->refcnt, 1);
 
 
retry:
retry:
        addr->len = sprintf(addr->name->sun_path+1, "%05x", ordernum) + 1 + sizeof(short);
        addr->len = sprintf(addr->name->sun_path+1, "%05x", ordernum) + 1 + sizeof(short);
        addr->hash = unix_hash_fold(csum_partial((void*)addr->name, addr->len, 0));
        addr->hash = unix_hash_fold(csum_partial((void*)addr->name, addr->len, 0));
 
 
        write_lock(&unix_table_lock);
        write_lock(&unix_table_lock);
        ordernum = (ordernum+1)&0xFFFFF;
        ordernum = (ordernum+1)&0xFFFFF;
 
 
        if (__unix_find_socket_byname(addr->name, addr->len, sock->type,
        if (__unix_find_socket_byname(addr->name, addr->len, sock->type,
                                      addr->hash)) {
                                      addr->hash)) {
                write_unlock(&unix_table_lock);
                write_unlock(&unix_table_lock);
                /* Sanity yield. It is unusual case, but yet... */
                /* Sanity yield. It is unusual case, but yet... */
                if (!(ordernum&0xFF))
                if (!(ordernum&0xFF))
                        yield();
                        yield();
                goto retry;
                goto retry;
        }
        }
        addr->hash ^= sk->type;
        addr->hash ^= sk->type;
 
 
        __unix_remove_socket(sk);
        __unix_remove_socket(sk);
        sk->protinfo.af_unix.addr = addr;
        sk->protinfo.af_unix.addr = addr;
        __unix_insert_socket(&unix_socket_table[addr->hash], sk);
        __unix_insert_socket(&unix_socket_table[addr->hash], sk);
        write_unlock(&unix_table_lock);
        write_unlock(&unix_table_lock);
        err = 0;
        err = 0;
 
 
out:
out:
        up(&sk->protinfo.af_unix.readsem);
        up(&sk->protinfo.af_unix.readsem);
        return err;
        return err;
}
}
 
 
static unix_socket *unix_find_other(struct sockaddr_un *sunname, int len,
static unix_socket *unix_find_other(struct sockaddr_un *sunname, int len,
                                    int type, unsigned hash, int *error)
                                    int type, unsigned hash, int *error)
{
{
        unix_socket *u;
        unix_socket *u;
        struct nameidata nd;
        struct nameidata nd;
        int err = 0;
        int err = 0;
 
 
        if (sunname->sun_path[0]) {
        if (sunname->sun_path[0]) {
                if (path_init(sunname->sun_path,
                if (path_init(sunname->sun_path,
                              LOOKUP_POSITIVE|LOOKUP_FOLLOW, &nd))
                              LOOKUP_POSITIVE|LOOKUP_FOLLOW, &nd))
                        err = path_walk(sunname->sun_path, &nd);
                        err = path_walk(sunname->sun_path, &nd);
                if (err)
                if (err)
                        goto fail;
                        goto fail;
                err = permission(nd.dentry->d_inode,MAY_WRITE);
                err = permission(nd.dentry->d_inode,MAY_WRITE);
                if (err)
                if (err)
                        goto put_fail;
                        goto put_fail;
 
 
                err = -ECONNREFUSED;
                err = -ECONNREFUSED;
                if (!S_ISSOCK(nd.dentry->d_inode->i_mode))
                if (!S_ISSOCK(nd.dentry->d_inode->i_mode))
                        goto put_fail;
                        goto put_fail;
                u=unix_find_socket_byinode(nd.dentry->d_inode);
                u=unix_find_socket_byinode(nd.dentry->d_inode);
                if (!u)
                if (!u)
                        goto put_fail;
                        goto put_fail;
 
 
                if (u->type == type)
                if (u->type == type)
                        UPDATE_ATIME(nd.dentry->d_inode);
                        UPDATE_ATIME(nd.dentry->d_inode);
 
 
                path_release(&nd);
                path_release(&nd);
 
 
                err=-EPROTOTYPE;
                err=-EPROTOTYPE;
                if (u->type != type) {
                if (u->type != type) {
                        sock_put(u);
                        sock_put(u);
                        goto fail;
                        goto fail;
                }
                }
        } else {
        } else {
                err = -ECONNREFUSED;
                err = -ECONNREFUSED;
                u=unix_find_socket_byname(sunname, len, type, hash);
                u=unix_find_socket_byname(sunname, len, type, hash);
                if (u) {
                if (u) {
                        struct dentry *dentry;
                        struct dentry *dentry;
                        dentry = u->protinfo.af_unix.dentry;
                        dentry = u->protinfo.af_unix.dentry;
                        if (dentry)
                        if (dentry)
                                UPDATE_ATIME(dentry->d_inode);
                                UPDATE_ATIME(dentry->d_inode);
                } else
                } else
                        goto fail;
                        goto fail;
        }
        }
        return u;
        return u;
 
 
put_fail:
put_fail:
        path_release(&nd);
        path_release(&nd);
fail:
fail:
        *error=err;
        *error=err;
        return NULL;
        return NULL;
}
}
 
 
 
 
static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;
        struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;
        struct dentry * dentry = NULL;
        struct dentry * dentry = NULL;
        struct nameidata nd;
        struct nameidata nd;
        int err;
        int err;
        unsigned hash;
        unsigned hash;
        struct unix_address *addr;
        struct unix_address *addr;
        unix_socket **list;
        unix_socket **list;
 
 
        err = -EINVAL;
        err = -EINVAL;
        if (sunaddr->sun_family != AF_UNIX)
        if (sunaddr->sun_family != AF_UNIX)
                goto out;
                goto out;
 
 
        if (addr_len==sizeof(short)) {
        if (addr_len==sizeof(short)) {
                err = unix_autobind(sock);
                err = unix_autobind(sock);
                goto out;
                goto out;
        }
        }
 
 
        err = unix_mkname(sunaddr, addr_len, &hash);
        err = unix_mkname(sunaddr, addr_len, &hash);
        if (err < 0)
        if (err < 0)
                goto out;
                goto out;
        addr_len = err;
        addr_len = err;
 
 
        down(&sk->protinfo.af_unix.readsem);
        down(&sk->protinfo.af_unix.readsem);
 
 
        err = -EINVAL;
        err = -EINVAL;
        if (sk->protinfo.af_unix.addr)
        if (sk->protinfo.af_unix.addr)
                goto out_up;
                goto out_up;
 
 
        err = -ENOMEM;
        err = -ENOMEM;
        addr = kmalloc(sizeof(*addr)+addr_len, GFP_KERNEL);
        addr = kmalloc(sizeof(*addr)+addr_len, GFP_KERNEL);
        if (!addr)
        if (!addr)
                goto out_up;
                goto out_up;
 
 
        memcpy(addr->name, sunaddr, addr_len);
        memcpy(addr->name, sunaddr, addr_len);
        addr->len = addr_len;
        addr->len = addr_len;
        addr->hash = hash^sk->type;
        addr->hash = hash^sk->type;
        atomic_set(&addr->refcnt, 1);
        atomic_set(&addr->refcnt, 1);
 
 
        if (sunaddr->sun_path[0]) {
        if (sunaddr->sun_path[0]) {
                unsigned int mode;
                unsigned int mode;
                err = 0;
                err = 0;
                /*
                /*
                 * Get the parent directory, calculate the hash for last
                 * Get the parent directory, calculate the hash for last
                 * component.
                 * component.
                 */
                 */
                if (path_init(sunaddr->sun_path, LOOKUP_PARENT, &nd))
                if (path_init(sunaddr->sun_path, LOOKUP_PARENT, &nd))
                        err = path_walk(sunaddr->sun_path, &nd);
                        err = path_walk(sunaddr->sun_path, &nd);
                if (err)
                if (err)
                        goto out_mknod_parent;
                        goto out_mknod_parent;
                /*
                /*
                 * Yucky last component or no last component at all?
                 * Yucky last component or no last component at all?
                 * (foo/., foo/.., /////)
                 * (foo/., foo/.., /////)
                 */
                 */
                err = -EEXIST;
                err = -EEXIST;
                if (nd.last_type != LAST_NORM)
                if (nd.last_type != LAST_NORM)
                        goto out_mknod;
                        goto out_mknod;
                /*
                /*
                 * Lock the directory.
                 * Lock the directory.
                 */
                 */
                down(&nd.dentry->d_inode->i_sem);
                down(&nd.dentry->d_inode->i_sem);
                /*
                /*
                 * Do the final lookup.
                 * Do the final lookup.
                 */
                 */
                dentry = lookup_hash(&nd.last, nd.dentry);
                dentry = lookup_hash(&nd.last, nd.dentry);
                err = PTR_ERR(dentry);
                err = PTR_ERR(dentry);
                if (IS_ERR(dentry))
                if (IS_ERR(dentry))
                        goto out_mknod_unlock;
                        goto out_mknod_unlock;
                err = -ENOENT;
                err = -ENOENT;
                /*
                /*
                 * Special case - lookup gave negative, but... we had foo/bar/
                 * Special case - lookup gave negative, but... we had foo/bar/
                 * From the vfs_mknod() POV we just have a negative dentry -
                 * From the vfs_mknod() POV we just have a negative dentry -
                 * all is fine. Let's be bastards - you had / on the end, you've
                 * all is fine. Let's be bastards - you had / on the end, you've
                 * been asking for (non-existent) directory. -ENOENT for you.
                 * been asking for (non-existent) directory. -ENOENT for you.
                 */
                 */
                if (nd.last.name[nd.last.len] && !dentry->d_inode)
                if (nd.last.name[nd.last.len] && !dentry->d_inode)
                        goto out_mknod_dput;
                        goto out_mknod_dput;
                /*
                /*
                 * All right, let's create it.
                 * All right, let's create it.
                 */
                 */
                mode = S_IFSOCK | (sock->inode->i_mode & ~current->fs->umask);
                mode = S_IFSOCK | (sock->inode->i_mode & ~current->fs->umask);
                err = vfs_mknod(nd.dentry->d_inode, dentry, mode, 0);
                err = vfs_mknod(nd.dentry->d_inode, dentry, mode, 0);
                if (err)
                if (err)
                        goto out_mknod_dput;
                        goto out_mknod_dput;
                up(&nd.dentry->d_inode->i_sem);
                up(&nd.dentry->d_inode->i_sem);
                dput(nd.dentry);
                dput(nd.dentry);
                nd.dentry = dentry;
                nd.dentry = dentry;
 
 
                addr->hash = UNIX_HASH_SIZE;
                addr->hash = UNIX_HASH_SIZE;
        }
        }
 
 
        write_lock(&unix_table_lock);
        write_lock(&unix_table_lock);
 
 
        if (!sunaddr->sun_path[0]) {
        if (!sunaddr->sun_path[0]) {
                err = -EADDRINUSE;
                err = -EADDRINUSE;
                if (__unix_find_socket_byname(sunaddr, addr_len,
                if (__unix_find_socket_byname(sunaddr, addr_len,
                                              sk->type, hash)) {
                                              sk->type, hash)) {
                        unix_release_addr(addr);
                        unix_release_addr(addr);
                        goto out_unlock;
                        goto out_unlock;
                }
                }
 
 
                list = &unix_socket_table[addr->hash];
                list = &unix_socket_table[addr->hash];
        } else {
        } else {
                list = &unix_socket_table[dentry->d_inode->i_ino & (UNIX_HASH_SIZE-1)];
                list = &unix_socket_table[dentry->d_inode->i_ino & (UNIX_HASH_SIZE-1)];
                sk->protinfo.af_unix.dentry = nd.dentry;
                sk->protinfo.af_unix.dentry = nd.dentry;
                sk->protinfo.af_unix.mnt = nd.mnt;
                sk->protinfo.af_unix.mnt = nd.mnt;
        }
        }
 
 
        err = 0;
        err = 0;
        __unix_remove_socket(sk);
        __unix_remove_socket(sk);
        sk->protinfo.af_unix.addr = addr;
        sk->protinfo.af_unix.addr = addr;
        __unix_insert_socket(list, sk);
        __unix_insert_socket(list, sk);
 
 
out_unlock:
out_unlock:
        write_unlock(&unix_table_lock);
        write_unlock(&unix_table_lock);
out_up:
out_up:
        up(&sk->protinfo.af_unix.readsem);
        up(&sk->protinfo.af_unix.readsem);
out:
out:
        return err;
        return err;
 
 
out_mknod_dput:
out_mknod_dput:
        dput(dentry);
        dput(dentry);
out_mknod_unlock:
out_mknod_unlock:
        up(&nd.dentry->d_inode->i_sem);
        up(&nd.dentry->d_inode->i_sem);
out_mknod:
out_mknod:
        path_release(&nd);
        path_release(&nd);
out_mknod_parent:
out_mknod_parent:
        if (err==-EEXIST)
        if (err==-EEXIST)
                err=-EADDRINUSE;
                err=-EADDRINUSE;
        unix_release_addr(addr);
        unix_release_addr(addr);
        goto out_up;
        goto out_up;
}
}
 
 
static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr,
static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr,
                              int alen, int flags)
                              int alen, int flags)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct sockaddr_un *sunaddr=(struct sockaddr_un*)addr;
        struct sockaddr_un *sunaddr=(struct sockaddr_un*)addr;
        struct sock *other;
        struct sock *other;
        unsigned hash;
        unsigned hash;
        int err;
        int err;
 
 
        if (addr->sa_family != AF_UNSPEC) {
        if (addr->sa_family != AF_UNSPEC) {
                err = unix_mkname(sunaddr, alen, &hash);
                err = unix_mkname(sunaddr, alen, &hash);
                if (err < 0)
                if (err < 0)
                        goto out;
                        goto out;
                alen = err;
                alen = err;
 
 
                if (sock->passcred && !sk->protinfo.af_unix.addr &&
                if (sock->passcred && !sk->protinfo.af_unix.addr &&
                    (err = unix_autobind(sock)) != 0)
                    (err = unix_autobind(sock)) != 0)
                        goto out;
                        goto out;
 
 
                other=unix_find_other(sunaddr, alen, sock->type, hash, &err);
                other=unix_find_other(sunaddr, alen, sock->type, hash, &err);
                if (!other)
                if (!other)
                        goto out;
                        goto out;
 
 
                unix_state_wlock(sk);
                unix_state_wlock(sk);
 
 
                err = -EPERM;
                err = -EPERM;
                if (!unix_may_send(sk, other))
                if (!unix_may_send(sk, other))
                        goto out_unlock;
                        goto out_unlock;
        } else {
        } else {
                /*
                /*
                 *      1003.1g breaking connected state with AF_UNSPEC
                 *      1003.1g breaking connected state with AF_UNSPEC
                 */
                 */
                other = NULL;
                other = NULL;
                unix_state_wlock(sk);
                unix_state_wlock(sk);
        }
        }
 
 
        /*
        /*
         * If it was connected, reconnect.
         * If it was connected, reconnect.
         */
         */
        if (unix_peer(sk)) {
        if (unix_peer(sk)) {
                struct sock *old_peer = unix_peer(sk);
                struct sock *old_peer = unix_peer(sk);
                unix_peer(sk)=other;
                unix_peer(sk)=other;
                unix_state_wunlock(sk);
                unix_state_wunlock(sk);
 
 
                if (other != old_peer)
                if (other != old_peer)
                        unix_dgram_disconnected(sk, old_peer);
                        unix_dgram_disconnected(sk, old_peer);
                sock_put(old_peer);
                sock_put(old_peer);
        } else {
        } else {
                unix_peer(sk)=other;
                unix_peer(sk)=other;
                unix_state_wunlock(sk);
                unix_state_wunlock(sk);
        }
        }
        return 0;
        return 0;
 
 
out_unlock:
out_unlock:
        unix_state_wunlock(sk);
        unix_state_wunlock(sk);
        sock_put(other);
        sock_put(other);
out:
out:
        return err;
        return err;
}
}
 
 
static long unix_wait_for_peer(unix_socket *other, long timeo)
static long unix_wait_for_peer(unix_socket *other, long timeo)
{
{
        int sched;
        int sched;
        DECLARE_WAITQUEUE(wait, current);
        DECLARE_WAITQUEUE(wait, current);
 
 
        __set_current_state(TASK_INTERRUPTIBLE);
        __set_current_state(TASK_INTERRUPTIBLE);
        add_wait_queue_exclusive(&other->protinfo.af_unix.peer_wait, &wait);
        add_wait_queue_exclusive(&other->protinfo.af_unix.peer_wait, &wait);
 
 
        sched = (!other->dead &&
        sched = (!other->dead &&
                 !(other->shutdown&RCV_SHUTDOWN) &&
                 !(other->shutdown&RCV_SHUTDOWN) &&
                 skb_queue_len(&other->receive_queue) > other->max_ack_backlog);
                 skb_queue_len(&other->receive_queue) > other->max_ack_backlog);
 
 
        unix_state_runlock(other);
        unix_state_runlock(other);
 
 
        if (sched)
        if (sched)
                timeo = schedule_timeout(timeo);
                timeo = schedule_timeout(timeo);
 
 
        __set_current_state(TASK_RUNNING);
        __set_current_state(TASK_RUNNING);
        remove_wait_queue(&other->protinfo.af_unix.peer_wait, &wait);
        remove_wait_queue(&other->protinfo.af_unix.peer_wait, &wait);
        return timeo;
        return timeo;
}
}
 
 
static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
                               int addr_len, int flags)
                               int addr_len, int flags)
{
{
        struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;
        struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct sock *newsk = NULL;
        struct sock *newsk = NULL;
        unix_socket *other = NULL;
        unix_socket *other = NULL;
        struct sk_buff *skb = NULL;
        struct sk_buff *skb = NULL;
        unsigned hash;
        unsigned hash;
        int st;
        int st;
        int err;
        int err;
        long timeo;
        long timeo;
 
 
        err = unix_mkname(sunaddr, addr_len, &hash);
        err = unix_mkname(sunaddr, addr_len, &hash);
        if (err < 0)
        if (err < 0)
                goto out;
                goto out;
        addr_len = err;
        addr_len = err;
 
 
        if (sock->passcred && !sk->protinfo.af_unix.addr &&
        if (sock->passcred && !sk->protinfo.af_unix.addr &&
            (err = unix_autobind(sock)) != 0)
            (err = unix_autobind(sock)) != 0)
                goto out;
                goto out;
 
 
        timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
        timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
 
 
        /* First of all allocate resources.
        /* First of all allocate resources.
           If we will make it after state is locked,
           If we will make it after state is locked,
           we will have to recheck all again in any case.
           we will have to recheck all again in any case.
         */
         */
 
 
        err = -ENOMEM;
        err = -ENOMEM;
 
 
        /* create new sock for complete connection */
        /* create new sock for complete connection */
        newsk = unix_create1(NULL);
        newsk = unix_create1(NULL);
        if (newsk == NULL)
        if (newsk == NULL)
                goto out;
                goto out;
 
 
        /* Allocate skb for sending to listening sock */
        /* Allocate skb for sending to listening sock */
        skb = sock_wmalloc(newsk, 1, 0, GFP_KERNEL);
        skb = sock_wmalloc(newsk, 1, 0, GFP_KERNEL);
        if (skb == NULL)
        if (skb == NULL)
                goto out;
                goto out;
 
 
restart:
restart:
        /*  Find listening sock. */
        /*  Find listening sock. */
        other=unix_find_other(sunaddr, addr_len, sk->type, hash, &err);
        other=unix_find_other(sunaddr, addr_len, sk->type, hash, &err);
        if (!other)
        if (!other)
                goto out;
                goto out;
 
 
        /* Latch state of peer */
        /* Latch state of peer */
        unix_state_rlock(other);
        unix_state_rlock(other);
 
 
        /* Apparently VFS overslept socket death. Retry. */
        /* Apparently VFS overslept socket death. Retry. */
        if (other->dead) {
        if (other->dead) {
                unix_state_runlock(other);
                unix_state_runlock(other);
                sock_put(other);
                sock_put(other);
                goto restart;
                goto restart;
        }
        }
 
 
        err = -ECONNREFUSED;
        err = -ECONNREFUSED;
        if (other->state != TCP_LISTEN)
        if (other->state != TCP_LISTEN)
                goto out_unlock;
                goto out_unlock;
 
 
        if (skb_queue_len(&other->receive_queue) > other->max_ack_backlog) {
        if (skb_queue_len(&other->receive_queue) > other->max_ack_backlog) {
                err = -EAGAIN;
                err = -EAGAIN;
                if (!timeo)
                if (!timeo)
                        goto out_unlock;
                        goto out_unlock;
 
 
                timeo = unix_wait_for_peer(other, timeo);
                timeo = unix_wait_for_peer(other, timeo);
 
 
                err = sock_intr_errno(timeo);
                err = sock_intr_errno(timeo);
                if (signal_pending(current))
                if (signal_pending(current))
                        goto out;
                        goto out;
                sock_put(other);
                sock_put(other);
                goto restart;
                goto restart;
        }
        }
 
 
        /* Latch our state.
        /* Latch our state.
 
 
           It is tricky place. We need to grab write lock and cannot
           It is tricky place. We need to grab write lock and cannot
           drop lock on peer. It is dangerous because deadlock is
           drop lock on peer. It is dangerous because deadlock is
           possible. Connect to self case and simultaneous
           possible. Connect to self case and simultaneous
           attempt to connect are eliminated by checking socket
           attempt to connect are eliminated by checking socket
           state. other is TCP_LISTEN, if sk is TCP_LISTEN we
           state. other is TCP_LISTEN, if sk is TCP_LISTEN we
           check this before attempt to grab lock.
           check this before attempt to grab lock.
 
 
           Well, and we have to recheck the state after socket locked.
           Well, and we have to recheck the state after socket locked.
         */
         */
        st = sk->state;
        st = sk->state;
 
 
        switch (st) {
        switch (st) {
        case TCP_CLOSE:
        case TCP_CLOSE:
                /* This is ok... continue with connect */
                /* This is ok... continue with connect */
                break;
                break;
        case TCP_ESTABLISHED:
        case TCP_ESTABLISHED:
                /* Socket is already connected */
                /* Socket is already connected */
                err = -EISCONN;
                err = -EISCONN;
                goto out_unlock;
                goto out_unlock;
        default:
        default:
                err = -EINVAL;
                err = -EINVAL;
                goto out_unlock;
                goto out_unlock;
        }
        }
 
 
        unix_state_wlock(sk);
        unix_state_wlock(sk);
 
 
        if (sk->state != st) {
        if (sk->state != st) {
                unix_state_wunlock(sk);
                unix_state_wunlock(sk);
                unix_state_runlock(other);
                unix_state_runlock(other);
                sock_put(other);
                sock_put(other);
                goto restart;
                goto restart;
        }
        }
 
 
        /* The way is open! Fastly set all the necessary fields... */
        /* The way is open! Fastly set all the necessary fields... */
 
 
        sock_hold(sk);
        sock_hold(sk);
        unix_peer(newsk)=sk;
        unix_peer(newsk)=sk;
        newsk->state=TCP_ESTABLISHED;
        newsk->state=TCP_ESTABLISHED;
        newsk->type=SOCK_STREAM;
        newsk->type=SOCK_STREAM;
        newsk->peercred.pid = current->pid;
        newsk->peercred.pid = current->pid;
        newsk->peercred.uid = current->euid;
        newsk->peercred.uid = current->euid;
        newsk->peercred.gid = current->egid;
        newsk->peercred.gid = current->egid;
        newsk->sleep = &newsk->protinfo.af_unix.peer_wait;
        newsk->sleep = &newsk->protinfo.af_unix.peer_wait;
 
 
        /* copy address information from listening to new sock*/
        /* copy address information from listening to new sock*/
        if (other->protinfo.af_unix.addr)
        if (other->protinfo.af_unix.addr)
        {
        {
                atomic_inc(&other->protinfo.af_unix.addr->refcnt);
                atomic_inc(&other->protinfo.af_unix.addr->refcnt);
                newsk->protinfo.af_unix.addr=other->protinfo.af_unix.addr;
                newsk->protinfo.af_unix.addr=other->protinfo.af_unix.addr;
        }
        }
        if (other->protinfo.af_unix.dentry) {
        if (other->protinfo.af_unix.dentry) {
                newsk->protinfo.af_unix.dentry=dget(other->protinfo.af_unix.dentry);
                newsk->protinfo.af_unix.dentry=dget(other->protinfo.af_unix.dentry);
                newsk->protinfo.af_unix.mnt=mntget(other->protinfo.af_unix.mnt);
                newsk->protinfo.af_unix.mnt=mntget(other->protinfo.af_unix.mnt);
        }
        }
 
 
        /* Set credentials */
        /* Set credentials */
        sk->peercred = other->peercred;
        sk->peercred = other->peercred;
 
 
        sock_hold(newsk);
        sock_hold(newsk);
        unix_peer(sk)=newsk;
        unix_peer(sk)=newsk;
        sock->state=SS_CONNECTED;
        sock->state=SS_CONNECTED;
        sk->state=TCP_ESTABLISHED;
        sk->state=TCP_ESTABLISHED;
 
 
        unix_state_wunlock(sk);
        unix_state_wunlock(sk);
 
 
        /* take ten and and send info to listening sock */
        /* take ten and and send info to listening sock */
        spin_lock(&other->receive_queue.lock);
        spin_lock(&other->receive_queue.lock);
        __skb_queue_tail(&other->receive_queue,skb);
        __skb_queue_tail(&other->receive_queue,skb);
        /* Undo artificially decreased inflight after embrion
        /* Undo artificially decreased inflight after embrion
         * is installed to listening socket. */
         * is installed to listening socket. */
        atomic_inc(&newsk->protinfo.af_unix.inflight);
        atomic_inc(&newsk->protinfo.af_unix.inflight);
        spin_unlock(&other->receive_queue.lock);
        spin_unlock(&other->receive_queue.lock);
        unix_state_runlock(other);
        unix_state_runlock(other);
        other->data_ready(other, 0);
        other->data_ready(other, 0);
        sock_put(other);
        sock_put(other);
        return 0;
        return 0;
 
 
out_unlock:
out_unlock:
        if (other)
        if (other)
                unix_state_runlock(other);
                unix_state_runlock(other);
 
 
out:
out:
        if (skb)
        if (skb)
                kfree_skb(skb);
                kfree_skb(skb);
        if (newsk)
        if (newsk)
                unix_release_sock(newsk, 0);
                unix_release_sock(newsk, 0);
        if (other)
        if (other)
                sock_put(other);
                sock_put(other);
        return err;
        return err;
}
}
 
 
static int unix_socketpair(struct socket *socka, struct socket *sockb)
static int unix_socketpair(struct socket *socka, struct socket *sockb)
{
{
        struct sock *ska=socka->sk, *skb = sockb->sk;
        struct sock *ska=socka->sk, *skb = sockb->sk;
 
 
        /* Join our sockets back to back */
        /* Join our sockets back to back */
        sock_hold(ska);
        sock_hold(ska);
        sock_hold(skb);
        sock_hold(skb);
        unix_peer(ska)=skb;
        unix_peer(ska)=skb;
        unix_peer(skb)=ska;
        unix_peer(skb)=ska;
        ska->peercred.pid = skb->peercred.pid = current->pid;
        ska->peercred.pid = skb->peercred.pid = current->pid;
        ska->peercred.uid = skb->peercred.uid = current->euid;
        ska->peercred.uid = skb->peercred.uid = current->euid;
        ska->peercred.gid = skb->peercred.gid = current->egid;
        ska->peercred.gid = skb->peercred.gid = current->egid;
 
 
        if (ska->type != SOCK_DGRAM)
        if (ska->type != SOCK_DGRAM)
        {
        {
                ska->state=TCP_ESTABLISHED;
                ska->state=TCP_ESTABLISHED;
                skb->state=TCP_ESTABLISHED;
                skb->state=TCP_ESTABLISHED;
                socka->state=SS_CONNECTED;
                socka->state=SS_CONNECTED;
                sockb->state=SS_CONNECTED;
                sockb->state=SS_CONNECTED;
        }
        }
        return 0;
        return 0;
}
}
 
 
static int unix_accept(struct socket *sock, struct socket *newsock, int flags)
static int unix_accept(struct socket *sock, struct socket *newsock, int flags)
{
{
        unix_socket *sk = sock->sk;
        unix_socket *sk = sock->sk;
        unix_socket *tsk;
        unix_socket *tsk;
        struct sk_buff *skb;
        struct sk_buff *skb;
        int err;
        int err;
 
 
        err = -EOPNOTSUPP;
        err = -EOPNOTSUPP;
        if (sock->type!=SOCK_STREAM)
        if (sock->type!=SOCK_STREAM)
                goto out;
                goto out;
 
 
        err = -EINVAL;
        err = -EINVAL;
        if (sk->state!=TCP_LISTEN)
        if (sk->state!=TCP_LISTEN)
                goto out;
                goto out;
 
 
        /* If socket state is TCP_LISTEN it cannot change (for now...),
        /* If socket state is TCP_LISTEN it cannot change (for now...),
         * so that no locks are necessary.
         * so that no locks are necessary.
         */
         */
 
 
        skb = skb_recv_datagram(sk, 0, flags&O_NONBLOCK, &err);
        skb = skb_recv_datagram(sk, 0, flags&O_NONBLOCK, &err);
        if (!skb) {
        if (!skb) {
                /* This means receive shutdown. */
                /* This means receive shutdown. */
                if (err == 0)
                if (err == 0)
                        err = -EINVAL;
                        err = -EINVAL;
                goto out;
                goto out;
        }
        }
 
 
        tsk = skb->sk;
        tsk = skb->sk;
        skb_free_datagram(sk, skb);
        skb_free_datagram(sk, skb);
        wake_up_interruptible(&sk->protinfo.af_unix.peer_wait);
        wake_up_interruptible(&sk->protinfo.af_unix.peer_wait);
 
 
        /* attach accepted sock to socket */
        /* attach accepted sock to socket */
        unix_state_wlock(tsk);
        unix_state_wlock(tsk);
        newsock->state = SS_CONNECTED;
        newsock->state = SS_CONNECTED;
        sock_graft(tsk, newsock);
        sock_graft(tsk, newsock);
        unix_state_wunlock(tsk);
        unix_state_wunlock(tsk);
        return 0;
        return 0;
 
 
out:
out:
        return err;
        return err;
}
}
 
 
 
 
static int unix_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer)
static int unix_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;
        struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;
        int err = 0;
        int err = 0;
 
 
        if (peer) {
        if (peer) {
                sk = unix_peer_get(sk);
                sk = unix_peer_get(sk);
 
 
                err = -ENOTCONN;
                err = -ENOTCONN;
                if (!sk)
                if (!sk)
                        goto out;
                        goto out;
                err = 0;
                err = 0;
        } else {
        } else {
                sock_hold(sk);
                sock_hold(sk);
        }
        }
 
 
        unix_state_rlock(sk);
        unix_state_rlock(sk);
        if (!sk->protinfo.af_unix.addr) {
        if (!sk->protinfo.af_unix.addr) {
                sunaddr->sun_family = AF_UNIX;
                sunaddr->sun_family = AF_UNIX;
                sunaddr->sun_path[0] = 0;
                sunaddr->sun_path[0] = 0;
                *uaddr_len = sizeof(short);
                *uaddr_len = sizeof(short);
        } else {
        } else {
                struct unix_address *addr = sk->protinfo.af_unix.addr;
                struct unix_address *addr = sk->protinfo.af_unix.addr;
 
 
                *uaddr_len = addr->len;
                *uaddr_len = addr->len;
                memcpy(sunaddr, addr->name, *uaddr_len);
                memcpy(sunaddr, addr->name, *uaddr_len);
        }
        }
        unix_state_runlock(sk);
        unix_state_runlock(sk);
        sock_put(sk);
        sock_put(sk);
out:
out:
        return err;
        return err;
}
}
 
 
static void unix_detach_fds(struct scm_cookie *scm, struct sk_buff *skb)
static void unix_detach_fds(struct scm_cookie *scm, struct sk_buff *skb)
{
{
        int i;
        int i;
 
 
        scm->fp = UNIXCB(skb).fp;
        scm->fp = UNIXCB(skb).fp;
        skb->destructor = sock_wfree;
        skb->destructor = sock_wfree;
        UNIXCB(skb).fp = NULL;
        UNIXCB(skb).fp = NULL;
 
 
        for (i=scm->fp->count-1; i>=0; i--)
        for (i=scm->fp->count-1; i>=0; i--)
                unix_notinflight(scm->fp->fp[i]);
                unix_notinflight(scm->fp->fp[i]);
}
}
 
 
static void unix_destruct_fds(struct sk_buff *skb)
static void unix_destruct_fds(struct sk_buff *skb)
{
{
        struct scm_cookie scm;
        struct scm_cookie scm;
        memset(&scm, 0, sizeof(scm));
        memset(&scm, 0, sizeof(scm));
        unix_detach_fds(&scm, skb);
        unix_detach_fds(&scm, skb);
 
 
        /* Alas, it calls VFS */
        /* Alas, it calls VFS */
        /* So fscking what? fput() had been SMP-safe since the last Summer */
        /* So fscking what? fput() had been SMP-safe since the last Summer */
        scm_destroy(&scm);
        scm_destroy(&scm);
        sock_wfree(skb);
        sock_wfree(skb);
}
}
 
 
static void unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
static void unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
{
{
        int i;
        int i;
        for (i=scm->fp->count-1; i>=0; i--)
        for (i=scm->fp->count-1; i>=0; i--)
                unix_inflight(scm->fp->fp[i]);
                unix_inflight(scm->fp->fp[i]);
        UNIXCB(skb).fp = scm->fp;
        UNIXCB(skb).fp = scm->fp;
        skb->destructor = unix_destruct_fds;
        skb->destructor = unix_destruct_fds;
        scm->fp = NULL;
        scm->fp = NULL;
}
}
 
 
/*
/*
 *      Send AF_UNIX data.
 *      Send AF_UNIX data.
 */
 */
 
 
static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, int len,
static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, int len,
                              struct scm_cookie *scm)
                              struct scm_cookie *scm)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct sockaddr_un *sunaddr=msg->msg_name;
        struct sockaddr_un *sunaddr=msg->msg_name;
        unix_socket *other = NULL;
        unix_socket *other = NULL;
        int namelen = 0; /* fake GCC */
        int namelen = 0; /* fake GCC */
        int err;
        int err;
        unsigned hash;
        unsigned hash;
        struct sk_buff *skb;
        struct sk_buff *skb;
        long timeo;
        long timeo;
 
 
        err = -EOPNOTSUPP;
        err = -EOPNOTSUPP;
        if (msg->msg_flags&MSG_OOB)
        if (msg->msg_flags&MSG_OOB)
                goto out;
                goto out;
 
 
        if (msg->msg_namelen) {
        if (msg->msg_namelen) {
                err = unix_mkname(sunaddr, msg->msg_namelen, &hash);
                err = unix_mkname(sunaddr, msg->msg_namelen, &hash);
                if (err < 0)
                if (err < 0)
                        goto out;
                        goto out;
                namelen = err;
                namelen = err;
        } else {
        } else {
                sunaddr = NULL;
                sunaddr = NULL;
                err = -ENOTCONN;
                err = -ENOTCONN;
                other = unix_peer_get(sk);
                other = unix_peer_get(sk);
                if (!other)
                if (!other)
                        goto out;
                        goto out;
        }
        }
 
 
        if (sock->passcred && !sk->protinfo.af_unix.addr &&
        if (sock->passcred && !sk->protinfo.af_unix.addr &&
            (err = unix_autobind(sock)) != 0)
            (err = unix_autobind(sock)) != 0)
                goto out;
                goto out;
 
 
        err = -EMSGSIZE;
        err = -EMSGSIZE;
        if ((unsigned)len > sk->sndbuf - 32)
        if ((unsigned)len > sk->sndbuf - 32)
                goto out;
                goto out;
 
 
        skb = sock_alloc_send_skb(sk, len, msg->msg_flags&MSG_DONTWAIT, &err);
        skb = sock_alloc_send_skb(sk, len, msg->msg_flags&MSG_DONTWAIT, &err);
        if (skb==NULL)
        if (skb==NULL)
                goto out;
                goto out;
 
 
        memcpy(UNIXCREDS(skb), &scm->creds, sizeof(struct ucred));
        memcpy(UNIXCREDS(skb), &scm->creds, sizeof(struct ucred));
        if (scm->fp)
        if (scm->fp)
                unix_attach_fds(scm, skb);
                unix_attach_fds(scm, skb);
 
 
        skb->h.raw = skb->data;
        skb->h.raw = skb->data;
        err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
        err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
        if (err)
        if (err)
                goto out_free;
                goto out_free;
 
 
        timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
        timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
 
 
restart:
restart:
        if (!other) {
        if (!other) {
                err = -ECONNRESET;
                err = -ECONNRESET;
                if (sunaddr == NULL)
                if (sunaddr == NULL)
                        goto out_free;
                        goto out_free;
 
 
                other = unix_find_other(sunaddr, namelen, sk->type, hash, &err);
                other = unix_find_other(sunaddr, namelen, sk->type, hash, &err);
                if (other==NULL)
                if (other==NULL)
                        goto out_free;
                        goto out_free;
        }
        }
 
 
        unix_state_rlock(other);
        unix_state_rlock(other);
        err = -EPERM;
        err = -EPERM;
        if (!unix_may_send(sk, other))
        if (!unix_may_send(sk, other))
                goto out_unlock;
                goto out_unlock;
 
 
        if (other->dead) {
        if (other->dead) {
                /*
                /*
                 *      Check with 1003.1g - what should
                 *      Check with 1003.1g - what should
                 *      datagram error
                 *      datagram error
                 */
                 */
                unix_state_runlock(other);
                unix_state_runlock(other);
                sock_put(other);
                sock_put(other);
 
 
                err = 0;
                err = 0;
                unix_state_wlock(sk);
                unix_state_wlock(sk);
                if (unix_peer(sk) == other) {
                if (unix_peer(sk) == other) {
                        unix_peer(sk)=NULL;
                        unix_peer(sk)=NULL;
                        unix_state_wunlock(sk);
                        unix_state_wunlock(sk);
 
 
                        unix_dgram_disconnected(sk, other);
                        unix_dgram_disconnected(sk, other);
                        sock_put(other);
                        sock_put(other);
                        err = -ECONNREFUSED;
                        err = -ECONNREFUSED;
                } else {
                } else {
                        unix_state_wunlock(sk);
                        unix_state_wunlock(sk);
                }
                }
 
 
                other = NULL;
                other = NULL;
                if (err)
                if (err)
                        goto out_free;
                        goto out_free;
                goto restart;
                goto restart;
        }
        }
 
 
        err = -EPIPE;
        err = -EPIPE;
        if (other->shutdown&RCV_SHUTDOWN)
        if (other->shutdown&RCV_SHUTDOWN)
                goto out_unlock;
                goto out_unlock;
 
 
        if (unix_peer(other) != sk &&
        if (unix_peer(other) != sk &&
            skb_queue_len(&other->receive_queue) > other->max_ack_backlog) {
            skb_queue_len(&other->receive_queue) > other->max_ack_backlog) {
                if (!timeo) {
                if (!timeo) {
                        err = -EAGAIN;
                        err = -EAGAIN;
                        goto out_unlock;
                        goto out_unlock;
                }
                }
 
 
                timeo = unix_wait_for_peer(other, timeo);
                timeo = unix_wait_for_peer(other, timeo);
 
 
                err = sock_intr_errno(timeo);
                err = sock_intr_errno(timeo);
                if (signal_pending(current))
                if (signal_pending(current))
                        goto out_free;
                        goto out_free;
 
 
                goto restart;
                goto restart;
        }
        }
 
 
        skb_queue_tail(&other->receive_queue, skb);
        skb_queue_tail(&other->receive_queue, skb);
        unix_state_runlock(other);
        unix_state_runlock(other);
        other->data_ready(other, len);
        other->data_ready(other, len);
        sock_put(other);
        sock_put(other);
        return len;
        return len;
 
 
out_unlock:
out_unlock:
        unix_state_runlock(other);
        unix_state_runlock(other);
out_free:
out_free:
        kfree_skb(skb);
        kfree_skb(skb);
out:
out:
        if (other)
        if (other)
                sock_put(other);
                sock_put(other);
        return err;
        return err;
}
}
 
 
 
 
static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, int len,
static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, int len,
                               struct scm_cookie *scm)
                               struct scm_cookie *scm)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        unix_socket *other = NULL;
        unix_socket *other = NULL;
        struct sockaddr_un *sunaddr=msg->msg_name;
        struct sockaddr_un *sunaddr=msg->msg_name;
        int err,size;
        int err,size;
        struct sk_buff *skb;
        struct sk_buff *skb;
        int sent=0;
        int sent=0;
 
 
        err = -EOPNOTSUPP;
        err = -EOPNOTSUPP;
        if (msg->msg_flags&MSG_OOB)
        if (msg->msg_flags&MSG_OOB)
                goto out_err;
                goto out_err;
 
 
        if (msg->msg_namelen) {
        if (msg->msg_namelen) {
                err = (sk->state==TCP_ESTABLISHED ? -EISCONN : -EOPNOTSUPP);
                err = (sk->state==TCP_ESTABLISHED ? -EISCONN : -EOPNOTSUPP);
                goto out_err;
                goto out_err;
        } else {
        } else {
                sunaddr = NULL;
                sunaddr = NULL;
                err = -ENOTCONN;
                err = -ENOTCONN;
                other = unix_peer_get(sk);
                other = unix_peer_get(sk);
                if (!other)
                if (!other)
                        goto out_err;
                        goto out_err;
        }
        }
 
 
        if (sk->shutdown&SEND_SHUTDOWN)
        if (sk->shutdown&SEND_SHUTDOWN)
                goto pipe_err;
                goto pipe_err;
 
 
        while(sent < len)
        while(sent < len)
        {
        {
                /*
                /*
                 *      Optimisation for the fact that under 0.01% of X messages typically
                 *      Optimisation for the fact that under 0.01% of X messages typically
                 *      need breaking up.
                 *      need breaking up.
                 */
                 */
 
 
                size=len-sent;
                size=len-sent;
 
 
                /* Keep two messages in the pipe so it schedules better */
                /* Keep two messages in the pipe so it schedules better */
                if (size > sk->sndbuf/2 - 64)
                if (size > sk->sndbuf/2 - 64)
                        size = sk->sndbuf/2 - 64;
                        size = sk->sndbuf/2 - 64;
 
 
                if (size > SKB_MAX_ALLOC)
                if (size > SKB_MAX_ALLOC)
                        size = SKB_MAX_ALLOC;
                        size = SKB_MAX_ALLOC;
 
 
                /*
                /*
                 *      Grab a buffer
                 *      Grab a buffer
                 */
                 */
 
 
                skb=sock_alloc_send_skb(sk,size,msg->msg_flags&MSG_DONTWAIT, &err);
                skb=sock_alloc_send_skb(sk,size,msg->msg_flags&MSG_DONTWAIT, &err);
 
 
                if (skb==NULL)
                if (skb==NULL)
                        goto out_err;
                        goto out_err;
 
 
                /*
                /*
                 *      If you pass two values to the sock_alloc_send_skb
                 *      If you pass two values to the sock_alloc_send_skb
                 *      it tries to grab the large buffer with GFP_NOFS
                 *      it tries to grab the large buffer with GFP_NOFS
                 *      (which can fail easily), and if it fails grab the
                 *      (which can fail easily), and if it fails grab the
                 *      fallback size buffer which is under a page and will
                 *      fallback size buffer which is under a page and will
                 *      succeed. [Alan]
                 *      succeed. [Alan]
                 */
                 */
                size = min_t(int, size, skb_tailroom(skb));
                size = min_t(int, size, skb_tailroom(skb));
 
 
                memcpy(UNIXCREDS(skb), &scm->creds, sizeof(struct ucred));
                memcpy(UNIXCREDS(skb), &scm->creds, sizeof(struct ucred));
                if (scm->fp)
                if (scm->fp)
                        unix_attach_fds(scm, skb);
                        unix_attach_fds(scm, skb);
 
 
                if ((err = memcpy_fromiovec(skb_put(skb,size), msg->msg_iov, size)) != 0) {
                if ((err = memcpy_fromiovec(skb_put(skb,size), msg->msg_iov, size)) != 0) {
                        kfree_skb(skb);
                        kfree_skb(skb);
                        goto out_err;
                        goto out_err;
                }
                }
 
 
                unix_state_rlock(other);
                unix_state_rlock(other);
 
 
                if (other->dead || (other->shutdown & RCV_SHUTDOWN))
                if (other->dead || (other->shutdown & RCV_SHUTDOWN))
                        goto pipe_err_free;
                        goto pipe_err_free;
 
 
                skb_queue_tail(&other->receive_queue, skb);
                skb_queue_tail(&other->receive_queue, skb);
                unix_state_runlock(other);
                unix_state_runlock(other);
                other->data_ready(other, size);
                other->data_ready(other, size);
                sent+=size;
                sent+=size;
        }
        }
        sock_put(other);
        sock_put(other);
        return sent;
        return sent;
 
 
pipe_err_free:
pipe_err_free:
        unix_state_runlock(other);
        unix_state_runlock(other);
        kfree_skb(skb);
        kfree_skb(skb);
pipe_err:
pipe_err:
        if (sent==0 && !(msg->msg_flags&MSG_NOSIGNAL))
        if (sent==0 && !(msg->msg_flags&MSG_NOSIGNAL))
                send_sig(SIGPIPE,current,0);
                send_sig(SIGPIPE,current,0);
        err = -EPIPE;
        err = -EPIPE;
out_err:
out_err:
        if (other)
        if (other)
                sock_put(other);
                sock_put(other);
        return sent ? : err;
        return sent ? : err;
}
}
 
 
static void unix_copy_addr(struct msghdr *msg, struct sock *sk)
static void unix_copy_addr(struct msghdr *msg, struct sock *sk)
{
{
        msg->msg_namelen = 0;
        msg->msg_namelen = 0;
        if (sk->protinfo.af_unix.addr) {
        if (sk->protinfo.af_unix.addr) {
                msg->msg_namelen=sk->protinfo.af_unix.addr->len;
                msg->msg_namelen=sk->protinfo.af_unix.addr->len;
                memcpy(msg->msg_name,
                memcpy(msg->msg_name,
                       sk->protinfo.af_unix.addr->name,
                       sk->protinfo.af_unix.addr->name,
                       sk->protinfo.af_unix.addr->len);
                       sk->protinfo.af_unix.addr->len);
        }
        }
}
}
 
 
static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg, int size,
static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg, int size,
                              int flags, struct scm_cookie *scm)
                              int flags, struct scm_cookie *scm)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        int noblock = flags & MSG_DONTWAIT;
        int noblock = flags & MSG_DONTWAIT;
        struct sk_buff *skb;
        struct sk_buff *skb;
        int err;
        int err;
 
 
        err = -EOPNOTSUPP;
        err = -EOPNOTSUPP;
        if (flags&MSG_OOB)
        if (flags&MSG_OOB)
                goto out;
                goto out;
 
 
        msg->msg_namelen = 0;
        msg->msg_namelen = 0;
 
 
        skb = skb_recv_datagram(sk, flags, noblock, &err);
        skb = skb_recv_datagram(sk, flags, noblock, &err);
        if (!skb)
        if (!skb)
                goto out;
                goto out;
 
 
        wake_up_interruptible(&sk->protinfo.af_unix.peer_wait);
        wake_up_interruptible(&sk->protinfo.af_unix.peer_wait);
 
 
        if (msg->msg_name)
        if (msg->msg_name)
                unix_copy_addr(msg, skb->sk);
                unix_copy_addr(msg, skb->sk);
 
 
        if (size > skb->len)
        if (size > skb->len)
                size = skb->len;
                size = skb->len;
        else if (size < skb->len)
        else if (size < skb->len)
                msg->msg_flags |= MSG_TRUNC;
                msg->msg_flags |= MSG_TRUNC;
 
 
        err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, size);
        err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, size);
        if (err)
        if (err)
                goto out_free;
                goto out_free;
 
 
        scm->creds = *UNIXCREDS(skb);
        scm->creds = *UNIXCREDS(skb);
 
 
        if (!(flags & MSG_PEEK))
        if (!(flags & MSG_PEEK))
        {
        {
                if (UNIXCB(skb).fp)
                if (UNIXCB(skb).fp)
                        unix_detach_fds(scm, skb);
                        unix_detach_fds(scm, skb);
        }
        }
        else
        else
        {
        {
                /* It is questionable: on PEEK we could:
                /* It is questionable: on PEEK we could:
                   - do not return fds - good, but too simple 8)
                   - do not return fds - good, but too simple 8)
                   - return fds, and do not return them on read (old strategy,
                   - return fds, and do not return them on read (old strategy,
                     apparently wrong)
                     apparently wrong)
                   - clone fds (I choosed it for now, it is the most universal
                   - clone fds (I choosed it for now, it is the most universal
                     solution)
                     solution)
 
 
                   POSIX 1003.1g does not actually define this clearly
                   POSIX 1003.1g does not actually define this clearly
                   at all. POSIX 1003.1g doesn't define a lot of things
                   at all. POSIX 1003.1g doesn't define a lot of things
                   clearly however!
                   clearly however!
 
 
                */
                */
                if (UNIXCB(skb).fp)
                if (UNIXCB(skb).fp)
                        scm->fp = scm_fp_dup(UNIXCB(skb).fp);
                        scm->fp = scm_fp_dup(UNIXCB(skb).fp);
        }
        }
        err = size;
        err = size;
 
 
out_free:
out_free:
        skb_free_datagram(sk,skb);
        skb_free_datagram(sk,skb);
out:
out:
        return err;
        return err;
}
}
 
 
/*
/*
 *      Sleep until data has arrive. But check for races..
 *      Sleep until data has arrive. But check for races..
 */
 */
 
 
static long unix_stream_data_wait(unix_socket * sk, long timeo)
static long unix_stream_data_wait(unix_socket * sk, long timeo)
{
{
        DECLARE_WAITQUEUE(wait, current);
        DECLARE_WAITQUEUE(wait, current);
 
 
        unix_state_rlock(sk);
        unix_state_rlock(sk);
 
 
        add_wait_queue(sk->sleep, &wait);
        add_wait_queue(sk->sleep, &wait);
 
 
        for (;;) {
        for (;;) {
                set_current_state(TASK_INTERRUPTIBLE);
                set_current_state(TASK_INTERRUPTIBLE);
 
 
                if (skb_queue_len(&sk->receive_queue) ||
                if (skb_queue_len(&sk->receive_queue) ||
                    sk->err ||
                    sk->err ||
                    (sk->shutdown & RCV_SHUTDOWN) ||
                    (sk->shutdown & RCV_SHUTDOWN) ||
                    signal_pending(current) ||
                    signal_pending(current) ||
                    !timeo)
                    !timeo)
                        break;
                        break;
 
 
                set_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags);
                set_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags);
                unix_state_runlock(sk);
                unix_state_runlock(sk);
                timeo = schedule_timeout(timeo);
                timeo = schedule_timeout(timeo);
                unix_state_rlock(sk);
                unix_state_rlock(sk);
                clear_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags);
                clear_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags);
        }
        }
 
 
        __set_current_state(TASK_RUNNING);
        __set_current_state(TASK_RUNNING);
        remove_wait_queue(sk->sleep, &wait);
        remove_wait_queue(sk->sleep, &wait);
        unix_state_runlock(sk);
        unix_state_runlock(sk);
        return timeo;
        return timeo;
}
}
 
 
 
 
 
 
static int unix_stream_recvmsg(struct socket *sock, struct msghdr *msg, int size,
static int unix_stream_recvmsg(struct socket *sock, struct msghdr *msg, int size,
                               int flags, struct scm_cookie *scm)
                               int flags, struct scm_cookie *scm)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct sockaddr_un *sunaddr=msg->msg_name;
        struct sockaddr_un *sunaddr=msg->msg_name;
        int copied = 0;
        int copied = 0;
        int check_creds = 0;
        int check_creds = 0;
        int target;
        int target;
        int err = 0;
        int err = 0;
        long timeo;
        long timeo;
 
 
        err = -EINVAL;
        err = -EINVAL;
        if (sk->state != TCP_ESTABLISHED)
        if (sk->state != TCP_ESTABLISHED)
                goto out;
                goto out;
 
 
        err = -EOPNOTSUPP;
        err = -EOPNOTSUPP;
        if (flags&MSG_OOB)
        if (flags&MSG_OOB)
                goto out;
                goto out;
 
 
        target = sock_rcvlowat(sk, flags&MSG_WAITALL, size);
        target = sock_rcvlowat(sk, flags&MSG_WAITALL, size);
        timeo = sock_rcvtimeo(sk, flags&MSG_DONTWAIT);
        timeo = sock_rcvtimeo(sk, flags&MSG_DONTWAIT);
 
 
        msg->msg_namelen = 0;
        msg->msg_namelen = 0;
 
 
        /* Lock the socket to prevent queue disordering
        /* Lock the socket to prevent queue disordering
         * while sleeps in memcpy_tomsg
         * while sleeps in memcpy_tomsg
         */
         */
 
 
        down(&sk->protinfo.af_unix.readsem);
        down(&sk->protinfo.af_unix.readsem);
 
 
        do
        do
        {
        {
                int chunk;
                int chunk;
                struct sk_buff *skb;
                struct sk_buff *skb;
 
 
                skb=skb_dequeue(&sk->receive_queue);
                skb=skb_dequeue(&sk->receive_queue);
                if (skb==NULL)
                if (skb==NULL)
                {
                {
                        if (copied >= target)
                        if (copied >= target)
                                break;
                                break;
 
 
                        /*
                        /*
                         *      POSIX 1003.1g mandates this order.
                         *      POSIX 1003.1g mandates this order.
                         */
                         */
 
 
                        if ((err = sock_error(sk)) != 0)
                        if ((err = sock_error(sk)) != 0)
                                break;
                                break;
                        if (sk->shutdown & RCV_SHUTDOWN)
                        if (sk->shutdown & RCV_SHUTDOWN)
                                break;
                                break;
                        err = -EAGAIN;
                        err = -EAGAIN;
                        if (!timeo)
                        if (!timeo)
                                break;
                                break;
                        up(&sk->protinfo.af_unix.readsem);
                        up(&sk->protinfo.af_unix.readsem);
 
 
                        timeo = unix_stream_data_wait(sk, timeo);
                        timeo = unix_stream_data_wait(sk, timeo);
 
 
                        if (signal_pending(current)) {
                        if (signal_pending(current)) {
                                err = sock_intr_errno(timeo);
                                err = sock_intr_errno(timeo);
                                goto out;
                                goto out;
                        }
                        }
                        down(&sk->protinfo.af_unix.readsem);
                        down(&sk->protinfo.af_unix.readsem);
                        continue;
                        continue;
                }
                }
 
 
                if (check_creds) {
                if (check_creds) {
                        /* Never glue messages from different writers */
                        /* Never glue messages from different writers */
                        if (memcmp(UNIXCREDS(skb), &scm->creds, sizeof(scm->creds)) != 0) {
                        if (memcmp(UNIXCREDS(skb), &scm->creds, sizeof(scm->creds)) != 0) {
                                skb_queue_head(&sk->receive_queue, skb);
                                skb_queue_head(&sk->receive_queue, skb);
                                break;
                                break;
                        }
                        }
                } else {
                } else {
                        /* Copy credentials */
                        /* Copy credentials */
                        scm->creds = *UNIXCREDS(skb);
                        scm->creds = *UNIXCREDS(skb);
                        check_creds = 1;
                        check_creds = 1;
                }
                }
 
 
                /* Copy address just once */
                /* Copy address just once */
                if (sunaddr)
                if (sunaddr)
                {
                {
                        unix_copy_addr(msg, skb->sk);
                        unix_copy_addr(msg, skb->sk);
                        sunaddr = NULL;
                        sunaddr = NULL;
                }
                }
 
 
                chunk = min_t(unsigned int, skb->len, size);
                chunk = min_t(unsigned int, skb->len, size);
                if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
                if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
                        skb_queue_head(&sk->receive_queue, skb);
                        skb_queue_head(&sk->receive_queue, skb);
                        if (copied == 0)
                        if (copied == 0)
                                copied = -EFAULT;
                                copied = -EFAULT;
                        break;
                        break;
                }
                }
                copied += chunk;
                copied += chunk;
                size -= chunk;
                size -= chunk;
 
 
                /* Mark read part of skb as used */
                /* Mark read part of skb as used */
                if (!(flags & MSG_PEEK))
                if (!(flags & MSG_PEEK))
                {
                {
                        skb_pull(skb, chunk);
                        skb_pull(skb, chunk);
 
 
                        if (UNIXCB(skb).fp)
                        if (UNIXCB(skb).fp)
                                unix_detach_fds(scm, skb);
                                unix_detach_fds(scm, skb);
 
 
                        /* put the skb back if we didn't use it up.. */
                        /* put the skb back if we didn't use it up.. */
                        if (skb->len)
                        if (skb->len)
                        {
                        {
                                skb_queue_head(&sk->receive_queue, skb);
                                skb_queue_head(&sk->receive_queue, skb);
                                break;
                                break;
                        }
                        }
 
 
                        kfree_skb(skb);
                        kfree_skb(skb);
 
 
                        if (scm->fp)
                        if (scm->fp)
                                break;
                                break;
                }
                }
                else
                else
                {
                {
                        /* It is questionable, see note in unix_dgram_recvmsg.
                        /* It is questionable, see note in unix_dgram_recvmsg.
                         */
                         */
                        if (UNIXCB(skb).fp)
                        if (UNIXCB(skb).fp)
                                scm->fp = scm_fp_dup(UNIXCB(skb).fp);
                                scm->fp = scm_fp_dup(UNIXCB(skb).fp);
 
 
                        /* put message back and return */
                        /* put message back and return */
                        skb_queue_head(&sk->receive_queue, skb);
                        skb_queue_head(&sk->receive_queue, skb);
                        break;
                        break;
                }
                }
        } while (size);
        } while (size);
 
 
        up(&sk->protinfo.af_unix.readsem);
        up(&sk->protinfo.af_unix.readsem);
out:
out:
        return copied ? : err;
        return copied ? : err;
}
}
 
 
static int unix_shutdown(struct socket *sock, int mode)
static int unix_shutdown(struct socket *sock, int mode)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        unix_socket *other;
        unix_socket *other;
 
 
        mode = (mode+1)&(RCV_SHUTDOWN|SEND_SHUTDOWN);
        mode = (mode+1)&(RCV_SHUTDOWN|SEND_SHUTDOWN);
 
 
        if (mode) {
        if (mode) {
                unix_state_wlock(sk);
                unix_state_wlock(sk);
                sk->shutdown |= mode;
                sk->shutdown |= mode;
                other=unix_peer(sk);
                other=unix_peer(sk);
                if (other)
                if (other)
                        sock_hold(other);
                        sock_hold(other);
                unix_state_wunlock(sk);
                unix_state_wunlock(sk);
                sk->state_change(sk);
                sk->state_change(sk);
 
 
                if (other && sk->type == SOCK_STREAM) {
                if (other && sk->type == SOCK_STREAM) {
                        int peer_mode = 0;
                        int peer_mode = 0;
 
 
                        if (mode&RCV_SHUTDOWN)
                        if (mode&RCV_SHUTDOWN)
                                peer_mode |= SEND_SHUTDOWN;
                                peer_mode |= SEND_SHUTDOWN;
                        if (mode&SEND_SHUTDOWN)
                        if (mode&SEND_SHUTDOWN)
                                peer_mode |= RCV_SHUTDOWN;
                                peer_mode |= RCV_SHUTDOWN;
                        unix_state_wlock(other);
                        unix_state_wlock(other);
                        other->shutdown |= peer_mode;
                        other->shutdown |= peer_mode;
                        unix_state_wunlock(other);
                        unix_state_wunlock(other);
                        other->state_change(other);
                        other->state_change(other);
                        read_lock(&other->callback_lock);
                        read_lock(&other->callback_lock);
                        if (peer_mode == SHUTDOWN_MASK)
                        if (peer_mode == SHUTDOWN_MASK)
                                sk_wake_async(other,1,POLL_HUP);
                                sk_wake_async(other,1,POLL_HUP);
                        else if (peer_mode & RCV_SHUTDOWN)
                        else if (peer_mode & RCV_SHUTDOWN)
                                sk_wake_async(other,1,POLL_IN);
                                sk_wake_async(other,1,POLL_IN);
                        read_unlock(&other->callback_lock);
                        read_unlock(&other->callback_lock);
                }
                }
                if (other)
                if (other)
                        sock_put(other);
                        sock_put(other);
        }
        }
        return 0;
        return 0;
}
}
 
 
static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        long amount=0;
        long amount=0;
        int err;
        int err;
 
 
        switch(cmd)
        switch(cmd)
        {
        {
                case SIOCOUTQ:
                case SIOCOUTQ:
                        amount = atomic_read(&sk->wmem_alloc);
                        amount = atomic_read(&sk->wmem_alloc);
                        err = put_user(amount, (int *)arg);
                        err = put_user(amount, (int *)arg);
                        break;
                        break;
                case SIOCINQ:
                case SIOCINQ:
                {
                {
                        struct sk_buff *skb;
                        struct sk_buff *skb;
                        if (sk->state==TCP_LISTEN) {
                        if (sk->state==TCP_LISTEN) {
                                err = -EINVAL;
                                err = -EINVAL;
                                break;
                                break;
                        }
                        }
 
 
                        spin_lock(&sk->receive_queue.lock);
                        spin_lock(&sk->receive_queue.lock);
                        if((skb=skb_peek(&sk->receive_queue))!=NULL)
                        if((skb=skb_peek(&sk->receive_queue))!=NULL)
                                amount=skb->len;
                                amount=skb->len;
                        spin_unlock(&sk->receive_queue.lock);
                        spin_unlock(&sk->receive_queue.lock);
                        err = put_user(amount, (int *)arg);
                        err = put_user(amount, (int *)arg);
                        break;
                        break;
                }
                }
 
 
                default:
                default:
                        err = dev_ioctl(cmd, (void *)arg);
                        err = dev_ioctl(cmd, (void *)arg);
                        break;
                        break;
        }
        }
        return err;
        return err;
}
}
 
 
static unsigned int unix_poll(struct file * file, struct socket *sock, poll_table *wait)
static unsigned int unix_poll(struct file * file, struct socket *sock, poll_table *wait)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        unsigned int mask;
        unsigned int mask;
 
 
        poll_wait(file, sk->sleep, wait);
        poll_wait(file, sk->sleep, wait);
        mask = 0;
        mask = 0;
 
 
        /* exceptional events? */
        /* exceptional events? */
        if (sk->err)
        if (sk->err)
                mask |= POLLERR;
                mask |= POLLERR;
        if (sk->shutdown == SHUTDOWN_MASK)
        if (sk->shutdown == SHUTDOWN_MASK)
                mask |= POLLHUP;
                mask |= POLLHUP;
 
 
        /* readable? */
        /* readable? */
        if (!skb_queue_empty(&sk->receive_queue) || (sk->shutdown&RCV_SHUTDOWN))
        if (!skb_queue_empty(&sk->receive_queue) || (sk->shutdown&RCV_SHUTDOWN))
                mask |= POLLIN | POLLRDNORM;
                mask |= POLLIN | POLLRDNORM;
 
 
        /* Connection-based need to check for termination and startup */
        /* Connection-based need to check for termination and startup */
        if (sk->type == SOCK_STREAM && sk->state==TCP_CLOSE)
        if (sk->type == SOCK_STREAM && sk->state==TCP_CLOSE)
                mask |= POLLHUP;
                mask |= POLLHUP;
 
 
        /*
        /*
         * we set writable also when the other side has shut down the
         * we set writable also when the other side has shut down the
         * connection. This prevents stuck sockets.
         * connection. This prevents stuck sockets.
         */
         */
        if (unix_writable(sk))
        if (unix_writable(sk))
                mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
                mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
 
 
        return mask;
        return mask;
}
}
 
 
 
 
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_PROC_FS
static int unix_read_proc(char *buffer, char **start, off_t offset,
static int unix_read_proc(char *buffer, char **start, off_t offset,
                          int length, int *eof, void *data)
                          int length, int *eof, void *data)
{
{
        off_t pos=0;
        off_t pos=0;
        off_t begin=0;
        off_t begin=0;
        int len=0;
        int len=0;
        int i;
        int i;
        unix_socket *s;
        unix_socket *s;
 
 
        len+= sprintf(buffer,"Num       RefCount Protocol Flags    Type St "
        len+= sprintf(buffer,"Num       RefCount Protocol Flags    Type St "
            "Inode Path\n");
            "Inode Path\n");
 
 
        read_lock(&unix_table_lock);
        read_lock(&unix_table_lock);
        forall_unix_sockets (i,s)
        forall_unix_sockets (i,s)
        {
        {
                unix_state_rlock(s);
                unix_state_rlock(s);
 
 
                len+=sprintf(buffer+len,"%p: %08X %08X %08X %04X %02X %5lu",
                len+=sprintf(buffer+len,"%p: %08X %08X %08X %04X %02X %5lu",
                        s,
                        s,
                        atomic_read(&s->refcnt),
                        atomic_read(&s->refcnt),
                        0,
                        0,
                        s->state == TCP_LISTEN ? __SO_ACCEPTCON : 0,
                        s->state == TCP_LISTEN ? __SO_ACCEPTCON : 0,
                        s->type,
                        s->type,
                        s->socket ?
                        s->socket ?
                        (s->state == TCP_ESTABLISHED ? SS_CONNECTED : SS_UNCONNECTED) :
                        (s->state == TCP_ESTABLISHED ? SS_CONNECTED : SS_UNCONNECTED) :
                        (s->state == TCP_ESTABLISHED ? SS_CONNECTING : SS_DISCONNECTING),
                        (s->state == TCP_ESTABLISHED ? SS_CONNECTING : SS_DISCONNECTING),
                        sock_i_ino(s));
                        sock_i_ino(s));
 
 
                if (s->protinfo.af_unix.addr)
                if (s->protinfo.af_unix.addr)
                {
                {
                        buffer[len++] = ' ';
                        buffer[len++] = ' ';
                        memcpy(buffer+len, s->protinfo.af_unix.addr->name->sun_path,
                        memcpy(buffer+len, s->protinfo.af_unix.addr->name->sun_path,
                               s->protinfo.af_unix.addr->len-sizeof(short));
                               s->protinfo.af_unix.addr->len-sizeof(short));
                        if (!UNIX_ABSTRACT(s))
                        if (!UNIX_ABSTRACT(s))
                                len--;
                                len--;
                        else
                        else
                                buffer[len] = '@';
                                buffer[len] = '@';
                        len += s->protinfo.af_unix.addr->len - sizeof(short);
                        len += s->protinfo.af_unix.addr->len - sizeof(short);
                }
                }
                unix_state_runlock(s);
                unix_state_runlock(s);
 
 
                buffer[len++]='\n';
                buffer[len++]='\n';
 
 
                pos = begin + len;
                pos = begin + len;
                if(pos<offset)
                if(pos<offset)
                {
                {
                        len=0;
                        len=0;
                        begin=pos;
                        begin=pos;
                }
                }
                if(pos>offset+length)
                if(pos>offset+length)
                        goto done;
                        goto done;
        }
        }
        *eof = 1;
        *eof = 1;
done:
done:
        read_unlock(&unix_table_lock);
        read_unlock(&unix_table_lock);
        *start=buffer+(offset-begin);
        *start=buffer+(offset-begin);
        len-=(offset-begin);
        len-=(offset-begin);
        if(len>length)
        if(len>length)
                len=length;
                len=length;
        if (len < 0)
        if (len < 0)
                len = 0;
                len = 0;
        return len;
        return len;
}
}
#endif
#endif
 
 
struct proto_ops unix_stream_ops = {
struct proto_ops unix_stream_ops = {
        family:         PF_UNIX,
        family:         PF_UNIX,
 
 
        release:        unix_release,
        release:        unix_release,
        bind:           unix_bind,
        bind:           unix_bind,
        connect:        unix_stream_connect,
        connect:        unix_stream_connect,
        socketpair:     unix_socketpair,
        socketpair:     unix_socketpair,
        accept:         unix_accept,
        accept:         unix_accept,
        getname:        unix_getname,
        getname:        unix_getname,
        poll:           unix_poll,
        poll:           unix_poll,
        ioctl:          unix_ioctl,
        ioctl:          unix_ioctl,
        listen:         unix_listen,
        listen:         unix_listen,
        shutdown:       unix_shutdown,
        shutdown:       unix_shutdown,
        setsockopt:     sock_no_setsockopt,
        setsockopt:     sock_no_setsockopt,
        getsockopt:     sock_no_getsockopt,
        getsockopt:     sock_no_getsockopt,
        sendmsg:        unix_stream_sendmsg,
        sendmsg:        unix_stream_sendmsg,
        recvmsg:        unix_stream_recvmsg,
        recvmsg:        unix_stream_recvmsg,
        mmap:           sock_no_mmap,
        mmap:           sock_no_mmap,
        sendpage:       sock_no_sendpage,
        sendpage:       sock_no_sendpage,
};
};
 
 
struct proto_ops unix_dgram_ops = {
struct proto_ops unix_dgram_ops = {
        family:         PF_UNIX,
        family:         PF_UNIX,
 
 
        release:        unix_release,
        release:        unix_release,
        bind:           unix_bind,
        bind:           unix_bind,
        connect:        unix_dgram_connect,
        connect:        unix_dgram_connect,
        socketpair:     unix_socketpair,
        socketpair:     unix_socketpair,
        accept:         sock_no_accept,
        accept:         sock_no_accept,
        getname:        unix_getname,
        getname:        unix_getname,
        poll:           datagram_poll,
        poll:           datagram_poll,
        ioctl:          unix_ioctl,
        ioctl:          unix_ioctl,
        listen:         sock_no_listen,
        listen:         sock_no_listen,
        shutdown:       unix_shutdown,
        shutdown:       unix_shutdown,
        setsockopt:     sock_no_setsockopt,
        setsockopt:     sock_no_setsockopt,
        getsockopt:     sock_no_getsockopt,
        getsockopt:     sock_no_getsockopt,
        sendmsg:        unix_dgram_sendmsg,
        sendmsg:        unix_dgram_sendmsg,
        recvmsg:        unix_dgram_recvmsg,
        recvmsg:        unix_dgram_recvmsg,
        mmap:           sock_no_mmap,
        mmap:           sock_no_mmap,
        sendpage:       sock_no_sendpage,
        sendpage:       sock_no_sendpage,
};
};
 
 
struct net_proto_family unix_family_ops = {
struct net_proto_family unix_family_ops = {
        family:         PF_UNIX,
        family:         PF_UNIX,
        create:         unix_create
        create:         unix_create
};
};
 
 
#ifdef CONFIG_SYSCTL
#ifdef CONFIG_SYSCTL
extern void unix_sysctl_register(void);
extern void unix_sysctl_register(void);
extern void unix_sysctl_unregister(void);
extern void unix_sysctl_unregister(void);
#else
#else
static inline void unix_sysctl_register(void) {}
static inline void unix_sysctl_register(void) {}
static inline void unix_sysctl_unregister(void) {}
static inline void unix_sysctl_unregister(void) {}
#endif
#endif
 
 
static char banner[] __initdata = KERN_INFO "NET4: Unix domain sockets 1.0/SMP for Linux NET4.0.\n";
static char banner[] __initdata = KERN_INFO "NET4: Unix domain sockets 1.0/SMP for Linux NET4.0.\n";
 
 
static int __init af_unix_init(void)
static int __init af_unix_init(void)
{
{
        struct sk_buff *dummy_skb;
        struct sk_buff *dummy_skb;
 
 
        printk(banner);
        printk(banner);
        if (sizeof(struct unix_skb_parms) > sizeof(dummy_skb->cb))
        if (sizeof(struct unix_skb_parms) > sizeof(dummy_skb->cb))
        {
        {
                printk(KERN_CRIT "unix_proto_init: panic\n");
                printk(KERN_CRIT "unix_proto_init: panic\n");
                return -1;
                return -1;
        }
        }
        sock_register(&unix_family_ops);
        sock_register(&unix_family_ops);
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_PROC_FS
        create_proc_read_entry("net/unix", 0, 0, unix_read_proc, NULL);
        create_proc_read_entry("net/unix", 0, 0, unix_read_proc, NULL);
#endif
#endif
        unix_sysctl_register();
        unix_sysctl_register();
        return 0;
        return 0;
}
}
 
 
static void __exit af_unix_exit(void)
static void __exit af_unix_exit(void)
{
{
        sock_unregister(PF_UNIX);
        sock_unregister(PF_UNIX);
        unix_sysctl_unregister();
        unix_sysctl_unregister();
        remove_proc_entry("net/unix", 0);
        remove_proc_entry("net/unix", 0);
}
}
 
 
module_init(af_unix_init);
module_init(af_unix_init);
module_exit(af_unix_exit);
module_exit(af_unix_exit);
 
 
MODULE_LICENSE("GPL");
MODULE_LICENSE("GPL");
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.