Do you have any idea what else might be missing?
A systematic sweep over the AF_UNIX surface, run against a build carrying the commits from this MR, found most of it in good shape. SO_TYPE, SO_ERROR, SO_RCVBUF/SO_SNDBUF, SO_REUSEADDR, SO_LINGER, SO_PROTOCOL_INFOW, FIONBIO, FIONREAD, WSASend/WSARecv, shutdown, select/WSAPoll, WSADuplicateSocketW, getsockname/getpeername, AcceptEx, DisconnectEx, WSASendMsg, WSAEnumProtocols, bind() returning WSAEADDRINUSE on a taken path, socket(AF_UNIX, SOCK_DGRAM) being rejected, and DeleteFile on a bound socket all behave as expected. Three things came up. Patches for all three are below; they apply to current master. ### 1. SIO_AF_UNIX_GETPEERPID is missing from include/afunix.h Not only from the implementation. The Windows SDK (and mingw-w64) define it in that header as `_WSAIOR(IOC_VENDOR, 256)`, so code including afunix.h and using the ioctl does not compile against Wine's headers at all. ### 2. ConnectEx() does not work on AF_UNIX It fails with WSAEINVAL (10022), and wineserver logs: ``` sock_get_ntstatus() can't map error: No such file or directory ``` connect() appends the translated Unix path after the sockaddr in the IOCTL_AFD_WINE_CONNECT request; WS2_ConnectEx() does not, it copies only the sockaddr and the send buffer. The server derives `unix_path_len` from everything following the address, so it ends up with an empty path, and connect(2) returns ENOENT - for which sock_get_ntstatus() has no case, so it falls through to STATUS_UNSUCCESSFUL. There is a design question behind this. In the server the send data and the Unix path occupy the same vararg slot: `send_len` is computed identically to `unix_path_len` and then reduced by it. Appending the path in WS2_ConnectEx() fixes the common `send_len == 0` case, but ConnectEx *with* connect data on an AF_UNIX address stays ambiguous unless `afd_connect_params` gains an explicit length field. The patch below takes the conservative route and rejects that combination with WSAEOPNOTSUPP; adding a length field would be the more complete fix, should connect data on AF_UNIX be worth supporting. Independently of AF_UNIX, sock_get_ntstatus() having no ENOENT case means any path related failure ends up as that perror line plus STATUS_UNSUCCESSFUL. There is no patch for it here, because the matching Winsock error would need to be checked against Windows first - STATUS_OBJECT_NAME_NOT_FOUND currently maps to WSAENETDOWN in ws2_32, which does not look right for a missing socket file. ### 3. The reparse tag is only visible through part of the paths that expose it For a bound socket: ``` GetFileAttributes 0x420, REPARSE_POINT set ok GetFileAttributesEx 0x420, REPARSE_POINT set ok FSCTL_GET_REPARSE_POINT tag 0x80000023 ok GetFileInformationByHandleEx/AttributeTagInfo attr 0x20, tag 0 missing FindFirstFile (dwReserved0) tag 0 missing ``` The handle based case is the interesting one: on the very same handle FSCTL_GET_REPARSE_POINT returns IO_REPARSE_TAG_AF_UNIX correctly, while FileAttributeTagInfo reports neither the tag nor even FILE_ATTRIBUTE_REPARSE_POINT. NtQueryInformationFile(FileAttributeTagInformation) goes through fd_get_file_info(), which - unlike get_file_info() - has no S_ISSOCK case, so what a caller sees depends on whether it asks by path or by handle. FindFirstFile is a separate and not AF_UNIX specific matter: the EaSize field of the directory info structures is hardcoded to 0 with a FIXME in dlls/ntdll/unix/file.c, so directory enumeration reports no reparse tag for symlinks and mount points either. Left alone here. ### Patches **include: add SIO_AF_UNIX_GETPEERPID to afunix.h** ```diff --- a/include/afunix.h +++ b/include/afunix.h @@ -33,4 +33,6 @@ typedef struct WS(sockaddr_un) char sun_path[UNIX_PATH_MAX]; } SOCKADDR_UN, *PSOCKADDR_UN; +#define SIO_AF_UNIX_GETPEERPID _WSAIOR(WS(IOC_VENDOR), 256) + #endif /* _WS2AFUNIX_ */ ``` **ntdll: report AF_UNIX sockets as reparse points for handle based queries** ```diff --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -1684,6 +1684,12 @@ static int fd_get_file_info( HANDLE handle, int fd, unsigned int options, if (ret == -1) return ret; *attr |= get_file_attributes( st ); if (reparse_tag) *reparse_tag = 0; + /* a bound AF_UNIX socket is a reparse point on Windows */ + if (S_ISSOCK( st->st_mode )) + { + *attr |= FILE_ATTRIBUTE_REPARSE_POINT; + if (reparse_tag) *reparse_tag = IO_REPARSE_TAG_AF_UNIX; + } /* consider mount points to be reparse points (IO_REPARSE_TAG_MOUNT_POINT) */ if (options & FILE_OPEN_REPARSE_POINT) { ``` **ws2_32: pass the Unix path to the server in ConnectEx() on AF_UNIX sockets** ```diff --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -1398,6 +1398,8 @@ static BOOL WINAPI WS2_ConnectEx( SOCKET s, const struct sockaddr *name, int nam { struct afd_connect_params *params; void *cvalue = NULL; + char *unix_path = NULL; + int unix_varargs_size = 0; NTSTATUS status; TRACE( "socket %#Ix, ptr %p %s, length %d, send_buffer %p, send_len %lu, overlapped %p\n", @@ -1409,24 +1411,50 @@ static BOOL WINAPI WS2_ConnectEx( SOCKET s, const struct sockaddr *name, int nam return FALSE; } + if (name->sa_family == AF_UNIX && *name->sa_data) + { + WCHAR *sun_pathW; + + /* The server expects the translated Unix path appended to the address, + * which leaves no room for connect data on AF_UNIX sockets. */ + if (send_len) + { + SetLastError( WSAEOPNOTSUPP ); + return FALSE; + } + + sun_pathW = strdupAtoW( name->sa_data ); + unix_path = wine_get_unix_file_name( sun_pathW ); + free( sun_pathW ); + if (!unix_path) + return FALSE; + unix_varargs_size = strlen( unix_path ); + } + if (!((ULONG_PTR)overlapped->hEvent & 1)) cvalue = overlapped; overlapped->Internal = STATUS_PENDING; overlapped->InternalHigh = 0; - if (!(params = malloc( sizeof(*params) + namelen + send_len ))) + if (!(params = malloc( sizeof(*params) + namelen + send_len + unix_varargs_size ))) { + free( unix_path ); SetLastError( ERROR_NOT_ENOUGH_MEMORY ); return SOCKET_ERROR; } params->addr_len = namelen; params->synchronous = FALSE; memcpy( params + 1, name, namelen ); - memcpy( (char *)(params + 1) + namelen, send_buffer, send_len ); + if (unix_path) + memcpy( (char *)(params + 1) + namelen, unix_path, unix_varargs_size ); + else + memcpy( (char *)(params + 1) + namelen, send_buffer, send_len ); status = NtDeviceIoControlFile( SOCKET2HANDLE(s), overlapped->hEvent, NULL, cvalue, (IO_STATUS_BLOCK *)overlapped, IOCTL_AFD_WINE_CONNECT, - params, sizeof(*params) + namelen + send_len, NULL, 0 ); + params, sizeof(*params) + namelen + send_len + unix_varargs_size, + NULL, 0 ); free( params ); + free( unix_path ); if (ret_len) *ret_len = overlapped->InternalHigh; SetLastError( NtStatusToWSAError( status ) ); TRACE( "status %#lx.\n", status ); ``` -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7650#note_149411