📄 request.c
字号:
* Returns: <0 on error * 0 if no request could be completely sent * 1 if all data for one request was sent */int smb_request_send_server(struct smb_sb_info *server){ struct list_head *head; struct smb_request *req; int result; if (server->state != CONN_VALID) return 0; /* dequeue first request, if any */ req = NULL; head = server->xmitq.next; if (head != &server->xmitq) { req = list_entry(head, struct smb_request, rq_queue); } if (!req) return 0; result = smb_request_send_req(req); if (result < 0) { server->conn_error = result; list_move(&req->rq_queue, &server->xmitq); result = -EIO; goto out; }out: return result;}/* * Try to find a request matching this "mid". Typically the first entry will * be the matching one. */static struct smb_request *find_request(struct smb_sb_info *server, int mid){ struct list_head *tmp; struct smb_request *req = NULL; list_for_each(tmp, &server->recvq) { req = list_entry(tmp, struct smb_request, rq_queue); if (req->rq_mid == mid) { break; } req = NULL; } if (!req) { VERBOSE("received reply with mid %d but no request!\n", WVAL(server->header, smb_mid)); server->rstate = SMB_RECV_DROP; } return req;}/* * Called when we have read the smb header and believe this is a response. */static int smb_init_request(struct smb_sb_info *server, struct smb_request *req){ int hdrlen, wct; memcpy(req->rq_header, server->header, SMB_HEADER_LEN); wct = *(req->rq_header + smb_wct); if (wct > 20) { PARANOIA("wct too large, %d > 20\n", wct); server->rstate = SMB_RECV_DROP; return 0; } req->rq_resp_wct = wct; hdrlen = SMB_HEADER_LEN + wct*2 + 2; VERBOSE("header length: %d smb_wct: %2d\n", hdrlen, wct); req->rq_bytes_recvd = SMB_HEADER_LEN; req->rq_rlen = hdrlen; req->rq_iov[0].iov_base = req->rq_header; req->rq_iov[0].iov_len = hdrlen; req->rq_iovlen = 1; server->rstate = SMB_RECV_PARAM;#ifdef SMB_DEBUG_PACKET_SIZE add_recv_stats(smb_len(server->header));#endif return 0;}/* * Reads the SMB parameters */static int smb_recv_param(struct smb_sb_info *server, struct smb_request *req){ int result; result = smb_receive(server, req); if (result < 0) return result; if (req->rq_bytes_recvd < req->rq_rlen) return 0; VERBOSE("result: %d smb_bcc: %04x\n", result, WVAL(req->rq_header, SMB_HEADER_LEN + (*(req->rq_header + smb_wct) * 2))); result = 0; req->rq_iov[0].iov_base = NULL; req->rq_rlen = 0; if (req->rq_callback) req->rq_callback(req); else if (req->rq_setup_read) result = req->rq_setup_read(req); if (result < 0) { server->rstate = SMB_RECV_DROP; return result; } server->rstate = req->rq_rlen > 0 ? SMB_RECV_DATA : SMB_RECV_END; req->rq_bytes_recvd = 0; // recvd out of the iov VERBOSE("rlen: %d\n", req->rq_rlen); if (req->rq_rlen < 0) { PARANOIA("Parameters read beyond end of packet!\n"); server->rstate = SMB_RECV_END; return -EIO; } return 0;}/* * Reads the SMB data */static int smb_recv_data(struct smb_sb_info *server, struct smb_request *req){ int result; result = smb_receive(server, req); if (result < 0) goto out; if (req->rq_bytes_recvd < req->rq_rlen) goto out; server->rstate = SMB_RECV_END;out: VERBOSE("result: %d\n", result); return result;}/* * Receive a transaction2 response * Return: 0 if the response has been fully read * 1 if there are further "fragments" to read * <0 if there is an error */static int smb_recv_trans2(struct smb_sb_info *server, struct smb_request *req){ unsigned char *inbuf; unsigned int parm_disp, parm_offset, parm_count, parm_tot; unsigned int data_disp, data_offset, data_count, data_tot; int hdrlen = SMB_HEADER_LEN + req->rq_resp_wct*2 - 2; VERBOSE("handling trans2\n"); inbuf = req->rq_header; 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); /* Modify offset for the split header/buffer we use */ if (data_count || data_offset) { if (unlikely(data_offset < hdrlen)) goto out_bad_data; else data_offset -= hdrlen; } if (parm_count || parm_offset) { if (unlikely(parm_offset < hdrlen)) goto out_bad_parm; else parm_offset -= hdrlen; } if (parm_count == parm_tot && data_count == data_tot) { /* * This packet has all the trans2 data. * * We setup the request so that this will be the common * case. It may be a server error to not return a * response that fits. */ VERBOSE("single trans2 response " "dcnt=%u, pcnt=%u, doff=%u, poff=%u\n", data_count, parm_count, data_offset, parm_offset); req->rq_ldata = data_count; req->rq_lparm = parm_count; req->rq_data = req->rq_buffer + data_offset; req->rq_parm = req->rq_buffer + parm_offset; if (unlikely(parm_offset + parm_count > req->rq_rlen)) goto out_bad_parm; if (unlikely(data_offset + data_count > req->rq_rlen)) goto out_bad_data; return 0; } VERBOSE("multi trans2 response " "frag=%d, dcnt=%u, pcnt=%u, doff=%u, poff=%u\n", req->rq_fragment, data_count, parm_count, data_offset, parm_offset); if (!req->rq_fragment) { int buf_len; /* We got the first trans2 fragment */ req->rq_fragment = 1; req->rq_total_data = data_tot; req->rq_total_parm = parm_tot; req->rq_ldata = 0; req->rq_lparm = 0; buf_len = data_tot + parm_tot; if (buf_len > SMB_MAX_PACKET_SIZE) goto out_too_long; req->rq_trans2bufsize = buf_len; req->rq_trans2buffer = kzalloc(buf_len, GFP_NOFS); if (!req->rq_trans2buffer) goto out_no_mem; req->rq_parm = req->rq_trans2buffer; req->rq_data = req->rq_trans2buffer + parm_tot; } else if (unlikely(req->rq_total_data < data_tot || req->rq_total_parm < parm_tot)) goto out_data_grew; if (unlikely(parm_disp + parm_count > req->rq_total_parm || parm_offset + parm_count > req->rq_rlen)) goto out_bad_parm; if (unlikely(data_disp + data_count > req->rq_total_data || data_offset + data_count > req->rq_rlen)) goto out_bad_data; inbuf = req->rq_buffer; memcpy(req->rq_parm + parm_disp, inbuf + parm_offset, parm_count); memcpy(req->rq_data + data_disp, inbuf + data_offset, data_count); req->rq_ldata += data_count; req->rq_lparm += parm_count; /* * Check whether we've received all of the data. Note that * we use the packet totals -- total lengths might shrink! */ if (req->rq_ldata >= data_tot && req->rq_lparm >= parm_tot) { req->rq_ldata = data_tot; req->rq_lparm = parm_tot; return 0; } return 1;out_too_long: printk(KERN_ERR "smb_trans2: data/param too long, data=%u, parm=%u\n", data_tot, parm_tot); goto out_EIO;out_no_mem: printk(KERN_ERR "smb_trans2: couldn't allocate data area of %d bytes\n", req->rq_trans2bufsize); req->rq_errno = -ENOMEM; goto out;out_data_grew: printk(KERN_ERR "smb_trans2: data/params grew!\n"); goto out_EIO;out_bad_parm: printk(KERN_ERR "smb_trans2: invalid parms, disp=%u, cnt=%u, tot=%u, ofs=%u\n", parm_disp, parm_count, parm_tot, parm_offset); goto out_EIO;out_bad_data: printk(KERN_ERR "smb_trans2: invalid data, disp=%u, cnt=%u, tot=%u, ofs=%u\n", data_disp, data_count, data_tot, data_offset);out_EIO: req->rq_errno = -EIO;out: return req->rq_errno;}/* * State machine for receiving responses. We handle the fact that we can't * read the full response in one try by having states telling us how much we * have read. * * Must be called with the server lock held (only called from smbiod). * * Return: <0 on error */int smb_request_recv(struct smb_sb_info *server){ struct smb_request *req = NULL; int result = 0; if (smb_recv_available(server) <= 0) return 0; VERBOSE("state: %d\n", server->rstate); switch (server->rstate) { case SMB_RECV_DROP: result = smb_receive_drop(server); if (result < 0) break; if (server->rstate == SMB_RECV_DROP) break; server->rstate = SMB_RECV_START; /* fallthrough */ case SMB_RECV_START: server->smb_read = 0; server->rstate = SMB_RECV_HEADER; /* fallthrough */ case SMB_RECV_HEADER: result = smb_receive_header(server); if (result < 0) break; if (server->rstate == SMB_RECV_HEADER) break; if (! (*(server->header + smb_flg) & SMB_FLAGS_REPLY) ) { server->rstate = SMB_RECV_REQUEST; break; } if (server->rstate != SMB_RECV_HCOMPLETE) break; /* fallthrough */ case SMB_RECV_HCOMPLETE: req = find_request(server, WVAL(server->header, smb_mid)); if (!req) break; smb_init_request(server, req); req->rq_rcls = *(req->rq_header + smb_rcls); req->rq_err = WVAL(req->rq_header, smb_err); if (server->rstate != SMB_RECV_PARAM) break; /* fallthrough */ case SMB_RECV_PARAM: if (!req) req = find_request(server,WVAL(server->header,smb_mid)); if (!req) break; result = smb_recv_param(server, req); if (result < 0) break; if (server->rstate != SMB_RECV_DATA) break; /* fallthrough */ case SMB_RECV_DATA: if (!req) req = find_request(server,WVAL(server->header,smb_mid)); if (!req) break; result = smb_recv_data(server, req); if (result < 0) break; break; /* We should never be called with any of these states */ case SMB_RECV_END: case SMB_RECV_REQUEST: BUG(); } if (result < 0) { /* We saw an error */ return result; } if (server->rstate != SMB_RECV_END) return 0; result = 0; if (req->rq_trans2_command && req->rq_rcls == SUCCESS) result = smb_recv_trans2(server, req); /* * Response completely read. Drop any extra bytes sent by the server. * (Yes, servers sometimes add extra bytes to responses) */ VERBOSE("smb_len: %d smb_read: %d\n", server->smb_len, server->smb_read); if (server->smb_read < server->smb_len) smb_receive_drop(server); server->rstate = SMB_RECV_START; if (!result) { list_del_init(&req->rq_queue); req->rq_flags |= SMB_REQ_RECEIVED; smb_rput(req); wake_up_interruptible(&req->rq_wait); } return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -