Merge branch 'linus' into x86/urgent
[safe/jmp/linux-2.6] / fs / cifs / connect.c
index a83684d..f428bf3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/connect.c
  *
- *   Copyright (C) International Business Machines  Corp., 2002,2007
+ *   Copyright (C) International Business Machines  Corp., 2002,2008
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -49,8 +49,6 @@
 #define CIFS_PORT 445
 #define RFC1001_PORT 139
 
-static DECLARE_COMPLETION(cifsd_complete);
-
 extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
                         unsigned char *p24);
 
@@ -71,23 +69,23 @@ struct smb_vol {
        mode_t file_mode;
        mode_t dir_mode;
        unsigned secFlg;
-       unsigned rw:1;
-       unsigned retry:1;
-       unsigned intr:1;
-       unsigned setuids:1;
-       unsigned override_uid:1;
-       unsigned override_gid:1;
-       unsigned noperm:1;
-       unsigned no_psx_acl:1; /* set if posix acl support should be disabled */
-       unsigned cifs_acl:1;
-       unsigned no_xattr:1;   /* set if xattr (EA) support should be disabled*/
-       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 no_linux_ext:1;
-       unsigned sfu_emul:1;
-       unsigned nullauth:1; /* attempt to authenticate with null user */
+       bool rw:1;
+       bool retry:1;
+       bool intr:1;
+       bool setuids:1;
+       bool override_uid:1;
+       bool override_gid:1;
+       bool noperm:1;
+       bool no_psx_acl:1; /* set if posix acl support should be disabled */
+       bool cifs_acl:1;
+       bool no_xattr:1;   /* set if xattr (EA) support should be disabled*/
+       bool server_ino:1; /* use inode numbers from server ie UniqueId */
+       bool direct_io:1;
+       bool remap:1;     /* set to remap seven reserved chars in filenames */
+       bool posix_paths:1;   /* unset to not ask for posix pathnames. */
+       bool no_linux_ext:1;
+       bool sfu_emul:1;
+       bool nullauth:1; /* attempt to authenticate with null user */
        unsigned nocase;     /* request case insensitive filenames */
        unsigned nobrl;      /* disable sending byte range locks to srv */
        unsigned int rsize;
@@ -160,7 +158,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
        if (server->ssocket) {
                cFYI(1, ("State: 0x%x Flags: 0x%lx", server->ssocket->state,
                        server->ssocket->flags));
-               server->ssocket->ops->shutdown(server->ssocket, SEND_SHUTDOWN);
+               kernel_sock_shutdown(server->ssocket, SHUT_WR);
                cFYI(1, ("Post shutdown state: 0x%x Flags: 0x%lx",
                        server->ssocket->state,
                        server->ssocket->flags));
@@ -345,18 +343,16 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
        struct task_struct *task_to_wake = NULL;
        struct mid_q_entry *mid_entry;
        char temp;
-       int isLargeBuf = FALSE;
-       int isMultiRsp;
+       bool isLargeBuf = false;
+       bool isMultiRsp;
        int reconnect;
 
        current->flags |= PF_MEMALLOC;
-       server->tsk = current;  /* save process info to wake at shutdown */
-       cFYI(1, ("Demultiplex PID: %d", current->pid));
+       cFYI(1, ("Demultiplex PID: %d", task_pid_nr(current)));
        write_lock(&GlobalSMBSeslock);
        atomic_inc(&tcpSesAllocCount);
        length = tcpSesAllocCount.counter;
        write_unlock(&GlobalSMBSeslock);
-       complete(&cifsd_complete);
        if (length  > 1)
                mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
                                GFP_KERNEL);
@@ -390,8 +386,8 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
                } else /* if existing small buf clear beginning */
                        memset(smallbuf, 0, sizeof(struct smb_hdr));
 
-               isLargeBuf = FALSE;
-               isMultiRsp = FALSE;
+               isLargeBuf = false;
+               isMultiRsp = false;
                smb_buffer = smallbuf;
                iov.iov_base = smb_buffer;
                iov.iov_len = 4;
@@ -415,7 +411,10 @@ incomplete_rcv:
                        msleep(1); /* minimum sleep to prevent looping
                                allowing socket to clear and app threads to set
                                tcpStatus CifsNeedReconnect if server hung */
-                       continue;
+                       if (pdu_length < 4)
+                               goto incomplete_rcv;
+                       else
+                               continue;
                } else if (length <= 0) {
                        if (server->tcpStatus == CifsNew) {
                                cFYI(1, ("tcp session abend after SMBnegprot"));
@@ -435,9 +434,9 @@ incomplete_rcv:
                        csocket = server->ssocket;
                        wake_up(&server->response_q);
                        continue;
-               } else if (length < 4) {
-                       cFYI(1, ("less than four bytes received (%d bytes)",
-                             length));
+               } else if (length < pdu_length) {
+                       cFYI(1, ("requested %d bytes but only got %d bytes",
+                                 pdu_length, length));
                        pdu_length -= length;
                        msleep(1);
                        goto incomplete_rcv;
@@ -514,7 +513,7 @@ incomplete_rcv:
                reconnect = 0;
 
                if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) {
-                       isLargeBuf = TRUE;
+                       isLargeBuf = true;
                        memcpy(bigbuf, smallbuf, 4);
                        smb_buffer = bigbuf;
                }
@@ -543,6 +542,7 @@ incomplete_rcv:
                                              allowing socket to clear and app
                                              threads to set tcpStatus
                                              CifsNeedReconnect if server hung*/
+                               length = 0;
                                continue;
                        } else if (length <= 0) {
                                cERROR(1, ("Received no data, expecting %d",
@@ -578,16 +578,18 @@ incomplete_rcv:
                            (mid_entry->command == smb_buffer->Command)) {
                                if (check2ndT2(smb_buffer,server->maxBuf) > 0) {
                                        /* We have a multipart transact2 resp */
-                                       isMultiRsp = TRUE;
+                                       isMultiRsp = true;
                                        if (mid_entry->resp_buf) {
                                                /* merge response - fix up 1st*/
                                                if (coalesce_t2(smb_buffer,
                                                        mid_entry->resp_buf)) {
-                                                       mid_entry->multiRsp = 1;
+                                                       mid_entry->multiRsp =
+                                                                true;
                                                        break;
                                                } else {
                                                        /* all parts received */
-                                                       mid_entry->multiEnd = 1;
+                                                       mid_entry->multiEnd =
+                                                                true;
                                                        goto multi_t2_fnd;
                                                }
                                        } else {
@@ -599,17 +601,15 @@ incomplete_rcv:
                                                        /* Have first buffer */
                                                        mid_entry->resp_buf =
                                                                 smb_buffer;
-                                                       mid_entry->largeBuf = 1;
+                                                       mid_entry->largeBuf =
+                                                                true;
                                                        bigbuf = NULL;
                                                }
                                        }
                                        break;
                                }
                                mid_entry->resp_buf = smb_buffer;
-                               if (isLargeBuf)
-                                       mid_entry->largeBuf = 1;
-                               else
-                                       mid_entry->largeBuf = 0;
+                               mid_entry->largeBuf = isLargeBuf;
 multi_t2_fnd:
                                task_to_wake = mid_entry->tsk;
                                mid_entry->midState = MID_RESPONSE_RECEIVED;
@@ -634,8 +634,8 @@ multi_t2_fnd:
                                        smallbuf = NULL;
                        }
                        wake_up_process(task_to_wake);
-               } else if ((is_valid_oplock_break(smb_buffer, server) == FALSE)
-                   && (isMultiRsp == FALSE)) {
+               } else if (!is_valid_oplock_break(smb_buffer, server) &&
+                          !isMultiRsp) {
                        cERROR(1, ("No task to wake, unknown frame received! "
                                   "NumMids %d", midCount.counter));
                        cifs_dump_mem("Received Data is: ", (char *)smb_buffer,
@@ -650,10 +650,20 @@ multi_t2_fnd:
 
        spin_lock(&GlobalMid_Lock);
        server->tcpStatus = CifsExiting;
-       server->tsk = NULL;
+       spin_unlock(&GlobalMid_Lock);
+
+       /* don't exit until kthread_stop is called */
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       while (!kthread_should_stop()) {
+               schedule();
+               set_current_state(TASK_UNINTERRUPTIBLE);
+       }
+       set_current_state(TASK_RUNNING);
+
        /* check if we have blocked requests that need to free */
        /* Note that cifs_max_pending is normally 50, but
        can be set at module install time to as little as two */
+       spin_lock(&GlobalMid_Lock);
        if (atomic_read(&server->inFlight) >= cifs_max_pending)
                atomic_set(&server->inFlight, cifs_max_pending - 1);
        /* We do not want to set the max_pending too low or we
@@ -673,9 +683,8 @@ multi_t2_fnd:
                server->ssocket = NULL;
        }
        /* buffer usuallly freed in free_mid - need to free it here on exit */
-       if (bigbuf != NULL)
-               cifs_buf_release(bigbuf);
-       if (smallbuf != NULL)
+       cifs_buf_release(bigbuf);
+       if (smallbuf) /* no sense logging a debug message if NULL */
                cifs_small_buf_release(smallbuf);
 
        read_lock(&GlobalSMBSeslock);
@@ -749,6 +758,7 @@ multi_t2_fnd:
        }
        write_unlock(&GlobalSMBSeslock);
 
+       kfree(server->hostname);
        kfree(server);
        if (length  > 0)
                mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
@@ -757,6 +767,34 @@ multi_t2_fnd:
        return 0;
 }
 
+/* extract the host portion of the UNC string */
+static char *
+extract_hostname(const char *unc)
+{
+       const char *src;
+       char *dst, *delim;
+       unsigned int len;
+
+       /* skip double chars at beginning of string */
+       /* BB: check validity of these bytes? */
+       src = unc + 2;
+
+       /* delimiter between hostname and sharename is always '\\' now */
+       delim = strchr(src, '\\');
+       if (!delim)
+               return ERR_PTR(-EINVAL);
+
+       len = delim - src;
+       dst = kmalloc((len + 1), GFP_KERNEL);
+       if (dst == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       memcpy(dst, src, len);
+       dst[len] = '\0';
+
+       return dst;
+}
+
 static int
 cifs_parse_mount_options(char *options, const char *devname,
                         struct smb_vol *vol)
@@ -790,10 +828,10 @@ cifs_parse_mount_options(char *options, const char *devname,
        vol->linux_gid = current->gid;
        vol->dir_mode = S_IRWXUGO;
        /* 2767 perms indicate mandatory locking support */
-       vol->file_mode = S_IALLUGO & ~(S_ISUID | S_IXGRP);
+       vol->file_mode = (S_IRWXUGO | S_ISGID) & (~S_IXGRP);
 
        /* vol->retry default is 0 (i.e. "soft" limited retry not hard retry) */
-       vol->rw = TRUE;
+       vol->rw = true;
        /* default is always to request posix paths. */
        vol->posix_paths = 1;
 
@@ -1149,7 +1187,7 @@ cifs_parse_mount_options(char *options, const char *devname,
                } else if (strnicmp(data, "guest", 5) == 0) {
                        /* ignore */
                } else if (strnicmp(data, "rw", 2) == 0) {
-                       vol->rw = TRUE;
+                       vol->rw = true;
                } else if ((strnicmp(data, "suid", 4) == 0) ||
                                   (strnicmp(data, "nosuid", 6) == 0) ||
                                   (strnicmp(data, "exec", 4) == 0) ||
@@ -1165,7 +1203,7 @@ cifs_parse_mount_options(char *options, const char *devname,
                            is ok to just ignore them */
                        continue;
                } else if (strnicmp(data, "ro", 2) == 0) {
-                       vol->rw = FALSE;
+                       vol->rw = false;
                } else if (strnicmp(data, "hard", 4) == 0) {
                        vol->retry = 1;
                } else if (strnicmp(data, "soft", 4) == 0) {
@@ -1273,6 +1311,9 @@ cifs_parse_mount_options(char *options, const char *devname,
                                                    "begin with // or \\\\ \n");
                                return 1;
                        }
+                       value = strpbrk(vol->UNC+2, "/\\");
+                       if (value)
+                               *value = '\\';
                } else {
                        printk(KERN_WARNING "CIFS: UNC name too long\n");
                        return 1;
@@ -1286,42 +1327,43 @@ cifs_parse_mount_options(char *options, const char *devname,
 
 static struct cifsSesInfo *
 cifs_find_tcp_session(struct in_addr *target_ip_addr,
-               struct in6_addr *target_ip6_addr,
-                char *userName, struct TCP_Server_Info **psrvTcp)
+                     struct in6_addr *target_ip6_addr,
+                     char *userName, struct TCP_Server_Info **psrvTcp)
 {
        struct list_head *tmp;
        struct cifsSesInfo *ses;
+
        *psrvTcp = NULL;
-       read_lock(&GlobalSMBSeslock);
 
+       read_lock(&GlobalSMBSeslock);
        list_for_each(tmp, &GlobalSMBSessionList) {
                ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
-               if (ses->server) {
-                       if ((target_ip_addr &&
-                               (ses->server->addr.sockAddr.sin_addr.s_addr
-                                 == target_ip_addr->s_addr)) || (target_ip6_addr
-                               && memcmp(&ses->server->addr.sockAddr6.sin6_addr,
-                                       target_ip6_addr, sizeof(*target_ip6_addr)))) {
-                               /* BB lock server and tcp session and increment
-                                     use count here?? */
-
-                               /* found a match on the TCP session */
-                               *psrvTcp = ses->server;
-
-                               /* BB check if reconnection needed */
-                               if (strncmp
-                                   (ses->userName, userName,
-                                    MAX_USERNAME_SIZE) == 0){
-                                       read_unlock(&GlobalSMBSeslock);
-                                       /* Found exact match on both TCP and
-                                          SMB sessions */
-                                       return ses;
-                               }
-                       }
+               if (!ses->server)
+                       continue;
+
+               if (target_ip_addr &&
+                   ses->server->addr.sockAddr.sin_addr.s_addr != target_ip_addr->s_addr)
+                               continue;
+               else if (target_ip6_addr &&
+                        memcmp(&ses->server->addr.sockAddr6.sin6_addr,
+                               target_ip6_addr, sizeof(*target_ip6_addr)))
+                               continue;
+               /* BB lock server and tcp session; increment use count here?? */
+
+               /* found a match on the TCP session */
+               *psrvTcp = ses->server;
+
+               /* BB check if reconnection needed */
+               if (strncmp(ses->userName, userName, MAX_USERNAME_SIZE) == 0) {
+                       read_unlock(&GlobalSMBSeslock);
+                       /* Found exact match on both TCP and
+                          SMB sessions */
+                       return ses;
                }
                /* else tcp and smb sessions need reconnection */
        }
        read_unlock(&GlobalSMBSeslock);
+
        return NULL;
 }
 
@@ -1330,45 +1372,43 @@ find_unc(__be32 new_target_ip_addr, char *uncName, char *userName)
 {
        struct list_head *tmp;
        struct cifsTconInfo *tcon;
+       __be32 old_ip;
 
        read_lock(&GlobalSMBSeslock);
+
        list_for_each(tmp, &GlobalTreeConnectionList) {
                cFYI(1, ("Next tcon"));
                tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
-               if (tcon->ses) {
-                       if (tcon->ses->server) {
-                               cFYI(1,
-                                    ("old ip addr: %x == new ip %x ?",
-                                     tcon->ses->server->addr.sockAddr.sin_addr.
-                                     s_addr, new_target_ip_addr));
-                               if (tcon->ses->server->addr.sockAddr.sin_addr.
-                                   s_addr == new_target_ip_addr) {
-       /* BB lock tcon, server and tcp session and increment use count here? */
-                                       /* found a match on the TCP session */
-                                       /* BB check if reconnection needed */
-                                       cFYI(1,
-                                             ("IP match, old UNC: %s new: %s",
-                                             tcon->treeName, uncName));
-                                       if (strncmp
-                                           (tcon->treeName, uncName,
-                                            MAX_TREE_SIZE) == 0) {
-                                               cFYI(1,
-                                                    ("and old usr: %s new: %s",
-                                                     tcon->treeName, uncName));
-                                               if (strncmp
-                                                   (tcon->ses->userName,
-                                                    userName,
-                                                    MAX_USERNAME_SIZE) == 0) {
-                                                       read_unlock(&GlobalSMBSeslock);
-                                                       /* matched smb session
-                                                       (user name */
-                                                       return tcon;
-                                               }
-                                       }
-                               }
-                       }
-               }
+               if (!tcon->ses || !tcon->ses->server)
+                       continue;
+
+               old_ip = tcon->ses->server->addr.sockAddr.sin_addr.s_addr;
+               cFYI(1, ("old ip addr: %x == new ip %x ?",
+                       old_ip, new_target_ip_addr));
+
+               if (old_ip != new_target_ip_addr)
+                       continue;
+
+               /* BB lock tcon, server, tcp session and increment use count? */
+               /* found a match on the TCP session */
+               /* BB check if reconnection needed */
+               cFYI(1, ("IP match, old UNC: %s new: %s",
+                       tcon->treeName, uncName));
+
+               if (strncmp(tcon->treeName, uncName, MAX_TREE_SIZE))
+                       continue;
+
+               cFYI(1, ("and old usr: %s new: %s",
+                       tcon->treeName, uncName));
+
+               if (strncmp(tcon->ses->userName, userName, MAX_USERNAME_SIZE))
+                       continue;
+
+               /* matched smb session (user name) */
+               read_unlock(&GlobalSMBSeslock);
+               return tcon;
        }
+
        read_unlock(&GlobalSMBSeslock);
        return NULL;
 }
@@ -1378,7 +1418,7 @@ connect_to_dfs_path(int xid, struct cifsSesInfo *pSesInfo,
                    const char *old_path, const struct nls_table *nls_codepage,
                    int remap)
 {
-       unsigned char *referrals = NULL;
+       struct dfs_info3_param *referrals = NULL;
        unsigned int num_referrals;
        int rc = 0;
 
@@ -1397,12 +1437,14 @@ connect_to_dfs_path(int xid, struct cifsSesInfo *pSesInfo,
 int
 get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
             const struct nls_table *nls_codepage, unsigned int *pnum_referrals,
-            unsigned char **preferrals, int remap)
+            struct dfs_info3_param **preferrals, int remap)
 {
        char *temp_unc;
        int rc = 0;
+       unsigned char *targetUNCs;
 
        *pnum_referrals = 0;
+       *preferrals = NULL;
 
        if (pSesInfo->ipc_tid == 0) {
                temp_unc = kmalloc(2 /* for slashes */ +
@@ -1422,8 +1464,10 @@ get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
                kfree(temp_unc);
        }
        if (rc == 0)
-               rc = CIFSGetDFSRefer(xid, pSesInfo, old_path, preferrals,
+               rc = CIFSGetDFSRefer(xid, pSesInfo, old_path, &targetUNCs,
                                     pnum_referrals, nls_codepage, remap);
+       /* BB map targetUNCs to dfs_info3 structures, here or
+               in CIFSGetDFSRefer BB */
 
        return rc;
 }
@@ -1469,7 +1513,7 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket,
        if (psin_server->sin_port) { /* user overrode default port */
                rc = (*csocket)->ops->connect(*csocket,
                                (struct sockaddr *) psin_server,
-                               sizeof (struct sockaddr_in), 0);
+                               sizeof(struct sockaddr_in), 0);
                if (rc >= 0)
                        connected = 1;
        }
@@ -1485,7 +1529,7 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket,
 
                        rc = (*csocket)->ops->connect(*csocket,
                                        (struct sockaddr *) psin_server,
-                                       sizeof (struct sockaddr_in), 0);
+                                       sizeof(struct sockaddr_in), 0);
                        if (rc >= 0)
                                connected = 1;
                }
@@ -1494,7 +1538,7 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket,
                psin_server->sin_port = htons(RFC1001_PORT);
                rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *)
                                              psin_server,
-                                             sizeof (struct sockaddr_in), 0);
+                                             sizeof(struct sockaddr_in), 0);
                if (rc >= 0)
                        connected = 1;
        }
@@ -1602,7 +1646,7 @@ ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket)
        if (psin_server->sin6_port) { /* user overrode default port */
                rc = (*csocket)->ops->connect(*csocket,
                                (struct sockaddr *) psin_server,
-                               sizeof (struct sockaddr_in6), 0);
+                               sizeof(struct sockaddr_in6), 0);
                if (rc >= 0)
                        connected = 1;
        }
@@ -1618,7 +1662,7 @@ ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket)
 
                        rc = (*csocket)->ops->connect(*csocket,
                                        (struct sockaddr *) psin_server,
-                                       sizeof (struct sockaddr_in6), 0);
+                                       sizeof(struct sockaddr_in6), 0);
                        if (rc >= 0)
                                connected = 1;
                }
@@ -1626,7 +1670,7 @@ ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket)
        if (!connected) {
                psin_server->sin6_port = htons(RFC1001_PORT);
                rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *)
-                                psin_server, sizeof (struct sockaddr_in6), 0);
+                                psin_server, sizeof(struct sockaddr_in6), 0);
                if (rc >= 0)
                        connected = 1;
        }
@@ -1686,8 +1730,15 @@ void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon,
                           originally at mount time */
                        if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0)
                                cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
-                       if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0)
+                       if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) {
+                               if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP)
+                                       cERROR(1, ("POSIXPATH support change"));
                                cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
+                       } else if ((cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) {
+                               cERROR(1, ("possible reconnect error"));
+                               cERROR(1,
+                                       ("server disabled POSIX path support"));
+                       }
                }
 
                cap &= CIFS_UNIX_CAP_MASK;
@@ -1717,9 +1768,8 @@ void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon,
                if (sb && (CIFS_SB(sb)->rsize > 127 * 1024)) {
                        if ((cap & CIFS_UNIX_LARGE_READ_CAP) == 0) {
                                CIFS_SB(sb)->rsize = 127 * 1024;
-#ifdef CONFIG_CIFS_DEBUG2
-                               cFYI(1, ("larger reads not supported by srv"));
-#endif
+                               cFYI(DBG2,
+                                       ("larger reads not supported by srv"));
                        }
                }
 
@@ -1742,11 +1792,40 @@ void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon,
                        cFYI(1, ("very large write cap"));
 #endif /* CIFS_DEBUG2 */
                if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) {
-                       cFYI(1, ("setting capabilities failed"));
+                       if (vol_info == NULL) {
+                               cFYI(1, ("resetting capabilities failed"));
+                       } else
+                               cERROR(1, ("Negotiating Unix capabilities "
+                                          "with the server failed.  Consider "
+                                          "mounting with the Unix Extensions\n"
+                                          "disabled, if problems are found, "
+                                          "by specifying the nounix mount "
+                                          "option."));
+
                }
        }
 }
 
+static void
+convert_delimiter(char *path, char delim)
+{
+       int i;
+       char old_delim;
+
+       if (path == NULL)
+               return;
+
+       if (delim == '/') 
+               old_delim = '\\';
+       else
+               old_delim = '/';
+
+       for (i = 0; path[i] != '\0'; i++) {
+               if (path[i] == old_delim)
+                       path[i] = delim;
+       }
+}
+
 int
 cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
           char *mount_data, const char *devname)
