transport.c 9.43 KB
Newer Older
1 2 3
/*
 *   fs/cifs/transport.c
 *
4
 *   Copyright (C) International Business Machines  Corp., 2002,2003
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
 *   Author(s): Steve French (sfrench@us.ibm.com)
 *
 *   This library is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published
 *   by the Free Software Foundation; either version 2.1 of the License, or
 *   (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with this library; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 */

#include <linux/fs.h>
#include <linux/list.h>
#include <linux/wait.h>
#include <linux/net.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
#include "cifspdu.h"
#include "cifsglob.h"
#include "cifsproto.h"
#include "cifs_debug.h"

extern kmem_cache_t *cifs_mid_cachep;
34
extern kmem_cache_t *cifs_oplock_cachep;
35 36 37 38 39 40 41

struct mid_q_entry *
AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
{
	struct mid_q_entry *temp;

	if (ses == NULL) {
42
		cERROR(1, ("Null session passed in to AllocMidQEntry "));
43 44
		return NULL;
	}
45 46 47 48 49
	if (ses->server == NULL) {
		cERROR(1, ("Null TCP session in AllocMidQEntry"));
		return NULL;
	}
	
50 51 52 53 54 55 56 57 58
	temp = (struct mid_q_entry *) kmem_cache_alloc(cifs_mid_cachep,
						       SLAB_KERNEL);
	if (temp == NULL)
		return temp;
	else {
		memset(temp, 0, sizeof (struct mid_q_entry));
		temp->mid = smb_buffer->Mid;	/* always LE */
		temp->pid = current->pid;
		temp->command = smb_buffer->Command;
59
		cFYI(1, ("For smb_command %d", temp->command));
60 61 62 63
		do_gettimeofday(&temp->when_sent);
		temp->ses = ses;
		temp->tsk = current;
	}
64

65 66 67 68 69
	spin_lock(&GlobalMid_Lock);
	list_add_tail(&temp->qhead, &ses->server->pending_mid_q);
	atomic_inc(&midCount);
	temp->midState = MID_REQUEST_ALLOCATED;
	spin_unlock(&GlobalMid_Lock);
70 71 72 73 74 75
	return temp;
}

void
DeleteMidQEntry(struct mid_q_entry *midEntry)
{
76
	spin_lock(&GlobalMid_Lock);
77 78 79
	midEntry->midState = MID_FREE;
	list_del(&midEntry->qhead);
	atomic_dec(&midCount);
80
	spin_unlock(&GlobalMid_Lock);
81 82
	buf_release(midEntry->resp_buf);
	kmem_cache_free(cifs_mid_cachep, midEntry);
83 84
}

85
struct oplock_q_entry *
86
AllocOplockQEntry(struct inode * pinode, __u16 fid, struct cifsTconInfo * tcon)
87 88
{
	struct oplock_q_entry *temp;
89
	if ((pinode== NULL) || (tcon == NULL)) {
90 91 92 93 94 95 96 97
		cERROR(1, ("Null parms passed to AllocOplockQEntry"));
		return NULL;
	}
	temp = (struct oplock_q_entry *) kmem_cache_alloc(cifs_oplock_cachep,
						       SLAB_KERNEL);
	if (temp == NULL)
		return temp;
	else {
98
		temp->pinode = pinode;
99
		temp->tcon = tcon;
100
		temp->netfid = fid;
101
		spin_lock(&GlobalMid_Lock);
102
		list_add_tail(&temp->qhead, &GlobalOplock_Q);
103
		spin_unlock(&GlobalMid_Lock);
104
	}
105
	return temp;
106 107 108 109 110

}

void DeleteOplockQEntry(struct oplock_q_entry * oplockEntry)
{
111
	spin_lock(&GlobalMid_Lock); 
112 113
    /* should we check if list empty first? */
	list_del(&oplockEntry->qhead);
114
	spin_unlock(&GlobalMid_Lock);
115 116 117
	kmem_cache_free(cifs_oplock_cachep, oplockEntry);
}

