]> err.no Git - linux-2.6/blobdiff - fs/cifs/connect.c
[POWERPC] fsl_lbc: implement few UPM routines
[linux-2.6] / fs / cifs / connect.c
index c52a76ff4bb9db8c2625ed6213dc0a8b026664d6..8dbfa97cd18ca6ae60ba6621e8e9214cb7a1641d 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
@@ -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;
 }
@@ -1689,8 +1722,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;
@@ -1720,9 +1760,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"));
                        }
                }
 
@@ -1759,6 +1798,26 @@ void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon,
        }
 }
 
+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)
@@ -1781,11 +1840,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 +1854,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 +1874,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 +1885,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 +1904,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 +1919,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 +1944,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 +1959,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 +1974,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 +1994,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 +2016,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 +2042,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 */
@@ -2042,7 +2083,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;
@@ -2106,9 +2151,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
@@ -2211,11 +2255,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,
@@ -2231,6 +2279,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 +2428,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 +2732,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 +3159,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 +3435,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 +3560,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,