diff options
Diffstat (limited to 'fs/cifs/connect.c')
-rw-r--r-- | fs/cifs/connect.c | 873 |
1 files changed, 447 insertions, 426 deletions
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 71b7661e2260..c7d341714586 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
@@ -92,6 +92,8 @@ struct smb_vol { | |||
92 | bool seal:1; /* request transport encryption on share */ | 92 | bool seal:1; /* request transport encryption on share */ |
93 | bool nodfs:1; /* Do not request DFS, even if available */ | 93 | bool nodfs:1; /* Do not request DFS, even if available */ |
94 | bool local_lease:1; /* check leases only on local system, not remote */ | 94 | bool local_lease:1; /* check leases only on local system, not remote */ |
95 | bool noblocksnd:1; | ||
96 | bool noautotune:1; | ||
95 | unsigned int rsize; | 97 | unsigned int rsize; |
96 | unsigned int wsize; | 98 | unsigned int wsize; |
97 | unsigned int sockopt; | 99 | unsigned int sockopt; |
@@ -102,9 +104,11 @@ struct smb_vol { | |||
102 | static int ipv4_connect(struct sockaddr_in *psin_server, | 104 | static int ipv4_connect(struct sockaddr_in *psin_server, |
103 | struct socket **csocket, | 105 | struct socket **csocket, |
104 | char *netb_name, | 106 | char *netb_name, |
105 | char *server_netb_name); | 107 | char *server_netb_name, |
108 | bool noblocksnd, | ||
109 | bool nosndbuf); /* ipv6 never set sndbuf size */ | ||
106 | static int ipv6_connect(struct sockaddr_in6 *psin_server, | 110 | static int ipv6_connect(struct sockaddr_in6 *psin_server, |
107 | struct socket **csocket); | 111 | struct socket **csocket, bool noblocksnd); |
108 | 112 | ||
109 | 113 | ||
110 | /* | 114 | /* |
@@ -120,7 +124,7 @@ static int | |||
120 | cifs_reconnect(struct TCP_Server_Info *server) | 124 | cifs_reconnect(struct TCP_Server_Info *server) |
121 | { | 125 | { |
122 | int rc = 0; | 126 | int rc = 0; |
123 | struct list_head *tmp; | 127 | struct list_head *tmp, *tmp2; |
124 | struct cifsSesInfo *ses; | 128 | struct cifsSesInfo *ses; |
125 | struct cifsTconInfo *tcon; | 129 | struct cifsTconInfo *tcon; |
126 | struct mid_q_entry *mid_entry; | 130 | struct mid_q_entry *mid_entry; |
@@ -140,23 +144,17 @@ cifs_reconnect(struct TCP_Server_Info *server) | |||
140 | 144 | ||
141 | /* before reconnecting the tcp session, mark the smb session (uid) | 145 | /* before reconnecting the tcp session, mark the smb session (uid) |
142 | and the tid bad so they are not used until reconnected */ | 146 | and the tid bad so they are not used until reconnected */ |
143 | read_lock(&GlobalSMBSeslock); | 147 | read_lock(&cifs_tcp_ses_lock); |
144 | list_for_each(tmp, &GlobalSMBSessionList) { | 148 | list_for_each(tmp, &server->smb_ses_list) { |
145 | ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); | 149 | ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); |
146 | if (ses->server) { | 150 | ses->need_reconnect = true; |
147 | if (ses->server == server) { | 151 | ses->ipc_tid = 0; |
148 | ses->status = CifsNeedReconnect; | 152 | list_for_each(tmp2, &ses->tcon_list) { |
149 | ses->ipc_tid = 0; | 153 | tcon = list_entry(tmp2, struct cifsTconInfo, tcon_list); |
150 | } | 154 | tcon->need_reconnect = true; |
151 | } | 155 | } |
152 | /* else tcp and smb sessions need reconnection */ | ||
153 | } | ||
154 | list_for_each(tmp, &GlobalTreeConnectionList) { | ||
155 | tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); | ||
156 | if ((tcon->ses) && (tcon->ses->server == server)) | ||
157 | tcon->tidStatus = CifsNeedReconnect; | ||
158 | } | 156 | } |
159 | read_unlock(&GlobalSMBSeslock); | 157 | read_unlock(&cifs_tcp_ses_lock); |
160 | /* do not want to be sending data on a socket we are freeing */ | 158 | /* do not want to be sending data on a socket we are freeing */ |
161 | down(&server->tcpSem); | 159 | down(&server->tcpSem); |
162 | if (server->ssocket) { | 160 | if (server->ssocket) { |
@@ -189,14 +187,15 @@ cifs_reconnect(struct TCP_Server_Info *server) | |||
189 | while ((server->tcpStatus != CifsExiting) && | 187 | while ((server->tcpStatus != CifsExiting) && |
190 | (server->tcpStatus != CifsGood)) { | 188 | (server->tcpStatus != CifsGood)) { |
191 | try_to_freeze(); | 189 | try_to_freeze(); |
192 | if (server->protocolType == IPV6) { | 190 | if (server->addr.sockAddr6.sin6_family == AF_INET6) { |
193 | rc = ipv6_connect(&server->addr.sockAddr6, | 191 | rc = ipv6_connect(&server->addr.sockAddr6, |
194 | &server->ssocket); | 192 | &server->ssocket, server->noautotune); |
195 | } else { | 193 | } else { |
196 | rc = ipv4_connect(&server->addr.sockAddr, | 194 | rc = ipv4_connect(&server->addr.sockAddr, |
197 | &server->ssocket, | 195 | &server->ssocket, |
198 | server->workstation_RFC1001_name, | 196 | server->workstation_RFC1001_name, |
199 | server->server_RFC1001_name); | 197 | server->server_RFC1001_name, |
198 | server->noblocksnd, server->noautotune); | ||
200 | } | 199 | } |
201 | if (rc) { | 200 | if (rc) { |
202 | cFYI(1, ("reconnect error %d", rc)); | 201 | cFYI(1, ("reconnect error %d", rc)); |
@@ -412,9 +411,14 @@ incomplete_rcv: | |||
412 | msleep(1); /* minimum sleep to prevent looping | 411 | msleep(1); /* minimum sleep to prevent looping |
413 | allowing socket to clear and app threads to set | 412 | allowing socket to clear and app threads to set |
414 | tcpStatus CifsNeedReconnect if server hung */ | 413 | tcpStatus CifsNeedReconnect if server hung */ |
415 | if (pdu_length < 4) | 414 | if (pdu_length < 4) { |
415 | iov.iov_base = (4 - pdu_length) + | ||
416 | (char *)smb_buffer; | ||
417 | iov.iov_len = pdu_length; | ||
418 | smb_msg.msg_control = NULL; | ||
419 | smb_msg.msg_controllen = 0; | ||
416 | goto incomplete_rcv; | 420 | goto incomplete_rcv; |
417 | else | 421 | } else |
418 | continue; | 422 | continue; |
419 | } else if (length <= 0) { | 423 | } else if (length <= 0) { |
420 | if (server->tcpStatus == CifsNew) { | 424 | if (server->tcpStatus == CifsNew) { |
@@ -649,6 +653,11 @@ multi_t2_fnd: | |||
649 | } | 653 | } |
650 | } /* end while !EXITING */ | 654 | } /* end while !EXITING */ |
651 | 655 | ||
656 | /* take it off the list, if it's not already */ | ||
657 | write_lock(&cifs_tcp_ses_lock); | ||
658 | list_del_init(&server->tcp_ses_list); | ||
659 | write_unlock(&cifs_tcp_ses_lock); | ||
660 | |||
652 | spin_lock(&GlobalMid_Lock); | 661 | spin_lock(&GlobalMid_Lock); |
653 | server->tcpStatus = CifsExiting; | 662 | server->tcpStatus = CifsExiting; |
654 | spin_unlock(&GlobalMid_Lock); | 663 | spin_unlock(&GlobalMid_Lock); |
@@ -681,29 +690,29 @@ multi_t2_fnd: | |||
681 | if (smallbuf) /* no sense logging a debug message if NULL */ | 690 | if (smallbuf) /* no sense logging a debug message if NULL */ |
682 | cifs_small_buf_release(smallbuf); | 691 | cifs_small_buf_release(smallbuf); |
683 | 692 | ||
684 | read_lock(&GlobalSMBSeslock); | 693 | /* |
694 | * BB: we shouldn't have to do any of this. It shouldn't be | ||
695 | * possible to exit from the thread with active SMB sessions | ||
696 | */ | ||
697 | read_lock(&cifs_tcp_ses_lock); | ||
685 | if (list_empty(&server->pending_mid_q)) { | 698 | if (list_empty(&server->pending_mid_q)) { |
686 | /* loop through server session structures attached to this and | 699 | /* loop through server session structures attached to this and |
687 | mark them dead */ | 700 | mark them dead */ |
688 | list_for_each(tmp, &GlobalSMBSessionList) { | 701 | list_for_each(tmp, &server->smb_ses_list) { |
689 | ses = | 702 | ses = list_entry(tmp, struct cifsSesInfo, |
690 | list_entry(tmp, struct cifsSesInfo, | 703 | smb_ses_list); |
691 | cifsSessionList); | 704 | ses->status = CifsExiting; |
692 | if (ses->server == server) { | 705 | ses->server = NULL; |
693 | ses->status = CifsExiting; | ||
694 | ses->server = NULL; | ||
695 | } | ||
696 | } | 706 | } |
697 | read_unlock(&GlobalSMBSeslock); | 707 | read_unlock(&cifs_tcp_ses_lock); |
698 | } else { | 708 | } else { |
699 | /* although we can not zero the server struct pointer yet, | 709 | /* although we can not zero the server struct pointer yet, |
700 | since there are active requests which may depnd on them, | 710 | since there are active requests which may depnd on them, |
701 | mark the corresponding SMB sessions as exiting too */ | 711 | mark the corresponding SMB sessions as exiting too */ |
702 | list_for_each(tmp, &GlobalSMBSessionList) { | 712 | list_for_each(tmp, &server->smb_ses_list) { |
703 | ses = list_entry(tmp, struct cifsSesInfo, | 713 | ses = list_entry(tmp, struct cifsSesInfo, |
704 | cifsSessionList); | 714 | smb_ses_list); |
705 | if (ses->server == server) | 715 | ses->status = CifsExiting; |
706 | ses->status = CifsExiting; | ||
707 | } | 716 | } |
708 | 717 | ||
709 | spin_lock(&GlobalMid_Lock); | 718 | spin_lock(&GlobalMid_Lock); |
@@ -718,7 +727,7 @@ multi_t2_fnd: | |||
718 | } | 727 | } |
719 | } | 728 | } |
720 | spin_unlock(&GlobalMid_Lock); | 729 | spin_unlock(&GlobalMid_Lock); |
721 | read_unlock(&GlobalSMBSeslock); | 730 | read_unlock(&cifs_tcp_ses_lock); |
722 | /* 1/8th of sec is more than enough time for them to exit */ | 731 | /* 1/8th of sec is more than enough time for them to exit */ |
723 | msleep(125); | 732 | msleep(125); |
724 | } | 733 | } |
@@ -740,14 +749,13 @@ multi_t2_fnd: | |||
740 | if there are any pointing to this (e.g | 749 | if there are any pointing to this (e.g |
741 | if a crazy root user tried to kill cifsd | 750 | if a crazy root user tried to kill cifsd |
742 | kernel thread explicitly this might happen) */ | 751 | kernel thread explicitly this might happen) */ |
743 | write_lock(&GlobalSMBSeslock); | 752 | /* BB: This shouldn't be necessary, see above */ |
744 | list_for_each(tmp, &GlobalSMBSessionList) { | 753 | read_lock(&cifs_tcp_ses_lock); |
745 | ses = list_entry(tmp, struct cifsSesInfo, | 754 | list_for_each(tmp, &server->smb_ses_list) { |
746 | cifsSessionList); | 755 | ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); |
747 | if (ses->server == server) | 756 | ses->server = NULL; |
748 | ses->server = NULL; | ||
749 | } | 757 | } |
750 | write_unlock(&GlobalSMBSeslock); | 758 | read_unlock(&cifs_tcp_ses_lock); |
751 | 759 | ||
752 | kfree(server->hostname); | 760 | kfree(server->hostname); |
753 | task_to_wake = xchg(&server->tsk, NULL); | 761 | task_to_wake = xchg(&server->tsk, NULL); |
@@ -1192,6 +1200,10 @@ cifs_parse_mount_options(char *options, const char *devname, | |||
1192 | /* ignore */ | 1200 | /* ignore */ |
1193 | } else if (strnicmp(data, "rw", 2) == 0) { | 1201 | } else if (strnicmp(data, "rw", 2) == 0) { |
1194 | vol->rw = true; | 1202 | vol->rw = true; |
1203 | } else if (strnicmp(data, "noblocksend", 11) == 0) { | ||
1204 | vol->noblocksnd = 1; | ||
1205 | } else if (strnicmp(data, "noautotune", 10) == 0) { | ||
1206 | vol->noautotune = 1; | ||
1195 | } else if ((strnicmp(data, "suid", 4) == 0) || | 1207 | } else if ((strnicmp(data, "suid", 4) == 0) || |
1196 | (strnicmp(data, "nosuid", 6) == 0) || | 1208 | (strnicmp(data, "nosuid", 6) == 0) || |
1197 | (strnicmp(data, "exec", 4) == 0) || | 1209 | (strnicmp(data, "exec", 4) == 0) || |
@@ -1343,94 +1355,158 @@ cifs_parse_mount_options(char *options, const char *devname, | |||
1343 | return 0; | 1355 | return 0; |
1344 | } | 1356 | } |
1345 | 1357 | ||
1346 | static struct cifsSesInfo * | 1358 | static struct TCP_Server_Info * |
1347 | cifs_find_tcp_session(struct in_addr *target_ip_addr, | 1359 | cifs_find_tcp_session(struct sockaddr *addr) |
1348 | struct in6_addr *target_ip6_addr, | ||
1349 | char *userName, struct TCP_Server_Info **psrvTcp) | ||
1350 | { | 1360 | { |
1351 | struct list_head *tmp; | 1361 | struct list_head *tmp; |
1352 | struct cifsSesInfo *ses; | 1362 | struct TCP_Server_Info *server; |
1353 | 1363 | struct sockaddr_in *addr4 = (struct sockaddr_in *) addr; | |
1354 | *psrvTcp = NULL; | 1364 | struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addr; |
1365 | |||
1366 | write_lock(&cifs_tcp_ses_lock); | ||
1367 | list_for_each(tmp, &cifs_tcp_ses_list) { | ||
1368 | server = list_entry(tmp, struct TCP_Server_Info, | ||
1369 | tcp_ses_list); | ||
1370 | /* | ||
1371 | * the demux thread can exit on its own while still in CifsNew | ||
1372 | * so don't accept any sockets in that state. Since the | ||
1373 | * tcpStatus never changes back to CifsNew it's safe to check | ||
1374 | * for this without a lock. | ||
1375 | */ | ||
1376 | if (server->tcpStatus == CifsNew) | ||
1377 | continue; | ||
1355 | 1378 | ||
1356 | read_lock(&GlobalSMBSeslock); | 1379 | if (addr->sa_family == AF_INET && |
1357 | list_for_each(tmp, &GlobalSMBSessionList) { | 1380 | (addr4->sin_addr.s_addr != |
1358 | ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); | 1381 | server->addr.sockAddr.sin_addr.s_addr)) |
1359 | if (!ses->server) | 1382 | continue; |
1383 | else if (addr->sa_family == AF_INET6 && | ||
1384 | memcmp(&server->addr.sockAddr6.sin6_addr, | ||
1385 | &addr6->sin6_addr, sizeof(addr6->sin6_addr))) | ||
1360 | continue; | 1386 | continue; |
1361 | 1387 | ||
1362 | if (target_ip_addr && | 1388 | ++server->srv_count; |
1363 | ses->server->addr.sockAddr.sin_addr.s_addr != target_ip_addr->s_addr) | 1389 | write_unlock(&cifs_tcp_ses_lock); |
1364 | continue; | 1390 | cFYI(1, ("Existing tcp session with server found")); |
1365 | else if (target_ip6_addr && | 1391 | return server; |
1366 | memcmp(&ses->server->addr.sockAddr6.sin6_addr, | 1392 | } |
1367 | target_ip6_addr, sizeof(*target_ip6_addr))) | 1393 | write_unlock(&cifs_tcp_ses_lock); |
1368 | continue; | 1394 | return NULL; |
1369 | /* BB lock server and tcp session; increment use count here?? */ | 1395 | } |
1370 | 1396 | ||
1371 | /* found a match on the TCP session */ | 1397 | static void |
1372 | *psrvTcp = ses->server; | 1398 | cifs_put_tcp_session(struct TCP_Server_Info *server) |
1399 | { | ||
1400 | struct task_struct *task; | ||
1373 | 1401 | ||
1374 | /* BB check if reconnection needed */ | 1402 | write_lock(&cifs_tcp_ses_lock); |
1375 | if (strncmp(ses->userName, userName, MAX_USERNAME_SIZE) == 0) { | 1403 | if (--server->srv_count > 0) { |
1376 | read_unlock(&GlobalSMBSeslock); | 1404 | write_unlock(&cifs_tcp_ses_lock); |
1377 | /* Found exact match on both TCP and | 1405 | return; |
1378 | SMB sessions */ | ||
1379 | return ses; | ||
1380 | } | ||
1381 | /* else tcp and smb sessions need reconnection */ | ||
1382 | } | 1406 | } |
1383 | read_unlock(&GlobalSMBSeslock); | ||
1384 | 1407 | ||
1385 | return NULL; | 1408 | list_del_init(&server->tcp_ses_list); |
1409 | write_unlock(&cifs_tcp_ses_lock); | ||
1410 | |||
1411 | spin_lock(&GlobalMid_Lock); | ||
1412 | server->tcpStatus = CifsExiting; | ||
1413 | spin_unlock(&GlobalMid_Lock); | ||
1414 | |||
1415 | task = xchg(&server->tsk, NULL); | ||
1416 | if (task) | ||
1417 | force_sig(SIGKILL, task); | ||
1386 | } | 1418 | } |
1387 | 1419 | ||
1388 | static struct cifsTconInfo * | 1420 | static struct cifsSesInfo * |
1389 | find_unc(__be32 new_target_ip_addr, char *uncName, char *userName) | 1421 | cifs_find_smb_ses(struct TCP_Server_Info *server, char *username) |
1390 | { | 1422 | { |
1391 | struct list_head *tmp; | 1423 | struct list_head *tmp; |
1392 | struct cifsTconInfo *tcon; | 1424 | struct cifsSesInfo *ses; |
1393 | __be32 old_ip; | ||
1394 | |||
1395 | read_lock(&GlobalSMBSeslock); | ||
1396 | 1425 | ||
1397 | list_for_each(tmp, &GlobalTreeConnectionList) { | 1426 | write_lock(&cifs_tcp_ses_lock); |
1398 | cFYI(1, ("Next tcon")); | 1427 | list_for_each(tmp, &server->smb_ses_list) { |
1399 | tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); | 1428 | ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); |
1400 | if (!tcon->ses || !tcon->ses->server) | 1429 | if (strncmp(ses->userName, username, MAX_USERNAME_SIZE)) |
1401 | continue; | 1430 | continue; |
1402 | 1431 | ||
1403 | old_ip = tcon->ses->server->addr.sockAddr.sin_addr.s_addr; | 1432 | ++ses->ses_count; |
1404 | cFYI(1, ("old ip addr: %x == new ip %x ?", | 1433 | write_unlock(&cifs_tcp_ses_lock); |
1405 | old_ip, new_target_ip_addr)); | 1434 | return ses; |
1435 | } | ||
1436 | write_unlock(&cifs_tcp_ses_lock); | ||
1437 | return NULL; | ||
1438 | } | ||
1439 | |||
1440 | static void | ||
1441 | cifs_put_smb_ses(struct cifsSesInfo *ses) | ||
1442 | { | ||
1443 | int xid; | ||
1444 | struct TCP_Server_Info *server = ses->server; | ||
1406 | 1445 | ||
1407 | if (old_ip != new_target_ip_addr) | 1446 | write_lock(&cifs_tcp_ses_lock); |
1408 | continue; | 1447 | if (--ses->ses_count > 0) { |
1448 | write_unlock(&cifs_tcp_ses_lock); | ||
1449 | return; | ||
1450 | } | ||
1409 | 1451 | ||
1410 | /* BB lock tcon, server, tcp session and increment use count? */ | 1452 | list_del_init(&ses->smb_ses_list); |
1411 | /* found a match on the TCP session */ | 1453 | write_unlock(&cifs_tcp_ses_lock); |
1412 | /* BB check if reconnection needed */ | ||
1413 | cFYI(1, ("IP match, old UNC: %s new: %s", | ||
1414 | tcon->treeName, uncName)); | ||
1415 | 1454 | ||
1416 | if (strncmp(tcon->treeName, uncName, MAX_TREE_SIZE)) | 1455 | if (ses->status == CifsGood) { |
1417 | continue; | 1456 | xid = GetXid(); |
1457 | CIFSSMBLogoff(xid, ses); | ||
1458 | _FreeXid(xid); | ||
1459 | } | ||
1460 | sesInfoFree(ses); | ||
1461 | cifs_put_tcp_session(server); | ||
1462 | } | ||
1418 | 1463 | ||
1419 | cFYI(1, ("and old usr: %s new: %s", | 1464 | static struct cifsTconInfo * |
1420 | tcon->treeName, uncName)); | 1465 | cifs_find_tcon(struct cifsSesInfo *ses, const char *unc) |
1466 | { | ||
1467 | struct list_head *tmp; | ||
1468 | struct cifsTconInfo *tcon; | ||
1421 | 1469 | ||
1422 | if (strncmp(tcon->ses->userName, userName, MAX_USERNAME_SIZE)) | 1470 | write_lock(&cifs_tcp_ses_lock); |
1471 | list_for_each(tmp, &ses->tcon_list) { | ||
1472 | tcon = list_entry(tmp, struct cifsTconInfo, tcon_list); | ||
1473 | if (tcon->tidStatus == CifsExiting) | ||
1474 | continue; | ||
1475 | if (strncmp(tcon->treeName, unc, MAX_TREE_SIZE)) | ||
1423 | continue; | 1476 | continue; |
1424 | 1477 | ||
1425 | /* matched smb session (user name) */ | 1478 | ++tcon->tc_count; |
1426 | read_unlock(&GlobalSMBSeslock); | 1479 | write_unlock(&cifs_tcp_ses_lock); |
1427 | return tcon; | 1480 | return tcon; |
1428 | } | 1481 | } |
1429 | 1482 | write_unlock(&cifs_tcp_ses_lock); | |
1430 | read_unlock(&GlobalSMBSeslock); | ||
1431 | return NULL; | 1483 | return NULL; |
1432 | } | 1484 | } |
1433 | 1485 | ||
1486 | static void | ||
1487 | cifs_put_tcon(struct cifsTconInfo *tcon) | ||
1488 | { | ||
1489 | int xid; | ||
1490 | struct cifsSesInfo *ses = tcon->ses; | ||
1491 | |||
1492 | write_lock(&cifs_tcp_ses_lock); | ||
1493 | if (--tcon->tc_count > 0) { | ||
1494 | write_unlock(&cifs_tcp_ses_lock); | ||
1495 | return; | ||
1496 | } | ||
1497 | |||
1498 | list_del_init(&tcon->tcon_list); | ||
1499 | write_unlock(&cifs_tcp_ses_lock); | ||
1500 | |||
1501 | xid = GetXid(); | ||
1502 | CIFSSMBTDis(xid, tcon); | ||
1503 | _FreeXid(xid); | ||
1504 | |||
1505 | DeleteTconOplockQEntries(tcon); | ||
1506 | tconInfoFree(tcon); | ||
1507 | cifs_put_smb_ses(ses); | ||
1508 | } | ||
1509 | |||
1434 | int | 1510 | int |
1435 | get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, | 1511 | get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, |
1436 | const struct nls_table *nls_codepage, unsigned int *pnum_referrals, | 1512 | const struct nls_table *nls_codepage, unsigned int *pnum_referrals, |
@@ -1518,7 +1594,8 @@ static void rfc1002mangle(char *target, char *source, unsigned int length) | |||
1518 | 1594 | ||
1519 | static int | 1595 | static int |
1520 | ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, | 1596 | ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, |
1521 | char *netbios_name, char *target_name) | 1597 | char *netbios_name, char *target_name, |
1598 | bool noblocksnd, bool noautotune) | ||
1522 | { | 1599 | { |
1523 | int rc = 0; | 1600 | int rc = 0; |
1524 | int connected = 0; | 1601 | int connected = 0; |
@@ -1590,11 +1667,16 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, | |||
1590 | (*csocket)->sk->sk_sndbuf, | 1667 | (*csocket)->sk->sk_sndbuf, |
1591 | (*csocket)->sk->sk_rcvbuf, (*csocket)->sk->sk_rcvtimeo)); | 1668 | (*csocket)->sk->sk_rcvbuf, (*csocket)->sk->sk_rcvtimeo)); |
1592 | (*csocket)->sk->sk_rcvtimeo = 7 * HZ; | 1669 | (*csocket)->sk->sk_rcvtimeo = 7 * HZ; |
1670 | if (!noblocksnd) | ||
1671 | (*csocket)->sk->sk_sndtimeo = 3 * HZ; | ||
1672 | |||
1593 | /* make the bufsizes depend on wsize/rsize and max requests */ | 1673 | /* make the bufsizes depend on wsize/rsize and max requests */ |
1594 | if ((*csocket)->sk->sk_sndbuf < (200 * 1024)) | 1674 | if (noautotune) { |
1595 | (*csocket)->sk->sk_sndbuf = 200 * 1024; | 1675 | if ((*csocket)->sk->sk_sndbuf < (200 * 1024)) |
1596 | if ((*csocket)->sk->sk_rcvbuf < (140 * 1024)) | 1676 | (*csocket)->sk->sk_sndbuf = 200 * 1024; |
1597 | (*csocket)->sk->sk_rcvbuf = 140 * 1024; | 1677 | if ((*csocket)->sk->sk_rcvbuf < (140 * 1024)) |
1678 | (*csocket)->sk->sk_rcvbuf = 140 * 1024; | ||
1679 | } | ||
1598 | 1680 | ||
1599 | /* send RFC1001 sessinit */ | 1681 | /* send RFC1001 sessinit */ |
1600 | if (psin_server->sin_port == htons(RFC1001_PORT)) { | 1682 | if (psin_server->sin_port == htons(RFC1001_PORT)) { |
@@ -1631,7 +1713,7 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, | |||
1631 | /* sizeof RFC1002_SESSION_REQUEST with no scope */ | 1713 | /* sizeof RFC1002_SESSION_REQUEST with no scope */ |
1632 | smb_buf->smb_buf_length = 0x81000044; | 1714 | smb_buf->smb_buf_length = 0x81000044; |
1633 | rc = smb_send(*csocket, smb_buf, 0x44, | 1715 | rc = smb_send(*csocket, smb_buf, 0x44, |
1634 | (struct sockaddr *)psin_server); | 1716 | (struct sockaddr *)psin_server, noblocksnd); |
1635 | kfree(ses_init_buf); | 1717 | kfree(ses_init_buf); |
1636 | msleep(1); /* RFC1001 layer in at least one server | 1718 | msleep(1); /* RFC1001 layer in at least one server |
1637 | requires very short break before negprot | 1719 | requires very short break before negprot |
@@ -1651,7 +1733,8 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, | |||
1651 | } | 1733 | } |
1652 | 1734 | ||
1653 | static int | 1735 | static int |
1654 | ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket) | 1736 | ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket, |
1737 | bool noblocksnd) | ||
1655 | { | 1738 | { |
1656 | int rc = 0; | 1739 | int rc = 0; |
1657 | int connected = 0; | 1740 | int connected = 0; |
@@ -1720,6 +1803,9 @@ ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket) | |||
1720 | the default. sock_setsockopt not used because it expects | 1803 | the default. sock_setsockopt not used because it expects |
1721 | user space buffer */ | 1804 | user space buffer */ |
1722 | (*csocket)->sk->sk_rcvtimeo = 7 * HZ; | 1805 | (*csocket)->sk->sk_rcvtimeo = 7 * HZ; |
1806 | if (!noblocksnd) | ||
1807 | (*csocket)->sk->sk_sndtimeo = 3 * HZ; | ||
1808 | |||
1723 | 1809 | ||
1724 | return rc; | 1810 | return rc; |
1725 | } | 1811 | } |
@@ -1857,14 +1943,90 @@ convert_delimiter(char *path, char delim) | |||
1857 | } | 1943 | } |
1858 | } | 1944 | } |
1859 | 1945 | ||
1860 | static void | 1946 | static void setup_cifs_sb(struct smb_vol *pvolume_info, |
1861 | kill_cifsd(struct TCP_Server_Info *server) | 1947 | struct cifs_sb_info *cifs_sb) |
1862 | { | 1948 | { |
1863 | struct task_struct *task; | 1949 | if (pvolume_info->rsize > CIFSMaxBufSize) { |
1864 | 1950 | cERROR(1, ("rsize %d too large, using MaxBufSize", | |
1865 | task = xchg(&server->tsk, NULL); | 1951 | pvolume_info->rsize)); |
1866 | if (task) | 1952 | cifs_sb->rsize = CIFSMaxBufSize; |
1867 | force_sig(SIGKILL, task); | 1953 | } else if ((pvolume_info->rsize) && |
1954 | (pvolume_info->rsize <= CIFSMaxBufSize)) | ||
1955 | cifs_sb->rsize = pvolume_info->rsize; | ||
1956 | else /* default */ | ||
1957 | cifs_sb->rsize = CIFSMaxBufSize; | ||
1958 | |||
1959 | if (pvolume_info->wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) { | ||
1960 | cERROR(1, ("wsize %d too large, using 4096 instead", | ||
1961 | pvolume_info->wsize)); | ||
1962 | cifs_sb->wsize = 4096; | ||
1963 | } else if (pvolume_info->wsize) | ||
1964 | cifs_sb->wsize = pvolume_info->wsize; | ||
1965 | else | ||
1966 | cifs_sb->wsize = min_t(const int, | ||
1967 | PAGEVEC_SIZE * PAGE_CACHE_SIZE, | ||
1968 | 127*1024); | ||
1969 | /* old default of CIFSMaxBufSize was too small now | ||
1970 | that SMB Write2 can send multiple pages in kvec. | ||
1971 | RFC1001 does not describe what happens when frame | ||
1972 | bigger than 128K is sent so use that as max in | ||
1973 | conjunction with 52K kvec constraint on arch with 4K | ||
1974 | page size */ | ||
1975 | |||
1976 | if (cifs_sb->rsize < 2048) { | ||
1977 | cifs_sb->rsize = 2048; | ||
1978 | /* Windows ME may prefer this */ | ||
1979 | cFYI(1, ("readsize set to minimum: 2048")); | ||
1980 | } | ||
1981 | /* calculate prepath */ | ||
1982 | cifs_sb->prepath = pvolume_info->prepath; | ||
1983 | if (cifs_sb->prepath) { | ||
1984 | cifs_sb->prepathlen = strlen(cifs_sb->prepath); | ||
1985 | /* we can not convert the / to \ in the path | ||
1986 | separators in the prefixpath yet because we do not | ||
1987 | know (until reset_cifs_unix_caps is called later) | ||
1988 | whether POSIX PATH CAP is available. We normalize | ||
1989 | the / to \ after reset_cifs_unix_caps is called */ | ||
1990 | pvolume_info->prepath = NULL; | ||
1991 | } else | ||
1992 | cifs_sb->prepathlen = 0; | ||
1993 | cifs_sb->mnt_uid = pvolume_info->linux_uid; | ||
1994 | cifs_sb->mnt_gid = pvolume_info->linux_gid; | ||
1995 | cifs_sb->mnt_file_mode = pvolume_info->file_mode; | ||
1996 | cifs_sb->mnt_dir_mode = pvolume_info->dir_mode; | ||
1997 | cFYI(1, ("file mode: 0x%x dir mode: 0x%x", | ||
1998 | cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode)); | ||
1999 | |||
2000 | if (pvolume_info->noperm) | ||
2001 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM; | ||
2002 | if (pvolume_info->setuids) | ||
2003 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID; | ||
2004 | if (pvolume_info->server_ino) | ||
2005 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM; | ||
2006 | if (pvolume_info->remap) | ||
2007 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR; | ||
2008 | if (pvolume_info->no_xattr) | ||
2009 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR; | ||
2010 | if (pvolume_info->sfu_emul) | ||
2011 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL; | ||
2012 | if (pvolume_info->nobrl) | ||
2013 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL; | ||
2014 | if (pvolume_info->cifs_acl) | ||
2015 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL; | ||
2016 | if (pvolume_info->override_uid) | ||
2017 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID; | ||
2018 | if (pvolume_info->override_gid) | ||
2019 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID; | ||
2020 | if (pvolume_info->dynperm) | ||
2021 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; | ||
2022 | if (pvolume_info->direct_io) { | ||
2023 | cFYI(1, ("mounting share using direct i/o")); | ||
2024 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; | ||
2025 | } | ||
2026 | |||
2027 | if ((pvolume_info->cifs_acl) && (pvolume_info->dynperm)) | ||
2028 | cERROR(1, ("mount option dynperm ignored if cifsacl " | ||
2029 | "mount option supported")); | ||
1868 | } | 2030 | } |
1869 | 2031 | ||
1870 | int | 2032 | int |
@@ -1873,13 +2035,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
1873 | { | 2035 | { |
1874 | int rc = 0; | 2036 | int rc = 0; |
1875 | int xid; | 2037 | int xid; |
1876 | int address_type = AF_INET; | ||
1877 | struct socket *csocket = NULL; | 2038 | struct socket *csocket = NULL; |
1878 | struct sockaddr_in sin_server; | 2039 | struct sockaddr addr; |
1879 | struct sockaddr_in6 sin_server6; | 2040 | struct sockaddr_in *sin_server = (struct sockaddr_in *) &addr; |
2041 | struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr; | ||
1880 | struct smb_vol volume_info; | 2042 | struct smb_vol volume_info; |
1881 | struct cifsSesInfo *pSesInfo = NULL; | 2043 | struct cifsSesInfo *pSesInfo = NULL; |
1882 | struct cifsSesInfo *existingCifsSes = NULL; | ||
1883 | struct cifsTconInfo *tcon = NULL; | 2044 | struct cifsTconInfo *tcon = NULL; |
1884 | struct TCP_Server_Info *srvTcp = NULL; | 2045 | struct TCP_Server_Info *srvTcp = NULL; |
1885 | 2046 | ||
@@ -1887,6 +2048,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
1887 | 2048 | ||
1888 | /* cFYI(1, ("Entering cifs_mount. Xid: %d with: %s", xid, mount_data)); */ | 2049 | /* cFYI(1, ("Entering cifs_mount. Xid: %d with: %s", xid, mount_data)); */ |
1889 | 2050 | ||
2051 | memset(&addr, 0, sizeof(struct sockaddr)); | ||
1890 | memset(&volume_info, 0, sizeof(struct smb_vol)); | 2052 | memset(&volume_info, 0, sizeof(struct smb_vol)); |
1891 | if (cifs_parse_mount_options(mount_data, devname, &volume_info)) { | 2053 | if (cifs_parse_mount_options(mount_data, devname, &volume_info)) { |
1892 | rc = -EINVAL; | 2054 | rc = -EINVAL; |
@@ -1909,16 +2071,16 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
1909 | 2071 | ||
1910 | if (volume_info.UNCip && volume_info.UNC) { | 2072 | if (volume_info.UNCip && volume_info.UNC) { |
1911 | rc = cifs_inet_pton(AF_INET, volume_info.UNCip, | 2073 | rc = cifs_inet_pton(AF_INET, volume_info.UNCip, |
1912 | &sin_server.sin_addr.s_addr); | 2074 | &sin_server->sin_addr.s_addr); |
1913 | 2075 | ||
1914 | if (rc <= 0) { | 2076 | if (rc <= 0) { |
1915 | /* not ipv4 address, try ipv6 */ | 2077 | /* not ipv4 address, try ipv6 */ |
1916 | rc = cifs_inet_pton(AF_INET6, volume_info.UNCip, | 2078 | rc = cifs_inet_pton(AF_INET6, volume_info.UNCip, |
1917 | &sin_server6.sin6_addr.in6_u); | 2079 | &sin_server6->sin6_addr.in6_u); |
1918 | if (rc > 0) | 2080 | if (rc > 0) |
1919 | address_type = AF_INET6; | 2081 | addr.sa_family = AF_INET6; |
1920 | } else { | 2082 | } else { |
1921 | address_type = AF_INET; | 2083 | addr.sa_family = AF_INET; |
1922 | } | 2084 | } |
1923 | 2085 | ||
1924 | if (rc <= 0) { | 2086 | if (rc <= 0) { |
@@ -1958,38 +2120,25 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
1958 | } | 2120 | } |
1959 | } | 2121 | } |
1960 | 2122 | ||
1961 | if (address_type == AF_INET) | 2123 | srvTcp = cifs_find_tcp_session(&addr); |
1962 | existingCifsSes = cifs_find_tcp_session(&sin_server.sin_addr, | 2124 | if (!srvTcp) { /* create socket */ |
1963 | NULL /* no ipv6 addr */, | 2125 | if (addr.sa_family == AF_INET6) { |
1964 | volume_info.username, &srvTcp); | ||
1965 | else if (address_type == AF_INET6) { | ||
1966 | cFYI(1, ("looking for ipv6 address")); | ||
1967 | existingCifsSes = cifs_find_tcp_session(NULL /* no ipv4 addr */, | ||
1968 | &sin_server6.sin6_addr, | ||
1969 | volume_info.username, &srvTcp); | ||
1970 | } else { | ||
1971 | rc = -EINVAL; | ||
1972 | goto out; | ||
1973 | } | ||
1974 | |||
1975 | if (srvTcp) { | ||
1976 | cFYI(1, ("Existing tcp session with server found")); | ||
1977 | } else { /* create socket */ | ||
1978 | if (volume_info.port) | ||
1979 | sin_server.sin_port = htons(volume_info.port); | ||
1980 | else | ||
1981 | sin_server.sin_port = 0; | ||
1982 | if (address_type == AF_INET6) { | ||
1983 | cFYI(1, ("attempting ipv6 connect")); | 2126 | cFYI(1, ("attempting ipv6 connect")); |
1984 | /* BB should we allow ipv6 on port 139? */ | 2127 | /* BB should we allow ipv6 on port 139? */ |
1985 | /* other OS never observed in Wild doing 139 with v6 */ | 2128 | /* other OS never observed in Wild doing 139 with v6 */ |
1986 | rc = ipv6_connect(&sin_server6, &csocket); | 2129 | sin_server6->sin6_port = htons(volume_info.port); |
1987 | } else | 2130 | rc = ipv6_connect(sin_server6, &csocket, |
1988 | rc = ipv4_connect(&sin_server, &csocket, | 2131 | volume_info.noblocksnd); |
2132 | } else { | ||
2133 | sin_server->sin_port = htons(volume_info.port); | ||
2134 | rc = ipv4_connect(sin_server, &csocket, | ||
1989 | volume_info.source_rfc1001_name, | 2135 | volume_info.source_rfc1001_name, |
1990 | volume_info.target_rfc1001_name); | 2136 | volume_info.target_rfc1001_name, |
2137 | volume_info.noblocksnd, | ||
2138 | volume_info.noautotune); | ||
2139 | } | ||
1991 | if (rc < 0) { | 2140 | if (rc < 0) { |
1992 | cERROR(1, ("Error connecting to IPv4 socket. " | 2141 | cERROR(1, ("Error connecting to socket. " |
1993 | "Aborting operation")); | 2142 | "Aborting operation")); |
1994 | if (csocket != NULL) | 2143 | if (csocket != NULL) |
1995 | sock_release(csocket); | 2144 | sock_release(csocket); |
@@ -2002,12 +2151,17 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2002 | sock_release(csocket); | 2151 | sock_release(csocket); |
2003 | goto out; | 2152 | goto out; |
2004 | } else { | 2153 | } else { |
2005 | memcpy(&srvTcp->addr.sockAddr, &sin_server, | 2154 | srvTcp->noblocksnd = volume_info.noblocksnd; |
2006 | sizeof(struct sockaddr_in)); | 2155 | srvTcp->noautotune = volume_info.noautotune; |
2156 | if (addr.sa_family == AF_INET6) | ||
2157 | memcpy(&srvTcp->addr.sockAddr6, sin_server6, | ||
2158 | sizeof(struct sockaddr_in6)); | ||
2159 | else | ||
2160 | memcpy(&srvTcp->addr.sockAddr, sin_server, | ||
2161 | sizeof(struct sockaddr_in)); | ||
2007 | atomic_set(&srvTcp->inFlight, 0); | 2162 | atomic_set(&srvTcp->inFlight, 0); |
2008 | /* BB Add code for ipv6 case too */ | 2163 | /* BB Add code for ipv6 case too */ |
2009 | srvTcp->ssocket = csocket; | 2164 | srvTcp->ssocket = csocket; |
2010 | srvTcp->protocolType = IPV4; | ||
2011 | srvTcp->hostname = extract_hostname(volume_info.UNC); | 2165 | srvTcp->hostname = extract_hostname(volume_info.UNC); |
2012 | if (IS_ERR(srvTcp->hostname)) { | 2166 | if (IS_ERR(srvTcp->hostname)) { |
2013 | rc = PTR_ERR(srvTcp->hostname); | 2167 | rc = PTR_ERR(srvTcp->hostname); |
@@ -2037,15 +2191,28 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2037 | memcpy(srvTcp->server_RFC1001_name, | 2191 | memcpy(srvTcp->server_RFC1001_name, |
2038 | volume_info.target_rfc1001_name, 16); | 2192 | volume_info.target_rfc1001_name, 16); |
2039 | srvTcp->sequence_number = 0; | 2193 | srvTcp->sequence_number = 0; |
2194 | INIT_LIST_HEAD(&srvTcp->tcp_ses_list); | ||
2195 | INIT_LIST_HEAD(&srvTcp->smb_ses_list); | ||
2196 | ++srvTcp->srv_count; | ||
2197 | write_lock(&cifs_tcp_ses_lock); | ||
2198 | list_add(&srvTcp->tcp_ses_list, | ||
2199 | &cifs_tcp_ses_list); | ||
2200 | write_unlock(&cifs_tcp_ses_lock); | ||
2040 | } | 2201 | } |
2041 | } | 2202 | } |
2042 | 2203 | ||
2043 | if (existingCifsSes) { | 2204 | pSesInfo = cifs_find_smb_ses(srvTcp, volume_info.username); |
2044 | pSesInfo = existingCifsSes; | 2205 | if (pSesInfo) { |
2045 | cFYI(1, ("Existing smb sess found (status=%d)", | 2206 | cFYI(1, ("Existing smb sess found (status=%d)", |
2046 | pSesInfo->status)); | 2207 | pSesInfo->status)); |
2208 | /* | ||
2209 | * The existing SMB session already has a reference to srvTcp, | ||
2210 | * so we can put back the extra one we got before | ||
2211 | */ | ||
2212 | cifs_put_tcp_session(srvTcp); | ||
2213 | |||
2047 | down(&pSesInfo->sesSem); | 2214 | down(&pSesInfo->sesSem); |
2048 | if (pSesInfo->status == CifsNeedReconnect) { | 2215 | if (pSesInfo->need_reconnect) { |
2049 | cFYI(1, ("Session needs reconnect")); | 2216 | cFYI(1, ("Session needs reconnect")); |
2050 | rc = cifs_setup_session(xid, pSesInfo, | 2217 | rc = cifs_setup_session(xid, pSesInfo, |
2051 | cifs_sb->local_nls); | 2218 | cifs_sb->local_nls); |
@@ -2054,187 +2221,101 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2054 | } else if (!rc) { | 2221 | } else if (!rc) { |
2055 | cFYI(1, ("Existing smb sess not found")); | 2222 | cFYI(1, ("Existing smb sess not found")); |
2056 | pSesInfo = sesInfoAlloc(); | 2223 | pSesInfo = sesInfoAlloc(); |
2057 | if (pSesInfo == NULL) | 2224 | if (pSesInfo == NULL) { |
2058 | rc = -ENOMEM; | 2225 | rc = -ENOMEM; |
2059 | else { | 2226 | goto mount_fail_check; |
2060 | pSesInfo->server = srvTcp; | 2227 | } |
2061 | sprintf(pSesInfo->serverName, "%u.%u.%u.%u", | 2228 | |
2062 | NIPQUAD(sin_server.sin_addr.s_addr)); | 2229 | /* new SMB session uses our srvTcp ref */ |
2063 | } | 2230 | pSesInfo->server = srvTcp; |
2231 | sprintf(pSesInfo->serverName, "%u.%u.%u.%u", | ||
2232 | NIPQUAD(sin_server->sin_addr.s_addr)); | ||
2233 | |||
2234 | write_lock(&cifs_tcp_ses_lock); | ||
2235 | list_add(&pSesInfo->smb_ses_list, &srvTcp->smb_ses_list); | ||
2236 | write_unlock(&cifs_tcp_ses_lock); | ||
2237 | |||
2238 | /* volume_info.password freed at unmount */ | ||
2239 | if (volume_info.password) { | ||
2240 | pSesInfo->password = volume_info.password; | ||
2241 | /* set to NULL to prevent freeing on exit */ | ||
2242 | volume_info.password = NULL; | ||
2243 | } | ||
2244 | if (volume_info.username) | ||
2245 | strncpy(pSesInfo->userName, volume_info.username, | ||
2246 | MAX_USERNAME_SIZE); | ||
2247 | if (volume_info.domainname) { | ||
2248 | int len = strlen(volume_info.domainname); | ||
2249 | pSesInfo->domainName = kmalloc(len + 1, GFP_KERNEL); | ||
2250 | if (pSesInfo->domainName) | ||
2251 | strcpy(pSesInfo->domainName, | ||
2252 | volume_info.domainname); | ||
2253 | } | ||
2254 | pSesInfo->linux_uid = volume_info.linux_uid; | ||
2255 | pSesInfo->overrideSecFlg = volume_info.secFlg; | ||
2256 | down(&pSesInfo->sesSem); | ||
2064 | 2257 | ||
2065 | if (!rc) { | 2258 | /* BB FIXME need to pass vol->secFlgs BB */ |
2066 | /* volume_info.password freed at unmount */ | 2259 | rc = cifs_setup_session(xid, pSesInfo, |
2067 | if (volume_info.password) { | 2260 | cifs_sb->local_nls); |
2068 | pSesInfo->password = volume_info.password; | 2261 | up(&pSesInfo->sesSem); |
2069 | /* set to NULL to prevent freeing on exit */ | ||
2070 | volume_info.password = NULL; | ||
2071 | } | ||
2072 | if (volume_info.username) | ||
2073 | strncpy(pSesInfo->userName, | ||
2074 | volume_info.username, | ||
2075 | MAX_USERNAME_SIZE); | ||
2076 | if (volume_info.domainname) { | ||
2077 | int len = strlen(volume_info.domainname); | ||
2078 | pSesInfo->domainName = | ||
2079 | kmalloc(len + 1, GFP_KERNEL); | ||
2080 | if (pSesInfo->domainName) | ||
2081 | strcpy(pSesInfo->domainName, | ||
2082 | volume_info.domainname); | ||
2083 | } | ||
2084 | pSesInfo->linux_uid = volume_info.linux_uid; | ||
2085 | pSesInfo->overrideSecFlg = volume_info.secFlg; | ||
2086 | down(&pSesInfo->sesSem); | ||
2087 | /* BB FIXME need to pass vol->secFlgs BB */ | ||
2088 | rc = cifs_setup_session(xid, pSesInfo, | ||
2089 | cifs_sb->local_nls); | ||
2090 | up(&pSesInfo->sesSem); | ||
2091 | if (!rc) | ||
2092 | atomic_inc(&srvTcp->socketUseCount); | ||
2093 | } | ||
2094 | } | 2262 | } |
2095 | 2263 | ||
2096 | /* search for existing tcon to this server share */ | 2264 | /* search for existing tcon to this server share */ |
2097 | if (!rc) { | 2265 | if (!rc) { |
2098 | if (volume_info.rsize > CIFSMaxBufSize) { | 2266 | setup_cifs_sb(&volume_info, cifs_sb); |
2099 | cERROR(1, ("rsize %d too large, using MaxBufSize", | ||
2100 | volume_info.rsize)); | ||
2101 | cifs_sb->rsize = CIFSMaxBufSize; | ||
2102 | } else if ((volume_info.rsize) && | ||
2103 | (volume_info.rsize <= CIFSMaxBufSize)) | ||
2104 | cifs_sb->rsize = volume_info.rsize; | ||
2105 | else /* default */ | ||
2106 | cifs_sb->rsize = CIFSMaxBufSize; | ||
2107 | |||
2108 | if (volume_info.wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) { | ||
2109 | cERROR(1, ("wsize %d too large, using 4096 instead", | ||
2110 | volume_info.wsize)); | ||
2111 | cifs_sb->wsize = 4096; | ||
2112 | } else if (volume_info.wsize) | ||
2113 | cifs_sb->wsize = volume_info.wsize; | ||
2114 | else | ||
2115 | cifs_sb->wsize = | ||
2116 | min_t(const int, PAGEVEC_SIZE * PAGE_CACHE_SIZE, | ||
2117 | 127*1024); | ||
2118 | /* old default of CIFSMaxBufSize was too small now | ||
2119 | that SMB Write2 can send multiple pages in kvec. | ||
2120 | RFC1001 does not describe what happens when frame | ||
2121 | bigger than 128K is sent so use that as max in | ||
2122 | conjunction with 52K kvec constraint on arch with 4K | ||
2123 | page size */ | ||
2124 | |||
2125 | if (cifs_sb->rsize < 2048) { | ||
2126 | cifs_sb->rsize = 2048; | ||
2127 | /* Windows ME may prefer this */ | ||
2128 | cFYI(1, ("readsize set to minimum: 2048")); | ||
2129 | } | ||
2130 | /* calculate prepath */ | ||
2131 | cifs_sb->prepath = volume_info.prepath; | ||
2132 | if (cifs_sb->prepath) { | ||
2133 | cifs_sb->prepathlen = strlen(cifs_sb->prepath); | ||
2134 | /* we can not convert the / to \ in the path | ||
2135 | separators in the prefixpath yet because we do not | ||
2136 | know (until reset_cifs_unix_caps is called later) | ||
2137 | whether POSIX PATH CAP is available. We normalize | ||
2138 | the / to \ after reset_cifs_unix_caps is called */ | ||
2139 | volume_info.prepath = NULL; | ||
2140 | } else | ||
2141 | cifs_sb->prepathlen = 0; | ||
2142 | cifs_sb->mnt_uid = volume_info.linux_uid; | ||
2143 | cifs_sb->mnt_gid = volume_info.linux_gid; | ||
2144 | cifs_sb->mnt_file_mode = volume_info.file_mode; | ||
2145 | cifs_sb->mnt_dir_mode = volume_info.dir_mode; | ||
2146 | cFYI(1, ("file mode: 0x%x dir mode: 0x%x", | ||
2147 | cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode)); | ||
2148 | |||
2149 | if (volume_info.noperm) | ||
2150 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM; | ||
2151 | if (volume_info.setuids) | ||
2152 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID; | ||
2153 | if (volume_info.server_ino) | ||
2154 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM; | ||
2155 | if (volume_info.remap) | ||
2156 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR; | ||
2157 | if (volume_info.no_xattr) | ||
2158 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR; | ||
2159 | if (volume_info.sfu_emul) | ||
2160 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL; | ||
2161 | if (volume_info.nobrl) | ||
2162 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL; | ||
2163 | if (volume_info.cifs_acl) | ||
2164 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL; | ||
2165 | if (volume_info.override_uid) | ||
2166 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID; | ||
2167 | if (volume_info.override_gid) | ||
2168 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID; | ||
2169 | if (volume_info.dynperm) | ||
2170 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; | ||
2171 | if (volume_info.direct_io) { | ||
2172 | cFYI(1, ("mounting share using direct i/o")); | ||
2173 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; | ||
2174 | } | ||
2175 | |||
2176 | if ((volume_info.cifs_acl) && (volume_info.dynperm)) | ||
2177 | cERROR(1, ("mount option dynperm ignored if cifsacl " | ||
2178 | "mount option supported")); | ||
2179 | 2267 | ||
2180 | tcon = | 2268 | tcon = cifs_find_tcon(pSesInfo, volume_info.UNC); |
2181 | find_unc(sin_server.sin_addr.s_addr, volume_info.UNC, | ||
2182 | volume_info.username); | ||
2183 | if (tcon) { | 2269 | if (tcon) { |
2184 | cFYI(1, ("Found match on UNC path")); | 2270 | cFYI(1, ("Found match on UNC path")); |
2185 | /* we can have only one retry value for a connection | 2271 | /* existing tcon already has a reference */ |
2186 | to a share so for resources mounted more than once | 2272 | cifs_put_smb_ses(pSesInfo); |
2187 | to the same server share the last value passed in | ||
2188 | for the retry flag is used */ | ||
2189 | tcon->retry = volume_info.retry; | ||
2190 | tcon->nocase = volume_info.nocase; | ||
2191 | tcon->local_lease = volume_info.local_lease; | ||
2192 | if (tcon->seal != volume_info.seal) | 2273 | if (tcon->seal != volume_info.seal) |
2193 | cERROR(1, ("transport encryption setting " | 2274 | cERROR(1, ("transport encryption setting " |
2194 | "conflicts with existing tid")); | 2275 | "conflicts with existing tid")); |
2195 | } else { | 2276 | } else { |
2196 | tcon = tconInfoAlloc(); | 2277 | tcon = tconInfoAlloc(); |
2197 | if (tcon == NULL) | 2278 | if (tcon == NULL) { |
2198 | rc = -ENOMEM; | 2279 | rc = -ENOMEM; |
2199 | else { | 2280 | goto mount_fail_check; |
2200 | /* check for null share name ie connecting to | 2281 | } |
2201 | * dfs root */ | 2282 | tcon->ses = pSesInfo; |
2202 | 2283 | ||
2203 | /* BB check if this works for exactly length | 2284 | /* check for null share name ie connect to dfs root */ |
2204 | * three strings */ | 2285 | if ((strchr(volume_info.UNC + 3, '\\') == NULL) |
2205 | if ((strchr(volume_info.UNC + 3, '\\') == NULL) | 2286 | && (strchr(volume_info.UNC + 3, '/') == NULL)) { |
2206 | && (strchr(volume_info.UNC + 3, '/') == | 2287 | /* rc = connect_to_dfs_path(...) */ |
2207 | NULL)) { | 2288 | cFYI(1, ("DFS root not supported")); |
2208 | /* rc = connect_to_dfs_path(xid, pSesInfo, | 2289 | rc = -ENODEV; |
2209 | "", cifs_sb->local_nls, | 2290 | goto mount_fail_check; |
2210 | cifs_sb->mnt_cifs_flags & | 2291 | } else { |
2211 | CIFS_MOUNT_MAP_SPECIAL_CHR);*/ | 2292 | /* BB Do we need to wrap sesSem around |
2212 | cFYI(1, ("DFS root not supported")); | 2293 | * this TCon call and Unix SetFS as |
2213 | rc = -ENODEV; | 2294 | * we do on SessSetup and reconnect? */ |
2214 | goto out; | 2295 | rc = CIFSTCon(xid, pSesInfo, volume_info.UNC, |
2215 | } else { | 2296 | tcon, cifs_sb->local_nls); |
2216 | /* BB Do we need to wrap sesSem around | 2297 | cFYI(1, ("CIFS Tcon rc = %d", rc)); |
2217 | * this TCon call and Unix SetFS as | 2298 | if (volume_info.nodfs) { |
2218 | * we do on SessSetup and reconnect? */ | 2299 | tcon->Flags &= ~SMB_SHARE_IS_IN_DFS; |
2219 | rc = CIFSTCon(xid, pSesInfo, | 2300 | cFYI(1, ("DFS disabled (%d)", |
2220 | volume_info.UNC, | 2301 | tcon->Flags)); |
2221 | tcon, cifs_sb->local_nls); | ||
2222 | cFYI(1, ("CIFS Tcon rc = %d", rc)); | ||
2223 | if (volume_info.nodfs) { | ||
2224 | tcon->Flags &= | ||
2225 | ~SMB_SHARE_IS_IN_DFS; | ||
2226 | cFYI(1, ("DFS disabled (%d)", | ||
2227 | tcon->Flags)); | ||
2228 | } | ||
2229 | } | ||
2230 | if (!rc) { | ||
2231 | atomic_inc(&pSesInfo->inUse); | ||
2232 | tcon->retry = volume_info.retry; | ||
2233 | tcon->nocase = volume_info.nocase; | ||
2234 | tcon->seal = volume_info.seal; | ||
2235 | } | 2302 | } |
2236 | } | 2303 | } |
2237 | } | 2304 | if (rc) |
2305 | goto mount_fail_check; | ||
2306 | tcon->seal = volume_info.seal; | ||
2307 | write_lock(&cifs_tcp_ses_lock); | ||
2308 | list_add(&tcon->tcon_list, &pSesInfo->tcon_list); | ||
2309 | write_unlock(&cifs_tcp_ses_lock); | ||
2310 | } | ||
2311 | |||
2312 | /* we can have only one retry value for a connection | ||
2313 | to a share so for resources mounted more than once | ||
2314 | to the same server share the last value passed in | ||
2315 | for the retry flag is used */ | ||
2316 | tcon->retry = volume_info.retry; | ||
2317 | tcon->nocase = volume_info.nocase; | ||
2318 | tcon->local_lease = volume_info.local_lease; | ||
2238 | } | 2319 | } |
2239 | if (pSesInfo) { | 2320 | if (pSesInfo) { |
2240 | if (pSesInfo->capabilities & CAP_LARGE_FILES) { | 2321 | if (pSesInfo->capabilities & CAP_LARGE_FILES) { |
@@ -2246,80 +2327,49 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2246 | /* BB FIXME fix time_gran to be larger for LANMAN sessions */ | 2327 | /* BB FIXME fix time_gran to be larger for LANMAN sessions */ |
2247 | sb->s_time_gran = 100; | 2328 | sb->s_time_gran = 100; |
2248 | 2329 | ||
2249 | /* on error free sesinfo and tcon struct if needed */ | 2330 | mount_fail_check: |
2331 | /* on error free sesinfo and tcon struct if needed */ | ||
2250 | if (rc) { | 2332 | if (rc) { |
2251 | /* if session setup failed, use count is zero but | 2333 | /* If find_unc succeeded then rc == 0 so we can not end */ |
2252 | we still need to free cifsd thread */ | 2334 | /* up accidently freeing someone elses tcon struct */ |
2253 | if (atomic_read(&srvTcp->socketUseCount) == 0) { | 2335 | if (tcon) |
2254 | spin_lock(&GlobalMid_Lock); | 2336 | cifs_put_tcon(tcon); |
2255 | srvTcp->tcpStatus = CifsExiting; | 2337 | else if (pSesInfo) |
2256 | spin_unlock(&GlobalMid_Lock); | 2338 | cifs_put_smb_ses(pSesInfo); |
2257 | kill_cifsd(srvTcp); | ||
2258 | } | ||
2259 | /* If find_unc succeeded then rc == 0 so we can not end */ | ||
2260 | if (tcon) /* up accidently freeing someone elses tcon struct */ | ||
2261 | tconInfoFree(tcon); | ||
2262 | if (existingCifsSes == NULL) { | ||
2263 | if (pSesInfo) { | ||
2264 | if ((pSesInfo->server) && | ||
2265 | (pSesInfo->status == CifsGood)) { | ||
2266 | int temp_rc; | ||
2267 | temp_rc = CIFSSMBLogoff(xid, pSesInfo); | ||
2268 | /* if the socketUseCount is now zero */ | ||
2269 | if ((temp_rc == -ESHUTDOWN) && | ||
2270 | (pSesInfo->server)) | ||
2271 | kill_cifsd(pSesInfo->server); | ||
2272 | } else { | ||
2273 | cFYI(1, ("No session or bad tcon")); | ||
2274 | if (pSesInfo->server) { | ||
2275 | spin_lock(&GlobalMid_Lock); | ||
2276 | srvTcp->tcpStatus = CifsExiting; | ||
2277 | spin_unlock(&GlobalMid_Lock); | ||
2278 | kill_cifsd(pSesInfo->server); | ||
2279 | } | ||
2280 | } | ||
2281 | sesInfoFree(pSesInfo); | ||
2282 | /* pSesInfo = NULL; */ | ||
2283 | } | ||
2284 | } | ||
2285 | } else { | ||
2286 | atomic_inc(&tcon->useCount); | ||
2287 | cifs_sb->tcon = tcon; | ||
2288 | tcon->ses = pSesInfo; | ||
2289 | |||
2290 | /* do not care if following two calls succeed - informational */ | ||
2291 | if (!tcon->ipc) { | ||
2292 | CIFSSMBQFSDeviceInfo(xid, tcon); | ||
2293 | CIFSSMBQFSAttributeInfo(xid, tcon); | ||
2294 | } | ||
2295 | |||
2296 | /* tell server which Unix caps we support */ | ||
2297 | if (tcon->ses->capabilities & CAP_UNIX) | ||
2298 | /* reset of caps checks mount to see if unix extensions | ||
2299 | disabled for just this mount */ | ||
2300 | reset_cifs_unix_caps(xid, tcon, sb, &volume_info); | ||
2301 | else | 2339 | else |
2302 | tcon->unix_ext = 0; /* server does not support them */ | 2340 | cifs_put_tcp_session(srvTcp); |
2341 | goto out; | ||
2342 | } | ||
2343 | cifs_sb->tcon = tcon; | ||
2303 | 2344 | ||
2304 | /* convert forward to back slashes in prepath here if needed */ | 2345 | /* do not care if following two calls succeed - informational */ |
2305 | if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0) | 2346 | if (!tcon->ipc) { |
2306 | convert_delimiter(cifs_sb->prepath, | 2347 | CIFSSMBQFSDeviceInfo(xid, tcon); |
2307 | CIFS_DIR_SEP(cifs_sb)); | 2348 | CIFSSMBQFSAttributeInfo(xid, tcon); |
2349 | } | ||
2308 | 2350 | ||
2309 | if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) { | 2351 | /* tell server which Unix caps we support */ |
2310 | cifs_sb->rsize = 1024 * 127; | 2352 | if (tcon->ses->capabilities & CAP_UNIX) |
2311 | cFYI(DBG2, | 2353 | /* reset of caps checks mount to see if unix extensions |
2312 | ("no very large read support, rsize now 127K")); | 2354 | disabled for just this mount */ |
2313 | } | 2355 | reset_cifs_unix_caps(xid, tcon, sb, &volume_info); |
2314 | if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X)) | 2356 | else |
2315 | cifs_sb->wsize = min(cifs_sb->wsize, | 2357 | tcon->unix_ext = 0; /* server does not support them */ |
2316 | (tcon->ses->server->maxBuf - | 2358 | |
2317 | MAX_CIFS_HDR_SIZE)); | 2359 | /* convert forward to back slashes in prepath here if needed */ |
2318 | if (!(tcon->ses->capabilities & CAP_LARGE_READ_X)) | 2360 | if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0) |
2319 | cifs_sb->rsize = min(cifs_sb->rsize, | 2361 | convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb)); |
2320 | (tcon->ses->server->maxBuf - | 2362 | |
2321 | MAX_CIFS_HDR_SIZE)); | 2363 | if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) { |
2364 | cifs_sb->rsize = 1024 * 127; | ||
2365 | cFYI(DBG2, ("no very large read support, rsize now 127K")); | ||
2322 | } | 2366 | } |
2367 | if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X)) | ||
2368 | cifs_sb->wsize = min(cifs_sb->wsize, | ||
2369 | (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)); | ||
2370 | if (!(tcon->ses->capabilities & CAP_LARGE_READ_X)) | ||
2371 | cifs_sb->rsize = min(cifs_sb->rsize, | ||
2372 | (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)); | ||
2323 | 2373 | ||
2324 | /* volume_info.password is freed above when existing session found | 2374 | /* volume_info.password is freed above when existing session found |
2325 | (in which case it is not needed anymore) but when new sesion is created | 2375 | (in which case it is not needed anymore) but when new sesion is created |
@@ -3489,6 +3539,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, | |||
3489 | /* above now done in SendReceive */ | 3539 | /* above now done in SendReceive */ |
3490 | if ((rc == 0) && (tcon != NULL)) { | 3540 | if ((rc == 0) && (tcon != NULL)) { |
3491 | tcon->tidStatus = CifsGood; | 3541 | tcon->tidStatus = CifsGood; |
3542 | tcon->need_reconnect = false; | ||
3492 | tcon->tid = smb_buffer_response->Tid; | 3543 | tcon->tid = smb_buffer_response->Tid; |
3493 | bcc_ptr = pByteArea(smb_buffer_response); | 3544 | bcc_ptr = pByteArea(smb_buffer_response); |
3494 | length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2); | 3545 | length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2); |
@@ -3560,48 +3611,17 @@ int | |||
3560 | cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) | 3611 | cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) |
3561 | { | 3612 | { |
3562 | int rc = 0; | 3613 | int rc = 0; |
3563 | int xid; | ||
3564 | struct cifsSesInfo *ses = NULL; | ||
3565 | char *tmp; | 3614 | char *tmp; |
3566 | 3615 | ||
3567 | xid = GetXid(); | 3616 | if (cifs_sb->tcon) |
3568 | 3617 | cifs_put_tcon(cifs_sb->tcon); | |
3569 | if (cifs_sb->tcon) { | ||
3570 | ses = cifs_sb->tcon->ses; /* save ptr to ses before delete tcon!*/ | ||
3571 | rc = CIFSSMBTDis(xid, cifs_sb->tcon); | ||
3572 | if (rc == -EBUSY) { | ||
3573 | FreeXid(xid); | ||
3574 | return 0; | ||
3575 | } | ||
3576 | DeleteTconOplockQEntries(cifs_sb->tcon); | ||
3577 | tconInfoFree(cifs_sb->tcon); | ||
3578 | if ((ses) && (ses->server)) { | ||
3579 | /* save off task so we do not refer to ses later */ | ||
3580 | cFYI(1, ("About to do SMBLogoff ")); | ||
3581 | rc = CIFSSMBLogoff(xid, ses); | ||
3582 | if (rc == -EBUSY) { | ||
3583 | FreeXid(xid); | ||
3584 | return 0; | ||
3585 | } else if (rc == -ESHUTDOWN) { | ||
3586 | cFYI(1, ("Waking up socket by sending signal")); | ||
3587 | if (ses->server) | ||
3588 | kill_cifsd(ses->server); | ||
3589 | rc = 0; | ||
3590 | } /* else - we have an smb session | ||
3591 | left on this socket do not kill cifsd */ | ||
3592 | } else | ||
3593 | cFYI(1, ("No session or bad tcon")); | ||
3594 | } | ||
3595 | 3618 | ||
3596 | cifs_sb->tcon = NULL; | 3619 | cifs_sb->tcon = NULL; |
3597 | tmp = cifs_sb->prepath; | 3620 | tmp = cifs_sb->prepath; |
3598 | cifs_sb->prepathlen = 0; | 3621 | cifs_sb->prepathlen = 0; |
3599 | cifs_sb->prepath = NULL; | 3622 | cifs_sb->prepath = NULL; |
3600 | kfree(tmp); | 3623 | kfree(tmp); |
3601 | if (ses) | ||
3602 | sesInfoFree(ses); | ||
3603 | 3624 | ||
3604 | FreeXid(xid); | ||
3605 | return rc; | 3625 | return rc; |
3606 | } | 3626 | } |
3607 | 3627 | ||
@@ -3717,6 +3737,7 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, | |||
3717 | cFYI(1, ("CIFS Session Established successfully")); | 3737 | cFYI(1, ("CIFS Session Established successfully")); |
3718 | spin_lock(&GlobalMid_Lock); | 3738 | spin_lock(&GlobalMid_Lock); |
3719 | pSesInfo->status = CifsGood; | 3739 | pSesInfo->status = CifsGood; |
3740 | pSesInfo->need_reconnect = false; | ||
3720 | spin_unlock(&GlobalMid_Lock); | 3741 | spin_unlock(&GlobalMid_Lock); |
3721 | } | 3742 | } |
3722 | 3743 | ||