📄 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 intunix_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 intunix_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 intunix_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 intunix_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 intunix_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 intunix_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 intunix_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 intunix_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 voidunix_close(struct inode * inode, struct file * file){ dprintf(1, "UNIX: close\n");}static intunix_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 */};voidunix_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 + -