/* * 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; }
/* * 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; }
/* * 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; }