From: Giang Nguyen <nen24t@gmail.com> Windows exposes a bound AF_UNIX socket file as a reparse point: opening it without FILE_OPEN_REPARSE_POINT fails with STATUS_IO_REPARSE_TAG_NOT_HANDLED, opening it with the flag yields a handle that carries only metadata, and FSCTL_GET_REPARSE_POINT on that handle reports IO_REPARSE_TAG_AF_UNIX with an empty reparse buffer. open() on a socket file fails with ENXIO on Linux, and the socket branch in open_fd() only caught that for FILE_DELETE_ON_CLOSE, so every other open ended in STATUS_NO_SUCH_DEVICE. get_reparse_point() recognises a reparse point by the '?' name suffix plus the WINEREPARSE extended attribute, neither of which a real socket inode has. open_fd() now maps the plain open to STATUS_IO_REPARSE_TAG_NOT_HANDLED and serves FILE_OPEN_REPARSE_POINT from O_PATH, which is exactly a descriptor carrying metadata and nothing else. get_reparse_point() answers for a socket before the suffix check, from the inode type rather than the extended attribute. The FILE_DELETE_ON_CLOSE path keeps its own branch. O_PATH is Linux-specific and has no configure check. Guarded by #ifdef, a platform without it keeps the previous behaviour for the reparse open, while the plain open still reports the correct error. ws2_32:sock test_afunix: 16 failing assertions down to 0, clearing sock.c:14712, 14716, 14719 and 14720. ntdll:file and kernel32:file stay at 0 failures. Signed-off-by: Giang Nguyen <nen24t@gmail.com> --- server/fd.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/server/fd.c b/server/fd.c index 24c93aa3aab..d68fa489519 100644 --- a/server/fd.c +++ b/server/fd.c @@ -1958,8 +1958,32 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam * without lock support. Contrary to POSIX, Linux returns ENXIO in this * case, so we also check that error code here. */ - if ((errno == EOPNOTSUPP || errno == ENXIO) && S_ISSOCK(st.st_mode) && (options & FILE_DELETE_ON_CLOSE)) - ; /* no error, go to regular deletion code path */ + if ((errno == EOPNOTSUPP || errno == ENXIO) && S_ISSOCK(st.st_mode)) + { + /* Windows exposes a bound AF_UNIX socket as a reparse point: opening it + * without FILE_OPEN_REPARSE_POINT fails with STATUS_IO_REPARSE_TAG_NOT_HANDLED, + * and with the flag it yields a handle that only carries metadata, which is + * what O_PATH gives us here. + */ + if (options & FILE_DELETE_ON_CLOSE) + ; /* no error, go to regular deletion code path */ + else if (!(options & FILE_OPEN_REPARSE_POINT)) + { + set_error( STATUS_IO_REPARSE_TAG_NOT_HANDLED ); + goto error; + } + else + { +#ifdef O_PATH + fd->unix_fd = open( name, O_PATH ); +#endif + if (fd->unix_fd == -1) + { + file_set_error(); + goto error; + } + } + } else { file_set_error(); @@ -2490,6 +2514,7 @@ static void get_reparse_point( struct fd *fd, struct async *async ) /* we can't just allocate get_reply_max_size() here; * Linux won't return any data if the size is too small */ char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + struct stat st; int ret; if (!fd->unix_name) @@ -2504,6 +2529,26 @@ static void get_reparse_point( struct fd *fd, struct async *async ) return; } + /* A bound AF_UNIX socket carries its reparse tag in the inode type, not in the + * name suffix and extended attribute the other reparse points use. Windows + * reports IO_REPARSE_TAG_AF_UNIX with no reparse data at all. */ + if (fd->unix_fd != -1 && !fstat( fd->unix_fd, &st ) && S_ISSOCK( st.st_mode )) + { + REPARSE_DATA_BUFFER *data = (REPARSE_DATA_BUFFER *)buffer; + unsigned int size = sizeof(data->ReparseTag) + sizeof(data->ReparseDataLength) + + sizeof(data->Reserved); + + if (get_reply_max_size() < size) + { + set_error( STATUS_BUFFER_TOO_SMALL ); + return; + } + memset( data, 0, size ); + data->ReparseTag = IO_REPARSE_TAG_AF_UNIX; + set_reply_data( data, size ); + return; + } + if (fd->unix_name[strlen( fd->unix_name ) - 1] != '?') { set_error( STATUS_NOT_A_REPARSE_POINT ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/7650