static int smb_send_trans2(struct smb_sb_info *server, __u16 trans2_command, int ldata, unsigned char *data, int lparam, unsigned char *param) { struct socket *sock = server_sock(server); struct scm_cookie scm; int err; /* I know the following is very ugly, but I want to build the smb packet as efficiently as possible. */ const int smb_parameters = 15; const int oparam = ROUND_UP(SMB_HEADER_LEN + 2 * smb_parameters + 2 + 3); const int odata = ROUND_UP(oparam + lparam); const int bcc = odata + ldata - (SMB_HEADER_LEN + 2 * smb_parameters + 2); const int packet_length = SMB_HEADER_LEN + 2 * smb_parameters + bcc + 2; unsigned char padding[4] = {0,}; char *p; struct iovec iov[4]; struct msghdr msg; /* N.B. This test isn't valid! packet_size may be < max_xmit */ if ((bcc + oparam) > server->opt.max_xmit) { return -ENOMEM; } p = smb_setup_header(server, SMBtrans2, smb_parameters, bcc); WSET(server->packet, smb_tpscnt, lparam); WSET(server->packet, smb_tdscnt, ldata); /* N.B. these values should reflect out current packet size */ WSET(server->packet, smb_mprcnt, TRANS2_MAX_TRANSFER); WSET(server->packet, smb_mdrcnt, TRANS2_MAX_TRANSFER); WSET(server->packet, smb_msrcnt, 0); WSET(server->packet, smb_flags, 0); DSET(server->packet, smb_timeout, 0); WSET(server->packet, smb_pscnt, lparam); WSET(server->packet, smb_psoff, oparam - 4); WSET(server->packet, smb_dscnt, ldata); WSET(server->packet, smb_dsoff, odata - 4); WSET(server->packet, smb_suwcnt, 1); WSET(server->packet, smb_setup0, trans2_command); *p++ = 0; /* null smb_name for trans2 */ *p++ = 'D'; /* this was added because OS/2 does it */ *p++ = ' '; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_iov = iov; msg.msg_iovlen = 4; msg.msg_flags = 0; iov[0].iov_base = (void *) server->packet; iov[0].iov_len = oparam; iov[1].iov_base = (param == NULL) ? padding : param; iov[1].iov_len = lparam; iov[2].iov_base = padding; iov[2].iov_len = odata - oparam - lparam; iov[3].iov_base = (data == NULL) ? padding : data; iov[3].iov_len = ldata; err = scm_send(sock, &msg, &scm); if (err >= 0) { err = sock->ops->sendmsg(sock, &msg, packet_length, &scm); scm_destroy(&scm); } return err; }
/* * Prepare a transaction2 request structure */ static int smb_setup_trans2request(struct smb_request *req) { struct smb_sb_info *server = req->rq_server; int mparam, mdata; static unsigned char padding[4]; /* I know the following is very ugly, but I want to build the smb packet as efficiently as possible. */ const int smb_parameters = 15; const int header = SMB_HEADER_LEN + 2 * smb_parameters + 2; const int oparam = ROUND_UP(header + 3); const int odata = ROUND_UP(oparam + req->rq_lparm); const int bcc = (req->rq_data ? odata + req->rq_ldata : oparam + req->rq_lparm) - header; if ((bcc + oparam) > server->opt.max_xmit) return -ENOMEM; smb_setup_header(req, SMBtrans2, smb_parameters, bcc); /* * max parameters + max data + max setup == bufsize to make NT4 happy * and not abort the transfer or split into multiple responses. It also * makes smbfs happy as handling packets larger than the buffer size * is extra work. * * OS/2 is probably going to hate me for this ... */ mparam = SMB_TRANS2_MAX_PARAM; mdata = req->rq_bufsize - mparam; mdata = server->opt.max_xmit - mparam - 100; if (mdata < 1024) { mdata = 1024; mparam = 20; } #if 0 /* NT/win2k has ~4k max_xmit, so with this we request more than it wants to return as one SMB. Useful for testing the fragmented trans2 handling. */ mdata = 8192; #endif WSET(req->rq_header, smb_tpscnt, req->rq_lparm); WSET(req->rq_header, smb_tdscnt, req->rq_ldata); WSET(req->rq_header, smb_mprcnt, mparam); WSET(req->rq_header, smb_mdrcnt, mdata); WSET(req->rq_header, smb_msrcnt, 0); /* max setup always 0 ? */ WSET(req->rq_header, smb_flags, 0); DSET(req->rq_header, smb_timeout, 0); WSET(req->rq_header, smb_pscnt, req->rq_lparm); WSET(req->rq_header, smb_psoff, oparam - 4); WSET(req->rq_header, smb_dscnt, req->rq_ldata); WSET(req->rq_header, smb_dsoff, req->rq_data ? odata - 4 : 0); *(req->rq_header + smb_suwcnt) = 0x01; /* setup count */ *(req->rq_header + smb_suwcnt + 1) = 0x00; /* reserved */ WSET(req->rq_header, smb_setup0, req->rq_trans2_command); req->rq_iovlen = 2; req->rq_iov[0].iov_base = (void *) req->rq_header; req->rq_iov[0].iov_len = oparam; req->rq_iov[1].iov_base = (req->rq_parm==NULL) ? padding : req->rq_parm; req->rq_iov[1].iov_len = req->rq_lparm; req->rq_slen = oparam + req->rq_lparm; if (req->rq_data) { req->rq_iovlen += 2; req->rq_iov[2].iov_base = padding; req->rq_iov[2].iov_len = odata - oparam - req->rq_lparm; req->rq_iov[3].iov_base = req->rq_data; req->rq_iov[3].iov_len = req->rq_ldata; req->rq_slen = odata + req->rq_ldata; } /* always a data part for trans2 replies */ req->rq_setup_read = smb_setup_bcc; return 0; }