diff options
Diffstat (limited to 'libgo/go/syscall')
-rw-r--r-- | libgo/go/syscall/creds_test.go | 109 | ||||
-rw-r--r-- | libgo/go/syscall/env_windows.go | 6 | ||||
-rw-r--r-- | libgo/go/syscall/exec_unix.go | 30 | ||||
-rw-r--r-- | libgo/go/syscall/exec_windows.go | 16 | ||||
-rw-r--r-- | libgo/go/syscall/libcall_posix.go | 10 | ||||
-rw-r--r-- | libgo/go/syscall/mksyscall.awk | 22 | ||||
-rw-r--r-- | libgo/go/syscall/passfd_test.go | 151 | ||||
-rw-r--r-- | libgo/go/syscall/race0.go | 19 | ||||
-rw-r--r-- | libgo/go/syscall/security_windows.go | 10 | ||||
-rw-r--r-- | libgo/go/syscall/syscall.go | 16 | ||||
-rw-r--r-- | libgo/go/syscall/syscall_unix.go | 17 |
11 files changed, 363 insertions, 43 deletions
diff --git a/libgo/go/syscall/creds_test.go b/libgo/go/syscall/creds_test.go new file mode 100644 index 0000000..6eaa97e --- /dev/null +++ b/libgo/go/syscall/creds_test.go @@ -0,0 +1,109 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux + +package syscall_test + +import ( + "bytes" + "net" + "os" + "syscall" + "testing" +) + +// TestSCMCredentials tests the sending and receiving of credentials +// (PID, UID, GID) in an ancillary message between two UNIX +// sockets. The SO_PASSCRED socket option is enabled on the sending +// socket for this to work. +func TestSCMCredentials(t *testing.T) { + fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0) + if err != nil { + t.Fatalf("Socketpair: %v", err) + } + defer syscall.Close(fds[0]) + defer syscall.Close(fds[1]) + + err = syscall.SetsockoptInt(fds[0], syscall.SOL_SOCKET, syscall.SO_PASSCRED, 1) + if err != nil { + t.Fatalf("SetsockoptInt: %v", err) + } + + srv, err := net.FileConn(os.NewFile(uintptr(fds[0]), "")) + if err != nil { + t.Errorf("FileConn: %v", err) + return + } + defer srv.Close() + + cli, err := net.FileConn(os.NewFile(uintptr(fds[1]), "")) + if err != nil { + t.Errorf("FileConn: %v", err) + return + } + defer cli.Close() + + var ucred syscall.Ucred + if os.Getuid() != 0 { + ucred.Pid = int32(os.Getpid()) + ucred.Uid = 0 + ucred.Gid = 0 + oob := syscall.UnixCredentials(&ucred) + _, _, err := cli.(*net.UnixConn).WriteMsgUnix(nil, oob, nil) + if err.(*net.OpError).Err != syscall.EPERM { + t.Fatalf("WriteMsgUnix failed with %v, want EPERM", err) + } + } + + ucred.Pid = int32(os.Getpid()) + ucred.Uid = uint32(os.Getuid()) + ucred.Gid = uint32(os.Getgid()) + oob := syscall.UnixCredentials(&ucred) + + // this is going to send a dummy byte + n, oobn, err := cli.(*net.UnixConn).WriteMsgUnix(nil, oob, nil) + if err != nil { + t.Fatalf("WriteMsgUnix: %v", err) + } + if n != 0 { + t.Fatalf("WriteMsgUnix n = %d, want 0", n) + } + if oobn != len(oob) { + t.Fatalf("WriteMsgUnix oobn = %d, want %d", oobn, len(oob)) + } + + oob2 := make([]byte, 10*len(oob)) + n, oobn2, flags, _, err := srv.(*net.UnixConn).ReadMsgUnix(nil, oob2) + if err != nil { + t.Fatalf("ReadMsgUnix: %v", err) + } + if flags != 0 { + t.Fatalf("ReadMsgUnix flags = 0x%x, want 0", flags) + } + if n != 1 { + t.Fatalf("ReadMsgUnix n = %d, want 1 (dummy byte)", n) + } + if oobn2 != oobn { + // without SO_PASSCRED set on the socket, ReadMsgUnix will + // return zero oob bytes + t.Fatalf("ReadMsgUnix oobn = %d, want %d", oobn2, oobn) + } + oob2 = oob2[:oobn2] + if !bytes.Equal(oob, oob2) { + t.Fatal("ReadMsgUnix oob bytes don't match") + } + + scm, err := syscall.ParseSocketControlMessage(oob2) + if err != nil { + t.Fatalf("ParseSocketControlMessage: %v", err) + } + newUcred, err := syscall.ParseUnixCredentials(&scm[0]) + if err != nil { + t.Fatalf("ParseUnixCredentials: %v", err) + } + if *newUcred != ucred { + t.Fatalf("ParseUnixCredentials = %+v, want %+v", newUcred, ucred) + } +} diff --git a/libgo/go/syscall/env_windows.go b/libgo/go/syscall/env_windows.go index 3107ae5..39bd502 100644 --- a/libgo/go/syscall/env_windows.go +++ b/libgo/go/syscall/env_windows.go @@ -12,7 +12,7 @@ import ( ) func Getenv(key string) (value string, found bool) { - keyp, err := utf16PtrFromString(key) + keyp, err := UTF16PtrFromString(key) if err != nil { return "", false } @@ -38,12 +38,12 @@ func Setenv(key, value string) error { var v *uint16 var err error if len(value) > 0 { - v, err = utf16PtrFromString(value) + v, err = UTF16PtrFromString(value) if err != nil { return err } } - keyp, err := utf16PtrFromString(key) + keyp, err := UTF16PtrFromString(key) if err != nil { return err } diff --git a/libgo/go/syscall/exec_unix.go b/libgo/go/syscall/exec_unix.go index b34ee1b..813627b 100644 --- a/libgo/go/syscall/exec_unix.go +++ b/libgo/go/syscall/exec_unix.go @@ -50,10 +50,6 @@ import ( //sysnb raw_dup2(oldfd int, newfd int) (err Errno) //dup2(oldfd int, newfd int) int -// Note: not raw, returns error rather than Errno. -//sys read(fd int, p *byte, np int) (n int, err error) -//read(fd int, buf *byte, count Size_t) Ssize_t - // Lock synchronizing creation of new file descriptors with fork. // // We want the child in a fork/exec sequence to inherit only the @@ -103,7 +99,7 @@ import ( var ForkLock sync.RWMutex -// Convert array of string to array of NUL-terminated byte pointer. +// StringSlicePtr is deprecated. Use SlicePtrFromStrings instead. // If any string contains a NUL byte this function panics instead // of returning an error. func StringSlicePtr(ss []string) []*byte { @@ -115,14 +111,14 @@ func StringSlicePtr(ss []string) []*byte { return bb } -// slicePtrFromStrings converts a slice of strings to a slice of +// SlicePtrFromStrings converts a slice of strings to a slice of // pointers to NUL-terminated byte slices. If any string contains // a NUL byte, it returns (nil, EINVAL). -func slicePtrFromStrings(ss []string) ([]*byte, error) { +func SlicePtrFromStrings(ss []string) ([]*byte, error) { var err error bb := make([]*byte, len(ss)+1) for i := 0; i < len(ss); i++ { - bb[i], err = bytePtrFromString(ss[i]) + bb[i], err = BytePtrFromString(ss[i]) if err != nil { return nil, err } @@ -185,15 +181,15 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) p[1] = -1 // Convert args to C form. - argv0p, err := bytePtrFromString(argv0) + argv0p, err := BytePtrFromString(argv0) if err != nil { return 0, err } - argvp, err := slicePtrFromStrings(argv) + argvp, err := SlicePtrFromStrings(argv) if err != nil { return 0, err } - envvp, err := slicePtrFromStrings(attr.Env) + envvp, err := SlicePtrFromStrings(attr.Env) if err != nil { return 0, err } @@ -204,14 +200,14 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) var chroot *byte if sys.Chroot != "" { - chroot, err = bytePtrFromString(sys.Chroot) + chroot, err = BytePtrFromString(sys.Chroot) if err != nil { return 0, err } } var dir *byte if attr.Dir != "" { - dir, err = bytePtrFromString(attr.Dir) + dir, err = BytePtrFromString(attr.Dir) if err != nil { return 0, err } @@ -242,7 +238,7 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) // Read child error status from pipe. Close(p[1]) - n, err = read(p[0], (*byte)(unsafe.Pointer(&err1)), int(unsafe.Sizeof(err1))) + n, err = readlen(p[0], (*byte)(unsafe.Pointer(&err1)), int(unsafe.Sizeof(err1))) Close(p[0]) if err != nil || n != 0 { if n == int(unsafe.Sizeof(err1)) { @@ -286,15 +282,15 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle // Ordinary exec. func Exec(argv0 string, argv []string, envv []string) (err error) { - argv0p, err := bytePtrFromString(argv0) + argv0p, err := BytePtrFromString(argv0) if err != nil { return err } - argvp, err := slicePtrFromStrings(argv) + argvp, err := SlicePtrFromStrings(argv) if err != nil { return err } - envvp, err := slicePtrFromStrings(envv) + envvp, err := SlicePtrFromStrings(envv) if err != nil { return err } diff --git a/libgo/go/syscall/exec_windows.go b/libgo/go/syscall/exec_windows.go index 68779c4..82abc07 100644 --- a/libgo/go/syscall/exec_windows.go +++ b/libgo/go/syscall/exec_windows.go @@ -132,7 +132,7 @@ func SetNonblock(fd Handle, nonblocking bool) (err error) { // getFullPath retrieves the full path of the specified file. // Just a wrapper for Windows GetFullPathName api. func getFullPath(name string) (path string, err error) { - p, err := utf16PtrFromString(name) + p, err := UTF16PtrFromString(name) if err != nil { return "", err } @@ -228,8 +228,9 @@ type ProcAttr struct { } type SysProcAttr struct { - HideWindow bool - CmdLine string // used if non-empty, else the windows command line is built by escaping the arguments passed to StartProcess + HideWindow bool + CmdLine string // used if non-empty, else the windows command line is built by escaping the arguments passed to StartProcess + CreationFlags uint32 } var zeroProcAttr ProcAttr @@ -264,7 +265,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle return 0, 0, err } } - argv0p, err := utf16PtrFromString(argv0) + argv0p, err := UTF16PtrFromString(argv0) if err != nil { return 0, 0, err } @@ -281,7 +282,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle var argvp *uint16 if len(cmdline) != 0 { - argvp, err = utf16PtrFromString(cmdline) + argvp, err = UTF16PtrFromString(cmdline) if err != nil { return 0, 0, err } @@ -289,7 +290,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle var dirp *uint16 if len(attr.Dir) != 0 { - dirp, err = utf16PtrFromString(attr.Dir) + dirp, err = UTF16PtrFromString(attr.Dir) if err != nil { return 0, 0, err } @@ -325,7 +326,8 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle pi := new(ProcessInformation) - err = CreateProcess(argv0p, argvp, nil, nil, true, CREATE_UNICODE_ENVIRONMENT, createEnvBlock(attr.Env), dirp, si, pi) + flags := sys.CreationFlags | CREATE_UNICODE_ENVIRONMENT + err = CreateProcess(argv0p, argvp, nil, nil, true, flags, createEnvBlock(attr.Env), dirp, si, pi) if err != nil { return 0, 0, err } diff --git a/libgo/go/syscall/libcall_posix.go b/libgo/go/syscall/libcall_posix.go index 4f25b82..9623103 100644 --- a/libgo/go/syscall/libcall_posix.go +++ b/libgo/go/syscall/libcall_posix.go @@ -277,7 +277,10 @@ func Gettimeofday(tv *Timeval) (err error) { //sys Pause() (err error) //pause() int -//sys Read(fd int, p []byte) (n int, err error) +//sys read(fd int, p []byte) (n int, err error) +//read(fd int, buf *byte, count Size_t) Ssize_t + +//sys readlen(fd int, p *byte, np int) (n int, err error) //read(fd int, buf *byte, count Size_t) Ssize_t //sys Readlink(path string, buf []byte) (n int, err error) @@ -344,7 +347,10 @@ func Settimeofday(tv *Timeval) (err error) { //sys Utime(path string, buf *Utimbuf) (err error) //utime(path *byte, buf *Utimbuf) int -//sys Write(fd int, p []byte) (n int, err error) +//sys write(fd int, p []byte) (n int, err error) +//write(fd int, buf *byte, count Size_t) Ssize_t + +//sys writelen(fd int, p *byte, np int) (n int, err error) //write(fd int, buf *byte, count Size_t) Ssize_t //sys munmap(addr uintptr, length uintptr) (err error) diff --git a/libgo/go/syscall/mksyscall.awk b/libgo/go/syscall/mksyscall.awk index b817960..74f0e28 100644 --- a/libgo/go/syscall/mksyscall.awk +++ b/libgo/go/syscall/mksyscall.awk @@ -104,6 +104,19 @@ BEGIN { loc = gofnname "/" cfnname ":" + haserr = 0 + if (gofnresults != "") { + fields = split(gofnresults, goresults, ", *") + for (goresult = 1; goresults[goresult] != ""; goresult++) { + if (split(goresults[goresult], goparam) == 2) { + if (goparam[1] == "err") { + haserr = 1 + break + } + } + } + } + split(gofnparams, goargs, ", *") split(cfnparams, cargs, ", *") args = "" @@ -147,7 +160,14 @@ BEGIN { status = 1 next } - printf("\t_p%d := StringBytePtr(%s)\n", goarg, goname) + printf("\tvar _p%d *byte\n", goarg) + if (haserr) { + printf("\t_p%d, err = BytePtrFromString(%s)\n", goarg, goname) + printf("\tif err != nil {\n\t\treturn\n\t}\n") + } else { + print loc, "uses string arguments but has no error return" | "cat 1>&2" + printf("\t_p%d, _ = BytePtrFromString(%s)\n", goarg, goname) + } args = sprintf("%s_p%d", args, goarg) } else if (gotype ~ /^\[\](.*)/) { if (ctype !~ /^\*/ || cargs[carg + 1] == "") { diff --git a/libgo/go/syscall/passfd_test.go b/libgo/go/syscall/passfd_test.go new file mode 100644 index 0000000..20ef39e --- /dev/null +++ b/libgo/go/syscall/passfd_test.go @@ -0,0 +1,151 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux darwin + +package syscall_test + +import ( + "flag" + "fmt" + "io/ioutil" + "net" + "os" + "os/exec" + "syscall" + "testing" + "time" +) + +// TestPassFD tests passing a file descriptor over a Unix socket. +// +// This test involved both a parent and child process. The parent +// process is invoked as a normal test, with "go test", which then +// runs the child process by running the current test binary with args +// "-test.run=^TestPassFD$" and an environment variable used to signal +// that the test should become the child process instead. +func TestPassFD(t *testing.T) { + if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { + passFDChild() + return + } + + tempDir, err := ioutil.TempDir("", "TestPassFD") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tempDir) + + fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0) + if err != nil { + t.Fatalf("Socketpair: %v", err) + } + defer syscall.Close(fds[0]) + defer syscall.Close(fds[1]) + writeFile := os.NewFile(uintptr(fds[0]), "child-writes") + readFile := os.NewFile(uintptr(fds[1]), "parent-reads") + defer writeFile.Close() + defer readFile.Close() + + cmd := exec.Command(os.Args[0], "-test.run=^TestPassFD$", "--", tempDir) + cmd.Env = append([]string{"GO_WANT_HELPER_PROCESS=1"}, os.Environ()...) + cmd.ExtraFiles = []*os.File{writeFile} + + out, err := cmd.CombinedOutput() + if len(out) > 0 || err != nil { + t.Fatalf("child process: %q, %v", out, err) + } + + c, err := net.FileConn(readFile) + if err != nil { + t.Fatalf("FileConn: %v", err) + } + defer c.Close() + + uc, ok := c.(*net.UnixConn) + if !ok { + t.Fatalf("unexpected FileConn type; expected UnixConn, got %T", c) + } + + buf := make([]byte, 32) // expect 1 byte + oob := make([]byte, 32) // expect 24 bytes + closeUnix := time.AfterFunc(5*time.Second, func() { + t.Logf("timeout reading from unix socket") + uc.Close() + }) + _, oobn, _, _, err := uc.ReadMsgUnix(buf, oob) + closeUnix.Stop() + + scms, err := syscall.ParseSocketControlMessage(oob[:oobn]) + if err != nil { + t.Fatalf("ParseSocketControlMessage: %v", err) + } + if len(scms) != 1 { + t.Fatalf("expected 1 SocketControlMessage; got scms = %#v", scms) + } + scm := scms[0] + gotFds, err := syscall.ParseUnixRights(&scm) + if err != nil { + t.Fatalf("syscall.ParseUnixRights: %v", err) + } + if len(gotFds) != 1 { + t.Fatalf("wanted 1 fd; got %#v", gotFds) + } + + f := os.NewFile(uintptr(gotFds[0]), "fd-from-child") + defer f.Close() + + got, err := ioutil.ReadAll(f) + want := "Hello from child process!\n" + if string(got) != want { + t.Errorf("child process ReadAll: %q, %v; want %q", got, err, want) + } +} + +// passFDChild is the child process used by TestPassFD. +func passFDChild() { + defer os.Exit(0) + + // Look for our fd. It should be fd 3, but we work around an fd leak + // bug here (http://golang.org/issue/2603) to let it be elsewhere. + var uc *net.UnixConn + for fd := uintptr(3); fd <= 10; fd++ { + f := os.NewFile(fd, "unix-conn") + var ok bool + netc, _ := net.FileConn(f) + uc, ok = netc.(*net.UnixConn) + if ok { + break + } + } + if uc == nil { + fmt.Println("failed to find unix fd") + return + } + + // Make a file f to send to our parent process on uc. + // We make it in tempDir, which our parent will clean up. + flag.Parse() + tempDir := flag.Arg(0) + f, err := ioutil.TempFile(tempDir, "") + if err != nil { + fmt.Printf("TempFile: %v", err) + return + } + + f.Write([]byte("Hello from child process!\n")) + f.Seek(0, 0) + + rights := syscall.UnixRights(int(f.Fd())) + dummyByte := []byte("x") + n, oobn, err := uc.WriteMsgUnix(dummyByte, rights, nil) + if err != nil { + fmt.Printf("WriteMsgUnix: %v", err) + return + } + if n != 1 || oobn != len(rights) { + fmt.Printf("WriteMsgUnix = %d, %d; want 1, %d", n, oobn, len(rights)) + return + } +} diff --git a/libgo/go/syscall/race0.go b/libgo/go/syscall/race0.go new file mode 100644 index 0000000..e94fb47 --- /dev/null +++ b/libgo/go/syscall/race0.go @@ -0,0 +1,19 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !race + +package syscall + +import ( + "unsafe" +) + +const raceenabled = false + +func raceAcquire(addr unsafe.Pointer) { +} + +func raceReleaseMerge(addr unsafe.Pointer) { +} diff --git a/libgo/go/syscall/security_windows.go b/libgo/go/syscall/security_windows.go index 4353af4..71aef2f 100644 --- a/libgo/go/syscall/security_windows.go +++ b/libgo/go/syscall/security_windows.go @@ -37,7 +37,7 @@ const ( // TranslateAccountName converts a directory service // object name from one format to another. func TranslateAccountName(username string, from, to uint32, initSize int) (string, error) { - u, e := utf16PtrFromString(username) + u, e := UTF16PtrFromString(username) if e != nil { return "", e } @@ -97,7 +97,7 @@ type SID struct{} // sid into a valid, functional sid. func StringToSid(s string) (*SID, error) { var sid *SID - p, e := utf16PtrFromString(s) + p, e := UTF16PtrFromString(s) if e != nil { return nil, e } @@ -116,13 +116,13 @@ func LookupSID(system, account string) (sid *SID, domain string, accType uint32, if len(account) == 0 { return nil, "", 0, EINVAL } - acc, e := utf16PtrFromString(account) + acc, e := UTF16PtrFromString(account) if e != nil { return nil, "", 0, e } var sys *uint16 if len(system) > 0 { - sys, e = utf16PtrFromString(system) + sys, e = UTF16PtrFromString(system) if e != nil { return nil, "", 0, e } @@ -183,7 +183,7 @@ func (sid *SID) Copy() (*SID, error) { func (sid *SID) LookupAccount(system string) (account, domain string, accType uint32, err error) { var sys *uint16 if len(system) > 0 { - sys, err = utf16PtrFromString(system) + sys, err = UTF16PtrFromString(system) if err != nil { return "", "", 0, err } diff --git a/libgo/go/syscall/syscall.go b/libgo/go/syscall/syscall.go index 3090a5e..56296c8 100644 --- a/libgo/go/syscall/syscall.go +++ b/libgo/go/syscall/syscall.go @@ -16,21 +16,21 @@ package syscall import "unsafe" -// StringByteSlice returns a NUL-terminated slice of bytes containing the text of s. +// StringByteSlice is deprecated. Use ByteSliceFromString instead. // If s contains a NUL byte this function panics instead of // returning an error. func StringByteSlice(s string) []byte { - a, err := byteSliceFromString(s) + a, err := ByteSliceFromString(s) if err != nil { panic("syscall: string with NUL passed to StringByteSlice") } return a } -// byteSliceFromString returns a NUL-terminated slice of bytes +// ByteSliceFromString returns a NUL-terminated slice of bytes // containing the text of s. If s contains a NUL byte at any // location, it returns (nil, EINVAL). -func byteSliceFromString(s string) ([]byte, error) { +func ByteSliceFromString(s string) ([]byte, error) { for i := 0; i < len(s); i++ { if s[i] == 0 { return nil, EINVAL @@ -41,16 +41,16 @@ func byteSliceFromString(s string) ([]byte, error) { return a, nil } -// StringBytePtr returns a pointer to a NUL-terminated array of bytes containing the text of s. +// StringBytePtr is deprecated. Use BytePtrFromString instead. // If s contains a NUL byte this function panics instead of // returning an error. func StringBytePtr(s string) *byte { return &StringByteSlice(s)[0] } -// bytePtrFromString returns a pointer to a NUL-terminated array of +// BytePtrFromString returns a pointer to a NUL-terminated array of // bytes containing the text of s. If s contains a NUL byte at any // location, it returns (nil, EINVAL). -func bytePtrFromString(s string) (*byte, error) { - a, err := byteSliceFromString(s) +func BytePtrFromString(s string) (*byte, error) { + a, err := ByteSliceFromString(s) if err != nil { return nil, err } diff --git a/libgo/go/syscall/syscall_unix.go b/libgo/go/syscall/syscall_unix.go index d4bff9e..fb62681 100644 --- a/libgo/go/syscall/syscall_unix.go +++ b/libgo/go/syscall/syscall_unix.go @@ -179,3 +179,20 @@ func Signame(s Signal) string func (s Signal) String() string { return Signame(s) } + +func Read(fd int, p []byte) (n int, err error) { + n, err = read(fd, p) + if raceenabled && err == nil { + raceAcquire(unsafe.Pointer(&ioSync)) + } + return +} + +func Write(fd int, p []byte) (n int, err error) { + if raceenabled { + raceReleaseMerge(unsafe.Pointer(&ioSync)) + } + return write(fd, p) +} + +var ioSync int64 |