mirror of https://github.com/ethereum/go-ethereum
Merge pull request #2417 from fjl/rpc-go-winio
rpc: use go-winio instead of vendoring a named pipe implementationpull/2423/head
commit
7e02105672
@ -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,15 @@ |
|||||||
|
# go-winio |
||||||
|
|
||||||
|
This repository contains utilities for efficiently performing Win32 IO operations in |
||||||
|
Go. Currently, this is focused on accessing named pipes and other file handles, and |
||||||
|
for using named pipes as a net transport. |
||||||
|
|
||||||
|
This code relies on IO completion ports to avoid blocking IO on system threads, allowing Go |
||||||
|
to reuse the thread to schedule another goroutine. This limits support to Windows Vista and |
||||||
|
newer operating systems. This is similar to the implementation of network sockets in Go's net |
||||||
|
package. |
||||||
|
|
||||||
|
Please see the LICENSE file for licensing information. |
||||||
|
|
||||||
|
Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe |
||||||
|
for another named pipe implementation. |
@ -0,0 +1,266 @@ |
|||||||
|
package winio |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/binary" |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"io/ioutil" |
||||||
|
"os" |
||||||
|
"runtime" |
||||||
|
"syscall" |
||||||
|
"unicode/utf16" |
||||||
|
) |
||||||
|
|
||||||
|
//sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
|
||||||
|
//sys backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
|
||||||
|
|
||||||
|
const ( |
||||||
|
BackupData = uint32(iota + 1) |
||||||
|
BackupEaData |
||||||
|
BackupSecurity |
||||||
|
BackupAlternateData |
||||||
|
BackupLink |
||||||
|
BackupPropertyData |
||||||
|
BackupObjectId |
||||||
|
BackupReparseData |
||||||
|
BackupSparseBlock |
||||||
|
BackupTxfsData |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
StreamSparseAttributes = uint32(8) |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
WRITE_DAC = 0x40000 |
||||||
|
WRITE_OWNER = 0x80000 |
||||||
|
ACCESS_SYSTEM_SECURITY = 0x1000000 |
||||||
|
) |
||||||
|
|
||||||
|
// BackupHeader represents a backup stream of a file.
|
||||||
|
type BackupHeader struct { |
||||||
|
Id uint32 // The backup stream ID
|
||||||
|
Attributes uint32 // Stream attributes
|
||||||
|
Size int64 // The size of the stream in bytes
|
||||||
|
Name string // The name of the stream (for BackupAlternateData only).
|
||||||
|
Offset int64 // The offset of the stream in the file (for BackupSparseBlock only).
|
||||||
|
} |
||||||
|
|
||||||
|
type win32StreamId struct { |
||||||
|
StreamId uint32 |
||||||
|
Attributes uint32 |
||||||
|
Size uint64 |
||||||
|
NameSize uint32 |
||||||
|
} |
||||||
|
|
||||||
|
// BackupStreamReader reads from a stream produced by the BackupRead Win32 API and produces a series
|
||||||
|
// of BackupHeader values.
|
||||||
|
type BackupStreamReader struct { |
||||||
|
r io.Reader |
||||||
|
bytesLeft int64 |
||||||
|
} |
||||||
|
|
||||||
|
// NewBackupStreamReader produces a BackupStreamReader from any io.Reader.
|
||||||
|
func NewBackupStreamReader(r io.Reader) *BackupStreamReader { |
||||||
|
return &BackupStreamReader{r, 0} |
||||||
|
} |
||||||
|
|
||||||
|
// Next returns the next backup stream and prepares for calls to Write(). It skips the remainder of the current stream if
|
||||||
|
// it was not completely read.
|
||||||
|
func (r *BackupStreamReader) Next() (*BackupHeader, error) { |
||||||
|
if r.bytesLeft > 0 { |
||||||
|
if _, err := io.Copy(ioutil.Discard, r); err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
} |
||||||
|
var wsi win32StreamId |
||||||
|
if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
hdr := &BackupHeader{ |
||||||
|
Id: wsi.StreamId, |
||||||
|
Attributes: wsi.Attributes, |
||||||
|
Size: int64(wsi.Size), |
||||||
|
} |
||||||
|
if wsi.NameSize != 0 { |
||||||
|
name := make([]uint16, int(wsi.NameSize/2)) |
||||||
|
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
hdr.Name = syscall.UTF16ToString(name) |
||||||
|
} |
||||||
|
if wsi.StreamId == BackupSparseBlock { |
||||||
|
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
hdr.Size -= 8 |
||||||
|
} |
||||||
|
r.bytesLeft = hdr.Size |
||||||
|
return hdr, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Read reads from the current backup stream.
|
||||||
|
func (r *BackupStreamReader) Read(b []byte) (int, error) { |
||||||
|
if r.bytesLeft == 0 { |
||||||
|
return 0, io.EOF |
||||||
|
} |
||||||
|
if int64(len(b)) > r.bytesLeft { |
||||||
|
b = b[:r.bytesLeft] |
||||||
|
} |
||||||
|
n, err := r.r.Read(b) |
||||||
|
r.bytesLeft -= int64(n) |
||||||
|
if err == io.EOF { |
||||||
|
err = io.ErrUnexpectedEOF |
||||||
|
} else if r.bytesLeft == 0 && err == nil { |
||||||
|
err = io.EOF |
||||||
|
} |
||||||
|
return n, err |
||||||
|
} |
||||||
|
|
||||||
|
// BackupStreamWriter writes a stream compatible with the BackupWrite Win32 API.
|
||||||
|
type BackupStreamWriter struct { |
||||||
|
w io.Writer |
||||||
|
bytesLeft int64 |
||||||
|
} |
||||||
|
|
||||||
|
// NewBackupStreamWriter produces a BackupStreamWriter on top of an io.Writer.
|
||||||
|
func NewBackupStreamWriter(w io.Writer) *BackupStreamWriter { |
||||||
|
return &BackupStreamWriter{w, 0} |
||||||
|
} |
||||||
|
|
||||||
|
// WriteHeader writes the next backup stream header and prepares for calls to Write().
|
||||||
|
func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error { |
||||||
|
if w.bytesLeft != 0 { |
||||||
|
return fmt.Errorf("missing %d bytes", w.bytesLeft) |
||||||
|
} |
||||||
|
name := utf16.Encode([]rune(hdr.Name)) |
||||||
|
wsi := win32StreamId{ |
||||||
|
StreamId: hdr.Id, |
||||||
|
Attributes: hdr.Attributes, |
||||||
|
Size: uint64(hdr.Size), |
||||||
|
NameSize: uint32(len(name) * 2), |
||||||
|
} |
||||||
|
if hdr.Id == BackupSparseBlock { |
||||||
|
// Include space for the int64 block offset
|
||||||
|
wsi.Size += 8 |
||||||
|
} |
||||||
|
if err := binary.Write(w.w, binary.LittleEndian, &wsi); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if len(name) != 0 { |
||||||
|
if err := binary.Write(w.w, binary.LittleEndian, name); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
if hdr.Id == BackupSparseBlock { |
||||||
|
if err := binary.Write(w.w, binary.LittleEndian, hdr.Offset); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
w.bytesLeft = hdr.Size |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Write writes to the current backup stream.
|
||||||
|
func (w *BackupStreamWriter) Write(b []byte) (int, error) { |
||||||
|
if w.bytesLeft < int64(len(b)) { |
||||||
|
return 0, fmt.Errorf("too many bytes by %d", int64(len(b))-w.bytesLeft) |
||||||
|
} |
||||||
|
n, err := w.w.Write(b) |
||||||
|
w.bytesLeft -= int64(n) |
||||||
|
return n, err |
||||||
|
} |
||||||
|
|
||||||
|
// BackupFileReader provides an io.ReadCloser interface on top of the BackupRead Win32 API.
|
||||||
|
type BackupFileReader struct { |
||||||
|
f *os.File |
||||||
|
includeSecurity bool |
||||||
|
ctx uintptr |
||||||
|
} |
||||||
|
|
||||||
|
// NewBackupFileReader returns a new BackupFileReader from a file handle. If includeSecurity is true,
|
||||||
|
// Read will attempt to read the security descriptor of the file.
|
||||||
|
func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader { |
||||||
|
r := &BackupFileReader{f, includeSecurity, 0} |
||||||
|
runtime.SetFinalizer(r, func(r *BackupFileReader) { r.Close() }) |
||||||
|
return r |
||||||
|
} |
||||||
|
|
||||||
|
// Read reads a backup stream from the file by calling the Win32 API BackupRead().
|
||||||
|
func (r *BackupFileReader) Read(b []byte) (int, error) { |
||||||
|
var bytesRead uint32 |
||||||
|
err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx) |
||||||
|
if err != nil { |
||||||
|
return 0, &os.PathError{"BackupRead", r.f.Name(), err} |
||||||
|
} |
||||||
|
if bytesRead == 0 { |
||||||
|
return 0, io.EOF |
||||||
|
} |
||||||
|
return int(bytesRead), nil |
||||||
|
} |
||||||
|
|
||||||
|
// Close frees Win32 resources associated with the BackupFileReader. It does not close
|
||||||
|
// the underlying file.
|
||||||
|
func (r *BackupFileReader) Close() error { |
||||||
|
if r.ctx != 0 { |
||||||
|
backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx) |
||||||
|
r.ctx = 0 |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// BackupFileWriter provides an io.WriteCloser interface on top of the BackupWrite Win32 API.
|
||||||
|
type BackupFileWriter struct { |
||||||
|
f *os.File |
||||||
|
includeSecurity bool |
||||||
|
ctx uintptr |
||||||
|
} |
||||||
|
|
||||||
|
// NewBackupFileWrtier returns a new BackupFileWriter from a file handle. If includeSecurity is true,
|
||||||
|
// Write() will attempt to restore the security descriptor from the stream.
|
||||||
|
func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter { |
||||||
|
w := &BackupFileWriter{f, includeSecurity, 0} |
||||||
|
runtime.SetFinalizer(w, func(w *BackupFileWriter) { w.Close() }) |
||||||
|
return w |
||||||
|
} |
||||||
|
|
||||||
|
// Write restores a portion of the file using the provided backup stream.
|
||||||
|
func (w *BackupFileWriter) Write(b []byte) (int, error) { |
||||||
|
var bytesWritten uint32 |
||||||
|
err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx) |
||||||
|
if err != nil { |
||||||
|
return 0, &os.PathError{"BackupWrite", w.f.Name(), err} |
||||||
|
} |
||||||
|
if int(bytesWritten) != len(b) { |
||||||
|
return int(bytesWritten), errors.New("not all bytes could be written") |
||||||
|
} |
||||||
|
return len(b), nil |
||||||
|
} |
||||||
|
|
||||||
|
// Close frees Win32 resources associated with the BackupFileWriter. It does not
|
||||||
|
// close the underlying file.
|
||||||
|
func (w *BackupFileWriter) Close() error { |
||||||
|
if w.ctx != 0 { |
||||||
|
backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx) |
||||||
|
w.ctx = 0 |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// OpenForBackup opens a file or directory, potentially skipping access checks if the backup
|
||||||
|
// or restore privileges have been acquired.
|
||||||
|
//
|
||||||
|
// If the file opened was a directory, it cannot be used with Readdir().
|
||||||
|
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) { |
||||||
|
winPath, err := syscall.UTF16FromString(path) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
h, err := syscall.CreateFile(&winPath[0], access, share, nil, createmode, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) |
||||||
|
if err != nil { |
||||||
|
err = &os.PathError{Op: "open", Path: path, Err: err} |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return os.NewFile(uintptr(h), path), nil |
||||||
|
} |
@ -0,0 +1,219 @@ |
|||||||
|
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
|
||||||
|
//sys timeBeginPeriod(period uint32) (n int32) = winmm.timeBeginPeriod
|
||||||
|
|
||||||
|
const ( |
||||||
|
cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1 |
||||||
|
cFILE_SKIP_SET_EVENT_ON_HANDLE = 2 |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
ErrFileClosed = errors.New("file has already been closed") |
||||||
|
ErrTimeout = &timeoutError{} |
||||||
|
) |
||||||
|
|
||||||
|
type timeoutError struct{} |
||||||
|
|
||||||
|
func (e *timeoutError) Error() string { return "i/o timeout" } |
||||||
|
func (e *timeoutError) Timeout() bool { return true } |
||||||
|
func (e *timeoutError) Temporary() bool { return true } |
||||||
|
|
||||||
|
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, 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, cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS|cFILE_SKIP_SET_EVENT_ON_HANDLE) |
||||||
|
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.Close(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, ErrFileClosed |
||||||
|
} |
||||||
|
c := &ioOperation{} |
||||||
|
c.ch = make(chan ioResult) |
||||||
|
return c, nil |
||||||
|
} |
||||||
|
|
||||||
|
// ioCompletionProcessor processes completed async IOs forever
|
||||||
|
func ioCompletionProcessor(h syscall.Handle) { |
||||||
|
// Set the timer resolution to 1. This fixes a performance regression in golang 1.6.
|
||||||
|
timeBeginPeriod(1) |
||||||
|
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 |
||||||
|
} |
||||||
|
err = r.err |
||||||
|
if err == syscall.ERROR_OPERATION_ABORTED { |
||||||
|
if f.closing { |
||||||
|
err = ErrFileClosed |
||||||
|
} else if timedout { |
||||||
|
err = ErrTimeout |
||||||
|
} |
||||||
|
} |
||||||
|
f.wg.Done() |
||||||
|
return int(r.bytes), 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,54 @@ |
|||||||
|
package winio |
||||||
|
|
||||||
|
import ( |
||||||
|
"os" |
||||||
|
"syscall" |
||||||
|
"unsafe" |
||||||
|
) |
||||||
|
|
||||||
|
//sys getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = GetFileInformationByHandleEx
|
||||||
|
//sys setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = SetFileInformationByHandle
|
||||||
|
|
||||||
|
const ( |
||||||
|
fileBasicInfo = 0 |
||||||
|
fileIDInfo = 0x12 |
||||||
|
) |
||||||
|
|
||||||
|
// FileBasicInfo contains file access time and file attributes information.
|
||||||
|
type FileBasicInfo struct { |
||||||
|
CreationTime, LastAccessTime, LastWriteTime, ChangeTime syscall.Filetime |
||||||
|
FileAttributes uintptr // includes padding
|
||||||
|
} |
||||||
|
|
||||||
|
// GetFileBasicInfo retrieves times and attributes for a file.
|
||||||
|
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) { |
||||||
|
bi := &FileBasicInfo{} |
||||||
|
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { |
||||||
|
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} |
||||||
|
} |
||||||
|
return bi, nil |
||||||
|
} |
||||||
|
|
||||||
|
// SetFileBasicInfo sets times and attributes for a file.
|
||||||
|
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error { |
||||||
|
if err := setFileInformationByHandle(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { |
||||||
|
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// FileIDInfo contains the volume serial number and file ID for a file. This pair should be
|
||||||
|
// unique on a system.
|
||||||
|
type FileIDInfo struct { |
||||||
|
VolumeSerialNumber uint64 |
||||||
|
FileID [16]byte |
||||||
|
} |
||||||
|
|
||||||
|
// GetFileID retrieves the unique (volume, file ID) pair for a file.
|
||||||
|
func GetFileID(f *os.File) (*FileIDInfo, error) { |
||||||
|
fileID := &FileIDInfo{} |
||||||
|
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileIDInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil { |
||||||
|
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} |
||||||
|
} |
||||||
|
return fileID, nil |
||||||
|
} |
@ -0,0 +1,398 @@ |
|||||||
|
package winio |
||||||
|
|
||||||
|
import ( |
||||||
|
"errors" |
||||||
|
"io" |
||||||
|
"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 *securityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
|
||||||
|
//sys createFile(name string, access uint32, mode uint32, sa *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 getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
|
||||||
|
//sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
||||||
|
|
||||||
|
type securityAttributes struct { |
||||||
|
Length uint32 |
||||||
|
SecurityDescriptor *byte |
||||||
|
InheritHandle uint32 |
||||||
|
} |
||||||
|
|
||||||
|
const ( |
||||||
|
cERROR_PIPE_BUSY = syscall.Errno(231) |
||||||
|
cERROR_PIPE_CONNECTED = syscall.Errno(535) |
||||||
|
cERROR_SEM_TIMEOUT = syscall.Errno(121) |
||||||
|
|
||||||
|
cPIPE_ACCESS_DUPLEX = 0x3 |
||||||
|
cFILE_FLAG_FIRST_PIPE_INSTANCE = 0x80000 |
||||||
|
cSECURITY_SQOS_PRESENT = 0x100000 |
||||||
|
cSECURITY_ANONYMOUS = 0 |
||||||
|
|
||||||
|
cPIPE_REJECT_REMOTE_CLIENTS = 0x8 |
||||||
|
|
||||||
|
cPIPE_UNLIMITED_INSTANCES = 255 |
||||||
|
|
||||||
|
cNMPWAIT_USE_DEFAULT_WAIT = 0 |
||||||
|
cNMPWAIT_NOWAIT = 1 |
||||||
|
|
||||||
|
cPIPE_TYPE_MESSAGE = 4 |
||||||
|
|
||||||
|
cPIPE_READMODE_MESSAGE = 2 |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
// ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
|
||||||
|
// This error should match net.errClosing since docker takes a dependency on its text.
|
||||||
|
ErrPipeListenerClosed = errors.New("use of closed network connection") |
||||||
|
|
||||||
|
errPipeWriteClosed = errors.New("pipe has been closed for write") |
||||||
|
) |
||||||
|
|
||||||
|
type win32Pipe struct { |
||||||
|
*win32File |
||||||
|
path string |
||||||
|
} |
||||||
|
|
||||||
|
type win32MessageBytePipe struct { |
||||||
|
win32Pipe |
||||||
|
writeClosed bool |
||||||
|
readEOF bool |
||||||
|
} |
||||||
|
|
||||||
|
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 |
||||||
|
} |
||||||
|
|
||||||
|
// CloseWrite closes the write side of a message pipe in byte mode.
|
||||||
|
func (f *win32MessageBytePipe) CloseWrite() error { |
||||||
|
if f.writeClosed { |
||||||
|
return errPipeWriteClosed |
||||||
|
} |
||||||
|
_, err := f.win32File.Write(nil) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
f.writeClosed = true |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Write writes bytes to a message pipe in byte mode. Zero-byte writes are ignored, since
|
||||||
|
// they are used to implement CloseWrite().
|
||||||
|
func (f *win32MessageBytePipe) Write(b []byte) (int, error) { |
||||||
|
if f.writeClosed { |
||||||
|
return 0, errPipeWriteClosed |
||||||
|
} |
||||||
|
if len(b) == 0 { |
||||||
|
return 0, nil |
||||||
|
} |
||||||
|
return f.win32File.Write(b) |
||||||
|
} |
||||||
|
|
||||||
|
// Read reads bytes from a message pipe in byte mode. A read of a zero-byte message on a message
|
||||||
|
// mode pipe will return io.EOF, as will all subsequent reads.
|
||||||
|
func (f *win32MessageBytePipe) Read(b []byte) (int, error) { |
||||||
|
if f.readEOF { |
||||||
|
return 0, io.EOF |
||||||
|
} |
||||||
|
n, err := f.win32File.Read(b) |
||||||
|
if err == io.EOF { |
||||||
|
// If this was the result of a zero-byte read, then
|
||||||
|
// it is possible that the read was due to a zero-size
|
||||||
|
// message. Since we are simulating CloseWrite with a
|
||||||
|
// zero-byte message, ensure that all future Read() calls
|
||||||
|
// also return EOF.
|
||||||
|
f.readEOF = true |
||||||
|
} |
||||||
|
return n, err |
||||||
|
} |
||||||
|
|
||||||
|
func (s pipeAddress) Network() string { |
||||||
|
return "pipe" |
||||||
|
} |
||||||
|
|
||||||
|
func (s pipeAddress) String() string { |
||||||
|
return string(s) |
||||||
|
} |
||||||
|
|
||||||
|
// DialPipe connects to a named pipe by path, timing out if the connection
|
||||||
|
// takes longer than the specified duration. If timeout is nil, then the timeout
|
||||||
|
// is the default timeout established by the pipe server.
|
||||||
|
func DialPipe(path 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(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0) |
||||||
|
if err != cERROR_PIPE_BUSY { |
||||||
|
break |
||||||
|
} |
||||||
|
now := time.Now() |
||||||
|
var ms uint32 |
||||||
|
if absTimeout.IsZero() { |
||||||
|
ms = cNMPWAIT_USE_DEFAULT_WAIT |
||||||
|
} else if now.After(absTimeout) { |
||||||
|
ms = cNMPWAIT_NOWAIT |
||||||
|
} else { |
||||||
|
ms = uint32(absTimeout.Sub(now).Nanoseconds() / 1000 / 1000) |
||||||
|
} |
||||||
|
err = waitNamedPipe(path, ms) |
||||||
|
if err != nil { |
||||||
|
if err == cERROR_SEM_TIMEOUT { |
||||||
|
return nil, ErrTimeout |
||||||
|
} |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
if err != nil { |
||||||
|
return nil, &os.PathError{Op: "open", Path: path, Err: err} |
||||||
|
} |
||||||
|
|
||||||
|
var flags uint32 |
||||||
|
err = getNamedPipeInfo(h, &flags, nil, nil, nil) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
var state uint32 |
||||||
|
err = getNamedPipeHandleState(h, &state, nil, nil, nil, nil, 0) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
if state&cPIPE_READMODE_MESSAGE != 0 { |
||||||
|
return nil, &os.PathError{Op: "open", Path: path, Err: errors.New("message readmode pipes not supported")} |
||||||
|
} |
||||||
|
|
||||||
|
f, err := makeWin32File(h) |
||||||
|
if err != nil { |
||||||
|
syscall.Close(h) |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
// If the pipe is in message mode, return a message byte pipe, which
|
||||||
|
// supports CloseWrite().
|
||||||
|
if flags&cPIPE_TYPE_MESSAGE != 0 { |
||||||
|
return &win32MessageBytePipe{ |
||||||
|
win32Pipe: win32Pipe{win32File: f, path: path}, |
||||||
|
}, nil |
||||||
|
} |
||||||
|
return &win32Pipe{win32File: f, path: path}, nil |
||||||
|
} |
||||||
|
|
||||||
|
type acceptResponse struct { |
||||||
|
f *win32File |
||||||
|
err error |
||||||
|
} |
||||||
|
|
||||||
|
type win32PipeListener struct { |
||||||
|
firstHandle syscall.Handle |
||||||
|
path string |
||||||
|
securityDescriptor []byte |
||||||
|
config PipeConfig |
||||||
|
acceptCh chan (chan acceptResponse) |
||||||
|
closeCh chan int |
||||||
|
doneCh chan int |
||||||
|
} |
||||||
|
|
||||||
|
func makeServerPipeHandle(path string, securityDescriptor []byte, c *PipeConfig, first bool) (syscall.Handle, error) { |
||||||
|
var flags uint32 = cPIPE_ACCESS_DUPLEX | syscall.FILE_FLAG_OVERLAPPED |
||||||
|
if first { |
||||||
|
flags |= cFILE_FLAG_FIRST_PIPE_INSTANCE |
||||||
|
} |
||||||
|
|
||||||
|
var mode uint32 = cPIPE_REJECT_REMOTE_CLIENTS |
||||||
|
if c.MessageMode { |
||||||
|
mode |= cPIPE_TYPE_MESSAGE |
||||||
|
} |
||||||
|
|
||||||
|
var sa securityAttributes |
||||||
|
sa.Length = uint32(unsafe.Sizeof(sa)) |
||||||
|
if securityDescriptor != nil { |
||||||
|
sa.SecurityDescriptor = &securityDescriptor[0] |
||||||
|
} |
||||||
|
h, err := createNamedPipe(path, flags, mode, cPIPE_UNLIMITED_INSTANCES, uint32(c.OutputBufferSize), uint32(c.InputBufferSize), 0, &sa) |
||||||
|
if err != nil { |
||||||
|
return 0, &os.PathError{Op: "open", Path: path, Err: err} |
||||||
|
} |
||||||
|
return h, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (l *win32PipeListener) makeServerPipe() (*win32File, error) { |
||||||
|
h, err := makeServerPipeHandle(l.path, l.securityDescriptor, &l.config, false) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
f, err := makeWin32File(h) |
||||||
|
if err != nil { |
||||||
|
syscall.Close(h) |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return f, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (l *win32PipeListener) listenerRoutine() { |
||||||
|
closed := false |
||||||
|
for !closed { |
||||||
|
select { |
||||||
|
case <-l.closeCh: |
||||||
|
closed = true |
||||||
|
case responseCh := <-l.acceptCh: |
||||||
|
p, err := l.makeServerPipe() |
||||||
|
if err == nil { |
||||||
|
// Wait for the client to connect.
|
||||||
|
ch := make(chan error) |
||||||
|
go func() { |
||||||
|
ch <- connectPipe(p) |
||||||
|
}() |
||||||
|
select { |
||||||
|
case err = <-ch: |
||||||
|
if err != nil { |
||||||
|
p.Close() |
||||||
|
p = nil |
||||||
|
} |
||||||
|
case <-l.closeCh: |
||||||
|
// Abort the connect request by closing the handle.
|
||||||
|
p.Close() |
||||||
|
p = nil |
||||||
|
err = <-ch |
||||||
|
if err == nil || err == ErrFileClosed { |
||||||
|
err = ErrPipeListenerClosed |
||||||
|
} |
||||||
|
closed = true |
||||||
|
} |
||||||
|
} |
||||||
|
responseCh <- acceptResponse{p, err} |
||||||
|
} |
||||||
|
} |
||||||
|
syscall.Close(l.firstHandle) |
||||||
|
l.firstHandle = 0 |
||||||
|
// Notify Close() and Accept() callers that the handle has been closed.
|
||||||
|
close(l.doneCh) |
||||||
|
} |
||||||
|
|
||||||
|
// PipeConfig contain configuration for the pipe listener.
|
||||||
|
type PipeConfig struct { |
||||||
|
// SecurityDescriptor contains a Windows security descriptor in SDDL format.
|
||||||
|
SecurityDescriptor string |
||||||
|
|
||||||
|
// MessageMode determines whether the pipe is in byte or message mode. In either
|
||||||
|
// case the pipe is read in byte mode by default. The only practical difference in
|
||||||
|
// this implementation is that CloseWrite() is only supported for message mode pipes;
|
||||||
|
// CloseWrite() is implemented as a zero-byte write, but zero-byte writes are only
|
||||||
|
// transferred to the reader (and returned as io.EOF in this implementation)
|
||||||
|
// when the pipe is in message mode.
|
||||||
|
MessageMode bool |
||||||
|
|
||||||
|
// InputBufferSize specifies the size the input buffer, in bytes.
|
||||||
|
InputBufferSize int32 |
||||||
|
|
||||||
|
// OutputBufferSize specifies the size the input buffer, in bytes.
|
||||||
|
OutputBufferSize int32 |
||||||
|
} |
||||||
|
|
||||||
|
// ListenPipe creates a listener on a Windows named pipe path, e.g. \\.\pipe\mypipe.
|
||||||
|
// The pipe must not already exist.
|
||||||
|
func ListenPipe(path string, c *PipeConfig) (net.Listener, error) { |
||||||
|
var ( |
||||||
|
sd []byte |
||||||
|
err error |
||||||
|
) |
||||||
|
if c == nil { |
||||||
|
c = &PipeConfig{} |
||||||
|
} |
||||||
|
if c.SecurityDescriptor != "" { |
||||||
|
sd, err = SddlToSecurityDescriptor(c.SecurityDescriptor) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
} |
||||||
|
h, err := makeServerPipeHandle(path, sd, c, 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, cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0) |
||||||
|
if err != nil { |
||||||
|
syscall.Close(h) |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
syscall.Close(h2) |
||||||
|
l := &win32PipeListener{ |
||||||
|
firstHandle: h, |
||||||
|
path: path, |
||||||
|
securityDescriptor: sd, |
||||||
|
config: *c, |
||||||
|
acceptCh: make(chan (chan acceptResponse)), |
||||||
|
closeCh: make(chan int), |
||||||
|
doneCh: make(chan int), |
||||||
|
} |
||||||
|
go l.listenerRoutine() |
||||||
|
return l, nil |
||||||
|
} |
||||||
|
|
||||||
|
func connectPipe(p *win32File) 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) |
||||||
|
select { |
||||||
|
case l.acceptCh <- ch: |
||||||
|
response := <-ch |
||||||
|
err := response.err |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
if l.config.MessageMode { |
||||||
|
return &win32MessageBytePipe{ |
||||||
|
win32Pipe: win32Pipe{win32File: response.f, path: l.path}, |
||||||
|
}, nil |
||||||
|
} |
||||||
|
return &win32Pipe{win32File: response.f, path: l.path}, nil |
||||||
|
case <-l.doneCh: |
||||||
|
return nil, ErrPipeListenerClosed |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (l *win32PipeListener) Close() error { |
||||||
|
select { |
||||||
|
case l.closeCh <- 1: |
||||||
|
<-l.doneCh |
||||||
|
case <-l.doneCh: |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (l *win32PipeListener) Addr() net.Addr { |
||||||
|
return pipeAddress(l.path) |
||||||
|
} |
@ -0,0 +1,150 @@ |
|||||||
|
package winio |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"encoding/binary" |
||||||
|
"fmt" |
||||||
|
"runtime" |
||||||
|
"syscall" |
||||||
|
"unicode/utf16" |
||||||
|
) |
||||||
|
|
||||||
|
//sys adjustTokenPrivileges(token syscall.Handle, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
|
||||||
|
//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
|
||||||
|
//sys revertToSelf() (err error) = advapi32.RevertToSelf
|
||||||
|
//sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *syscall.Handle) (err error) = advapi32.OpenThreadToken
|
||||||
|
//sys getCurrentThread() (h syscall.Handle) = GetCurrentThread
|
||||||
|
//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
|
||||||
|
//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
|
||||||
|
//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
|
||||||
|
|
||||||
|
const ( |
||||||
|
SE_PRIVILEGE_ENABLED = 2 |
||||||
|
|
||||||
|
ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300 |
||||||
|
|
||||||
|
SeBackupPrivilege = "SeBackupPrivilege" |
||||||
|
SeRestorePrivilege = "SeRestorePrivilege" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
securityAnonymous = iota |
||||||
|
securityIdentification |
||||||
|
securityImpersonation |
||||||
|
securityDelegation |
||||||
|
) |
||||||
|
|
||||||
|
type PrivilegeError struct { |
||||||
|
privileges []uint64 |
||||||
|
} |
||||||
|
|
||||||
|
func (e *PrivilegeError) Error() string { |
||||||
|
s := "" |
||||||
|
if len(e.privileges) > 1 { |
||||||
|
s = "Could not enable privileges " |
||||||
|
} else { |
||||||
|
s = "Could not enable privilege " |
||||||
|
} |
||||||
|
for i, p := range e.privileges { |
||||||
|
if i != 0 { |
||||||
|
s += ", " |
||||||
|
} |
||||||
|
s += `"` |
||||||
|
s += getPrivilegeName(p) |
||||||
|
s += `"` |
||||||
|
} |
||||||
|
return s |
||||||
|
} |
||||||
|
|
||||||
|
func RunWithPrivilege(name string, fn func() error) error { |
||||||
|
return RunWithPrivileges([]string{name}, fn) |
||||||
|
} |
||||||
|
|
||||||
|
func RunWithPrivileges(names []string, fn func() error) error { |
||||||
|
var privileges []uint64 |
||||||
|
for _, name := range names { |
||||||
|
p := uint64(0) |
||||||
|
err := lookupPrivilegeValue("", name, &p) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
privileges = append(privileges, p) |
||||||
|
} |
||||||
|
runtime.LockOSThread() |
||||||
|
defer runtime.UnlockOSThread() |
||||||
|
token, err := newThreadToken() |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
defer releaseThreadToken(token) |
||||||
|
err = adjustPrivileges(token, privileges) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
return fn() |
||||||
|
} |
||||||
|
|
||||||
|
func adjustPrivileges(token syscall.Handle, privileges []uint64) error { |
||||||
|
var b bytes.Buffer |
||||||
|
binary.Write(&b, binary.LittleEndian, uint32(len(privileges))) |
||||||
|
for _, p := range privileges { |
||||||
|
binary.Write(&b, binary.LittleEndian, p) |
||||||
|
binary.Write(&b, binary.LittleEndian, uint32(SE_PRIVILEGE_ENABLED)) |
||||||
|
} |
||||||
|
prevState := make([]byte, b.Len()) |
||||||
|
reqSize := uint32(0) |
||||||
|
success, err := adjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(len(prevState)), &prevState[0], &reqSize) |
||||||
|
if !success { |
||||||
|
return err |
||||||
|
} |
||||||
|
if err == ERROR_NOT_ALL_ASSIGNED { |
||||||
|
return &PrivilegeError{privileges} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func getPrivilegeName(luid uint64) string { |
||||||
|
var nameBuffer [256]uint16 |
||||||
|
bufSize := uint32(len(nameBuffer)) |
||||||
|
err := lookupPrivilegeName("", &luid, &nameBuffer[0], &bufSize) |
||||||
|
if err != nil { |
||||||
|
return fmt.Sprintf("<unknown privilege %d>", luid) |
||||||
|
} |
||||||
|
|
||||||
|
var displayNameBuffer [256]uint16 |
||||||
|
displayBufSize := uint32(len(displayNameBuffer)) |
||||||
|
var langId uint32 |
||||||
|
err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langId) |
||||||
|
if err != nil { |
||||||
|
return fmt.Sprintf("<unknown privilege %s>", utf16.Decode(nameBuffer[:bufSize])) |
||||||
|
} |
||||||
|
|
||||||
|
return string(utf16.Decode(displayNameBuffer[:displayBufSize])) |
||||||
|
} |
||||||
|
|
||||||
|
func newThreadToken() (syscall.Handle, error) { |
||||||
|
err := impersonateSelf(securityImpersonation) |
||||||
|
if err != nil { |
||||||
|
panic(err) |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
|
||||||
|
var token syscall.Handle |
||||||
|
err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token) |
||||||
|
if err != nil { |
||||||
|
rerr := revertToSelf() |
||||||
|
if rerr != nil { |
||||||
|
panic(rerr) |
||||||
|
} |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
return token, nil |
||||||
|
} |
||||||
|
|
||||||
|
func releaseThreadToken(h syscall.Handle) { |
||||||
|
err := revertToSelf() |
||||||
|
if err != nil { |
||||||
|
panic(err) |
||||||
|
} |
||||||
|
syscall.Close(h) |
||||||
|
} |
@ -0,0 +1,124 @@ |
|||||||
|
package winio |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"encoding/binary" |
||||||
|
"fmt" |
||||||
|
"strings" |
||||||
|
"unicode/utf16" |
||||||
|
"unsafe" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
reparseTagMountPoint = 0xA0000003 |
||||||
|
reparseTagSymlink = 0xA000000C |
||||||
|
) |
||||||
|
|
||||||
|
type reparseDataBuffer struct { |
||||||
|
ReparseTag uint32 |
||||||
|
ReparseDataLength uint16 |
||||||
|
Reserved uint16 |
||||||
|
SubstituteNameOffset uint16 |
||||||
|
SubstituteNameLength uint16 |
||||||
|
PrintNameOffset uint16 |
||||||
|
PrintNameLength uint16 |
||||||
|
} |
||||||
|
|
||||||
|
// ReparsePoint describes a Win32 symlink or mount point.
|
||||||
|
type ReparsePoint struct { |
||||||
|
Target string |
||||||
|
IsMountPoint bool |
||||||
|
} |
||||||
|
|
||||||
|
// UnsupportedReparsePointError is returned when trying to decode a non-symlink or
|
||||||
|
// mount point reparse point.
|
||||||
|
type UnsupportedReparsePointError struct { |
||||||
|
Tag uint32 |
||||||
|
} |
||||||
|
|
||||||
|
func (e *UnsupportedReparsePointError) Error() string { |
||||||
|
return fmt.Sprintf("unsupported reparse point %x", e.Tag) |
||||||
|
} |
||||||
|
|
||||||
|
// DecodeReparsePoint decodes a Win32 REPARSE_DATA_BUFFER structure containing either a symlink
|
||||||
|
// or a mount point.
|
||||||
|
func DecodeReparsePoint(b []byte) (*ReparsePoint, error) { |
||||||
|
isMountPoint := false |
||||||
|
tag := binary.LittleEndian.Uint32(b[0:4]) |
||||||
|
switch tag { |
||||||
|
case reparseTagMountPoint: |
||||||
|
isMountPoint = true |
||||||
|
case reparseTagSymlink: |
||||||
|
default: |
||||||
|
return nil, &UnsupportedReparsePointError{tag} |
||||||
|
} |
||||||
|
nameOffset := 16 + binary.LittleEndian.Uint16(b[12:14]) |
||||||
|
if !isMountPoint { |
||||||
|
nameOffset += 4 |
||||||
|
} |
||||||
|
nameLength := binary.LittleEndian.Uint16(b[14:16]) |
||||||
|
name := make([]uint16, nameLength/2) |
||||||
|
err := binary.Read(bytes.NewReader(b[nameOffset:nameOffset+nameLength]), binary.LittleEndian, &name) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &ReparsePoint{string(utf16.Decode(name)), isMountPoint}, nil |
||||||
|
} |
||||||
|
|
||||||
|
func isDriveLetter(c byte) bool { |
||||||
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') |
||||||
|
} |
||||||
|
|
||||||
|
// EncodeReparsePoint encodes a Win32 REPARSE_DATA_BUFFER structure describing a symlink or
|
||||||
|
// mount point.
|
||||||
|
func EncodeReparsePoint(rp *ReparsePoint) []byte { |
||||||
|
// Generate an NT path and determine if this is a relative path.
|
||||||
|
var ntTarget string |
||||||
|
relative := false |
||||||
|
if strings.HasPrefix(rp.Target, `\\?\`) { |
||||||
|
ntTarget = rp.Target |
||||||
|
} else if strings.HasPrefix(rp.Target, `\\`) { |
||||||
|
ntTarget = `\??\UNC\` + rp.Target[2:] |
||||||
|
} else if len(rp.Target) >= 2 && isDriveLetter(rp.Target[0]) && rp.Target[1] == ':' { |
||||||
|
ntTarget = `\??\` + rp.Target |
||||||
|
} else { |
||||||
|
ntTarget = rp.Target |
||||||
|
relative = true |
||||||
|
} |
||||||
|
|
||||||
|
// The paths must be NUL-terminated even though they are counted strings.
|
||||||
|
target16 := utf16.Encode([]rune(rp.Target + "\x00")) |
||||||
|
ntTarget16 := utf16.Encode([]rune(ntTarget + "\x00")) |
||||||
|
|
||||||
|
size := int(unsafe.Sizeof(reparseDataBuffer{})) - 8 |
||||||
|
size += len(ntTarget16)*2 + len(target16)*2 |
||||||
|
|
||||||
|
tag := uint32(reparseTagMountPoint) |
||||||
|
if !rp.IsMountPoint { |
||||||
|
tag = reparseTagSymlink |
||||||
|
size += 4 // Add room for symlink flags
|
||||||
|
} |
||||||
|
|
||||||
|
data := reparseDataBuffer{ |
||||||
|
ReparseTag: tag, |
||||||
|
ReparseDataLength: uint16(size), |
||||||
|
SubstituteNameOffset: 0, |
||||||
|
SubstituteNameLength: uint16((len(ntTarget16) - 1) * 2), |
||||||
|
PrintNameOffset: uint16(len(ntTarget16) * 2), |
||||||
|
PrintNameLength: uint16((len(target16) - 1) * 2), |
||||||
|
} |
||||||
|
|
||||||
|
var b bytes.Buffer |
||||||
|
binary.Write(&b, binary.LittleEndian, &data) |
||||||
|
if !rp.IsMountPoint { |
||||||
|
flags := uint32(0) |
||||||
|
if relative { |
||||||
|
flags |= 1 |
||||||
|
} |
||||||
|
binary.Write(&b, binary.LittleEndian, flags) |
||||||
|
} |
||||||
|
|
||||||
|
binary.Write(&b, binary.LittleEndian, ntTarget16) |
||||||
|
binary.Write(&b, binary.LittleEndian, target16) |
||||||
|
return b.Bytes() |
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
package winio |
||||||
|
|
||||||
|
import ( |
||||||
|
"syscall" |
||||||
|
"unsafe" |
||||||
|
) |
||||||
|
|
||||||
|
//sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW
|
||||||
|
//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
|
||||||
|
//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
|
||||||
|
//sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW
|
||||||
|
//sys localFree(mem uintptr) = LocalFree
|
||||||
|
//sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
|
||||||
|
|
||||||
|
const ( |
||||||
|
cERROR_NONE_MAPPED = syscall.Errno(1332) |
||||||
|
) |
||||||
|
|
||||||
|
type AccountLookupError struct { |
||||||
|
Name string |
||||||
|
Err error |
||||||
|
} |
||||||
|
|
||||||
|
func (e *AccountLookupError) Error() string { |
||||||
|
if e.Name == "" { |
||||||
|
return "lookup account: empty account name specified" |
||||||
|
} |
||||||
|
var s string |
||||||
|
switch e.Err { |
||||||
|
case cERROR_NONE_MAPPED: |
||||||
|
s = "not found" |
||||||
|
default: |
||||||
|
s = e.Err.Error() |
||||||
|
} |
||||||
|
return "lookup account " + e.Name + ": " + s |
||||||
|
} |
||||||
|
|
||||||
|
type SddlConversionError struct { |
||||||
|
Sddl string |
||||||
|
Err error |
||||||
|
} |
||||||
|
|
||||||
|
func (e *SddlConversionError) Error() string { |
||||||
|
return "convert " + e.Sddl + ": " + e.Err.Error() |
||||||
|
} |
||||||
|
|
||||||
|
// LookupSidByName looks up the SID of an account by name
|
||||||
|
func LookupSidByName(name string) (sid string, err error) { |
||||||
|
if name == "" { |
||||||
|
return "", &AccountLookupError{name, cERROR_NONE_MAPPED} |
||||||
|
} |
||||||
|
|
||||||
|
var sidSize, sidNameUse, refDomainSize uint32 |
||||||
|
err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse) |
||||||
|
if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER { |
||||||
|
return "", &AccountLookupError{name, err} |
||||||
|
} |
||||||
|
sidBuffer := make([]byte, sidSize) |
||||||
|
refDomainBuffer := make([]uint16, refDomainSize) |
||||||
|
err = lookupAccountName(nil, name, &sidBuffer[0], &sidSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse) |
||||||
|
if err != nil { |
||||||
|
return "", &AccountLookupError{name, err} |
||||||
|
} |
||||||
|
var strBuffer *uint16 |
||||||
|
err = convertSidToStringSid(&sidBuffer[0], &strBuffer) |
||||||
|
if err != nil { |
||||||
|
return "", &AccountLookupError{name, err} |
||||||
|
} |
||||||
|
sid = syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:]) |
||||||
|
localFree(uintptr(unsafe.Pointer(strBuffer))) |
||||||
|
return sid, nil |
||||||
|
} |
||||||
|
|
||||||
|
func SddlToSecurityDescriptor(sddl string) ([]byte, error) { |
||||||
|
var sdBuffer uintptr |
||||||
|
err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil) |
||||||
|
if err != nil { |
||||||
|
return nil, &SddlConversionError{sddl, err} |
||||||
|
} |
||||||
|
defer localFree(sdBuffer) |
||||||
|
sd := make([]byte, getSecurityDescriptorLength(sdBuffer)) |
||||||
|
copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)]) |
||||||
|
return sd, nil |
||||||
|
} |
||||||
|
|
||||||
|
func SecurityDescriptorToSddl(sd []byte) (string, error) { |
||||||
|
var sddl *uint16 |
||||||
|
// The returned string length seems to including an aribtrary number of terminating NULs.
|
||||||
|
// Don't use it.
|
||||||
|
err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil) |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
defer localFree(uintptr(unsafe.Pointer(sddl))) |
||||||
|
return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(sddl))[:]), nil |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
package winio |
||||||
|
|
||||||
|
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go
|
@ -0,0 +1,492 @@ |
|||||||
|
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
|
||||||
|
|
||||||
|
package winio |
||||||
|
|
||||||
|
import "unsafe" |
||||||
|
import "syscall" |
||||||
|
|
||||||
|
var _ unsafe.Pointer |
||||||
|
|
||||||
|
var ( |
||||||
|
modkernel32 = syscall.NewLazyDLL("kernel32.dll") |
||||||
|
modwinmm = syscall.NewLazyDLL("winmm.dll") |
||||||
|
modadvapi32 = syscall.NewLazyDLL("advapi32.dll") |
||||||
|
|
||||||
|
procCancelIoEx = modkernel32.NewProc("CancelIoEx") |
||||||
|
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort") |
||||||
|
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus") |
||||||
|
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes") |
||||||
|
proctimeBeginPeriod = modwinmm.NewProc("timeBeginPeriod") |
||||||
|
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") |
||||||
|
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") |
||||||
|
procCreateFileW = modkernel32.NewProc("CreateFileW") |
||||||
|
procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW") |
||||||
|
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo") |
||||||
|
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW") |
||||||
|
procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW") |
||||||
|
procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW") |
||||||
|
procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW") |
||||||
|
procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW") |
||||||
|
procLocalFree = modkernel32.NewProc("LocalFree") |
||||||
|
procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength") |
||||||
|
procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx") |
||||||
|
procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle") |
||||||
|
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") |
||||||
|
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf") |
||||||
|
procRevertToSelf = modadvapi32.NewProc("RevertToSelf") |
||||||
|
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken") |
||||||
|
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread") |
||||||
|
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW") |
||||||
|
procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW") |
||||||
|
procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW") |
||||||
|
procBackupRead = modkernel32.NewProc("BackupRead") |
||||||
|
procBackupWrite = modkernel32.NewProc("BackupWrite") |
||||||
|
) |
||||||
|
|
||||||
|
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 timeBeginPeriod(period uint32) (n int32) { |
||||||
|
r0, _, _ := syscall.Syscall(proctimeBeginPeriod.Addr(), 1, uintptr(period), 0, 0) |
||||||
|
n = int32(r0) |
||||||
|
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 *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 *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 *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 *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 getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) { |
||||||
|
r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) { |
||||||
|
r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { |
||||||
|
var _p0 *uint16 |
||||||
|
_p0, err = syscall.UTF16PtrFromString(accountName) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse) |
||||||
|
} |
||||||
|
|
||||||
|
func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { |
||||||
|
r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func convertSidToStringSid(sid *byte, str **uint16) (err error) { |
||||||
|
r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 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 convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) { |
||||||
|
r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 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 |
||||||
|
} |
||||||
|
|
||||||
|
func getSecurityDescriptorLength(sd uintptr) (len uint32) { |
||||||
|
r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0) |
||||||
|
len = uint32(r0) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) { |
||||||
|
r1, _, e1 := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) { |
||||||
|
r1, _, e1 := syscall.Syscall6(procSetFileInformationByHandle.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func adjustTokenPrivileges(token syscall.Handle, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) { |
||||||
|
var _p0 uint32 |
||||||
|
if releaseAll { |
||||||
|
_p0 = 1 |
||||||
|
} else { |
||||||
|
_p0 = 0 |
||||||
|
} |
||||||
|
r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize))) |
||||||
|
success = r0 != 0 |
||||||
|
if true { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func impersonateSelf(level uint32) (err error) { |
||||||
|
r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func revertToSelf() (err error) { |
||||||
|
r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *syscall.Handle) (err error) { |
||||||
|
var _p0 uint32 |
||||||
|
if openAsSelf { |
||||||
|
_p0 = 1 |
||||||
|
} else { |
||||||
|
_p0 = 0 |
||||||
|
} |
||||||
|
r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func getCurrentThread() (h syscall.Handle) { |
||||||
|
r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0) |
||||||
|
h = syscall.Handle(r0) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) { |
||||||
|
var _p0 *uint16 |
||||||
|
_p0, err = syscall.UTF16PtrFromString(systemName) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
var _p1 *uint16 |
||||||
|
_p1, err = syscall.UTF16PtrFromString(name) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
return _lookupPrivilegeValue(_p0, _p1, luid) |
||||||
|
} |
||||||
|
|
||||||
|
func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) { |
||||||
|
r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid))) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) { |
||||||
|
var _p0 *uint16 |
||||||
|
_p0, err = syscall.UTF16PtrFromString(systemName) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
return _lookupPrivilegeName(_p0, luid, buffer, size) |
||||||
|
} |
||||||
|
|
||||||
|
func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) { |
||||||
|
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) { |
||||||
|
var _p0 *uint16 |
||||||
|
_p0, err = syscall.UTF16PtrFromString(systemName) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId) |
||||||
|
} |
||||||
|
|
||||||
|
func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) { |
||||||
|
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) { |
||||||
|
var _p0 *byte |
||||||
|
if len(b) > 0 { |
||||||
|
_p0 = &b[0] |
||||||
|
} |
||||||
|
var _p1 uint32 |
||||||
|
if abort { |
||||||
|
_p1 = 1 |
||||||
|
} else { |
||||||
|
_p1 = 0 |
||||||
|
} |
||||||
|
var _p2 uint32 |
||||||
|
if processSecurity { |
||||||
|
_p2 = 1 |
||||||
|
} else { |
||||||
|
_p2 = 0 |
||||||
|
} |
||||||
|
r1, _, e1 := syscall.Syscall9(procBackupRead.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) { |
||||||
|
var _p0 *byte |
||||||
|
if len(b) > 0 { |
||||||
|
_p0 = &b[0] |
||||||
|
} |
||||||
|
var _p1 uint32 |
||||||
|
if abort { |
||||||
|
_p1 = 1 |
||||||
|
} else { |
||||||
|
_p1 = 0 |
||||||
|
} |
||||||
|
var _p2 uint32 |
||||||
|
if processSecurity { |
||||||
|
_p2 = 1 |
||||||
|
} else { |
||||||
|
_p2 = 0 |
||||||
|
} |
||||||
|
r1, _, e1 := syscall.Syscall9(procBackupWrite.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
Loading…
Reference in new issue