@@ -1769,16 +1848,13 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 
        memset(&volume_info, 0, sizeof(struct smb_vol));
        if (cifs_parse_mount_options(mount_data, devname, &volume_info)) {
-               kfree(volume_info.UNC);
-               kfree(volume_info.password);
-               kfree(volume_info.prepath);
-               FreeXid(xid);
-               return -EINVAL;
+               rc = -EINVAL;
+               goto out;
        }
 
        if (volume_info.nullauth) {
                cFYI(1, ("null user"));
-               volume_info.username = NULL;
+               volume_info.username = "";
        } else if (volume_info.username) {
                /* BB fixme parse for domain name here */
                cFYI(1, ("Username: %s", volume_info.username));
@@ -1786,11 +1862,8 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                cifserror("No username specified");
        /* In userspace mount helper we can get user name from alternate
           locations such as env variables and files on disk */
-               kfree(volume_info.UNC);
-               kfree(volume_info.password);
-               kfree(volume_info.prepath);
-               FreeXid(xid);
-               return -EINVAL;
+               rc = -EINVAL;
+               goto out;
        }
 
        if (volume_info.UNCip && volume_info.UNC) {
@@ -1809,11 +1882,8 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 
                if (rc <= 0) {
                        /* we failed translating address */
-                       kfree(volume_info.UNC);
-                       kfree(volume_info.password);
-                       kfree(volume_info.prepath);
-                       FreeXid(xid);
-                       return -EINVAL;
+                       rc = -EINVAL;
+                       goto out;
                }
 
                cFYI(1, ("UNC: %s ip: %s", volume_info.UNC, volume_info.UNCip));
@@ -1823,20 +1893,14 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                /* BB using ip addr as server name to connect to the
                   DFS root below */
                cERROR(1, ("Connecting to DFS root not implemented yet"));
-               kfree(volume_info.UNC);
-               kfree(volume_info.password);
-               kfree(volume_info.prepath);
-               FreeXid(xid);
-               return -EINVAL;
+               rc = -EINVAL;
+               goto out;
        } else /* which servers DFS root would we conect to */ {
                cERROR(1,
                       ("CIFS mount error: No UNC path (e.g. -o "
                        "unc=//192.168.1.100/public) specified"));
-               kfree(volume_info.UNC);
-               kfree(volume_info.password);
-               kfree(volume_info.prepath);
-               FreeXid(xid);
-               return -EINVAL;
+               rc = -EINVAL;
+               goto out;
        }
 
        /* this is needed for ASCII cp to Unicode converts */
@@ -1848,11 +1912,8 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                if (cifs_sb->local_nls == NULL) {
                        cERROR(1, ("CIFS mount error: iocharset %s not found",
                                 volume_info.iocharset));
-                       kfree(volume_info.UNC);
-                       kfree(volume_info.password);
-                       kfree(volume_info.prepath);
-                       FreeXid(xid);
-                       return -ELIBACC;
+                       rc = -ELIBACC;
+                       goto out;
                }
        }
 
@@ -1866,11 +1927,8 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                        &sin_server6.sin6_addr,
                        volume_info.username, &srvTcp);
        } else {
-               kfree(volume_info.UNC);
-               kfree(volume_info.password);
-               kfree(volume_info.prepath);
-               FreeXid(xid);
-               return -EINVAL;
+               rc = -EINVAL;
+               goto out;
        }
 
        if (srvTcp) {
@@ -1894,30 +1952,27 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                                   "Aborting operation"));
                        if (csocket != NULL)
                                sock_release(csocket);
-                       kfree(volume_info.UNC);
-                       kfree(volume_info.password);
-                       kfree(volume_info.prepath);
-                       FreeXid(xid);
-                       return rc;
+                       goto out;
                }
 
-               srvTcp = kmalloc(sizeof (struct TCP_Server_Info), GFP_KERNEL);
-               if (srvTcp == NULL) {
+               srvTcp = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL);
+               if (!srvTcp) {
                        rc = -ENOMEM;
                        sock_release(csocket);
-                       kfree(volume_info.UNC);
-                       kfree(volume_info.password);
-                       kfree(volume_info.prepath);
-                       FreeXid(xid);
-                       return rc;
+                       goto out;
                } else {
-                       memset(srvTcp, 0, sizeof (struct TCP_Server_Info));
                        memcpy(&srvTcp->addr.sockAddr, &sin_server,
-                               sizeof (struct sockaddr_in));
+                               sizeof(struct sockaddr_in));
                        atomic_set(&srvTcp->inFlight, 0);
                        /* BB Add code for ipv6 case too */
                        srvTcp->ssocket = csocket;
                        srvTcp->protocolType = IPV4;
+                       srvTcp->hostname = extract_hostname(volume_info.UNC);
+                       if (IS_ERR(srvTcp->hostname)) {
+                               rc = PTR_ERR(srvTcp->hostname);
+                               sock_release(csocket);
+                               goto out;
+                       }
                        init_waitqueue_head(&srvTcp->response_q);
                        init_waitqueue_head(&srvTcp->request_q);
                        INIT_LIST_HEAD(&srvTcp->pending_mid_q);
@@ -1927,18 +1982,14 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                        srvTcp->tcpStatus = CifsNew;
                        init_MUTEX(&srvTcp->tcpSem);
                        srvTcp->tsk = kthread_run((void *)(void *)cifs_demultiplex_thread, srvTcp, "cifsd");
-                       if ( IS_ERR(srvTcp->tsk) ) {
+                       if (IS_ERR(srvTcp->tsk)) {
                                rc = PTR_ERR(srvTcp->tsk);
                                cERROR(1, ("error %d create cifsd thread", rc));
                                srvTcp->tsk = NULL;
                                sock_release(csocket);
-                               kfree(volume_info.UNC);
-                               kfree(volume_info.password);
-                               kfree(volume_info.prepath);
-                               FreeXid(xid);
-                               return rc;
+                               kfree(srvTcp->hostname);
+                               goto out;
                        }
-                       wait_for_completion(&cifsd_complete);
                        rc = 0;
                        memcpy(srvTcp->workstation_RFC1001_name,
                                volume_info.source_rfc1001_name, 16);
@@ -1950,9 +2001,15 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 
        if (existingCifsSes) {
                pSesInfo = existingCifsSes;
-               cFYI(1, ("Existing smb sess found"));
-               kfree(volume_info.password);
-               /* volume_info.UNC freed at end of function */
+               cFYI(1, ("Existing smb sess found (status=%d)",
+                       pSesInfo->status));
+               down(&pSesInfo->sesSem);
+               if (pSesInfo->status == CifsNeedReconnect) {
+                       cFYI(1, ("Session needs reconnect"));
+                       rc = cifs_setup_session(xid, pSesInfo,
+                                               cifs_sb->local_nls);
+               }
+               up(&pSesInfo->sesSem);
        } else if (!rc) {
                cFYI(1, ("Existing smb sess not found"));
                pSesInfo = sesInfoAlloc();
@@ -1966,8 +2023,11 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 
                if (!rc) {
                        /* volume_info.password freed at unmount */
-                       if (volume_info.password)
+                       if (volume_info.password) {
                                pSesInfo->password = volume_info.password;
+                               /* set to NULL to prevent freeing on exit */
+                               volume_info.password = NULL;
+                       }
                        if (volume_info.username)
                                strncpy(pSesInfo->userName,
                                        volume_info.username,
@@ -1989,8 +2049,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                        up(&pSesInfo->sesSem);
                        if (!rc)
                                atomic_inc(&srvTcp->socketUseCount);
-               } else
-                       kfree(volume_info.password);
+               }
        }
 
        /* search for existing tcon to this server share */
@@ -2031,7 +2090,11 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                cifs_sb->prepath = volume_info.prepath;
                if (cifs_sb->prepath) {
                        cifs_sb->prepathlen = strlen(cifs_sb->prepath);
-                       cifs_sb->prepath[0] = CIFS_DIR_SEP(cifs_sb);
+                       /* we can not convert the / to \ in the path
+                       separators in the prefixpath yet because we do not
+                       know (until reset_cifs_unix_caps is called later)
+                       whether POSIX PATH CAP is available. We normalize
+                       the / to \ after reset_cifs_unix_caps is called */
                        volume_info.prepath = NULL;
                } else
                        cifs_sb->prepathlen = 0;
@@ -2095,9 +2158,8 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                                                "", cifs_sb->local_nls,
                                                cifs_sb->mnt_cifs_flags &
                                                  CIFS_MOUNT_MAP_SPECIAL_CHR);
-                                       kfree(volume_info.UNC);
-                                       FreeXid(xid);
-                                       return -ENODEV;
+                                       rc = -ENODEV;
+                                       goto out;
                                } else {
                                        /* BB Do we need to wrap sesSem around
                                         * this TCon call and Unix SetFS as
@@ -2134,15 +2196,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                        srvTcp->tcpStatus = CifsExiting;
                        spin_unlock(&GlobalMid_Lock);
                        if (srvTcp->tsk) {
-                               struct task_struct *tsk;
                                /* If we could verify that kthread_stop would
                                   always wake up processes blocked in
                                   tcp in recv_mesg then we could remove the
                                   send_sig call */
                                force_sig(SIGKILL, srvTcp->tsk);
-                               tsk = srvTcp->tsk;
-                               if (tsk)
-                                       kthread_stop(tsk);
+                               kthread_stop(srvTcp->tsk);
                        }
                }
                 /* If find_unc succeeded then rc == 0 so we can not end */
@@ -2158,15 +2217,19 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                                        if ((temp_rc == -ESHUTDOWN) &&
                                            (pSesInfo->server) &&
                                            (pSesInfo->server->tsk)) {
-                                               struct task_struct *tsk;
                                                force_sig(SIGKILL,
                                                        pSesInfo->server->tsk);
-                                               tsk = pSesInfo->server->tsk;
-                                               if (tsk)
-                                                       kthread_stop(tsk);
+                                               kthread_stop(pSesInfo->server->tsk);
                                        }
-                               } else
+                               } else {
                                        cFYI(1, ("No session or bad tcon"));
+                                       if ((pSesInfo->server) &&
+                                           (pSesInfo->server->tsk)) {
+                                               force_sig(SIGKILL,
+                                                       pSesInfo->server->tsk);
+                                               kthread_stop(pSesInfo->server->tsk);
+                                       }
+                               }
                                sesInfoFree(pSesInfo);
                                /* pSesInfo = NULL; */
                        }
@@ -2177,8 +2240,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                tcon->ses = pSesInfo;
 
                /* do not care if following two calls succeed - informational */
-               CIFSSMBQFSDeviceInfo(xid, tcon);
-               CIFSSMBQFSAttributeInfo(xid, tcon);
+               if (!tcon->ipc) {
+                       CIFSSMBQFSDeviceInfo(xid, tcon);
+                       CIFSSMBQFSAttributeInfo(xid, tcon);
+               }
 
                /* tell server which Unix caps we support */
                if (tcon->ses->capabilities & CAP_UNIX)
@@ -2188,11 +2253,15 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                else
                        tcon->unix_ext = 0; /* server does not support them */
 
+               /* convert forward to back slashes in prepath here if needed */
+               if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0)
+                       convert_delimiter(cifs_sb->prepath,
+                                         CIFS_DIR_SEP(cifs_sb));
+
                if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) {
                        cifs_sb->rsize = 1024 * 127;
-#ifdef CONFIG_CIFS_DEBUG2
-                       cFYI(1, ("no very large read support, rsize now 127K"));
-#endif
+                       cFYI(DBG2,
+                               ("no very large read support, rsize now 127K"));
                }
                if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X))
                        cifs_sb->wsize = min(cifs_sb->wsize,
@@ -2208,6 +2277,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
        (in which case it is not needed anymore) but when new sesion is created
        the password ptr is put in the new session structure (in which case the
        password will be freed at unmount time) */
+out:
+       /* zero out password before freeing */
+       if (volume_info.password != NULL) {
+               memset(volume_info.password, 0, strlen(volume_info.password));
+               kfree(volume_info.password);
+       }
        kfree(volume_info.UNC);
        kfree(volume_info.prepath);
        FreeXid(xid);
@@ -2351,7 +2426,7 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
        pSMB->req_no_secext.ByteCount = cpu_to_le16(count);
 
        rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
-                        &bytes_returned, 1);
+                        &bytes_returned, CIFS_LONG_OP);
        if (rc) {
 /* rc = map_smb_to_linux_error(smb_buffer_response); now done in SendReceive */
        } else if ((smb_buffer_response->WordCount == 3)
@@ -2518,15 +2593,14 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
 sesssetup_nomem:       /* do not return an error on nomem for the info strings,
                           since that could make reconnection harder, and
                           reconnection might be needed to free memory */
-       if (smb_buffer)
-               cifs_buf_release(smb_buffer);
+       cifs_buf_release(smb_buffer);
 
        return rc;
 }
 
 static int
 CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
-                             struct cifsSesInfo *ses, int *pNTLMv2_flag,
+                             struct cifsSesInfo *ses, bool *pNTLMv2_flag,
                              const struct nls_table *nls_codepage)
 {
        struct smb_hdr *smb_buffer;
@@ -2539,7 +2613,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
        int remaining_words = 0;
        int bytes_returned = 0;
        int len;
-       int SecurityBlobLength = sizeof (NEGOTIATE_MESSAGE);
+       int SecurityBlobLength = sizeof(NEGOTIATE_MESSAGE);
        PNEGOTIATE_MESSAGE SecurityBlob;
        PCHALLENGE_MESSAGE SecurityBlob2;
        __u32 negotiate_flags, capabilities;
@@ -2549,7 +2623,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
        if (ses == NULL)
                return -EINVAL;
        domain = ses->domainName;
-       *pNTLMv2_flag = FALSE;
+       *pNTLMv2_flag = false;
        smb_buffer = cifs_buf_get();
        if (smb_buffer == NULL) {
                return -ENOMEM;
@@ -2656,7 +2730,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
        pSMB->req.ByteCount = cpu_to_le16(count);
 
        rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
-                        &bytes_returned, 1);
+                        &bytes_returned, CIFS_LONG_OP);
 
        if (smb_buffer_response->Status.CifsError ==
            cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))
@@ -2702,7 +2776,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
                                       CIFS_CRYPTO_KEY_SIZE);
                                if (SecurityBlob2->NegotiateFlags &
                                        cpu_to_le32(NTLMSSP_NEGOTIATE_NTLMV2))
-                                       *pNTLMv2_flag = TRUE;
+                                       *pNTLMv2_flag = true;
 
                                if ((SecurityBlob2->NegotiateFlags &
                                        cpu_to_le32(NTLMSSP_NEGOTIATE_ALWAYS_SIGN))
@@ -2857,15 +2931,14 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
                rc = -EIO;
        }
 
-       if (smb_buffer)
-               cifs_buf_release(smb_buffer);
+       cifs_buf_release(smb_buffer);
 
        return rc;
 }
 static int
 CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
-               char *ntlm_session_key, int ntlmv2_flag,
-               const struct nls_table *nls_codepage)
+                       char *ntlm_session_key, bool ntlmv2_flag,
+                       const struct nls_table *nls_codepage)
 {
        struct smb_hdr *smb_buffer;
        struct smb_hdr *smb_buffer_response;
@@ -2878,7 +2951,7 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
        int remaining_words = 0;
        int bytes_returned = 0;
        int len;
-       int SecurityBlobLength = sizeof (AUTHENTICATE_MESSAGE);
+       int SecurityBlobLength = sizeof(AUTHENTICATE_MESSAGE);
        PAUTHENTICATE_MESSAGE SecurityBlob;
        __u32 negotiate_flags, capabilities;
        __u16 count;
@@ -2893,8 +2966,8 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
                return -ENOMEM;
        }
        smb_buffer_response = smb_buffer;
-       pSMB = (SESSION_SETUP_ANDX *) smb_buffer;
-       pSMBr = (SESSION_SETUP_ANDX *) smb_buffer_response;
+       pSMB = (SESSION_SETUP_ANDX *)smb_buffer;
+       pSMBr = (SESSION_SETUP_ANDX *)smb_buffer_response;
 
        /* send SMBsessionSetup here */
        header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
@@ -2913,7 +2986,7 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
                smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
 
        capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
-           CAP_EXTENDED_SECURITY;
+                       CAP_EXTENDED_SECURITY;
        if (ses->capabilities & CAP_UNICODE) {
                smb_buffer->Flags2 |= SMBFLG2_UNICODE;
                capabilities |= CAP_UNICODE;
@@ -2928,15 +3001,14 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
        }
        pSMB->req.Capabilities = cpu_to_le32(capabilities);
 
-       bcc_ptr = (char *) &pSMB->req.SecurityBlob;
-       SecurityBlob = (PAUTHENTICATE_MESSAGE) bcc_ptr;
+       bcc_ptr = (char *)&pSMB->req.SecurityBlob;
+       SecurityBlob = (PAUTHENTICATE_MESSAGE)bcc_ptr;
        strncpy(SecurityBlob->Signature, NTLMSSP_SIGNATURE, 8);
        SecurityBlob->MessageType = NtLmAuthenticate;
        bcc_ptr += SecurityBlobLength;
-       negotiate_flags =
-           NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_REQUEST_TARGET |
-           NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO |
-           0x80000000 | NTLMSSP_NEGOTIATE_128;
+       negotiate_flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_REQUEST_TARGET |
+                       NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO |
+                       0x80000000 | NTLMSSP_NEGOTIATE_128;
        if (sign_CIFS_PDUs)
                negotiate_flags |= /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN |*/ NTLMSSP_NEGOTIATE_SIGN;
        if (ntlmv2_flag)
@@ -3085,14 +3157,13 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
        pSMB->req.ByteCount = cpu_to_le16(count);
 
        rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
-                        &bytes_returned, 1);
+                        &bytes_returned, CIFS_LONG_OP);
        if (rc) {
-/*    rc = map_smb_to_linux_error(smb_buffer_response);  *//* done in SendReceive now */
-       } else if ((smb_buffer_response->WordCount == 3)
-                  || (smb_buffer_response->WordCount == 4)) {
+/*   rc = map_smb_to_linux_error(smb_buffer_response) done in SendReceive now */
+       } else if ((smb_buffer_response->WordCount == 3) ||
+                  (smb_buffer_response->WordCount == 4)) {
                __u16 action = le16_to_cpu(pSMBr->resp.Action);
-               __u16 blob_len =
-                   le16_to_cpu(pSMBr->resp.SecurityBlobLength);
+               __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength);
                if (action & GUEST_LOGIN)
                        cFYI(1, (" Guest login")); /* BB Should we set anything
                                                         in SesInfo struct ? */
@@ -3246,28 +3317,24 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
                                                bcc_ptr[0] = 0;
                                                bcc_ptr++;
                                        } else
-                                               cFYI(1,
-                                                    ("field of length %d "
+                                               cFYI(1, ("field of length %d "
                                                   "extends beyond end of smb ",
                                                      len));
                                }
                        } else {
-                               cERROR(1,
-                                      (" Security Blob extends beyond end "
+                               cERROR(1, ("Security Blob extends beyond end "
                                        "of SMB"));
                        }
                } else {
                        cERROR(1, ("No session structure passed in."));
                }
        } else {
-               cERROR(1,
-                      (" Invalid Word count %d: ",
+               cERROR(1, ("Invalid Word count %d: ",
                        smb_buffer_response->WordCount));
                rc = -EIO;
        }
 
-       if (smb_buffer)
-               cifs_buf_release(smb_buffer);
+       cifs_buf_release(smb_buffer);
 
        return rc;
 }
@@ -3366,7 +3433,8 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
        pSMB->hdr.smb_buf_length += count;
        pSMB->ByteCount = cpu_to_le16(count);
 
-       rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length, 0);
+       rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length,
+                        CIFS_STD_OP);
 
        /* if (rc) rc = map_smb_to_linux_error(smb_buffer_response); */
        /* above now done in SendReceive */
@@ -3376,6 +3444,18 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
                bcc_ptr = pByteArea(smb_buffer_response);
                length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2);
                /* skip service field (NB: this field is always ASCII) */
+               if (length == 3) {
+                       if ((bcc_ptr[0] == 'I') && (bcc_ptr[1] == 'P') &&
+                           (bcc_ptr[2] == 'C')) {
+                               cFYI(1, ("IPC connection"));
+                               tcon->ipc = 1;
+                       }
+               } else if (length == 2) {
+                       if ((bcc_ptr[0] == 'A') && (bcc_ptr[1] == ':')) {
+                               /* the most common case */
+                               cFYI(1, ("disk share connection"));
+                       }
+               }
                bcc_ptr += length + 1;
                strncpy(tcon->treeName, tree, MAX_TREE_SIZE);
                if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
@@ -3386,9 +3466,11 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
                                kfree(tcon->nativeFileSystem);
                                tcon->nativeFileSystem =
                                    kzalloc(length + 2, GFP_KERNEL);
-                               cifs_strfromUCS_le(tcon->nativeFileSystem,
-                                                  (__le16 *) bcc_ptr,
-                                                  length, nls_codepage);
+                               if (tcon->nativeFileSystem)
+                                       cifs_strfromUCS_le(
+                                               tcon->nativeFileSystem,
+                                               (__le16 *) bcc_ptr,
+                                               length, nls_codepage);
                                bcc_ptr += 2 * length;
                                bcc_ptr[0] = 0; /* null terminate the string */
                                bcc_ptr[1] = 0;
@@ -3403,8 +3485,9 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
                                kfree(tcon->nativeFileSystem);
                                tcon->nativeFileSystem =
                                    kzalloc(length + 1, GFP_KERNEL);
-                               strncpy(tcon->nativeFileSystem, bcc_ptr,
-                                       length);
+                               if (tcon->nativeFileSystem)
+                                       strncpy(tcon->nativeFileSystem, bcc_ptr,
+                                               length);
                        }
                        /* else do not bother copying these information fields*/
                }
@@ -3420,8 +3503,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
                ses->ipc_tid = smb_buffer_response->Tid;
        }
 
-       if (smb_buffer)
-               cifs_buf_release(smb_buffer);
+       cifs_buf_release(smb_buffer);
        return rc;
 }
 
@@ -3443,6 +3525,7 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
                        FreeXid(xid);
                        return 0;
                }
+               DeleteTconOplockQEntries(cifs_sb->tcon);
                tconInfoFree(cifs_sb->tcon);
                if ((ses) && (ses->server)) {
                        /* save off task so we do not refer to ses later */
@@ -3471,12 +3554,10 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
        cifs_sb->prepath = NULL;
        kfree(tmp);
        if (ses)
-               schedule_timeout_interruptible(msecs_to_jiffies(500));
-       if (ses)
                sesInfoFree(ses);
 
        FreeXid(xid);
-       return rc;      /* BB check if we should always return zero here */
+       return rc;
 }
 
 int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo,
@@ -3484,7 +3565,7 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo,
 {
        int rc = 0;
        char ntlm_session_key[CIFS_SESS_KEY_SIZE];
-       int ntlmv2_flag = FALSE;
+       bool ntlmv2_flag = false;
        int first_time = 0;
 
        /* what if server changes its buffer size after dropping the session? */