diff options
Diffstat (limited to 'fs/cifs/connect.c')
-rw-r--r-- | fs/cifs/connect.c | 226 |
1 files changed, 125 insertions, 101 deletions
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index a0314259f94d..44130e052e0b 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
@@ -144,23 +144,18 @@ cifs_reconnect(struct TCP_Server_Info *server) | |||
144 | 144 | ||
145 | /* before reconnecting the tcp session, mark the smb session (uid) | 145 | /* before reconnecting the tcp session, mark the smb session (uid) |
146 | and the tid bad so they are not used until reconnected */ | 146 | and the tid bad so they are not used until reconnected */ |
147 | read_lock(&GlobalSMBSeslock); | 147 | read_lock(&cifs_tcp_ses_lock); |
148 | list_for_each(tmp, &GlobalSMBSessionList) { | 148 | list_for_each(tmp, &server->smb_ses_list) { |
149 | ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); | 149 | ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); |
150 | if (ses->server) { | 150 | ses->need_reconnect = true; |
151 | if (ses->server == server) { | 151 | ses->ipc_tid = 0; |
152 | ses->need_reconnect = true; | ||
153 | ses->ipc_tid = 0; | ||
154 | } | ||
155 | } | ||
156 | /* else tcp and smb sessions need reconnection */ | ||
157 | } | 152 | } |
153 | read_unlock(&cifs_tcp_ses_lock); | ||
158 | list_for_each(tmp, &GlobalTreeConnectionList) { | 154 | list_for_each(tmp, &GlobalTreeConnectionList) { |
159 | tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); | 155 | tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); |
160 | if ((tcon->ses) && (tcon->ses->server == server)) | 156 | if ((tcon->ses) && (tcon->ses->server == server)) |
161 | tcon->need_reconnect = true; | 157 | tcon->need_reconnect = true; |
162 | } | 158 | } |
163 | read_unlock(&GlobalSMBSeslock); | ||
164 | /* do not want to be sending data on a socket we are freeing */ | 159 | /* do not want to be sending data on a socket we are freeing */ |
165 | down(&server->tcpSem); | 160 | down(&server->tcpSem); |
166 | if (server->ssocket) { | 161 | if (server->ssocket) { |
@@ -696,29 +691,29 @@ multi_t2_fnd: | |||
696 | if (smallbuf) /* no sense logging a debug message if NULL */ | 691 | if (smallbuf) /* no sense logging a debug message if NULL */ |
697 | cifs_small_buf_release(smallbuf); | 692 | cifs_small_buf_release(smallbuf); |
698 | 693 | ||
699 | read_lock(&GlobalSMBSeslock); | 694 | /* |
695 | * BB: we shouldn't have to do any of this. It shouldn't be | ||
696 | * possible to exit from the thread with active SMB sessions | ||
697 | */ | ||
698 | read_lock(&cifs_tcp_ses_lock); | ||
700 | if (list_empty(&server->pending_mid_q)) { | 699 | if (list_empty(&server->pending_mid_q)) { |
701 | /* loop through server session structures attached to this and | 700 | /* loop through server session structures attached to this and |
702 | mark them dead */ | 701 | mark them dead */ |
703 | list_for_each(tmp, &GlobalSMBSessionList) { | 702 | list_for_each(tmp, &server->smb_ses_list) { |
704 | ses = | 703 | ses = list_entry(tmp, struct cifsSesInfo, |
705 | list_entry(tmp, struct cifsSesInfo, | 704 | smb_ses_list); |
706 | cifsSessionList); | 705 | ses->status = CifsExiting; |
707 | if (ses->server == server) { | 706 | ses->server = NULL; |
708 | ses->status = CifsExiting; | ||
709 | ses->server = NULL; | ||
710 | } | ||
711 | } | 707 | } |
712 | read_unlock(&GlobalSMBSeslock); | 708 | read_unlock(&cifs_tcp_ses_lock); |
713 | } else { | 709 | } else { |
714 | /* although we can not zero the server struct pointer yet, | 710 | /* although we can not zero the server struct pointer yet, |
715 | since there are active requests which may depnd on them, | 711 | since there are active requests which may depnd on them, |
716 | mark the corresponding SMB sessions as exiting too */ | 712 | mark the corresponding SMB sessions as exiting too */ |
717 | list_for_each(tmp, &GlobalSMBSessionList) { | 713 | list_for_each(tmp, &server->smb_ses_list) { |
718 | ses = list_entry(tmp, struct cifsSesInfo, | 714 | ses = list_entry(tmp, struct cifsSesInfo, |
719 | cifsSessionList); | 715 | smb_ses_list); |
720 | if (ses->server == server) | 716 | ses->status = CifsExiting; |
721 | ses->status = CifsExiting; | ||
722 | } | 717 | } |
723 | 718 | ||
724 | spin_lock(&GlobalMid_Lock); | 719 | spin_lock(&GlobalMid_Lock); |
@@ -733,7 +728,7 @@ multi_t2_fnd: | |||
733 | } | 728 | } |
734 | } | 729 | } |
735 | spin_unlock(&GlobalMid_Lock); | 730 | spin_unlock(&GlobalMid_Lock); |
736 | read_unlock(&GlobalSMBSeslock); | 731 | read_unlock(&cifs_tcp_ses_lock); |
737 | /* 1/8th of sec is more than enough time for them to exit */ | 732 | /* 1/8th of sec is more than enough time for them to exit */ |
738 | msleep(125); | 733 | msleep(125); |
739 | } | 734 | } |
@@ -755,14 +750,13 @@ multi_t2_fnd: | |||
755 | if there are any pointing to this (e.g | 750 | if there are any pointing to this (e.g |
756 | if a crazy root user tried to kill cifsd | 751 | if a crazy root user tried to kill cifsd |
757 | kernel thread explicitly this might happen) */ | 752 | kernel thread explicitly this might happen) */ |
758 | write_lock(&GlobalSMBSeslock); | 753 | /* BB: This shouldn't be necessary, see above */ |
759 | list_for_each(tmp, &GlobalSMBSessionList) { | 754 | read_lock(&cifs_tcp_ses_lock); |
760 | ses = list_entry(tmp, struct cifsSesInfo, | 755 | list_for_each(tmp, &server->smb_ses_list) { |
761 | cifsSessionList); | 756 | ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); |
762 | if (ses->server == server) | 757 | ses->server = NULL; |
763 | ses->server = NULL; | ||
764 | } | 758 | } |
765 | write_unlock(&GlobalSMBSeslock); | 759 | read_unlock(&cifs_tcp_ses_lock); |
766 | 760 | ||
767 | kfree(server->hostname); | 761 | kfree(server->hostname); |
768 | task_to_wake = xchg(&server->tsk, NULL); | 762 | task_to_wake = xchg(&server->tsk, NULL); |
@@ -1401,7 +1395,7 @@ cifs_find_tcp_session(struct sockaddr *addr) | |||
1401 | return NULL; | 1395 | return NULL; |
1402 | } | 1396 | } |
1403 | 1397 | ||
1404 | void | 1398 | static void |
1405 | cifs_put_tcp_session(struct TCP_Server_Info *server) | 1399 | cifs_put_tcp_session(struct TCP_Server_Info *server) |
1406 | { | 1400 | { |
1407 | struct task_struct *task; | 1401 | struct task_struct *task; |
@@ -1424,6 +1418,50 @@ cifs_put_tcp_session(struct TCP_Server_Info *server) | |||
1424 | force_sig(SIGKILL, task); | 1418 | force_sig(SIGKILL, task); |
1425 | } | 1419 | } |
1426 | 1420 | ||
1421 | static struct cifsSesInfo * | ||
1422 | cifs_find_smb_ses(struct TCP_Server_Info *server, char *username) | ||
1423 | { | ||
1424 | struct list_head *tmp; | ||
1425 | struct cifsSesInfo *ses; | ||
1426 | |||
1427 | write_lock(&cifs_tcp_ses_lock); | ||
1428 | list_for_each(tmp, &server->smb_ses_list) { | ||
1429 | ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); | ||
1430 | if (strncmp(ses->userName, username, MAX_USERNAME_SIZE)) | ||
1431 | continue; | ||
1432 | |||
1433 | ++ses->ses_count; | ||
1434 | write_unlock(&cifs_tcp_ses_lock); | ||
1435 | return ses; | ||
1436 | } | ||
1437 | write_unlock(&cifs_tcp_ses_lock); | ||
1438 | return NULL; | ||
1439 | } | ||
1440 | |||
1441 | static void | ||
1442 | cifs_put_smb_ses(struct cifsSesInfo *ses) | ||
1443 | { | ||
1444 | int xid; | ||
1445 | struct TCP_Server_Info *server = ses->server; | ||
1446 | |||
1447 | write_lock(&cifs_tcp_ses_lock); | ||
1448 | if (--ses->ses_count > 0) { | ||
1449 | write_unlock(&cifs_tcp_ses_lock); | ||
1450 | return; | ||
1451 | } | ||
1452 | |||
1453 | list_del_init(&ses->smb_ses_list); | ||
1454 | write_unlock(&cifs_tcp_ses_lock); | ||
1455 | |||
1456 | if (ses->status == CifsGood) { | ||
1457 | xid = GetXid(); | ||
1458 | CIFSSMBLogoff(xid, ses); | ||
1459 | _FreeXid(xid); | ||
1460 | } | ||
1461 | sesInfoFree(ses); | ||
1462 | cifs_put_tcp_session(server); | ||
1463 | } | ||
1464 | |||
1427 | int | 1465 | int |
1428 | get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, | 1466 | get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, |
1429 | const struct nls_table *nls_codepage, unsigned int *pnum_referrals, | 1467 | const struct nls_table *nls_codepage, unsigned int *pnum_referrals, |
@@ -1958,7 +1996,6 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
1958 | struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr; | 1996 | struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr; |
1959 | struct smb_vol volume_info; | 1997 | struct smb_vol volume_info; |
1960 | struct cifsSesInfo *pSesInfo = NULL; | 1998 | struct cifsSesInfo *pSesInfo = NULL; |
1961 | struct cifsSesInfo *existingCifsSes = NULL; | ||
1962 | struct cifsTconInfo *tcon = NULL; | 1999 | struct cifsTconInfo *tcon = NULL; |
1963 | struct TCP_Server_Info *srvTcp = NULL; | 2000 | struct TCP_Server_Info *srvTcp = NULL; |
1964 | 2001 | ||
@@ -2112,6 +2149,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2112 | volume_info.target_rfc1001_name, 16); | 2149 | volume_info.target_rfc1001_name, 16); |
2113 | srvTcp->sequence_number = 0; | 2150 | srvTcp->sequence_number = 0; |
2114 | INIT_LIST_HEAD(&srvTcp->tcp_ses_list); | 2151 | INIT_LIST_HEAD(&srvTcp->tcp_ses_list); |
2152 | INIT_LIST_HEAD(&srvTcp->smb_ses_list); | ||
2115 | ++srvTcp->srv_count; | 2153 | ++srvTcp->srv_count; |
2116 | write_lock(&cifs_tcp_ses_lock); | 2154 | write_lock(&cifs_tcp_ses_lock); |
2117 | list_add(&srvTcp->tcp_ses_list, | 2155 | list_add(&srvTcp->tcp_ses_list, |
@@ -2120,10 +2158,16 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2120 | } | 2158 | } |
2121 | } | 2159 | } |
2122 | 2160 | ||
2123 | if (existingCifsSes) { | 2161 | pSesInfo = cifs_find_smb_ses(srvTcp, volume_info.username); |
2124 | pSesInfo = existingCifsSes; | 2162 | if (pSesInfo) { |
2125 | cFYI(1, ("Existing smb sess found (status=%d)", | 2163 | cFYI(1, ("Existing smb sess found (status=%d)", |
2126 | pSesInfo->status)); | 2164 | pSesInfo->status)); |
2165 | /* | ||
2166 | * The existing SMB session already has a reference to srvTcp, | ||
2167 | * so we can put back the extra one we got before | ||
2168 | */ | ||
2169 | cifs_put_tcp_session(srvTcp); | ||
2170 | |||
2127 | down(&pSesInfo->sesSem); | 2171 | down(&pSesInfo->sesSem); |
2128 | if (pSesInfo->need_reconnect) { | 2172 | if (pSesInfo->need_reconnect) { |
2129 | cFYI(1, ("Session needs reconnect")); | 2173 | cFYI(1, ("Session needs reconnect")); |
@@ -2134,41 +2178,44 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2134 | } else if (!rc) { | 2178 | } else if (!rc) { |
2135 | cFYI(1, ("Existing smb sess not found")); | 2179 | cFYI(1, ("Existing smb sess not found")); |
2136 | pSesInfo = sesInfoAlloc(); | 2180 | pSesInfo = sesInfoAlloc(); |
2137 | if (pSesInfo == NULL) | 2181 | if (pSesInfo == NULL) { |
2138 | rc = -ENOMEM; | 2182 | rc = -ENOMEM; |
2139 | else { | 2183 | goto mount_fail_check; |
2140 | pSesInfo->server = srvTcp; | ||
2141 | sprintf(pSesInfo->serverName, "%u.%u.%u.%u", | ||
2142 | NIPQUAD(sin_server->sin_addr.s_addr)); | ||
2143 | } | 2184 | } |
2144 | 2185 | ||
2145 | if (!rc) { | 2186 | /* new SMB session uses our srvTcp ref */ |
2146 | /* volume_info.password freed at unmount */ | 2187 | pSesInfo->server = srvTcp; |
2147 | if (volume_info.password) { | 2188 | sprintf(pSesInfo->serverName, "%u.%u.%u.%u", |
2148 | pSesInfo->password = volume_info.password; | 2189 | NIPQUAD(sin_server->sin_addr.s_addr)); |
2149 | /* set to NULL to prevent freeing on exit */ | 2190 | |
2150 | volume_info.password = NULL; | 2191 | write_lock(&cifs_tcp_ses_lock); |
2151 | } | 2192 | list_add(&pSesInfo->smb_ses_list, &srvTcp->smb_ses_list); |
2152 | if (volume_info.username) | 2193 | write_unlock(&cifs_tcp_ses_lock); |
2153 | strncpy(pSesInfo->userName, | 2194 | |
2154 | volume_info.username, | 2195 | /* volume_info.password freed at unmount */ |
2155 | MAX_USERNAME_SIZE); | 2196 | if (volume_info.password) { |
2156 | if (volume_info.domainname) { | 2197 | pSesInfo->password = volume_info.password; |
2157 | int len = strlen(volume_info.domainname); | 2198 | /* set to NULL to prevent freeing on exit */ |
2158 | pSesInfo->domainName = | 2199 | volume_info.password = NULL; |
2159 | kmalloc(len + 1, GFP_KERNEL); | 2200 | } |
2160 | if (pSesInfo->domainName) | 2201 | if (volume_info.username) |
2161 | strcpy(pSesInfo->domainName, | 2202 | strncpy(pSesInfo->userName, volume_info.username, |
2162 | volume_info.domainname); | 2203 | MAX_USERNAME_SIZE); |
2163 | } | 2204 | if (volume_info.domainname) { |
2164 | pSesInfo->linux_uid = volume_info.linux_uid; | 2205 | int len = strlen(volume_info.domainname); |
2165 | pSesInfo->overrideSecFlg = volume_info.secFlg; | 2206 | pSesInfo->domainName = kmalloc(len + 1, GFP_KERNEL); |
2166 | down(&pSesInfo->sesSem); | 2207 | if (pSesInfo->domainName) |
2167 | /* BB FIXME need to pass vol->secFlgs BB */ | 2208 | strcpy(pSesInfo->domainName, |
2168 | rc = cifs_setup_session(xid, pSesInfo, | 2209 | volume_info.domainname); |
2169 | cifs_sb->local_nls); | 2210 | } |
2170 | up(&pSesInfo->sesSem); | 2211 | pSesInfo->linux_uid = volume_info.linux_uid; |
2171 | } | 2212 | pSesInfo->overrideSecFlg = volume_info.secFlg; |
2213 | down(&pSesInfo->sesSem); | ||
2214 | |||
2215 | /* BB FIXME need to pass vol->secFlgs BB */ | ||
2216 | rc = cifs_setup_session(xid, pSesInfo, | ||
2217 | cifs_sb->local_nls); | ||
2218 | up(&pSesInfo->sesSem); | ||
2172 | } | 2219 | } |
2173 | 2220 | ||
2174 | /* search for existing tcon to this server share */ | 2221 | /* search for existing tcon to this server share */ |
@@ -2209,11 +2256,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2209 | tcon->Flags)); | 2256 | tcon->Flags)); |
2210 | } | 2257 | } |
2211 | } | 2258 | } |
2212 | if (!rc) { | 2259 | if (rc) |
2213 | atomic_inc(&pSesInfo->inUse); | ||
2214 | tcon->seal = volume_info.seal; | ||
2215 | } else | ||
2216 | goto mount_fail_check; | 2260 | goto mount_fail_check; |
2261 | tcon->seal = volume_info.seal; | ||
2217 | } | 2262 | } |
2218 | 2263 | ||
2219 | /* we can have only one retry value for a connection | 2264 | /* we can have only one retry value for a connection |
@@ -2234,29 +2279,19 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2234 | /* BB FIXME fix time_gran to be larger for LANMAN sessions */ | 2279 | /* BB FIXME fix time_gran to be larger for LANMAN sessions */ |
2235 | sb->s_time_gran = 100; | 2280 | sb->s_time_gran = 100; |
2236 | 2281 | ||
2237 | /* on error free sesinfo and tcon struct if needed */ | ||
2238 | mount_fail_check: | 2282 | mount_fail_check: |
2283 | /* on error free sesinfo and tcon struct if needed */ | ||
2239 | if (rc) { | 2284 | if (rc) { |
2240 | /* If find_unc succeeded then rc == 0 so we can not end */ | 2285 | /* If find_unc succeeded then rc == 0 so we can not end */ |
2241 | /* up accidently freeing someone elses tcon struct */ | 2286 | /* up accidently freeing someone elses tcon struct */ |
2242 | if (tcon) | 2287 | if (tcon) |
2243 | tconInfoFree(tcon); | 2288 | tconInfoFree(tcon); |
2244 | 2289 | ||
2245 | if (existingCifsSes == NULL) { | 2290 | /* should also end up putting our tcp session ref if needed */ |
2246 | if (pSesInfo) { | 2291 | if (pSesInfo) |
2247 | if ((pSesInfo->server) && | 2292 | cifs_put_smb_ses(pSesInfo); |
2248 | (pSesInfo->status == CifsGood)) | 2293 | else |
2249 | CIFSSMBLogoff(xid, pSesInfo); | 2294 | cifs_put_tcp_session(srvTcp); |
2250 | else { | ||
2251 | cFYI(1, ("No session or bad tcon")); | ||
2252 | if (pSesInfo->server) | ||
2253 | cifs_put_tcp_session( | ||
2254 | pSesInfo->server); | ||
2255 | } | ||
2256 | sesInfoFree(pSesInfo); | ||
2257 | /* pSesInfo = NULL; */ | ||
2258 | } | ||
2259 | } | ||
2260 | } else { | 2295 | } else { |
2261 | atomic_inc(&tcon->useCount); | 2296 | atomic_inc(&tcon->useCount); |
2262 | cifs_sb->tcon = tcon; | 2297 | cifs_sb->tcon = tcon; |
@@ -3551,16 +3586,7 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) | |||
3551 | } | 3586 | } |
3552 | DeleteTconOplockQEntries(cifs_sb->tcon); | 3587 | DeleteTconOplockQEntries(cifs_sb->tcon); |
3553 | tconInfoFree(cifs_sb->tcon); | 3588 | tconInfoFree(cifs_sb->tcon); |
3554 | if ((ses) && (ses->server)) { | 3589 | cifs_put_smb_ses(ses); |
3555 | /* save off task so we do not refer to ses later */ | ||
3556 | cFYI(1, ("About to do SMBLogoff ")); | ||
3557 | rc = CIFSSMBLogoff(xid, ses); | ||
3558 | if (rc == -EBUSY) { | ||
3559 | FreeXid(xid); | ||
3560 | return 0; | ||
3561 | } | ||
3562 | } else | ||
3563 | cFYI(1, ("No session or bad tcon")); | ||
3564 | } | 3590 | } |
3565 | 3591 | ||
3566 | cifs_sb->tcon = NULL; | 3592 | cifs_sb->tcon = NULL; |
@@ -3568,8 +3594,6 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) | |||
3568 | cifs_sb->prepathlen = 0; | 3594 | cifs_sb->prepathlen = 0; |
3569 | cifs_sb->prepath = NULL; | 3595 | cifs_sb->prepath = NULL; |
3570 | kfree(tmp); | 3596 | kfree(tmp); |
3571 | if (ses) | ||
3572 | sesInfoFree(ses); | ||
3573 | 3597 | ||
3574 | FreeXid(xid); | 3598 | FreeXid(xid); |
3575 | return rc; | 3599 | return rc; |