Ejemplo n.º 1
0
/*
 * smb_receive
 * fs points to the correct segment
 */
static int
smb_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;
	}
	memcpy(packet, peek_buf, 4);
	result = smb_receive_raw(socket, packet + 4, len);
	if (result < 0)
	{
#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_receive: receive error: %d\n", result);
#endif
		goto out;
	}
	server->rcls = *(packet + smb_rcls);
	server->err  = WVAL(packet, smb_err);

#ifdef SMBFS_DEBUG_VERBOSE
if (server->rcls != 0)
printk("smb_receive: rcls=%d, err=%d\n", server->rcls, server->err);
#endif
out:
	return result;
}
Ejemplo n.º 2
0
/*
 * This routine checks first for "fast track" processing, as most
 * packets won't need to be copied. Otherwise, it allocates a new
 * packet to hold the incoming data.
 *
 * Note that the final server packet must be the larger of the two;
 * server packets aren't allowed to shrink.
 */
static int
smb_receive_trans2(struct smb_sb_info *server,
		   int *ldata, unsigned char **data,
		   int *lparm, unsigned char **parm)
{
	unsigned char *inbuf, *base, *rcv_buf = NULL;
	unsigned int parm_disp, parm_offset, parm_count, parm_tot, parm_len = 0;
	unsigned int data_disp, data_offset, data_count, data_tot, data_len = 0;
	unsigned int total_p = 0, total_d = 0, buf_len = 0;
	int result;

	while (1)
	{
		result = smb_receive(server);
		if (result < 0)
			goto out;
		inbuf = server->packet;
		if (server->rcls != 0)
		{
			*parm = *data = inbuf;
			*ldata = *lparm = 0;
			goto out;
		}
		/*
		 * Extract the control data from the packet.
		 */
		data_tot    = WVAL(inbuf, smb_tdrcnt);
		parm_tot    = WVAL(inbuf, smb_tprcnt);
		parm_disp   = WVAL(inbuf, smb_prdisp);
		parm_offset = WVAL(inbuf, smb_proff);
		parm_count  = WVAL(inbuf, smb_prcnt);
		data_disp   = WVAL(inbuf, smb_drdisp);
		data_offset = WVAL(inbuf, smb_droff);
		data_count  = WVAL(inbuf, smb_drcnt);
		base = smb_base(inbuf);

		/*
		 * Assume success and increment lengths.
		 */
		parm_len += parm_count;
		data_len += data_count;

		if (!rcv_buf)
		{
			/*
			 * Check for fast track processing ... just this packet.
			 */
			if (parm_count == parm_tot && data_count == data_tot)
			{
				VERBOSE("fast track, parm=%u %u %u, data=%u %u %u\n",
					parm_disp, parm_offset, parm_count, 
					data_disp, data_offset, data_count);

				*parm  = base + parm_offset;
				*data  = base + data_offset;
				goto success;
			}

			if (parm_tot > TRANS2_MAX_TRANSFER ||
	  		    data_tot > TRANS2_MAX_TRANSFER)
				goto out_too_long;

			/*
			 * Save the total parameter and data length.
			 */
			total_d = data_tot;
			total_p = parm_tot;

			buf_len = total_d + total_p;
			if (server->packet_size > buf_len)
				buf_len = server->packet_size;
			buf_len = smb_round_length(buf_len);

			rcv_buf = smb_vmalloc(buf_len);
			if (!rcv_buf)
				goto out_no_mem;
			*parm = rcv_buf;
			*data = rcv_buf + total_p;
		}
		else if (data_tot > total_d || parm_tot > total_p)
			goto out_data_grew;

		if (parm_disp + parm_count > total_p)
			goto out_bad_parm;
		if (data_disp + data_count > total_d)
			goto out_bad_data;
		memcpy(*parm + parm_disp, base + parm_offset, parm_count);
		memcpy(*data + data_disp, base + data_offset, data_count);

		PARANOIA("copied, parm=%u of %u, data=%u of %u\n",
			 parm_len, parm_tot, data_len, data_tot);

		/*
		 * Check whether we've received all of the data. Note that
		 * we use the packet totals -- total lengths might shrink!
		 */
		if (data_len >= data_tot && parm_len >= parm_tot)
			break;
	}

	/*
	 * Install the new packet.  Note that it's possible, though
	 * unlikely, that the new packet could be smaller than the
	 * old one, in which case we just copy the data.
	 */
	inbuf = server->packet;
	if (buf_len >= server->packet_size)
	{
		server->packet_size = buf_len;
		server->packet = rcv_buf;
		rcv_buf = inbuf;
	} else {
		PARANOIA("copying data, old size=%d, new size=%u\n",
			 server->packet_size, buf_len);
		memcpy(inbuf, rcv_buf, parm_len + data_len);
	}

success:
	*ldata = data_len;
	*lparm = parm_len;
out:
	if (rcv_buf)
		smb_vfree(rcv_buf);
	return result;

out_no_mem:
	printk(KERN_ERR "smb_receive_trans2: couldn't allocate data area\n");
	result = -ENOMEM;
	goto out;
out_too_long:
	printk(KERN_ERR "smb_receive_trans2: data/param too long, data=%d, parm=%d\n",
	       data_tot, parm_tot);
	goto out_error;
out_data_grew:
	printk(KERN_ERR "smb_receive_trans2: data/params grew!\n");
	goto out_error;
out_bad_parm:
	printk(KERN_ERR "smb_receive_trans2: invalid parms, disp=%d, cnt=%d, tot=%d\n",
	       parm_disp, parm_count, parm_tot);
	goto out_error;
out_bad_data:
	printk(KERN_ERR "smb_receive_trans2: invalid data, disp=%d, cnt=%d, tot=%d\n",
	       data_disp, data_count, data_tot);
out_error:
	result = -EIO;
	goto out;
}
Ejemplo n.º 3
0
/*
 * Add a request and tell smbiod to process it
 */
int smb_add_request(struct smb_request *req)
{
	long timeleft;
	struct smb_sb_info *server = req->rq_server;
	int result = 0;

	smb_setup_request(req);
	if (req->rq_trans2_command) {
		if (req->rq_buffer == NULL) {
			PARANOIA("trans2 attempted without response buffer!\n");
			return -EIO;
		}
		result = smb_setup_trans2request(req);
	}
	if (result < 0)
		return result;

#ifdef SMB_DEBUG_PACKET_SIZE
	add_xmit_stats(req);
#endif

	/* add 'req' to the queue of requests */
	if (smb_lock_server_interruptible(server))
		return -EINTR;

	/*
	 * Try to send the request as the process. If that fails we queue the
	 * request and let smbiod send it later.
	 */

	/* FIXME: each server has a number on the maximum number of parallel
	   requests. 10, 50 or so. We should not allow more requests to be
	   active. */
	if (server->mid > 0xf000)
		server->mid = 0;
	req->rq_mid = server->mid++;
	WSET(req->rq_header, smb_mid, req->rq_mid);

	result = 0;
	if (server->state == CONN_VALID) {
		if (list_empty(&server->xmitq))
			result = smb_request_send_req(req);
		if (result < 0) {
			/* Connection lost? */
			server->conn_error = result;
			server->state = CONN_INVALID;
		}
	}
	if (result != 1)
		list_add_tail(&req->rq_queue, &server->xmitq);
	smb_rget(req);

	if (server->state != CONN_VALID)
		smbiod_retry(server);

	smb_unlock_server(server);

	smbiod_wake_up();

	timeleft = wait_event_interruptible_timeout(req->rq_wait,
				    req->rq_flags & SMB_REQ_RECEIVED, 30*HZ);
	if (!timeleft || signal_pending(current)) {
		/*
		 * On timeout or on interrupt we want to try and remove the
		 * request from the recvq/xmitq.
		 * First check if the request is still part of a queue. (May
		 * have been removed by some error condition)
		 */
		smb_lock_server(server);
		if (!list_empty(&req->rq_queue)) {
			list_del_init(&req->rq_queue);
			smb_rput(req);
		}
		smb_unlock_server(server);
	}

	if (!timeleft) {
		PARANOIA("request [%p, mid=%d] timed out!\n",
			 req, req->rq_mid);
		VERBOSE("smb_com:  %02x\n", *(req->rq_header + smb_com));
		VERBOSE("smb_rcls: %02x\n", *(req->rq_header + smb_rcls));
		VERBOSE("smb_flg:  %02x\n", *(req->rq_header + smb_flg));
		VERBOSE("smb_tid:  %04x\n", WVAL(req->rq_header, smb_tid));
		VERBOSE("smb_pid:  %04x\n", WVAL(req->rq_header, smb_pid));
		VERBOSE("smb_uid:  %04x\n", WVAL(req->rq_header, smb_uid));
		VERBOSE("smb_mid:  %04x\n", WVAL(req->rq_header, smb_mid));
		VERBOSE("smb_wct:  %02x\n", *(req->rq_header + smb_wct));

		req->rq_rcls = ERRSRV;
		req->rq_err  = ERRtimeout;

		/* Just in case it was "stuck" */
		smbiod_wake_up();
	}
	VERBOSE("woke up, rcls=%d\n", req->rq_rcls);

	if (req->rq_rcls != 0)
		req->rq_errno = smb_errno(req);
	if (signal_pending(current))
		req->rq_errno = -ERESTARTSYS;
	return req->rq_errno;
}