| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | *  request.c | 
|  | 3 | * | 
|  | 4 | *  Copyright (C) 2001 by Urban Widmark | 
|  | 5 | * | 
|  | 6 | *  Please add a note about your changes to smbfs in the ChangeLog file. | 
|  | 7 | */ | 
|  | 8 |  | 
|  | 9 | #include <linux/types.h> | 
|  | 10 | #include <linux/fs.h> | 
|  | 11 | #include <linux/slab.h> | 
|  | 12 | #include <linux/net.h> | 
|  | 13 |  | 
|  | 14 | #include <linux/smb_fs.h> | 
|  | 15 | #include <linux/smbno.h> | 
|  | 16 | #include <linux/smb_mount.h> | 
|  | 17 |  | 
|  | 18 | #include "smb_debug.h" | 
|  | 19 | #include "request.h" | 
|  | 20 | #include "proto.h" | 
|  | 21 |  | 
|  | 22 | /* #define SMB_SLAB_DEBUG	(SLAB_RED_ZONE | SLAB_POISON) */ | 
|  | 23 | #define SMB_SLAB_DEBUG	0 | 
|  | 24 |  | 
|  | 25 | #define ROUND_UP(x) (((x)+3) & ~3) | 
|  | 26 |  | 
|  | 27 | /* cache for request structures */ | 
|  | 28 | static kmem_cache_t *req_cachep; | 
|  | 29 |  | 
|  | 30 | static int smb_request_send_req(struct smb_request *req); | 
|  | 31 |  | 
|  | 32 | /* | 
|  | 33 | /proc/slabinfo: | 
|  | 34 | name, active, num, objsize, active_slabs, num_slaps, #pages | 
|  | 35 | */ | 
|  | 36 |  | 
|  | 37 |  | 
|  | 38 | int smb_init_request_cache(void) | 
|  | 39 | { | 
|  | 40 | req_cachep = kmem_cache_create("smb_request", | 
|  | 41 | sizeof(struct smb_request), 0, | 
|  | 42 | SMB_SLAB_DEBUG | SLAB_HWCACHE_ALIGN, | 
|  | 43 | NULL, NULL); | 
|  | 44 | if (req_cachep == NULL) | 
|  | 45 | return -ENOMEM; | 
|  | 46 |  | 
|  | 47 | return 0; | 
|  | 48 | } | 
|  | 49 |  | 
|  | 50 | void smb_destroy_request_cache(void) | 
|  | 51 | { | 
|  | 52 | if (kmem_cache_destroy(req_cachep)) | 
|  | 53 | printk(KERN_INFO "smb_destroy_request_cache: not all structures were freed\n"); | 
|  | 54 | } | 
|  | 55 |  | 
|  | 56 | /* | 
|  | 57 | * Allocate and initialise a request structure | 
|  | 58 | */ | 
|  | 59 | static struct smb_request *smb_do_alloc_request(struct smb_sb_info *server, | 
|  | 60 | int bufsize) | 
|  | 61 | { | 
|  | 62 | struct smb_request *req; | 
|  | 63 | unsigned char *buf = NULL; | 
|  | 64 |  | 
|  | 65 | req = kmem_cache_alloc(req_cachep, SLAB_KERNEL); | 
|  | 66 | VERBOSE("allocating request: %p\n", req); | 
|  | 67 | if (!req) | 
|  | 68 | goto out; | 
|  | 69 |  | 
|  | 70 | if (bufsize > 0) { | 
| Pekka Enberg | d063389 | 2006-01-14 13:21:13 -0800 | [diff] [blame] | 71 | buf = kmalloc(bufsize, GFP_NOFS); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 72 | if (!buf) { | 
|  | 73 | kmem_cache_free(req_cachep, req); | 
|  | 74 | return NULL; | 
|  | 75 | } | 
|  | 76 | } | 
|  | 77 |  | 
|  | 78 | memset(req, 0, sizeof(struct smb_request)); | 
|  | 79 | req->rq_buffer = buf; | 
|  | 80 | req->rq_bufsize = bufsize; | 
|  | 81 | req->rq_server = server; | 
|  | 82 | init_waitqueue_head(&req->rq_wait); | 
|  | 83 | INIT_LIST_HEAD(&req->rq_queue); | 
|  | 84 | atomic_set(&req->rq_count, 1); | 
|  | 85 |  | 
|  | 86 | out: | 
|  | 87 | return req; | 
|  | 88 | } | 
|  | 89 |  | 
|  | 90 | struct smb_request *smb_alloc_request(struct smb_sb_info *server, int bufsize) | 
|  | 91 | { | 
|  | 92 | struct smb_request *req = NULL; | 
|  | 93 |  | 
|  | 94 | for (;;) { | 
|  | 95 | atomic_inc(&server->nr_requests); | 
|  | 96 | if (atomic_read(&server->nr_requests) <= MAX_REQUEST_HARD) { | 
|  | 97 | req = smb_do_alloc_request(server, bufsize); | 
|  | 98 | if (req != NULL) | 
|  | 99 | break; | 
|  | 100 | } | 
|  | 101 |  | 
|  | 102 | #if 0 | 
|  | 103 | /* | 
|  | 104 | * Try to free up at least one request in order to stay | 
|  | 105 | * below the hard limit | 
|  | 106 | */ | 
|  | 107 | if (nfs_try_to_free_pages(server)) | 
|  | 108 | continue; | 
|  | 109 |  | 
|  | 110 | if (signalled() && (server->flags & NFS_MOUNT_INTR)) | 
|  | 111 | return ERR_PTR(-ERESTARTSYS); | 
|  | 112 | current->policy = SCHED_YIELD; | 
|  | 113 | schedule(); | 
|  | 114 | #else | 
|  | 115 | /* FIXME: we want something like nfs does above, but that | 
|  | 116 | requires changes to all callers and can wait. */ | 
|  | 117 | break; | 
|  | 118 | #endif | 
|  | 119 | } | 
|  | 120 | return req; | 
|  | 121 | } | 
|  | 122 |  | 
|  | 123 | static void smb_free_request(struct smb_request *req) | 
|  | 124 | { | 
|  | 125 | atomic_dec(&req->rq_server->nr_requests); | 
|  | 126 | if (req->rq_buffer && !(req->rq_flags & SMB_REQ_STATIC)) | 
| Pekka Enberg | d063389 | 2006-01-14 13:21:13 -0800 | [diff] [blame] | 127 | kfree(req->rq_buffer); | 
|  | 128 | kfree(req->rq_trans2buffer); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 129 | kmem_cache_free(req_cachep, req); | 
|  | 130 | } | 
|  | 131 |  | 
|  | 132 | /* | 
|  | 133 | * What prevents a rget to race with a rput? The count must never drop to zero | 
|  | 134 | * while it is in use. Only rput if it is ok that it is free'd. | 
|  | 135 | */ | 
|  | 136 | static void smb_rget(struct smb_request *req) | 
|  | 137 | { | 
|  | 138 | atomic_inc(&req->rq_count); | 
|  | 139 | } | 
|  | 140 | void smb_rput(struct smb_request *req) | 
|  | 141 | { | 
|  | 142 | if (atomic_dec_and_test(&req->rq_count)) { | 
|  | 143 | list_del_init(&req->rq_queue); | 
|  | 144 | smb_free_request(req); | 
|  | 145 | } | 
|  | 146 | } | 
|  | 147 |  | 
|  | 148 | /* setup to receive the data part of the SMB */ | 
|  | 149 | static int smb_setup_bcc(struct smb_request *req) | 
|  | 150 | { | 
|  | 151 | int result = 0; | 
|  | 152 | req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd; | 
|  | 153 |  | 
|  | 154 | if (req->rq_rlen > req->rq_bufsize) { | 
|  | 155 | PARANOIA("Packet too large %d > %d\n", | 
|  | 156 | req->rq_rlen, req->rq_bufsize); | 
|  | 157 | return -ENOBUFS; | 
|  | 158 | } | 
|  | 159 |  | 
|  | 160 | req->rq_iov[0].iov_base = req->rq_buffer; | 
|  | 161 | req->rq_iov[0].iov_len  = req->rq_rlen; | 
|  | 162 | req->rq_iovlen = 1; | 
|  | 163 |  | 
|  | 164 | return result; | 
|  | 165 | } | 
|  | 166 |  | 
|  | 167 | /* | 
|  | 168 | * Prepare a "normal" request structure. | 
|  | 169 | */ | 
|  | 170 | static int smb_setup_request(struct smb_request *req) | 
|  | 171 | { | 
|  | 172 | int len = smb_len(req->rq_header) + 4; | 
|  | 173 | req->rq_slen = len; | 
|  | 174 |  | 
|  | 175 | /* if we expect a data part in the reply we set the iov's to read it */ | 
|  | 176 | if (req->rq_resp_bcc) | 
|  | 177 | req->rq_setup_read = smb_setup_bcc; | 
|  | 178 |  | 
|  | 179 | /* This tries to support re-using the same request */ | 
|  | 180 | req->rq_bytes_sent = 0; | 
|  | 181 | req->rq_rcls = 0; | 
|  | 182 | req->rq_err = 0; | 
|  | 183 | req->rq_errno = 0; | 
|  | 184 | req->rq_fragment = 0; | 
| Pekka Enberg | d063389 | 2006-01-14 13:21:13 -0800 | [diff] [blame] | 185 | kfree(req->rq_trans2buffer); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 186 |  | 
|  | 187 | return 0; | 
|  | 188 | } | 
|  | 189 |  | 
|  | 190 | /* | 
|  | 191 | * Prepare a transaction2 request structure | 
|  | 192 | */ | 
|  | 193 | static int smb_setup_trans2request(struct smb_request *req) | 
|  | 194 | { | 
|  | 195 | struct smb_sb_info *server = req->rq_server; | 
|  | 196 | int mparam, mdata; | 
|  | 197 | static unsigned char padding[4]; | 
|  | 198 |  | 
|  | 199 | /* I know the following is very ugly, but I want to build the | 
|  | 200 | smb packet as efficiently as possible. */ | 
|  | 201 |  | 
|  | 202 | const int smb_parameters = 15; | 
|  | 203 | const int header = SMB_HEADER_LEN + 2 * smb_parameters + 2; | 
|  | 204 | const int oparam = ROUND_UP(header + 3); | 
|  | 205 | const int odata  = ROUND_UP(oparam + req->rq_lparm); | 
|  | 206 | const int bcc = (req->rq_data ? odata + req->rq_ldata : | 
|  | 207 | oparam + req->rq_lparm) - header; | 
|  | 208 |  | 
|  | 209 | if ((bcc + oparam) > server->opt.max_xmit) | 
|  | 210 | return -ENOMEM; | 
|  | 211 | smb_setup_header(req, SMBtrans2, smb_parameters, bcc); | 
|  | 212 |  | 
|  | 213 | /* | 
|  | 214 | * max parameters + max data + max setup == bufsize to make NT4 happy | 
|  | 215 | * and not abort the transfer or split into multiple responses. It also | 
|  | 216 | * makes smbfs happy as handling packets larger than the buffer size | 
|  | 217 | * is extra work. | 
|  | 218 | * | 
|  | 219 | * OS/2 is probably going to hate me for this ... | 
|  | 220 | */ | 
|  | 221 | mparam = SMB_TRANS2_MAX_PARAM; | 
|  | 222 | mdata = req->rq_bufsize - mparam; | 
|  | 223 |  | 
|  | 224 | mdata = server->opt.max_xmit - mparam - 100; | 
|  | 225 | if (mdata < 1024) { | 
|  | 226 | mdata = 1024; | 
|  | 227 | mparam = 20; | 
|  | 228 | } | 
|  | 229 |  | 
|  | 230 | #if 0 | 
|  | 231 | /* NT/win2k has ~4k max_xmit, so with this we request more than it wants | 
|  | 232 | to return as one SMB. Useful for testing the fragmented trans2 | 
|  | 233 | handling. */ | 
|  | 234 | mdata = 8192; | 
|  | 235 | #endif | 
|  | 236 |  | 
|  | 237 | WSET(req->rq_header, smb_tpscnt, req->rq_lparm); | 
|  | 238 | WSET(req->rq_header, smb_tdscnt, req->rq_ldata); | 
|  | 239 | WSET(req->rq_header, smb_mprcnt, mparam); | 
|  | 240 | WSET(req->rq_header, smb_mdrcnt, mdata); | 
|  | 241 | WSET(req->rq_header, smb_msrcnt, 0);    /* max setup always 0 ? */ | 
|  | 242 | WSET(req->rq_header, smb_flags, 0); | 
|  | 243 | DSET(req->rq_header, smb_timeout, 0); | 
|  | 244 | WSET(req->rq_header, smb_pscnt, req->rq_lparm); | 
|  | 245 | WSET(req->rq_header, smb_psoff, oparam - 4); | 
|  | 246 | WSET(req->rq_header, smb_dscnt, req->rq_ldata); | 
|  | 247 | WSET(req->rq_header, smb_dsoff, req->rq_data ? odata - 4 : 0); | 
|  | 248 | *(req->rq_header + smb_suwcnt) = 0x01;          /* setup count */ | 
|  | 249 | *(req->rq_header + smb_suwcnt + 1) = 0x00;      /* reserved */ | 
|  | 250 | WSET(req->rq_header, smb_setup0, req->rq_trans2_command); | 
|  | 251 |  | 
|  | 252 | req->rq_iovlen = 2; | 
|  | 253 | req->rq_iov[0].iov_base = (void *) req->rq_header; | 
|  | 254 | req->rq_iov[0].iov_len = oparam; | 
|  | 255 | req->rq_iov[1].iov_base = (req->rq_parm==NULL) ? padding : req->rq_parm; | 
|  | 256 | req->rq_iov[1].iov_len = req->rq_lparm; | 
|  | 257 | req->rq_slen = oparam + req->rq_lparm; | 
|  | 258 |  | 
|  | 259 | if (req->rq_data) { | 
|  | 260 | req->rq_iovlen += 2; | 
|  | 261 | req->rq_iov[2].iov_base = padding; | 
|  | 262 | req->rq_iov[2].iov_len = odata - oparam - req->rq_lparm; | 
|  | 263 | req->rq_iov[3].iov_base = req->rq_data; | 
|  | 264 | req->rq_iov[3].iov_len = req->rq_ldata; | 
|  | 265 | req->rq_slen = odata + req->rq_ldata; | 
|  | 266 | } | 
|  | 267 |  | 
|  | 268 | /* always a data part for trans2 replies */ | 
|  | 269 | req->rq_setup_read = smb_setup_bcc; | 
|  | 270 |  | 
|  | 271 | return 0; | 
|  | 272 | } | 
|  | 273 |  | 
|  | 274 | /* | 
|  | 275 | * Add a request and tell smbiod to process it | 
|  | 276 | */ | 
|  | 277 | int smb_add_request(struct smb_request *req) | 
|  | 278 | { | 
|  | 279 | long timeleft; | 
|  | 280 | struct smb_sb_info *server = req->rq_server; | 
|  | 281 | int result = 0; | 
|  | 282 |  | 
|  | 283 | smb_setup_request(req); | 
|  | 284 | if (req->rq_trans2_command) { | 
|  | 285 | if (req->rq_buffer == NULL) { | 
|  | 286 | PARANOIA("trans2 attempted without response buffer!\n"); | 
|  | 287 | return -EIO; | 
|  | 288 | } | 
|  | 289 | result = smb_setup_trans2request(req); | 
|  | 290 | } | 
|  | 291 | if (result < 0) | 
|  | 292 | return result; | 
|  | 293 |  | 
|  | 294 | #ifdef SMB_DEBUG_PACKET_SIZE | 
|  | 295 | add_xmit_stats(req); | 
|  | 296 | #endif | 
|  | 297 |  | 
|  | 298 | /* add 'req' to the queue of requests */ | 
|  | 299 | if (smb_lock_server_interruptible(server)) | 
|  | 300 | return -EINTR; | 
|  | 301 |  | 
|  | 302 | /* | 
|  | 303 | * Try to send the request as the process. If that fails we queue the | 
|  | 304 | * request and let smbiod send it later. | 
|  | 305 | */ | 
|  | 306 |  | 
|  | 307 | /* FIXME: each server has a number on the maximum number of parallel | 
|  | 308 | requests. 10, 50 or so. We should not allow more requests to be | 
|  | 309 | active. */ | 
|  | 310 | if (server->mid > 0xf000) | 
|  | 311 | server->mid = 0; | 
|  | 312 | req->rq_mid = server->mid++; | 
|  | 313 | WSET(req->rq_header, smb_mid, req->rq_mid); | 
|  | 314 |  | 
|  | 315 | result = 0; | 
|  | 316 | if (server->state == CONN_VALID) { | 
|  | 317 | if (list_empty(&server->xmitq)) | 
|  | 318 | result = smb_request_send_req(req); | 
|  | 319 | if (result < 0) { | 
|  | 320 | /* Connection lost? */ | 
|  | 321 | server->conn_error = result; | 
|  | 322 | server->state = CONN_INVALID; | 
|  | 323 | } | 
|  | 324 | } | 
|  | 325 | if (result != 1) | 
|  | 326 | list_add_tail(&req->rq_queue, &server->xmitq); | 
|  | 327 | smb_rget(req); | 
|  | 328 |  | 
|  | 329 | if (server->state != CONN_VALID) | 
|  | 330 | smbiod_retry(server); | 
|  | 331 |  | 
|  | 332 | smb_unlock_server(server); | 
|  | 333 |  | 
|  | 334 | smbiod_wake_up(); | 
|  | 335 |  | 
|  | 336 | timeleft = wait_event_interruptible_timeout(req->rq_wait, | 
|  | 337 | req->rq_flags & SMB_REQ_RECEIVED, 30*HZ); | 
|  | 338 | if (!timeleft || signal_pending(current)) { | 
|  | 339 | /* | 
|  | 340 | * On timeout or on interrupt we want to try and remove the | 
|  | 341 | * request from the recvq/xmitq. | 
|  | 342 | */ | 
|  | 343 | smb_lock_server(server); | 
|  | 344 | if (!(req->rq_flags & SMB_REQ_RECEIVED)) { | 
|  | 345 | list_del_init(&req->rq_queue); | 
|  | 346 | smb_rput(req); | 
|  | 347 | } | 
|  | 348 | smb_unlock_server(server); | 
|  | 349 | } | 
|  | 350 |  | 
|  | 351 | if (!timeleft) { | 
|  | 352 | PARANOIA("request [%p, mid=%d] timed out!\n", | 
|  | 353 | req, req->rq_mid); | 
|  | 354 | VERBOSE("smb_com:  %02x\n", *(req->rq_header + smb_com)); | 
|  | 355 | VERBOSE("smb_rcls: %02x\n", *(req->rq_header + smb_rcls)); | 
|  | 356 | VERBOSE("smb_flg:  %02x\n", *(req->rq_header + smb_flg)); | 
|  | 357 | VERBOSE("smb_tid:  %04x\n", WVAL(req->rq_header, smb_tid)); | 
|  | 358 | VERBOSE("smb_pid:  %04x\n", WVAL(req->rq_header, smb_pid)); | 
|  | 359 | VERBOSE("smb_uid:  %04x\n", WVAL(req->rq_header, smb_uid)); | 
|  | 360 | VERBOSE("smb_mid:  %04x\n", WVAL(req->rq_header, smb_mid)); | 
|  | 361 | VERBOSE("smb_wct:  %02x\n", *(req->rq_header + smb_wct)); | 
|  | 362 |  | 
|  | 363 | req->rq_rcls = ERRSRV; | 
|  | 364 | req->rq_err  = ERRtimeout; | 
|  | 365 |  | 
|  | 366 | /* Just in case it was "stuck" */ | 
|  | 367 | smbiod_wake_up(); | 
|  | 368 | } | 
|  | 369 | VERBOSE("woke up, rcls=%d\n", req->rq_rcls); | 
|  | 370 |  | 
|  | 371 | if (req->rq_rcls != 0) | 
|  | 372 | req->rq_errno = smb_errno(req); | 
|  | 373 | if (signal_pending(current)) | 
|  | 374 | req->rq_errno = -ERESTARTSYS; | 
|  | 375 | return req->rq_errno; | 
|  | 376 | } | 
|  | 377 |  | 
|  | 378 | /* | 
|  | 379 | * Send a request and place it on the recvq if successfully sent. | 
|  | 380 | * Must be called with the server lock held. | 
|  | 381 | */ | 
|  | 382 | static int smb_request_send_req(struct smb_request *req) | 
|  | 383 | { | 
|  | 384 | struct smb_sb_info *server = req->rq_server; | 
|  | 385 | int result; | 
|  | 386 |  | 
|  | 387 | if (req->rq_bytes_sent == 0) { | 
|  | 388 | WSET(req->rq_header, smb_tid, server->opt.tid); | 
|  | 389 | WSET(req->rq_header, smb_pid, 1); | 
|  | 390 | WSET(req->rq_header, smb_uid, server->opt.server_uid); | 
|  | 391 | } | 
|  | 392 |  | 
|  | 393 | result = smb_send_request(req); | 
|  | 394 | if (result < 0 && result != -EAGAIN) | 
|  | 395 | goto out; | 
|  | 396 |  | 
|  | 397 | result = 0; | 
|  | 398 | if (!(req->rq_flags & SMB_REQ_TRANSMITTED)) | 
|  | 399 | goto out; | 
|  | 400 |  | 
|  | 401 | list_del_init(&req->rq_queue); | 
|  | 402 | list_add_tail(&req->rq_queue, &server->recvq); | 
|  | 403 | result = 1; | 
|  | 404 | out: | 
|  | 405 | return result; | 
|  | 406 | } | 
|  | 407 |  | 
|  | 408 | /* | 
|  | 409 | * Sends one request for this server. (smbiod) | 
|  | 410 | * Must be called with the server lock held. | 
|  | 411 | * Returns: <0 on error | 
|  | 412 | *           0 if no request could be completely sent | 
|  | 413 | *           1 if all data for one request was sent | 
|  | 414 | */ | 
|  | 415 | int smb_request_send_server(struct smb_sb_info *server) | 
|  | 416 | { | 
|  | 417 | struct list_head *head; | 
|  | 418 | struct smb_request *req; | 
|  | 419 | int result; | 
|  | 420 |  | 
|  | 421 | if (server->state != CONN_VALID) | 
|  | 422 | return 0; | 
|  | 423 |  | 
|  | 424 | /* dequeue first request, if any */ | 
|  | 425 | req = NULL; | 
|  | 426 | head = server->xmitq.next; | 
|  | 427 | if (head != &server->xmitq) { | 
|  | 428 | req = list_entry(head, struct smb_request, rq_queue); | 
|  | 429 | } | 
|  | 430 | if (!req) | 
|  | 431 | return 0; | 
|  | 432 |  | 
|  | 433 | result = smb_request_send_req(req); | 
|  | 434 | if (result < 0) { | 
|  | 435 | server->conn_error = result; | 
|  | 436 | list_del_init(&req->rq_queue); | 
|  | 437 | list_add(&req->rq_queue, &server->xmitq); | 
|  | 438 | result = -EIO; | 
|  | 439 | goto out; | 
|  | 440 | } | 
|  | 441 |  | 
|  | 442 | out: | 
|  | 443 | return result; | 
|  | 444 | } | 
|  | 445 |  | 
|  | 446 | /* | 
|  | 447 | * Try to find a request matching this "mid". Typically the first entry will | 
|  | 448 | * be the matching one. | 
|  | 449 | */ | 
|  | 450 | static struct smb_request *find_request(struct smb_sb_info *server, int mid) | 
|  | 451 | { | 
|  | 452 | struct list_head *tmp; | 
|  | 453 | struct smb_request *req = NULL; | 
|  | 454 |  | 
|  | 455 | list_for_each(tmp, &server->recvq) { | 
|  | 456 | req = list_entry(tmp, struct smb_request, rq_queue); | 
|  | 457 | if (req->rq_mid == mid) { | 
|  | 458 | break; | 
|  | 459 | } | 
|  | 460 | req = NULL; | 
|  | 461 | } | 
|  | 462 |  | 
|  | 463 | if (!req) { | 
|  | 464 | VERBOSE("received reply with mid %d but no request!\n", | 
|  | 465 | WVAL(server->header, smb_mid)); | 
|  | 466 | server->rstate = SMB_RECV_DROP; | 
|  | 467 | } | 
|  | 468 |  | 
|  | 469 | return req; | 
|  | 470 | } | 
|  | 471 |  | 
|  | 472 | /* | 
|  | 473 | * Called when we have read the smb header and believe this is a response. | 
|  | 474 | */ | 
|  | 475 | static int smb_init_request(struct smb_sb_info *server, struct smb_request *req) | 
|  | 476 | { | 
|  | 477 | int hdrlen, wct; | 
|  | 478 |  | 
|  | 479 | memcpy(req->rq_header, server->header, SMB_HEADER_LEN); | 
|  | 480 |  | 
|  | 481 | wct = *(req->rq_header + smb_wct); | 
|  | 482 | if (wct > 20) { | 
|  | 483 | PARANOIA("wct too large, %d > 20\n", wct); | 
|  | 484 | server->rstate = SMB_RECV_DROP; | 
|  | 485 | return 0; | 
|  | 486 | } | 
|  | 487 |  | 
|  | 488 | req->rq_resp_wct = wct; | 
|  | 489 | hdrlen = SMB_HEADER_LEN + wct*2 + 2; | 
|  | 490 | VERBOSE("header length: %d   smb_wct: %2d\n", hdrlen, wct); | 
|  | 491 |  | 
|  | 492 | req->rq_bytes_recvd = SMB_HEADER_LEN; | 
|  | 493 | req->rq_rlen = hdrlen; | 
|  | 494 | req->rq_iov[0].iov_base = req->rq_header; | 
|  | 495 | req->rq_iov[0].iov_len  = hdrlen; | 
|  | 496 | req->rq_iovlen = 1; | 
|  | 497 | server->rstate = SMB_RECV_PARAM; | 
|  | 498 |  | 
|  | 499 | #ifdef SMB_DEBUG_PACKET_SIZE | 
|  | 500 | add_recv_stats(smb_len(server->header)); | 
|  | 501 | #endif | 
|  | 502 | return 0; | 
|  | 503 | } | 
|  | 504 |  | 
|  | 505 | /* | 
|  | 506 | * Reads the SMB parameters | 
|  | 507 | */ | 
|  | 508 | static int smb_recv_param(struct smb_sb_info *server, struct smb_request *req) | 
|  | 509 | { | 
|  | 510 | int result; | 
|  | 511 |  | 
|  | 512 | result = smb_receive(server, req); | 
|  | 513 | if (result < 0) | 
|  | 514 | return result; | 
|  | 515 | if (req->rq_bytes_recvd < req->rq_rlen) | 
|  | 516 | return 0; | 
|  | 517 |  | 
|  | 518 | VERBOSE("result: %d   smb_bcc:  %04x\n", result, | 
|  | 519 | WVAL(req->rq_header, SMB_HEADER_LEN + | 
|  | 520 | (*(req->rq_header + smb_wct) * 2))); | 
|  | 521 |  | 
|  | 522 | result = 0; | 
|  | 523 | req->rq_iov[0].iov_base = NULL; | 
|  | 524 | req->rq_rlen = 0; | 
|  | 525 | if (req->rq_callback) | 
|  | 526 | req->rq_callback(req); | 
|  | 527 | else if (req->rq_setup_read) | 
|  | 528 | result = req->rq_setup_read(req); | 
|  | 529 | if (result < 0) { | 
|  | 530 | server->rstate = SMB_RECV_DROP; | 
|  | 531 | return result; | 
|  | 532 | } | 
|  | 533 |  | 
|  | 534 | server->rstate = req->rq_rlen > 0 ? SMB_RECV_DATA : SMB_RECV_END; | 
|  | 535 |  | 
|  | 536 | req->rq_bytes_recvd = 0;	// recvd out of the iov | 
|  | 537 |  | 
|  | 538 | VERBOSE("rlen: %d\n", req->rq_rlen); | 
|  | 539 | if (req->rq_rlen < 0) { | 
|  | 540 | PARANOIA("Parameters read beyond end of packet!\n"); | 
|  | 541 | server->rstate = SMB_RECV_END; | 
|  | 542 | return -EIO; | 
|  | 543 | } | 
|  | 544 | return 0; | 
|  | 545 | } | 
|  | 546 |  | 
|  | 547 | /* | 
|  | 548 | * Reads the SMB data | 
|  | 549 | */ | 
|  | 550 | static int smb_recv_data(struct smb_sb_info *server, struct smb_request *req) | 
|  | 551 | { | 
|  | 552 | int result; | 
|  | 553 |  | 
|  | 554 | result = smb_receive(server, req); | 
|  | 555 | if (result < 0) | 
|  | 556 | goto out; | 
|  | 557 | if (req->rq_bytes_recvd < req->rq_rlen) | 
|  | 558 | goto out; | 
|  | 559 | server->rstate = SMB_RECV_END; | 
|  | 560 | out: | 
|  | 561 | VERBOSE("result: %d\n", result); | 
|  | 562 | return result; | 
|  | 563 | } | 
|  | 564 |  | 
|  | 565 | /* | 
|  | 566 | * Receive a transaction2 response | 
|  | 567 | * Return: 0 if the response has been fully read | 
|  | 568 | *         1 if there are further "fragments" to read | 
|  | 569 | *        <0 if there is an error | 
|  | 570 | */ | 
|  | 571 | static int smb_recv_trans2(struct smb_sb_info *server, struct smb_request *req) | 
|  | 572 | { | 
|  | 573 | unsigned char *inbuf; | 
|  | 574 | unsigned int parm_disp, parm_offset, parm_count, parm_tot; | 
|  | 575 | unsigned int data_disp, data_offset, data_count, data_tot; | 
|  | 576 | int hdrlen = SMB_HEADER_LEN + req->rq_resp_wct*2 - 2; | 
|  | 577 |  | 
|  | 578 | VERBOSE("handling trans2\n"); | 
|  | 579 |  | 
|  | 580 | inbuf = req->rq_header; | 
|  | 581 | data_tot    = WVAL(inbuf, smb_tdrcnt); | 
|  | 582 | parm_tot    = WVAL(inbuf, smb_tprcnt); | 
|  | 583 | parm_disp   = WVAL(inbuf, smb_prdisp); | 
|  | 584 | parm_offset = WVAL(inbuf, smb_proff); | 
|  | 585 | parm_count  = WVAL(inbuf, smb_prcnt); | 
|  | 586 | data_disp   = WVAL(inbuf, smb_drdisp); | 
|  | 587 | data_offset = WVAL(inbuf, smb_droff); | 
|  | 588 | data_count  = WVAL(inbuf, smb_drcnt); | 
|  | 589 |  | 
|  | 590 | /* Modify offset for the split header/buffer we use */ | 
|  | 591 | if (data_count || data_offset) { | 
|  | 592 | if (unlikely(data_offset < hdrlen)) | 
|  | 593 | goto out_bad_data; | 
|  | 594 | else | 
|  | 595 | data_offset -= hdrlen; | 
|  | 596 | } | 
|  | 597 | if (parm_count || parm_offset) { | 
|  | 598 | if (unlikely(parm_offset < hdrlen)) | 
|  | 599 | goto out_bad_parm; | 
|  | 600 | else | 
|  | 601 | parm_offset -= hdrlen; | 
|  | 602 | } | 
|  | 603 |  | 
|  | 604 | if (parm_count == parm_tot && data_count == data_tot) { | 
|  | 605 | /* | 
|  | 606 | * This packet has all the trans2 data. | 
|  | 607 | * | 
|  | 608 | * We setup the request so that this will be the common | 
|  | 609 | * case. It may be a server error to not return a | 
|  | 610 | * response that fits. | 
|  | 611 | */ | 
|  | 612 | VERBOSE("single trans2 response  " | 
|  | 613 | "dcnt=%u, pcnt=%u, doff=%u, poff=%u\n", | 
|  | 614 | data_count, parm_count, | 
|  | 615 | data_offset, parm_offset); | 
|  | 616 | req->rq_ldata = data_count; | 
|  | 617 | req->rq_lparm = parm_count; | 
|  | 618 | req->rq_data = req->rq_buffer + data_offset; | 
|  | 619 | req->rq_parm = req->rq_buffer + parm_offset; | 
|  | 620 | if (unlikely(parm_offset + parm_count > req->rq_rlen)) | 
|  | 621 | goto out_bad_parm; | 
|  | 622 | if (unlikely(data_offset + data_count > req->rq_rlen)) | 
|  | 623 | goto out_bad_data; | 
|  | 624 | return 0; | 
|  | 625 | } | 
|  | 626 |  | 
|  | 627 | VERBOSE("multi trans2 response  " | 
|  | 628 | "frag=%d, dcnt=%u, pcnt=%u, doff=%u, poff=%u\n", | 
|  | 629 | req->rq_fragment, | 
|  | 630 | data_count, parm_count, | 
|  | 631 | data_offset, parm_offset); | 
|  | 632 |  | 
|  | 633 | if (!req->rq_fragment) { | 
|  | 634 | int buf_len; | 
|  | 635 |  | 
|  | 636 | /* We got the first trans2 fragment */ | 
|  | 637 | req->rq_fragment = 1; | 
|  | 638 | req->rq_total_data = data_tot; | 
|  | 639 | req->rq_total_parm = parm_tot; | 
|  | 640 | req->rq_ldata = 0; | 
|  | 641 | req->rq_lparm = 0; | 
|  | 642 |  | 
|  | 643 | buf_len = data_tot + parm_tot; | 
|  | 644 | if (buf_len > SMB_MAX_PACKET_SIZE) | 
|  | 645 | goto out_too_long; | 
|  | 646 |  | 
|  | 647 | req->rq_trans2bufsize = buf_len; | 
| Pekka Enberg | d063389 | 2006-01-14 13:21:13 -0800 | [diff] [blame] | 648 | req->rq_trans2buffer = kzalloc(buf_len, GFP_NOFS); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 649 | if (!req->rq_trans2buffer) | 
|  | 650 | goto out_no_mem; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 651 |  | 
|  | 652 | req->rq_parm = req->rq_trans2buffer; | 
|  | 653 | req->rq_data = req->rq_trans2buffer + parm_tot; | 
|  | 654 | } else if (unlikely(req->rq_total_data < data_tot || | 
|  | 655 | req->rq_total_parm < parm_tot)) | 
|  | 656 | goto out_data_grew; | 
|  | 657 |  | 
|  | 658 | if (unlikely(parm_disp + parm_count > req->rq_total_parm || | 
|  | 659 | parm_offset + parm_count > req->rq_rlen)) | 
|  | 660 | goto out_bad_parm; | 
|  | 661 | if (unlikely(data_disp + data_count > req->rq_total_data || | 
|  | 662 | data_offset + data_count > req->rq_rlen)) | 
|  | 663 | goto out_bad_data; | 
|  | 664 |  | 
|  | 665 | inbuf = req->rq_buffer; | 
|  | 666 | memcpy(req->rq_parm + parm_disp, inbuf + parm_offset, parm_count); | 
|  | 667 | memcpy(req->rq_data + data_disp, inbuf + data_offset, data_count); | 
|  | 668 |  | 
|  | 669 | req->rq_ldata += data_count; | 
|  | 670 | req->rq_lparm += parm_count; | 
|  | 671 |  | 
|  | 672 | /* | 
|  | 673 | * Check whether we've received all of the data. Note that | 
|  | 674 | * we use the packet totals -- total lengths might shrink! | 
|  | 675 | */ | 
|  | 676 | if (req->rq_ldata >= data_tot && req->rq_lparm >= parm_tot) { | 
|  | 677 | req->rq_ldata = data_tot; | 
|  | 678 | req->rq_lparm = parm_tot; | 
|  | 679 | return 0; | 
|  | 680 | } | 
|  | 681 | return 1; | 
|  | 682 |  | 
|  | 683 | out_too_long: | 
|  | 684 | printk(KERN_ERR "smb_trans2: data/param too long, data=%u, parm=%u\n", | 
|  | 685 | data_tot, parm_tot); | 
|  | 686 | goto out_EIO; | 
|  | 687 | out_no_mem: | 
|  | 688 | printk(KERN_ERR "smb_trans2: couldn't allocate data area of %d bytes\n", | 
|  | 689 | req->rq_trans2bufsize); | 
|  | 690 | req->rq_errno = -ENOMEM; | 
|  | 691 | goto out; | 
|  | 692 | out_data_grew: | 
|  | 693 | printk(KERN_ERR "smb_trans2: data/params grew!\n"); | 
|  | 694 | goto out_EIO; | 
|  | 695 | out_bad_parm: | 
|  | 696 | printk(KERN_ERR "smb_trans2: invalid parms, disp=%u, cnt=%u, tot=%u, ofs=%u\n", | 
|  | 697 | parm_disp, parm_count, parm_tot, parm_offset); | 
|  | 698 | goto out_EIO; | 
|  | 699 | out_bad_data: | 
|  | 700 | printk(KERN_ERR "smb_trans2: invalid data, disp=%u, cnt=%u, tot=%u, ofs=%u\n", | 
|  | 701 | data_disp, data_count, data_tot, data_offset); | 
|  | 702 | out_EIO: | 
|  | 703 | req->rq_errno = -EIO; | 
|  | 704 | out: | 
|  | 705 | return req->rq_errno; | 
|  | 706 | } | 
|  | 707 |  | 
|  | 708 | /* | 
|  | 709 | * State machine for receiving responses. We handle the fact that we can't | 
|  | 710 | * read the full response in one try by having states telling us how much we | 
|  | 711 | * have read. | 
|  | 712 | * | 
|  | 713 | * Must be called with the server lock held (only called from smbiod). | 
|  | 714 | * | 
|  | 715 | * Return: <0 on error | 
|  | 716 | */ | 
|  | 717 | int smb_request_recv(struct smb_sb_info *server) | 
|  | 718 | { | 
|  | 719 | struct smb_request *req = NULL; | 
|  | 720 | int result = 0; | 
|  | 721 |  | 
|  | 722 | if (smb_recv_available(server) <= 0) | 
|  | 723 | return 0; | 
|  | 724 |  | 
|  | 725 | VERBOSE("state: %d\n", server->rstate); | 
|  | 726 | switch (server->rstate) { | 
|  | 727 | case SMB_RECV_DROP: | 
|  | 728 | result = smb_receive_drop(server); | 
|  | 729 | if (result < 0) | 
|  | 730 | break; | 
|  | 731 | if (server->rstate == SMB_RECV_DROP) | 
|  | 732 | break; | 
|  | 733 | server->rstate = SMB_RECV_START; | 
|  | 734 | /* fallthrough */ | 
|  | 735 | case SMB_RECV_START: | 
|  | 736 | server->smb_read = 0; | 
|  | 737 | server->rstate = SMB_RECV_HEADER; | 
|  | 738 | /* fallthrough */ | 
|  | 739 | case SMB_RECV_HEADER: | 
|  | 740 | result = smb_receive_header(server); | 
|  | 741 | if (result < 0) | 
|  | 742 | break; | 
|  | 743 | if (server->rstate == SMB_RECV_HEADER) | 
|  | 744 | break; | 
|  | 745 | if (! (*(server->header + smb_flg) & SMB_FLAGS_REPLY) ) { | 
|  | 746 | server->rstate = SMB_RECV_REQUEST; | 
|  | 747 | break; | 
|  | 748 | } | 
|  | 749 | if (server->rstate != SMB_RECV_HCOMPLETE) | 
|  | 750 | break; | 
|  | 751 | /* fallthrough */ | 
|  | 752 | case SMB_RECV_HCOMPLETE: | 
|  | 753 | req = find_request(server, WVAL(server->header, smb_mid)); | 
|  | 754 | if (!req) | 
|  | 755 | break; | 
|  | 756 | smb_init_request(server, req); | 
|  | 757 | req->rq_rcls = *(req->rq_header + smb_rcls); | 
|  | 758 | req->rq_err  = WVAL(req->rq_header, smb_err); | 
|  | 759 | if (server->rstate != SMB_RECV_PARAM) | 
|  | 760 | break; | 
|  | 761 | /* fallthrough */ | 
|  | 762 | case SMB_RECV_PARAM: | 
|  | 763 | if (!req) | 
|  | 764 | req = find_request(server,WVAL(server->header,smb_mid)); | 
|  | 765 | if (!req) | 
|  | 766 | break; | 
|  | 767 | result = smb_recv_param(server, req); | 
|  | 768 | if (result < 0) | 
|  | 769 | break; | 
|  | 770 | if (server->rstate != SMB_RECV_DATA) | 
|  | 771 | break; | 
|  | 772 | /* fallthrough */ | 
|  | 773 | case SMB_RECV_DATA: | 
|  | 774 | if (!req) | 
|  | 775 | req = find_request(server,WVAL(server->header,smb_mid)); | 
|  | 776 | if (!req) | 
|  | 777 | break; | 
|  | 778 | result = smb_recv_data(server, req); | 
|  | 779 | if (result < 0) | 
|  | 780 | break; | 
|  | 781 | break; | 
|  | 782 |  | 
|  | 783 | /* We should never be called with any of these states */ | 
|  | 784 | case SMB_RECV_END: | 
|  | 785 | case SMB_RECV_REQUEST: | 
| Adrian Bunk | ccb6e36 | 2005-11-07 00:59:23 -0800 | [diff] [blame] | 786 | BUG(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 787 | } | 
|  | 788 |  | 
|  | 789 | if (result < 0) { | 
|  | 790 | /* We saw an error */ | 
|  | 791 | return result; | 
|  | 792 | } | 
|  | 793 |  | 
|  | 794 | if (server->rstate != SMB_RECV_END) | 
|  | 795 | return 0; | 
|  | 796 |  | 
|  | 797 | result = 0; | 
|  | 798 | if (req->rq_trans2_command && req->rq_rcls == SUCCESS) | 
|  | 799 | result = smb_recv_trans2(server, req); | 
|  | 800 |  | 
|  | 801 | /* | 
|  | 802 | * Response completely read. Drop any extra bytes sent by the server. | 
|  | 803 | * (Yes, servers sometimes add extra bytes to responses) | 
|  | 804 | */ | 
|  | 805 | VERBOSE("smb_len: %d   smb_read: %d\n", | 
|  | 806 | server->smb_len, server->smb_read); | 
|  | 807 | if (server->smb_read < server->smb_len) | 
|  | 808 | smb_receive_drop(server); | 
|  | 809 |  | 
|  | 810 | server->rstate = SMB_RECV_START; | 
|  | 811 |  | 
|  | 812 | if (!result) { | 
|  | 813 | list_del_init(&req->rq_queue); | 
|  | 814 | req->rq_flags |= SMB_REQ_RECEIVED; | 
|  | 815 | smb_rput(req); | 
|  | 816 | wake_up_interruptible(&req->rq_wait); | 
|  | 817 | } | 
|  | 818 | return 0; | 
|  | 819 | } |