Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Geth-Modification
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张蕾
Geth-Modification
Commits
7e021056
Commit
7e021056
authored
Apr 05, 2016
by
Jeffrey Wilcke
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2417 from fjl/rpc-go-winio
rpc: use go-winio instead of vendoring a named pipe implementation
parents
ed92f116
467bb7a7
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
1849 additions
and
622 deletions
+1849
-622
Godeps.json
Godeps/Godeps.json
+6
-0
LICENSE
Godeps/_workspace/src/github.com/microsoft/go-winio/LICENSE
+22
-0
README.md
...ps/_workspace/src/github.com/microsoft/go-winio/README.md
+15
-0
backup.go
...ps/_workspace/src/github.com/microsoft/go-winio/backup.go
+266
-0
file.go
Godeps/_workspace/src/github.com/microsoft/go-winio/file.go
+219
-0
fileinfo.go
.../_workspace/src/github.com/microsoft/go-winio/fileinfo.go
+54
-0
pipe.go
Godeps/_workspace/src/github.com/microsoft/go-winio/pipe.go
+398
-0
privilege.go
..._workspace/src/github.com/microsoft/go-winio/privilege.go
+150
-0
reparse.go
...s/_workspace/src/github.com/microsoft/go-winio/reparse.go
+124
-0
sd.go
Godeps/_workspace/src/github.com/microsoft/go-winio/sd.go
+96
-0
syscall.go
...s/_workspace/src/github.com/microsoft/go-winio/syscall.go
+3
-0
zsyscall.go
.../_workspace/src/github.com/microsoft/go-winio/zsyscall.go
+492
-0
ipc_windows.go
rpc/ipc_windows.go
+4
-622
No files found.
Godeps/Godeps.json
View file @
7e021056
{
"ImportPath"
:
"github.com/ethereum/go-ethereum"
,
"GoVersion"
:
"go1.5.2"
,
"GodepVersion"
:
"v60"
,
"Packages"
:
[
"./..."
],
...
...
@@ -93,6 +94,11 @@
"Comment"
:
"travisish-44-ge882a96"
,
"Rev"
:
"e882a96ec18dd43fa283187b66af74497c9101c0"
},
{
"ImportPath"
:
"github.com/microsoft/go-winio"
,
"Comment"
:
"v0.2.0"
,
"Rev"
:
"9e2895e5f6c3f16473b91d37fae6e89990a4520c"
},
{
"ImportPath"
:
"github.com/nsf/termbox-go"
,
"Rev"
:
"362329b0aa6447eadd52edd8d660ec1dff470295"
...
...
Godeps/_workspace/src/github.com/microsoft/go-winio/LICENSE
0 → 100644
View file @
7e021056
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.
Godeps/_workspace/src/github.com/microsoft/go-winio/README.md
0 → 100644
View file @
7e021056
# 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.
Godeps/_workspace/src/github.com/microsoft/go-winio/backup.go
0 → 100644
View file @
7e021056
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
}
Godeps/_workspace/src/github.com/microsoft/go-winio/file.go
0 → 100644
View file @
7e021056
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
}
Godeps/_workspace/src/github.com/microsoft/go-winio/fileinfo.go
0 → 100644
View file @
7e021056
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
}
Godeps/_workspace/src/github.com/microsoft/go-winio/pipe.go
0 → 100644
View file @
7e021056
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
)
}
Godeps/_workspace/src/github.com/microsoft/go-winio/privilege.go
0 → 100644
View file @
7e021056
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
)
}
Godeps/_workspace/src/github.com/microsoft/go-winio/reparse.go
0 → 100644
View file @
7e021056
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
()
}
Godeps/_workspace/src/github.com/microsoft/go-winio/sd.go
0 → 100644
View file @
7e021056
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
}
Godeps/_workspace/src/github.com/microsoft/go-winio/syscall.go
0 → 100644
View file @
7e021056
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
Godeps/_workspace/src/github.com/microsoft/go-winio/zsyscall.go
0 → 100644
View file @
7e021056
// 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
}
rpc/ipc_windows.go
View file @
7e021056
...
...
@@ -19,637 +19,19 @@
package
rpc
import
(
"fmt"
"io"
"net"
"sync"
"syscall"
"time"
"unsafe"
)
var
(
modkernel32
=
syscall
.
NewLazyDLL
(
"kernel32.dll"
)
procCreateNamedPipeW
=
modkernel32
.
NewProc
(
"CreateNamedPipeW"
)
procConnectNamedPipe
=
modkernel32
.
NewProc
(
"ConnectNamedPipe"
)
procDisconnectNamedPipe
=
modkernel32
.
NewProc
(
"DisconnectNamedPipe"
)
procWaitNamedPipeW
=
modkernel32
.
NewProc
(
"WaitNamedPipeW"
)
procCreateEventW
=
modkernel32
.
NewProc
(
"CreateEventW"
)
procGetOverlappedResult
=
modkernel32
.
NewProc
(
"GetOverlappedResult"
)
procCancelIoEx
=
modkernel32
.
NewProc
(
"CancelIoEx"
)
"github.com/microsoft/go-winio"
)
func
createNamedPipe
(
name
*
uint16
,
openMode
uint32
,
pipeMode
uint32
,
maxInstances
uint32
,
outBufSize
uint32
,
inBufSize
uint32
,
defaultTimeout
uint32
,
sa
*
syscall
.
SecurityAttributes
)
(
handle
syscall
.
Handle
,
err
error
)
{
r0
,
_
,
e1
:=
syscall
.
Syscall9
(
procCreateNamedPipeW
.
Addr
(),
8
,
uintptr
(
unsafe
.
Pointer
(
name
)),
uintptr
(
openMode
),
uintptr
(
pipeMode
),
uintptr
(
maxInstances
),
uintptr
(
outBufSize
),
uintptr
(
inBufSize
),
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
cancelIoEx
(
handle
syscall
.
Handle
,
overlapped
*
syscall
.
Overlapped
)
(
err
error
)
{
r1
,
_
,
e1
:=
syscall
.
Syscall
(
procCancelIoEx
.
Addr
(),
2
,
uintptr
(
handle
),
uintptr
(
unsafe
.
Pointer
(
overlapped
)),
0
)
if
r1
==
0
{
if
e1
!=
0
{
err
=
error
(
e1
)
}
else
{
err
=
syscall
.
EINVAL
}
}
return
}
func
connectNamedPipe
(
handle
syscall
.
Handle
,
overlapped
*
syscall
.
Overlapped
)
(
err
error
)
{
r1
,
_
,
e1
:=
syscall
.
Syscall
(
procConnectNamedPipe
.
Addr
(),
2
,
uintptr
(
handle
),
uintptr
(
unsafe
.
Pointer
(
overlapped
)),
0
)
if
r1
==
0
{
if
e1
!=
0
{
err
=
error
(
e1
)
}
else
{
err
=
syscall
.
EINVAL
}
}
return
}
func
disconnectNamedPipe
(
handle
syscall
.
Handle
)
(
err
error
)
{
r1
,
_
,
e1
:=
syscall
.
Syscall
(
procDisconnectNamedPipe
.
Addr
(),
1
,
uintptr
(
handle
),
0
,
0
)
if
r1
==
0
{
if
e1
!=
0
{
err
=
error
(
e1
)
}
else
{
err
=
syscall
.
EINVAL
}
}
return
}
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
createEvent
(
sa
*
syscall
.
SecurityAttributes
,
manualReset
bool
,
initialState
bool
,
name
*
uint16
)
(
handle
syscall
.
Handle
,
err
error
)
{
var
_p0
uint32
if
manualReset
{
_p0
=
1
}
else
{
_p0
=
0
}
var
_p1
uint32
if
initialState
{
_p1
=
1
}
else
{
_p1
=
0
}
r0
,
_
,
e1
:=
syscall
.
Syscall6
(
procCreateEventW
.
Addr
(),
4
,
uintptr
(
unsafe
.
Pointer
(
sa
)),
uintptr
(
_p0
),
uintptr
(
_p1
),
uintptr
(
unsafe
.
Pointer
(
name
)),
0
,
0
)
handle
=
syscall
.
Handle
(
r0
)
if
handle
==
syscall
.
InvalidHandle
{
if
e1
!=
0
{
err
=
error
(
e1
)
}
else
{
err
=
syscall
.
EINVAL
}
}
return
}
func
getOverlappedResult
(
handle
syscall
.
Handle
,
overlapped
*
syscall
.
Overlapped
,
transferred
*
uint32
,
wait
bool
)
(
err
error
)
{
var
_p0
uint32
if
wait
{
_p0
=
1
}
else
{
_p0
=
0
}
r1
,
_
,
e1
:=
syscall
.
Syscall6
(
procGetOverlappedResult
.
Addr
(),
4
,
uintptr
(
handle
),
uintptr
(
unsafe
.
Pointer
(
overlapped
)),
uintptr
(
unsafe
.
Pointer
(
transferred
)),
uintptr
(
_p0
),
0
,
0
)
if
r1
==
0
{
if
e1
!=
0
{
err
=
error
(
e1
)
}
else
{
err
=
syscall
.
EINVAL
}
}
return
}
const
(
// openMode
pipe_access_duplex
=
0x3
pipe_access_inbound
=
0x1
pipe_access_outbound
=
0x2
// openMode write flags
file_flag_first_pipe_instance
=
0x00080000
file_flag_write_through
=
0x80000000
file_flag_overlapped
=
0x40000000
// openMode ACL flags
write_dac
=
0x00040000
write_owner
=
0x00080000
access_system_security
=
0x01000000
// pipeMode
pipe_type_byte
=
0x0
pipe_type_message
=
0x4
// pipeMode read mode flags
pipe_readmode_byte
=
0x0
pipe_readmode_message
=
0x2
// pipeMode wait mode flags
pipe_wait
=
0x0
pipe_nowait
=
0x1
// pipeMode remote-client mode flags
pipe_accept_remote_clients
=
0x0
pipe_reject_remote_clients
=
0x8
pipe_unlimited_instances
=
255
nmpwait_wait_forever
=
0xFFFFFFFF
// the two not-an-errors below occur if a client connects to the pipe between
// the server's CreateNamedPipe and ConnectNamedPipe calls.
error_no_data
syscall
.
Errno
=
0xE8
error_pipe_connected
syscall
.
Errno
=
0x217
error_pipe_busy
syscall
.
Errno
=
0xE7
error_sem_timeout
syscall
.
Errno
=
0x79
error_bad_pathname
syscall
.
Errno
=
0xA1
error_invalid_name
syscall
.
Errno
=
0x7B
error_io_incomplete
syscall
.
Errno
=
0x3e4
)
var
_
net
.
Conn
=
(
*
PipeConn
)(
nil
)
var
_
net
.
Listener
=
(
*
PipeListener
)(
nil
)
// ErrClosed is the error returned by PipeListener.Accept when Close is called
// on the PipeListener.
var
ErrClosed
=
PipeError
{
"Pipe has been closed."
,
false
}
// PipeError is an error related to a call to a pipe
type
PipeError
struct
{
msg
string
timeout
bool
}
// Error implements the error interface
func
(
e
PipeError
)
Error
()
string
{
return
e
.
msg
}
// Timeout implements net.AddrError.Timeout()
func
(
e
PipeError
)
Timeout
()
bool
{
return
e
.
timeout
}
// Temporary implements net.AddrError.Temporary()
func
(
e
PipeError
)
Temporary
()
bool
{
return
false
}
// Dial connects to a named pipe with the given address. If the specified pipe is not available,
// it will wait indefinitely for the pipe to become available.
//
// The address must be of the form \\.\\pipe\<name> for local pipes and \\<computer>\pipe\<name>
// for remote pipes.
//
// Dial will return a PipeError if you pass in a badly formatted pipe name.
//
// Examples:
// // local pipe
// conn, err := Dial(`\\.\pipe\mypipename`)
//
// // remote pipe
// conn, err := Dial(`\\othercomp\pipe\mypipename`)
func
Dial
(
address
string
)
(
*
PipeConn
,
error
)
{
for
{
conn
,
err
:=
dial
(
address
,
nmpwait_wait_forever
)
if
err
==
nil
{
return
conn
,
nil
}
if
isPipeNotReady
(
err
)
{
<-
time
.
After
(
100
*
time
.
Millisecond
)
continue
}
return
nil
,
err
}
}
// DialTimeout acts like Dial, but will time out after the duration of timeout
func
DialTimeout
(
address
string
,
timeout
time
.
Duration
)
(
*
PipeConn
,
error
)
{
deadline
:=
time
.
Now
()
.
Add
(
timeout
)
now
:=
time
.
Now
()
for
now
.
Before
(
deadline
)
{
millis
:=
uint32
(
deadline
.
Sub
(
now
)
/
time
.
Millisecond
)
conn
,
err
:=
dial
(
address
,
millis
)
if
err
==
nil
{
return
conn
,
nil
}
if
err
==
error_sem_timeout
{
// This is WaitNamedPipe's timeout error, so we know we're done
return
nil
,
PipeError
{
fmt
.
Sprintf
(
"Timed out waiting for pipe '%s' to come available"
,
address
),
true
}
}
if
isPipeNotReady
(
err
)
{
left
:=
deadline
.
Sub
(
time
.
Now
())
retry
:=
100
*
time
.
Millisecond
if
left
>
retry
{
<-
time
.
After
(
retry
)
}
else
{
<-
time
.
After
(
left
-
time
.
Millisecond
)
}
now
=
time
.
Now
()
continue
}
return
nil
,
err
}
return
nil
,
PipeError
{
fmt
.
Sprintf
(
"Timed out waiting for pipe '%s' to come available"
,
address
),
true
}
}
// isPipeNotReady checks the error to see if it indicates the pipe is not ready
func
isPipeNotReady
(
err
error
)
bool
{
// Pipe Busy means another client just grabbed the open pipe end,
// and the server hasn't made a new one yet.
// File Not Found means the server hasn't created the pipe yet.
// Neither is a fatal error.
return
err
==
syscall
.
ERROR_FILE_NOT_FOUND
||
err
==
error_pipe_busy
}
// newOverlapped creates a structure used to track asynchronous
// I/O requests that have been issued.
func
newOverlapped
()
(
*
syscall
.
Overlapped
,
error
)
{
event
,
err
:=
createEvent
(
nil
,
true
,
true
,
nil
)
if
err
!=
nil
{
return
nil
,
err
}
return
&
syscall
.
Overlapped
{
HEvent
:
event
},
nil
}
// waitForCompletion waits for an asynchronous I/O request referred to by overlapped to complete.
// This function returns the number of bytes transferred by the operation and an error code if
// applicable (nil otherwise).
func
waitForCompletion
(
handle
syscall
.
Handle
,
overlapped
*
syscall
.
Overlapped
)
(
uint32
,
error
)
{
_
,
err
:=
syscall
.
WaitForSingleObject
(
overlapped
.
HEvent
,
syscall
.
INFINITE
)
if
err
!=
nil
{
return
0
,
err
}
var
transferred
uint32
err
=
getOverlappedResult
(
handle
,
overlapped
,
&
transferred
,
true
)
return
transferred
,
err
}
// dial is a helper to initiate a connection to a named pipe that has been started by a server.
// The timeout is only enforced if the pipe server has already created the pipe, otherwise
// this function will return immediately.
func
dial
(
address
string
,
timeout
uint32
)
(
*
PipeConn
,
error
)
{
name
,
err
:=
syscall
.
UTF16PtrFromString
(
string
(
address
))
if
err
!=
nil
{
return
nil
,
err
}
// If at least one instance of the pipe has been created, this function
// will wait timeout milliseconds for it to become available.
// It will return immediately regardless of timeout, if no instances
// of the named pipe have been created yet.
// If this returns with no error, there is a pipe available.
if
err
:=
waitNamedPipe
(
name
,
timeout
);
err
!=
nil
{
if
err
==
error_bad_pathname
{
// badly formatted pipe name
return
nil
,
badAddr
(
address
)
}
return
nil
,
err
}
pathp
,
err
:=
syscall
.
UTF16PtrFromString
(
address
)
if
err
!=
nil
{
return
nil
,
err
}
handle
,
err
:=
syscall
.
CreateFile
(
pathp
,
syscall
.
GENERIC_READ
|
syscall
.
GENERIC_WRITE
,
uint32
(
syscall
.
FILE_SHARE_READ
|
syscall
.
FILE_SHARE_WRITE
),
nil
,
syscall
.
OPEN_EXISTING
,
syscall
.
FILE_FLAG_OVERLAPPED
,
0
)
if
err
!=
nil
{
return
nil
,
err
}
return
&
PipeConn
{
handle
:
handle
,
addr
:
PipeAddr
(
address
)},
nil
}
// Listen returns a new PipeListener that will listen on a pipe with the given
// address. The address must be of the form \\.\pipe\<name>
//
// Listen will return a PipeError for an incorrectly formatted pipe name.
func
Listen
(
address
string
)
(
*
PipeListener
,
error
)
{
handle
,
err
:=
createPipe
(
address
,
true
)
if
err
==
error_invalid_name
{
return
nil
,
badAddr
(
address
)
}
if
err
!=
nil
{
return
nil
,
err
}
return
&
PipeListener
{
addr
:
PipeAddr
(
address
),
handle
:
handle
,
},
nil
}
// PipeListener is a named pipe listener. Clients should typically
// use variables of type net.Listener instead of assuming named pipe.
type
PipeListener
struct
{
addr
PipeAddr
handle
syscall
.
Handle
closed
bool
// acceptHandle contains the current handle waiting for
// an incoming connection or nil.
acceptHandle
syscall
.
Handle
// acceptOverlapped is set before waiting on a connection.
// If not waiting, it is nil.
acceptOverlapped
*
syscall
.
Overlapped
// acceptMutex protects the handle and overlapped structure.
acceptMutex
sync
.
Mutex
}
// Accept implements the Accept method in the net.Listener interface; it
// waits for the next call and returns a generic net.Conn.
func
(
l
*
PipeListener
)
Accept
()
(
net
.
Conn
,
error
)
{
c
,
err
:=
l
.
AcceptPipe
()
for
err
==
error_no_data
{
// Ignore clients that connect and immediately disconnect.
c
,
err
=
l
.
AcceptPipe
()
}
if
err
!=
nil
{
return
nil
,
err
}
return
c
,
nil
}
// AcceptPipe accepts the next incoming call and returns the new connection.
// It might return an error if a client connected and immediately cancelled
// the connection.
func
(
l
*
PipeListener
)
AcceptPipe
()
(
*
PipeConn
,
error
)
{
if
l
==
nil
||
l
.
addr
==
""
||
l
.
closed
{
return
nil
,
syscall
.
EINVAL
}
// the first time we call accept, the handle will have been created by the Listen
// call. This is to prevent race conditions where the client thinks the server
// isn't listening because it hasn't actually called create yet. After the first time, we'll
// have to create a new handle each time
handle
:=
l
.
handle
if
handle
==
0
{
var
err
error
handle
,
err
=
createPipe
(
string
(
l
.
addr
),
false
)
if
err
!=
nil
{
return
nil
,
err
}
}
else
{
l
.
handle
=
0
}
overlapped
,
err
:=
newOverlapped
()
if
err
!=
nil
{
return
nil
,
err
}
defer
syscall
.
CloseHandle
(
overlapped
.
HEvent
)
if
err
:=
connectNamedPipe
(
handle
,
overlapped
);
err
!=
nil
&&
err
!=
error_pipe_connected
{
if
err
==
error_io_incomplete
||
err
==
syscall
.
ERROR_IO_PENDING
{
l
.
acceptMutex
.
Lock
()
l
.
acceptOverlapped
=
overlapped
l
.
acceptHandle
=
handle
l
.
acceptMutex
.
Unlock
()
defer
func
()
{
l
.
acceptMutex
.
Lock
()
l
.
acceptOverlapped
=
nil
l
.
acceptHandle
=
0
l
.
acceptMutex
.
Unlock
()
}()
_
,
err
=
waitForCompletion
(
handle
,
overlapped
)
}
if
err
==
syscall
.
ERROR_OPERATION_ABORTED
{
// Return error compatible to net.Listener.Accept() in case the
// listener was closed.
return
nil
,
ErrClosed
}
if
err
!=
nil
{
return
nil
,
err
}
}
return
&
PipeConn
{
handle
:
handle
,
addr
:
l
.
addr
},
nil
}
// Close stops listening on the address.
// Already Accepted connections are not closed.
func
(
l
*
PipeListener
)
Close
()
error
{
if
l
.
closed
{
return
nil
}
l
.
closed
=
true
if
l
.
handle
!=
0
{
err
:=
disconnectNamedPipe
(
l
.
handle
)
if
err
!=
nil
{
return
err
}
err
=
syscall
.
CloseHandle
(
l
.
handle
)
if
err
!=
nil
{
return
err
}
l
.
handle
=
0
}
l
.
acceptMutex
.
Lock
()
defer
l
.
acceptMutex
.
Unlock
()
if
l
.
acceptOverlapped
!=
nil
&&
l
.
acceptHandle
!=
0
{
// Cancel the pending IO. This call does not block, so it is safe
// to hold onto the mutex above.
if
err
:=
cancelIoEx
(
l
.
acceptHandle
,
l
.
acceptOverlapped
);
err
!=
nil
{
return
err
}
err
:=
syscall
.
CloseHandle
(
l
.
acceptOverlapped
.
HEvent
)
if
err
!=
nil
{
return
err
}
l
.
acceptOverlapped
.
HEvent
=
0
err
=
syscall
.
CloseHandle
(
l
.
acceptHandle
)
if
err
!=
nil
{
return
err
}
l
.
acceptHandle
=
0
}
return
nil
}
// Addr returns the listener's network address, a PipeAddr.
func
(
l
*
PipeListener
)
Addr
()
net
.
Addr
{
return
l
.
addr
}
// PipeConn is the implementation of the net.Conn interface for named pipe connections.
type
PipeConn
struct
{
handle
syscall
.
Handle
addr
PipeAddr
// these aren't actually used yet
readDeadline
*
time
.
Time
writeDeadline
*
time
.
Time
}
type
iodata
struct
{
n
uint32
err
error
}
// completeRequest looks at iodata to see if a request is pending. If so, it waits for it to either complete or to
// abort due to hitting the specified deadline. Deadline may be set to nil to wait forever. If no request is pending,
// the content of iodata is returned.
func
(
c
*
PipeConn
)
completeRequest
(
data
iodata
,
deadline
*
time
.
Time
,
overlapped
*
syscall
.
Overlapped
)
(
int
,
error
)
{
if
data
.
err
==
error_io_incomplete
||
data
.
err
==
syscall
.
ERROR_IO_PENDING
{
var
timer
<-
chan
time
.
Time
if
deadline
!=
nil
{
if
timeDiff
:=
deadline
.
Sub
(
time
.
Now
());
timeDiff
>
0
{
timer
=
time
.
After
(
timeDiff
)
}
}
done
:=
make
(
chan
iodata
)
go
func
()
{
n
,
err
:=
waitForCompletion
(
c
.
handle
,
overlapped
)
done
<-
iodata
{
n
,
err
}
}()
select
{
case
data
=
<-
done
:
case
<-
timer
:
syscall
.
CancelIoEx
(
c
.
handle
,
overlapped
)
data
=
iodata
{
0
,
timeout
(
c
.
addr
.
String
())}
}
}
// Windows will produce ERROR_BROKEN_PIPE upon closing
// a handle on the other end of a connection. Go RPC
// expects an io.EOF error in this case.
if
data
.
err
==
syscall
.
ERROR_BROKEN_PIPE
{
data
.
err
=
io
.
EOF
}
return
int
(
data
.
n
),
data
.
err
}
// Read implements the net.Conn Read method.
func
(
c
*
PipeConn
)
Read
(
b
[]
byte
)
(
int
,
error
)
{
// Use ReadFile() rather than Read() because the latter
// contains a workaround that eats ERROR_BROKEN_PIPE.
overlapped
,
err
:=
newOverlapped
()
if
err
!=
nil
{
return
0
,
err
}
defer
syscall
.
CloseHandle
(
overlapped
.
HEvent
)
var
n
uint32
err
=
syscall
.
ReadFile
(
c
.
handle
,
b
,
&
n
,
overlapped
)
return
c
.
completeRequest
(
iodata
{
n
,
err
},
c
.
readDeadline
,
overlapped
)
}
// Write implements the net.Conn Write method.
func
(
c
*
PipeConn
)
Write
(
b
[]
byte
)
(
int
,
error
)
{
overlapped
,
err
:=
newOverlapped
()
if
err
!=
nil
{
return
0
,
err
}
defer
syscall
.
CloseHandle
(
overlapped
.
HEvent
)
var
n
uint32
err
=
syscall
.
WriteFile
(
c
.
handle
,
b
,
&
n
,
overlapped
)
return
c
.
completeRequest
(
iodata
{
n
,
err
},
c
.
writeDeadline
,
overlapped
)
}
// Close closes the connection.
func
(
c
*
PipeConn
)
Close
()
error
{
return
syscall
.
CloseHandle
(
c
.
handle
)
}
// LocalAddr returns the local network address.
func
(
c
*
PipeConn
)
LocalAddr
()
net
.
Addr
{
return
c
.
addr
}
// RemoteAddr returns the remote network address.
func
(
c
*
PipeConn
)
RemoteAddr
()
net
.
Addr
{
// not sure what to do here, we don't have remote addr....
return
c
.
addr
}
// SetDeadline implements the net.Conn SetDeadline method.
// Note that timeouts are only supported on Windows Vista/Server 2008 and above
func
(
c
*
PipeConn
)
SetDeadline
(
t
time
.
Time
)
error
{
c
.
SetReadDeadline
(
t
)
c
.
SetWriteDeadline
(
t
)
return
nil
}
// SetReadDeadline implements the net.Conn SetReadDeadline method.
// Note that timeouts are only supported on Windows Vista/Server 2008 and above
func
(
c
*
PipeConn
)
SetReadDeadline
(
t
time
.
Time
)
error
{
c
.
readDeadline
=
&
t
return
nil
}
// SetWriteDeadline implements the net.Conn SetWriteDeadline method.
// Note that timeouts are only supported on Windows Vista/Server 2008 and above
func
(
c
*
PipeConn
)
SetWriteDeadline
(
t
time
.
Time
)
error
{
c
.
writeDeadline
=
&
t
return
nil
}
// PipeAddr represents the address of a named pipe.
type
PipeAddr
string
// Network returns the address's network name, "pipe".
func
(
a
PipeAddr
)
Network
()
string
{
return
"pipe"
}
// String returns the address of the pipe
func
(
a
PipeAddr
)
String
()
string
{
return
string
(
a
)
}
// createPipe is a helper function to make sure we always create pipes
// with the same arguments, since subsequent calls to create pipe need
// to use the same arguments as the first one. If first is set, fail
// if the pipe already exists.
func
createPipe
(
address
string
,
first
bool
)
(
syscall
.
Handle
,
error
)
{
n
,
err
:=
syscall
.
UTF16PtrFromString
(
address
)
if
err
!=
nil
{
return
0
,
err
}
mode
:=
uint32
(
pipe_access_duplex
|
syscall
.
FILE_FLAG_OVERLAPPED
)
if
first
{
mode
|=
file_flag_first_pipe_instance
}
return
createNamedPipe
(
n
,
mode
,
pipe_type_byte
,
pipe_unlimited_instances
,
512
,
512
,
0
,
nil
)
}
func
badAddr
(
addr
string
)
PipeError
{
return
PipeError
{
fmt
.
Sprintf
(
"Invalid pipe address '%s'."
,
addr
),
false
}
}
func
timeout
(
addr
string
)
PipeError
{
return
PipeError
{
fmt
.
Sprintf
(
"Pipe IO timed out waiting for '%s'"
,
addr
),
true
}
}
// ipcListen will create a named pipe on the given endpoint.
func
ipcListen
(
endpoint
string
)
(
net
.
Listener
,
error
)
{
return
Listen
(
endpoint
)
return
winio
.
ListenPipe
(
endpoint
,
&
winio
.
PipeConfig
{}
)
}
// newIPCConnection will connect to a named pipe with the given endpoint as name.
func
newIPCConnection
(
endpoint
string
)
(
net
.
Conn
,
error
)
{
return
Dial
(
endpoint
)
timeout
:=
5
*
time
.
Second
return
winio
.
DialPipe
(
endpoint
,
&
timeout
)
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment