cifs: account for IPv6 in ses->serverName and clean up netbios name handling
[safe/jmp/linux-2.6] / fs / cifs / connect.c
index 44130e0..65c1219 100644 (file)
@@ -124,7 +124,7 @@ static int
 cifs_reconnect(struct TCP_Server_Info *server)
 {
        int rc = 0;
-       struct list_head *tmp;
+       struct list_head *tmp, *tmp2;
        struct cifsSesInfo *ses;
        struct cifsTconInfo *tcon;
        struct mid_q_entry *mid_entry;
@@ -149,15 +149,14 @@ cifs_reconnect(struct TCP_Server_Info *server)
                ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
                ses->need_reconnect = true;
                ses->ipc_tid = 0;
-       }
-       read_unlock(&cifs_tcp_ses_lock);
-       list_for_each(tmp, &GlobalTreeConnectionList) {
-               tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
-               if ((tcon->ses) && (tcon->ses->server == server))
+               list_for_each(tmp2, &ses->tcon_list) {
+                       tcon = list_entry(tmp2, struct cifsTconInfo, tcon_list);
                        tcon->need_reconnect = true;
+               }
        }
+       read_unlock(&cifs_tcp_ses_lock);
        /* do not want to be sending data on a socket we are freeing */
-       down(&server->tcpSem);
+       mutex_lock(&server->srv_mutex);
        if (server->ssocket) {
                cFYI(1, ("State: 0x%x Flags: 0x%lx", server->ssocket->state,
                        server->ssocket->flags));
@@ -183,7 +182,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
                }
        }
        spin_unlock(&GlobalMid_Lock);
-       up(&server->tcpSem);
+       mutex_unlock(&server->srv_mutex);
 
        while ((server->tcpStatus != CifsExiting) &&
               (server->tcpStatus != CifsGood)) {
@@ -777,7 +776,7 @@ multi_t2_fnd:
                set_current_state(TASK_RUNNING);
        }
 
-       return 0;
+       module_put_and_exit(0);
 }
 
 /* extract the host portion of the UNC string */
@@ -1368,7 +1367,6 @@ cifs_find_tcp_session(struct sockaddr *addr)
        list_for_each(tmp, &cifs_tcp_ses_list) {
                server = list_entry(tmp, struct TCP_Server_Info,
                                    tcp_ses_list);
-
                /*
                 * the demux thread can exit on its own while still in CifsNew
                 * so don't accept any sockets in that state. Since the
@@ -1389,6 +1387,7 @@ cifs_find_tcp_session(struct sockaddr *addr)
 
                ++server->srv_count;
                write_unlock(&cifs_tcp_ses_lock);
+               cFYI(1, ("Existing tcp session with server found"));
                return server;
        }
        write_unlock(&cifs_tcp_ses_lock);
@@ -1462,6 +1461,52 @@ cifs_put_smb_ses(struct cifsSesInfo *ses)
        cifs_put_tcp_session(server);
 }
 
+static struct cifsTconInfo *
+cifs_find_tcon(struct cifsSesInfo *ses, const char *unc)
+{
+       struct list_head *tmp;
+       struct cifsTconInfo *tcon;
+
+       write_lock(&cifs_tcp_ses_lock);
+       list_for_each(tmp, &ses->tcon_list) {
+               tcon = list_entry(tmp, struct cifsTconInfo, tcon_list);
+               if (tcon->tidStatus == CifsExiting)
+                       continue;
+               if (strncmp(tcon->treeName, unc, MAX_TREE_SIZE))
+                       continue;
+
+               ++tcon->tc_count;
+               write_unlock(&cifs_tcp_ses_lock);
+               return tcon;
+       }
+       write_unlock(&cifs_tcp_ses_lock);
+       return NULL;
+}
+
+static void
+cifs_put_tcon(struct cifsTconInfo *tcon)
+{
+       int xid;
+       struct cifsSesInfo *ses = tcon->ses;
+
+       write_lock(&cifs_tcp_ses_lock);
+       if (--tcon->tc_count > 0) {
+               write_unlock(&cifs_tcp_ses_lock);
+               return;
+       }
+
+       list_del_init(&tcon->tcon_list);
+       write_unlock(&cifs_tcp_ses_lock);
+
+       xid = GetXid();
+       CIFSSMBTDis(xid, tcon);
+       _FreeXid(xid);
+
+       DeleteTconOplockQEntries(tcon);
+       tconInfoFree(tcon);
+       cifs_put_smb_ses(ses);
+}
+
 int
 get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
             const struct nls_table *nls_codepage, unsigned int *pnum_referrals,
@@ -1645,22 +1690,30 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket,
                if (ses_init_buf) {
                        ses_init_buf->trailer.session_req.called_len = 32;
                        if (target_name && (target_name[0] != 0)) {
-                               rfc1002mangle(ses_init_buf->trailer.session_req.called_name,
-                                       target_name, 16);
+                               rfc1002mangle(ses_init_buf->trailer.
+                                               session_req.called_name,
+                                             target_name,
+                                             RFC1001_NAME_LEN_WITH_NULL);
                        } else {
-                               rfc1002mangle(ses_init_buf->trailer.session_req.called_name,
-                                       DEFAULT_CIFS_CALLED_NAME, 16);
+                               rfc1002mangle(ses_init_buf->trailer.
+                                               session_req.called_name,
+                                             DEFAULT_CIFS_CALLED_NAME,
+                                             RFC1001_NAME_LEN_WITH_NULL);
                        }
 
                        ses_init_buf->trailer.session_req.calling_len = 32;
                        /* calling name ends in null (byte 16) from old smb
                        convention. */
                        if (netbios_name && (netbios_name[0] != 0)) {
-                               rfc1002mangle(ses_init_buf->trailer.session_req.calling_name,
-                                       netbios_name, 16);
+                               rfc1002mangle(ses_init_buf->trailer.
+                                               session_req.calling_name,
+                                             netbios_name,
+                                             RFC1001_NAME_LEN_WITH_NULL);
                        } else {
-                               rfc1002mangle(ses_init_buf->trailer.session_req.calling_name,
-                                       "LINUX_CIFS_CLNT", 16);
+                               rfc1002mangle(ses_init_buf->trailer.
+                                               session_req.calling_name,
+                                             "LINUX_CIFS_CLNT",
+                                             RFC1001_NAME_LEN_WITH_NULL);
                        }
                        ses_init_buf->trailer.session_req.scope1 = 0;
                        ses_init_buf->trailer.session_req.scope2 = 0;
@@ -2076,9 +2129,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
        }
 
        srvTcp = cifs_find_tcp_session(&addr);
-       if (srvTcp) {
-               cFYI(1, ("Existing tcp session with server found"));
-       } else {        /* create socket */
+       if (!srvTcp) { /* create socket */
                if (addr.sa_family == AF_INET6) {
                        cFYI(1, ("attempting ipv6 connect"));
                        /* BB should we allow ipv6 on port 139? */
@@ -2132,11 +2183,18 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                        to the struct since the kernel thread not created yet
                        so no need to spinlock this init of tcpStatus */
                        srvTcp->tcpStatus = CifsNew;
-                       init_MUTEX(&srvTcp->tcpSem);
+                       mutex_init(&srvTcp->srv_mutex);
+
+                       /*
+                        * since we're in a cifs function already, we know that
+                        * this will succeed. No need for try_module_get().
+                        */
+                       __module_get(THIS_MODULE);
                        srvTcp->tsk = kthread_run((void *)(void *)cifs_demultiplex_thread, srvTcp, "cifsd");
                        if (IS_ERR(srvTcp->tsk)) {
                                rc = PTR_ERR(srvTcp->tsk);
                                cERROR(1, ("error %d create cifsd thread", rc));
+                               module_put(THIS_MODULE);
                                srvTcp->tsk = NULL;
                                sock_release(csocket);
                                kfree(srvTcp->hostname);
@@ -2144,9 +2202,11 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                        }
                        rc = 0;
                        memcpy(srvTcp->workstation_RFC1001_name,
-                               volume_info.source_rfc1001_name, 16);
+                               volume_info.source_rfc1001_name,
+                               RFC1001_NAME_LEN_WITH_NULL);
                        memcpy(srvTcp->server_RFC1001_name,
-                               volume_info.target_rfc1001_name, 16);
+                               volume_info.target_rfc1001_name,
+                               RFC1001_NAME_LEN_WITH_NULL);
                        srvTcp->sequence_number = 0;
                        INIT_LIST_HEAD(&srvTcp->tcp_ses_list);
                        INIT_LIST_HEAD(&srvTcp->smb_ses_list);
@@ -2185,8 +2245,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 
                /* new SMB session uses our srvTcp ref */
                pSesInfo->server = srvTcp;
-               sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
-                       NIPQUAD(sin_server->sin_addr.s_addr));
+               if (addr.sa_family == AF_INET6)
+                       sprintf(pSesInfo->serverName, NIP6_FMT,
+                               NIP6(sin_server6->sin6_addr));
+               else
+                       sprintf(pSesInfo->serverName, NIPQUAD_FMT,
+                               NIPQUAD(sin_server->sin_addr.s_addr));
 
                write_lock(&cifs_tcp_ses_lock);
                list_add(&pSesInfo->smb_ses_list, &srvTcp->smb_ses_list);
@@ -2222,8 +2286,11 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
        if (!rc) {
                setup_cifs_sb(&volume_info, cifs_sb);
 
+               tcon = cifs_find_tcon(pSesInfo, volume_info.UNC);
                if (tcon) {
                        cFYI(1, ("Found match on UNC path"));
+                       /* existing tcon already has a reference */
+                       cifs_put_smb_ses(pSesInfo);
                        if (tcon->seal != volume_info.seal)
                                cERROR(1, ("transport encryption setting "
                                           "conflicts with existing tid"));
@@ -2233,10 +2300,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                                rc = -ENOMEM;
                                goto mount_fail_check;
                        }
+                       tcon->ses = pSesInfo;
 
                        /* check for null share name ie connect to dfs root */
-
-                       /* BB check if works for exactly length 3 strings */
                        if ((strchr(volume_info.UNC + 3, '\\') == NULL)
                            && (strchr(volume_info.UNC + 3, '/') == NULL)) {
                                /* rc = connect_to_dfs_path(...) */
@@ -2259,6 +2325,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                        if (rc)
                                goto mount_fail_check;
                        tcon->seal = volume_info.seal;
+                       write_lock(&cifs_tcp_ses_lock);
+                       list_add(&tcon->tcon_list, &pSesInfo->tcon_list);
+                       write_unlock(&cifs_tcp_ses_lock);
                }
 
                /* we can have only one retry value for a connection
@@ -2285,51 +2354,43 @@ mount_fail_check:
                /* If find_unc succeeded then rc == 0 so we can not end */
                /* up accidently freeing someone elses tcon struct */
                if (tcon)
-                       tconInfoFree(tcon);
-
-               /* should also end up putting our tcp session ref if needed */
-               if (pSesInfo)
+                       cifs_put_tcon(tcon);
+               else if (pSesInfo)
                        cifs_put_smb_ses(pSesInfo);
                else
                        cifs_put_tcp_session(srvTcp);
-       } else {
-               atomic_inc(&tcon->useCount);
-               cifs_sb->tcon = tcon;
-               tcon->ses = pSesInfo;
-
-               /* do not care if following two calls succeed - informational */
-               if (!tcon->ipc) {
-                       CIFSSMBQFSDeviceInfo(xid, tcon);
-                       CIFSSMBQFSAttributeInfo(xid, tcon);
-               }
+               goto out;
+       }
+       cifs_sb->tcon = tcon;
 
-               /* tell server which Unix caps we support */
-               if (tcon->ses->capabilities & CAP_UNIX)
-                       /* reset of caps checks mount to see if unix extensions
-                          disabled for just this mount */
-                       reset_cifs_unix_caps(xid, tcon, sb, &volume_info);
-               else
-                       tcon->unix_ext = 0; /* server does not support them */
+       /* do not care if following two calls succeed - informational */
+       if (!tcon->ipc) {
+               CIFSSMBQFSDeviceInfo(xid, tcon);
+               CIFSSMBQFSAttributeInfo(xid, tcon);
+       }
 
-               /* 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));
+       /* tell server which Unix caps we support */
+       if (tcon->ses->capabilities & CAP_UNIX)
+               /* reset of caps checks mount to see if unix extensions
+                  disabled for just this mount */
+               reset_cifs_unix_caps(xid, tcon, sb, &volume_info);
+       else
+               tcon->unix_ext = 0; /* server does not support them */
 
-               if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) {
-                       cifs_sb->rsize = 1024 * 127;
-                       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,
-                                            (tcon->ses->server->maxBuf -
-                                             MAX_CIFS_HDR_SIZE));
-               if (!(tcon->ses->capabilities & CAP_LARGE_READ_X))
-                       cifs_sb->rsize = min(cifs_sb->rsize,
-                                            (tcon->ses->server->maxBuf -
-                                             MAX_CIFS_HDR_SIZE));
+       /* 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;
+               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,
+                              (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE));
+       if (!(tcon->ses->capabilities & CAP_LARGE_READ_X))
+               cifs_sb->rsize = min(cifs_sb->rsize,
+                              (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE));
 
        /* volume_info.password is freed above when existing session found
        (in which case it is not needed anymore) but when new sesion is created
@@ -3571,23 +3632,10 @@ int
 cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
 {
        int rc = 0;
-       int xid;
-       struct cifsSesInfo *ses = NULL;
        char *tmp;
 
-       xid = GetXid();
-
-       if (cifs_sb->tcon) {
-               ses = cifs_sb->tcon->ses; /* save ptr to ses before delete tcon!*/
-               rc = CIFSSMBTDis(xid, cifs_sb->tcon);
-               if (rc == -EBUSY) {
-                       FreeXid(xid);
-                       return 0;
-               }
-               DeleteTconOplockQEntries(cifs_sb->tcon);
-               tconInfoFree(cifs_sb->tcon);
-               cifs_put_smb_ses(ses);
-       }
+       if (cifs_sb->tcon)
+               cifs_put_tcon(cifs_sb->tcon);
 
        cifs_sb->tcon = NULL;
        tmp = cifs_sb->prepath;
@@ -3595,7 +3643,6 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
        cifs_sb->prepath = NULL;
        kfree(tmp);
 
-       FreeXid(xid);
        return rc;
 }