fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / fs / smbfs / request.c
index 563f7a1..a4bcae8 100644 (file)
@@ -25,7 +25,9 @@
 #define ROUND_UP(x) (((x)+3) & ~3)
 
 /* cache for request structures */
-static kmem_cache_t *req_cachep;
+static struct kmem_cache *req_cachep;
+
+static int smb_request_send_req(struct smb_request *req);
 
 /*
   /proc/slabinfo:
@@ -47,8 +49,7 @@ int smb_init_request_cache(void)
 
 void smb_destroy_request_cache(void)
 {
-       if (kmem_cache_destroy(req_cachep))
-               printk(KERN_INFO "smb_destroy_request_cache: not all structures were freed\n");
+       kmem_cache_destroy(req_cachep);
 }
 
 /*
@@ -60,13 +61,13 @@ static struct smb_request *smb_do_alloc_request(struct smb_sb_info *server,
        struct smb_request *req;
        unsigned char *buf = NULL;
 
-       req = kmem_cache_alloc(req_cachep, SLAB_KERNEL);
+       req = kmem_cache_alloc(req_cachep, GFP_KERNEL);
        VERBOSE("allocating request: %p\n", req);
        if (!req)
                goto out;
 
        if (bufsize > 0) {
-               buf = smb_kmalloc(bufsize, GFP_NOFS);
+               buf = kmalloc(bufsize, GFP_NOFS);
                if (!buf) {
                        kmem_cache_free(req_cachep, req);
                        return NULL;
@@ -122,9 +123,8 @@ static void smb_free_request(struct smb_request *req)
 {
        atomic_dec(&req->rq_server->nr_requests);
        if (req->rq_buffer && !(req->rq_flags & SMB_REQ_STATIC))
-               smb_kfree(req->rq_buffer);
-       if (req->rq_trans2buffer)
-               smb_kfree(req->rq_trans2buffer);
+               kfree(req->rq_buffer);
+       kfree(req->rq_trans2buffer);
        kmem_cache_free(req_cachep, req);
 }
 
@@ -132,7 +132,7 @@ static void smb_free_request(struct smb_request *req)
  * What prevents a rget to race with a rput? The count must never drop to zero
  * while it is in use. Only rput if it is ok that it is free'd.
  */
-void smb_rget(struct smb_request *req)
+static void smb_rget(struct smb_request *req)
 {
        atomic_inc(&req->rq_count);
 }
@@ -181,8 +181,7 @@ static int smb_setup_request(struct smb_request *req)
        req->rq_err = 0;
        req->rq_errno = 0;
        req->rq_fragment = 0;
-       if (req->rq_trans2buffer)
-               smb_kfree(req->rq_trans2buffer);
+       kfree(req->rq_trans2buffer);
 
        return 0;
 }
@@ -339,9 +338,11 @@ int smb_add_request(struct smb_request *req)
                /*
                 * 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 (!(req->rq_flags & SMB_REQ_RECEIVED)) {
+               if (!list_empty(&req->rq_queue)) {
                        list_del_init(&req->rq_queue);
                        smb_rput(req);
                }
@@ -379,7 +380,7 @@ int smb_add_request(struct smb_request *req)
  * Send a request and place it on the recvq if successfully sent.
  * Must be called with the server lock held.
  */
-int smb_request_send_req(struct smb_request *req)
+static int smb_request_send_req(struct smb_request *req)
 {
        struct smb_sb_info *server = req->rq_server;
        int result;
@@ -398,8 +399,7 @@ int smb_request_send_req(struct smb_request *req)
        if (!(req->rq_flags & SMB_REQ_TRANSMITTED))
                goto out;
 
-       list_del_init(&req->rq_queue);
-       list_add_tail(&req->rq_queue, &server->recvq);
+       list_move_tail(&req->rq_queue, &server->recvq);
        result = 1;
 out:
        return result;
@@ -433,8 +433,7 @@ int smb_request_send_server(struct smb_sb_info *server)
        result = smb_request_send_req(req);
        if (result < 0) {
                server->conn_error = result;
-               list_del_init(&req->rq_queue);
-               list_add(&req->rq_queue, &server->xmitq);
+               list_move(&req->rq_queue, &server->xmitq);
                result = -EIO;
                goto out;
        }
@@ -588,8 +587,18 @@ static int smb_recv_trans2(struct smb_sb_info *server, struct smb_request *req)
        data_count  = WVAL(inbuf, smb_drcnt);
 
        /* Modify offset for the split header/buffer we use */
-       data_offset -= hdrlen;
-       parm_offset -= hdrlen;
+       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) {
                /*
@@ -600,18 +609,22 @@ static int smb_recv_trans2(struct smb_sb_info *server, struct smb_request *req)
                 * response that fits.
                 */
                VERBOSE("single trans2 response  "
-                       "dcnt=%d, pcnt=%d, doff=%d, poff=%d\n",
+                       "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=%d, pcnt=%d, doff=%d, poff=%d\n",
+               "frag=%d, dcnt=%u, pcnt=%u, doff=%u, poff=%u\n",
                req->rq_fragment,
                data_count, parm_count,
                data_offset, parm_offset);
@@ -631,19 +644,21 @@ static int smb_recv_trans2(struct smb_sb_info *server, struct smb_request *req)
                        goto out_too_long;
 
                req->rq_trans2bufsize = buf_len;
-               req->rq_trans2buffer = smb_kmalloc(buf_len, GFP_NOFS);
+               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 (req->rq_total_data < data_tot ||
-                  req->rq_total_parm < parm_tot)
+       } else if (unlikely(req->rq_total_data < data_tot ||
+                           req->rq_total_parm < parm_tot))
                goto out_data_grew;
 
-       if (parm_disp + parm_count > req->rq_total_parm)
+       if (unlikely(parm_disp + parm_count > req->rq_total_parm ||
+                    parm_offset + parm_count > req->rq_rlen))
                goto out_bad_parm;
-       if (data_disp + data_count > req->rq_total_data)
+       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;
@@ -657,15 +672,17 @@ static int smb_recv_trans2(struct smb_sb_info *server, struct smb_request *req)
         * 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)
+       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=%d, parm=%d\n",
+       printk(KERN_ERR "smb_trans2: data/param too long, data=%u, parm=%u\n",
                data_tot, parm_tot);
-       req->rq_errno = -EIO;
-       goto out;
+       goto out_EIO;
 out_no_mem:
        printk(KERN_ERR "smb_trans2: couldn't allocate data area of %d bytes\n",
               req->rq_trans2bufsize);
@@ -673,16 +690,15 @@ out_no_mem:
        goto out;
 out_data_grew:
        printk(KERN_ERR "smb_trans2: data/params grew!\n");
-       req->rq_errno = -EIO;
-       goto out;
+       goto out_EIO;
 out_bad_parm:
-       printk(KERN_ERR "smb_trans2: invalid parms, disp=%d, cnt=%d, tot=%d\n",
-              parm_disp, parm_count, parm_tot);
-       req->rq_errno = -EIO;
-       goto out;
+       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=%d, cnt=%d, tot=%d\n",
-              data_disp, data_count, data_tot);
+       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;
@@ -766,8 +782,7 @@ int smb_request_recv(struct smb_sb_info *server)
                /* We should never be called with any of these states */
        case SMB_RECV_END:
        case SMB_RECV_REQUEST:
-               server->rstate = SMB_RECV_END;
-               break;
+               BUG();
        }
 
        if (result < 0) {