118 119 120 121 122 123 124 125 126
int
smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
	 unsigned int smb_buf_length, struct sockaddr *sin)
{
	int rc = 0;
	struct msghdr smb_msg;
	struct iovec iov;
	mm_segment_t temp_fs;

127 128
	if(ssocket == NULL)
		return -ENOTSOCK; /* BB eventually add reconnect code here */
129 130 131 132 133 134 135 136 137 138
/*  ssocket->sk->allocation = GFP_BUFFER; *//* BB is this spurious? */
	iov.iov_base = smb_buffer;
	iov.iov_len = smb_buf_length + 4;

	smb_msg.msg_name = sin;
	smb_msg.msg_namelen = sizeof (struct sockaddr);
	smb_msg.msg_iov = &iov;
	smb_msg.msg_iovlen = 1;
	smb_msg.msg_control = NULL;
	smb_msg.msg_controllen = 0;
139
	smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/
140

141 142
	/* smb header is converted in header_assemble. bcc and rest of SMB word
	   area, and byte area if necessary, is converted to littleendian in 
Steve French's avatar
Steve French committed
143 144
	   cifssmb.c and RFC1001 len is converted to bigendian in smb_send 
	   Flags2 is converted in SendReceive */
145 146

	smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
147
	cFYI(1, ("Sending smb of length %d ", smb_buf_length));
148 149 150 151 152
	dump_smb(smb_buffer, smb_buf_length + 4);

	temp_fs = get_fs();	/* we must turn off socket api parm checking */
	set_fs(get_ds());
	rc = sock_sendmsg(ssocket, &smb_msg, smb_buf_length + 4);
153
	while((rc == -ENOSPC) || (rc == -EAGAIN)) {
Steve French's avatar
Steve French committed
154 155 156
		schedule_timeout(HZ/2);
		rc = sock_sendmsg(ssocket, &smb_msg, smb_buf_length + 4);
	}
157 158 159 160
	set_fs(temp_fs);

	if (rc < 0) {
		cERROR(1,
161
		       ("Error %d sending data on socket to server.", rc));
162 163 164 165 166 167 168 169 170 171 172 173
	} else
		rc = 0;

	return rc;
}

int
SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
	    struct smb_hdr *in_buf, struct smb_hdr *out_buf,
	    int *pbytes_returned, const int long_op)
{
	int rc = 0;
174
	unsigned int receive_len;
175 176 177
	long timeout;
	struct mid_q_entry *midQ;

178 179 180 181 182 183 184 185 186 187 188 189 190 191
	if ((ses == NULL) || (ses->server == NULL)) {
		cERROR(1,("Null tcp session or smb session: %p",ses));
		return -EIO;
	}

	if (ses->server->tcpStatus == CifsExiting) {
		return -ENOENT;
	} else if (ses->server->tcpStatus == CifsNeedReconnect) {
		cFYI(1,("tcp session dead - return to caller to retry"));
		return -EAGAIN;
	} else if (ses->status != CifsGood) {
		/* check if SMB session is bad because we are setting it up */
		if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && 
			(in_buf->Command != SMB_COM_NEGOTIATE)) {
192
			return -EAGAIN;
193 194
		} /* else ok - we are setting up session */
	}
195 196 197 198
	/* make sure that we sign in the same order that we send on this socket 
		and avoid races inside tcp sendmsg code that could cause corruption
		of smb data */
	down(&ses->server->tcpSem); 
199
	midQ = AllocMidQEntry(in_buf, ses);
200
	if (midQ == NULL) {
201
		up(&ses->server->tcpSem);
202
		return -EIO;
203 204
	}

205
	if (in_buf->smb_buf_length > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE - 4) {
206
		up(&ses->server->tcpSem);
207
		cERROR(1,
208
		       ("Illegal length, greater than maximum frame, %d ",
209 210 211 212
			in_buf->smb_buf_length));
		DeleteMidQEntry(midQ);
		return -EIO;
	}
Steve French's avatar
Steve French committed
213

214 215
	if (in_buf->smb_buf_length > 12)
		in_buf->Flags2 = cpu_to_le16(in_buf->Flags2);
216
	
217
	rc = cifs_sign_smb(in_buf, ses, &midQ->sequence_number);
Steve French's avatar
Steve French committed
218

219 220 221
	midQ->midState = MID_REQUEST_SUBMITTED;
	rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
		      (struct sockaddr *) &(ses->server->sockAddr));
222
	up(&ses->server->tcpSem);
223 224
	if (long_op == -1)
		goto cifs_no_response_exit;
225
	if (long_op > 1) /* writes past end of file can take looooong time */
226 227
		timeout = 300 * HZ;
	else if (long_op == 1)
228 229
		timeout = 45 * HZ; /* should be greater than 
			servers oplock break timeout (about 43 seconds) */
230 231
	else
		timeout = 15 * HZ;
232 233
	/* wait for 15 seconds or until woken up due to response arriving or 
	   due to last connection to this server being unmounted */
234 235

	timeout = wait_event_interruptible_timeout(ses->server->response_q,
236 237 238
				midQ->
				midState & MID_RESPONSE_RECEIVED,
				timeout);
239
	if (signal_pending(current)) {
240
		cFYI(1, ("CIFS: caught signal"));
241 242
		DeleteMidQEntry(midQ);
		return -EINTR;
243 244 245 246
	} else {  /* BB spinlock protect this against races with demux thread */
		spin_lock(&GlobalMid_Lock);
		if (midQ->resp_buf) {
			spin_unlock(&GlobalMid_Lock);
247 248
			receive_len =
			    be32_to_cpu(midQ->resp_buf->smb_buf_length);
249
		} else {
250
			cFYI(1,("No response buffer"));
251
			if(midQ->midState == MID_REQUEST_SUBMITTED) {
252
				ses->server->tcpStatus = CifsNeedReconnect;
253
				midQ->midState = MID_RETRY_NEEDED;
254 255 256 257 258
			}

			if(midQ->midState == MID_RETRY_NEEDED) {
				rc = -EAGAIN;
				cFYI(1,("marking request for retry"));
259
			} else {
260
				rc = -EIO;
261
			}
262 263 264
			spin_unlock(&GlobalMid_Lock);
			DeleteMidQEntry(midQ);
			return rc;
265 266 267
		}
	}

268
	if (receive_len > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE) {
269
		cERROR(1,
270
		       ("Frame too large received.  Length: %d  Xid: %d",
271 272 273 274 275 276 277 278 279 280 281
			receive_len, xid));
		rc = -EIO;
	} else {		/* rcvd frame is ok */

		if (midQ->resp_buf && out_buf
		    && (midQ->midState == MID_RESPONSE_RECEIVED)) {
			memcpy(out_buf, midQ->resp_buf,
			       receive_len +
			       4 /* include 4 byte RFC1001 header */ );

			dump_smb(out_buf, 92);
Steve French's avatar
Steve French committed
282
			/* convert the length into a more usable form */
283 284
			out_buf->smb_buf_length =
			    be32_to_cpu(out_buf->smb_buf_length);
Steve French's avatar
Steve French committed
285 286 287 288 289 290 291
			if((out_buf->smb_buf_length > 24) &&
			   (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) {
				rc = cifs_verify_signature(out_buf, ses->mac_signing_key,midQ->sequence_number); /* BB fix BB */
				if(rc)
					cFYI(1,("Unexpected signature received from server"));
			}

292 293 294 295 296 297 298 299 300 301 302
			if (out_buf->smb_buf_length > 12)
				out_buf->Flags2 = le16_to_cpu(out_buf->Flags2);
			if (out_buf->smb_buf_length > 28)
				out_buf->Pid = le16_to_cpu(out_buf->Pid);
			if (out_buf->smb_buf_length > 28)
				out_buf->PidHigh =
				    le16_to_cpu(out_buf->PidHigh);

			*pbytes_returned = out_buf->smb_buf_length;

			/* BB special case reconnect tid and reconnect uid here? */
303
			rc = map_smb_to_linux_error(out_buf);
304 305 306 307 308 309 310

			/* convert ByteCount if necessary */
			if (receive_len >=
			    sizeof (struct smb_hdr) -
			    4 /* do not count RFC1001 header */  +
			    (2 * out_buf->WordCount) + 2 /* bcc */ )
				BCC(out_buf) = le16_to_cpu(BCC(out_buf));
311
		} else {
312
			rc = -EIO;
313 314
			cFYI(1,("Bad MID state? "));
		}
315
	}
316
cifs_no_response_exit:
317 318 319
	DeleteMidQEntry(midQ);	/* BB what if process is killed?
			 - BB add background daemon to clean up Mid entries from
			 killed processes & test killing process with active mid */
320 321
	return rc;
}