?? sock.c
字號:
/* * UNIX An implementation of the AF_UNIX network domain for the * LINUX operating system. UNIX is implemented using the * BSD Socket interface as the means of communication with * the user level. * * Version: @(#)sock.c 1.0.5 05/25/93 * * Authors: Orest Zborowski, <obz@Kodak.COM> * Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> * * Fixes: * Alan Cox : Verify Area * NET2E Team : Page fault locks * Dmitry Gorodchanin : /proc locking * * To Do: * Some nice person is looking into Unix sockets done properly. NET3 * will replace all of this and include datagram sockets and socket * options - so please stop asking me for them 8-) * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or(at your option) any later version. */#include <linux/config.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/stat.h>#include <linux/socket.h>#include <linux/un.h>#include <linux/fcntl.h>#include <linux/termios.h>#include <linux/sockios.h>#include <linux/net.h>#include <linux/fs.h>#include <linux/ddi.h>#include <linux/malloc.h>#include <asm/system.h>#include <asm/segment.h>#include <stdarg.h>#include "unix.h"struct unix_proto_data unix_datas[NSOCKETS];static int unix_debug = 0;static int unix_proto_create(struct socket *sock, int protocol);static int unix_proto_dup(struct socket *newsock, struct socket *oldsock);static int unix_proto_release(struct socket *sock, struct socket *peer);static int unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr, int sockaddr_len);static int unix_proto_connect(struct socket *sock, struct sockaddr *uservaddr, int sockaddr_len, int flags);static int unix_proto_socketpair(struct socket *sock1, struct socket *sock2);static int unix_proto_accept(struct socket *sock, struct socket *newsock, int flags);static int unix_proto_getname(struct socket *sock, struct sockaddr *usockaddr, int *usockaddr_len, int peer);static int unix_proto_read(struct socket *sock, char *ubuf, int size, int nonblock);static int unix_proto_write(struct socket *sock, char *ubuf, int size, int nonblock);static int unix_proto_select(struct socket *sock, int sel_type, select_table * wait);static int unix_proto_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);static int unix_proto_listen(struct socket *sock, int backlog);static int unix_proto_send(struct socket *sock, void *buff, int len, int nonblock, unsigned flags);static int unix_proto_recv(struct socket *sock, void *buff, int len, int nonblock, unsigned flags);static int unix_proto_sendto(struct socket *sock, void *buff, int len, int nonblock, unsigned flags, struct sockaddr *addr, int addr_len);static int unix_proto_recvfrom(struct socket *sock, void *buff, int len, int nonblock, unsigned flags, struct sockaddr *addr, int *addr_len);static int unix_proto_shutdown(struct socket *sock, int how);static int unix_proto_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen);static int unix_proto_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen);static voiddprintf(int level, char *fmt, ...){ va_list args; char *buff; extern int vsprintf(char * buf, const char * fmt, va_list args); if (level != unix_debug) return; buff = (char *) kmalloc(256, GFP_KERNEL); if (buff != NULL) { va_start(args, fmt); vsprintf(buff, fmt, args); va_end(args); printk(buff); kfree(buff); }}static inline intmin(int a, int b){ if (a < b) return(a); return(b);}voidsockaddr_un_printk(struct sockaddr_un *sockun, int sockaddr_len){ char buf[sizeof(sockun->sun_path) + 1]; if (unix_debug == 0) return; sockaddr_len -= UN_PATH_OFFSET; if (sockun->sun_family != AF_UNIX) printk("UNIX: Badd addr family %d>\n", sockun->sun_family); else if (sockaddr_len <= 0 || sockaddr_len >= sizeof(buf)) printk("UNIX: Bad addr len %d>\n", sockaddr_len); else { memcpy(buf, sockun->sun_path, sockaddr_len); buf[sockaddr_len] = '\0'; printk("\"%s\"[%lu]\n", buf, sockaddr_len + UN_PATH_OFFSET); }} /* Support routines doing anti page fault locking * FvK & Matt Dillon (borrowed From NET2E3) *//* * Locking for unix-domain sockets. We don't use the socket structure's * wait queue because it is allowed to 'go away' outside of our control, * whereas unix_proto_data structures stick around. */void unix_lock(struct unix_proto_data *upd){ while (upd->lock_flag) sleep_on(&upd->wait); upd->lock_flag = 1;}void unix_unlock(struct unix_proto_data *upd){ upd->lock_flag = 0; wake_up(&upd->wait);}/* don't have to do anything. */static intunix_proto_listen(struct socket *sock, int backlog){ return(0);}static intunix_proto_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen){ return(-EOPNOTSUPP);}static intunix_proto_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen){ return(-EOPNOTSUPP);}static intunix_proto_sendto(struct socket *sock, void *buff, int len, int nonblock, unsigned flags, struct sockaddr *addr, int addr_len){ return(-EOPNOTSUPP);} static intunix_proto_recvfrom(struct socket *sock, void *buff, int len, int nonblock, unsigned flags, struct sockaddr *addr, int *addr_len){ return(-EOPNOTSUPP);} static intunix_proto_shutdown(struct socket *sock, int how){ return(-EOPNOTSUPP);}/* This error needs to be checked. */static intunix_proto_send(struct socket *sock, void *buff, int len, int nonblock, unsigned flags){ if (flags != 0) return(-EINVAL); return(unix_proto_write(sock, (char *) buff, len, nonblock));}/* This error needs to be checked. */static intunix_proto_recv(struct socket *sock, void *buff, int len, int nonblock, unsigned flags){ if (flags != 0) return(-EINVAL); return(unix_proto_read(sock, (char *) buff, len, nonblock));}static struct unix_proto_data *unix_data_lookup(struct sockaddr_un *sockun, int sockaddr_len, struct inode *inode){ struct unix_proto_data *upd; for(upd = unix_datas; upd <= last_unix_data; ++upd) { if (upd->refcnt > 0 && upd->socket && upd->socket->state == SS_UNCONNECTED && upd->sockaddr_un.sun_family == sockun->sun_family && upd->inode == inode) return(upd); } return(NULL);}static struct unix_proto_data *unix_data_alloc(void){ struct unix_proto_data *upd; cli(); for(upd = unix_datas; upd <= last_unix_data; ++upd) { if (!upd->refcnt) { upd->refcnt = -1; /* unix domain socket not yet initialised - bgm */ sti(); upd->socket = NULL; upd->sockaddr_len = 0; upd->sockaddr_un.sun_family = 0; upd->buf = NULL; upd->bp_head = upd->bp_tail = 0; upd->inode = NULL; upd->peerupd = NULL; return(upd); } } sti(); return(NULL);}static inline voidunix_data_ref(struct unix_proto_data *upd){ if (!upd) { dprintf(1, "UNIX: data_ref: upd = NULL\n"); return; } ++upd->refcnt; dprintf(1, "UNIX: data_ref: refing data 0x%x(%d)\n", upd, upd->refcnt);}static voidunix_data_deref(struct unix_proto_data *upd){ if (!upd) { dprintf(1, "UNIX: data_deref: upd = NULL\n"); return; } if (upd->refcnt == 1) { dprintf(1, "UNIX: data_deref: releasing data 0x%x\n", upd); if (upd->buf) { free_page((unsigned long)upd->buf); upd->buf = NULL; upd->bp_head = upd->bp_tail = 0; } } --upd->refcnt;}/* * Upon a create, we allocate an empty protocol data, * and grab a page to buffer writes. */static intunix_proto_create(struct socket *sock, int protocol){ struct unix_proto_data *upd; dprintf(1, "UNIX: create: socket 0x%x, proto %d\n", sock, protocol); if (protocol != 0) { dprintf(1, "UNIX: create: protocol != 0\n"); return(-EINVAL); } if (!(upd = unix_data_alloc())) { printk("UNIX: create: can't allocate buffer\n"); return(-ENOMEM); } if (!(upd->buf = (char*) get_free_page(GFP_USER))) { printk("UNIX: create: can't get page!\n"); unix_data_deref(upd); return(-ENOMEM); } upd->protocol = protocol; upd->socket = sock; UN_DATA(sock) = upd; upd->refcnt = 1; /* Now its complete - bgm */ dprintf(1, "UNIX: create: allocated data 0x%x\n", upd); return(0);}static intunix_proto_dup(struct socket *newsock, struct socket *oldsock){ struct unix_proto_data *upd = UN_DATA(oldsock); return(unix_proto_create(newsock, upd->protocol));}static intunix_proto_release(struct socket *sock, struct socket *peer){ struct unix_proto_data *upd = UN_DATA(sock); dprintf(1, "UNIX: release: socket 0x%x, unix_data 0x%x\n", sock, upd); if (!upd) return(0); if (upd->socket != sock) { printk("UNIX: release: socket link mismatch!\n"); return(-EINVAL); } if (upd->inode) { dprintf(1, "UNIX: release: releasing inode 0x%x\n", upd->inode); iput(upd->inode); upd->inode = NULL; } UN_DATA(sock) = NULL; upd->socket = NULL; if (upd->peerupd) unix_data_deref(upd->peerupd); unix_data_deref(upd); return(0);}/* * Bind a name to a socket. * This is where much of the work is done: we allocate a fresh page for * the buffer, grab the appropriate inode and set things up. * * FIXME: what should we do if an address is already bound? * Here we return EINVAL, but it may be necessary to re-bind. * I think thats what BSD does in the case of datagram sockets... */static intunix_proto_bind(struct socket *sock, struct sockaddr *umyaddr, int sockaddr_len){ char fname[sizeof(((struct sockaddr_un *)0)->sun_path) + 1]; struct unix_proto_data *upd = UN_DATA(sock); unsigned long old_fs; int i; int er; dprintf(1, "UNIX: bind: socket 0x%x, len=%d\n", sock, sockaddr_len); if (sockaddr_len <= UN_PATH_OFFSET || sockaddr_len > sizeof(struct sockaddr_un)) { dprintf(1, "UNIX: bind: bad length %d\n", sockaddr_len); return(-EINVAL); } if (upd->sockaddr_len || upd->inode) { printk("UNIX: bind: already bound!\n"); return(-EINVAL); } er=verify_area(VERIFY_WRITE, umyaddr, sockaddr_len); if(er) return er; memcpy_fromfs(&upd->sockaddr_un, umyaddr, sockaddr_len); upd->sockaddr_un.sun_path[sockaddr_len-UN_PATH_OFFSET] = '\0'; if (upd->sockaddr_un.sun_family != AF_UNIX) { dprintf(1, "UNIX: bind: family is %d, not AF_UNIX(%d)\n", upd->sockaddr_un.sun_family, AF_UNIX); return(-EINVAL); } memcpy(fname, upd->sockaddr_un.sun_path, sockaddr_len-UN_PATH_OFFSET); fname[sockaddr_len-UN_PATH_OFFSET] = '\0'; old_fs = get_fs(); set_fs(get_ds()); i = do_mknod(fname, S_IFSOCK | S_IRWXUGO, 0); if (i == 0) i = open_namei(fname, 0, S_IFSOCK, &upd->inode, NULL); set_fs(old_fs); if (i < 0) { printk("UNIX: bind: can't open socket %s\n", fname); return(i); } upd->sockaddr_len = sockaddr_len; /* now its legal */ dprintf(1, "UNIX: bind: bound socket address: "); sockaddr_un_printk(&upd->sockaddr_un, upd->sockaddr_len); dprintf(1, "to inode 0x%x\n", upd->inode); return(0);}/* * Perform a connection. we can only connect to unix sockets * (I can't for the life of me find an application where that * wouldn't be the case!) */static intunix_proto_connect(struct socket *sock, struct sockaddr *uservaddr, int sockaddr_len, int flags){ char fname[sizeof(((struct sockaddr_un *)0)->sun_path) + 1]; struct sockaddr_un sockun; struct unix_proto_data *serv_upd; struct inode *inode; unsigned long old_fs; int i; int er; dprintf(1, "UNIX: connect: socket 0x%x, servlen=%d\n", sock, sockaddr_len); if (sockaddr_len <= UN_PATH_OFFSET || sockaddr_len > sizeof(struct sockaddr_un)) { dprintf(1, "UNIX: connect: bad length %d\n", sockaddr_len); return(-EINVAL); } if (sock->state == SS_CONNECTING) return(-EINPROGRESS); if (sock->state == SS_CONNECTED) return(-EISCONN); er=verify_area(VERIFY_READ, uservaddr, sockaddr_len); if(er) return er; memcpy_fromfs(&sockun, uservaddr, sockaddr_len); sockun.sun_path[sockaddr_len-UN_PATH_OFFSET] = '\0'; if (sockun.sun_family != AF_UNIX) { dprintf(1, "UNIX: connect: family is %d, not AF_UNIX(%d)\n", sockun.sun_family, AF_UNIX); return(-EINVAL); } /* * Try to open the name in the filesystem - this is how we * identify ourselves and our server. Note that we don't * hold onto the inode that long, just enough to find our * server. When we're connected, we mooch off the server. */
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -