Merge with /pub/scm/linux/kernel/git/sfrench/cifs-2.6.git/
authorSteve French <sfrench@us.ibm.com>
Fri, 21 Oct 2005 15:39:12 +0000 (08:39 -0700)
committerSteve French <sfrench@us.ibm.com>
Fri, 21 Oct 2005 15:39:12 +0000 (08:39 -0700)
1  2 
fs/cifs/cifsfs.c
fs/cifs/connect.c
fs/cifs/dir.c

diff --combined fs/cifs/cifsfs.c
@@@ -59,6 -59,8 +59,8 @@@ unsigned int ntlmv2_support = 0
  unsigned int sign_CIFS_PDUs = 1;
  extern struct task_struct * oplockThread; /* remove sparse warning */
  struct task_struct * oplockThread = NULL;
+ extern struct task_struct * dnotifyThread; /* remove sparse warning */
+ struct task_struct * dnotifyThread = NULL;
  unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE;
  module_param(CIFSMaxBufSize, int, 0);
  MODULE_PARM_DESC(CIFSMaxBufSize,"Network buffer size (not including header). Default: 16384 Range: 8192 to 130048");
@@@ -73,6 -75,7 +75,7 @@@ module_param(cifs_max_pending, int, 0)
  MODULE_PARM_DESC(cifs_max_pending,"Simultaneous requests to server. Default: 50 Range: 2 to 256");
  
  static DECLARE_COMPLETION(cifs_oplock_exited);
+ static DECLARE_COMPLETION(cifs_dnotify_exited);
  
  extern mempool_t *cifs_sm_req_poolp;
  extern mempool_t *cifs_req_poolp;
@@@ -202,6 -205,10 +205,10 @@@ cifs_statfs(struct super_block *sb, str
  #endif /* CIFS_EXPERIMENTAL */
        rc = CIFSSMBQFSInfo(xid, pTcon, buf);
  
+       /* Old Windows servers do not support level 103, retry with level 
+          one if old server failed the previous call */ 
+       if(rc)
+               rc = SMBOldQFSInfo(xid, pTcon, buf);
        /*     
           int f_type;
           __fsid_t f_fsid;
@@@ -253,7 -260,7 +260,7 @@@ cifs_alloc_inode(struct super_block *sb
        cifs_inode->clientCanCacheAll = FALSE;
        cifs_inode->vfs_inode.i_blksize = CIFS_MAX_MSGSIZE;
        cifs_inode->vfs_inode.i_blkbits = 14;  /* 2**14 = CIFS_MAX_MSGSIZE */
+       cifs_inode->vfs_inode.i_flags = S_NOATIME | S_NOCMTIME;
        INIT_LIST_HEAD(&cifs_inode->openFileList);
        return &cifs_inode->vfs_inode;
  }
@@@ -398,6 -405,34 +405,34 @@@ static struct quotactl_ops cifs_quotact
  };
  #endif
  
+ static void cifs_umount_begin(struct super_block * sblock)
+ {
+       struct cifs_sb_info *cifs_sb;
+       struct cifsTconInfo * tcon;
+       cifs_sb = CIFS_SB(sblock);
+       if(cifs_sb == NULL)
+               return;
+       tcon = cifs_sb->tcon;
+       if(tcon == NULL)
+               return;
+       down(&tcon->tconSem);
+       if (atomic_read(&tcon->useCount) == 1)
+               tcon->tidStatus = CifsExiting;
+       up(&tcon->tconSem);
+       if(tcon->ses && tcon->ses->server)
+       {
+               cERROR(1,("wake up tasks now - umount begin not complete"));
+               wake_up_all(&tcon->ses->server->request_q);
+       }
+ /* BB FIXME - finish add checks for tidStatus BB */
+       return;
+ }
+       
  static int cifs_remount(struct super_block *sb, int *flags, char *data)
  {
        *flags |= MS_NODIRATIME;
@@@ -415,7 -450,7 +450,7 @@@ struct super_operations cifs_super_ops 
     unless later we add lazy close of inodes or unless the kernel forgets to call
     us with the same number of releases (closes) as opens */
        .show_options = cifs_show_options,
- /*    .umount_begin   = cifs_umount_begin, *//* consider adding in the future */
+ /*    .umount_begin   = cifs_umount_begin, */ /* BB finish in the future */
        .remount_fs = cifs_remount,
  };
  
@@@ -781,11 -816,9 +816,9 @@@ static int cifs_oplock_thread(void * du
  
        oplockThread = current;
        do {
 -              if(try_to_freeze()) 
 +              if (try_to_freeze()) 
                        continue;
-               set_current_state(TASK_INTERRUPTIBLE);
                
-               schedule_timeout(1*HZ);  
                spin_lock(&GlobalMid_Lock);
                if(list_empty(&GlobalOplock_Q)) {
                        spin_unlock(&GlobalMid_Lock);
                                }
                        } else
                                spin_unlock(&GlobalMid_Lock);
+                       set_current_state(TASK_INTERRUPTIBLE);
+                       schedule_timeout(1);  /* yield in case q were corrupt */
                }
        } while(!signal_pending(current));
-       complete_and_exit (&cifs_oplock_exited, 0);
        oplockThread = NULL;
+       complete_and_exit (&cifs_oplock_exited, 0);
+ }
+ static int cifs_dnotify_thread(void * dummyarg)
+ {
+       daemonize("cifsdnotifyd");
+       allow_signal(SIGTERM);
+       dnotifyThread = current;
+       do {
+               if(try_to_freeze())
+                       continue;
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(39*HZ);
+       } while(!signal_pending(current));
+       complete_and_exit (&cifs_dnotify_exited, 0);
  }
  
  static int __init
@@@ -851,6 -901,10 +901,10 @@@ init_cifs(void
        INIT_LIST_HEAD(&GlobalSMBSessionList);
        INIT_LIST_HEAD(&GlobalTreeConnectionList);
        INIT_LIST_HEAD(&GlobalOplock_Q);
+ #ifdef CONFIG_CIFS_EXPERIMENTAL
+       INIT_LIST_HEAD(&GlobalDnotifyReqList);
+       INIT_LIST_HEAD(&GlobalDnotifyRsp_Q);
+ #endif        
  /*
   *  Initialize Global counters
   */
                                if (!rc) {                
                                        rc = (int)kernel_thread(cifs_oplock_thread, NULL, 
                                                CLONE_FS | CLONE_FILES | CLONE_VM);
-                                       if(rc > 0)
-                                               return 0;
-                                       else 
+                                       if(rc > 0) {
+                                               rc = (int)kernel_thread(cifs_dnotify_thread, NULL,
+                                                       CLONE_FS | CLONE_FILES | CLONE_VM);
+                                               if(rc > 0)
+                                                       return 0;
+                                               else
+                                                       cERROR(1,("error %d create dnotify thread", rc));
+                                       } else {
                                                cERROR(1,("error %d create oplock thread",rc));
+                                       }
                                }
                                cifs_destroy_request_bufs();
                        }
@@@ -918,6 -978,10 +978,10 @@@ exit_cifs(void
                send_sig(SIGTERM, oplockThread, 1);
                wait_for_completion(&cifs_oplock_exited);
        }
+       if(dnotifyThread) {
+               send_sig(SIGTERM, dnotifyThread, 1);
+               wait_for_completion(&cifs_dnotify_exited);
+       }
  }
  
  MODULE_AUTHOR("Steve French <sfrench@us.ibm.com>");
diff --combined fs/cifs/connect.c
@@@ -29,6 -29,8 +29,8 @@@
  #include <linux/utsname.h>
  #include <linux/mempool.h>
  #include <linux/delay.h>
+ #include <linux/completion.h>
+ #include <linux/pagevec.h>
  #include <asm/uaccess.h>
  #include <asm/processor.h>
  #include "cifspdu.h"
@@@ -44,6 -46,8 +46,8 @@@
  #define CIFS_PORT 445
  #define RFC1001_PORT 139
  
+ static DECLARE_COMPLETION(cifsd_complete);
  extern void SMBencrypt(unsigned char *passwd, unsigned char *c8,
                       unsigned char *p24);
  extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
@@@ -60,6 -64,7 +64,7 @@@ struct smb_vol 
        char *in6_addr;  /* ipv6 address as human readable form of in6_addr */
        char *iocharset;  /* local code page for mapping to and from Unicode */
        char source_rfc1001_name[16]; /* netbios name of client */
+       char target_rfc1001_name[16]; /* netbios name of server for Win9x/ME */
        uid_t linux_uid;
        gid_t linux_gid;
        mode_t file_mode;
        unsigned server_ino:1; /* use inode numbers from server ie UniqueId */
        unsigned direct_io:1;
        unsigned remap:1;   /* set to remap seven reserved chars in filenames */
+       unsigned posix_paths:1;   /* unset to not ask for posix pathnames. */
+       unsigned sfu_emul:1;
+       unsigned nocase;     /* request case insensitive filenames */
+       unsigned nobrl;      /* disable sending byte range locks to srv */
        unsigned int rsize;
        unsigned int wsize;
        unsigned int sockopt;
@@@ -82,7 -91,8 +91,8 @@@
  
  static int ipv4_connect(struct sockaddr_in *psin_server, 
                        struct socket **csocket,
-                       char * netb_name);
+                       char * netb_name,
+                       char * server_netb_name);
  static int ipv6_connect(struct sockaddr_in6 *psin_server, 
                        struct socket **csocket);
  
@@@ -175,9 -185,11 +185,11 @@@ cifs_reconnect(struct TCP_Server_Info *
                } else {
                        rc = ipv4_connect(&server->addr.sockAddr, 
                                        &server->ssocket,
-                                       server->workstation_RFC1001_name);
+                                       server->workstation_RFC1001_name,
+                                       server->server_RFC1001_name);
                }
                if(rc) {
+                       cFYI(1,("reconnect error %d",rc));
                        msleep(3000);
                } else {
                        atomic_inc(&tcpSesReconnectCount);
@@@ -293,12 -305,12 +305,12 @@@ static int coalesce_t2(struct smb_hdr 
        byte_count += total_in_buf2;
        BCC_LE(pTargetSMB) = cpu_to_le16(byte_count);
  
-       byte_count = be32_to_cpu(pTargetSMB->smb_buf_length);
+       byte_count = pTargetSMB->smb_buf_length;
        byte_count += total_in_buf2;
  
        /* BB also add check that we are not beyond maximum buffer size */
                
-       pTargetSMB->smb_buf_length = cpu_to_be32(byte_count);
+       pTargetSMB->smb_buf_length = byte_count;
  
        if(remaining == total_in_buf2) {
                cFYI(1,("found the last secondary response"));
@@@ -323,7 -335,7 +335,7 @@@ cifs_demultiplex_thread(struct TCP_Serv
        struct cifsSesInfo *ses;
        struct task_struct *task_to_wake = NULL;
        struct mid_q_entry *mid_entry;
-       char *temp;
+       char temp;
        int isLargeBuf = FALSE;
        int isMultiRsp;
        int reconnect;
        atomic_inc(&tcpSesAllocCount);
        length = tcpSesAllocCount.counter;
        write_unlock(&GlobalSMBSeslock);
+       complete(&cifsd_complete);
        if(length  > 1) {
                mempool_resize(cifs_req_poolp,
                        length + cifs_min_rcv,
        }
  
        while (server->tcpStatus != CifsExiting) {
 -              if(try_to_freeze())
 +              if (try_to_freeze())
                        continue;
                if (bigbuf == NULL) {
                        bigbuf = cifs_buf_get();
                        continue;
                }
  
-               /* the right amount was read from socket - 4 bytes */
+               /* The right amount was read from socket - 4 bytes */
+               /* so we can now interpret the length field */
+               /* the first byte big endian of the length field,
+               is actually not part of the length but the type
+               with the most common, zero, as regular data */
+               temp = *((char *) smb_buffer);
  
+               /* Note that FC 1001 length is big endian on the wire, 
+               but we convert it here so it is always manipulated
+               as host byte order */
                pdu_length = ntohl(smb_buffer->smb_buf_length);
-               cFYI(1,("rfc1002 length(big endian)0x%x)", pdu_length+4));
+               smb_buffer->smb_buf_length = pdu_length;
+               cFYI(1,("rfc1002 length 0x%x)", pdu_length+4));
  
-               temp = (char *) smb_buffer;
-               if (temp[0] == (char) RFC1002_SESSION_KEEP_ALIVE) {
+               if (temp == (char) RFC1002_SESSION_KEEP_ALIVE) {
                        continue; 
-               } else if (temp[0] == (char)RFC1002_POSITIVE_SESSION_RESPONSE) {
+               } else if (temp == (char)RFC1002_POSITIVE_SESSION_RESPONSE) {
                        cFYI(1,("Good RFC 1002 session rsp"));
                        continue;
-               } else if (temp[0] == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) {
+               } else if (temp == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) {
                        /* we get this from Windows 98 instead of 
                           an error on SMB negprot response */
                        cFYI(1,("Negative RFC1002 Session Response Error 0x%x)",
-                               temp[4]));
+                               pdu_length));
                        if(server->tcpStatus == CifsNew) {
                                /* if nack on negprot (rather than 
                                ret of smb negprot error) reconnecting
                                wake_up(&server->response_q);
                                continue;
                        }
-               } else if (temp[0] != (char) 0) {
+               } else if (temp != (char) 0) {
                        cERROR(1,("Unknown RFC 1002 frame"));
-                       cifs_dump_mem(" Received Data: ", temp, length);
+                       cifs_dump_mem(" Received Data: ", (char *)smb_buffer,
+                                     length);
                        cifs_reconnect(server);
                        csocket = server->ssocket;
                        continue;
  
                dump_smb(smb_buffer, length);
                if (checkSMB (smb_buffer, smb_buffer->Mid, total_read+4)) {
-                       cERROR(1, ("Bad SMB Received "));
+                       cifs_dump_mem("Bad SMB: ", smb_buffer, 48);
                        continue;
                }
  
  multi_t2_fnd:
                                task_to_wake = mid_entry->tsk;
                                mid_entry->midState = MID_RESPONSE_RECEIVED;
+ #ifdef CONFIG_CIFS_STATS2
+                               mid_entry->when_received = jiffies;
+ #endif
                                break;
                        }
                }
                } else if ((is_valid_oplock_break(smb_buffer) == FALSE)
                    && (isMultiRsp == FALSE)) {                          
                        cERROR(1, ("No task to wake, unknown frame rcvd!"));
-                       cifs_dump_mem("Received Data is: ",temp,sizeof(struct smb_hdr));
+                       cifs_dump_mem("Received Data is: ",(char *)smb_buffer,
+                                     sizeof(struct smb_hdr));
                }
        } /* end while !EXITING */
  
                msleep(125);
        }
  
-       if (list_empty(&server->pending_mid_q)) {
+       if (!list_empty(&server->pending_mid_q)) {
                /* mpx threads have not exited yet give them 
                at least the smb send timeout time for long ops */
                /* due to delays on oplock break requests, we need
                        GFP_KERNEL);
        }
        
-       msleep(250);
+       complete_and_exit(&cifsd_complete, 0);
        return 0;
  }
  
@@@ -737,7 -765,9 +765,9 @@@ cifs_parse_mount_options(char *options
                        toupper(system_utsname.nodename[i]);
        }
        vol->source_rfc1001_name[15] = 0;
+       /* null target name indicates to use *SMBSERVR default called name
+          if we end up sending RFC1001 session initialize */
+       vol->target_rfc1001_name[0] = 0;
        vol->linux_uid = current->uid;  /* current->euid instead? */
        vol->linux_gid = current->gid;
        vol->dir_mode = S_IRWXUGO;
        /* vol->retry default is 0 (i.e. "soft" limited retry not hard retry) */
        vol->rw = TRUE;
  
+       /* default is always to request posix paths. */
+       vol->posix_paths = 1;
        if (!options)
                return 1;
  
                                /* go from value to value + temp_len condensing 
                                double commas to singles. Note that this ends up
                                allocating a few bytes too many, which is ok */
 -                              vol->password = kcalloc(1, temp_len, GFP_KERNEL);
 +                              vol->password = kzalloc(temp_len, GFP_KERNEL);
                                if(vol->password == NULL) {
                                        printk("CIFS: no memory for pass\n");
                                        return 1;
                                }
                                vol->password[j] = 0;
                        } else {
 -                              vol->password = kcalloc(1, temp_len+1, GFP_KERNEL);
 +                              vol->password = kzalloc(temp_len+1, GFP_KERNEL);
                                if(vol->password == NULL) {
                                        printk("CIFS: no memory for pass\n");
                                        return 1;
                                /* The string has 16th byte zero still from
                                set at top of the function  */
                                if((i==15) && (value[i] != 0))
-                                       printk(KERN_WARNING "CIFS: netbiosname longer than 15 and was truncated.\n");
+                                       printk(KERN_WARNING "CIFS: netbiosname longer than 15 truncated.\n");
+                       }
+               } else if (strnicmp(data, "servern", 7) == 0) {
+                       /* servernetbiosname specified override *SMBSERVER */
+                       if (!value || !*value || (*value == ' ')) {
+                               cFYI(1,("empty server netbiosname specified"));
+                       } else {
+                               /* last byte, type, is 0x20 for servr type */
+                               memset(vol->target_rfc1001_name,0x20,16);
+                               for(i=0;i<15;i++) {
+                               /* BB are there cases in which a comma can be
+                                  valid in this workstation netbios name (and need
+                                  special handling)? */
+                               /* user or mount helper must uppercase netbiosname */
+                                       if (value[i]==0)
+                                               break;
+                                       else
+                                               vol->target_rfc1001_name[i] = value[i];
+                               }
+                               /* The string has 16th byte zero still from
+                                  set at top of the function  */
+                               if((i==15) && (value[i] != 0))
+                                       printk(KERN_WARNING "CIFS: server netbiosname longer than 15 truncated.\n");
                        }
                } else if (strnicmp(data, "credentials", 4) == 0) {
                        /* ignore */
                        vol->remap = 1;
                } else if (strnicmp(data, "nomapchars", 10) == 0) {
                        vol->remap = 0;
+                 } else if (strnicmp(data, "sfu", 3) == 0) {
+                         vol->sfu_emul = 1;
+                 } else if (strnicmp(data, "nosfu", 5) == 0) {
+                         vol->sfu_emul = 0;
+               } else if (strnicmp(data, "posixpaths", 10) == 0) {
+                       vol->posix_paths = 1;
+               } else if (strnicmp(data, "noposixpaths", 12) == 0) {
+                       vol->posix_paths = 0;
+                 } else if ((strnicmp(data, "nocase", 6) == 0) ||
+                          (strnicmp(data, "ignorecase", 10)  == 0)) {
+                         vol->nocase = 1;
+               } else if (strnicmp(data, "brl", 3) == 0) {
+                       vol->nobrl =  0;
+               } else if ((strnicmp(data, "nobrl", 5) == 0) || 
+                          (strnicmp(data, "nolock", 6) == 0)) {
+                       vol->nobrl =  1;
+                       /* turn off mandatory locking in mode
+                       if remote locking is turned off since the
+                       local vfs will do advisory */
+                       if(vol->file_mode == (S_IALLUGO & ~(S_ISUID | S_IXGRP)))
+                               vol->file_mode = S_IALLUGO;
                } else if (strnicmp(data, "setuids", 7) == 0) {
                        vol->setuids = 1;
                } else if (strnicmp(data, "nosetuids", 9) == 0) {
@@@ -1244,7 -1322,7 +1322,7 @@@ static void rfc1002mangle(char * target
  
  static int
  ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, 
-                        char * netbios_name)
+            char * netbios_name, char * target_name)
  {
        int rc = 0;
        int connected = 0;
        /* Eventually check for other socket options to change from 
                the default. sock_setsockopt not used because it expects 
                user space buffer */
+        cFYI(1,("sndbuf %d rcvbuf %d rcvtimeo 0x%lx",(*csocket)->sk->sk_sndbuf,
+                (*csocket)->sk->sk_rcvbuf, (*csocket)->sk->sk_rcvtimeo));
        (*csocket)->sk->sk_rcvtimeo = 7 * HZ;
+       /* make the bufsizes depend on wsize/rsize and max requests */
+       if((*csocket)->sk->sk_sndbuf < (200 * 1024))
+               (*csocket)->sk->sk_sndbuf = 200 * 1024;
+       if((*csocket)->sk->sk_rcvbuf < (140 * 1024))
+               (*csocket)->sk->sk_rcvbuf = 140 * 1024;
  
        /* send RFC1001 sessinit */
        if(psin_server->sin_port == htons(RFC1001_PORT)) {
                /* some servers require RFC1001 sessinit before sending
                negprot - BB check reconnection in case where second 
                sessinit is sent but no second negprot */
                struct rfc1002_session_packet * ses_init_buf;
                struct smb_hdr * smb_buf;
 -              ses_init_buf = kcalloc(1, sizeof(struct rfc1002_session_packet), GFP_KERNEL);
 +              ses_init_buf = kzalloc(sizeof(struct rfc1002_session_packet), GFP_KERNEL);
                if(ses_init_buf) {
                        ses_init_buf->trailer.session_req.called_len = 32;
-                       rfc1002mangle(ses_init_buf->trailer.session_req.called_name,
-                               DEFAULT_CIFS_CALLED_NAME,16);
+                       if(target_name && (target_name[0] != 0)) {
+                               rfc1002mangle(ses_init_buf->trailer.session_req.called_name,
+                                       target_name, 16);
+                       } else {
+                               rfc1002mangle(ses_init_buf->trailer.session_req.called_name,
+                                       DEFAULT_CIFS_CALLED_NAME,16);
+                       }
                        ses_init_buf->trailer.session_req.calling_len = 32;
                        /* calling name ends in null (byte 16) from old smb
                        convention. */
@@@ -1556,7 -1646,9 +1646,9 @@@ cifs_mount(struct super_block *sb, stru
                        sin_server.sin_port = htons(volume_info.port);
                else
                        sin_server.sin_port = 0;
-               rc = ipv4_connect(&sin_server,&csocket,volume_info.source_rfc1001_name);
+               rc = ipv4_connect(&sin_server,&csocket,
+                                 volume_info.source_rfc1001_name,
+                                 volume_info.target_rfc1001_name);
                if (rc < 0) {
                        cERROR(1,
                               ("Error connecting to IPv4 socket. Aborting operation"));
                                        kfree(volume_info.password);
                                FreeXid(xid);
                                return rc;
-                       } else
-                               rc = 0;
+                       }
+                       wait_for_completion(&cifsd_complete);
+                       rc = 0;
                        memcpy(srvTcp->workstation_RFC1001_name, volume_info.source_rfc1001_name,16);
+                       memcpy(srvTcp->server_RFC1001_name, volume_info.target_rfc1001_name,16);
                        srvTcp->sequence_number = 0;
                }
        }
      
        /* search for existing tcon to this server share */
        if (!rc) {
-               if((volume_info.rsize) && (volume_info.rsize <= CIFSMaxBufSize))
+               if(volume_info.rsize > CIFSMaxBufSize) {
+                       cERROR(1,("rsize %d too large, using MaxBufSize",
+                               volume_info.rsize));
+                       cifs_sb->rsize = CIFSMaxBufSize;
+               } else if((volume_info.rsize) && (volume_info.rsize <= CIFSMaxBufSize))
                        cifs_sb->rsize = volume_info.rsize;
-               else
-                       cifs_sb->rsize = srvTcp->maxBuf - MAX_CIFS_HDR_SIZE; /* default */
-               if((volume_info.wsize) && (volume_info.wsize <= CIFSMaxBufSize))
+               else /* default */
+                       cifs_sb->rsize = CIFSMaxBufSize;
+               if(volume_info.wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) {
+                       cERROR(1,("wsize %d too large using 4096 instead",
+                                 volume_info.wsize));
+                       cifs_sb->wsize = 4096;
+               } else if(volume_info.wsize)
                        cifs_sb->wsize = volume_info.wsize;
                else
                        cifs_sb->wsize = CIFSMaxBufSize; /* default */
                if(cifs_sb->rsize < PAGE_CACHE_SIZE) {
-                       cifs_sb->rsize = PAGE_CACHE_SIZE;
-                       cERROR(1,("Attempt to set readsize for mount to less than one page (4096)"));
+                       cifs_sb->rsize = PAGE_CACHE_SIZE; 
+                       /* Windows ME does this */
+                       cFYI(1,("Attempt to set readsize for mount to less than one page (4096)"));
                }
                cifs_sb->mnt_uid = volume_info.linux_uid;
                cifs_sb->mnt_gid = volume_info.linux_gid;
                        cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR;
                if(volume_info.no_xattr)
                        cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR;
+               if(volume_info.sfu_emul)
+                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL;
+               if(volume_info.nobrl)
+                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL;
                if(volume_info.direct_io) {
-                       cERROR(1,("mounting share using direct i/o"));
+                       cFYI(1,("mounting share using direct i/o"));
                        cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;
                }
  
                           to the same server share the last value passed in 
                           for the retry flag is used */
                        tcon->retry = volume_info.retry;
+                       tcon->nocase = volume_info.nocase;
                } else {
                        tcon = tconInfoAlloc();
                        if (tcon == NULL)
                                if (!rc) {
                                        atomic_inc(&pSesInfo->inUse);
                                        tcon->retry = volume_info.retry;
+                                       tcon->nocase = volume_info.nocase;
                                }
                        }
                }
                        spin_lock(&GlobalMid_Lock);
                        srvTcp->tcpStatus = CifsExiting;
                        spin_unlock(&GlobalMid_Lock);
-                       if(srvTcp->tsk)
+                       if(srvTcp->tsk) {
                                send_sig(SIGKILL,srvTcp->tsk,1);
+                               wait_for_completion(&cifsd_complete);
+                       }
                }
                 /* If find_unc succeeded then rc == 0 so we can not end */
                if (tcon)  /* up accidently freeing someone elses tcon struct */
                                        temp_rc = CIFSSMBLogoff(xid, pSesInfo);
                                        /* if the socketUseCount is now zero */
                                        if((temp_rc == -ESHUTDOWN) &&
-                                          (pSesInfo->server->tsk))
+                                          (pSesInfo->server->tsk)) {
                                                send_sig(SIGKILL,pSesInfo->server->tsk,1);
+                                               wait_for_completion(&cifsd_complete);
+                                       }
                                } else
                                        cFYI(1, ("No session or bad tcon"));
                                sesInfoFree(pSesInfo);
                                                cFYI(1,("server negotiated posix acl support"));
                                                sb->s_flags |= MS_POSIXACL;
                                }
+                               /* Try and negotiate POSIX pathnames if we can. */
+                               if (volume_info.posix_paths && (CIFS_UNIX_POSIX_PATHNAMES_CAP &
+                                   le64_to_cpu(tcon->fsUnixInfo.Capability))) {
+                                       if (!CIFSSMBSetFSUnixInfo(xid, tcon, CIFS_UNIX_POSIX_PATHNAMES_CAP))  {
+                                               cFYI(1,("negotiated posix pathnames support"));
+                                               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS;
+                                       } else {
+                                               cFYI(1,("posix pathnames support requested but not supported"));
+                                       }
+                               }
                        }
                }
+               if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X))
+                       cifs_sb->wsize = min(cifs_sb->wsize,
+                                            (tcon->ses->server->maxBuf -
+                                             MAX_CIFS_HDR_SIZE));
+               if (!(tcon->ses->capabilities & CAP_LARGE_READ_X))
+                         cifs_sb->rsize = min(cifs_sb->rsize,
+                                              (tcon->ses->server->maxBuf -
+                                               MAX_CIFS_HDR_SIZE));
        }
  
        /* volume_info.password is freed above when existing session found
@@@ -1832,6 -1966,7 +1966,7 @@@ CIFSSessSetup(unsigned int xid, struct 
        header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
                        NULL /* no tCon exists yet */ , 13 /* wct */ );
  
+       smb_buffer->Mid = GetNextMid(ses->server);
        pSMB->req_no_secext.AndXCommand = 0xFF;
        pSMB->req_no_secext.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
        pSMB->req_no_secext.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
  /* We look for obvious messed up bcc or strings in response so we do not go off
     the end since (at least) WIN2K and Windows XP have a major bug in not null
     terminating last Unicode string in response  */
 -                              ses->serverOS = kcalloc(1, 2 * (len + 1), GFP_KERNEL);
 +                              ses->serverOS = kzalloc(2 * (len + 1), GFP_KERNEL);
                                if(ses->serverOS == NULL)
                                        goto sesssetup_nomem;
                                cifs_strfromUCS_le(ses->serverOS,
                                if (remaining_words > 0) {
                                        len = UniStrnlen((wchar_t *)bcc_ptr,
                                                         remaining_words-1);
 -                                      ses->serverNOS = kcalloc(1, 2 * (len + 1),GFP_KERNEL);
 +                                      ses->serverNOS = kzalloc(2 * (len + 1),GFP_KERNEL);
                                        if(ses->serverNOS == NULL)
                                                goto sesssetup_nomem;
                                        cifs_strfromUCS_le(ses->serverNOS,
                                                len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words);
            /* last string is not always null terminated (for e.g. for Windows XP & 2000) */
                                                ses->serverDomain =
 -                                                  kcalloc(1, 2*(len+1),GFP_KERNEL);
 +                                                  kzalloc(2*(len+1),GFP_KERNEL);
                                                if(ses->serverDomain == NULL)
                                                        goto sesssetup_nomem;
                                                cifs_strfromUCS_le(ses->serverDomain,
                                        } /* else no more room so create dummy domain string */
                                        else
                                                ses->serverDomain = 
 -                                                      kcalloc(1, 2, GFP_KERNEL);
 +                                                      kzalloc(2, GFP_KERNEL);
                                } else {        /* no room so create dummy domain and NOS string */
                                        /* if these kcallocs fail not much we
                                           can do, but better to not fail the
                                           sesssetup itself */
                                        ses->serverDomain =
 -                                          kcalloc(1, 2, GFP_KERNEL);
 +                                          kzalloc(2, GFP_KERNEL);
                                        ses->serverNOS =
 -                                          kcalloc(1, 2, GFP_KERNEL);
 +                                          kzalloc(2, GFP_KERNEL);
                                }
                        } else {        /* ASCII */
                                len = strnlen(bcc_ptr, 1024);
                                if (((long) bcc_ptr + len) - (long)
                                    pByteArea(smb_buffer_response)
                                            <= BCC(smb_buffer_response)) {
 -                                      ses->serverOS = kcalloc(1, len + 1,GFP_KERNEL);
 +                                      ses->serverOS = kzalloc(len + 1,GFP_KERNEL);
                                        if(ses->serverOS == NULL)
                                                goto sesssetup_nomem;
                                        strncpy(ses->serverOS,bcc_ptr, len);
                                        bcc_ptr++;
  
                                        len = strnlen(bcc_ptr, 1024);
 -                                      ses->serverNOS = kcalloc(1, len + 1,GFP_KERNEL);
 +                                      ses->serverNOS = kzalloc(len + 1,GFP_KERNEL);
                                        if(ses->serverNOS == NULL)
                                                goto sesssetup_nomem;
                                        strncpy(ses->serverNOS, bcc_ptr, len);
                                        bcc_ptr++;
  
                                        len = strnlen(bcc_ptr, 1024);
 -                                      ses->serverDomain = kcalloc(1, len + 1,GFP_KERNEL);
 +                                      ses->serverDomain = kzalloc(len + 1,GFP_KERNEL);
                                        if(ses->serverDomain == NULL)
                                                goto sesssetup_nomem;
                                        strncpy(ses->serverDomain, bcc_ptr, len);
@@@ -2107,6 -2242,8 +2242,8 @@@ CIFSSpnegoSessSetup(unsigned int xid, s
        /* send SMBsessionSetup here */
        header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
                        NULL /* no tCon exists yet */ , 12 /* wct */ );
+       smb_buffer->Mid = GetNextMid(ses->server);
        pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
        pSMB->req.AndXCommand = 0xFF;
        pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
     the end since (at least) WIN2K and Windows XP have a major bug in not null
     terminating last Unicode string in response  */
                                        ses->serverOS =
 -                                          kcalloc(1, 2 * (len + 1), GFP_KERNEL);
 +                                          kzalloc(2 * (len + 1), GFP_KERNEL);
                                        cifs_strfromUCS_le(ses->serverOS,
                                                           (wchar_t *)
                                                           bcc_ptr, len,
                                                                 remaining_words
                                                                 - 1);
                                                ses->serverNOS =
 -                                                  kcalloc(1, 2 * (len + 1),
 +                                                  kzalloc(2 * (len + 1),
                                                            GFP_KERNEL);
                                                cifs_strfromUCS_le(ses->serverNOS,
                                                                   (wchar_t *)bcc_ptr,
                                                if (remaining_words > 0) {
                                                        len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words); 
                              /* last string is not always null terminated (for e.g. for Windows XP & 2000) */
 -                                                      ses->serverDomain = kcalloc(1, 2*(len+1),GFP_KERNEL);
 +                                                      ses->serverDomain = kzalloc(2*(len+1),GFP_KERNEL);
                                                        cifs_strfromUCS_le(ses->serverDomain,
                                                             (wchar_t *)bcc_ptr, 
                                   len,
                                                } /* else no more room so create dummy domain string */
                                                else
                                                        ses->serverDomain =
 -                                                          kcalloc(1, 2,GFP_KERNEL);
 +                                                          kzalloc(2,GFP_KERNEL);
                                        } else {        /* no room so create dummy domain and NOS string */
 -                                              ses->serverDomain = kcalloc(1, 2, GFP_KERNEL);
 -                                              ses->serverNOS = kcalloc(1, 2, GFP_KERNEL);
 +                                              ses->serverDomain = kzalloc(2, GFP_KERNEL);
 +                                              ses->serverNOS = kzalloc(2, GFP_KERNEL);
                                        }
                                } else {        /* ASCII */
  
                                        if (((long) bcc_ptr + len) - (long)
                                            pByteArea(smb_buffer_response)
                                            <= BCC(smb_buffer_response)) {
 -                                              ses->serverOS = kcalloc(1, len + 1, GFP_KERNEL);
 +                                              ses->serverOS = kzalloc(len + 1, GFP_KERNEL);
                                                strncpy(ses->serverOS, bcc_ptr, len);
  
                                                bcc_ptr += len;
                                                bcc_ptr++;
  
                                                len = strnlen(bcc_ptr, 1024);
 -                                              ses->serverNOS = kcalloc(1, len + 1,GFP_KERNEL);
 +                                              ses->serverNOS = kzalloc(len + 1,GFP_KERNEL);
                                                strncpy(ses->serverNOS, bcc_ptr, len);
                                                bcc_ptr += len;
                                                bcc_ptr[0] = 0;
                                                bcc_ptr++;
  
                                                len = strnlen(bcc_ptr, 1024);
 -                                              ses->serverDomain = kcalloc(1, len + 1, GFP_KERNEL);
 +                                              ses->serverDomain = kzalloc(len + 1, GFP_KERNEL);
                                                strncpy(ses->serverDomain, bcc_ptr, len);
                                                bcc_ptr += len;
                                                bcc_ptr[0] = 0;
@@@ -2373,6 -2510,8 +2510,8 @@@ CIFSNTLMSSPNegotiateSessSetup(unsigned 
        /* send SMBsessionSetup here */
        header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
                        NULL /* no tCon exists yet */ , 12 /* wct */ );
+       smb_buffer->Mid = GetNextMid(ses->server);
        pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
        pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT);
  
     the end since (at least) WIN2K and Windows XP have a major bug in not null
     terminating last Unicode string in response  */
                                        ses->serverOS =
 -                                          kcalloc(1, 2 * (len + 1), GFP_KERNEL);
 +                                          kzalloc(2 * (len + 1), GFP_KERNEL);
                                        cifs_strfromUCS_le(ses->serverOS,
                                                           (wchar_t *)
                                                           bcc_ptr, len,
                                                                 remaining_words
                                                                 - 1);
                                                ses->serverNOS =
 -                                                  kcalloc(1, 2 * (len + 1),
 +                                                  kzalloc(2 * (len + 1),
                                                            GFP_KERNEL);
                                                cifs_strfromUCS_le(ses->
                                                                   serverNOS,
                                                        len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words); 
             /* last string is not always null terminated (for e.g. for Windows XP & 2000) */
                                                        ses->serverDomain =
 -                                                          kcalloc(1, 2 *
 +                                                          kzalloc(2 *
                                                                    (len +
                                                                     1),
                                                                    GFP_KERNEL);
                                                } /* else no more room so create dummy domain string */
                                                else
                                                        ses->serverDomain =
 -                                                          kcalloc(1, 2,
 +                                                          kzalloc(2,
                                                                    GFP_KERNEL);
                                        } else {        /* no room so create dummy domain and NOS string */
                                                ses->serverDomain =
 -                                                  kcalloc(1, 2, GFP_KERNEL);
 +                                                  kzalloc(2, GFP_KERNEL);
                                                ses->serverNOS =
 -                                                  kcalloc(1, 2, GFP_KERNEL);
 +                                                  kzalloc(2, GFP_KERNEL);
                                        }
                                } else {        /* ASCII */
                                        len = strnlen(bcc_ptr, 1024);
                                            pByteArea(smb_buffer_response)
                                            <= BCC(smb_buffer_response)) {
                                                ses->serverOS =
 -                                                  kcalloc(1, len + 1,
 +                                                  kzalloc(len + 1,
                                                            GFP_KERNEL);
                                                strncpy(ses->serverOS,
                                                        bcc_ptr, len);
  
                                                len = strnlen(bcc_ptr, 1024);
                                                ses->serverNOS =
 -                                                  kcalloc(1, len + 1,
 +                                                  kzalloc(len + 1,
                                                            GFP_KERNEL);
                                                strncpy(ses->serverNOS, bcc_ptr, len);
                                                bcc_ptr += len;
  
                                                len = strnlen(bcc_ptr, 1024);
                                                ses->serverDomain =
 -                                                  kcalloc(1, len + 1,
 +                                                  kzalloc(len + 1,
                                                            GFP_KERNEL);
                                                strncpy(ses->serverDomain, bcc_ptr, len);       
                                                bcc_ptr += len;
@@@ -2715,6 -2854,8 +2854,8 @@@ CIFSNTLMSSPAuthSessSetup(unsigned int x
        /* send SMBsessionSetup here */
        header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
                        NULL /* no tCon exists yet */ , 12 /* wct */ );
+       smb_buffer->Mid = GetNextMid(ses->server);
        pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT);
        pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
        pSMB->req.AndXCommand = 0xFF;
    the end since (at least) WIN2K and Windows XP have a major bug in not null
    terminating last Unicode string in response  */
                                        ses->serverOS =
 -                                          kcalloc(1, 2 * (len + 1), GFP_KERNEL);
 +                                          kzalloc(2 * (len + 1), GFP_KERNEL);
                                        cifs_strfromUCS_le(ses->serverOS,
                                                           (wchar_t *)
                                                           bcc_ptr, len,
                                                                 remaining_words
                                                                 - 1);
                                                ses->serverNOS =
 -                                                  kcalloc(1, 2 * (len + 1),
 +                                                  kzalloc(2 * (len + 1),
                                                            GFP_KERNEL);
                                                cifs_strfromUCS_le(ses->
                                                                   serverNOS,
                                                        len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words); 
       /* last string not always null terminated (e.g. for Windows XP & 2000) */
                                                        ses->serverDomain =
 -                                                          kcalloc(1, 2 *
 +                                                          kzalloc(2 *
                                                                    (len +
                                                                     1),
                                                                    GFP_KERNEL);
                                                            = 0;
                                                } /* else no more room so create dummy domain string */
                                                else
 -                                                      ses->serverDomain = kcalloc(1, 2,GFP_KERNEL);
 +                                                      ses->serverDomain = kzalloc(2,GFP_KERNEL);
                                        } else {  /* no room so create dummy domain and NOS string */
 -                                              ses->serverDomain = kcalloc(1, 2, GFP_KERNEL);
 -                                              ses->serverNOS = kcalloc(1, 2, GFP_KERNEL);
 +                                              ses->serverDomain = kzalloc(2, GFP_KERNEL);
 +                                              ses->serverNOS = kzalloc(2, GFP_KERNEL);
                                        }
                                } else {        /* ASCII */
                                        len = strnlen(bcc_ptr, 1024);
                                        if (((long) bcc_ptr + len) - 
                          (long) pByteArea(smb_buffer_response) 
                              <= BCC(smb_buffer_response)) {
 -                                              ses->serverOS = kcalloc(1, len + 1,GFP_KERNEL);
 +                                              ses->serverOS = kzalloc(len + 1,GFP_KERNEL);
                                                strncpy(ses->serverOS,bcc_ptr, len);
  
                                                bcc_ptr += len;
                                                bcc_ptr++;
  
                                                len = strnlen(bcc_ptr, 1024);
 -                                              ses->serverNOS = kcalloc(1, len+1,GFP_KERNEL);
 +                                              ses->serverNOS = kzalloc(len+1,GFP_KERNEL);
                                                strncpy(ses->serverNOS, bcc_ptr, len);  
                                                bcc_ptr += len;
                                                bcc_ptr[0] = 0;
                                                bcc_ptr++;
  
                                                len = strnlen(bcc_ptr, 1024);
 -                                              ses->serverDomain = kcalloc(1, len+1,GFP_KERNEL);
 +                                              ses->serverDomain = kzalloc(len+1,GFP_KERNEL);
                                                strncpy(ses->serverDomain, bcc_ptr, len);
                                                bcc_ptr += len;
                                                bcc_ptr[0] = 0;
@@@ -3086,6 -3227,8 +3227,8 @@@ CIFSTCon(unsigned int xid, struct cifsS
  
        header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX,
                        NULL /*no tid */ , 4 /*wct */ );
+       smb_buffer->Mid = GetNextMid(ses->server);
        smb_buffer->Uid = ses->Suid;
        pSMB = (TCONX_REQ *) smb_buffer;
        pSMBr = (TCONX_RSP *) smb_buffer_response;
                                if(tcon->nativeFileSystem)
                                        kfree(tcon->nativeFileSystem);
                                tcon->nativeFileSystem =
 -                                  kcalloc(1, length + 2, GFP_KERNEL);
 +                                  kzalloc(length + 2, GFP_KERNEL);
                                cifs_strfromUCS_le(tcon->nativeFileSystem,
                                                   (wchar_t *) bcc_ptr,
                                                   length, nls_codepage);
                                if(tcon->nativeFileSystem)
                                        kfree(tcon->nativeFileSystem);
                                tcon->nativeFileSystem =
 -                                  kcalloc(1, length + 1, GFP_KERNEL);
 +                                  kzalloc(length + 1, GFP_KERNEL);
                                strncpy(tcon->nativeFileSystem, bcc_ptr,
                                        length);
                        }
@@@ -3207,8 -3350,10 +3350,10 @@@ cifs_umount(struct super_block *sb, str
                                return 0;
                        } else if (rc == -ESHUTDOWN) {
                                cFYI(1,("Waking up socket by sending it signal"));
-                               if(cifsd_task)
+                               if(cifsd_task) {
                                        send_sig(SIGKILL,cifsd_task,1);
+                                       wait_for_completion(&cifsd_complete);
+                               }
                                rc = 0;
                        } /* else - we have an smb session
                                left on this socket do not kill cifsd */
        }
        
        cifs_sb->tcon = NULL;
 -      if (ses) {
 -              set_current_state(TASK_INTERRUPTIBLE);
 -              schedule_timeout(HZ / 2);
 -      }
 +      if (ses)
 +              schedule_timeout_interruptible(msecs_to_jiffies(500));
        if (ses)
                sesInfoFree(ses);
  
diff --combined fs/cifs/dir.c
@@@ -48,6 -48,7 +48,7 @@@ build_path_from_dentry(struct dentry *d
        struct dentry *temp;
        int namelen = 0;
        char *full_path;
+       char dirsep = CIFS_DIR_SEP(CIFS_SB(direntry->d_sb));
  
        if(direntry == NULL)
                return NULL;  /* not much we can do if dentry is freed and
@@@ -74,7 -75,7 +75,7 @@@ cifs_bp_rename_retry
                if (namelen < 0) {
                        break;
                } else {
-                       full_path[namelen] = '\\';
+                       full_path[namelen] = dirsep;
                        strncpy(full_path + namelen + 1, temp->d_name.name,
                                temp->d_name.len);
                        cFYI(0, (" name: %s ", full_path + namelen));
@@@ -145,23 -146,24 +146,23 @@@ cifs_create(struct inode *inode, struc
                return -ENOMEM;
        }
  
 -      if(nd) {
 -              if ((nd->intent.open.flags & O_ACCMODE) == O_RDONLY)
 -                      desiredAccess = GENERIC_READ;
 -              else if ((nd->intent.open.flags & O_ACCMODE) == O_WRONLY) {
 -                      desiredAccess = GENERIC_WRITE;
 -                      write_only = TRUE;
 -              } else if ((nd->intent.open.flags & O_ACCMODE) == O_RDWR) {
 -                      /* GENERIC_ALL is too much permission to request */
 -                      /* can cause unnecessary access denied on create */
 -                      /* desiredAccess = GENERIC_ALL; */
 -                      desiredAccess = GENERIC_READ | GENERIC_WRITE;
 +      if(nd && (nd->flags & LOOKUP_OPEN)) {
 +              int oflags = nd->intent.open.flags;
 +
 +              desiredAccess = 0;
 +              if (oflags & FMODE_READ)
 +                      desiredAccess |= GENERIC_READ;
 +              if (oflags & FMODE_WRITE) {
 +                      desiredAccess |= GENERIC_WRITE;
 +                      if (!(oflags & FMODE_READ))
 +                              write_only = TRUE;
                }
  
 -              if((nd->intent.open.flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
 +              if((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
                        disposition = FILE_CREATE;
 -              else if((nd->intent.open.flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
 +              else if((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
                        disposition = FILE_OVERWRITE_IF;
 -              else if((nd->intent.open.flags & O_CREAT) == O_CREAT)
 +              else if((oflags & O_CREAT) == O_CREAT)
                        disposition = FILE_OPEN_IF;
                else {
                        cFYI(1,("Create flag not set in create function"));
                         desiredAccess, CREATE_NOT_DIR,
                         &fileHandle, &oplock, buf, cifs_sb->local_nls,
                         cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+       if(rc == -EIO) {
+               /* old server, retry the open legacy style */
+               rc = SMBLegacyOpen(xid, pTcon, full_path, disposition,
+                       desiredAccess, CREATE_NOT_DIR,
+                       &fileHandle, &oplock, buf, cifs_sb->local_nls,
+                       cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+       } 
        if (rc) {
                cFYI(1, ("cifs_create returned 0x%x ", rc));
        } else {
                                                CIFS_MOUNT_MAP_SPECIAL_CHR);
                        }
                else {
-                       /* BB implement via Windows security descriptors */
+                       /* BB implement mode setting via Windows security descriptors */
                        /* eg CIFSSMBWinSetPerms(xid,pTcon,full_path,mode,-1,-1,local_nls);*/
                        /* could set r/o dos attribute if mode & 0222 == 0 */
                }
                }
  
                if (rc != 0) {
-                       cFYI(1,("Create worked but get_inode_info failed with rc = %d",
+                       cFYI(1,
+                            ("Create worked but get_inode_info failed rc = %d",
                              rc));
                } else {
-                       direntry->d_op = &cifs_dentry_ops;
+                       if (pTcon->nocase)
+                               direntry->d_op = &cifs_ci_dentry_ops;
+                       else
+                               direntry->d_op = &cifs_dentry_ops;
                        d_instantiate(direntry, newinode);
                }
                if((nd->flags & LOOKUP_OPEN) == FALSE) {
@@@ -302,8 -315,7 +314,7 @@@ int cifs_mknod(struct inode *inode, str
        up(&direntry->d_sb->s_vfs_rename_sem);
        if(full_path == NULL)
                rc = -ENOMEM;
-       
-       if (full_path && (pTcon->ses->capabilities & CAP_UNIX)) {
+       else if (pTcon->ses->capabilities & CAP_UNIX) {
                if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
                        rc = CIFSSMBUnixSetPerms(xid, pTcon, full_path,
                                mode,(__u64)current->euid,(__u64)current->egid,
                if(!rc) {
                        rc = cifs_get_inode_info_unix(&newinode, full_path,
                                                inode->i_sb,xid);
-                       direntry->d_op = &cifs_dentry_ops;
+                       if (pTcon->nocase)
+                               direntry->d_op = &cifs_ci_dentry_ops;
+                       else
+                               direntry->d_op = &cifs_dentry_ops;
                        if(rc == 0)
                                d_instantiate(direntry, newinode);
                }
+       } else {
+               if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
+                       int oplock = 0;
+                       u16 fileHandle;
+                       FILE_ALL_INFO * buf;
+                       cFYI(1,("sfu compat create special file"));
+                       buf = kmalloc(sizeof(FILE_ALL_INFO),GFP_KERNEL);
+                       if(buf == NULL) {
+                               kfree(full_path);
+                               FreeXid(xid);
+                               return -ENOMEM;
+                       }
+                       rc = CIFSSMBOpen(xid, pTcon, full_path,
+                                        FILE_CREATE, /* fail if exists */
+                                        GENERIC_WRITE /* BB would 
+                                         WRITE_OWNER | WRITE_DAC be better? */,
+                                        /* Create a file and set the
+                                           file attribute to SYSTEM */
+                                        CREATE_NOT_DIR | CREATE_OPTION_SPECIAL,
+                                        &fileHandle, &oplock, buf,
+                                        cifs_sb->local_nls,
+                                        cifs_sb->mnt_cifs_flags & 
+                                           CIFS_MOUNT_MAP_SPECIAL_CHR);
+                       if(!rc) {
+                               /* BB Do not bother to decode buf since no
+                                  local inode yet to put timestamps in */
+                               CIFSSMBClose(xid, pTcon, fileHandle);
+                               d_drop(direntry);
+                       }
+                       kfree(buf);
+                       /* add code here to set EAs */
+               }
        }
  
        kfree(full_path);
@@@ -381,7 -432,10 +431,10 @@@ cifs_lookup(struct inode *parent_dir_in
                                         parent_dir_inode->i_sb,xid);
  
        if ((rc == 0) && (newInode != NULL)) {
-               direntry->d_op = &cifs_dentry_ops;
+               if (pTcon->nocase)
+                       direntry->d_op = &cifs_ci_dentry_ops;
+               else
+                       direntry->d_op = &cifs_dentry_ops;
                d_add(direntry, newInode);
  
                /* since paths are not looked up by component - the parent directories are presumed to be good here */
@@@ -440,3 -494,42 +493,42 @@@ struct dentry_operations cifs_dentry_op
  /* d_delete:       cifs_d_delete,       *//* not needed except for debugging */
        /* no need for d_hash, d_compare, d_release, d_iput ... yet. BB confirm this BB */
  };
+ static int cifs_ci_hash(struct dentry *dentry, struct qstr *q)
+ {
+       struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
+       unsigned long hash;
+       int i;
+       hash = init_name_hash();
+       for (i = 0; i < q->len; i++)
+               hash = partial_name_hash(nls_tolower(codepage, q->name[i]),
+                                        hash);
+       q->hash = end_name_hash(hash);
+       return 0;
+ }
+ static int cifs_ci_compare(struct dentry *dentry, struct qstr *a,
+                          struct qstr *b)
+ {
+       struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
+       if ((a->len == b->len) &&
+           (nls_strnicmp(codepage, a->name, b->name, a->len) == 0)) {
+               /*
+                * To preserve case, don't let an existing negative dentry's
+                * case take precedence.  If a is not a negative dentry, this
+                * should have no side effects
+                */
+               memcpy((unsigned char *)a->name, b->name, a->len);
+               return 0;
+       }
+       return 1;
+ }
+ struct dentry_operations cifs_ci_dentry_ops = {
+       .d_revalidate = cifs_d_revalidate,
+       .d_hash = cifs_ci_hash,
+       .d_compare = cifs_ci_compare,
+ };