📄 sock.c
字号:
/* * sock.c * * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke * Copyright (C) 1997 by Volker Lendecke * * Please add a note about your changes to smbfs in the ChangeLog file. */#include <linux/fs.h>#include <linux/time.h>#include <linux/errno.h>#include <linux/socket.h>#include <linux/fcntl.h>#include <linux/file.h>#include <linux/in.h>#include <linux/net.h>#include <linux/mm.h>#include <linux/netdevice.h>#include <linux/workqueue.h>#include <net/scm.h>#include <net/tcp_states.h>#include <net/ip.h>#include <linux/smb_fs.h>#include <linux/smb.h>#include <linux/smbno.h>#include <asm/uaccess.h>#include <asm/ioctls.h>#include "smb_debug.h"#include "proto.h"#include "request.h"static int_recvfrom(struct socket *socket, unsigned char *ubuf, int size, unsigned flags){ struct kvec iov = {ubuf, size}; struct msghdr msg = {.msg_flags = flags}; msg.msg_flags |= MSG_DONTWAIT | MSG_NOSIGNAL; return kernel_recvmsg(socket, &msg, &iov, 1, size, msg.msg_flags);}/* * Return the server this socket belongs to */static struct smb_sb_info *server_from_socket(struct socket *socket){ return socket->sk->sk_user_data;}/* * Called when there is data on the socket. */voidsmb_data_ready(struct sock *sk, int len){ struct smb_sb_info *server = server_from_socket(sk->sk_socket); void (*data_ready)(struct sock *, int) = server->data_ready; data_ready(sk, len); VERBOSE("(%p, %d)\n", sk, len); smbiod_wake_up();}intsmb_valid_socket(struct inode * inode){ return (inode && S_ISSOCK(inode->i_mode) && SOCKET_I(inode)->type == SOCK_STREAM);}static struct socket *server_sock(struct smb_sb_info *server){ struct file *file; if (server && (file = server->sock_file)) {#ifdef SMBFS_PARANOIA if (!smb_valid_socket(file->f_path.dentry->d_inode)) PARANOIA("bad socket!\n");#endif return SOCKET_I(file->f_path.dentry->d_inode); } return NULL;}voidsmb_close_socket(struct smb_sb_info *server){ struct file * file = server->sock_file; if (file) { struct socket *sock = server_sock(server); VERBOSE("closing socket %p\n", sock); sock->sk->sk_data_ready = server->data_ready; server->sock_file = NULL; fput(file); }}static intsmb_get_length(struct socket *socket, unsigned char *header){ int result; result = _recvfrom(socket, header, 4, MSG_PEEK); if (result == -EAGAIN) return -ENODATA; if (result < 0) { PARANOIA("recv error = %d\n", -result); return result; } if (result < 4) return -ENODATA; switch (header[0]) { case 0x00: case 0x82: break; case 0x85: DEBUG1("Got SESSION KEEP ALIVE\n"); _recvfrom(socket, header, 4, 0); /* read away */ return -ENODATA; default: PARANOIA("Invalid NBT packet, code=%x\n", header[0]); return -EIO; } /* The length in the RFC NB header is the raw data length */ return smb_len(header);}intsmb_recv_available(struct smb_sb_info *server){ mm_segment_t oldfs; int avail, err; struct socket *sock = server_sock(server); oldfs = get_fs(); set_fs(get_ds()); err = sock->ops->ioctl(sock, SIOCINQ, (unsigned long) &avail); set_fs(oldfs); return (err >= 0) ? avail : err;}/* * Adjust the kvec to move on 'n' bytes (from nfs/sunrpc) */static intsmb_move_iov(struct kvec **data, size_t *num, struct kvec *vec, unsigned amount){ struct kvec *iv = *data; int i; int len; /* * Eat any sent kvecs */ while (iv->iov_len <= amount) { amount -= iv->iov_len; iv++; (*num)--; } /* * And chew down the partial one */ vec[0].iov_len = iv->iov_len-amount; vec[0].iov_base =((unsigned char *)iv->iov_base)+amount; iv++; len = vec[0].iov_len; /* * And copy any others */ for (i = 1; i < *num; i++) { vec[i] = *iv++; len += vec[i].iov_len; } *data = vec; return len;}/* * smb_receive_header * Only called by the smbiod thread. */intsmb_receive_header(struct smb_sb_info *server){ struct socket *sock; int result = 0; unsigned char peek_buf[4]; result = -EIO; sock = server_sock(server); if (!sock) goto out; if (sock->sk->sk_state != TCP_ESTABLISHED) goto out; if (!server->smb_read) { result = smb_get_length(sock, peek_buf); if (result < 0) { if (result == -ENODATA) result = 0; goto out; } server->smb_len = result + 4; if (server->smb_len < SMB_HEADER_LEN) { PARANOIA("short packet: %d\n", result); server->rstate = SMB_RECV_DROP; result = -EIO; goto out; } if (server->smb_len > SMB_MAX_PACKET_SIZE) { PARANOIA("long packet: %d\n", result); server->rstate = SMB_RECV_DROP; result = -EIO; goto out; } } result = _recvfrom(sock, server->header + server->smb_read, SMB_HEADER_LEN - server->smb_read, 0); VERBOSE("_recvfrom: %d\n", result); if (result < 0) { VERBOSE("receive error: %d\n", result); goto out; } server->smb_read += result; if (server->smb_read == SMB_HEADER_LEN) server->rstate = SMB_RECV_HCOMPLETE;out: return result;}static char drop_buffer[PAGE_SIZE];/* * smb_receive_drop - read and throw away the data * Only called by the smbiod thread. * * FIXME: we are in the kernel, could we just tell the socket that we want * to drop stuff from the buffer? */intsmb_receive_drop(struct smb_sb_info *server){ struct socket *sock; unsigned int flags; struct kvec iov; struct msghdr msg; int rlen = smb_len(server->header) - server->smb_read + 4; int result = -EIO; if (rlen > PAGE_SIZE) rlen = PAGE_SIZE; sock = server_sock(server); if (!sock) goto out; if (sock->sk->sk_state != TCP_ESTABLISHED) goto out; flags = MSG_DONTWAIT | MSG_NOSIGNAL; iov.iov_base = drop_buffer; iov.iov_len = PAGE_SIZE; msg.msg_flags = flags; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_control = NULL; result = kernel_recvmsg(sock, &msg, &iov, 1, rlen, flags); VERBOSE("read: %d\n", result); if (result < 0) { VERBOSE("receive error: %d\n", result); goto out; } server->smb_read += result; if (server->smb_read >= server->smb_len) server->rstate = SMB_RECV_END;out: return result;}/* * smb_receive * Only called by the smbiod thread. */intsmb_receive(struct smb_sb_info *server, struct smb_request *req){ struct socket *sock; unsigned int flags; struct kvec iov[4]; struct kvec *p = req->rq_iov; size_t num = req->rq_iovlen; struct msghdr msg; int rlen; int result = -EIO; sock = server_sock(server); if (!sock) goto out; if (sock->sk->sk_state != TCP_ESTABLISHED) goto out; flags = MSG_DONTWAIT | MSG_NOSIGNAL; msg.msg_flags = flags; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_control = NULL; /* Dont repeat bytes and count available bufferspace */ rlen = smb_move_iov(&p, &num, iov, req->rq_bytes_recvd); if (req->rq_rlen < rlen) rlen = req->rq_rlen; result = kernel_recvmsg(sock, &msg, p, num, rlen, flags); VERBOSE("read: %d\n", result); if (result < 0) { VERBOSE("receive error: %d\n", result); goto out; } req->rq_bytes_recvd += result; server->smb_read += result;out: return result;}/* * Try to send a SMB request. This may return after sending only parts of the * request. SMB_REQ_TRANSMITTED will be set if a request was fully sent. * * Parts of this was taken from xprt_sendmsg from net/sunrpc/xprt.c */intsmb_send_request(struct smb_request *req){ struct smb_sb_info *server = req->rq_server; struct socket *sock; struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT}; int slen = req->rq_slen - req->rq_bytes_sent; int result = -EIO; struct kvec iov[4]; struct kvec *p = req->rq_iov; size_t num = req->rq_iovlen; sock = server_sock(server); if (!sock) goto out; if (sock->sk->sk_state != TCP_ESTABLISHED) goto out; /* Dont repeat bytes */ if (req->rq_bytes_sent) smb_move_iov(&p, &num, iov, req->rq_bytes_sent); result = kernel_sendmsg(sock, &msg, p, num, slen); if (result >= 0) { req->rq_bytes_sent += result; if (req->rq_bytes_sent >= req->rq_slen) req->rq_flags |= SMB_REQ_TRANSMITTED; }out: return result;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -