]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/cifs/connect.c
ACPI hibernation: Call _PTS before suspending devices
[linux-2.6-omap-h63xx.git] / fs / cifs / connect.c
index 1102160f6661069e725345642f4af2c7439fe6fa..65d0ba72e78f1e304af44941c74a4a1f9adb2a5d 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
@@ -160,7 +160,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));
@@ -438,9 +438,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;
@@ -752,6 +752,7 @@ multi_t2_fnd:
        }
        write_unlock(&GlobalSMBSeslock);
 
+       kfree(server->hostname);
        kfree(server);
        if (length  > 0)
                mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
@@ -760,6 +761,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)
@@ -1381,7 +1410,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;
 
@@ -1400,12 +1429,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 */ +
@@ -1425,8 +1456,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;
 }
@@ -1781,11 +1814,8 @@ 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) {
@@ -1798,11 +1828,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) {
@@ -1821,11 +1848,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));
@@ -1835,20 +1859,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 */
@@ -1860,11 +1878,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;
                }
        }
 
@@ -1878,11 +1893,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) {
@@ -1906,22 +1918,14 @@ 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 = 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 {
                        memcpy(&srvTcp->addr.sockAddr, &sin_server,
                                sizeof(struct sockaddr_in));
@@ -1929,6 +1933,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                        /* 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);
@@ -1938,16 +1948,13 @@ 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;
@@ -1961,9 +1968,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();
@@ -1977,8 +1990,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,
@@ -2000,8 +2016,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 */
@@ -2106,9 +2121,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
@@ -2231,6 +2245,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);
@@ -2374,7 +2394,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)
@@ -2678,7 +2698,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))
@@ -3105,7 +3125,7 @@ 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) ||
@@ -3381,7 +3401,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 */
@@ -3505,7 +3526,7 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
                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,