-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
os: os.ModeSocket is not set for domain socket files in Windows #33357
Comments
is the |
The |
As far as I looked this problem, FindFirstFile and GetFileAttributes does not provide bit mask to indicate socket files. |
I tested a domain socket file. GetFileAttributes(f) return FILE_ATTRIBUTE_ARCHIVE(0x20). |
This is a code I used. https://gist.github.com/mattn/2dee75a1731bd471db44fc966a607f62 package main
import (
"fmt"
"log"
"os"
"syscall"
)
func main() {
fi, err := os.Stat("server.sock")
if err != nil {
log.Fatal(err)
}
fmt.Printf("%x\n", fi.Sys().(*syscall.Win32FileAttributeData).FileAttributes)
} |
/cc @rsc @ianlancetaylor |
We can check the ReparsePoint data associated with the file and match the ReparsePoint Tag with that of domain sockets:
While Microsoft documents some of the tags, the value |
As far as I know, until Windows 10 version 1809 (include Windows Server 2019) creates unix domain socket as a reparse point with IO_REPARSE_TAG_AF_UNIX tag. |
I used fsutil. but another result.
|
I got confirmation from Microsoft people in the Kubernetes SIG-Windows community that the 1903 behavior where the reparse point is not being reported for domain sockets is a bug (being tracked to be fixed internally at Microsoft) For now, I am able to have something like this:
return me the data I want in Windows 2019 LTS (following the pattern in ReadLink) |
@ddebroy Thanks for the confirmation. I guess that GetFileAttributes will return correct value (FILE_ATTRIBUTE_REPARSE_POINT) when the bug of 1903 will be fixed. However, until this bug is really fixed, nothing can be done for Go standard package. |
That is what I see here too. I am on Version 1809 of Windows 10.
I agree. 0x80000023 is missing from that page.
Do you know what is the bug? Because we could adjust current os.Stat to look for 0x80000023 in reparse point tag and translate it into os.ModeSocket.
I can see GetFileAttributesExW with GetFileExInfoStandard returns FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_REPARSE_POINT for WIN32_FILE_ATTRIBUTE_DATA.dwFileAttributes field. So, if we assume that sockets will return 0x80000023 reparse tag, that should be enough to identify sockets. No? Alex |
@alexbrainman Yes, 1809 return correct value. But 1903 does not return correct value. Currently, we can't know when this bug will be fixed. |
I don't have 1903 to test, so I would not know what the problem is there. Regardless, we still need to do something here. I don't see how os.ModeSocket would suddenly appear in os.Stat after Microsoft fixes some bug. Alex |
Until the bug - golang/go#33357 is fixed, os.stat wouldn't return the right mode(socket) on windows. Hence deleting the file, without checking whether its a socket, on windows.
- Until the bug - golang/go#33357 is fixed, os.stat wouldn't return the right mode(socket) on windows. Hence deleting the file, without checking whether its a socket, on windows. - Place os specific logic into util_{linux, windows} files.
- Until the bug - golang/go#33357 is fixed, os.stat wouldn't return the right mode(socket) on windows. Hence deleting the file, without checking whether its a socket, on windows. - Place os specific logic into util_{linux, windows} files.
- Until the bug - golang/go#33357 is fixed, os.stat wouldn't return the right mode(socket) on windows. Hence deleting the file, without checking whether its a socket, on windows. - Place os specific logic into util_{linux, windows} files and move util under pkg.
- Until the bug - golang/go#33357 is fixed, os.stat wouldn't return the right mode(socket) on windows. Hence deleting the file, without checking whether its a socket, on windows. - Place os specific logic into util_{linux, windows} files and move util under pkg.
- Until the bug - golang/go#33357 is fixed, os.stat wouldn't return the right mode(socket) on windows. Hence deleting the file, without checking whether its a socket, on windows. - Place os specific logic into util_{linux, windows} files and move util under pkg.
@mattn @ddebroy Is the suggestion here to augment If that series of steps works, it seems like that'd be feasible, since I assume we're already calling |
That approach would work, except that That said, obtaining a file handle using |
This change works well for me. But I wonder that Go have the way to create the file with AF_UNIX attribute. diff --git a/src/os/stat_windows.go b/src/os/stat_windows.go
index da4c49090e..22c78b8380 100644
--- a/src/os/stat_windows.go
+++ b/src/os/stat_windows.go
@@ -42,6 +42,41 @@ func (file *File) Stat() (FileInfo, error) {
return fs, err
}
+const (
+ _MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024
+ _IO_REPARSE_TAG_SOCKET = 0x80000023
+)
+
+type _REPARSE_DATA_BUFFER_HEADER struct {
+ ReparseTag uint32
+ ReparseDataLength uint16
+ Reserved uint16
+}
+
+type _REPARSE_DATA_BUFFER struct {
+ header _REPARSE_DATA_BUFFER_HEADER
+ detail [_MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte
+}
+
+func isSocketFile(name string) (bool, error) {
+ fd, err := syscall.CreateFile(syscall.StringToUTF16Ptr(name), syscall.GENERIC_READ, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
+ if err != nil {
+ return false, err
+ }
+ defer syscall.CloseHandle(fd)
+
+ rdbbuf := make([]byte, _MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
+ var bytesReturned uint32
+ if err = syscall.DeviceIoControl(fd, syscall.FSCTL_GET_REPARSE_POINT, nil, 0, &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil); err != nil {
+ return false, err
+ }
+ rdb := (*_REPARSE_DATA_BUFFER)(unsafe.Pointer(&rdbbuf[0]))
+ if rdb.header.ReparseTag == _IO_REPARSE_TAG_SOCKET {
+ return true, nil
+ }
+ return false, nil
+}
+
// stat implements both Stat and Lstat of a file.
func stat(funcname, name string, createFileAttrs uint32) (FileInfo, error) {
if len(name) == 0 {
@@ -59,20 +94,34 @@ func stat(funcname, name string, createFileAttrs uint32) (FileInfo, error) {
// See https://golang.org/issues/19922#issuecomment-300031421 for details.
var fa syscall.Win32FileAttributeData
err = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa)))
- if err == nil && fa.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
- // Not a symlink.
- fs := &fileStat{
- FileAttributes: fa.FileAttributes,
- CreationTime: fa.CreationTime,
- LastAccessTime: fa.LastAccessTime,
- LastWriteTime: fa.LastWriteTime,
- FileSizeHigh: fa.FileSizeHigh,
- FileSizeLow: fa.FileSizeLow,
+ if err == nil {
+ if fa.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
+ // Not a symlink.
+ fs := &fileStat{
+ FileAttributes: fa.FileAttributes,
+ CreationTime: fa.CreationTime,
+ LastAccessTime: fa.LastAccessTime,
+ LastWriteTime: fa.LastWriteTime,
+ FileSizeHigh: fa.FileSizeHigh,
+ FileSizeLow: fa.FileSizeLow,
+ }
+ if err := fs.saveInfoFromPath(name); err != nil {
+ return nil, err
+ }
+ return fs, nil
}
- if err := fs.saveInfoFromPath(name); err != nil {
- return nil, err
+
+ if ok, err := isSocketFile(name); err == nil && ok {
+ fs := &fileStat{
+ FileAttributes: fa.FileAttributes,
+ CreationTime: fa.CreationTime,
+ LastAccessTime: fa.LastAccessTime,
+ LastWriteTime: fa.LastWriteTime,
+ FileSizeHigh: fa.FileSizeHigh,
+ FileSizeLow: fa.FileSizeLow,
+ }
+ return fs, nil
}
- return fs, nil
}
// GetFileAttributesEx fails with ERROR_SHARING_VIOLATION error for
// files, like c:\pagefile.sys. Use FindFirstFile for such files.
diff --git a/src/os/types_windows.go b/src/os/types_windows.go
index 59bf5ca381..15a920a7f6 100644
--- a/src/os/types_windows.go
+++ b/src/os/types_windows.go
@@ -121,6 +121,9 @@ func (fs *fileStat) Mode() (m FileMode) {
if fs.isSymlink() {
return m | ModeSymlink
}
+ if fs.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 {
+ return m | ModeSocket
+ }
if fs.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
m |= ModeDir | 0111
} |
@mattn one of your related CL's was mentioned here: Commenting so Github links them together.
Your patch is working for me with a socket created by package main
import (
"fmt"
"io/fs"
"log"
"net"
"os"
)
func main() {
log.SetFlags(log.Lshortfile)
socketFilePath := "uds"
unixListener, err := net.Listen("unix", socketFilePath)
if err != nil {
log.Fatal(err)
}
defer unixListener.Close()
stat, err := os.Stat(socketFilePath)
if err != nil {
log.Print(err)
return
}
fileMode := stat.Mode()
fmt.Println(fileMode)
fmt.Println("Has socket bit?", fileMode&fs.ModeSocket != 0)
}
|
Change https://golang.org/cl/338069 mentions this issue: |
@djdv That result is curious to me. I came across this bug while trying to understand "The file cannot be accessed by the system." error in one of my own tests.
|
@alexbrainman @zx2c4 Could you please review https://go-review.googlesource.com/c/go/+/338069/ ? |
Done. Alex |
Change https://go.dev/cl/561937 mentions this issue: |
See: golang/go#33357 Go 1.22 has made it so that os.Stat now follows all reparse points, of which IO_REPARSE_TAG_AF_UNIX (the reparse point that os.ModeSocket should have been triggering) is one
See: golang/go#33357 Go 1.22 has made it so that os.Stat now follows all reparse points, of which IO_REPARSE_TAG_AF_UNIX (the reparse point that os.ModeSocket should have been triggering) is one
Unix sockets are identified by the IO_REPARSE_TAG_AF_UNIX reparse tag. Teach fileStat.Mode() to recognize this tag and set the os.ModeSocket bit in such case. Note that there is a bug starting in Windows 19H1 until 20H1 that makes the IO_REPARSE_TAG_AF_UNIX tag not being set for unix sockets. This CL doesn't provide a workaround for this bug. Fixes golang#33357. Change-Id: Iea8f24b20672c8d4b03f55ef298d128431dc3fac Reviewed-on: https://go-review.googlesource.com/c/go/+/561937 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Alex Brainman <[email protected]> Reviewed-by: David Chase <[email protected]> Reviewed-by: Bryan Mills <[email protected]>
Change https://go.dev/cl/565135 mentions this issue: |
Updates #33357. Change-Id: I66866475447e03f604202f34ddfbafdc0512cb72 Reviewed-on: https://go-review.googlesource.com/c/go/+/565135 Reviewed-by: Carlos Amedee <[email protected]> Reviewed-by: Bryan Mills <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Alex Brainman <[email protected]>
Update socket file detection logic to use os.Stat as per upstream Go fix for golang/go#33357. This resolves the issue where socket files could not be properly identified on Windows systems.
Update socket file detection logic to use os.Stat as per upstream Go fix for golang/go#33357. This resolves the issue where socket files could not be properly identified on Windows systems.
Update socket file detection logic to use os.Stat as per upstream Go fix for golang/go#33357. This resolves the issue where socket files could not be properly identified on Windows systems.
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
I created a Windows golang program to listen on a Windows domain socket. In another Windows golang program, on the file corresponding to the socket, I invoked the following code:
What did you expect to see?
socket file found: filename
(as in Linux)What did you see instead?
Ignoring non socket file: filename
The text was updated successfully, but these errors were encountered: