📄 sock.c
字号:
memcpy(fname, sockun.sun_path, sockaddr_len-UN_PATH_OFFSET);
fname[sockaddr_len-UN_PATH_OFFSET] = '\0';
old_fs = get_fs();
set_fs(get_ds());
i = open_namei(fname, 0, S_IFSOCK, &inode, NULL);
set_fs(old_fs);
if (i < 0) {
dprintf(1, "UNIX: connect: can't open socket %s\n", fname);
return(i);
}
serv_upd = unix_data_lookup(&sockun, sockaddr_len, inode);
iput(inode);
if (!serv_upd) {
dprintf(1, "UNIX: connect: can't locate peer %s at inode 0x%x\n",
fname, inode);
return(-EINVAL);
}
if ((i = sock_awaitconn(sock, serv_upd->socket)) < 0) {
dprintf(1, "UNIX: connect: can't await connection\n");
return(i);
}
if (sock->conn) {
unix_data_ref(UN_DATA(sock->conn));
UN_DATA(sock)->peerupd = UN_DATA(sock->conn); /* ref server */
}
return(0);
}
/*
* To do a socketpair, we just connect the two datas, easy!
* Since we always wait on the socket inode, they're no contention
* for a wait area, and deadlock prevention in the case of a process
* writing to itself is, ignored, in true unix fashion!
*/
static int
unix_proto_socketpair(struct socket *sock1, struct socket *sock2)
{
struct unix_proto_data *upd1 = UN_DATA(sock1), *upd2 = UN_DATA(sock2);
unix_data_ref(upd1);
unix_data_ref(upd2);
upd1->peerupd = upd2;
upd2->peerupd = upd1;
return(0);
}
/* On accept, we ref the peer's data for safe writes. */
static int
unix_proto_accept(struct socket *sock, struct socket *newsock, int flags)
{
struct socket *clientsock;
dprintf(1, "UNIX: accept: socket 0x%x accepted via socket 0x%x\n",
sock, newsock);
/*
* If there aren't any sockets awaiting connection,
* then wait for one, unless nonblocking.
*/
while(!(clientsock = sock->iconn)) {
if (flags & O_NONBLOCK) return(-EAGAIN);
interruptible_sleep_on(sock->wait);
if (current->signal & ~current->blocked) {
dprintf(1, "UNIX: accept: sleep was interrupted\n");
return(-ERESTARTSYS);
}
}
/*
* Great. Finish the connection relative to server and client,
* wake up the client and return the new fd to the server.
*/
sock->iconn = clientsock->next;
clientsock->next = NULL;
newsock->conn = clientsock;
clientsock->conn = newsock;
clientsock->state = SS_CONNECTED;
newsock->state = SS_CONNECTED;
unix_data_ref(UN_DATA(clientsock));
UN_DATA(newsock)->peerupd = UN_DATA(clientsock);
UN_DATA(newsock)->sockaddr_un = UN_DATA(sock)->sockaddr_un;
UN_DATA(newsock)->sockaddr_len = UN_DATA(sock)->sockaddr_len;
wake_up_interruptible(clientsock->wait);
return(0);
}
/* Gets the current name or the name of the connected socket. */
static int
unix_proto_getname(struct socket *sock, struct sockaddr *usockaddr,
int *usockaddr_len, int peer)
{
struct unix_proto_data *upd;
int len;
int er;
dprintf(1, "UNIX: getname: socket 0x%x for %s\n", sock, peer?"peer":"self");
if (peer) {
if (sock->state != SS_CONNECTED) {
dprintf(1, "UNIX: getname: socket not connected\n");
return(-EINVAL);
}
upd = UN_DATA(sock->conn);
} else
upd = UN_DATA(sock);
er=verify_area(VERIFY_WRITE, usockaddr_len, sizeof(*usockaddr_len));
if(er)
return er;
if ((len = get_fs_long(usockaddr_len)) <= 0) return(-EINVAL);
if (len > upd->sockaddr_len) len = upd->sockaddr_len;
if (len) {
er=verify_area(VERIFY_WRITE, usockaddr, len);
if(er)
return er;
memcpy_tofs(usockaddr, &upd->sockaddr_un, len);
}
put_fs_long(len, usockaddr_len);
return(0);
}
/* We read from our own buf. */
static int
unix_proto_read(struct socket *sock, char *ubuf, int size, int nonblock)
{
struct unix_proto_data *upd;
int todo, avail;
int er;
if ((todo = size) <= 0) return(0);
upd = UN_DATA(sock);
while(!(avail = UN_BUF_AVAIL(upd))) {
if (sock->state != SS_CONNECTED) {
dprintf(1, "UNIX: read: socket not connected\n");
return((sock->state == SS_DISCONNECTING) ? 0 : -EINVAL);
}
dprintf(1, "UNIX: read: no data available...\n");
if (nonblock) return(-EAGAIN);
interruptible_sleep_on(sock->wait);
if (current->signal & ~current->blocked) {
dprintf(1, "UNIX: read: interrupted\n");
return(-ERESTARTSYS);
}
}
/*
* Copy from the read buffer into the user's buffer,
* watching for wraparound. Then we wake up the writer.
*/
unix_lock(upd);
do {
int part, cando;
if (avail <= 0) {
printk("UNIX: read: AVAIL IS NEGATIVE!!!\n");
send_sig(SIGKILL, current, 1);
return(-EPIPE);
}
if ((cando = todo) > avail) cando = avail;
if (cando >(part = BUF_SIZE - upd->bp_tail)) cando = part;
dprintf(1, "UNIX: read: avail=%d, todo=%d, cando=%d\n",
avail, todo, cando);
if((er=verify_area(VERIFY_WRITE,ubuf,cando))<0)
{
unix_unlock(upd);
return er;
}
memcpy_tofs(ubuf, upd->buf + upd->bp_tail, cando);
upd->bp_tail =(upd->bp_tail + cando) &(BUF_SIZE-1);
ubuf += cando;
todo -= cando;
if (sock->state == SS_CONNECTED)
wake_up_interruptible(sock->conn->wait);
avail = UN_BUF_AVAIL(upd);
} while(todo && avail);
unix_unlock(upd);
return(size - todo);
}
/*
* We write to our peer's buf. When we connected we ref'd this
* peer so we are safe that the buffer remains, even after the
* peer has disconnected, which we check other ways.
*/
static int
unix_proto_write(struct socket *sock, char *ubuf, int size, int nonblock)
{
struct unix_proto_data *pupd;
int todo, space;
int er;
if ((todo = size) <= 0) return(0);
if (sock->state != SS_CONNECTED) {
dprintf(1, "UNIX: write: socket not connected\n");
if (sock->state == SS_DISCONNECTING) {
send_sig(SIGPIPE, current, 1);
return(-EPIPE);
}
return(-EINVAL);
}
pupd = UN_DATA(sock)->peerupd; /* safer than sock->conn */
while(!(space = UN_BUF_SPACE(pupd))) {
dprintf(1, "UNIX: write: no space left...\n");
if (nonblock) return(-EAGAIN);
interruptible_sleep_on(sock->wait);
if (current->signal & ~current->blocked) {
dprintf(1, "UNIX: write: interrupted\n");
return(-ERESTARTSYS);
}
if (sock->state == SS_DISCONNECTING) {
dprintf(1, "UNIX: write: disconnected(SIGPIPE)\n");
send_sig(SIGPIPE, current, 1);
return(-EPIPE);
}
}
/*
* Copy from the user's buffer to the write buffer,
* watching for wraparound. Then we wake up the reader.
*/
unix_lock(pupd);
do {
int part, cando;
if (space <= 0) {
printk("UNIX: write: SPACE IS NEGATIVE!!!\n");
send_sig(SIGKILL, current, 1);
return(-EPIPE);
}
/*
* We may become disconnected inside this loop, so watch
* for it (peerupd is safe until we close).
*/
if (sock->state == SS_DISCONNECTING) {
send_sig(SIGPIPE, current, 1);
unix_unlock(pupd);
return(-EPIPE);
}
if ((cando = todo) > space) cando = space;
if (cando >(part = BUF_SIZE - pupd->bp_head)) cando = part;
dprintf(1, "UNIX: write: space=%d, todo=%d, cando=%d\n",
space, todo, cando);
er=verify_area(VERIFY_READ, ubuf, cando);
if(er)
{
unix_unlock(pupd);
return er;
}
memcpy_fromfs(pupd->buf + pupd->bp_head, ubuf, cando);
pupd->bp_head =(pupd->bp_head + cando) &(BUF_SIZE-1);
ubuf += cando;
todo -= cando;
if (sock->state == SS_CONNECTED)
wake_up_interruptible(sock->conn->wait);
space = UN_BUF_SPACE(pupd);
} while(todo && space);
unix_unlock(pupd);
return(size - todo);
}
static int
unix_proto_select(struct socket *sock, int sel_type, select_table * wait)
{
struct unix_proto_data *upd, *peerupd;
/* Handle server sockets specially. */
if (sock->flags & SO_ACCEPTCON) {
if (sel_type == SEL_IN) {
dprintf(1, "UNIX: select: %sconnections pending\n",
sock->iconn ? "" : "no ");
if (sock->iconn) return(1);
select_wait(sock->wait, wait);
return(sock->iconn ? 1 : 0);
}
dprintf(1, "UNIX: select: nothing else for server socket\n");
select_wait(sock->wait, wait);
return(0);
}
if (sel_type == SEL_IN) {
upd = UN_DATA(sock);
dprintf(1, "UNIX: select: there is%s data available\n",
UN_BUF_AVAIL(upd) ? "" : " no");
if (UN_BUF_AVAIL(upd)) /* even if disconnected */
return(1);
else if (sock->state != SS_CONNECTED) {
dprintf(1, "UNIX: select: socket not connected(read EOF)\n");
return(1);
}
select_wait(sock->wait,wait);
return(0);
}
if (sel_type == SEL_OUT) {
if (sock->state != SS_CONNECTED) {
dprintf(1, "UNIX: select: socket not connected(write EOF)\n");
return(1);
}
peerupd = UN_DATA(sock->conn);
dprintf(1, "UNIX: select: there is%s space available\n",
UN_BUF_SPACE(peerupd) ? "" : " no");
if (UN_BUF_SPACE(peerupd) > 0) return(1);
select_wait(sock->wait,wait);
return(0);
}
/* SEL_EX */
dprintf(1, "UNIX: select: there are no exceptions here?!\n");
return(0);
}
static int
unix_proto_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct unix_proto_data *upd, *peerupd;
int er;
upd = UN_DATA(sock);
peerupd = (sock->state == SS_CONNECTED) ? UN_DATA(sock->conn) : NULL;
switch(cmd) {
case TIOCINQ:
if (sock->flags & SO_ACCEPTCON) return(-EINVAL);
er=verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long));
if(er)
return er;
if (UN_BUF_AVAIL(upd) || peerupd)
put_fs_long(UN_BUF_AVAIL(upd),(unsigned long *)arg);
else
put_fs_long(0,(unsigned long *)arg);
break;
case TIOCOUTQ:
if (sock->flags & SO_ACCEPTCON) return(-EINVAL);
er=verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long));
if(er)
return er;
if (peerupd) put_fs_long(UN_BUF_SPACE(peerupd),
(unsigned long *)arg);
else
put_fs_long(0,(unsigned long *)arg);
break;
default:
return(-EINVAL);
}
return(0);
}
static int
unix_open(struct inode * inode, struct file * file)
{
int minor;
dprintf(1, "UNIX: open\n");
minor = MINOR(inode->i_rdev);
if (minor != 0) return(-ENODEV);
return(0);
}
static void
unix_close(struct inode * inode, struct file * file)
{
dprintf(1, "UNIX: close\n");
}
static int
unix_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int minor, ret;
int er;
dprintf(1, "UNIX: ioctl(0x%X, 0x%X)\n", cmd, arg);
minor = MINOR(inode->i_rdev);
if (minor != 0) return(-ENODEV);
ret = -EINVAL;
switch(cmd) {
case DDIOCSDBG:
er=verify_area(VERIFY_READ,(void *)arg, sizeof(int));
if(er)
return er;
unix_debug = get_fs_long((int *)arg);
if (unix_debug != 0 && unix_debug != 1) {
unix_debug = 0;
return(-EINVAL);
}
return(0);
case SIOCSIFLINK:
printk("UNIX: cannot link streams!\n");
break;
default:
break;
}
return(ret);
}
static struct file_operations unix_fops = {
NULL, /* LSEEK */
NULL, /* READ */
NULL, /* WRITE */
NULL, /* READDIR */
NULL, /* SELECT */
unix_ioctl, /* IOCTL */
NULL, /* MMAP */
unix_open, /* OPEN */
unix_close /* CLOSE */
};
static struct proto_ops unix_proto_ops = {
AF_UNIX,
unix_proto_create,
unix_proto_dup,
unix_proto_release,
unix_proto_bind,
unix_proto_connect,
unix_proto_socketpair,
unix_proto_accept,
unix_proto_getname,
unix_proto_read,
unix_proto_write,
unix_proto_select,
unix_proto_ioctl,
unix_proto_listen,
unix_proto_send,
unix_proto_recv,
unix_proto_sendto,
unix_proto_recvfrom,
unix_proto_shutdown,
unix_proto_setsockopt,
unix_proto_getsockopt,
NULL /* unix_proto_fcntl */
};
void
unix_proto_init(struct ddi_proto *pro)
{
struct unix_proto_data *upd;
dprintf(1, "%s: init: initializing...\n", pro->name);
if (register_chrdev(AF_UNIX_MAJOR, "af_unix", &unix_fops) < 0) {
printk("%s: cannot register major device %d!\n",
pro->name, AF_UNIX_MAJOR);
return;
}
/* Tell SOCKET that we are alive... */
(void) sock_register(unix_proto_ops.family, &unix_proto_ops);
for(upd = unix_datas; upd <= last_unix_data; ++upd) {
upd->refcnt = 0;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -