4848#include <winsock2.h>
4949#include <ws2tcpip.h>
5050#include <afunix.h>
51+ #include <assert.h>
5152#include <windows.h>
5253#include <direct.h>
5354#include <errno.h>
@@ -440,6 +441,20 @@ static SOCKET win_socket_from_fd(int32_t fd) {
440441 return result ;
441442}
442443
444+ static int win_socket_blocking_from_fd (int32_t fd ) {
445+ int result = 1 ;
446+ ensure_socket_table ();
447+ EnterCriticalSection (& win_socket_table_lock );
448+ for (int i = 0 ; i < WIN_SOCKET_TABLE_SIZE ; i ++ ) {
449+ if (win_socket_table [i ].fd == fd ) {
450+ result = win_socket_table [i ].blocking ;
451+ break ;
452+ }
453+ }
454+ LeaveCriticalSection (& win_socket_table_lock );
455+ return result ;
456+ }
457+
443458static int win_socket_entry_index (int32_t fd ) {
444459 int result = -1 ;
445460 ensure_socket_table ();
@@ -454,7 +469,7 @@ static int win_socket_entry_index(int32_t fd) {
454469 return result ;
455470}
456471
457- static int win_alloc_socket_fd (SOCKET s ) {
472+ static int win_alloc_socket_fd (SOCKET s , int blocking ) {
458473 int fd = _open ("NUL" , _O_RDONLY | _O_BINARY | _O_NOINHERIT );
459474 if (fd < 0 ) {
460475 capture_errno ();
@@ -468,7 +483,7 @@ static int win_alloc_socket_fd(SOCKET s) {
468483 if (win_socket_table [i ].fd < 0 ) {
469484 win_socket_table [i ].fd = fd ;
470485 win_socket_table [i ].socket = s ;
471- win_socket_table [i ].blocking = 1 ;
486+ win_socket_table [i ].blocking = blocking ;
472487 LeaveCriticalSection (& win_socket_table_lock );
473488 return fd ;
474489 }
@@ -498,17 +513,106 @@ static SOCKET win_remove_socket_fd(int32_t fd) {
498513 return result ;
499514}
500515
501- static int win_socket_dup (int32_t fd ) {
502- SOCKET s = win_socket_from_fd (fd );
516+ GP_EXPORT int32_t set_inheritable (int32_t fd , int32_t inheritable );
517+
518+ /* The caller must hold win_socket_table_lock. */
519+ static int win_replace_socket_fd_locked (int32_t fd , SOCKET s , int blocking , SOCKET * replaced ) {
520+ assert (win_socket_table_lock .OwningThread == (HANDLE ) (uintptr_t ) GetCurrentThreadId ());
521+ int free_index = -1 ;
522+ for (int i = 0 ; i < WIN_SOCKET_TABLE_SIZE ; i ++ ) {
523+ if (win_socket_table [i ].fd == fd ) {
524+ * replaced = win_socket_table [i ].socket ;
525+ win_socket_table [i ].socket = s ;
526+ win_socket_table [i ].blocking = blocking ;
527+ return 0 ;
528+ }
529+ if (free_index < 0 && win_socket_table [i ].fd < 0 ) {
530+ free_index = i ;
531+ }
532+ }
533+ if (free_index < 0 ) {
534+ set_posix_errno (EMFILE );
535+ return -1 ;
536+ }
537+ win_socket_table [free_index ].fd = fd ;
538+ win_socket_table [free_index ].socket = s ;
539+ win_socket_table [free_index ].blocking = blocking ;
540+ * replaced = INVALID_SOCKET ;
541+ return 0 ;
542+ }
543+
544+ static SOCKET win_duplicate_socket (SOCKET s ) {
503545 WSAPROTOCOL_INFOA protocol_info ;
504546 if (WSADuplicateSocketA (s , GetCurrentProcessId (), & protocol_info ) == SOCKET_ERROR ) {
505- return set_wsa_errno ();
547+ set_wsa_errno ();
548+ return INVALID_SOCKET ;
506549 }
507550 SOCKET dup = WSASocketA (FROM_PROTOCOL_INFO , FROM_PROTOCOL_INFO , FROM_PROTOCOL_INFO , & protocol_info , 0 , WSA_FLAG_NO_HANDLE_INHERIT );
508551 if (dup == INVALID_SOCKET ) {
509- return set_wsa_errno ();
552+ set_wsa_errno ();
553+ return INVALID_SOCKET ;
510554 }
511- return win_alloc_socket_fd (dup );
555+ return dup ;
556+ }
557+
558+ static int win_socket_dup (int32_t fd ) {
559+ SOCKET s = win_socket_from_fd (fd );
560+ int blocking = win_socket_blocking_from_fd (fd );
561+ SOCKET dup = win_duplicate_socket (s );
562+ if (dup == INVALID_SOCKET ) {
563+ return -1 ;
564+ }
565+ return win_alloc_socket_fd (dup , blocking );
566+ }
567+
568+ static int win_socket_dup2 (int32_t oldfd , int32_t newfd , int32_t inheritable ) {
569+ SOCKET dup = win_duplicate_socket (win_socket_from_fd (oldfd ));
570+ if (dup == INVALID_SOCKET ) {
571+ return -1 ;
572+ }
573+ int blocking = win_socket_blocking_from_fd (oldfd );
574+
575+ /*
576+ * Keep the table locked while replacing the dummy CRT fd. This ensures that
577+ * a full table is reported before _dup2 changes newfd and that the table
578+ * entry is updated atomically with respect to other table users.
579+ */
580+ ensure_socket_table ();
581+ EnterCriticalSection (& win_socket_table_lock );
582+ int table_has_slot = 0 ;
583+ for (int i = 0 ; i < WIN_SOCKET_TABLE_SIZE ; i ++ ) {
584+ if (win_socket_table [i ].fd == newfd || win_socket_table [i ].fd < 0 ) {
585+ table_has_slot = 1 ;
586+ break ;
587+ }
588+ }
589+ if (!table_has_slot ) {
590+ LeaveCriticalSection (& win_socket_table_lock );
591+ closesocket (dup );
592+ set_posix_errno (EMFILE );
593+ return -1 ;
594+ }
595+
596+ if (dup2_noraise (oldfd , newfd ) != 0 ) {
597+ LeaveCriticalSection (& win_socket_table_lock );
598+ closesocket (dup );
599+ return -1 ;
600+ }
601+
602+ SOCKET replaced = INVALID_SOCKET ;
603+ int replaced_result = win_replace_socket_fd_locked (newfd , dup , blocking , & replaced );
604+ LeaveCriticalSection (& win_socket_table_lock );
605+ if (replaced_result < 0 ) {
606+ closesocket (dup );
607+ return -1 ;
608+ }
609+ if (replaced != INVALID_SOCKET ) {
610+ closesocket (replaced );
611+ }
612+ if (!inheritable ) {
613+ return set_inheritable (newfd , 0 );
614+ }
615+ return 0 ;
512616}
513617
514618static intptr_t get_osfhandle_noraise (int32_t fd ) {
@@ -915,8 +1019,25 @@ GP_EXPORT int32_t call_dup(int32_t fd) {
9151019}
9161020
9171021GP_EXPORT int32_t call_dup2 (int32_t oldfd , int32_t newfd , int32_t inheritable ) {
1022+ if (oldfd == newfd ) {
1023+ int result = dup2_noraise (oldfd , newfd );
1024+ if (result == 0 && !inheritable ) {
1025+ result = set_inheritable (newfd , 0 );
1026+ }
1027+ return result == 0 ? newfd : -1 ;
1028+ }
1029+ if (win_socket_entry_index (oldfd ) >= 0 ) {
1030+ return win_socket_dup2 (oldfd , newfd , inheritable ) == 0 ? newfd : -1 ;
1031+ }
9181032 int result = dup2_noraise (oldfd , newfd );
919- if (result == 0 && !inheritable ) {
1033+ if (result != 0 ) {
1034+ return -1 ;
1035+ }
1036+ SOCKET stale_socket = win_remove_socket_fd (newfd );
1037+ if (stale_socket != INVALID_SOCKET ) {
1038+ closesocket (stale_socket );
1039+ }
1040+ if (!inheritable ) {
9201041 result = set_inheritable (newfd , 0 );
9211042 }
9221043 return result == 0 ? newfd : -1 ;
@@ -1038,17 +1159,7 @@ GP_EXPORT int32_t call_fsync(int32_t fd) {
10381159GP_EXPORT int32_t call_flock (int32_t fd , int32_t operation ) { return unsupported (); }
10391160GP_EXPORT int32_t call_fcntl_lock (int32_t fd , int32_t blocking , int32_t lockType , int32_t whence , int64_t start , int64_t length ) { return unsupported (); }
10401161GP_EXPORT int32_t get_blocking (int32_t fd ) {
1041- int result = 1 ;
1042- ensure_socket_table ();
1043- EnterCriticalSection (& win_socket_table_lock );
1044- for (int i = 0 ; i < WIN_SOCKET_TABLE_SIZE ; i ++ ) {
1045- if (win_socket_table [i ].fd == fd ) {
1046- result = win_socket_table [i ].blocking ;
1047- break ;
1048- }
1049- }
1050- LeaveCriticalSection (& win_socket_table_lock );
1051- return result ;
1162+ return win_socket_blocking_from_fd (fd );
10521163}
10531164
10541165GP_EXPORT int32_t set_blocking (int32_t fd , int32_t blocking ) {
@@ -1607,7 +1718,7 @@ GP_EXPORT int32_t call_socket(int32_t family, int32_t type, int32_t protocol) {
16071718 if (s == INVALID_SOCKET ) {
16081719 return set_wsa_errno ();
16091720 }
1610- return win_alloc_socket_fd (s );
1721+ return win_alloc_socket_fd (s , 1 );
16111722}
16121723
16131724GP_EXPORT int32_t call_accept (int32_t sockfd , int8_t * addr , int32_t * addr_len ) {
@@ -1617,7 +1728,7 @@ GP_EXPORT int32_t call_accept(int32_t sockfd, int8_t *addr, int32_t *addr_len) {
16171728 return set_wsa_errno ();
16181729 }
16191730 * addr_len = len ;
1620- return win_alloc_socket_fd (s );
1731+ return win_alloc_socket_fd (s , 1 );
16211732}
16221733
16231734GP_EXPORT int32_t call_bind (int32_t sockfd , int8_t * addr , int32_t addr_len ) { int r = bind (win_socket_from_fd (sockfd ), (struct sockaddr * ) addr , addr_len ); return r == SOCKET_ERROR ? set_wsa_errno () : r ; }
0 commit comments