diff options
Diffstat (limited to 'fs/cifs/connect.c')
| -rw-r--r-- | fs/cifs/connect.c | 534 |
1 files changed, 478 insertions, 56 deletions
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 88c84a38bccb..7e73176acb58 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
| @@ -47,7 +47,6 @@ | |||
| 47 | #include "ntlmssp.h" | 47 | #include "ntlmssp.h" |
| 48 | #include "nterr.h" | 48 | #include "nterr.h" |
| 49 | #include "rfc1002pdu.h" | 49 | #include "rfc1002pdu.h" |
| 50 | #include "cn_cifs.h" | ||
| 51 | #include "fscache.h" | 50 | #include "fscache.h" |
| 52 | 51 | ||
| 53 | #define CIFS_PORT 445 | 52 | #define CIFS_PORT 445 |
| @@ -100,16 +99,24 @@ struct smb_vol { | |||
| 100 | bool noautotune:1; | 99 | bool noautotune:1; |
| 101 | bool nostrictsync:1; /* do not force expensive SMBflush on every sync */ | 100 | bool nostrictsync:1; /* do not force expensive SMBflush on every sync */ |
| 102 | bool fsc:1; /* enable fscache */ | 101 | bool fsc:1; /* enable fscache */ |
| 102 | bool mfsymlinks:1; /* use Minshall+French Symlinks */ | ||
| 103 | bool multiuser:1; | ||
| 103 | unsigned int rsize; | 104 | unsigned int rsize; |
| 104 | unsigned int wsize; | 105 | unsigned int wsize; |
| 105 | bool sockopt_tcp_nodelay:1; | 106 | bool sockopt_tcp_nodelay:1; |
| 106 | unsigned short int port; | 107 | unsigned short int port; |
| 107 | char *prepath; | 108 | char *prepath; |
| 109 | struct sockaddr_storage srcaddr; /* allow binding to a local IP */ | ||
| 108 | struct nls_table *local_nls; | 110 | struct nls_table *local_nls; |
| 109 | }; | 111 | }; |
| 110 | 112 | ||
| 113 | /* FIXME: should these be tunable? */ | ||
| 114 | #define TLINK_ERROR_EXPIRE (1 * HZ) | ||
| 115 | #define TLINK_IDLE_EXPIRE (600 * HZ) | ||
| 116 | |||
| 111 | static int ipv4_connect(struct TCP_Server_Info *server); | 117 | static int ipv4_connect(struct TCP_Server_Info *server); |
| 112 | static int ipv6_connect(struct TCP_Server_Info *server); | 118 | static int ipv6_connect(struct TCP_Server_Info *server); |
| 119 | static void cifs_prune_tlinks(struct work_struct *work); | ||
| 113 | 120 | ||
| 114 | /* | 121 | /* |
| 115 | * cifs tcp session reconnection | 122 | * cifs tcp session reconnection |
| @@ -143,7 +150,7 @@ cifs_reconnect(struct TCP_Server_Info *server) | |||
| 143 | 150 | ||
| 144 | /* before reconnecting the tcp session, mark the smb session (uid) | 151 | /* before reconnecting the tcp session, mark the smb session (uid) |
| 145 | and the tid bad so they are not used until reconnected */ | 152 | and the tid bad so they are not used until reconnected */ |
| 146 | read_lock(&cifs_tcp_ses_lock); | 153 | spin_lock(&cifs_tcp_ses_lock); |
| 147 | list_for_each(tmp, &server->smb_ses_list) { | 154 | list_for_each(tmp, &server->smb_ses_list) { |
| 148 | ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); | 155 | ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); |
| 149 | ses->need_reconnect = true; | 156 | ses->need_reconnect = true; |
| @@ -153,7 +160,7 @@ cifs_reconnect(struct TCP_Server_Info *server) | |||
| 153 | tcon->need_reconnect = true; | 160 | tcon->need_reconnect = true; |
| 154 | } | 161 | } |
| 155 | } | 162 | } |
| 156 | read_unlock(&cifs_tcp_ses_lock); | 163 | spin_unlock(&cifs_tcp_ses_lock); |
| 157 | /* do not want to be sending data on a socket we are freeing */ | 164 | /* do not want to be sending data on a socket we are freeing */ |
| 158 | mutex_lock(&server->srv_mutex); | 165 | mutex_lock(&server->srv_mutex); |
| 159 | if (server->ssocket) { | 166 | if (server->ssocket) { |
| @@ -166,6 +173,8 @@ cifs_reconnect(struct TCP_Server_Info *server) | |||
| 166 | sock_release(server->ssocket); | 173 | sock_release(server->ssocket); |
| 167 | server->ssocket = NULL; | 174 | server->ssocket = NULL; |
| 168 | } | 175 | } |
| 176 | server->sequence_number = 0; | ||
| 177 | server->session_estab = false; | ||
| 169 | 178 | ||
| 170 | spin_lock(&GlobalMid_Lock); | 179 | spin_lock(&GlobalMid_Lock); |
| 171 | list_for_each(tmp, &server->pending_mid_q) { | 180 | list_for_each(tmp, &server->pending_mid_q) { |
| @@ -198,7 +207,6 @@ cifs_reconnect(struct TCP_Server_Info *server) | |||
| 198 | spin_lock(&GlobalMid_Lock); | 207 | spin_lock(&GlobalMid_Lock); |
| 199 | if (server->tcpStatus != CifsExiting) | 208 | if (server->tcpStatus != CifsExiting) |
| 200 | server->tcpStatus = CifsGood; | 209 | server->tcpStatus = CifsGood; |
| 201 | server->sequence_number = 0; | ||
| 202 | spin_unlock(&GlobalMid_Lock); | 210 | spin_unlock(&GlobalMid_Lock); |
| 203 | /* atomic_set(&server->inFlight,0);*/ | 211 | /* atomic_set(&server->inFlight,0);*/ |
| 204 | wake_up(&server->response_q); | 212 | wake_up(&server->response_q); |
| @@ -629,9 +637,9 @@ multi_t2_fnd: | |||
| 629 | } /* end while !EXITING */ | 637 | } /* end while !EXITING */ |
| 630 | 638 | ||
| 631 | /* take it off the list, if it's not already */ | 639 | /* take it off the list, if it's not already */ |
| 632 | write_lock(&cifs_tcp_ses_lock); | 640 | spin_lock(&cifs_tcp_ses_lock); |
| 633 | list_del_init(&server->tcp_ses_list); | 641 | list_del_init(&server->tcp_ses_list); |
| 634 | write_unlock(&cifs_tcp_ses_lock); | 642 | spin_unlock(&cifs_tcp_ses_lock); |
| 635 | 643 | ||
| 636 | spin_lock(&GlobalMid_Lock); | 644 | spin_lock(&GlobalMid_Lock); |
| 637 | server->tcpStatus = CifsExiting; | 645 | server->tcpStatus = CifsExiting; |
| @@ -669,7 +677,7 @@ multi_t2_fnd: | |||
| 669 | * BB: we shouldn't have to do any of this. It shouldn't be | 677 | * BB: we shouldn't have to do any of this. It shouldn't be |
| 670 | * possible to exit from the thread with active SMB sessions | 678 | * possible to exit from the thread with active SMB sessions |
| 671 | */ | 679 | */ |
| 672 | read_lock(&cifs_tcp_ses_lock); | 680 | spin_lock(&cifs_tcp_ses_lock); |
| 673 | if (list_empty(&server->pending_mid_q)) { | 681 | if (list_empty(&server->pending_mid_q)) { |
| 674 | /* loop through server session structures attached to this and | 682 | /* loop through server session structures attached to this and |
| 675 | mark them dead */ | 683 | mark them dead */ |
| @@ -679,7 +687,7 @@ multi_t2_fnd: | |||
| 679 | ses->status = CifsExiting; | 687 | ses->status = CifsExiting; |
| 680 | ses->server = NULL; | 688 | ses->server = NULL; |
| 681 | } | 689 | } |
| 682 | read_unlock(&cifs_tcp_ses_lock); | 690 | spin_unlock(&cifs_tcp_ses_lock); |
| 683 | } else { | 691 | } else { |
| 684 | /* although we can not zero the server struct pointer yet, | 692 | /* although we can not zero the server struct pointer yet, |
| 685 | since there are active requests which may depnd on them, | 693 | since there are active requests which may depnd on them, |
| @@ -702,7 +710,7 @@ multi_t2_fnd: | |||
| 702 | } | 710 | } |
| 703 | } | 711 | } |
| 704 | spin_unlock(&GlobalMid_Lock); | 712 | spin_unlock(&GlobalMid_Lock); |
| 705 | read_unlock(&cifs_tcp_ses_lock); | 713 | spin_unlock(&cifs_tcp_ses_lock); |
| 706 | /* 1/8th of sec is more than enough time for them to exit */ | 714 | /* 1/8th of sec is more than enough time for them to exit */ |
| 707 | msleep(125); | 715 | msleep(125); |
| 708 | } | 716 | } |
| @@ -725,12 +733,12 @@ multi_t2_fnd: | |||
| 725 | if a crazy root user tried to kill cifsd | 733 | if a crazy root user tried to kill cifsd |
| 726 | kernel thread explicitly this might happen) */ | 734 | kernel thread explicitly this might happen) */ |
| 727 | /* BB: This shouldn't be necessary, see above */ | 735 | /* BB: This shouldn't be necessary, see above */ |
| 728 | read_lock(&cifs_tcp_ses_lock); | 736 | spin_lock(&cifs_tcp_ses_lock); |
| 729 | list_for_each(tmp, &server->smb_ses_list) { | 737 | list_for_each(tmp, &server->smb_ses_list) { |
| 730 | ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); | 738 | ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); |
| 731 | ses->server = NULL; | 739 | ses->server = NULL; |
| 732 | } | 740 | } |
| 733 | read_unlock(&cifs_tcp_ses_lock); | 741 | spin_unlock(&cifs_tcp_ses_lock); |
| 734 | 742 | ||
| 735 | kfree(server->hostname); | 743 | kfree(server->hostname); |
| 736 | task_to_wake = xchg(&server->tsk, NULL); | 744 | task_to_wake = xchg(&server->tsk, NULL); |
| @@ -1046,6 +1054,22 @@ cifs_parse_mount_options(char *options, const char *devname, | |||
| 1046 | "long\n"); | 1054 | "long\n"); |
| 1047 | return 1; | 1055 | return 1; |
| 1048 | } | 1056 | } |
| 1057 | } else if (strnicmp(data, "srcaddr", 7) == 0) { | ||
| 1058 | vol->srcaddr.ss_family = AF_UNSPEC; | ||
| 1059 | |||
| 1060 | if (!value || !*value) { | ||
| 1061 | printk(KERN_WARNING "CIFS: srcaddr value" | ||
| 1062 | " not specified.\n"); | ||
| 1063 | return 1; /* needs_arg; */ | ||
| 1064 | } | ||
| 1065 | i = cifs_convert_address((struct sockaddr *)&vol->srcaddr, | ||
| 1066 | value, strlen(value)); | ||
| 1067 | if (i < 0) { | ||
| 1068 | printk(KERN_WARNING "CIFS: Could not parse" | ||
| 1069 | " srcaddr: %s\n", | ||
| 1070 | value); | ||
| 1071 | return 1; | ||
| 1072 | } | ||
| 1049 | } else if (strnicmp(data, "prefixpath", 10) == 0) { | 1073 | } else if (strnicmp(data, "prefixpath", 10) == 0) { |
| 1050 | if (!value || !*value) { | 1074 | if (!value || !*value) { |
| 1051 | printk(KERN_WARNING | 1075 | printk(KERN_WARNING |
| @@ -1325,6 +1349,10 @@ cifs_parse_mount_options(char *options, const char *devname, | |||
| 1325 | "/proc/fs/cifs/LookupCacheEnabled to 0\n"); | 1349 | "/proc/fs/cifs/LookupCacheEnabled to 0\n"); |
| 1326 | } else if (strnicmp(data, "fsc", 3) == 0) { | 1350 | } else if (strnicmp(data, "fsc", 3) == 0) { |
| 1327 | vol->fsc = true; | 1351 | vol->fsc = true; |
| 1352 | } else if (strnicmp(data, "mfsymlinks", 10) == 0) { | ||
| 1353 | vol->mfsymlinks = true; | ||
| 1354 | } else if (strnicmp(data, "multiuser", 8) == 0) { | ||
| 1355 | vol->multiuser = true; | ||
| 1328 | } else | 1356 | } else |
| 1329 | printk(KERN_WARNING "CIFS: Unknown mount option %s\n", | 1357 | printk(KERN_WARNING "CIFS: Unknown mount option %s\n", |
| 1330 | data); | 1358 | data); |
| @@ -1356,6 +1384,13 @@ cifs_parse_mount_options(char *options, const char *devname, | |||
| 1356 | return 1; | 1384 | return 1; |
| 1357 | } | 1385 | } |
| 1358 | } | 1386 | } |
| 1387 | |||
| 1388 | if (vol->multiuser && !(vol->secFlg & CIFSSEC_MAY_KRB5)) { | ||
| 1389 | cERROR(1, "Multiuser mounts currently require krb5 " | ||
| 1390 | "authentication!"); | ||
| 1391 | return 1; | ||
| 1392 | } | ||
| 1393 | |||
| 1359 | if (vol->UNCip == NULL) | 1394 | if (vol->UNCip == NULL) |
| 1360 | vol->UNCip = &vol->UNC[2]; | 1395 | vol->UNCip = &vol->UNC[2]; |
| 1361 | 1396 | ||
| @@ -1374,8 +1409,36 @@ cifs_parse_mount_options(char *options, const char *devname, | |||
| 1374 | return 0; | 1409 | return 0; |
| 1375 | } | 1410 | } |
| 1376 | 1411 | ||
| 1412 | /** Returns true if srcaddr isn't specified and rhs isn't | ||
| 1413 | * specified, or if srcaddr is specified and | ||
| 1414 | * matches the IP address of the rhs argument. | ||
| 1415 | */ | ||
| 1416 | static bool | ||
| 1417 | srcip_matches(struct sockaddr *srcaddr, struct sockaddr *rhs) | ||
| 1418 | { | ||
| 1419 | switch (srcaddr->sa_family) { | ||
| 1420 | case AF_UNSPEC: | ||
| 1421 | return (rhs->sa_family == AF_UNSPEC); | ||
| 1422 | case AF_INET: { | ||
| 1423 | struct sockaddr_in *saddr4 = (struct sockaddr_in *)srcaddr; | ||
| 1424 | struct sockaddr_in *vaddr4 = (struct sockaddr_in *)rhs; | ||
| 1425 | return (saddr4->sin_addr.s_addr == vaddr4->sin_addr.s_addr); | ||
| 1426 | } | ||
| 1427 | case AF_INET6: { | ||
| 1428 | struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr; | ||
| 1429 | struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)&rhs; | ||
| 1430 | return ipv6_addr_equal(&saddr6->sin6_addr, &vaddr6->sin6_addr); | ||
| 1431 | } | ||
| 1432 | default: | ||
| 1433 | WARN_ON(1); | ||
| 1434 | return false; /* don't expect to be here */ | ||
| 1435 | } | ||
| 1436 | } | ||
| 1437 | |||
| 1438 | |||
| 1377 | static bool | 1439 | static bool |
| 1378 | match_address(struct TCP_Server_Info *server, struct sockaddr *addr) | 1440 | match_address(struct TCP_Server_Info *server, struct sockaddr *addr, |
| 1441 | struct sockaddr *srcaddr) | ||
| 1379 | { | 1442 | { |
| 1380 | struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; | 1443 | struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; |
| 1381 | struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; | 1444 | struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; |
| @@ -1402,6 +1465,9 @@ match_address(struct TCP_Server_Info *server, struct sockaddr *addr) | |||
| 1402 | break; | 1465 | break; |
| 1403 | } | 1466 | } |
| 1404 | 1467 | ||
| 1468 | if (!srcip_matches(srcaddr, (struct sockaddr *)&server->srcaddr)) | ||
| 1469 | return false; | ||
| 1470 | |||
| 1405 | return true; | 1471 | return true; |
| 1406 | } | 1472 | } |
| 1407 | 1473 | ||
| @@ -1458,29 +1524,21 @@ cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol) | |||
| 1458 | { | 1524 | { |
| 1459 | struct TCP_Server_Info *server; | 1525 | struct TCP_Server_Info *server; |
| 1460 | 1526 | ||
| 1461 | write_lock(&cifs_tcp_ses_lock); | 1527 | spin_lock(&cifs_tcp_ses_lock); |
| 1462 | list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { | 1528 | list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { |
| 1463 | /* | 1529 | if (!match_address(server, addr, |
| 1464 | * the demux thread can exit on its own while still in CifsNew | 1530 | (struct sockaddr *)&vol->srcaddr)) |
| 1465 | * so don't accept any sockets in that state. Since the | ||
| 1466 | * tcpStatus never changes back to CifsNew it's safe to check | ||
| 1467 | * for this without a lock. | ||
| 1468 | */ | ||
| 1469 | if (server->tcpStatus == CifsNew) | ||
| 1470 | continue; | ||
| 1471 | |||
| 1472 | if (!match_address(server, addr)) | ||
| 1473 | continue; | 1531 | continue; |
| 1474 | 1532 | ||
| 1475 | if (!match_security(server, vol)) | 1533 | if (!match_security(server, vol)) |
| 1476 | continue; | 1534 | continue; |
| 1477 | 1535 | ||
| 1478 | ++server->srv_count; | 1536 | ++server->srv_count; |
| 1479 | write_unlock(&cifs_tcp_ses_lock); | 1537 | spin_unlock(&cifs_tcp_ses_lock); |
| 1480 | cFYI(1, "Existing tcp session with server found"); | 1538 | cFYI(1, "Existing tcp session with server found"); |
| 1481 | return server; | 1539 | return server; |
| 1482 | } | 1540 | } |
| 1483 | write_unlock(&cifs_tcp_ses_lock); | 1541 | spin_unlock(&cifs_tcp_ses_lock); |
| 1484 | return NULL; | 1542 | return NULL; |
| 1485 | } | 1543 | } |
| 1486 | 1544 | ||
| @@ -1489,14 +1547,14 @@ cifs_put_tcp_session(struct TCP_Server_Info *server) | |||
| 1489 | { | 1547 | { |
| 1490 | struct task_struct *task; | 1548 | struct task_struct *task; |
| 1491 | 1549 | ||
| 1492 | write_lock(&cifs_tcp_ses_lock); | 1550 | spin_lock(&cifs_tcp_ses_lock); |
| 1493 | if (--server->srv_count > 0) { | 1551 | if (--server->srv_count > 0) { |
| 1494 | write_unlock(&cifs_tcp_ses_lock); | 1552 | spin_unlock(&cifs_tcp_ses_lock); |
| 1495 | return; | 1553 | return; |
| 1496 | } | 1554 | } |
| 1497 | 1555 | ||
| 1498 | list_del_init(&server->tcp_ses_list); | 1556 | list_del_init(&server->tcp_ses_list); |
| 1499 | write_unlock(&cifs_tcp_ses_lock); | 1557 | spin_unlock(&cifs_tcp_ses_lock); |
| 1500 | 1558 | ||
| 1501 | spin_lock(&GlobalMid_Lock); | 1559 | spin_lock(&GlobalMid_Lock); |
| 1502 | server->tcpStatus = CifsExiting; | 1560 | server->tcpStatus = CifsExiting; |
| @@ -1574,6 +1632,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) | |||
| 1574 | volume_info->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); | 1632 | volume_info->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); |
| 1575 | memcpy(tcp_ses->server_RFC1001_name, | 1633 | memcpy(tcp_ses->server_RFC1001_name, |
| 1576 | volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); | 1634 | volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); |
| 1635 | tcp_ses->session_estab = false; | ||
| 1577 | tcp_ses->sequence_number = 0; | 1636 | tcp_ses->sequence_number = 0; |
| 1578 | INIT_LIST_HEAD(&tcp_ses->tcp_ses_list); | 1637 | INIT_LIST_HEAD(&tcp_ses->tcp_ses_list); |
| 1579 | INIT_LIST_HEAD(&tcp_ses->smb_ses_list); | 1638 | INIT_LIST_HEAD(&tcp_ses->smb_ses_list); |
| @@ -1584,6 +1643,8 @@ cifs_get_tcp_session(struct smb_vol *volume_info) | |||
| 1584 | * no need to spinlock this init of tcpStatus or srv_count | 1643 | * no need to spinlock this init of tcpStatus or srv_count |
| 1585 | */ | 1644 | */ |
| 1586 | tcp_ses->tcpStatus = CifsNew; | 1645 | tcp_ses->tcpStatus = CifsNew; |
| 1646 | memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr, | ||
| 1647 | sizeof(tcp_ses->srcaddr)); | ||
| 1587 | ++tcp_ses->srv_count; | 1648 | ++tcp_ses->srv_count; |
| 1588 | 1649 | ||
| 1589 | if (addr.ss_family == AF_INET6) { | 1650 | if (addr.ss_family == AF_INET6) { |
| @@ -1618,9 +1679,9 @@ cifs_get_tcp_session(struct smb_vol *volume_info) | |||
| 1618 | } | 1679 | } |
| 1619 | 1680 | ||
| 1620 | /* thread spawned, put it on the list */ | 1681 | /* thread spawned, put it on the list */ |
| 1621 | write_lock(&cifs_tcp_ses_lock); | 1682 | spin_lock(&cifs_tcp_ses_lock); |
| 1622 | list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list); | 1683 | list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list); |
| 1623 | write_unlock(&cifs_tcp_ses_lock); | 1684 | spin_unlock(&cifs_tcp_ses_lock); |
| 1624 | 1685 | ||
| 1625 | cifs_fscache_get_client_cookie(tcp_ses); | 1686 | cifs_fscache_get_client_cookie(tcp_ses); |
| 1626 | 1687 | ||
| @@ -1642,7 +1703,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol) | |||
| 1642 | { | 1703 | { |
| 1643 | struct cifsSesInfo *ses; | 1704 | struct cifsSesInfo *ses; |
| 1644 | 1705 | ||
| 1645 | write_lock(&cifs_tcp_ses_lock); | 1706 | spin_lock(&cifs_tcp_ses_lock); |
| 1646 | list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { | 1707 | list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { |
| 1647 | switch (server->secType) { | 1708 | switch (server->secType) { |
| 1648 | case Kerberos: | 1709 | case Kerberos: |
| @@ -1662,10 +1723,10 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol) | |||
| 1662 | continue; | 1723 | continue; |
| 1663 | } | 1724 | } |
| 1664 | ++ses->ses_count; | 1725 | ++ses->ses_count; |
| 1665 | write_unlock(&cifs_tcp_ses_lock); | 1726 | spin_unlock(&cifs_tcp_ses_lock); |
| 1666 | return ses; | 1727 | return ses; |
| 1667 | } | 1728 | } |
| 1668 | write_unlock(&cifs_tcp_ses_lock); | 1729 | spin_unlock(&cifs_tcp_ses_lock); |
| 1669 | return NULL; | 1730 | return NULL; |
| 1670 | } | 1731 | } |
| 1671 | 1732 | ||
| @@ -1676,14 +1737,14 @@ cifs_put_smb_ses(struct cifsSesInfo *ses) | |||
| 1676 | struct TCP_Server_Info *server = ses->server; | 1737 | struct TCP_Server_Info *server = ses->server; |
| 1677 | 1738 | ||
| 1678 | cFYI(1, "%s: ses_count=%d\n", __func__, ses->ses_count); | 1739 | cFYI(1, "%s: ses_count=%d\n", __func__, ses->ses_count); |
| 1679 | write_lock(&cifs_tcp_ses_lock); | 1740 | spin_lock(&cifs_tcp_ses_lock); |
| 1680 | if (--ses->ses_count > 0) { | 1741 | if (--ses->ses_count > 0) { |
| 1681 | write_unlock(&cifs_tcp_ses_lock); | 1742 | spin_unlock(&cifs_tcp_ses_lock); |
| 1682 | return; | 1743 | return; |
| 1683 | } | 1744 | } |
| 1684 | 1745 | ||
| 1685 | list_del_init(&ses->smb_ses_list); | 1746 | list_del_init(&ses->smb_ses_list); |
| 1686 | write_unlock(&cifs_tcp_ses_lock); | 1747 | spin_unlock(&cifs_tcp_ses_lock); |
| 1687 | 1748 | ||
| 1688 | if (ses->status == CifsGood) { | 1749 | if (ses->status == CifsGood) { |
| 1689 | xid = GetXid(); | 1750 | xid = GetXid(); |
| @@ -1740,6 +1801,8 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) | |||
| 1740 | if (ses == NULL) | 1801 | if (ses == NULL) |
| 1741 | goto get_ses_fail; | 1802 | goto get_ses_fail; |
| 1742 | 1803 | ||
| 1804 | ses->tilen = 0; | ||
| 1805 | ses->tiblob = NULL; | ||
| 1743 | /* new SMB session uses our server ref */ | 1806 | /* new SMB session uses our server ref */ |
| 1744 | ses->server = server; | 1807 | ses->server = server; |
| 1745 | if (server->addr.sockAddr6.sin6_family == AF_INET6) | 1808 | if (server->addr.sockAddr6.sin6_family == AF_INET6) |
| @@ -1778,9 +1841,9 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) | |||
| 1778 | goto get_ses_fail; | 1841 | goto get_ses_fail; |
| 1779 | 1842 | ||
| 1780 | /* success, put it on the list */ | 1843 | /* success, put it on the list */ |
| 1781 | write_lock(&cifs_tcp_ses_lock); | 1844 | spin_lock(&cifs_tcp_ses_lock); |
| 1782 | list_add(&ses->smb_ses_list, &server->smb_ses_list); | 1845 | list_add(&ses->smb_ses_list, &server->smb_ses_list); |
| 1783 | write_unlock(&cifs_tcp_ses_lock); | 1846 | spin_unlock(&cifs_tcp_ses_lock); |
| 1784 | 1847 | ||
| 1785 | FreeXid(xid); | 1848 | FreeXid(xid); |
| 1786 | return ses; | 1849 | return ses; |
| @@ -1797,7 +1860,7 @@ cifs_find_tcon(struct cifsSesInfo *ses, const char *unc) | |||
| 1797 | struct list_head *tmp; | 1860 | struct list_head *tmp; |
| 1798 | struct cifsTconInfo *tcon; | 1861 | struct cifsTconInfo *tcon; |
| 1799 | 1862 | ||
| 1800 | write_lock(&cifs_tcp_ses_lock); | 1863 | spin_lock(&cifs_tcp_ses_lock); |
| 1801 | list_for_each(tmp, &ses->tcon_list) { | 1864 | list_for_each(tmp, &ses->tcon_list) { |
| 1802 | tcon = list_entry(tmp, struct cifsTconInfo, tcon_list); | 1865 | tcon = list_entry(tmp, struct cifsTconInfo, tcon_list); |
| 1803 | if (tcon->tidStatus == CifsExiting) | 1866 | if (tcon->tidStatus == CifsExiting) |
| @@ -1806,10 +1869,10 @@ cifs_find_tcon(struct cifsSesInfo *ses, const char *unc) | |||
| 1806 | continue; | 1869 | continue; |
| 1807 | 1870 | ||
| 1808 | ++tcon->tc_count; | 1871 | ++tcon->tc_count; |
| 1809 | write_unlock(&cifs_tcp_ses_lock); | 1872 | spin_unlock(&cifs_tcp_ses_lock); |
| 1810 | return tcon; | 1873 | return tcon; |
| 1811 | } | 1874 | } |
| 1812 | write_unlock(&cifs_tcp_ses_lock); | 1875 | spin_unlock(&cifs_tcp_ses_lock); |
| 1813 | return NULL; | 1876 | return NULL; |
| 1814 | } | 1877 | } |
| 1815 | 1878 | ||
| @@ -1820,14 +1883,14 @@ cifs_put_tcon(struct cifsTconInfo *tcon) | |||
| 1820 | struct cifsSesInfo *ses = tcon->ses; | 1883 | struct cifsSesInfo *ses = tcon->ses; |
| 1821 | 1884 | ||
| 1822 | cFYI(1, "%s: tc_count=%d\n", __func__, tcon->tc_count); | 1885 | cFYI(1, "%s: tc_count=%d\n", __func__, tcon->tc_count); |
| 1823 | write_lock(&cifs_tcp_ses_lock); | 1886 | spin_lock(&cifs_tcp_ses_lock); |
| 1824 | if (--tcon->tc_count > 0) { | 1887 | if (--tcon->tc_count > 0) { |
| 1825 | write_unlock(&cifs_tcp_ses_lock); | 1888 | spin_unlock(&cifs_tcp_ses_lock); |
| 1826 | return; | 1889 | return; |
| 1827 | } | 1890 | } |
| 1828 | 1891 | ||
| 1829 | list_del_init(&tcon->tcon_list); | 1892 | list_del_init(&tcon->tcon_list); |
| 1830 | write_unlock(&cifs_tcp_ses_lock); | 1893 | spin_unlock(&cifs_tcp_ses_lock); |
| 1831 | 1894 | ||
| 1832 | xid = GetXid(); | 1895 | xid = GetXid(); |
| 1833 | CIFSSMBTDis(xid, tcon); | 1896 | CIFSSMBTDis(xid, tcon); |
| @@ -1900,9 +1963,9 @@ cifs_get_tcon(struct cifsSesInfo *ses, struct smb_vol *volume_info) | |||
| 1900 | tcon->nocase = volume_info->nocase; | 1963 | tcon->nocase = volume_info->nocase; |
| 1901 | tcon->local_lease = volume_info->local_lease; | 1964 | tcon->local_lease = volume_info->local_lease; |
| 1902 | 1965 | ||
| 1903 | write_lock(&cifs_tcp_ses_lock); | 1966 | spin_lock(&cifs_tcp_ses_lock); |
| 1904 | list_add(&tcon->tcon_list, &ses->tcon_list); | 1967 | list_add(&tcon->tcon_list, &ses->tcon_list); |
| 1905 | write_unlock(&cifs_tcp_ses_lock); | 1968 | spin_unlock(&cifs_tcp_ses_lock); |
| 1906 | 1969 | ||
| 1907 | cifs_fscache_get_super_cookie(tcon); | 1970 | cifs_fscache_get_super_cookie(tcon); |
| 1908 | 1971 | ||
| @@ -1913,6 +1976,23 @@ out_fail: | |||
| 1913 | return ERR_PTR(rc); | 1976 | return ERR_PTR(rc); |
| 1914 | } | 1977 | } |
| 1915 | 1978 | ||
| 1979 | void | ||
| 1980 | cifs_put_tlink(struct tcon_link *tlink) | ||
| 1981 | { | ||
| 1982 | if (!tlink || IS_ERR(tlink)) | ||
| 1983 | return; | ||
| 1984 | |||
| 1985 | if (!atomic_dec_and_test(&tlink->tl_count) || | ||
| 1986 | test_bit(TCON_LINK_IN_TREE, &tlink->tl_flags)) { | ||
| 1987 | tlink->tl_time = jiffies; | ||
| 1988 | return; | ||
| 1989 | } | ||
| 1990 | |||
| 1991 | if (!IS_ERR(tlink_tcon(tlink))) | ||
| 1992 | cifs_put_tcon(tlink_tcon(tlink)); | ||
| 1993 | kfree(tlink); | ||
| 1994 | return; | ||
| 1995 | } | ||
| 1916 | 1996 | ||
| 1917 | int | 1997 | int |
| 1918 | get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, | 1998 | get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, |
| @@ -1997,6 +2077,33 @@ static void rfc1002mangle(char *target, char *source, unsigned int length) | |||
| 1997 | 2077 | ||
| 1998 | } | 2078 | } |
| 1999 | 2079 | ||
| 2080 | static int | ||
| 2081 | bind_socket(struct TCP_Server_Info *server) | ||
| 2082 | { | ||
| 2083 | int rc = 0; | ||
| 2084 | if (server->srcaddr.ss_family != AF_UNSPEC) { | ||
| 2085 | /* Bind to the specified local IP address */ | ||
| 2086 | struct socket *socket = server->ssocket; | ||
| 2087 | rc = socket->ops->bind(socket, | ||
| 2088 | (struct sockaddr *) &server->srcaddr, | ||
| 2089 | sizeof(server->srcaddr)); | ||
| 2090 | if (rc < 0) { | ||
| 2091 | struct sockaddr_in *saddr4; | ||
| 2092 | struct sockaddr_in6 *saddr6; | ||
| 2093 | saddr4 = (struct sockaddr_in *)&server->srcaddr; | ||
| 2094 | saddr6 = (struct sockaddr_in6 *)&server->srcaddr; | ||
| 2095 | if (saddr6->sin6_family == AF_INET6) | ||
| 2096 | cERROR(1, "cifs: " | ||
| 2097 | "Failed to bind to: %pI6c, error: %d\n", | ||
| 2098 | &saddr6->sin6_addr, rc); | ||
| 2099 | else | ||
| 2100 | cERROR(1, "cifs: " | ||
| 2101 | "Failed to bind to: %pI4, error: %d\n", | ||
| 2102 | &saddr4->sin_addr.s_addr, rc); | ||
| 2103 | } | ||
| 2104 | } | ||
| 2105 | return rc; | ||
| 2106 | } | ||
| 2000 | 2107 | ||
| 2001 | static int | 2108 | static int |
| 2002 | ipv4_connect(struct TCP_Server_Info *server) | 2109 | ipv4_connect(struct TCP_Server_Info *server) |
| @@ -2022,6 +2129,10 @@ ipv4_connect(struct TCP_Server_Info *server) | |||
| 2022 | cifs_reclassify_socket4(socket); | 2129 | cifs_reclassify_socket4(socket); |
| 2023 | } | 2130 | } |
| 2024 | 2131 | ||
| 2132 | rc = bind_socket(server); | ||
| 2133 | if (rc < 0) | ||
| 2134 | return rc; | ||
| 2135 | |||
| 2025 | /* user overrode default port */ | 2136 | /* user overrode default port */ |
| 2026 | if (server->addr.sockAddr.sin_port) { | 2137 | if (server->addr.sockAddr.sin_port) { |
| 2027 | rc = socket->ops->connect(socket, (struct sockaddr *) | 2138 | rc = socket->ops->connect(socket, (struct sockaddr *) |
| @@ -2184,6 +2295,10 @@ ipv6_connect(struct TCP_Server_Info *server) | |||
| 2184 | cifs_reclassify_socket6(socket); | 2295 | cifs_reclassify_socket6(socket); |
| 2185 | } | 2296 | } |
| 2186 | 2297 | ||
| 2298 | rc = bind_socket(server); | ||
| 2299 | if (rc < 0) | ||
| 2300 | return rc; | ||
| 2301 | |||
| 2187 | /* user overrode default port */ | 2302 | /* user overrode default port */ |
| 2188 | if (server->addr.sockAddr6.sin6_port) { | 2303 | if (server->addr.sockAddr6.sin6_port) { |
| 2189 | rc = socket->ops->connect(socket, | 2304 | rc = socket->ops->connect(socket, |
| @@ -2383,6 +2498,8 @@ convert_delimiter(char *path, char delim) | |||
| 2383 | static void setup_cifs_sb(struct smb_vol *pvolume_info, | 2498 | static void setup_cifs_sb(struct smb_vol *pvolume_info, |
| 2384 | struct cifs_sb_info *cifs_sb) | 2499 | struct cifs_sb_info *cifs_sb) |
| 2385 | { | 2500 | { |
| 2501 | INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks); | ||
| 2502 | |||
| 2386 | if (pvolume_info->rsize > CIFSMaxBufSize) { | 2503 | if (pvolume_info->rsize > CIFSMaxBufSize) { |
| 2387 | cERROR(1, "rsize %d too large, using MaxBufSize", | 2504 | cERROR(1, "rsize %d too large, using MaxBufSize", |
| 2388 | pvolume_info->rsize); | 2505 | pvolume_info->rsize); |
| @@ -2462,10 +2579,21 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info, | |||
| 2462 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; | 2579 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; |
| 2463 | if (pvolume_info->fsc) | 2580 | if (pvolume_info->fsc) |
| 2464 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_FSCACHE; | 2581 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_FSCACHE; |
| 2582 | if (pvolume_info->multiuser) | ||
| 2583 | cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_MULTIUSER | | ||
| 2584 | CIFS_MOUNT_NO_PERM); | ||
| 2465 | if (pvolume_info->direct_io) { | 2585 | if (pvolume_info->direct_io) { |
| 2466 | cFYI(1, "mounting share using direct i/o"); | 2586 | cFYI(1, "mounting share using direct i/o"); |
| 2467 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; | 2587 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; |
| 2468 | } | 2588 | } |
| 2589 | if (pvolume_info->mfsymlinks) { | ||
| 2590 | if (pvolume_info->sfu_emul) { | ||
| 2591 | cERROR(1, "mount option mfsymlinks ignored if sfu " | ||
| 2592 | "mount option is used"); | ||
| 2593 | } else { | ||
| 2594 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MF_SYMLINKS; | ||
| 2595 | } | ||
| 2596 | } | ||
| 2469 | 2597 | ||
| 2470 | if ((pvolume_info->cifs_acl) && (pvolume_info->dynperm)) | 2598 | if ((pvolume_info->cifs_acl) && (pvolume_info->dynperm)) |
| 2471 | cERROR(1, "mount option dynperm ignored if cifsacl " | 2599 | cERROR(1, "mount option dynperm ignored if cifsacl " |
| @@ -2552,6 +2680,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
| 2552 | struct TCP_Server_Info *srvTcp; | 2680 | struct TCP_Server_Info *srvTcp; |
| 2553 | char *full_path; | 2681 | char *full_path; |
| 2554 | char *mount_data = mount_data_global; | 2682 | char *mount_data = mount_data_global; |
| 2683 | struct tcon_link *tlink; | ||
| 2555 | #ifdef CONFIG_CIFS_DFS_UPCALL | 2684 | #ifdef CONFIG_CIFS_DFS_UPCALL |
| 2556 | struct dfs_info3_param *referrals = NULL; | 2685 | struct dfs_info3_param *referrals = NULL; |
| 2557 | unsigned int num_referrals = 0; | 2686 | unsigned int num_referrals = 0; |
| @@ -2563,6 +2692,7 @@ try_mount_again: | |||
| 2563 | pSesInfo = NULL; | 2692 | pSesInfo = NULL; |
| 2564 | srvTcp = NULL; | 2693 | srvTcp = NULL; |
| 2565 | full_path = NULL; | 2694 | full_path = NULL; |
| 2695 | tlink = NULL; | ||
| 2566 | 2696 | ||
| 2567 | xid = GetXid(); | 2697 | xid = GetXid(); |
| 2568 | 2698 | ||
| @@ -2638,8 +2768,6 @@ try_mount_again: | |||
| 2638 | goto remote_path_check; | 2768 | goto remote_path_check; |
| 2639 | } | 2769 | } |
| 2640 | 2770 | ||
| 2641 | cifs_sb->tcon = tcon; | ||
| 2642 | |||
| 2643 | /* do not care if following two calls succeed - informational */ | 2771 | /* do not care if following two calls succeed - informational */ |
| 2644 | if (!tcon->ipc) { | 2772 | if (!tcon->ipc) { |
| 2645 | CIFSSMBQFSDeviceInfo(xid, tcon); | 2773 | CIFSSMBQFSDeviceInfo(xid, tcon); |
| @@ -2748,6 +2876,38 @@ remote_path_check: | |||
| 2748 | #endif | 2876 | #endif |
| 2749 | } | 2877 | } |
| 2750 | 2878 | ||
| 2879 | if (rc) | ||
| 2880 | goto mount_fail_check; | ||
| 2881 | |||
| 2882 | /* now, hang the tcon off of the superblock */ | ||
| 2883 | tlink = kzalloc(sizeof *tlink, GFP_KERNEL); | ||
| 2884 | if (tlink == NULL) { | ||
| 2885 | rc = -ENOMEM; | ||
| 2886 | goto mount_fail_check; | ||
| 2887 | } | ||
| 2888 | |||
| 2889 | tlink->tl_index = pSesInfo->linux_uid; | ||
| 2890 | tlink->tl_tcon = tcon; | ||
| 2891 | tlink->tl_time = jiffies; | ||
| 2892 | set_bit(TCON_LINK_MASTER, &tlink->tl_flags); | ||
| 2893 | set_bit(TCON_LINK_IN_TREE, &tlink->tl_flags); | ||
| 2894 | |||
| 2895 | rc = radix_tree_preload(GFP_KERNEL); | ||
| 2896 | if (rc == -ENOMEM) { | ||
| 2897 | kfree(tlink); | ||
| 2898 | goto mount_fail_check; | ||
| 2899 | } | ||
| 2900 | |||
| 2901 | spin_lock(&cifs_sb->tlink_tree_lock); | ||
| 2902 | radix_tree_insert(&cifs_sb->tlink_tree, pSesInfo->linux_uid, tlink); | ||
| 2903 | radix_tree_tag_set(&cifs_sb->tlink_tree, pSesInfo->linux_uid, | ||
| 2904 | CIFS_TLINK_MASTER_TAG); | ||
| 2905 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
| 2906 | radix_tree_preload_end(); | ||
| 2907 | |||
| 2908 | queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks, | ||
| 2909 | TLINK_IDLE_EXPIRE); | ||
| 2910 | |||
| 2751 | mount_fail_check: | 2911 | mount_fail_check: |
| 2752 | /* on error free sesinfo and tcon struct if needed */ | 2912 | /* on error free sesinfo and tcon struct if needed */ |
| 2753 | if (rc) { | 2913 | if (rc) { |
| @@ -2825,14 +2985,13 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, | |||
| 2825 | #ifdef CONFIG_CIFS_WEAK_PW_HASH | 2985 | #ifdef CONFIG_CIFS_WEAK_PW_HASH |
| 2826 | if ((global_secflags & CIFSSEC_MAY_LANMAN) && | 2986 | if ((global_secflags & CIFSSEC_MAY_LANMAN) && |
| 2827 | (ses->server->secType == LANMAN)) | 2987 | (ses->server->secType == LANMAN)) |
| 2828 | calc_lanman_hash(tcon->password, ses->server->cryptKey, | 2988 | calc_lanman_hash(tcon->password, ses->cryptKey, |
| 2829 | ses->server->secMode & | 2989 | ses->server->secMode & |
| 2830 | SECMODE_PW_ENCRYPT ? true : false, | 2990 | SECMODE_PW_ENCRYPT ? true : false, |
| 2831 | bcc_ptr); | 2991 | bcc_ptr); |
| 2832 | else | 2992 | else |
| 2833 | #endif /* CIFS_WEAK_PW_HASH */ | 2993 | #endif /* CIFS_WEAK_PW_HASH */ |
| 2834 | SMBNTencrypt(tcon->password, ses->server->cryptKey, | 2994 | SMBNTencrypt(tcon->password, ses->cryptKey, bcc_ptr); |
| 2835 | bcc_ptr); | ||
| 2836 | 2995 | ||
| 2837 | bcc_ptr += CIFS_SESS_KEY_SIZE; | 2996 | bcc_ptr += CIFS_SESS_KEY_SIZE; |
| 2838 | if (ses->capabilities & CAP_UNICODE) { | 2997 | if (ses->capabilities & CAP_UNICODE) { |
| @@ -2934,19 +3093,39 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, | |||
| 2934 | int | 3093 | int |
| 2935 | cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) | 3094 | cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) |
| 2936 | { | 3095 | { |
| 2937 | int rc = 0; | 3096 | int i, ret; |
| 2938 | char *tmp; | 3097 | char *tmp; |
| 3098 | struct tcon_link *tlink[8]; | ||
| 3099 | unsigned long index = 0; | ||
| 3100 | |||
| 3101 | cancel_delayed_work_sync(&cifs_sb->prune_tlinks); | ||
| 3102 | |||
| 3103 | do { | ||
| 3104 | spin_lock(&cifs_sb->tlink_tree_lock); | ||
| 3105 | ret = radix_tree_gang_lookup(&cifs_sb->tlink_tree, | ||
| 3106 | (void **)tlink, index, | ||
| 3107 | ARRAY_SIZE(tlink)); | ||
| 3108 | /* increment index for next pass */ | ||
| 3109 | if (ret > 0) | ||
| 3110 | index = tlink[ret - 1]->tl_index + 1; | ||
| 3111 | for (i = 0; i < ret; i++) { | ||
| 3112 | cifs_get_tlink(tlink[i]); | ||
| 3113 | clear_bit(TCON_LINK_IN_TREE, &tlink[i]->tl_flags); | ||
| 3114 | radix_tree_delete(&cifs_sb->tlink_tree, | ||
| 3115 | tlink[i]->tl_index); | ||
| 3116 | } | ||
| 3117 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
| 2939 | 3118 | ||
| 2940 | if (cifs_sb->tcon) | 3119 | for (i = 0; i < ret; i++) |
| 2941 | cifs_put_tcon(cifs_sb->tcon); | 3120 | cifs_put_tlink(tlink[i]); |
| 3121 | } while (ret != 0); | ||
| 2942 | 3122 | ||
| 2943 | cifs_sb->tcon = NULL; | ||
| 2944 | tmp = cifs_sb->prepath; | 3123 | tmp = cifs_sb->prepath; |
| 2945 | cifs_sb->prepathlen = 0; | 3124 | cifs_sb->prepathlen = 0; |
| 2946 | cifs_sb->prepath = NULL; | 3125 | cifs_sb->prepath = NULL; |
| 2947 | kfree(tmp); | 3126 | kfree(tmp); |
| 2948 | 3127 | ||
| 2949 | return rc; | 3128 | return 0; |
| 2950 | } | 3129 | } |
| 2951 | 3130 | ||
| 2952 | int cifs_negotiate_protocol(unsigned int xid, struct cifsSesInfo *ses) | 3131 | int cifs_negotiate_protocol(unsigned int xid, struct cifsSesInfo *ses) |
| @@ -2997,6 +3176,15 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses, | |||
| 2997 | if (rc) { | 3176 | if (rc) { |
| 2998 | cERROR(1, "Send error in SessSetup = %d", rc); | 3177 | cERROR(1, "Send error in SessSetup = %d", rc); |
| 2999 | } else { | 3178 | } else { |
| 3179 | mutex_lock(&ses->server->srv_mutex); | ||
| 3180 | if (!server->session_estab) { | ||
| 3181 | memcpy(&server->session_key.data, | ||
| 3182 | &ses->auth_key.data, ses->auth_key.len); | ||
| 3183 | server->session_key.len = ses->auth_key.len; | ||
| 3184 | ses->server->session_estab = true; | ||
| 3185 | } | ||
| 3186 | mutex_unlock(&server->srv_mutex); | ||
| 3187 | |||
| 3000 | cFYI(1, "CIFS Session Established successfully"); | 3188 | cFYI(1, "CIFS Session Established successfully"); |
| 3001 | spin_lock(&GlobalMid_Lock); | 3189 | spin_lock(&GlobalMid_Lock); |
| 3002 | ses->status = CifsGood; | 3190 | ses->status = CifsGood; |
| @@ -3007,3 +3195,237 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses, | |||
| 3007 | return rc; | 3195 | return rc; |
| 3008 | } | 3196 | } |
| 3009 | 3197 | ||
| 3198 | static struct cifsTconInfo * | ||
| 3199 | cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) | ||
| 3200 | { | ||
| 3201 | struct cifsTconInfo *master_tcon = cifs_sb_master_tcon(cifs_sb); | ||
| 3202 | struct cifsSesInfo *ses; | ||
| 3203 | struct cifsTconInfo *tcon = NULL; | ||
| 3204 | struct smb_vol *vol_info; | ||
| 3205 | char username[MAX_USERNAME_SIZE + 1]; | ||
| 3206 | |||
| 3207 | vol_info = kzalloc(sizeof(*vol_info), GFP_KERNEL); | ||
| 3208 | if (vol_info == NULL) { | ||
| 3209 | tcon = ERR_PTR(-ENOMEM); | ||
| 3210 | goto out; | ||
| 3211 | } | ||
| 3212 | |||
| 3213 | snprintf(username, MAX_USERNAME_SIZE, "krb50x%x", fsuid); | ||
| 3214 | vol_info->username = username; | ||
| 3215 | vol_info->local_nls = cifs_sb->local_nls; | ||
| 3216 | vol_info->linux_uid = fsuid; | ||
| 3217 | vol_info->cred_uid = fsuid; | ||
| 3218 | vol_info->UNC = master_tcon->treeName; | ||
| 3219 | vol_info->retry = master_tcon->retry; | ||
| 3220 | vol_info->nocase = master_tcon->nocase; | ||
| 3221 | vol_info->local_lease = master_tcon->local_lease; | ||
| 3222 | vol_info->no_linux_ext = !master_tcon->unix_ext; | ||
| 3223 | |||
| 3224 | /* FIXME: allow for other secFlg settings */ | ||
| 3225 | vol_info->secFlg = CIFSSEC_MUST_KRB5; | ||
| 3226 | |||
| 3227 | /* get a reference for the same TCP session */ | ||
| 3228 | spin_lock(&cifs_tcp_ses_lock); | ||
| 3229 | ++master_tcon->ses->server->srv_count; | ||
| 3230 | spin_unlock(&cifs_tcp_ses_lock); | ||
| 3231 | |||
| 3232 | ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info); | ||
| 3233 | if (IS_ERR(ses)) { | ||
| 3234 | tcon = (struct cifsTconInfo *)ses; | ||
| 3235 | cifs_put_tcp_session(master_tcon->ses->server); | ||
| 3236 | goto out; | ||
| 3237 | } | ||
| 3238 | |||
| 3239 | tcon = cifs_get_tcon(ses, vol_info); | ||
| 3240 | if (IS_ERR(tcon)) { | ||
| 3241 | cifs_put_smb_ses(ses); | ||
| 3242 | goto out; | ||
| 3243 | } | ||
| 3244 | |||
| 3245 | if (ses->capabilities & CAP_UNIX) | ||
| 3246 | reset_cifs_unix_caps(0, tcon, NULL, vol_info); | ||
| 3247 | out: | ||
| 3248 | kfree(vol_info); | ||
| 3249 | |||
| 3250 | return tcon; | ||
| 3251 | } | ||
| 3252 | |||
| 3253 | static struct tcon_link * | ||
| 3254 | cifs_sb_master_tlink(struct cifs_sb_info *cifs_sb) | ||
| 3255 | { | ||
| 3256 | struct tcon_link *tlink; | ||
| 3257 | unsigned int ret; | ||
| 3258 | |||
| 3259 | spin_lock(&cifs_sb->tlink_tree_lock); | ||
| 3260 | ret = radix_tree_gang_lookup_tag(&cifs_sb->tlink_tree, (void **)&tlink, | ||
| 3261 | 0, 1, CIFS_TLINK_MASTER_TAG); | ||
| 3262 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
| 3263 | |||
| 3264 | /* the master tcon should always be present */ | ||
| 3265 | if (ret == 0) | ||
| 3266 | BUG(); | ||
| 3267 | |||
| 3268 | return tlink; | ||
| 3269 | } | ||
| 3270 | |||
| 3271 | struct cifsTconInfo * | ||
| 3272 | cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) | ||
| 3273 | { | ||
| 3274 | return tlink_tcon(cifs_sb_master_tlink(cifs_sb)); | ||
| 3275 | } | ||
| 3276 | |||
| 3277 | static int | ||
| 3278 | cifs_sb_tcon_pending_wait(void *unused) | ||
| 3279 | { | ||
| 3280 | schedule(); | ||
| 3281 | return signal_pending(current) ? -ERESTARTSYS : 0; | ||
| 3282 | } | ||
| 3283 | |||
| 3284 | /* | ||
| 3285 | * Find or construct an appropriate tcon given a cifs_sb and the fsuid of the | ||
| 3286 | * current task. | ||
| 3287 | * | ||
| 3288 | * If the superblock doesn't refer to a multiuser mount, then just return | ||
| 3289 | * the master tcon for the mount. | ||
| 3290 | * | ||
| 3291 | * First, search the radix tree for an existing tcon for this fsuid. If one | ||
| 3292 | * exists, then check to see if it's pending construction. If it is then wait | ||
| 3293 | * for construction to complete. Once it's no longer pending, check to see if | ||
| 3294 | * it failed and either return an error or retry construction, depending on | ||
| 3295 | * the timeout. | ||
| 3296 | * | ||
| 3297 | * If one doesn't exist then insert a new tcon_link struct into the tree and | ||
| 3298 | * try to construct a new one. | ||
| 3299 | */ | ||
| 3300 | struct tcon_link * | ||
| 3301 | cifs_sb_tlink(struct cifs_sb_info *cifs_sb) | ||
| 3302 | { | ||
| 3303 | int ret; | ||
| 3304 | unsigned long fsuid = (unsigned long) current_fsuid(); | ||
| 3305 | struct tcon_link *tlink, *newtlink; | ||
| 3306 | |||
| 3307 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) | ||
| 3308 | return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb)); | ||
| 3309 | |||
| 3310 | spin_lock(&cifs_sb->tlink_tree_lock); | ||
| 3311 | tlink = radix_tree_lookup(&cifs_sb->tlink_tree, fsuid); | ||
| 3312 | if (tlink) | ||
| 3313 | cifs_get_tlink(tlink); | ||
| 3314 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
| 3315 | |||
| 3316 | if (tlink == NULL) { | ||
| 3317 | newtlink = kzalloc(sizeof(*tlink), GFP_KERNEL); | ||
| 3318 | if (newtlink == NULL) | ||
| 3319 | return ERR_PTR(-ENOMEM); | ||
| 3320 | newtlink->tl_index = fsuid; | ||
| 3321 | newtlink->tl_tcon = ERR_PTR(-EACCES); | ||
| 3322 | set_bit(TCON_LINK_PENDING, &newtlink->tl_flags); | ||
| 3323 | set_bit(TCON_LINK_IN_TREE, &newtlink->tl_flags); | ||
| 3324 | cifs_get_tlink(newtlink); | ||
| 3325 | |||
| 3326 | ret = radix_tree_preload(GFP_KERNEL); | ||
| 3327 | if (ret != 0) { | ||
| 3328 | kfree(newtlink); | ||
| 3329 | return ERR_PTR(ret); | ||
| 3330 | } | ||
| 3331 | |||
| 3332 | spin_lock(&cifs_sb->tlink_tree_lock); | ||
| 3333 | /* was one inserted after previous search? */ | ||
| 3334 | tlink = radix_tree_lookup(&cifs_sb->tlink_tree, fsuid); | ||
| 3335 | if (tlink) { | ||
| 3336 | cifs_get_tlink(tlink); | ||
| 3337 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
| 3338 | radix_tree_preload_end(); | ||
| 3339 | kfree(newtlink); | ||
| 3340 | goto wait_for_construction; | ||
| 3341 | } | ||
| 3342 | ret = radix_tree_insert(&cifs_sb->tlink_tree, fsuid, newtlink); | ||
| 3343 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
| 3344 | radix_tree_preload_end(); | ||
| 3345 | if (ret) { | ||
| 3346 | kfree(newtlink); | ||
| 3347 | return ERR_PTR(ret); | ||
| 3348 | } | ||
| 3349 | tlink = newtlink; | ||
| 3350 | } else { | ||
| 3351 | wait_for_construction: | ||
| 3352 | ret = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING, | ||
| 3353 | cifs_sb_tcon_pending_wait, | ||
| 3354 | TASK_INTERRUPTIBLE); | ||
| 3355 | if (ret) { | ||
| 3356 | cifs_put_tlink(tlink); | ||
| 3357 | return ERR_PTR(ret); | ||
| 3358 | } | ||
| 3359 | |||
| 3360 | /* if it's good, return it */ | ||
| 3361 | if (!IS_ERR(tlink->tl_tcon)) | ||
| 3362 | return tlink; | ||
| 3363 | |||
| 3364 | /* return error if we tried this already recently */ | ||
| 3365 | if (time_before(jiffies, tlink->tl_time + TLINK_ERROR_EXPIRE)) { | ||
| 3366 | cifs_put_tlink(tlink); | ||
| 3367 | return ERR_PTR(-EACCES); | ||
| 3368 | } | ||
| 3369 | |||
| 3370 | if (test_and_set_bit(TCON_LINK_PENDING, &tlink->tl_flags)) | ||
| 3371 | goto wait_for_construction; | ||
| 3372 | } | ||
| 3373 | |||
| 3374 | tlink->tl_tcon = cifs_construct_tcon(cifs_sb, fsuid); | ||
| 3375 | clear_bit(TCON_LINK_PENDING, &tlink->tl_flags); | ||
| 3376 | wake_up_bit(&tlink->tl_flags, TCON_LINK_PENDING); | ||
| 3377 | |||
| 3378 | if (IS_ERR(tlink->tl_tcon)) { | ||
| 3379 | cifs_put_tlink(tlink); | ||
| 3380 | return ERR_PTR(-EACCES); | ||
| 3381 | } | ||
| 3382 | |||
| 3383 | return tlink; | ||
| 3384 | } | ||
| 3385 | |||
| 3386 | /* | ||
| 3387 | * periodic workqueue job that scans tcon_tree for a superblock and closes | ||
| 3388 | * out tcons. | ||
| 3389 | */ | ||
| 3390 | static void | ||
| 3391 | cifs_prune_tlinks(struct work_struct *work) | ||
| 3392 | { | ||
| 3393 | struct cifs_sb_info *cifs_sb = container_of(work, struct cifs_sb_info, | ||
| 3394 | prune_tlinks.work); | ||
| 3395 | struct tcon_link *tlink[8]; | ||
| 3396 | unsigned long now = jiffies; | ||
| 3397 | unsigned long index = 0; | ||
| 3398 | int i, ret; | ||
| 3399 | |||
| 3400 | do { | ||
| 3401 | spin_lock(&cifs_sb->tlink_tree_lock); | ||
| 3402 | ret = radix_tree_gang_lookup(&cifs_sb->tlink_tree, | ||
| 3403 | (void **)tlink, index, | ||
| 3404 | ARRAY_SIZE(tlink)); | ||
| 3405 | /* increment index for next pass */ | ||
| 3406 | if (ret > 0) | ||
| 3407 | index = tlink[ret - 1]->tl_index + 1; | ||
| 3408 | for (i = 0; i < ret; i++) { | ||
| 3409 | if (test_bit(TCON_LINK_MASTER, &tlink[i]->tl_flags) || | ||
| 3410 | atomic_read(&tlink[i]->tl_count) != 0 || | ||
| 3411 | time_after(tlink[i]->tl_time + TLINK_IDLE_EXPIRE, | ||
| 3412 | now)) { | ||
| 3413 | tlink[i] = NULL; | ||
| 3414 | continue; | ||
| 3415 | } | ||
| 3416 | cifs_get_tlink(tlink[i]); | ||
| 3417 | clear_bit(TCON_LINK_IN_TREE, &tlink[i]->tl_flags); | ||
| 3418 | radix_tree_delete(&cifs_sb->tlink_tree, | ||
| 3419 | tlink[i]->tl_index); | ||
| 3420 | } | ||
| 3421 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
| 3422 | |||
| 3423 | for (i = 0; i < ret; i++) { | ||
| 3424 | if (tlink[i] != NULL) | ||
| 3425 | cifs_put_tlink(tlink[i]); | ||
| 3426 | } | ||
| 3427 | } while (ret != 0); | ||
| 3428 | |||
| 3429 | queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks, | ||
| 3430 | TLINK_IDLE_EXPIRE); | ||
| 3431 | } | ||
