vserver 1.9.5.x5
[linux-2.6.git] / fs / cifs / misc.c
index ca96ffe..c79d8f7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/misc.c
  *
- *   Copyright (C) International Business Machines  Corp., 2002,2003
+ *   Copyright (C) International Business Machines  Corp., 2002,2004
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -21,6 +21,7 @@
 
 #include <linux/slab.h>
 #include <linux/ctype.h>
+#include <linux/mempool.h>
 #include "cifspdu.h"
 #include "cifsglob.h"
 #include "cifsproto.h"
@@ -28,7 +29,8 @@
 #include "smberr.h"
 #include "nterr.h"
 
-extern kmem_cache_t *cifs_req_cachep;
+extern mempool_t *cifs_sm_req_poolp;
+extern mempool_t *cifs_req_poolp;
 extern struct task_struct * oplockThread;
 
 __u16 GlobalMid;               /* multiplex id - rotating counter */
@@ -122,6 +124,9 @@ tconInfoAlloc(void)
                ret_buf->tidStatus = CifsNew;
                INIT_LIST_HEAD(&ret_buf->openFileList);
                init_MUTEX(&ret_buf->tconSem);
+#ifdef CONFIG_CIFS_STATS
+               spin_lock_init(&ret_buf->stat_lock);
+#endif
                write_unlock(&GlobalSMBSeslock);
        }
        return ret_buf;
@@ -146,18 +151,19 @@ tconInfoFree(struct cifsTconInfo *buf_to_free)
 struct smb_hdr *
 cifs_buf_get(void)
 {
-       struct smb_hdr *ret_buf = 0;
+       struct smb_hdr *ret_buf = NULL;
 
 /* We could use negotiated size instead of max_msgsize - 
    but it may be more efficient to always alloc same size 
    albeit slightly larger than necessary and maxbuffersize 
    defaults to this and can not be bigger */
        ret_buf =
-           (struct smb_hdr *) kmem_cache_alloc(cifs_req_cachep, SLAB_KERNEL | SLAB_NOFS);
+           (struct smb_hdr *) mempool_alloc(cifs_req_poolp, SLAB_KERNEL | SLAB_NOFS);
 
        /* clear the first few header bytes */
+       /* for most paths, more is cleared in header_assemble */
        if (ret_buf) {
-               memset(ret_buf, 0, sizeof (struct smb_hdr));
+               memset(ret_buf, 0, sizeof(struct smb_hdr) + 3);
                atomic_inc(&bufAllocCount);
        }
 
@@ -169,30 +175,58 @@ cifs_buf_release(void *buf_to_free)
 {
 
        if (buf_to_free == NULL) {
-               cFYI(1, ("Null buffer passed to cifs_buf_release"));
+               /* cFYI(1, ("Null buffer passed to cifs_buf_release"));*/
                return;
        }
-       kmem_cache_free(cifs_req_cachep, buf_to_free);
+       mempool_free(buf_to_free,cifs_req_poolp);
 
        atomic_dec(&bufAllocCount);
        return;
 }
 
+struct smb_hdr *
+cifs_small_buf_get(void)
+{
+       struct smb_hdr *ret_buf = NULL;
+
+/* We could use negotiated size instead of max_msgsize - 
+   but it may be more efficient to always alloc same size 
+   albeit slightly larger than necessary and maxbuffersize 
+   defaults to this and can not be bigger */
+       ret_buf =
+           (struct smb_hdr *) mempool_alloc(cifs_sm_req_poolp, SLAB_KERNEL | SLAB_NOFS);
+       if (ret_buf) {
+       /* No need to clear memory here, cleared in header assemble */
+       /*      memset(ret_buf, 0, sizeof(struct smb_hdr) + 27);*/
+               atomic_inc(&smBufAllocCount);
+       }
+       return ret_buf;
+}
+
+void
+cifs_small_buf_release(void *buf_to_free)
+{
+
+       if (buf_to_free == NULL) {
+               cFYI(1, ("Null buffer passed to cifs_small_buf_release"));
+               return;
+       }
+       mempool_free(buf_to_free,cifs_sm_req_poolp);
+
+       atomic_dec(&smBufAllocCount);
+       return;
+}
+
 void
 header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
                const struct cifsTconInfo *treeCon, int word_count
-               /* length of fixed section (word count) in two byte units  */
-    )
+               /* length of fixed section (word count) in two byte units  */)
 {
-       int i;
-       __u32 tmp;
        struct list_head* temp_item;
        struct cifsSesInfo * ses;
        char *temp = (char *) buffer;
 
-       for (i = 0; i < MAX_CIFS_HDR_SIZE; i++) {
-               temp[i] = 0;    /* BB is this needed ?? */
-       }
+       memset(temp,0,MAX_CIFS_HDR_SIZE);
 
        buffer->smb_buf_length =
            (2 * word_count) + sizeof (struct smb_hdr) -
@@ -207,10 +241,8 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
        buffer->Command = smb_command;
        buffer->Flags = 0x00;   /* case sensitive */
        buffer->Flags2 = SMBFLG2_KNOWS_LONG_NAMES;
-       tmp = cpu_to_le32(current->tgid);
-       buffer->Pid = tmp & 0xFFFF;
-       tmp >>= 16;
-       buffer->PidHigh = tmp & 0xFFFF;
+       buffer->Pid = cpu_to_le16((__u16)current->tgid);
+       buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16));
        spin_lock(&GlobalMid_Lock);
        GlobalMid++;
        buffer->Mid = GlobalMid;
@@ -272,7 +304,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
                }
                if (treeCon->Flags & SMB_SHARE_IS_IN_DFS)
                        buffer->Flags2 |= SMBFLG2_DFS;
