📄 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/sched.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/smp_lock.h>#include <net/scm.h>#include <net/ip.h>#include <linux/smb_fs.h>#include <linux/smb.h>#include <linux/smbno.h>#include <asm/uaccess.h>#include "smb_debug.h"#include "proto.h"static int_recvfrom(struct socket *socket, unsigned char *ubuf, int size, unsigned flags){ struct iovec iov; struct msghdr msg; struct scm_cookie scm; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; iov.iov_base = ubuf; iov.iov_len = size; memset(&scm, 0,sizeof(scm)); size=socket->ops->recvmsg(socket, &msg, size, flags, &scm); if(size>=0) scm_recv(socket,&msg,&scm,flags); return size;}static int_send(struct socket *socket, const void *buff, int len){ struct iovec iov; struct msghdr msg; struct scm_cookie scm; int err; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; msg.msg_controllen = 0; iov.iov_base = (void *)buff; iov.iov_len = len; msg.msg_flags = 0; err = scm_send(socket, &msg, &scm); if (err >= 0) { err = socket->ops->sendmsg(socket, &msg, len, &scm); scm_destroy(&scm); } return err;}struct data_callback { struct tq_struct cb; struct sock *sk;};/* * N.B. What happens if we're in here when the socket closes?? */static voidfound_data(struct sock *sk){ /* * FIXME: copied from sock_def_readable, it should be a call to * server->data_ready() -- manfreds@colorfullife.com */ read_lock(&sk->callback_lock); if(!sk->dead) { wake_up_interruptible(sk->sleep); sock_wake_async(sk->socket,1,POLL_IN); } read_unlock(&sk->callback_lock);}static voidsmb_data_callback(void* ptr){ struct data_callback* job=ptr; struct socket *socket = job->sk->socket; unsigned char peek_buf[4]; int result = 0; mm_segment_t fs; int count = 100; /* this is a lot, we should have some data waiting */ int found = 0; fs = get_fs(); set_fs(get_ds()); lock_kernel(); while (count-- > 0) { peek_buf[0] = 0; result = -EIO; if (job->sk->dead) { PARANOIA("sock dead!\n"); break; } result = _recvfrom(socket, (void *) peek_buf, 1, MSG_PEEK | MSG_DONTWAIT); if (result < 0) break; if (peek_buf[0] != 0x85) break; /* got SESSION KEEP ALIVE */ result = _recvfrom(socket, (void *) peek_buf, 4, MSG_DONTWAIT); DEBUG1("got SESSION KEEPALIVE\n"); if (result < 0) break; found = 1; } unlock_kernel(); set_fs(fs); DEBUG1("found=%d, count=%d, result=%d\n", found, count, result); if (found) found_data(job->sk); smb_kfree(ptr);}static voidsmb_data_ready(struct sock *sk, int len){ struct data_callback* job; job = smb_kmalloc(sizeof(struct data_callback),GFP_ATOMIC); if(job == 0) { printk("smb_data_ready: lost SESSION KEEPALIVE due to OOM.\n"); found_data(sk); return; } INIT_LIST_HEAD(&job->cb.list); job->cb.sync = 0; job->cb.routine = smb_data_callback; job->cb.data = job; job->sk = sk; schedule_task(&job->cb);}intsmb_valid_socket(struct inode * inode){ return (inode && S_ISSOCK(inode->i_mode) && inode->u.socket_i.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_dentry->d_inode)) PARANOIA("bad socket!\n");#endif return &file->f_dentry->d_inode->u.socket_i; } return NULL;}intsmb_catch_keepalive(struct smb_sb_info *server){ struct socket *socket; struct sock *sk; void *data_ready; int error; error = -EINVAL; socket = server_sock(server); if (!socket) { printk(KERN_DEBUG "smb_catch_keepalive: did not get valid server!\n"); server->data_ready = NULL; goto out; } sk = socket->sk; if (sk == NULL) { DEBUG1("sk == NULL"); server->data_ready = NULL; goto out; } DEBUG1("sk->d_r = %x, server->d_r = %x\n", (unsigned int) (sk->data_ready), (unsigned int) (server->data_ready)); /* * Install the callback atomically to avoid races ... */ data_ready = xchg(&sk->data_ready, smb_data_ready); if (data_ready != smb_data_ready) { server->data_ready = data_ready; error = 0; } else printk(KERN_ERR "smb_catch_keepalive: already done\n");out: return error;}intsmb_dont_catch_keepalive(struct smb_sb_info *server){ struct socket *socket; struct sock *sk; void * data_ready; int error; error = -EINVAL; socket = server_sock(server); if (!socket) { printk(KERN_DEBUG "smb_dont_catch_keepalive: did not get valid server!\n"); goto out; } sk = socket->sk; if (sk == NULL) { DEBUG1("sk == NULL"); goto out; } /* Is this really an error?? */ if (server->data_ready == NULL) { printk(KERN_DEBUG "smb_dont_catch_keepalive: " "server->data_ready == NULL\n"); goto out; } DEBUG1("smb_dont_catch_keepalive: sk->d_r = %x, server->d_r = %x\n", (unsigned int) (sk->data_ready), (unsigned int) (server->data_ready)); /* * Restore the original callback atomically to avoid races ... */ data_ready = xchg(&sk->data_ready, server->data_ready); server->data_ready = NULL; if (data_ready != smb_data_ready) { printk(KERN_ERR "smb_dont_catch_keepalive: " "sk->data_ready != smb_data_ready\n"); } error = 0;out: return error;}/* * Called with the server locked. */voidsmb_close_socket(struct smb_sb_info *server){ struct file * file = server->sock_file; if (file) { VERBOSE("closing socket %p\n", server_sock(server));#ifdef SMBFS_PARANOIA if (server_sock(server)->sk->data_ready == smb_data_ready) PARANOIA("still catching keepalives!\n");#endif server->sock_file = NULL; fput(file); }}static intsmb_send_raw(struct socket *socket, unsigned char *source, int length){ int result; int already_sent = 0; while (already_sent < length) { result = _send(socket, (void *) (source + already_sent), length - already_sent); if (result == 0) { return -EIO; } if (result < 0) { DEBUG1("smb_send_raw: sendto error = %d\n", -result); return result; } already_sent += result; } return already_sent;}static intsmb_receive_raw(struct socket *socket, unsigned char *target, int length){ int result; int already_read = 0; while (already_read < length) { result = _recvfrom(socket, (void *) (target + already_read), length - already_read, 0); if (result == 0) { return -EIO; } if (result < 0) { DEBUG1("recvfrom error = %d\n", -result); return result; } already_read += result; } return already_read;}static intsmb_get_length(struct socket *socket, unsigned char *header){ int result; unsigned char peek_buf[4]; mm_segment_t fs; re_recv: fs = get_fs(); set_fs(get_ds()); result = smb_receive_raw(socket, peek_buf, 4); set_fs(fs); if (result < 0) { PARANOIA("recv error = %d\n", -result); return result; } switch (peek_buf[0]) { case 0x00: case 0x82: break; case 0x85: DEBUG1("Got SESSION KEEP ALIVE\n"); goto re_recv; default: PARANOIA("Invalid NBT packet, code=%x\n", peek_buf[0]); return -EIO; } if (header != NULL) { memcpy(header, peek_buf, 4); } /* The length in the RFC NB header is the raw data length */ return smb_len(peek_buf);}/* * Since we allocate memory in increments of PAGE_SIZE, * round up the packet length to the next multiple. */intsmb_round_length(int len){ return (len + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);} /* * smb_receive * fs points to the correct segment */static intsmb_receive(struct smb_sb_info *server){ struct socket *socket = server_sock(server); unsigned char * packet = server->packet; int len, result; unsigned char peek_buf[4]; result = smb_get_length(socket, peek_buf); if (result < 0) goto out; len = result; /* * Some servers do not respect our max_xmit and send * larger packets. Try to allocate a new packet, * but don't free the old one unless we succeed. */ if (len + 4 > server->packet_size) { int new_len = smb_round_length(len + 4); result = -ENOMEM; packet = smb_vmalloc(new_len); if (packet == NULL) goto out; smb_vfree(server->packet); server->packet = packet; server->packet_size = new_len; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -