patch-1.3.7 linux/fs/smbfs/sock.c
Next file: linux/fs/umsdos/dir.c
Previous file: linux/fs/smbfs/proc.c
Back to the patch index
Back to the overall index
- Lines: 601
- Date:
Sat Jul 1 19:00:19 1995
- Orig file:
v1.3.6/linux/fs/smbfs/sock.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v1.3.6/linux/fs/smbfs/sock.c linux/fs/smbfs/sock.c
@@ -0,0 +1,600 @@
+/*
+ * sock.c
+ *
+ * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/sched.h>
+#include <linux/smb_fs.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <asm/segment.h>
+#include <linux/in.h>
+#include <linux/net.h>
+#include <linux/mm.h>
+#include <linux/netdevice.h>
+#include "/usr/src/linux/include/net/ip.h"
+
+#include <linux/smb.h>
+#include <linux/smbno.h>
+
+
+#define _S(nr) (1<<((nr)-1))
+
+static void
+smb_data_callback(struct sock *sk,int len)
+{
+ struct socket *sock = sk->socket;
+
+ if(!sk->dead)
+ {
+ unsigned char peek_buf[4];
+ int result;
+ unsigned short fs;
+
+ fs = get_fs();
+ set_fs(get_ds());
+
+ result = sock->ops->recvfrom(sock, (void *)peek_buf, 1, 1,
+ MSG_PEEK, NULL, NULL);
+
+ while ((result != -EAGAIN) && (peek_buf[0] == 0x85)) {
+
+ /* got SESSION KEEP ALIVE */
+ result = sock->ops->recvfrom(sock, (void *)peek_buf,
+ 4, 1, 0, NULL, NULL);
+
+ DDPRINTK("smb_data_callback:"
+ " got SESSION KEEP ALIVE\n");
+
+ if (result == -EAGAIN)
+ break;
+
+ result = sock->ops->recvfrom(sock, (void *)peek_buf,
+ 1, 1, MSG_PEEK,
+ NULL, NULL);
+
+ }
+
+ set_fs(fs);
+
+ if (result != -EAGAIN) {
+ wake_up_interruptible(sk->sleep);
+ }
+ }
+}
+
+int
+smb_catch_keepalive(struct smb_server *server)
+{
+ struct file *file;
+ struct inode *inode;
+ struct socket *sock;
+ struct sock *sk;
+
+ if ( (server == NULL)
+ || ((file = server->sock_file) == NULL)
+ || ((inode = file->f_inode) == NULL)
+ || (!S_ISSOCK(inode->i_mode))) {
+
+ printk("smb_catch_keepalive: did not get valid server!\n");
+ server->data_ready = NULL;
+ return -EINVAL;
+ }
+
+ sock = &(inode->u.socket_i);
+
+ if (sock->type != SOCK_STREAM) {
+ printk("smb_catch_keepalive: did not get SOCK_STREAM\n");
+ server->data_ready = NULL;
+ return -EINVAL;
+ }
+
+ sk = (struct sock *)(sock->data);
+
+ if (sk == NULL) {
+ printk("smb_catch_keepalive: sk == NULL");
+ server->data_ready = NULL;
+ return -EINVAL;
+ }
+
+ DDPRINTK("smb_catch_keepalive.: sk->d_r = %x, server->d_r = %x\n",
+ (unsigned int)(sk->data_ready),
+ (unsigned int)(server->data_ready));
+
+ if (sk->data_ready == smb_data_callback) {
+ printk("smb_catch_keepalive: already done\n");
+ return -EINVAL;
+ }
+
+ server->data_ready = sk->data_ready;
+ sk->data_ready = smb_data_callback;
+ return 0;
+}
+
+int
+smb_dont_catch_keepalive(struct smb_server *server)
+{
+ struct file *file;
+ struct inode *inode;
+ struct socket *sock;
+ struct sock *sk;
+
+ if ( (server == NULL)
+ || ((file = server->sock_file) == NULL)
+ || ((inode = file->f_inode) == NULL)
+ || (!S_ISSOCK(inode->i_mode))) {
+
+ printk("smb_dont_catch_keepalive: "
+ "did not get valid server!\n");
+ return -EINVAL;
+ }
+
+ sock = &(inode->u.socket_i);
+
+ if (sock->type != SOCK_STREAM) {
+ printk("smb_dont_catch_keepalive: did not get SOCK_STREAM\n");
+ return -EINVAL;
+ }
+
+ sk = (struct sock *)(sock->data);
+
+ if (sk == NULL) {
+ printk("smb_dont_catch_keepalive: sk == NULL");
+ return -EINVAL;
+ }
+
+ if (server->data_ready == NULL) {
+ printk("smb_dont_catch_keepalive: "
+ "server->data_ready == NULL\n");
+ return -EINVAL;
+ }
+
+ if (sk->data_ready != smb_data_callback) {
+ printk("smb_dont_catch_keepalive: "
+ "sk->data_callback != smb_data_callback\n");
+ return -EINVAL;
+ }
+
+ DDPRINTK("smb_dont_catch_keepalive: sk->d_r = %x, server->d_r = %x\n",
+ (unsigned int)(sk->data_ready),
+ (unsigned int)(server->data_ready));
+
+ sk->data_ready = server->data_ready;
+ server->data_ready = NULL;
+ return 0;
+}
+
+/*
+ * smb_receive
+ * fs points to the correct segment, server != NULL, sock!=NULL
+ */
+static int
+smb_receive(struct smb_server *server, struct socket *sock)
+{
+ int len, result;
+ unsigned char peek_buf[4];
+
+ re_recv:
+
+ result = sock->ops->recvfrom(sock, (void *)peek_buf, 4, 0,
+ MSG_PEEK, NULL, NULL);
+
+ if (result < 0) {
+ DPRINTK("smb_receive: recv error = %d\n", -result);
+ return result;
+ }
+
+ if (result == 0) {
+ DPRINTK("smb_receive: got 0 bytes\n");
+ return -EIO;
+ }
+
+ switch (peek_buf[0]) {
+
+ case 0x00:
+ case 0x82:
+ break;
+
+ case 0x85:
+ DPRINTK("smb_receive: Got SESSION KEEP ALIVE\n");
+ sock->ops->recvfrom(sock, (void *)peek_buf, 4, 1,
+ 0, NULL, NULL);
+ goto re_recv;
+
+ default:
+ printk("smb_receive: Invalid packet\n");
+ return -EIO;
+ }
+
+ /* Length not including first four bytes. */
+ len = smb_len(peek_buf) + 4;
+ if (len > server->max_xmit) {
+ printk("smb_receive: Received length (%d) > max_xmit (%d)!\n",
+ len, server->max_xmit);
+ return -EIO;
+ }
+ else
+ {
+ int already_read = 0;
+
+ while (already_read < len) {
+
+ result = sock->ops->
+ recvfrom(sock,
+ (void *)(server->packet+already_read),
+ len - already_read, 0, 0,
+ NULL, NULL);
+
+ if (result < 0) {
+ printk("SMB: notice message: error = %d\n",
+ -result);
+ return result;
+ }
+
+ already_read += result;
+ }
+ result = already_read;
+ }
+
+ server->rcls = *((unsigned char *)(server->packet+9));
+ server->err = *((unsigned short *)(server->packet+11));
+
+ if (server->rcls != 0) {
+ DPRINTK("smb_response: rcls=%d, err=%d\n",
+ server->rcls, server->err);
+ }
+
+ return result;
+}
+
+
+/*
+ * smb_receive's preconditions also apply here.
+ */
+static int
+smb_receive_trans2(struct smb_server *server, struct socket *sock,
+ int *data_len, int *param_len,
+ char **data, char **param)
+{
+ int total_data=0;
+ int total_param=0;
+ int result;
+ unsigned char *inbuf = server->packet;
+
+ *data_len = *param_len = 0;
+
+ DDPRINTK("smb_receive_trans2: enter\n");
+
+ if ((result = smb_receive(server, sock)) < 0) {
+ return result;
+ }
+
+ if (server->rcls != 0) {
+ return result;
+ }
+
+ /* parse out the lengths */
+ total_data = WVAL(inbuf,smb_tdrcnt);
+ total_param = WVAL(inbuf,smb_tprcnt);
+
+ if ( (total_data > TRANS2_MAX_TRANSFER)
+ || (total_param > TRANS2_MAX_TRANSFER)) {
+ printk("smb_receive_trans2: data/param too long\n");
+ return -EIO;
+ }
+
+ /* allocate it */
+ if ((*data = smb_kmalloc(total_data, GFP_KERNEL)) == NULL) {
+ printk("smb_receive_trans2: could not alloc data area\n");
+ return -ENOMEM;
+ }
+
+ if ((*param = smb_kmalloc(total_param, GFP_KERNEL)) == NULL) {
+ printk("smb_receive_trans2: could not alloc param area\n");
+ smb_kfree_s(*data, total_data);
+ return -ENOMEM;
+ }
+
+ DDPRINTK("smb_rec_trans2: total_data/param: %d/%d\n",
+ total_data, total_param);
+
+ while (1)
+ {
+ if (WVAL(inbuf,smb_prdisp)+WVAL(inbuf, smb_prcnt)
+ > total_param) {
+ printk("smb_receive_trans2: invalid parameters\n");
+ result = -EIO;
+ goto fail;
+ }
+ memcpy(*param + WVAL(inbuf,smb_prdisp),
+ smb_base(inbuf) + WVAL(inbuf,smb_proff),
+ WVAL(inbuf,smb_prcnt));
+ *param_len += WVAL(inbuf,smb_prcnt);
+
+
+ if (WVAL(inbuf,smb_drdisp)+WVAL(inbuf, smb_drcnt)>total_data) {
+ printk("smb_receive_trans2: invalid data block\n");
+ result = -EIO;
+ goto fail;
+ }
+ memcpy(*data + WVAL(inbuf,smb_drdisp),
+ smb_base(inbuf) + WVAL(inbuf,smb_droff),
+ WVAL(inbuf,smb_drcnt));
+ *data_len += WVAL(inbuf,smb_drcnt);
+
+ DDPRINTK("smb_rec_trans2: drcnt/prcnt: %d/%d\n",
+ WVAL(inbuf, smb_drcnt), WVAL(inbuf, smb_prcnt));
+
+ /* parse out the total lengths again - they can shrink! */
+
+ if ( (WVAL(inbuf,smb_tdrcnt) > total_data)
+ || (WVAL(inbuf,smb_tprcnt) > total_param)) {
+ printk("smb_receive_trans2: data/params grew!\n");
+ result = -EIO;
+ goto fail;
+ }
+
+ total_data = WVAL(inbuf,smb_tdrcnt);
+ total_param = WVAL(inbuf,smb_tprcnt);
+
+ if (total_data <= *data_len && total_param <= *param_len)
+ break;
+
+ if ((result = smb_receive(server, sock)) < 0) {
+ goto fail;
+ }
+ if (server->rcls != 0) {
+ result = -EIO;
+ goto fail;
+ }
+ }
+
+ DDPRINTK("smb_receive_trans2: normal exit\n");
+
+ return 0;
+
+ fail:
+ DPRINTK("smb_receive_trans2: failed exit\n");
+
+ smb_kfree_s(*param, 0); *param = NULL;
+ smb_kfree_s(*data, 0); *data = NULL;
+ return result;
+}
+
+static inline struct socket *
+server_sock(struct smb_server *server)
+{
+ struct file *file;
+ struct inode *inode;
+
+ if (server == NULL)
+ return NULL;
+ if ((file = server->sock_file) == NULL)
+ return NULL;
+ if ((inode = file->f_inode) == NULL)
+ return NULL;
+ return &(inode->u.socket_i);
+}
+
+int
+smb_release(struct smb_server *server)
+{
+ struct socket *sock = server_sock(server);
+ int result;
+
+ if (sock == NULL)
+ return -EINVAL;
+
+ result = sock->ops->release(sock, NULL);
+ DPRINTK("smb_release: sock->ops->release = %d\n", result);
+
+ /* inet_release does not set sock->state. Maybe someone is
+ confused about sock->state being SS_CONNECTED while there
+ is nothing behind it, so I set it to SS_UNCONNECTED.*/
+ sock->state = SS_UNCONNECTED;
+
+ result = sock->ops->create(sock, 0);
+ DPRINTK("smb_release: sock->ops->create = %d\n", result);
+ return result;
+}
+
+int
+smb_connect(struct smb_server *server)
+{
+ struct socket *sock = server_sock(server);
+ if (sock == NULL)
+ return -EINVAL;
+ if (sock->state != SS_UNCONNECTED) {
+ DPRINTK("smb_connect: socket is not unconnected: %d\n",
+ sock->state);
+ }
+ return sock->ops->connect(sock, (struct sockaddr *)&(server->m.addr),
+ sizeof(struct sockaddr_in), 0);
+}
+
+/*****************************************************************************/
+/* */
+/* This routine was once taken from nfs, wich is for udp. Here TCP does */
+/* most of the ugly stuff for us (thanks, Alan!) */
+/* */
+/*****************************************************************************/
+int
+smb_request(struct smb_server *server)
+{
+ unsigned long old_mask;
+ unsigned short fs; /* We fool the kernel to believe
+ we call from user space. */
+ int len, result, result2;
+
+ struct socket *sock = server_sock(server);
+ unsigned char *buffer = (server == NULL) ? NULL : server->packet;
+
+ if ((sock == NULL) || (buffer == NULL)) {
+ printk("smb_request: Bad server!\n");
+ return -EBADF;
+ }
+
+ if (server->state != CONN_VALID)
+ return -EIO;
+
+#if 0
+ while (server->lock)
+ sleep_on(&server->wait);
+ server->lock = 1;
+#endif
+
+ if ((result = smb_dont_catch_keepalive(server)) != 0) {
+ server->state = CONN_INVALID;
+ smb_invalidate_all_inodes(server);
+ return result;
+ }
+
+ len = smb_len(buffer) + 4;
+
+ DDPRINTK("smb_request: len = %d cmd = 0x%X\n", len, buffer[8]);
+
+ old_mask = current->blocked;
+ current->blocked |= ~(_S(SIGKILL) | _S(SIGSTOP));
+ fs = get_fs();
+ set_fs(get_ds());
+
+ result = sock->ops->send(sock, (void *)buffer, len, 0, 0);
+ if (result < 0) {
+ printk("smb_request: send error = %d\n", result);
+ }
+ else {
+ result = smb_receive(server, sock);
+ }
+
+ /* read/write errors are handled by errno */
+ current->signal &= ~_S(SIGPIPE);
+
+ current->blocked = old_mask;
+ set_fs(fs);
+
+ if ((result2 = smb_catch_keepalive(server)) < 0) {
+ result = result2;
+ }
+
+#if 0
+ server->lock = 0;
+ wake_up(&server->wait);
+#endif
+
+ if (result < 0) {
+ server->state = CONN_INVALID;
+ smb_invalidate_all_inodes(server);
+ }
+
+ DDPRINTK("smb_request: result = %d\n", result);
+
+ return result;
+}
+
+/*
+ * This is not really a trans2 request, we assume that you only have
+ * one packet to send.
+ */
+int
+smb_trans2_request(struct smb_server *server,
+ int *data_len, int *param_len,
+ char **data, char **param)
+{
+ unsigned long old_mask;
+ unsigned short fs; /* We fool the kernel to believe
+ we call from user space. */
+ int len, result, result2;
+
+ struct socket *sock = server_sock(server);
+ unsigned char *buffer = (server == NULL) ? NULL : server->packet;
+
+ if ((sock == NULL) || (buffer == NULL)) {
+ printk("smb_trans2_request: Bad server!\n");
+ return -EBADF;
+ }
+
+ if (server->state != CONN_VALID)
+ return -EIO;
+
+#if 0
+ while (server->lock)
+ sleep_on(&server->wait);
+ server->lock = 1;
+#endif
+
+ if ((result = smb_dont_catch_keepalive(server)) != 0) {
+ server->state = CONN_INVALID;
+ smb_invalidate_all_inodes(server);
+ return result;
+ }
+
+ len = smb_len(buffer) + 4;
+
+ DDPRINTK("smb_request: len = %d cmd = 0x%X\n", len, buffer[8]);
+
+ old_mask = current->blocked;
+ current->blocked |= ~(_S(SIGKILL) | _S(SIGSTOP));
+ fs = get_fs();
+ set_fs(get_ds());
+
+ result = sock->ops->send(sock, (void *)buffer, len, 0, 0);
+ if (result < 0) {
+ printk("smb_trans2_request: send error = %d\n", result);
+ }
+ else {
+ result = smb_receive_trans2(server, sock,
+ data_len, param_len,
+ data, param);
+ }
+
+ /* read/write errors are handled by errno */
+ current->signal &= ~_S(SIGPIPE);
+
+ current->blocked = old_mask;
+ set_fs(fs);
+
+ if ((result2 = smb_catch_keepalive(server)) < 0) {
+ result = result2;
+ }
+
+#if 0
+ server->lock = 0;
+ wake_up(&server->wait);
+#endif
+
+ if (result < 0) {
+ server->state = CONN_INVALID;
+ smb_invalidate_all_inodes(server);
+ }
+
+ DDPRINTK("smb_trans2_request: result = %d\n", result);
+
+ return result;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this