-               if(treeCon->ses->server)
+               if((treeCon->ses) && (treeCon->ses->server))
                        if(treeCon->ses->server->secMode & 
                          (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
                                buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
@@ -288,7 +320,7 @@ checkSMBhdr(struct smb_hdr *smb, __u16 mid)
 {
        /* Make sure that this really is an SMB, that it is a response, 
           and that the message ids match */
-       if ((*(unsigned int *) smb->Protocol == cpu_to_le32(0x424d53ff)) && 
+       if ((*(__le32 *) smb->Protocol == cpu_to_le32(0x424d53ff)) && 
                (mid == smb->Mid)) {    
                if(smb->Flags & SMBFLG_RESPONSE)
                        return 0;                    
@@ -300,7 +332,7 @@ checkSMBhdr(struct smb_hdr *smb, __u16 mid)
                                cERROR(1, ("Rcvd Request not response "));         
                }
        } else { /* bad signature or mid */
-               if (*(unsigned int *) smb->Protocol != cpu_to_le32(0x424d53ff))
+               if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff))
                        cERROR(1,
                               ("Bad protocol string signature header %x ",
                                *(unsigned int *) smb->Protocol));
@@ -314,12 +346,12 @@ checkSMBhdr(struct smb_hdr *smb, __u16 mid)
 int
 checkSMB(struct smb_hdr *smb, __u16 mid, int length)
 {
+       __u32 len = be32_to_cpu(smb->smb_buf_length);
        cFYI(0,
             ("Entering checkSMB with Length: %x, smb_buf_length: %x ",
-             length, ntohl(smb->smb_buf_length)));
-       if (((unsigned int)length < 2 + sizeof (struct smb_hdr))
-           || (ntohl(smb->smb_buf_length) >
-               CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE - 4)) {
+             length, len));
+       if (((unsigned int)length < 2 + sizeof (struct smb_hdr)) ||
+           (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4)) {
                if ((unsigned int)length < 2 + sizeof (struct smb_hdr)) {
                        cERROR(1, ("Length less than 2 + sizeof smb_hdr "));
                        if (((unsigned int)length >= sizeof (struct smb_hdr) - 1)
@@ -327,10 +359,9 @@ checkSMB(struct smb_hdr *smb, __u16 mid, int length)
                                return 0;       /* some error cases do not return wct and bcc */
 
                }
-               if (ntohl(smb->smb_buf_length) >
-                   CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE - 4)
+               if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4)
                        cERROR(1,
-                              ("smb_buf_length greater than CIFS_MAX_MSGSIZE ... "));
+                              ("smb_buf_length greater than MaxBufSize"));
                cERROR(1,
                       ("bad smb detected. Illegal length. The mid=%d",
                        smb->Mid));
@@ -340,8 +371,8 @@ checkSMB(struct smb_hdr *smb, __u16 mid, int length)
        if (checkSMBhdr(smb, mid))
                return 1;
 
-       if ((4 + ntohl(smb->smb_buf_length) != smbCalcSize(smb))
-           || (4 + ntohl(smb->smb_buf_length) != (unsigned int)length)) {
+       if ((4 + len != smbCalcSize(smb))
+           || (4 + len != (unsigned int)length)) {
                return 0;
        } else {
                cERROR(1, ("smbCalcSize %x ", smbCalcSize(smb)));
@@ -359,8 +390,29 @@ is_valid_oplock_break(struct smb_hdr *buf)
        struct cifsTconInfo *tcon;
        struct cifsFileInfo *netfile;
 
-       /* could add check for smb response flag 0x80 */
-       cFYI(1,("Checking for oplock break"));    
+       cFYI(1,("Checking for oplock break or dnotify response"));
+       if((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) &&
+          (pSMB->hdr.Flags & SMBFLG_RESPONSE)) {
+               struct smb_com_transaction_change_notify_rsp * pSMBr =
+                       (struct smb_com_transaction_change_notify_rsp *)buf;
+               struct file_notify_information * pnotify;
+               __u32 data_offset = 0;
+               if(pSMBr->ByteCount > sizeof(struct file_notify_information)) {
+                       data_offset = le32_to_cpu(pSMBr->DataOffset);
+
+                       pnotify = (struct file_notify_information *)((char *)&pSMBr->hdr.Protocol
+                               + data_offset);
+                       cFYI(1,("dnotify on %s with action: 0x%x",pnotify->FileName,
+                               pnotify->Action));  /* BB removeme BB */
+                    /*   cifs_dump_mem("Received notify Data is: ",buf,sizeof(struct smb_hdr)+60); */
+                       return TRUE;
+               }
+               if(pSMBr->hdr.Status.CifsError) {
+                       cFYI(1,("notify err 0x%d",pSMBr->hdr.Status.CifsError));
+                       return TRUE;
+               }
+               return FALSE;
+       }  
        if(pSMB->hdr.Command != SMB_COM_LOCKING_ANDX)
                return FALSE;
        if(pSMB->hdr.Flags & SMBFLG_RESPONSE) {
@@ -391,6 +443,9 @@ is_valid_oplock_break(struct smb_hdr *buf)
        list_for_each(tmp, &GlobalTreeConnectionList) {
                tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
                if (tcon->tid == buf->Tid) {
+#ifdef CONFIG_CIFS_STATS
+                       atomic_inc(&tcon->num_oplock_brks);
+#endif
                        list_for_each(tmp1,&tcon->openFileList){
                                netfile = list_entry(tmp1,struct cifsFileInfo,tlist);
                                if(pSMB->Fid == netfile->netfid) {