mirror of
https://github.com/rwinkhart/go-winio.git
synced 2026-09-06 09:07:18 -04:00
Add file and pipe Windows wrappers
This includes Reader, Writer, and Closer interfaces on top of Windows file handles in a way that does not block the system thread while the IO is outstanding. This also includes net.Listener and net.Conn for named pipes.
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Microsoft
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# go-winio
|
||||||
|
|
||||||
|
This repository contains utilities for efficiently performing Win32 IO operations in
|
||||||
|
Go. Currently, this is focused on accessing named pipes.
|
||||||
|
|
||||||
|
Please see the LICENSE file for licensing information.
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx
|
||||||
|
//sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort
|
||||||
|
//sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
|
||||||
|
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
|
||||||
|
|
||||||
|
const (
|
||||||
|
fileSkipCompletionOnSuccess = 1
|
||||||
|
fileSkipSetEventOnHandle = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
var FileClosed = errors.New("File has already been closed.")
|
||||||
|
var ioInitOnce sync.Once
|
||||||
|
var ioCompletionPort syscall.Handle
|
||||||
|
|
||||||
|
// ioResult contains the result of an asynchronous IO operation
|
||||||
|
type ioResult struct {
|
||||||
|
bytes uint32
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ioOperation represents an outstanding asynchronous Win32 IO
|
||||||
|
type ioOperation struct {
|
||||||
|
o syscall.Overlapped
|
||||||
|
ch chan ioResult
|
||||||
|
}
|
||||||
|
|
||||||
|
func initIo() {
|
||||||
|
h, err := createIoCompletionPort(syscall.InvalidHandle, syscall.Handle(0), 0, 0xffffffff)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
ioCompletionPort = h
|
||||||
|
go ioCompletionProcessor(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
|
||||||
|
// It takes ownership of this handle and will close it if it is garbage collected.
|
||||||
|
type win32File struct {
|
||||||
|
handle syscall.Handle
|
||||||
|
wg sync.WaitGroup
|
||||||
|
closing bool
|
||||||
|
readDeadline time.Time
|
||||||
|
writeDeadline time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeWin32File makes a new win32File from an existing file handle
|
||||||
|
func makeWin32File(h syscall.Handle) (*win32File, error) {
|
||||||
|
f := &win32File{handle: h}
|
||||||
|
ioInitOnce.Do(initIo)
|
||||||
|
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = setFileCompletionNotificationModes(h, fileSkipCompletionOnSuccess|fileSkipSetEventOnHandle)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
runtime.SetFinalizer(f, (*win32File).closeHandle)
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
||||||
|
return makeWin32File(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// closeHandle closes the resources associated with a Win32 handle
|
||||||
|
func (f *win32File) closeHandle() {
|
||||||
|
if !f.closing {
|
||||||
|
// cancel all IO and wait for it to complete
|
||||||
|
f.closing = true
|
||||||
|
cancelIoEx(f.handle, nil)
|
||||||
|
f.wg.Wait()
|
||||||
|
// at this point, no new IO can start
|
||||||
|
syscall.CloseHandle(f.handle)
|
||||||
|
f.handle = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes a win32File.
|
||||||
|
func (f *win32File) Close() error {
|
||||||
|
f.closeHandle()
|
||||||
|
runtime.SetFinalizer(f, nil)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareIo prepares for a new IO operation
|
||||||
|
func (f *win32File) prepareIo() (*ioOperation, error) {
|
||||||
|
f.wg.Add(1)
|
||||||
|
if f.closing {
|
||||||
|
return nil, FileClosed
|
||||||
|
}
|
||||||
|
c := &ioOperation{}
|
||||||
|
c.ch = make(chan ioResult)
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ioCompletionProcessor processes completed async IOs forever
|
||||||
|
func ioCompletionProcessor(h syscall.Handle) {
|
||||||
|
for {
|
||||||
|
var bytes uint32
|
||||||
|
var key uintptr
|
||||||
|
var op *ioOperation
|
||||||
|
err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE)
|
||||||
|
if op == nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
op.ch <- ioResult{bytes, err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// asyncIo processes the return value from ReadFile or WriteFile, blocking until
|
||||||
|
// the operation has actually completed.
|
||||||
|
func (f *win32File) asyncIo(c *ioOperation, deadline time.Time, bytes uint32, err error) (int, error) {
|
||||||
|
if err != syscall.ERROR_IO_PENDING {
|
||||||
|
f.wg.Done()
|
||||||
|
return int(bytes), err
|
||||||
|
} else {
|
||||||
|
var r ioResult
|
||||||
|
wait := true
|
||||||
|
timedout := false
|
||||||
|
if f.closing {
|
||||||
|
cancelIoEx(f.handle, &c.o)
|
||||||
|
} else if !deadline.IsZero() {
|
||||||
|
now := time.Now()
|
||||||
|
if !deadline.After(now) {
|
||||||
|
timedout = true
|
||||||
|
} else {
|
||||||
|
timeout := time.After(deadline.Sub(now))
|
||||||
|
select {
|
||||||
|
case r = <-c.ch:
|
||||||
|
wait = false
|
||||||
|
case <-timeout:
|
||||||
|
timedout = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if timedout {
|
||||||
|
cancelIoEx(f.handle, &c.o)
|
||||||
|
}
|
||||||
|
if wait {
|
||||||
|
r = <-c.ch
|
||||||
|
if timedout && r.err == syscall.ERROR_OPERATION_ABORTED {
|
||||||
|
r.err = syscall.ETIMEDOUT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.wg.Done()
|
||||||
|
return int(r.bytes), r.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads from a file handle.
|
||||||
|
func (f *win32File) Read(b []byte) (int, error) {
|
||||||
|
c, err := f.prepareIo()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
var bytes uint32
|
||||||
|
err = syscall.ReadFile(f.handle, b, &bytes, &c.o)
|
||||||
|
n, err := f.asyncIo(c, f.readDeadline, bytes, err)
|
||||||
|
|
||||||
|
// Handle EOF conditions.
|
||||||
|
if err == nil && n == 0 && len(b) != 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
} else if err == syscall.ERROR_BROKEN_PIPE {
|
||||||
|
return 0, io.EOF
|
||||||
|
} else {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes to a file handle.
|
||||||
|
func (f *win32File) Write(b []byte) (int, error) {
|
||||||
|
c, err := f.prepareIo()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
var bytes uint32
|
||||||
|
err = syscall.WriteFile(f.handle, b, &bytes, &c.o)
|
||||||
|
return f.asyncIo(c, f.writeDeadline, bytes, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32File) SetReadDeadline(t time.Time) error {
|
||||||
|
f.readDeadline = t
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32File) SetWriteDeadline(t time.Time) error {
|
||||||
|
f.writeDeadline = t
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,797 @@
|
|||||||
|
// Copyright 2013 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 ignore
|
||||||
|
|
||||||
|
/*
|
||||||
|
mksyscall_windows generates windows system call bodies
|
||||||
|
|
||||||
|
It parses all files specified on command line containing function
|
||||||
|
prototypes (like syscall_windows.go) and prints system call bodies
|
||||||
|
to standard output.
|
||||||
|
|
||||||
|
The prototypes are marked by lines beginning with "//sys" and read
|
||||||
|
like func declarations if //sys is replaced by func, but:
|
||||||
|
|
||||||
|
* The parameter lists must give a name for each argument. This
|
||||||
|
includes return parameters.
|
||||||
|
|
||||||
|
* The parameter lists must give a type for each argument:
|
||||||
|
the (x, y, z int) shorthand is not allowed.
|
||||||
|
|
||||||
|
* If the return parameter is an error number, it must be named err.
|
||||||
|
|
||||||
|
* If go func name needs to be different from it's winapi dll name,
|
||||||
|
the winapi name could be specified at the end, after "=" sign, like
|
||||||
|
//sys LoadLibrary(libname string) (handle uint32, err error) = LoadLibraryA
|
||||||
|
|
||||||
|
* Each function that returns err needs to supply a condition, that
|
||||||
|
return value of winapi will be tested against to detect failure.
|
||||||
|
This would set err to windows "last-error", otherwise it will be nil.
|
||||||
|
The value can be provided at end of //sys declaration, like
|
||||||
|
//sys LoadLibrary(libname string) (handle uint32, err error) [failretval==-1] = LoadLibraryA
|
||||||
|
and is [failretval==0] by default.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
mksyscall_windows [flags] [path ...]
|
||||||
|
|
||||||
|
The flags are:
|
||||||
|
-output
|
||||||
|
Specify output file name (outputs to console if blank).
|
||||||
|
-trace
|
||||||
|
Generate print statement after every syscall.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"go/format"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
filename = flag.String("output", "", "output file name (standard output if omitted)")
|
||||||
|
printTraceFlag = flag.Bool("trace", false, "generate print statement after every syscall")
|
||||||
|
)
|
||||||
|
|
||||||
|
func trim(s string) string {
|
||||||
|
return strings.Trim(s, " \t")
|
||||||
|
}
|
||||||
|
|
||||||
|
var packageName string
|
||||||
|
|
||||||
|
func packagename() string {
|
||||||
|
return packageName
|
||||||
|
}
|
||||||
|
|
||||||
|
func syscalldot() string {
|
||||||
|
if packageName == "syscall" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return "syscall."
|
||||||
|
}
|
||||||
|
|
||||||
|
// Param is function parameter
|
||||||
|
type Param struct {
|
||||||
|
Name string
|
||||||
|
Type string
|
||||||
|
fn *Fn
|
||||||
|
tmpVarIdx int
|
||||||
|
}
|
||||||
|
|
||||||
|
// tmpVar returns temp variable name that will be used to represent p during syscall.
|
||||||
|
func (p *Param) tmpVar() string {
|
||||||
|
if p.tmpVarIdx < 0 {
|
||||||
|
p.tmpVarIdx = p.fn.curTmpVarIdx
|
||||||
|
p.fn.curTmpVarIdx++
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("_p%d", p.tmpVarIdx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolTmpVarCode returns source code for bool temp variable.
|
||||||
|
func (p *Param) BoolTmpVarCode() string {
|
||||||
|
const code = `var %s uint32
|
||||||
|
if %s {
|
||||||
|
%s = 1
|
||||||
|
} else {
|
||||||
|
%s = 0
|
||||||
|
}`
|
||||||
|
tmp := p.tmpVar()
|
||||||
|
return fmt.Sprintf(code, tmp, p.Name, tmp, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SliceTmpVarCode returns source code for slice temp variable.
|
||||||
|
func (p *Param) SliceTmpVarCode() string {
|
||||||
|
const code = `var %s *%s
|
||||||
|
if len(%s) > 0 {
|
||||||
|
%s = &%s[0]
|
||||||
|
}`
|
||||||
|
tmp := p.tmpVar()
|
||||||
|
return fmt.Sprintf(code, tmp, p.Type[2:], p.Name, tmp, p.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringTmpVarCode returns source code for string temp variable.
|
||||||
|
func (p *Param) StringTmpVarCode() string {
|
||||||
|
errvar := p.fn.Rets.ErrorVarName()
|
||||||
|
if errvar == "" {
|
||||||
|
errvar = "_"
|
||||||
|
}
|
||||||
|
tmp := p.tmpVar()
|
||||||
|
const code = `var %s %s
|
||||||
|
%s, %s = %s(%s)`
|
||||||
|
s := fmt.Sprintf(code, tmp, p.fn.StrconvType(), tmp, errvar, p.fn.StrconvFunc(), p.Name)
|
||||||
|
if errvar == "-" {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
const morecode = `
|
||||||
|
if %s != nil {
|
||||||
|
return
|
||||||
|
}`
|
||||||
|
return s + fmt.Sprintf(morecode, errvar)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TmpVarCode returns source code for temp variable.
|
||||||
|
func (p *Param) TmpVarCode() string {
|
||||||
|
switch {
|
||||||
|
case p.Type == "bool":
|
||||||
|
return p.BoolTmpVarCode()
|
||||||
|
case strings.HasPrefix(p.Type, "[]"):
|
||||||
|
return p.SliceTmpVarCode()
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TmpVarHelperCode returns source code for helper's temp variable.
|
||||||
|
func (p *Param) TmpVarHelperCode() string {
|
||||||
|
if p.Type != "string" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return p.StringTmpVarCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyscallArgList returns source code fragments representing p parameter
|
||||||
|
// in syscall. Slices are translated into 2 syscall parameters: pointer to
|
||||||
|
// the first element and length.
|
||||||
|
func (p *Param) SyscallArgList() []string {
|
||||||
|
t := p.HelperType()
|
||||||
|
var s string
|
||||||
|
switch {
|
||||||
|
case t[0] == '*':
|
||||||
|
s = fmt.Sprintf("unsafe.Pointer(%s)", p.Name)
|
||||||
|
case t == "bool":
|
||||||
|
s = p.tmpVar()
|
||||||
|
case strings.HasPrefix(t, "[]"):
|
||||||
|
return []string{
|
||||||
|
fmt.Sprintf("uintptr(unsafe.Pointer(%s))", p.tmpVar()),
|
||||||
|
fmt.Sprintf("uintptr(len(%s))", p.Name),
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
s = p.Name
|
||||||
|
}
|
||||||
|
return []string{fmt.Sprintf("uintptr(%s)", s)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsError determines if p parameter is used to return error.
|
||||||
|
func (p *Param) IsError() bool {
|
||||||
|
return p.Name == "err" && p.Type == "error"
|
||||||
|
}
|
||||||
|
|
||||||
|
// HelperType returns type of parameter p used in helper function.
|
||||||
|
func (p *Param) HelperType() string {
|
||||||
|
if p.Type == "string" {
|
||||||
|
return p.fn.StrconvType()
|
||||||
|
}
|
||||||
|
return p.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// join concatenates parameters ps into a string with sep separator.
|
||||||
|
// Each parameter is converted into string by applying fn to it
|
||||||
|
// before conversion.
|
||||||
|
func join(ps []*Param, fn func(*Param) string, sep string) string {
|
||||||
|
if len(ps) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
a := make([]string, 0)
|
||||||
|
for _, p := range ps {
|
||||||
|
a = append(a, fn(p))
|
||||||
|
}
|
||||||
|
return strings.Join(a, sep)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rets describes function return parameters.
|
||||||
|
type Rets struct {
|
||||||
|
Name string
|
||||||
|
Type string
|
||||||
|
ReturnsError bool
|
||||||
|
FailCond string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorVarName returns error variable name for r.
|
||||||
|
func (r *Rets) ErrorVarName() string {
|
||||||
|
if r.ReturnsError {
|
||||||
|
return "err"
|
||||||
|
}
|
||||||
|
if r.Type == "error" {
|
||||||
|
return r.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToParams converts r into slice of *Param.
|
||||||
|
func (r *Rets) ToParams() []*Param {
|
||||||
|
ps := make([]*Param, 0)
|
||||||
|
if len(r.Name) > 0 {
|
||||||
|
ps = append(ps, &Param{Name: r.Name, Type: r.Type})
|
||||||
|
}
|
||||||
|
if r.ReturnsError {
|
||||||
|
ps = append(ps, &Param{Name: "err", Type: "error"})
|
||||||
|
}
|
||||||
|
return ps
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns source code of syscall return parameters.
|
||||||
|
func (r *Rets) List() string {
|
||||||
|
s := join(r.ToParams(), func(p *Param) string { return p.Name + " " + p.Type }, ", ")
|
||||||
|
if len(s) > 0 {
|
||||||
|
s = "(" + s + ")"
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintList returns source code of trace printing part correspondent
|
||||||
|
// to syscall return values.
|
||||||
|
func (r *Rets) PrintList() string {
|
||||||
|
return join(r.ToParams(), func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReturnValuesCode returns source code that accepts syscall return values.
|
||||||
|
func (r *Rets) SetReturnValuesCode() string {
|
||||||
|
if r.Name == "" && !r.ReturnsError {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
retvar := "r0"
|
||||||
|
if r.Name == "" {
|
||||||
|
retvar = "r1"
|
||||||
|
}
|
||||||
|
errvar := "_"
|
||||||
|
if r.ReturnsError {
|
||||||
|
errvar = "e1"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s, _, %s := ", retvar, errvar)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Rets) useLongHandleErrorCode(retvar string) string {
|
||||||
|
const code = `if %s {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = %sEINVAL
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
cond := retvar + " == 0"
|
||||||
|
if r.FailCond != "" {
|
||||||
|
cond = strings.Replace(r.FailCond, "failretval", retvar, 1)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(code, cond, syscalldot())
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetErrorCode returns source code that sets return parameters.
|
||||||
|
func (r *Rets) SetErrorCode() string {
|
||||||
|
const code = `if r0 != 0 {
|
||||||
|
%s = %sErrno(r0)
|
||||||
|
}`
|
||||||
|
if r.Name == "" && !r.ReturnsError {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if r.Name == "" {
|
||||||
|
return r.useLongHandleErrorCode("r1")
|
||||||
|
}
|
||||||
|
if r.Type == "error" {
|
||||||
|
return fmt.Sprintf(code, r.Name, syscalldot())
|
||||||
|
}
|
||||||
|
s := ""
|
||||||
|
switch {
|
||||||
|
case r.Type[0] == '*':
|
||||||
|
s = fmt.Sprintf("%s = (%s)(unsafe.Pointer(r0))", r.Name, r.Type)
|
||||||
|
case r.Type == "bool":
|
||||||
|
s = fmt.Sprintf("%s = r0 != 0", r.Name)
|
||||||
|
default:
|
||||||
|
s = fmt.Sprintf("%s = %s(r0)", r.Name, r.Type)
|
||||||
|
}
|
||||||
|
if !r.ReturnsError {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return s + "\n\t" + r.useLongHandleErrorCode(r.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fn describes syscall function.
|
||||||
|
type Fn struct {
|
||||||
|
Name string
|
||||||
|
Params []*Param
|
||||||
|
Rets *Rets
|
||||||
|
PrintTrace bool
|
||||||
|
confirmproc bool
|
||||||
|
dllname string
|
||||||
|
dllfuncname string
|
||||||
|
src string
|
||||||
|
// TODO: get rid of this field and just use parameter index instead
|
||||||
|
curTmpVarIdx int // insure tmp variables have uniq names
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractParams parses s to extract function parameters.
|
||||||
|
func extractParams(s string, f *Fn) ([]*Param, error) {
|
||||||
|
s = trim(s)
|
||||||
|
if s == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
a := strings.Split(s, ",")
|
||||||
|
ps := make([]*Param, len(a))
|
||||||
|
for i := range ps {
|
||||||
|
s2 := trim(a[i])
|
||||||
|
b := strings.Split(s2, " ")
|
||||||
|
if len(b) != 2 {
|
||||||
|
b = strings.Split(s2, "\t")
|
||||||
|
if len(b) != 2 {
|
||||||
|
return nil, errors.New("Could not extract function parameter from \"" + s2 + "\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ps[i] = &Param{
|
||||||
|
Name: trim(b[0]),
|
||||||
|
Type: trim(b[1]),
|
||||||
|
fn: f,
|
||||||
|
tmpVarIdx: -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractSection extracts text out of string s starting after start
|
||||||
|
// and ending just before end. found return value will indicate success,
|
||||||
|
// and prefix, body and suffix will contain correspondent parts of string s.
|
||||||
|
func extractSection(s string, start, end rune) (prefix, body, suffix string, found bool) {
|
||||||
|
s = trim(s)
|
||||||
|
if strings.HasPrefix(s, string(start)) {
|
||||||
|
// no prefix
|
||||||
|
body = s[1:]
|
||||||
|
} else {
|
||||||
|
a := strings.SplitN(s, string(start), 2)
|
||||||
|
if len(a) != 2 {
|
||||||
|
return "", "", s, false
|
||||||
|
}
|
||||||
|
prefix = a[0]
|
||||||
|
body = a[1]
|
||||||
|
}
|
||||||
|
a := strings.SplitN(body, string(end), 2)
|
||||||
|
if len(a) != 2 {
|
||||||
|
return "", "", "", false
|
||||||
|
}
|
||||||
|
return prefix, a[0], a[1], true
|
||||||
|
}
|
||||||
|
|
||||||
|
// newFn parses string s and return created function Fn.
|
||||||
|
func newFn(s string) (*Fn, error) {
|
||||||
|
s = trim(s)
|
||||||
|
f := &Fn{
|
||||||
|
Rets: &Rets{},
|
||||||
|
src: s,
|
||||||
|
PrintTrace: *printTraceFlag,
|
||||||
|
}
|
||||||
|
// function name and args
|
||||||
|
prefix, body, s, found := extractSection(s, '(', ')')
|
||||||
|
if !found || prefix == "" {
|
||||||
|
return nil, errors.New("Could not extract function name and parameters from \"" + f.src + "\"")
|
||||||
|
}
|
||||||
|
f.Name = prefix
|
||||||
|
var err error
|
||||||
|
f.Params, err = extractParams(body, f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// return values
|
||||||
|
_, body, s, found = extractSection(s, '(', ')')
|
||||||
|
if found {
|
||||||
|
r, err := extractParams(body, f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch len(r) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
if r[0].IsError() {
|
||||||
|
f.Rets.ReturnsError = true
|
||||||
|
} else {
|
||||||
|
f.Rets.Name = r[0].Name
|
||||||
|
f.Rets.Type = r[0].Type
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
if !r[1].IsError() {
|
||||||
|
return nil, errors.New("Only last windows error is allowed as second return value in \"" + f.src + "\"")
|
||||||
|
}
|
||||||
|
f.Rets.ReturnsError = true
|
||||||
|
f.Rets.Name = r[0].Name
|
||||||
|
f.Rets.Type = r[0].Type
|
||||||
|
default:
|
||||||
|
return nil, errors.New("Too many return values in \"" + f.src + "\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// fail condition
|
||||||
|
_, body, s, found = extractSection(s, '[', ']')
|
||||||
|
if found {
|
||||||
|
f.Rets.FailCond = body
|
||||||
|
}
|
||||||
|
// dll and dll function names
|
||||||
|
s = trim(s)
|
||||||
|
if s == "" {
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(s, "=") {
|
||||||
|
return nil, errors.New("Could not extract dll name from \"" + f.src + "\"")
|
||||||
|
}
|
||||||
|
s = trim(s[1:])
|
||||||
|
a := strings.Split(s, ".")
|
||||||
|
switch len(a) {
|
||||||
|
case 1:
|
||||||
|
f.dllfuncname = a[0]
|
||||||
|
case 2:
|
||||||
|
f.dllname = a[0]
|
||||||
|
f.dllfuncname = a[1]
|
||||||
|
default:
|
||||||
|
return nil, errors.New("Could not extract dll name from \"" + f.src + "\"")
|
||||||
|
}
|
||||||
|
if f.dllfuncname[len(f.dllfuncname)-1] == '?' {
|
||||||
|
f.confirmproc = true
|
||||||
|
f.dllfuncname = f.dllfuncname[0 : len(f.dllfuncname)-1]
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DLLName returns DLL name for function f.
|
||||||
|
func (f *Fn) DLLName() string {
|
||||||
|
if f.dllname == "" {
|
||||||
|
return "kernel32"
|
||||||
|
}
|
||||||
|
return f.dllname
|
||||||
|
}
|
||||||
|
|
||||||
|
// DLLName returns DLL function name for function f.
|
||||||
|
func (f *Fn) DLLFuncName() string {
|
||||||
|
if f.dllfuncname == "" {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
return f.dllfuncname
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fn) ConfirmProc() bool {
|
||||||
|
return f.confirmproc
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParamList returns source code for function f parameters.
|
||||||
|
func (f *Fn) ParamList() string {
|
||||||
|
return join(f.Params, func(p *Param) string { return p.Name + " " + p.Type }, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// HelperParamList returns source code for helper function f parameters.
|
||||||
|
func (f *Fn) HelperParamList() string {
|
||||||
|
return join(f.Params, func(p *Param) string { return p.Name + " " + p.HelperType() }, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParamPrintList returns source code of trace printing part correspondent
|
||||||
|
// to syscall input parameters.
|
||||||
|
func (f *Fn) ParamPrintList() string {
|
||||||
|
return join(f.Params, func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParamCount return number of syscall parameters for function f.
|
||||||
|
func (f *Fn) ParamCount() int {
|
||||||
|
n := 0
|
||||||
|
for _, p := range f.Params {
|
||||||
|
n += len(p.SyscallArgList())
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyscallParamCount determines which version of Syscall/Syscall6/Syscall9/...
|
||||||
|
// to use. It returns parameter count for correspondent SyscallX function.
|
||||||
|
func (f *Fn) SyscallParamCount() int {
|
||||||
|
n := f.ParamCount()
|
||||||
|
switch {
|
||||||
|
case n <= 3:
|
||||||
|
return 3
|
||||||
|
case n <= 6:
|
||||||
|
return 6
|
||||||
|
case n <= 9:
|
||||||
|
return 9
|
||||||
|
case n <= 12:
|
||||||
|
return 12
|
||||||
|
case n <= 15:
|
||||||
|
return 15
|
||||||
|
default:
|
||||||
|
panic("too many arguments to system call")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Syscall determines which SyscallX function to use for function f.
|
||||||
|
func (f *Fn) Syscall() string {
|
||||||
|
c := f.SyscallParamCount()
|
||||||
|
if c == 3 {
|
||||||
|
return syscalldot() + "Syscall"
|
||||||
|
}
|
||||||
|
return syscalldot() + "Syscall" + strconv.Itoa(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyscallParamList returns source code for SyscallX parameters for function f.
|
||||||
|
func (f *Fn) SyscallParamList() string {
|
||||||
|
a := make([]string, 0)
|
||||||
|
for _, p := range f.Params {
|
||||||
|
a = append(a, p.SyscallArgList()...)
|
||||||
|
}
|
||||||
|
for len(a) < f.SyscallParamCount() {
|
||||||
|
a = append(a, "0")
|
||||||
|
}
|
||||||
|
return strings.Join(a, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// HelperCallParamList returns source code of call into function f helper.
|
||||||
|
func (f *Fn) HelperCallParamList() string {
|
||||||
|
a := make([]string, 0, len(f.Params))
|
||||||
|
for _, p := range f.Params {
|
||||||
|
s := p.Name
|
||||||
|
if p.Type == "string" {
|
||||||
|
s = p.tmpVar()
|
||||||
|
}
|
||||||
|
a = append(a, s)
|
||||||
|
}
|
||||||
|
return strings.Join(a, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUTF16 is true, if f is W (utf16) function. It is false
|
||||||
|
// for all A (ascii) functions.
|
||||||
|
func (_ *Fn) IsUTF16() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// StrconvFunc returns name of Go string to OS string function for f.
|
||||||
|
func (f *Fn) StrconvFunc() string {
|
||||||
|
if f.IsUTF16() {
|
||||||
|
return syscalldot() + "UTF16PtrFromString"
|
||||||
|
}
|
||||||
|
return syscalldot() + "BytePtrFromString"
|
||||||
|
}
|
||||||
|
|
||||||
|
// StrconvType returns Go type name used for OS string for f.
|
||||||
|
func (f *Fn) StrconvType() string {
|
||||||
|
if f.IsUTF16() {
|
||||||
|
return "*uint16"
|
||||||
|
}
|
||||||
|
return "*byte"
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasStringParam is true, if f has at least one string parameter.
|
||||||
|
// Otherwise it is false.
|
||||||
|
func (f *Fn) HasStringParam() bool {
|
||||||
|
for _, p := range f.Params {
|
||||||
|
if p.Type == "string" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// HelperName returns name of function f helper.
|
||||||
|
func (f *Fn) HelperName() string {
|
||||||
|
if !f.HasStringParam() {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
return "_" + f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Source files and functions.
|
||||||
|
type Source struct {
|
||||||
|
Funcs []*Fn
|
||||||
|
Files []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseFiles parses files listed in fs and extracts all syscall
|
||||||
|
// functions listed in sys comments. It returns source files
|
||||||
|
// and functions collection *Source if successful.
|
||||||
|
func ParseFiles(fs []string) (*Source, error) {
|
||||||
|
src := &Source{
|
||||||
|
Funcs: make([]*Fn, 0),
|
||||||
|
Files: make([]string, 0),
|
||||||
|
}
|
||||||
|
for _, file := range fs {
|
||||||
|
if err := src.ParseFile(file); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return src, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DLLs return dll names for a source set src.
|
||||||
|
func (src *Source) DLLs() []string {
|
||||||
|
uniq := make(map[string]bool)
|
||||||
|
r := make([]string, 0)
|
||||||
|
for _, f := range src.Funcs {
|
||||||
|
name := f.DLLName()
|
||||||
|
if _, found := uniq[name]; !found {
|
||||||
|
uniq[name] = true
|
||||||
|
r = append(r, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseFile adds additional file path to a source set src.
|
||||||
|
func (src *Source) ParseFile(path string) error {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
s := bufio.NewScanner(file)
|
||||||
|
for s.Scan() {
|
||||||
|
t := trim(s.Text())
|
||||||
|
if len(t) < 7 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(t, "//sys") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t = t[5:]
|
||||||
|
if !(t[0] == ' ' || t[0] == '\t') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
f, err := newFn(t[1:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
src.Funcs = append(src.Funcs, f)
|
||||||
|
}
|
||||||
|
if err := s.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
src.Files = append(src.Files, path)
|
||||||
|
|
||||||
|
// get package name
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
_, err = file.Seek(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pkg, err := parser.ParseFile(fset, "", file, parser.PackageClauseOnly)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
packageName = pkg.Name.Name
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate output source file from a source set src.
|
||||||
|
func (src *Source) Generate(w io.Writer) error {
|
||||||
|
funcMap := template.FuncMap{
|
||||||
|
"packagename": packagename,
|
||||||
|
"syscalldot": syscalldot,
|
||||||
|
}
|
||||||
|
t := template.Must(template.New("main").Funcs(funcMap).Parse(srcTemplate))
|
||||||
|
err := t.Execute(w, src)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("Failed to execute template: " + err.Error())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func usage() {
|
||||||
|
fmt.Fprintf(os.Stderr, "usage: mksyscall_windows [flags] [path ...]\n")
|
||||||
|
flag.PrintDefaults()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Usage = usage
|
||||||
|
flag.Parse()
|
||||||
|
if len(flag.Args()) <= 0 {
|
||||||
|
fmt.Fprintf(os.Stderr, "no files to parse provided\n")
|
||||||
|
usage()
|
||||||
|
}
|
||||||
|
|
||||||
|
src, err := ParseFiles(flag.Args())
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := src.Generate(&buf); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := format.Source(buf.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if *filename == "" {
|
||||||
|
_, err = os.Stdout.Write(data)
|
||||||
|
} else {
|
||||||
|
err = ioutil.WriteFile(*filename, data, 0644)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: use println instead to print in the following template
|
||||||
|
const srcTemplate = `
|
||||||
|
|
||||||
|
{{define "main"}}// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
|
||||||
|
|
||||||
|
package {{packagename}}
|
||||||
|
|
||||||
|
import "unsafe"{{if syscalldot}}
|
||||||
|
import "syscall"{{end}}
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
var (
|
||||||
|
{{template "dlls" .}}
|
||||||
|
{{template "funcnames" .}})
|
||||||
|
{{range .Funcs}}{{if .HasStringParam}}{{template "helperbody" .}}{{end}}{{template "funcbody" .}}{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{/* help functions */}}
|
||||||
|
|
||||||
|
{{define "dlls"}}{{range .DLLs}} mod{{.}} = {{syscalldot}}NewLazyDLL("{{.}}.dll")
|
||||||
|
{{end}}{{end}}
|
||||||
|
|
||||||
|
{{define "funcnames"}}{{range .Funcs}} proc{{.DLLFuncName}} = mod{{.DLLName}}.NewProc("{{.DLLFuncName}}")
|
||||||
|
{{end}}{{end}}
|
||||||
|
|
||||||
|
{{define "helperbody"}}
|
||||||
|
func {{.Name}}({{.ParamList}}) {{template "results" .}}{
|
||||||
|
{{template "helpertmpvars" .}} return {{.HelperName}}({{.HelperCallParamList}})
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{define "funcbody"}}
|
||||||
|
func {{.HelperName}}({{.HelperParamList}}) {{template "results" .}}{
|
||||||
|
{{template "tmpvars" .}} {{template "syscallcheck" .}}{{template "syscall" .}}
|
||||||
|
{{template "seterror" .}}{{template "printtrace" .}} return
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{define "helpertmpvars"}}{{range .Params}}{{if .TmpVarHelperCode}} {{.TmpVarHelperCode}}
|
||||||
|
{{end}}{{end}}{{end}}
|
||||||
|
|
||||||
|
{{define "tmpvars"}}{{range .Params}}{{if .TmpVarCode}} {{.TmpVarCode}}
|
||||||
|
{{end}}{{end}}{{end}}
|
||||||
|
|
||||||
|
{{define "results"}}{{if .Rets.List}}{{.Rets.List}} {{end}}{{end}}
|
||||||
|
|
||||||
|
{{define "syscallcheck"}}{{if .ConfirmProc}}if {{.Rets.ErrorVarName}} = proc{{.DLLFuncName}}.Find(); {{.Rets.ErrorVarName}} != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
{{end}}{{end}}
|
||||||
|
|
||||||
|
{{define "syscall"}}{{.Rets.SetReturnValuesCode}}{{.Syscall}}(proc{{.DLLFuncName}}.Addr(), {{.ParamCount}}, {{.SyscallParamList}}){{end}}
|
||||||
|
|
||||||
|
{{define "seterror"}}{{if .Rets.SetErrorCode}} {{.Rets.SetErrorCode}}
|
||||||
|
{{end}}{{end}}
|
||||||
|
|
||||||
|
{{define "printtrace"}}{{if .PrintTrace}} print("SYSCALL: {{.Name}}(", {{.ParamPrintList}}") (", {{.Rets.PrintList}}")\n")
|
||||||
|
{{end}}{{end}}
|
||||||
|
|
||||||
|
`
|
||||||
@@ -0,0 +1,249 @@
|
|||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
|
||||||
|
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
|
||||||
|
//sys createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW
|
||||||
|
//sys waitNamedPipe(name string, timeout uint32) (err error) = WaitNamedPipeW
|
||||||
|
//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
|
||||||
|
//sys localFree(mem uintptr) = LocalFree
|
||||||
|
|
||||||
|
const (
|
||||||
|
cERROR_PIPE_BUSY = syscall.Errno(231)
|
||||||
|
cERROR_PIPE_CONNECTED = syscall.Errno(535)
|
||||||
|
cERROR_SEM_TIMEOUT = syscall.Errno(121)
|
||||||
|
|
||||||
|
pipeFlagAccessDuplex = 0x3
|
||||||
|
pipeFlagFirstInstance = 0x80000
|
||||||
|
|
||||||
|
pipeModeRejectRemoteClients = 0x8
|
||||||
|
|
||||||
|
pipeUnlimitedInstances = 255
|
||||||
|
)
|
||||||
|
|
||||||
|
type win32Pipe struct {
|
||||||
|
*win32File
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
type pipeAddress string
|
||||||
|
|
||||||
|
func (f *win32Pipe) LocalAddr() net.Addr {
|
||||||
|
return pipeAddress(f.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32Pipe) RemoteAddr() net.Addr {
|
||||||
|
return pipeAddress(f.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32Pipe) SetDeadline(t time.Time) error {
|
||||||
|
f.SetReadDeadline(t)
|
||||||
|
f.SetWriteDeadline(t)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s pipeAddress) Network() string {
|
||||||
|
return "pipe"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s pipeAddress) String() string {
|
||||||
|
return string(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeWin32Pipe(h syscall.Handle, path string) (*win32Pipe, error) {
|
||||||
|
f, err := makeWin32File(h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &win32Pipe{f, path}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DialPipe(s string, timeout *time.Duration) (net.Conn, error) {
|
||||||
|
var absTimeout time.Time
|
||||||
|
if timeout != nil {
|
||||||
|
absTimeout = time.Now().Add(*timeout)
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
var h syscall.Handle
|
||||||
|
for {
|
||||||
|
h, err = createFile(s, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED, 0)
|
||||||
|
if err != cERROR_PIPE_BUSY {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
var ms uint32
|
||||||
|
if absTimeout.IsZero() {
|
||||||
|
ms = syscall.INFINITE
|
||||||
|
} else if now.After(absTimeout) {
|
||||||
|
ms = 1
|
||||||
|
} else {
|
||||||
|
ms = uint32(absTimeout.Sub(now).Nanoseconds() / 1000 / 1000)
|
||||||
|
}
|
||||||
|
err = waitNamedPipe(s, ms)
|
||||||
|
if err != nil {
|
||||||
|
if err == cERROR_SEM_TIMEOUT {
|
||||||
|
return nil, syscall.ETIMEDOUT
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, &os.PathError{"open", s, err}
|
||||||
|
}
|
||||||
|
p, err := makeWin32Pipe(h, s)
|
||||||
|
if err != nil {
|
||||||
|
syscall.CloseHandle(h)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type acceptResponse struct {
|
||||||
|
p *win32Pipe
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type win32PipeListener struct {
|
||||||
|
firstHandle syscall.Handle
|
||||||
|
path string
|
||||||
|
securityDescriptor string
|
||||||
|
closeCh chan (chan int)
|
||||||
|
acceptCh chan (chan acceptResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeServerPipeHandle(path, securityDescriptor string, first bool) (syscall.Handle, error) {
|
||||||
|
var flags uint32 = pipeFlagAccessDuplex | syscall.FILE_FLAG_OVERLAPPED
|
||||||
|
if first {
|
||||||
|
flags |= pipeFlagFirstInstance
|
||||||
|
}
|
||||||
|
var sd uintptr
|
||||||
|
if securityDescriptor != "" {
|
||||||
|
err := convertStringSecurityDescriptorToSecurityDescriptor(securityDescriptor, 1, &sd, nil)
|
||||||
|
if err != nil {
|
||||||
|
return syscall.Handle(0), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var sa syscall.SecurityAttributes
|
||||||
|
sa.Length = uint32(unsafe.Sizeof(sa))
|
||||||
|
sa.SecurityDescriptor = sd
|
||||||
|
h, err := createNamedPipe(path, flags, pipeModeRejectRemoteClients, pipeUnlimitedInstances, 4096, 4096, 0, &sa)
|
||||||
|
if sd != 0 {
|
||||||
|
localFree(sd)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return syscall.Handle(0), &os.PathError{"open", path, err}
|
||||||
|
}
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) makeServerPipe() (*win32Pipe, error) {
|
||||||
|
h, err := makeServerPipeHandle(l.path, l.securityDescriptor, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p, err := makeWin32Pipe(h, l.path)
|
||||||
|
if err != nil {
|
||||||
|
syscall.CloseHandle(h)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) listenerRoutine() {
|
||||||
|
var closeResponseCh (chan int)
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
var responseCh (chan acceptResponse)
|
||||||
|
select {
|
||||||
|
case closeResponseCh = <-l.closeCh:
|
||||||
|
break Loop
|
||||||
|
case responseCh = <-l.acceptCh:
|
||||||
|
}
|
||||||
|
p, err := l.makeServerPipe()
|
||||||
|
if err != nil {
|
||||||
|
responseCh <- acceptResponse{nil, err}
|
||||||
|
} else {
|
||||||
|
ch := make(chan error)
|
||||||
|
go func() {
|
||||||
|
ch <- connectPipe(p)
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case closeResponseCh = <-l.closeCh:
|
||||||
|
p.Close()
|
||||||
|
<-ch
|
||||||
|
break Loop
|
||||||
|
case err = <-ch:
|
||||||
|
if err != nil {
|
||||||
|
p.Close()
|
||||||
|
p = nil
|
||||||
|
}
|
||||||
|
responseCh <- acceptResponse{p, err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syscall.CloseHandle(l.firstHandle)
|
||||||
|
l.firstHandle = syscall.Handle(0)
|
||||||
|
closeResponseCh <- 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListenPipe(path, securityDescriptor string) (net.Listener, error) {
|
||||||
|
h, err := makeServerPipeHandle(path, securityDescriptor, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Immediately open and then close a client handle so that the named pipe is
|
||||||
|
// created but not currently accepting connections.
|
||||||
|
h2, err := createFile(path, 0, 0, nil, syscall.OPEN_EXISTING, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
syscall.CloseHandle(h)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
syscall.CloseHandle(h2)
|
||||||
|
l := &win32PipeListener{
|
||||||
|
firstHandle: h,
|
||||||
|
path: path,
|
||||||
|
securityDescriptor: securityDescriptor,
|
||||||
|
closeCh: make(chan (chan int)),
|
||||||
|
acceptCh: make(chan (chan acceptResponse)),
|
||||||
|
}
|
||||||
|
go l.listenerRoutine()
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectPipe(p *win32Pipe) error {
|
||||||
|
c, err := p.prepareIo()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = connectNamedPipe(p.handle, &c.o)
|
||||||
|
_, err = p.asyncIo(c, time.Time{}, 0, err)
|
||||||
|
if err != nil && err != cERROR_PIPE_CONNECTED {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) Accept() (net.Conn, error) {
|
||||||
|
ch := make(chan acceptResponse)
|
||||||
|
l.acceptCh <- ch
|
||||||
|
response := <-ch
|
||||||
|
return response.p, response.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) Close() error {
|
||||||
|
ch := make(chan int)
|
||||||
|
l.closeCh <- ch
|
||||||
|
<-ch
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) Addr() net.Addr {
|
||||||
|
return pipeAddress(l.path)
|
||||||
|
}
|
||||||
+148
@@ -0,0 +1,148 @@
|
|||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testPipeName = `\\.\pipe\winiotestpipe`
|
||||||
|
|
||||||
|
func TestDialUnknownFailsImmediately(t *testing.T) {
|
||||||
|
_, err := DialPipe(testPipeName, nil)
|
||||||
|
if err.(*os.PathError).Err != syscall.ENOENT {
|
||||||
|
t.Fatalf("expected ENOENT got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDialListenerTimesOut(t *testing.T) {
|
||||||
|
l, err := ListenPipe(testPipeName, "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
var d = time.Duration(10 * time.Millisecond)
|
||||||
|
_, err = DialPipe(testPipeName, &d)
|
||||||
|
if err != syscall.ETIMEDOUT {
|
||||||
|
t.Fatalf("expected ETIMEDOUT, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDialAccessDeniedWithRestrictedSD(t *testing.T) {
|
||||||
|
l, err := ListenPipe(testPipeName, "D:P(A;;0x1200FF;;;WD)")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
_, err = DialPipe(testPipeName, nil)
|
||||||
|
if err.(*os.PathError).Err != syscall.ERROR_ACCESS_DENIED {
|
||||||
|
t.Fatalf("expected EACCES, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConnection() (client net.Conn, server net.Conn, err error) {
|
||||||
|
l, err := ListenPipe(testPipeName, "")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
type response struct {
|
||||||
|
c net.Conn
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
ch := make(chan response)
|
||||||
|
go func() {
|
||||||
|
c, err := l.Accept()
|
||||||
|
ch <- response{c, err}
|
||||||
|
}()
|
||||||
|
|
||||||
|
c, err := DialPipe(testPipeName, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r := <-ch
|
||||||
|
if err = r.err; err != nil {
|
||||||
|
c.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
client = c
|
||||||
|
server = r.c
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadTimeout(t *testing.T) {
|
||||||
|
c, s, err := getConnection()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
c.SetReadDeadline(time.Now().Add(10 * time.Millisecond))
|
||||||
|
|
||||||
|
buf := make([]byte, 10)
|
||||||
|
_, err = c.Read(buf)
|
||||||
|
if err != syscall.ETIMEDOUT {
|
||||||
|
t.Fatalf("expected ETIMEDOUT, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func server(l net.Listener) {
|
||||||
|
c, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
rw := bufio.NewReadWriter(bufio.NewReader(c), bufio.NewWriter(c))
|
||||||
|
s, err := rw.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
_, err = rw.WriteString("got " + s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = rw.Flush()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFullListenDialReadWrite(t *testing.T) {
|
||||||
|
l, err := ListenPipe(testPipeName, "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go server(l)
|
||||||
|
|
||||||
|
c, err := DialPipe(testPipeName, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rw := bufio.NewReadWriter(bufio.NewReader(c), bufio.NewWriter(c))
|
||||||
|
_, err = rw.WriteString("hello world\n")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = rw.Flush()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := rw.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
ms := "got hello world\n"
|
||||||
|
if s != ms {
|
||||||
|
t.Errorf("expected '%s', got '%s'", ms, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package winio
|
||||||
|
|
||||||
|
//go:generate go run mksyscall_windows.go -output zsyscall.go file.go pipe.go
|
||||||
+176
@@ -0,0 +1,176 @@
|
|||||||
|
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
var (
|
||||||
|
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
modadvapi32 = syscall.NewLazyDLL("advapi32.dll")
|
||||||
|
|
||||||
|
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
|
||||||
|
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
||||||
|
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
|
||||||
|
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
|
||||||
|
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
|
||||||
|
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
|
||||||
|
procCreateFileW = modkernel32.NewProc("CreateFileW")
|
||||||
|
procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW")
|
||||||
|
procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW")
|
||||||
|
procLocalFree = modkernel32.NewProc("LocalFree")
|
||||||
|
)
|
||||||
|
|
||||||
|
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0)
|
||||||
|
newport = syscall.Handle(r0)
|
||||||
|
if newport == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0)
|
||||||
|
handle = syscall.Handle(r0)
|
||||||
|
if handle == syscall.InvalidHandle {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
|
||||||
|
handle = syscall.Handle(r0)
|
||||||
|
if handle == syscall.InvalidHandle {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitNamedPipe(name string, timeout uint32) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _waitNamedPipe(_p0, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _waitNamedPipe(name *uint16, timeout uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(str)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func localFree(mem uintptr) {
|
||||||
|
syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user