diff options
-rw-r--r-- | fs/cifs/connect.c | 282 |
1 files changed, 150 insertions, 132 deletions
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 65c121940060..56095b24d5b9 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
@@ -1417,6 +1417,148 @@ cifs_put_tcp_session(struct TCP_Server_Info *server) | |||
1417 | force_sig(SIGKILL, task); | 1417 | force_sig(SIGKILL, task); |
1418 | } | 1418 | } |
1419 | 1419 | ||
1420 | static struct TCP_Server_Info * | ||
1421 | cifs_get_tcp_session(struct smb_vol *volume_info) | ||
1422 | { | ||
1423 | struct TCP_Server_Info *tcp_ses = NULL; | ||
1424 | struct sockaddr addr; | ||
1425 | struct sockaddr_in *sin_server = (struct sockaddr_in *) &addr; | ||
1426 | struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr; | ||
1427 | int rc; | ||
1428 | |||
1429 | memset(&addr, 0, sizeof(struct sockaddr)); | ||
1430 | |||
1431 | if (volume_info->UNCip && volume_info->UNC) { | ||
1432 | rc = cifs_inet_pton(AF_INET, volume_info->UNCip, | ||
1433 | &sin_server->sin_addr.s_addr); | ||
1434 | |||
1435 | if (rc <= 0) { | ||
1436 | /* not ipv4 address, try ipv6 */ | ||
1437 | rc = cifs_inet_pton(AF_INET6, volume_info->UNCip, | ||
1438 | &sin_server6->sin6_addr.in6_u); | ||
1439 | if (rc > 0) | ||
1440 | addr.sa_family = AF_INET6; | ||
1441 | } else { | ||
1442 | addr.sa_family = AF_INET; | ||
1443 | } | ||
1444 | |||
1445 | if (rc <= 0) { | ||
1446 | /* we failed translating address */ | ||
1447 | rc = -EINVAL; | ||
1448 | goto out_err; | ||
1449 | } | ||
1450 | |||
1451 | cFYI(1, ("UNC: %s ip: %s", volume_info->UNC, | ||
1452 | volume_info->UNCip)); | ||
1453 | } else if (volume_info->UNCip) { | ||
1454 | /* BB using ip addr as tcp_ses name to connect to the | ||
1455 | DFS root below */ | ||
1456 | cERROR(1, ("Connecting to DFS root not implemented yet")); | ||
1457 | rc = -EINVAL; | ||
1458 | goto out_err; | ||
1459 | } else /* which tcp_sess DFS root would we conect to */ { | ||
1460 | cERROR(1, | ||
1461 | ("CIFS mount error: No UNC path (e.g. -o " | ||
1462 | "unc=//192.168.1.100/public) specified")); | ||
1463 | rc = -EINVAL; | ||
1464 | goto out_err; | ||
1465 | } | ||
1466 | |||
1467 | /* see if we already have a matching tcp_ses */ | ||
1468 | tcp_ses = cifs_find_tcp_session(&addr); | ||
1469 | if (tcp_ses) | ||
1470 | return tcp_ses; | ||
1471 | |||
1472 | tcp_ses = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL); | ||
1473 | if (!tcp_ses) { | ||
1474 | rc = -ENOMEM; | ||
1475 | goto out_err; | ||
1476 | } | ||
1477 | |||
1478 | tcp_ses->hostname = extract_hostname(volume_info->UNC); | ||
1479 | if (IS_ERR(tcp_ses->hostname)) { | ||
1480 | rc = PTR_ERR(tcp_ses->hostname); | ||
1481 | goto out_err; | ||
1482 | } | ||
1483 | |||
1484 | tcp_ses->noblocksnd = volume_info->noblocksnd; | ||
1485 | tcp_ses->noautotune = volume_info->noautotune; | ||
1486 | atomic_set(&tcp_ses->inFlight, 0); | ||
1487 | init_waitqueue_head(&tcp_ses->response_q); | ||
1488 | init_waitqueue_head(&tcp_ses->request_q); | ||
1489 | INIT_LIST_HEAD(&tcp_ses->pending_mid_q); | ||
1490 | mutex_init(&tcp_ses->srv_mutex); | ||
1491 | memcpy(tcp_ses->workstation_RFC1001_name, | ||
1492 | volume_info->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); | ||
1493 | memcpy(tcp_ses->server_RFC1001_name, | ||
1494 | volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); | ||
1495 | tcp_ses->sequence_number = 0; | ||
1496 | INIT_LIST_HEAD(&tcp_ses->tcp_ses_list); | ||
1497 | INIT_LIST_HEAD(&tcp_ses->smb_ses_list); | ||
1498 | |||
1499 | /* | ||
1500 | * at this point we are the only ones with the pointer | ||
1501 | * to the struct since the kernel thread not created yet | ||
1502 | * no need to spinlock this init of tcpStatus or srv_count | ||
1503 | */ | ||
1504 | tcp_ses->tcpStatus = CifsNew; | ||
1505 | ++tcp_ses->srv_count; | ||
1506 | |||
1507 | if (addr.sa_family == AF_INET6) { | ||
1508 | cFYI(1, ("attempting ipv6 connect")); | ||
1509 | /* BB should we allow ipv6 on port 139? */ | ||
1510 | /* other OS never observed in Wild doing 139 with v6 */ | ||
1511 | memcpy(&tcp_ses->addr.sockAddr6, sin_server6, | ||
1512 | sizeof(struct sockaddr_in6)); | ||
1513 | sin_server6->sin6_port = htons(volume_info->port); | ||
1514 | rc = ipv6_connect(sin_server6, &tcp_ses->ssocket, | ||
1515 | volume_info->noblocksnd); | ||
1516 | } else { | ||
1517 | memcpy(&tcp_ses->addr.sockAddr, sin_server, | ||
1518 | sizeof(struct sockaddr_in)); | ||
1519 | sin_server->sin_port = htons(volume_info->port); | ||
1520 | rc = ipv4_connect(sin_server, &tcp_ses->ssocket, | ||
1521 | volume_info->source_rfc1001_name, | ||
1522 | volume_info->target_rfc1001_name, | ||
1523 | volume_info->noblocksnd, | ||
1524 | volume_info->noautotune); | ||
1525 | } | ||
1526 | if (rc < 0) { | ||
1527 | cERROR(1, ("Error connecting to socket. Aborting operation")); | ||
1528 | goto out_err; | ||
1529 | } | ||
1530 | |||
1531 | /* | ||
1532 | * since we're in a cifs function already, we know that | ||
1533 | * this will succeed. No need for try_module_get(). | ||
1534 | */ | ||
1535 | __module_get(THIS_MODULE); | ||
1536 | tcp_ses->tsk = kthread_run((void *)(void *)cifs_demultiplex_thread, | ||
1537 | tcp_ses, "cifsd"); | ||
1538 | if (IS_ERR(tcp_ses->tsk)) { | ||
1539 | rc = PTR_ERR(tcp_ses->tsk); | ||
1540 | cERROR(1, ("error %d create cifsd thread", rc)); | ||
1541 | module_put(THIS_MODULE); | ||
1542 | goto out_err; | ||
1543 | } | ||
1544 | |||
1545 | /* thread spawned, put it on the list */ | ||
1546 | write_lock(&cifs_tcp_ses_lock); | ||
1547 | list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list); | ||
1548 | write_unlock(&cifs_tcp_ses_lock); | ||
1549 | |||
1550 | return tcp_ses; | ||
1551 | |||
1552 | out_err: | ||
1553 | if (tcp_ses) { | ||
1554 | kfree(tcp_ses->hostname); | ||
1555 | if (tcp_ses->ssocket) | ||
1556 | sock_release(tcp_ses->ssocket); | ||
1557 | kfree(tcp_ses); | ||
1558 | } | ||
1559 | return ERR_PTR(rc); | ||
1560 | } | ||
1561 | |||
1420 | static struct cifsSesInfo * | 1562 | static struct cifsSesInfo * |
1421 | cifs_find_smb_ses(struct TCP_Server_Info *server, char *username) | 1563 | cifs_find_smb_ses(struct TCP_Server_Info *server, char *username) |
1422 | { | 1564 | { |
@@ -2043,10 +2185,6 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2043 | { | 2185 | { |
2044 | int rc = 0; | 2186 | int rc = 0; |
2045 | int xid; | 2187 | int xid; |
2046 | struct socket *csocket = NULL; | ||
2047 | struct sockaddr addr; | ||
2048 | struct sockaddr_in *sin_server = (struct sockaddr_in *) &addr; | ||
2049 | struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr; | ||
2050 | struct smb_vol volume_info; | 2188 | struct smb_vol volume_info; |
2051 | struct cifsSesInfo *pSesInfo = NULL; | 2189 | struct cifsSesInfo *pSesInfo = NULL; |
2052 | struct cifsTconInfo *tcon = NULL; | 2190 | struct cifsTconInfo *tcon = NULL; |
@@ -2056,7 +2194,6 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2056 | 2194 | ||
2057 | /* cFYI(1, ("Entering cifs_mount. Xid: %d with: %s", xid, mount_data)); */ | 2195 | /* cFYI(1, ("Entering cifs_mount. Xid: %d with: %s", xid, mount_data)); */ |
2058 | 2196 | ||
2059 | memset(&addr, 0, sizeof(struct sockaddr)); | ||
2060 | memset(&volume_info, 0, sizeof(struct smb_vol)); | 2197 | memset(&volume_info, 0, sizeof(struct smb_vol)); |
2061 | if (cifs_parse_mount_options(mount_data, devname, &volume_info)) { | 2198 | if (cifs_parse_mount_options(mount_data, devname, &volume_info)) { |
2062 | rc = -EINVAL; | 2199 | rc = -EINVAL; |
@@ -2077,42 +2214,6 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2077 | goto out; | 2214 | goto out; |
2078 | } | 2215 | } |
2079 | 2216 | ||
2080 | if (volume_info.UNCip && volume_info.UNC) { | ||
2081 | rc = cifs_inet_pton(AF_INET, volume_info.UNCip, | ||
2082 | &sin_server->sin_addr.s_addr); | ||
2083 | |||
2084 | if (rc <= 0) { | ||
2085 | /* not ipv4 address, try ipv6 */ | ||
2086 | rc = cifs_inet_pton(AF_INET6, volume_info.UNCip, | ||
2087 | &sin_server6->sin6_addr.in6_u); | ||
2088 | if (rc > 0) | ||
2089 | addr.sa_family = AF_INET6; | ||
2090 | } else { | ||
2091 | addr.sa_family = AF_INET; | ||
2092 | } | ||
2093 | |||
2094 | if (rc <= 0) { | ||
2095 | /* we failed translating address */ | ||
2096 | rc = -EINVAL; | ||
2097 | goto out; | ||
2098 | } | ||
2099 | |||
2100 | cFYI(1, ("UNC: %s ip: %s", volume_info.UNC, volume_info.UNCip)); | ||
2101 | /* success */ | ||
2102 | rc = 0; | ||
2103 | } else if (volume_info.UNCip) { | ||
2104 | /* BB using ip addr as server name to connect to the | ||
2105 | DFS root below */ | ||
2106 | cERROR(1, ("Connecting to DFS root not implemented yet")); | ||
2107 | rc = -EINVAL; | ||
2108 | goto out; | ||
2109 | } else /* which servers DFS root would we conect to */ { | ||
2110 | cERROR(1, | ||
2111 | ("CIFS mount error: No UNC path (e.g. -o " | ||
2112 | "unc=//192.168.1.100/public) specified")); | ||
2113 | rc = -EINVAL; | ||
2114 | goto out; | ||
2115 | } | ||
2116 | 2217 | ||
2117 | /* this is needed for ASCII cp to Unicode converts */ | 2218 | /* this is needed for ASCII cp to Unicode converts */ |
2118 | if (volume_info.iocharset == NULL) { | 2219 | if (volume_info.iocharset == NULL) { |
@@ -2128,94 +2229,11 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2128 | } | 2229 | } |
2129 | } | 2230 | } |
2130 | 2231 | ||
2131 | srvTcp = cifs_find_tcp_session(&addr); | 2232 | /* get a reference to a tcp session */ |
2132 | if (!srvTcp) { /* create socket */ | 2233 | srvTcp = cifs_get_tcp_session(&volume_info); |
2133 | if (addr.sa_family == AF_INET6) { | 2234 | if (IS_ERR(srvTcp)) { |
2134 | cFYI(1, ("attempting ipv6 connect")); | 2235 | rc = PTR_ERR(srvTcp); |
2135 | /* BB should we allow ipv6 on port 139? */ | 2236 | goto out; |
2136 | /* other OS never observed in Wild doing 139 with v6 */ | ||
2137 | sin_server6->sin6_port = htons(volume_info.port); | ||
2138 | rc = ipv6_connect(sin_server6, &csocket, | ||
2139 | volume_info.noblocksnd); | ||
2140 | } else { | ||
2141 | sin_server->sin_port = htons(volume_info.port); | ||
2142 | rc = ipv4_connect(sin_server, &csocket, | ||
2143 | volume_info.source_rfc1001_name, | ||
2144 | volume_info.target_rfc1001_name, | ||
2145 | volume_info.noblocksnd, | ||
2146 | volume_info.noautotune); | ||
2147 | } | ||
2148 | if (rc < 0) { | ||
2149 | cERROR(1, ("Error connecting to socket. " | ||
2150 | "Aborting operation")); | ||
2151 | if (csocket != NULL) | ||
2152 | sock_release(csocket); | ||
2153 | goto out; | ||
2154 | } | ||
2155 | |||
2156 | srvTcp = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL); | ||
2157 | if (!srvTcp) { | ||
2158 | rc = -ENOMEM; | ||
2159 | sock_release(csocket); | ||
2160 | goto out; | ||
2161 | } else { | ||
2162 | srvTcp->noblocksnd = volume_info.noblocksnd; | ||
2163 | srvTcp->noautotune = volume_info.noautotune; | ||
2164 | if (addr.sa_family == AF_INET6) | ||
2165 | memcpy(&srvTcp->addr.sockAddr6, sin_server6, | ||
2166 | sizeof(struct sockaddr_in6)); | ||
2167 | else | ||
2168 | memcpy(&srvTcp->addr.sockAddr, sin_server, | ||
2169 | sizeof(struct sockaddr_in)); | ||
2170 | atomic_set(&srvTcp->inFlight, 0); | ||
2171 | /* BB Add code for ipv6 case too */ | ||
2172 | srvTcp->ssocket = csocket; | ||
2173 | srvTcp->hostname = extract_hostname(volume_info.UNC); | ||
2174 | if (IS_ERR(srvTcp->hostname)) { | ||
2175 | rc = PTR_ERR(srvTcp->hostname); | ||
2176 | sock_release(csocket); | ||
2177 | goto out; | ||
2178 | } | ||
2179 | init_waitqueue_head(&srvTcp->response_q); | ||
2180 | init_waitqueue_head(&srvTcp->request_q); | ||
2181 | INIT_LIST_HEAD(&srvTcp->pending_mid_q); | ||
2182 | /* at this point we are the only ones with the pointer | ||
2183 | to the struct since the kernel thread not created yet | ||
2184 | so no need to spinlock this init of tcpStatus */ | ||
2185 | srvTcp->tcpStatus = CifsNew; | ||
2186 | mutex_init(&srvTcp->srv_mutex); | ||
2187 | |||
2188 | /* | ||
2189 | * since we're in a cifs function already, we know that | ||
2190 | * this will succeed. No need for try_module_get(). | ||
2191 | */ | ||
2192 | __module_get(THIS_MODULE); | ||
2193 | srvTcp->tsk = kthread_run((void *)(void *)cifs_demultiplex_thread, srvTcp, "cifsd"); | ||
2194 | if (IS_ERR(srvTcp->tsk)) { | ||
2195 | rc = PTR_ERR(srvTcp->tsk); | ||
2196 | cERROR(1, ("error %d create cifsd thread", rc)); | ||
2197 | module_put(THIS_MODULE); | ||
2198 | srvTcp->tsk = NULL; | ||
2199 | sock_release(csocket); | ||
2200 | kfree(srvTcp->hostname); | ||
2201 | goto out; | ||
2202 | } | ||
2203 | rc = 0; | ||
2204 | memcpy(srvTcp->workstation_RFC1001_name, | ||
2205 | volume_info.source_rfc1001_name, | ||
2206 | RFC1001_NAME_LEN_WITH_NULL); | ||
2207 | memcpy(srvTcp->server_RFC1001_name, | ||
2208 | volume_info.target_rfc1001_name, | ||
2209 | RFC1001_NAME_LEN_WITH_NULL); | ||
2210 | srvTcp->sequence_number = 0; | ||
2211 | INIT_LIST_HEAD(&srvTcp->tcp_ses_list); | ||
2212 | INIT_LIST_HEAD(&srvTcp->smb_ses_list); | ||
2213 | ++srvTcp->srv_count; | ||
2214 | write_lock(&cifs_tcp_ses_lock); | ||
2215 | list_add(&srvTcp->tcp_ses_list, | ||
2216 | &cifs_tcp_ses_list); | ||
2217 | write_unlock(&cifs_tcp_ses_lock); | ||
2218 | } | ||
2219 | } | 2237 | } |
2220 | 2238 | ||
2221 | pSesInfo = cifs_find_smb_ses(srvTcp, volume_info.username); | 2239 | pSesInfo = cifs_find_smb_ses(srvTcp, volume_info.username); |
@@ -2245,12 +2263,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2245 | 2263 | ||
2246 | /* new SMB session uses our srvTcp ref */ | 2264 | /* new SMB session uses our srvTcp ref */ |
2247 | pSesInfo->server = srvTcp; | 2265 | pSesInfo->server = srvTcp; |
2248 | if (addr.sa_family == AF_INET6) | 2266 | if (srvTcp->addr.sockAddr6.sin6_family == AF_INET6) |
2249 | sprintf(pSesInfo->serverName, NIP6_FMT, | 2267 | sprintf(pSesInfo->serverName, NIP6_FMT, |
2250 | NIP6(sin_server6->sin6_addr)); | 2268 | NIP6(srvTcp->addr.sockAddr6.sin6_addr)); |
2251 | else | 2269 | else |
2252 | sprintf(pSesInfo->serverName, NIPQUAD_FMT, | 2270 | sprintf(pSesInfo->serverName, NIPQUAD_FMT, |
2253 | NIPQUAD(sin_server->sin_addr.s_addr)); | 2271 | NIPQUAD(srvTcp->addr.sockAddr.sin_addr.s_addr)); |
2254 | 2272 | ||
2255 | write_lock(&cifs_tcp_ses_lock); | 2273 | write_lock(&cifs_tcp_ses_lock); |
2256 | list_add(&pSesInfo->smb_ses_list, &srvTcp->smb_ses_list); | 2274 | list_add(&pSesInfo->smb_ses_list, &srvTcp->smb_ses_list); |