Skip to content
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

Open
ddebroy opened this issue Jul 29, 2019 · 15 comments
Open

os: os.ModeSocket is not set for domain socket files in Windows #33357

ddebroy opened this issue Jul 29, 2019 · 15 comments

Comments

@ddebroy
Copy link

@ddebroy ddebroy commented Jul 29, 2019

What version of Go are you using (go version)?

$ go version
go version go1.12.4 linux/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build497560436=/tmp/go-build -gno-record-gcc-switches"

What 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:

fi, err := os.Stat(filename)
if err != nil {
	return fmt.Errorf("stat file %s failed: %v", filename, err)
}

if !fi.IsDir() {
	if fi.Mode()&os.ModeSocket == 0 {
		klog.V(5).Infof("Ignoring non socket file: %s", fi.Name())
                 return nil
	}
	klog.V(5).Infof("socket file found: %s", fi.Name())
        return nil
}

What did you expect to see?

socket file found: filename (as in Linux)

What did you see instead?

Ignoring non socket file: filename

@liggitt

This comment has been minimized.

Copy link
Contributor

@liggitt liggitt commented Jul 30, 2019

is the go env output accurate? that seems to show running on linux (GOOS="linux")

@ddebroy

This comment has been minimized.

Copy link
Author

@ddebroy ddebroy commented Jul 30, 2019

The go env output is for the Linux environment where the Windows binary was compiled (using GOOS="windows"). The Windows Server 2019 environment where the Windows binary was copied over and executed does not have go installed.

@mattn

This comment has been minimized.

Copy link
Member

@mattn mattn commented Jul 30, 2019

As far as I looked this problem, FindFirstFile and GetFileAttributes does not provide bit mask to indicate socket files.

@mattn

This comment has been minimized.

Copy link
Member

@mattn mattn commented Jul 31, 2019

I tested a domain socket file. GetFileAttributes(f) return FILE_ATTRIBUTE_ARCHIVE(0x20).

@mattn

This comment has been minimized.

Copy link
Member

@mattn mattn commented Jul 31, 2019

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)
}
@katiehockman katiehockman changed the title os.ModeSocket is not set for domain socket files in Windows os: os.ModeSocket is not set for domain socket files in Windows Jul 31, 2019
@katiehockman

This comment has been minimized.

Copy link
Member

@katiehockman katiehockman commented Jul 31, 2019

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Jul 31, 2019

@ianlancetaylor ianlancetaylor added this to the Go1.14 milestone Jul 31, 2019
@ddebroy

This comment has been minimized.

Copy link
Author

@ddebroy ddebroy commented Aug 1, 2019

We can check the ReparsePoint data associated with the file and match the ReparsePoint Tag with that of domain sockets:

> fsutil reparsepoint query C:\temp\go2.sock
Reparse Tag Value : 0x80000023
Tag value: Microsoft

Reparse Data Length: 0x00000000

While Microsoft documents some of the tags, the value 0x80000023 does not appear to show up in any of the published docs yet.

@0xbadfca11

This comment has been minimized.

Copy link

@0xbadfca11 0xbadfca11 commented Aug 1, 2019

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.
Starting with Windows 10 version 1903 creates as a regular file.

@mattn

This comment has been minimized.

Copy link
Member

@mattn mattn commented Aug 1, 2019

I used fsutil. but another result.

C:\dev\c-sandbox\af_unix>fsutil reparsepoint query server.sock
Error:  The file or directory is not a reparse point.
@ddebroy

This comment has been minimized.

Copy link
Author

@ddebroy ddebroy commented Aug 12, 2019

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:

const (
	FSCTL_GET_REPARSE_POINT          = 0x900A8
	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(filePath string) (bool, error) {
	fd, err := windows.CreateFile(windows.StringToUTF16Ptr(filePath), windows.GENERIC_READ, 0, nil, windows.OPEN_EXISTING, windows.FILE_FLAG_OPEN_REPARSE_POINT|windows.FILE_FLAG_BACKUP_SEMANTICS, 0)
	if err != nil {
		return false, errors.Wrap(err, "CreateFile failed")
	}
	defer windows.CloseHandle(fd)

	rdbbuf := make([]byte, MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
	var bytesReturned uint32
	if err = windows.DeviceIoControl(fd, windows.FSCTL_GET_REPARSE_POINT, nil, 0, &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil); err != nil {
		return false, errors.Wrap(err, "FSCTL_GET_REPARSE_POINT failed")
	}
	rdb := (*REPARSE_DATA_BUFFER)(unsafe.Pointer(&rdbbuf[0]))
	if rdb.header.ReparseTag == IO_REPARSE_TAG_SOCKET {
		return true, nil
	}
	return false, nil
}

return me the data I want in Windows 2019 LTS (following the pattern in ReadLink)

@mattn

This comment has been minimized.

Copy link
Member

@mattn mattn commented Aug 13, 2019

@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.

@alexbrainman

This comment has been minimized.

Copy link
Member

@alexbrainman alexbrainman commented Aug 18, 2019

fsutil reparsepoint query C:\temp\go2.sock
Reparse Tag Value : 0x80000023
Tag value: Microsoft

Reparse Data Length: 0x00000000

That is what I see here too. I am on Version 1809 of Windows 10.

While Microsoft documents some of the tags, the value 0x80000023 does not appear to show up in any of the published docs yet.

I agree. 0x80000023 is missing from that page.

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)

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 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.

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

@mattn

This comment has been minimized.

Copy link
Member

@mattn mattn commented Aug 18, 2019

@alexbrainman Yes, 1809 return correct value. But 1903 does not return correct value. Currently, we can't know when this bug will be fixed.

@alexbrainman

This comment has been minimized.

Copy link
Member

@alexbrainman alexbrainman commented Aug 21, 2019

Yes, 1809 return correct value. But 1903 does not return correct value.

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

@rsc rsc modified the milestones: Go1.14, Backlog Oct 9, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
8 participants
You can’t perform that action at this time.