From 7089723ee2d39809081b56e1dc7392adbe300108 Mon Sep 17 00:00:00 2001 From: msabansal Date: Wed, 16 Mar 2016 11:07:20 -0700 Subject: [PATCH] Fixing support for static mac and updating dependencies Signed-off-by: msabansal --- libnetwork/Godeps/Godeps.json | 4 +- .../github.com/Microsoft/go-winio/backup.go | 241 ++++++ .../Microsoft/go-winio/backup_test.go | 255 ++++++ .../src/github.com/Microsoft/go-winio/file.go | 3 + .../github.com/Microsoft/go-winio/fileinfo.go | 30 + .../Microsoft/go-winio/mksyscall_windows.go | 797 ------------------ .../src/github.com/Microsoft/go-winio/pipe.go | 170 +++- .../Microsoft/go-winio/pipe_test.go | 81 +- .../Microsoft/go-winio/privilege.go | 150 ++++ .../Microsoft/go-winio/privileges_test.go | 17 + .../github.com/Microsoft/go-winio/reparse.go | 124 +++ .../src/github.com/Microsoft/go-winio/sd.go | 17 +- .../github.com/Microsoft/go-winio/syscall.go | 2 +- .../github.com/Microsoft/go-winio/zsyscall.go | 274 ++++++ .../github.com/Microsoft/hcsshim/copylayer.go | 34 - .../Microsoft/hcsshim/createprocess.go | 6 +- .../Microsoft/hcsshim/exportlayer.go | 122 ++- .../github.com/Microsoft/hcsshim/hcsshim.go | 40 +- .../github.com/Microsoft/hcsshim/hnsfuncs.go | 20 +- .../Microsoft/hcsshim/importlayer.go | 120 ++- .../github.com/Microsoft/hcsshim/legacy.go | 393 +++++++++ .../Microsoft/hcsshim/mksyscall_windows.go | 9 +- .../Microsoft/hcsshim/processimage.go | 23 + .../github.com/Microsoft/hcsshim/version.go | 7 + .../github.com/Microsoft/hcsshim/zhcsshim.go | 279 ++++-- libnetwork/drivers/windows/labels.go | 3 + libnetwork/drivers/windows/windows.go | 26 +- libnetwork/ipams/windowsipam/windowsipam.go | 7 +- .../ipams/windowsipam/windowsipam_test.go | 13 + 29 files changed, 2315 insertions(+), 952 deletions(-) create mode 100644 libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/backup.go create mode 100644 libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/backup_test.go create mode 100644 libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/fileinfo.go delete mode 100644 libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/mksyscall_windows.go create mode 100644 libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/privilege.go create mode 100644 libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/privileges_test.go create mode 100644 libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/reparse.go delete mode 100644 libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/copylayer.go create mode 100644 libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/legacy.go create mode 100644 libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/processimage.go create mode 100644 libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/version.go diff --git a/libnetwork/Godeps/Godeps.json b/libnetwork/Godeps/Godeps.json index 8fe48ac0ef..d84924d85f 100644 --- a/libnetwork/Godeps/Godeps.json +++ b/libnetwork/Godeps/Godeps.json @@ -11,11 +11,11 @@ }, { "ImportPath": "github.com/Microsoft/hcsshim", - "Rev": "43858ef3c5c944dfaaabfbe8b6ea093da7f28dba" + "Rev": "116e0e9f5ced0cec94ae46d0aa1b3002a325f532" }, { "ImportPath": "github.com/Microsoft/go-winio", - "Rev": "eb176a9831c54b88eaf9eb4fbc24b94080d910ad" + "Rev": "8f9387ea7efabb228a981b9c381142be7667967f" }, { "ImportPath": "github.com/BurntSushi/toml", diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/backup.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/backup.go new file mode 100644 index 0000000000..bfefd42c4d --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/backup.go @@ -0,0 +1,241 @@ +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 + + StreamSparseAttributes = uint32(8) +) + +// 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 +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/backup_test.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/backup_test.go new file mode 100644 index 0000000000..cc5a0c5ff0 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/backup_test.go @@ -0,0 +1,255 @@ +package winio + +import ( + "io" + "io/ioutil" + "os" + "syscall" + "testing" +) + +var testFileName string + +func TestMain(m *testing.M) { + f, err := ioutil.TempFile("", "tmp") + if err != nil { + panic(err) + } + testFileName = f.Name() + f.Close() + defer os.Remove(testFileName) + os.Exit(m.Run()) +} + +func makeTestFile(makeADS bool) error { + os.Remove(testFileName) + f, err := os.Create(testFileName) + if err != nil { + return err + } + defer f.Close() + _, err = f.Write([]byte("testing 1 2 3\n")) + if err != nil { + return err + } + if makeADS { + a, err := os.Create(testFileName + ":ads.txt") + if err != nil { + return err + } + defer a.Close() + _, err = a.Write([]byte("alternate data stream\n")) + if err != nil { + return err + } + } + return nil +} + +func TestBackupRead(t *testing.T) { + err := makeTestFile(true) + if err != nil { + t.Fatal(err) + } + + f, err := os.Open(testFileName) + if err != nil { + t.Fatal(err) + } + defer f.Close() + r := NewBackupFileReader(f, false) + defer r.Close() + b, err := ioutil.ReadAll(r) + if err != nil { + t.Fatal(err) + } + if len(b) == 0 { + t.Fatal("no data") + } +} + +func TestBackupStreamRead(t *testing.T) { + err := makeTestFile(true) + if err != nil { + t.Fatal(err) + } + + f, err := os.Open(testFileName) + if err != nil { + t.Fatal(err) + } + defer f.Close() + r := NewBackupFileReader(f, false) + defer r.Close() + + br := NewBackupStreamReader(r) + gotData := false + gotAltData := false + for { + hdr, err := br.Next() + if err == io.EOF { + break + } + if err != nil { + t.Fatal(err) + } + + switch hdr.Id { + case BackupData: + if gotData { + t.Fatal("duplicate data") + } + if hdr.Name != "" { + t.Fatalf("unexpected name %s", hdr.Name) + } + b, err := ioutil.ReadAll(br) + if err != nil { + t.Fatal(err) + } + if string(b) != "testing 1 2 3\n" { + t.Fatalf("incorrect data %v", b) + } + gotData = true + case BackupAlternateData: + if gotAltData { + t.Fatal("duplicate alt data") + } + if hdr.Name != ":ads.txt:$DATA" { + t.Fatalf("incorrect name %s", hdr.Name) + } + b, err := ioutil.ReadAll(br) + if err != nil { + t.Fatal(err) + } + if string(b) != "alternate data stream\n" { + t.Fatalf("incorrect data %v", b) + } + gotAltData = true + default: + t.Fatalf("unknown stream ID %d", hdr.Id) + } + } + if !gotData || !gotAltData { + t.Fatal("missing stream") + } +} + +func TestBackupStreamWrite(t *testing.T) { + f, err := os.Create(testFileName) + if err != nil { + t.Fatal(err) + } + defer f.Close() + w := NewBackupFileWriter(f, false) + defer w.Close() + + data := "testing 1 2 3\n" + altData := "alternate stream\n" + + br := NewBackupStreamWriter(w) + err = br.WriteHeader(&BackupHeader{Id: BackupData, Size: int64(len(data))}) + if err != nil { + t.Fatal(err) + } + n, err := br.Write([]byte(data)) + if err != nil { + t.Fatal(err) + } + if n != len(data) { + t.Fatal("short write") + } + + err = br.WriteHeader(&BackupHeader{Id: BackupAlternateData, Size: int64(len(altData)), Name: ":ads.txt:$DATA"}) + if err != nil { + t.Fatal(err) + } + n, err = br.Write([]byte(altData)) + if err != nil { + t.Fatal(err) + } + if n != len(altData) { + t.Fatal("short write") + } + + f.Close() + + b, err := ioutil.ReadFile(testFileName) + if err != nil { + t.Fatal(err) + } + if string(b) != data { + t.Fatalf("wrong data %v", b) + } + + b, err = ioutil.ReadFile(testFileName + ":ads.txt") + if err != nil { + t.Fatal(err) + } + if string(b) != altData { + t.Fatalf("wrong data %v", b) + } +} + +func makeSparseFile() error { + os.Remove(testFileName) + f, err := os.Create(testFileName) + if err != nil { + return err + } + defer f.Close() + + const ( + FSCTL_SET_SPARSE = 0x000900c4 + FSCTL_SET_ZERO_DATA = 0x000980c8 + ) + + err = syscall.DeviceIoControl(syscall.Handle(f.Fd()), FSCTL_SET_SPARSE, nil, 0, nil, 0, nil, nil) + if err != nil { + return err + } + + _, err = f.Write([]byte("testing 1 2 3\n")) + if err != nil { + return err + } + + _, err = f.Seek(1000000, 0) + if err != nil { + return err + } + + _, err = f.Write([]byte("more data later\n")) + if err != nil { + return err + } + + return nil +} + +func TestBackupSparseFile(t *testing.T) { + err := makeSparseFile() + if err != nil { + t.Fatal(err) + } + + f, err := os.Open(testFileName) + if err != nil { + t.Fatal(err) + } + defer f.Close() + r := NewBackupFileReader(f, false) + defer r.Close() + + br := NewBackupStreamReader(r) + for { + hdr, err := br.Next() + if err == io.EOF { + break + } + if err != nil { + t.Fatal(err) + } + + t.Log(hdr) + } +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/file.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/file.go index 02cd9a528c..fd16f00755 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/file.go +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/file.go @@ -13,6 +13,7 @@ import ( //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 @@ -117,6 +118,8 @@ func (f *win32File) prepareIo() (*ioOperation, error) { // 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 diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/fileinfo.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/fileinfo.go new file mode 100644 index 0000000000..dc05a8b334 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/fileinfo.go @@ -0,0 +1,30 @@ +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 + +type FileBasicInfo struct { + CreationTime, LastAccessTime, LastWriteTime, ChangeTime syscall.Filetime + FileAttributes uintptr // includes padding +} + +func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) { + bi := &FileBasicInfo{} + if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), 0, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { + return nil, &os.PathError{"GetFileInformationByHandleEx", f.Name(), err} + } + return bi, nil +} + +func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error { + if err := setFileInformationByHandle(syscall.Handle(f.Fd()), 0, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { + return &os.PathError{"SetFileInformationByHandle", f.Name(), err} + } + return nil +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/mksyscall_windows.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/mksyscall_windows.go deleted file mode 100644 index 652074c7f1..0000000000 --- a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/mksyscall_windows.go +++ /dev/null @@ -1,797 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build ignore - -/* -mksyscall_windows generates windows system call bodies - -It parses all files specified on command line containing function -prototypes (like syscall_windows.go) and prints system call bodies -to standard output. - -The prototypes are marked by lines beginning with "//sys" and read -like func declarations if //sys is replaced by func, but: - -* The parameter lists must give a name for each argument. This - includes return parameters. - -* The parameter lists must give a type for each argument: - the (x, y, z int) shorthand is not allowed. - -* If the return parameter is an error number, it must be named err. - -* If go func name needs to be different from it's winapi dll name, - the winapi name could be specified at the end, after "=" sign, like - //sys LoadLibrary(libname string) (handle uint32, err error) = LoadLibraryA - -* Each function that returns err needs to supply a condition, that - return value of winapi will be tested against to detect failure. - This would set err to windows "last-error", otherwise it will be nil. - The value can be provided at end of //sys declaration, like - //sys LoadLibrary(libname string) (handle uint32, err error) [failretval==-1] = LoadLibraryA - and is [failretval==0] by default. - -Usage: - mksyscall_windows [flags] [path ...] - -The flags are: - -output - Specify output file name (outputs to console if blank). - -trace - Generate print statement after every syscall. -*/ -package main - -import ( - "bufio" - "bytes" - "errors" - "flag" - "fmt" - "go/format" - "go/parser" - "go/token" - "io" - "io/ioutil" - "log" - "os" - "strconv" - "strings" - "text/template" -) - -var ( - filename = flag.String("output", "", "output file name (standard output if omitted)") - printTraceFlag = flag.Bool("trace", false, "generate print statement after every syscall") -) - -func trim(s string) string { - return strings.Trim(s, " \t") -} - -var packageName string - -func packagename() string { - return packageName -} - -func syscalldot() string { - if packageName == "syscall" { - return "" - } - return "syscall." -} - -// Param is function parameter -type Param struct { - Name string - Type string - fn *Fn - tmpVarIdx int -} - -// tmpVar returns temp variable name that will be used to represent p during syscall. -func (p *Param) tmpVar() string { - if p.tmpVarIdx < 0 { - p.tmpVarIdx = p.fn.curTmpVarIdx - p.fn.curTmpVarIdx++ - } - return fmt.Sprintf("_p%d", p.tmpVarIdx) -} - -// BoolTmpVarCode returns source code for bool temp variable. -func (p *Param) BoolTmpVarCode() string { - const code = `var %s uint32 - if %s { - %s = 1 - } else { - %s = 0 - }` - tmp := p.tmpVar() - return fmt.Sprintf(code, tmp, p.Name, tmp, tmp) -} - -// SliceTmpVarCode returns source code for slice temp variable. -func (p *Param) SliceTmpVarCode() string { - const code = `var %s *%s - if len(%s) > 0 { - %s = &%s[0] - }` - tmp := p.tmpVar() - return fmt.Sprintf(code, tmp, p.Type[2:], p.Name, tmp, p.Name) -} - -// StringTmpVarCode returns source code for string temp variable. -func (p *Param) StringTmpVarCode() string { - errvar := p.fn.Rets.ErrorVarName() - if errvar == "" { - errvar = "_" - } - tmp := p.tmpVar() - const code = `var %s %s - %s, %s = %s(%s)` - s := fmt.Sprintf(code, tmp, p.fn.StrconvType(), tmp, errvar, p.fn.StrconvFunc(), p.Name) - if errvar == "-" { - return s - } - const morecode = ` - if %s != nil { - return - }` - return s + fmt.Sprintf(morecode, errvar) -} - -// TmpVarCode returns source code for temp variable. -func (p *Param) TmpVarCode() string { - switch { - case p.Type == "bool": - return p.BoolTmpVarCode() - case strings.HasPrefix(p.Type, "[]"): - return p.SliceTmpVarCode() - default: - return "" - } -} - -// TmpVarHelperCode returns source code for helper's temp variable. -func (p *Param) TmpVarHelperCode() string { - if p.Type != "string" { - return "" - } - return p.StringTmpVarCode() -} - -// SyscallArgList returns source code fragments representing p parameter -// in syscall. Slices are translated into 2 syscall parameters: pointer to -// the first element and length. -func (p *Param) SyscallArgList() []string { - t := p.HelperType() - var s string - switch { - case t[0] == '*': - s = fmt.Sprintf("unsafe.Pointer(%s)", p.Name) - case t == "bool": - s = p.tmpVar() - case strings.HasPrefix(t, "[]"): - return []string{ - fmt.Sprintf("uintptr(unsafe.Pointer(%s))", p.tmpVar()), - fmt.Sprintf("uintptr(len(%s))", p.Name), - } - default: - s = p.Name - } - return []string{fmt.Sprintf("uintptr(%s)", s)} -} - -// IsError determines if p parameter is used to return error. -func (p *Param) IsError() bool { - return p.Name == "err" && p.Type == "error" -} - -// HelperType returns type of parameter p used in helper function. -func (p *Param) HelperType() string { - if p.Type == "string" { - return p.fn.StrconvType() - } - return p.Type -} - -// join concatenates parameters ps into a string with sep separator. -// Each parameter is converted into string by applying fn to it -// before conversion. -func join(ps []*Param, fn func(*Param) string, sep string) string { - if len(ps) == 0 { - return "" - } - a := make([]string, 0) - for _, p := range ps { - a = append(a, fn(p)) - } - return strings.Join(a, sep) -} - -// Rets describes function return parameters. -type Rets struct { - Name string - Type string - ReturnsError bool - FailCond string -} - -// ErrorVarName returns error variable name for r. -func (r *Rets) ErrorVarName() string { - if r.ReturnsError { - return "err" - } - if r.Type == "error" { - return r.Name - } - return "" -} - -// ToParams converts r into slice of *Param. -func (r *Rets) ToParams() []*Param { - ps := make([]*Param, 0) - if len(r.Name) > 0 { - ps = append(ps, &Param{Name: r.Name, Type: r.Type}) - } - if r.ReturnsError { - ps = append(ps, &Param{Name: "err", Type: "error"}) - } - return ps -} - -// List returns source code of syscall return parameters. -func (r *Rets) List() string { - s := join(r.ToParams(), func(p *Param) string { return p.Name + " " + p.Type }, ", ") - if len(s) > 0 { - s = "(" + s + ")" - } - return s -} - -// PrintList returns source code of trace printing part correspondent -// to syscall return values. -func (r *Rets) PrintList() string { - return join(r.ToParams(), func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `) -} - -// SetReturnValuesCode returns source code that accepts syscall return values. -func (r *Rets) SetReturnValuesCode() string { - if r.Name == "" && !r.ReturnsError { - return "" - } - retvar := "r0" - if r.Name == "" { - retvar = "r1" - } - errvar := "_" - if r.ReturnsError { - errvar = "e1" - } - return fmt.Sprintf("%s, _, %s := ", retvar, errvar) -} - -func (r *Rets) useLongHandleErrorCode(retvar string) string { - const code = `if %s { - if e1 != 0 { - err = error(e1) - } else { - err = %sEINVAL - } - }` - cond := retvar + " == 0" - if r.FailCond != "" { - cond = strings.Replace(r.FailCond, "failretval", retvar, 1) - } - return fmt.Sprintf(code, cond, syscalldot()) -} - -// SetErrorCode returns source code that sets return parameters. -func (r *Rets) SetErrorCode() string { - const code = `if r0 != 0 { - %s = %sErrno(r0) - }` - if r.Name == "" && !r.ReturnsError { - return "" - } - if r.Name == "" { - return r.useLongHandleErrorCode("r1") - } - if r.Type == "error" { - return fmt.Sprintf(code, r.Name, syscalldot()) - } - s := "" - switch { - case r.Type[0] == '*': - s = fmt.Sprintf("%s = (%s)(unsafe.Pointer(r0))", r.Name, r.Type) - case r.Type == "bool": - s = fmt.Sprintf("%s = r0 != 0", r.Name) - default: - s = fmt.Sprintf("%s = %s(r0)", r.Name, r.Type) - } - if !r.ReturnsError { - return s - } - return s + "\n\t" + r.useLongHandleErrorCode(r.Name) -} - -// Fn describes syscall function. -type Fn struct { - Name string - Params []*Param - Rets *Rets - PrintTrace bool - confirmproc bool - dllname string - dllfuncname string - src string - // TODO: get rid of this field and just use parameter index instead - curTmpVarIdx int // insure tmp variables have uniq names -} - -// extractParams parses s to extract function parameters. -func extractParams(s string, f *Fn) ([]*Param, error) { - s = trim(s) - if s == "" { - return nil, nil - } - a := strings.Split(s, ",") - ps := make([]*Param, len(a)) - for i := range ps { - s2 := trim(a[i]) - b := strings.Split(s2, " ") - if len(b) != 2 { - b = strings.Split(s2, "\t") - if len(b) != 2 { - return nil, errors.New("Could not extract function parameter from \"" + s2 + "\"") - } - } - ps[i] = &Param{ - Name: trim(b[0]), - Type: trim(b[1]), - fn: f, - tmpVarIdx: -1, - } - } - return ps, nil -} - -// extractSection extracts text out of string s starting after start -// and ending just before end. found return value will indicate success, -// and prefix, body and suffix will contain correspondent parts of string s. -func extractSection(s string, start, end rune) (prefix, body, suffix string, found bool) { - s = trim(s) - if strings.HasPrefix(s, string(start)) { - // no prefix - body = s[1:] - } else { - a := strings.SplitN(s, string(start), 2) - if len(a) != 2 { - return "", "", s, false - } - prefix = a[0] - body = a[1] - } - a := strings.SplitN(body, string(end), 2) - if len(a) != 2 { - return "", "", "", false - } - return prefix, a[0], a[1], true -} - -// newFn parses string s and return created function Fn. -func newFn(s string) (*Fn, error) { - s = trim(s) - f := &Fn{ - Rets: &Rets{}, - src: s, - PrintTrace: *printTraceFlag, - } - // function name and args - prefix, body, s, found := extractSection(s, '(', ')') - if !found || prefix == "" { - return nil, errors.New("Could not extract function name and parameters from \"" + f.src + "\"") - } - f.Name = prefix - var err error - f.Params, err = extractParams(body, f) - if err != nil { - return nil, err - } - // return values - _, body, s, found = extractSection(s, '(', ')') - if found { - r, err := extractParams(body, f) - if err != nil { - return nil, err - } - switch len(r) { - case 0: - case 1: - if r[0].IsError() { - f.Rets.ReturnsError = true - } else { - f.Rets.Name = r[0].Name - f.Rets.Type = r[0].Type - } - case 2: - if !r[1].IsError() { - return nil, errors.New("Only last windows error is allowed as second return value in \"" + f.src + "\"") - } - f.Rets.ReturnsError = true - f.Rets.Name = r[0].Name - f.Rets.Type = r[0].Type - default: - return nil, errors.New("Too many return values in \"" + f.src + "\"") - } - } - // fail condition - _, body, s, found = extractSection(s, '[', ']') - if found { - f.Rets.FailCond = body - } - // dll and dll function names - s = trim(s) - if s == "" { - return f, nil - } - if !strings.HasPrefix(s, "=") { - return nil, errors.New("Could not extract dll name from \"" + f.src + "\"") - } - s = trim(s[1:]) - a := strings.Split(s, ".") - switch len(a) { - case 1: - f.dllfuncname = a[0] - case 2: - f.dllname = a[0] - f.dllfuncname = a[1] - default: - return nil, errors.New("Could not extract dll name from \"" + f.src + "\"") - } - if f.dllfuncname[len(f.dllfuncname)-1] == '?' { - f.confirmproc = true - f.dllfuncname = f.dllfuncname[0 : len(f.dllfuncname)-1] - } - return f, nil -} - -// DLLName returns DLL name for function f. -func (f *Fn) DLLName() string { - if f.dllname == "" { - return "kernel32" - } - return f.dllname -} - -// DLLName returns DLL function name for function f. -func (f *Fn) DLLFuncName() string { - if f.dllfuncname == "" { - return f.Name - } - return f.dllfuncname -} - -func (f *Fn) ConfirmProc() bool { - return f.confirmproc -} - -// ParamList returns source code for function f parameters. -func (f *Fn) ParamList() string { - return join(f.Params, func(p *Param) string { return p.Name + " " + p.Type }, ", ") -} - -// HelperParamList returns source code for helper function f parameters. -func (f *Fn) HelperParamList() string { - return join(f.Params, func(p *Param) string { return p.Name + " " + p.HelperType() }, ", ") -} - -// ParamPrintList returns source code of trace printing part correspondent -// to syscall input parameters. -func (f *Fn) ParamPrintList() string { - return join(f.Params, func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `) -} - -// ParamCount return number of syscall parameters for function f. -func (f *Fn) ParamCount() int { - n := 0 - for _, p := range f.Params { - n += len(p.SyscallArgList()) - } - return n -} - -// SyscallParamCount determines which version of Syscall/Syscall6/Syscall9/... -// to use. It returns parameter count for correspondent SyscallX function. -func (f *Fn) SyscallParamCount() int { - n := f.ParamCount() - switch { - case n <= 3: - return 3 - case n <= 6: - return 6 - case n <= 9: - return 9 - case n <= 12: - return 12 - case n <= 15: - return 15 - default: - panic("too many arguments to system call") - } -} - -// Syscall determines which SyscallX function to use for function f. -func (f *Fn) Syscall() string { - c := f.SyscallParamCount() - if c == 3 { - return syscalldot() + "Syscall" - } - return syscalldot() + "Syscall" + strconv.Itoa(c) -} - -// SyscallParamList returns source code for SyscallX parameters for function f. -func (f *Fn) SyscallParamList() string { - a := make([]string, 0) - for _, p := range f.Params { - a = append(a, p.SyscallArgList()...) - } - for len(a) < f.SyscallParamCount() { - a = append(a, "0") - } - return strings.Join(a, ", ") -} - -// HelperCallParamList returns source code of call into function f helper. -func (f *Fn) HelperCallParamList() string { - a := make([]string, 0, len(f.Params)) - for _, p := range f.Params { - s := p.Name - if p.Type == "string" { - s = p.tmpVar() - } - a = append(a, s) - } - return strings.Join(a, ", ") -} - -// IsUTF16 is true, if f is W (utf16) function. It is false -// for all A (ascii) functions. -func (_ *Fn) IsUTF16() bool { - return true -} - -// StrconvFunc returns name of Go string to OS string function for f. -func (f *Fn) StrconvFunc() string { - if f.IsUTF16() { - return syscalldot() + "UTF16PtrFromString" - } - return syscalldot() + "BytePtrFromString" -} - -// StrconvType returns Go type name used for OS string for f. -func (f *Fn) StrconvType() string { - if f.IsUTF16() { - return "*uint16" - } - return "*byte" -} - -// HasStringParam is true, if f has at least one string parameter. -// Otherwise it is false. -func (f *Fn) HasStringParam() bool { - for _, p := range f.Params { - if p.Type == "string" { - return true - } - } - return false -} - -// HelperName returns name of function f helper. -func (f *Fn) HelperName() string { - if !f.HasStringParam() { - return f.Name - } - return "_" + f.Name -} - -// Source files and functions. -type Source struct { - Funcs []*Fn - Files []string -} - -// ParseFiles parses files listed in fs and extracts all syscall -// functions listed in sys comments. It returns source files -// and functions collection *Source if successful. -func ParseFiles(fs []string) (*Source, error) { - src := &Source{ - Funcs: make([]*Fn, 0), - Files: make([]string, 0), - } - for _, file := range fs { - if err := src.ParseFile(file); err != nil { - return nil, err - } - } - return src, nil -} - -// DLLs return dll names for a source set src. -func (src *Source) DLLs() []string { - uniq := make(map[string]bool) - r := make([]string, 0) - for _, f := range src.Funcs { - name := f.DLLName() - if _, found := uniq[name]; !found { - uniq[name] = true - r = append(r, name) - } - } - return r -} - -// ParseFile adds additional file path to a source set src. -func (src *Source) ParseFile(path string) error { - file, err := os.Open(path) - if err != nil { - return err - } - defer file.Close() - - s := bufio.NewScanner(file) - for s.Scan() { - t := trim(s.Text()) - if len(t) < 7 { - continue - } - if !strings.HasPrefix(t, "//sys") { - continue - } - t = t[5:] - if !(t[0] == ' ' || t[0] == '\t') { - continue - } - f, err := newFn(t[1:]) - if err != nil { - return err - } - src.Funcs = append(src.Funcs, f) - } - if err := s.Err(); err != nil { - return err - } - src.Files = append(src.Files, path) - - // get package name - fset := token.NewFileSet() - _, err = file.Seek(0, 0) - if err != nil { - return err - } - pkg, err := parser.ParseFile(fset, "", file, parser.PackageClauseOnly) - if err != nil { - return err - } - packageName = pkg.Name.Name - - return nil -} - -// Generate output source file from a source set src. -func (src *Source) Generate(w io.Writer) error { - funcMap := template.FuncMap{ - "packagename": packagename, - "syscalldot": syscalldot, - } - t := template.Must(template.New("main").Funcs(funcMap).Parse(srcTemplate)) - err := t.Execute(w, src) - if err != nil { - return errors.New("Failed to execute template: " + err.Error()) - } - return nil -} - -func usage() { - fmt.Fprintf(os.Stderr, "usage: mksyscall_windows [flags] [path ...]\n") - flag.PrintDefaults() - os.Exit(1) -} - -func main() { - flag.Usage = usage - flag.Parse() - if len(flag.Args()) <= 0 { - fmt.Fprintf(os.Stderr, "no files to parse provided\n") - usage() - } - - src, err := ParseFiles(flag.Args()) - if err != nil { - log.Fatal(err) - } - - var buf bytes.Buffer - if err := src.Generate(&buf); err != nil { - log.Fatal(err) - } - - data, err := format.Source(buf.Bytes()) - if err != nil { - log.Fatal(err) - } - if *filename == "" { - _, err = os.Stdout.Write(data) - } else { - err = ioutil.WriteFile(*filename, data, 0644) - } - if err != nil { - log.Fatal(err) - } -} - -// TODO: use println instead to print in the following template -const srcTemplate = ` - -{{define "main"}}// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT - -package {{packagename}} - -import "unsafe"{{if syscalldot}} -import "syscall"{{end}} - -var _ unsafe.Pointer - -var ( -{{template "dlls" .}} -{{template "funcnames" .}}) -{{range .Funcs}}{{if .HasStringParam}}{{template "helperbody" .}}{{end}}{{template "funcbody" .}}{{end}} -{{end}} - -{{/* help functions */}} - -{{define "dlls"}}{{range .DLLs}} mod{{.}} = {{syscalldot}}NewLazyDLL("{{.}}.dll") -{{end}}{{end}} - -{{define "funcnames"}}{{range .Funcs}} proc{{.DLLFuncName}} = mod{{.DLLName}}.NewProc("{{.DLLFuncName}}") -{{end}}{{end}} - -{{define "helperbody"}} -func {{.Name}}({{.ParamList}}) {{template "results" .}}{ -{{template "helpertmpvars" .}} return {{.HelperName}}({{.HelperCallParamList}}) -} -{{end}} - -{{define "funcbody"}} -func {{.HelperName}}({{.HelperParamList}}) {{template "results" .}}{ -{{template "tmpvars" .}} {{template "syscallcheck" .}}{{template "syscall" .}} -{{template "seterror" .}}{{template "printtrace" .}} return -} -{{end}} - -{{define "helpertmpvars"}}{{range .Params}}{{if .TmpVarHelperCode}} {{.TmpVarHelperCode}} -{{end}}{{end}}{{end}} - -{{define "tmpvars"}}{{range .Params}}{{if .TmpVarCode}} {{.TmpVarCode}} -{{end}}{{end}}{{end}} - -{{define "results"}}{{if .Rets.List}}{{.Rets.List}} {{end}}{{end}} - -{{define "syscallcheck"}}{{if .ConfirmProc}}if {{.Rets.ErrorVarName}} = proc{{.DLLFuncName}}.Find(); {{.Rets.ErrorVarName}} != nil { - return -} -{{end}}{{end}} - -{{define "syscall"}}{{.Rets.SetReturnValuesCode}}{{.Syscall}}(proc{{.DLLFuncName}}.Addr(), {{.ParamCount}}, {{.SyscallParamList}}){{end}} - -{{define "seterror"}}{{if .Rets.SetErrorCode}} {{.Rets.SetErrorCode}} -{{end}}{{end}} - -{{define "printtrace"}}{{if .PrintTrace}} print("SYSCALL: {{.Name}}(", {{.ParamPrintList}}") (", {{.Rets.PrintList}}")\n") -{{end}}{{end}} - -` diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/pipe.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/pipe.go index b281b5e23c..82db283061 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/pipe.go +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/pipe.go @@ -2,6 +2,7 @@ package winio import ( "errors" + "io" "net" "os" "syscall" @@ -13,6 +14,8 @@ import ( //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 @@ -36,11 +39,18 @@ const ( cNMPWAIT_USE_DEFAULT_WAIT = 0 cNMPWAIT_NOWAIT = 1 + + cPIPE_TYPE_MESSAGE = 4 + + cPIPE_READMODE_MESSAGE = 2 ) var ( - // This error should match net.errClosing since docker takes a dependency on its text + // 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 { @@ -48,6 +58,12 @@ type win32Pipe struct { path string } +type win32MessageBytePipe struct { + win32Pipe + writeClosed bool + readEOF bool +} + type pipeAddress string func (f *win32Pipe) LocalAddr() net.Addr { @@ -64,6 +80,49 @@ func (f *win32Pipe) SetDeadline(t time.Time) error { 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" } @@ -72,14 +131,6 @@ func (s pipeAddress) String() string { return string(s) } -func makeWin32Pipe(h syscall.Handle, path string) (*win32Pipe, error) { - f, err := makeWin32File(h) - if err != nil { - return nil, err - } - return &win32Pipe{f, path}, nil -} - // 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. @@ -113,18 +164,43 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) { } } if err != nil { - return nil, &os.PathError{"open", path, err} + return nil, &os.PathError{Op: "open", Path: path, Err: err} } - p, err := makeWin32Pipe(h, path) + + 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 } - return p, nil + + // 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 { - p *win32Pipe + f *win32File err error } @@ -132,39 +208,46 @@ 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, first bool) (syscall.Handle, error) { +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, cPIPE_REJECT_REMOTE_CLIENTS, cPIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa) + h, err := createNamedPipe(path, flags, mode, cPIPE_UNLIMITED_INSTANCES, uint32(c.OutputBufferSize), uint32(c.InputBufferSize), 0, &sa) if err != nil { - return 0, &os.PathError{"open", path, err} + return 0, &os.PathError{Op: "open", Path: path, Err: err} } return h, nil } -func (l *win32PipeListener) makeServerPipe() (*win32Pipe, error) { - h, err := makeServerPipeHandle(l.path, l.securityDescriptor, false) +func (l *win32PipeListener) makeServerPipe() (*win32File, error) { + h, err := makeServerPipeHandle(l.path, l.securityDescriptor, &l.config, false) if err != nil { return nil, err } - p, err := makeWin32Pipe(h, l.path) + f, err := makeWin32File(h) if err != nil { syscall.Close(h) return nil, err } - return p, nil + return f, nil } func (l *win32PipeListener) listenerRoutine() { @@ -207,18 +290,43 @@ func (l *win32PipeListener) listenerRoutine() { close(l.doneCh) } -func ListenPipe(path, sddl string) (net.Listener, error) { +// 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 sddl != "" { - sd, err = sddlToSecurityDescriptor(sddl) + if c == nil { + c = &PipeConfig{} + } + if c.SecurityDescriptor != "" { + sd, err = SddlToSecurityDescriptor(c.SecurityDescriptor) if err != nil { return nil, err } } - h, err := makeServerPipeHandle(path, sd, true) + h, err := makeServerPipeHandle(path, sd, c, true) if err != nil { return nil, err } @@ -234,6 +342,7 @@ func ListenPipe(path, sddl string) (net.Listener, error) { firstHandle: h, path: path, securityDescriptor: sd, + config: *c, acceptCh: make(chan (chan acceptResponse)), closeCh: make(chan int), doneCh: make(chan int), @@ -242,7 +351,7 @@ func ListenPipe(path, sddl string) (net.Listener, error) { return l, nil } -func connectPipe(p *win32Pipe) error { +func connectPipe(p *win32File) error { c, err := p.prepareIo() if err != nil { return err @@ -260,7 +369,16 @@ func (l *win32PipeListener) Accept() (net.Conn, error) { select { case l.acceptCh <- ch: response := <-ch - return response.p, response.err + 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 } diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/pipe_test.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/pipe_test.go index 67c86ce7a8..49dad98b32 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/pipe_test.go +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/pipe_test.go @@ -2,6 +2,7 @@ package winio import ( "bufio" + "io" "net" "os" "syscall" @@ -19,7 +20,7 @@ func TestDialUnknownFailsImmediately(t *testing.T) { } func TestDialListenerTimesOut(t *testing.T) { - l, err := ListenPipe(testPipeName, "") + l, err := ListenPipe(testPipeName, nil) if err != nil { t.Fatal(err) } @@ -32,7 +33,10 @@ func TestDialListenerTimesOut(t *testing.T) { } func TestDialAccessDeniedWithRestrictedSD(t *testing.T) { - l, err := ListenPipe(testPipeName, "D:P(A;;0x1200FF;;;WD)") + c := PipeConfig{ + SecurityDescriptor: "D:P(A;;0x1200FF;;;WD)", + } + l, err := ListenPipe(testPipeName, &c) if err != nil { t.Fatal(err) } @@ -43,8 +47,8 @@ func TestDialAccessDeniedWithRestrictedSD(t *testing.T) { } } -func getConnection() (client net.Conn, server net.Conn, err error) { - l, err := ListenPipe(testPipeName, "") +func getConnection(cfg *PipeConfig) (client net.Conn, server net.Conn, err error) { + l, err := ListenPipe(testPipeName, cfg) if err != nil { return } @@ -77,7 +81,7 @@ func getConnection() (client net.Conn, server net.Conn, err error) { } func TestReadTimeout(t *testing.T) { - c, s, err := getConnection() + c, s, err := getConnection(nil) if err != nil { t.Fatal(err) } @@ -116,7 +120,7 @@ func server(l net.Listener, ch chan int) { } func TestFullListenDialReadWrite(t *testing.T) { - l, err := ListenPipe(testPipeName, "") + l, err := ListenPipe(testPipeName, nil) if err != nil { t.Fatal(err) } @@ -154,7 +158,7 @@ func TestFullListenDialReadWrite(t *testing.T) { } func TestCloseAbortsListen(t *testing.T) { - l, err := ListenPipe(testPipeName, "") + l, err := ListenPipe(testPipeName, nil) if err != nil { t.Fatal(err) } @@ -174,8 +178,67 @@ func TestCloseAbortsListen(t *testing.T) { } } +func ensureEOFOnClose(t *testing.T, r io.Reader, w io.Closer) { + b := make([]byte, 10) + w.Close() + n, err := r.Read(b) + if n > 0 { + t.Errorf("unexpected byte count %d", n) + } + if err != io.EOF { + t.Errorf("expected EOF: %v", err) + } +} + +func TestCloseClientEOFServer(t *testing.T) { + c, s, err := getConnection(nil) + if err != nil { + t.Fatal(err) + } + defer c.Close() + defer s.Close() + ensureEOFOnClose(t, c, s) +} + +func TestCloseServerEOFClient(t *testing.T) { + c, s, err := getConnection(nil) + if err != nil { + t.Fatal(err) + } + defer c.Close() + defer s.Close() + ensureEOFOnClose(t, s, c) +} + +func TestCloseWriteEOF(t *testing.T) { + cfg := &PipeConfig{ + MessageMode: true, + } + c, s, err := getConnection(cfg) + if err != nil { + t.Fatal(err) + } + defer c.Close() + defer s.Close() + + type closeWriter interface { + CloseWrite() error + } + + err = c.(closeWriter).CloseWrite() + if err != nil { + t.Fatal(err) + } + + b := make([]byte, 10) + _, err = s.Read(b) + if err != io.EOF { + t.Fatal(err) + } +} + func TestAcceptAfterCloseFails(t *testing.T) { - l, err := ListenPipe(testPipeName, "") + l, err := ListenPipe(testPipeName, nil) if err != nil { t.Fatal(err) } @@ -187,7 +250,7 @@ func TestAcceptAfterCloseFails(t *testing.T) { } func TestDialTimesOutByDefault(t *testing.T) { - l, err := ListenPipe(testPipeName, "") + l, err := ListenPipe(testPipeName, nil) if err != nil { t.Fatal(err) } diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/privilege.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/privilege.go new file mode 100644 index 0000000000..81f9af7b70 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/privilege.go @@ -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("", 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("", 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) +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/privileges_test.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/privileges_test.go new file mode 100644 index 0000000000..5e94c48c23 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/privileges_test.go @@ -0,0 +1,17 @@ +package winio + +import "testing" + +func TestRunWithUnavailablePrivilege(t *testing.T) { + err := RunWithPrivilege("SeCreateTokenPrivilege", func() error { return nil }) + if _, ok := err.(*PrivilegeError); err == nil || !ok { + t.Fatal("expected PrivilegeError") + } +} + +func TestRunWithPrivileges(t *testing.T) { + err := RunWithPrivilege("SeShutdownPrivilege", func() error { return nil }) + if err != nil { + t.Fatal(err) + } +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/reparse.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/reparse.go new file mode 100644 index 0000000000..96d7b9a877 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/reparse.go @@ -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() +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/sd.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/sd.go index 2df5a7c52b..60ab56ce7a 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/sd.go +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/sd.go @@ -8,6 +8,7 @@ import ( //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 @@ -70,7 +71,7 @@ func LookupSidByName(name string) (sid string, err error) { return sid, nil } -func sddlToSecurityDescriptor(sddl string) ([]byte, error) { +func SddlToSecurityDescriptor(sddl string) ([]byte, error) { var sdBuffer uintptr err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil) if err != nil { @@ -78,6 +79,18 @@ func sddlToSecurityDescriptor(sddl string) ([]byte, error) { } defer localFree(sdBuffer) sd := make([]byte, getSecurityDescriptorLength(sdBuffer)) - copy(sd, (*[1 << 30]byte)(unsafe.Pointer(sdBuffer))[:len(sd)]) + 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 +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/syscall.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/syscall.go index 20767dcb22..96fdff7b49 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/syscall.go +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/syscall.go @@ -1,3 +1,3 @@ package winio -//go:generate go run mksyscall_windows.go -output zsyscall.go file.go pipe.go sd.go +//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 diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/zsyscall.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/zsyscall.go index bfe4ac3470..74b6e97a66 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/zsyscall.go +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/go-winio/zsyscall.go @@ -9,21 +9,38 @@ 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) { @@ -75,6 +92,12 @@ func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err erro 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 { @@ -152,6 +175,30 @@ func _waitNamedPipe(name *uint16, timeout uint32) (err error) { 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) @@ -206,6 +253,18 @@ func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision 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 @@ -216,3 +275,218 @@ func getSecurityDescriptorLength(sd uintptr) (len uint32) { 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 +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/copylayer.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/copylayer.go deleted file mode 100644 index abbe134e20..0000000000 --- a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/copylayer.go +++ /dev/null @@ -1,34 +0,0 @@ -package hcsshim - -import "github.com/Sirupsen/logrus" - -// CopyLayer performs a commit of the srcId (which is expected to be a read-write -// layer) into a new read-only layer at dstId. This requires the full list of -// on-disk paths to parent layers, provided in parentLayerPaths, in order to -// complete the commit. -func CopyLayer(info DriverInfo, srcId, dstId string, parentLayerPaths []string) error { - title := "hcsshim::CopyLayer " - logrus.Debugf(title+"srcId %s dstId", srcId, dstId) - - // Generate layer descriptors - layers, err := layerPathsToDescriptors(parentLayerPaths) - if err != nil { - return err - } - - // Convert info to API calling convention - infop, err := convertDriverInfo(info) - if err != nil { - return err - } - - err = copyLayer(&infop, srcId, dstId, layers) - if err != nil { - err = makeErrorf(err, title, "srcId=%s dstId=%d", srcId, dstId) - logrus.Error(err) - return err - } - - logrus.Debugf(title+" - succeeded srcId=%s dstId=%s", srcId, dstId) - return nil -} diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/createprocess.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/createprocess.go index b055ccb664..a2b6298546 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/createprocess.go +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/createprocess.go @@ -82,12 +82,12 @@ func CreateProcessInComputeSystem(id string, useStdin bool, useStdout bool, useS err = createProcessWithStdHandlesInComputeSystem(id, string(paramsJson), &pid, stdinParam, stdoutParam, stderrParam) if err != nil { herr := makeErrorf(err, title, "id=%s params=%v", id, params) - err = herr // Windows TP4: Hyper-V Containers may return this error with more than one // concurrent exec. Do not log it as an error - if herr.Err != WSAEINVAL { - logrus.Error(err) + if err != WSAEINVAL { + logrus.Error(herr) } + err = herr return } diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/exportlayer.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/exportlayer.go index 629dc04d5a..e197d575e5 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/exportlayer.go +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/exportlayer.go @@ -1,6 +1,15 @@ package hcsshim -import "github.com/Sirupsen/logrus" +import ( + "io" + "io/ioutil" + "os" + "runtime" + "syscall" + + "github.com/Microsoft/go-winio" + "github.com/Sirupsen/logrus" +) // ExportLayer will create a folder at exportFolderPath and fill that folder with // the transport format version of the layer identified by layerId. This transport @@ -34,3 +43,114 @@ func ExportLayer(info DriverInfo, layerId string, exportFolderPath string, paren logrus.Debugf(title+"succeeded flavour=%d layerId=%s folder=%s", info.Flavour, layerId, exportFolderPath) return nil } + +type LayerReader interface { + Next() (string, int64, *winio.FileBasicInfo, error) + Read(b []byte) (int, error) + Close() error +} + +// FilterLayerReader provides an interface for extracting the contents of an on-disk layer. +type FilterLayerReader struct { + context uintptr +} + +// Next reads the next available file from a layer, ensuring that parent directories are always read +// before child files and directories. +// +// Next returns the file's relative path, size, and basic file metadata. Read() should be used to +// extract a Win32 backup stream with the remainder of the metadata and the data. +func (r *FilterLayerReader) Next() (string, int64, *winio.FileBasicInfo, error) { + var fileNamep *uint16 + fileInfo := &winio.FileBasicInfo{} + var deleted uint32 + var fileSize int64 + err := exportLayerNext(r.context, &fileNamep, fileInfo, &fileSize, &deleted) + if err != nil { + if err == syscall.ERROR_NO_MORE_FILES { + err = io.EOF + } else { + err = makeError(err, "ExportLayerNext", "") + } + return "", 0, nil, err + } + fileName := convertAndFreeCoTaskMemString(fileNamep) + if deleted != 0 { + fileInfo = nil + } + if fileName[0] == '\\' { + fileName = fileName[1:] + } + return fileName, fileSize, fileInfo, nil +} + +// Read reads from the current file's Win32 backup stream. +func (r *FilterLayerReader) Read(b []byte) (int, error) { + var bytesRead uint32 + err := exportLayerRead(r.context, b, &bytesRead) + if err != nil { + return 0, makeError(err, "ExportLayerRead", "") + } + if bytesRead == 0 { + return 0, io.EOF + } + return int(bytesRead), nil +} + +// Close frees resources associated with the layer reader. It will return an +// error if there was an error while reading the layer or of the layer was not +// completely read. +func (r *FilterLayerReader) Close() (err error) { + if r.context != 0 { + err = exportLayerEnd(r.context) + if err != nil { + err = makeError(err, "ExportLayerEnd", "") + } + r.context = 0 + } + return +} + +// NewLayerReader returns a new layer reader for reading the contents of an on-disk layer. +func NewLayerReader(info DriverInfo, layerId string, parentLayerPaths []string) (LayerReader, error) { + if procExportLayerBegin.Find() != nil { + // The new layer reader is not available on this Windows build. Fall back to the + // legacy export code path. + path, err := ioutil.TempDir("", "hcs") + if err != nil { + return nil, err + } + err = ExportLayer(info, layerId, path, parentLayerPaths) + if err != nil { + os.RemoveAll(path) + return nil, err + } + return &legacyLayerReaderWrapper{NewLegacyLayerReader(path)}, nil + } + + layers, err := layerPathsToDescriptors(parentLayerPaths) + if err != nil { + return nil, err + } + infop, err := convertDriverInfo(info) + if err != nil { + return nil, err + } + r := &FilterLayerReader{} + err = exportLayerBegin(&infop, layerId, layers, &r.context) + if err != nil { + return nil, makeError(err, "ExportLayerBegin", "") + } + runtime.SetFinalizer(r, func(r *FilterLayerReader) { r.Close() }) + return r, err +} + +type legacyLayerReaderWrapper struct { + *LegacyLayerReader +} + +func (r *legacyLayerReaderWrapper) Close() error { + err := r.LegacyLayerReader.Close() + os.RemoveAll(r.root) + return err +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/hcsshim.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/hcsshim.go index 339b632ad3..88650b8ac9 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/hcsshim.go +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/hcsshim.go @@ -27,6 +27,18 @@ import ( //sys nameToGuid(name string, guid *GUID) (hr error) = vmcompute.NameToGuid? //sys prepareLayer(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.PrepareLayer? //sys unprepareLayer(info *driverInfo, id string) (hr error) = vmcompute.UnprepareLayer? +//sys processBaseImage(path string) (hr error) = vmcompute.ProcessBaseImage? +//sys processUtilityImage(path string) (hr error) = vmcompute.ProcessUtilityImage? + +//sys importLayerBegin(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) = vmcompute.ImportLayerBegin? +//sys importLayerNext(context uintptr, fileName string, fileInfo *winio.FileBasicInfo) (hr error) = vmcompute.ImportLayerNext? +//sys importLayerWrite(context uintptr, buffer []byte) (hr error) = vmcompute.ImportLayerWrite? +//sys importLayerEnd(context uintptr) (hr error) = vmcompute.ImportLayerEnd? + +//sys exportLayerBegin(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) = vmcompute.ExportLayerBegin? +//sys exportLayerNext(context uintptr, fileName **uint16, fileInfo *winio.FileBasicInfo, fileSize *int64, deleted *uint32) (hr error) = vmcompute.ExportLayerNext? +//sys exportLayerRead(context uintptr, buffer []byte, bytesRead *uint32) (hr error) = vmcompute.ExportLayerRead? +//sys exportLayerEnd(context uintptr) (hr error) = vmcompute.ExportLayerEnd? //sys createComputeSystem(id string, configuration string) (hr error) = vmcompute.CreateComputeSystem? //sys createProcessWithStdHandlesInComputeSystem(id string, paramsJson string, pid *uint32, stdin *syscall.Handle, stdout *syscall.Handle, stderr *syscall.Handle) (hr error) = vmcompute.CreateProcessWithStdHandlesInComputeSystem? @@ -57,16 +69,15 @@ type HcsError struct { Err error } -func makeError(err error, title, rest string) *HcsError { - if hr, ok := err.(syscall.Errno); ok { - // Convert the HRESULT to a Win32 error code so that it better matches - // error codes returned from go and other packages. - err = syscall.Errno(win32FromHresult(uint32(hr))) +func makeError(err error, title, rest string) error { + // Pass through DLL errors directly since they do not originate from HCS. + if _, ok := err.(*syscall.DLLError); ok { + return err } return &HcsError{title, rest, err} } -func makeErrorf(err error, title, format string, a ...interface{}) *HcsError { +func makeErrorf(err error, title, format string, a ...interface{}) error { return makeError(err, title, fmt.Sprintf(format, a...)) } @@ -75,12 +86,12 @@ func win32FromError(err error) uint32 { return win32FromError(herr.Err) } if code, ok := err.(syscall.Errno); ok { - return win32FromHresult(uint32(code)) + return uint32(code) } return uint32(ERROR_GEN_FAILURE) } -func win32FromHresult(hr uint32) uint32 { +func win32FromHresult(hr uintptr) uintptr { if hr&0x1fff0000 == 0x00070000 { return hr & 0xffff } @@ -88,7 +99,18 @@ func win32FromHresult(hr uint32) uint32 { } func (e *HcsError) Error() string { - return fmt.Sprintf("%s- Win32 API call returned error r1=0x%x err=%s%s", e.title, win32FromError(e.Err), e.Err, e.rest) + s := e.title + if len(s) > 0 && s[len(s)-1] != ' ' { + s += " " + } + s += fmt.Sprintf("failed in Win32: %s (0x%x)", e.Err, win32FromError(e.Err)) + if e.rest != "" { + if e.rest[0] != ' ' { + s += " " + } + s += e.rest + } + return s } func convertAndFreeCoTaskMemString(buffer *uint16) string { diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/hnsfuncs.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/hnsfuncs.go index affff7f86f..590d6e381f 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/hnsfuncs.go +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/hnsfuncs.go @@ -36,12 +36,16 @@ type MacPool struct { // HNSNetwork represents a network in HNS type HNSNetwork struct { - Id string `json:",omitempty"` - Name string `json:",omitempty"` - Type string `json:",omitempty"` - Policies []json.RawMessage `json:",omitempty"` - MacPools []MacPool `json:",omitempty"` - Subnets []Subnet `json:",omitempty"` + Id string `json:",omitempty"` + Name string `json:",omitempty"` + Type string `json:",omitempty"` + NetworkAdapterName string `json:",omitempty"` + SourceMac string `json:",omitempty"` + Policies []json.RawMessage `json:",omitempty"` + MacPools []MacPool `json:",omitempty"` + Subnets []Subnet `json:",omitempty"` + DNSSuffix string `json:",omitempty"` + DNSServerList string `json:",omitempty"` } // HNSEndpoint represents a network endpoint in HNS @@ -53,6 +57,10 @@ type HNSEndpoint struct { Policies []json.RawMessage `json:",omitempty"` MacAddress string `json:",omitempty"` IPAddress net.IP `json:",omitempty"` + DNSSuffix string `json:",omitempty"` + DNSServerList string `json:",omitempty"` + GatewayAddress string `json:",omitempty"` + PrefixLength uint8 `json:",omitempty"` } type hnsNetworkResponse struct { diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/importlayer.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/importlayer.go index 3ab1124e9c..800b300b3a 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/importlayer.go +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/importlayer.go @@ -1,6 +1,13 @@ package hcsshim -import "github.com/Sirupsen/logrus" +import ( + "io/ioutil" + "os" + "runtime" + + "github.com/Microsoft/go-winio" + "github.com/Sirupsen/logrus" +) // ImportLayer will take the contents of the folder at importFolderPath and import // that into a layer with the id layerId. Note that in order to correctly populate @@ -33,3 +40,114 @@ func ImportLayer(info DriverInfo, layerId string, importFolderPath string, paren logrus.Debugf(title+"succeeded flavour=%d layerId=%s folder=%s", info.Flavour, layerId, importFolderPath) return nil } + +type LayerWriter interface { + Add(name string, fileInfo *winio.FileBasicInfo) error + Remove(name string) error + Write(b []byte) (int, error) + Close() error +} + +// FilterLayerWriter provides an interface to write the contents of a layer to the file system. +type FilterLayerWriter struct { + context uintptr +} + +// Add adds a file or directory to the layer. The file's parent directory must have already been added. +// +// name contains the file's relative path. fileInfo contains file times and file attributes; the rest +// of the file metadata and the file data must be written as a Win32 backup stream to the Write() method. +// winio.BackupStreamWriter can be used to facilitate this. +func (w *FilterLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) error { + if name[0] != '\\' { + name = `\` + name + } + err := importLayerNext(w.context, name, fileInfo) + if err != nil { + return makeError(err, "ImportLayerNext", "") + } + return nil +} + +// Remove removes a file from the layer. The file must have been present in the parent layer. +// +// name contains the file's relative path. +func (w *FilterLayerWriter) Remove(name string) error { + if name[0] != '\\' { + name = `\` + name + } + err := importLayerNext(w.context, name, nil) + if err != nil { + return makeError(err, "ImportLayerNext", "") + } + return nil +} + +// Write writes more backup stream data to the current file. +func (w *FilterLayerWriter) Write(b []byte) (int, error) { + err := importLayerWrite(w.context, b) + if err != nil { + err = makeError(err, "ImportLayerWrite", "") + return 0, err + } + return len(b), err +} + +// Close completes the layer write operation. The error must be checked to ensure that the +// operation was successful. +func (w *FilterLayerWriter) Close() (err error) { + if w.context != 0 { + err = importLayerEnd(w.context) + if err != nil { + err = makeError(err, "ImportLayerEnd", "") + } + w.context = 0 + } + return +} + +type legacyLayerWriterWrapper struct { + *LegacyLayerWriter + info DriverInfo + layerId string + parentLayerPaths []string +} + +func (r *legacyLayerWriterWrapper) Close() error { + err := r.LegacyLayerWriter.Close() + if err == nil { + err = ImportLayer(r.info, r.layerId, r.root, r.parentLayerPaths) + } + os.RemoveAll(r.root) + return err +} + +// NewLayerWriter returns a new layer writer for creating a layer on disk. +func NewLayerWriter(info DriverInfo, layerId string, parentLayerPaths []string) (LayerWriter, error) { + if procImportLayerBegin.Find() != nil { + // The new layer reader is not available on this Windows build. Fall back to the + // legacy export code path. + path, err := ioutil.TempDir("", "hcs") + if err != nil { + return nil, err + } + return &legacyLayerWriterWrapper{NewLegacyLayerWriter(path), info, layerId, parentLayerPaths}, nil + } + layers, err := layerPathsToDescriptors(parentLayerPaths) + if err != nil { + return nil, err + } + + infop, err := convertDriverInfo(info) + if err != nil { + return nil, err + } + + w := &FilterLayerWriter{} + err = importLayerBegin(&infop, layerId, layers, &w.context) + if err != nil { + return nil, makeError(err, "ImportLayerStart", "") + } + runtime.SetFinalizer(w, func(w *FilterLayerWriter) { w.Close() }) + return w, nil +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/legacy.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/legacy.go new file mode 100644 index 0000000000..bc31f23656 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/legacy.go @@ -0,0 +1,393 @@ +package hcsshim + +import ( + "bufio" + "encoding/binary" + "errors" + "io" + "os" + "path/filepath" + "strings" + "syscall" + + "github.com/Microsoft/go-winio" +) + +var errorIterationCanceled = errors.New("") + +func openFileOrDir(path string, mode uint32, createDisposition uint32) (file *os.File, err error) { + winPath, err := syscall.UTF16FromString(path) + if err != nil { + return + } + h, err := syscall.CreateFile(&winPath[0], mode, syscall.FILE_SHARE_READ, nil, createDisposition, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) + if err != nil { + err = &os.PathError{"open", path, err} + return + } + file = os.NewFile(uintptr(h), path) + return +} + +type fileEntry struct { + path string + fi os.FileInfo + err error +} + +type LegacyLayerReader struct { + root string + result chan *fileEntry + proceed chan bool + currentFile *os.File + backupReader *winio.BackupFileReader + isTP4Format bool +} + +// NewLegacyLayerReader returns a new LayerReader that can read the Windows +// TP4 transport format from disk. +func NewLegacyLayerReader(root string) *LegacyLayerReader { + r := &LegacyLayerReader{ + root: root, + result: make(chan *fileEntry), + proceed: make(chan bool), + isTP4Format: IsTP4(), + } + go r.walk() + return r +} + +func readTombstones(path string) (map[string]([]string), error) { + tf, err := os.Open(filepath.Join(path, "tombstones.txt")) + if err != nil { + return nil, err + } + defer tf.Close() + s := bufio.NewScanner(tf) + if !s.Scan() || s.Text() != "\xef\xbb\xbfVersion 1.0" { + return nil, errors.New("Invalid tombstones file") + } + + ts := make(map[string]([]string)) + for s.Scan() { + t := s.Text()[1:] // skip leading `\` + dir := filepath.Dir(t) + ts[dir] = append(ts[dir], t) + } + if err = s.Err(); err != nil { + return nil, err + } + + return ts, nil +} + +func (r *LegacyLayerReader) walk() { + defer close(r.result) + if !<-r.proceed { + return + } + + ts, err := readTombstones(r.root) + if err != nil { + goto ErrorLoop + } + + err = filepath.Walk(r.root, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if path == r.root || path == filepath.Join(r.root, "tombstones.txt") || strings.HasSuffix(path, ".$wcidirs$") { + return nil + } + r.result <- &fileEntry{path, info, nil} + if !<-r.proceed { + return errorIterationCanceled + } + + // List all the tombstones. + if info.IsDir() { + relPath, err := filepath.Rel(r.root, path) + if err != nil { + return err + } + if dts, ok := ts[relPath]; ok { + for _, t := range dts { + r.result <- &fileEntry{t, nil, nil} + if !<-r.proceed { + return errorIterationCanceled + } + } + } + } + return nil + }) + if err == errorIterationCanceled { + return + } + if err == nil { + err = io.EOF + } + +ErrorLoop: + for { + r.result <- &fileEntry{err: err} + if !<-r.proceed { + break + } + } +} + +func (r *LegacyLayerReader) reset() { + if r.backupReader != nil { + r.backupReader.Close() + r.backupReader = nil + } + if r.currentFile != nil { + r.currentFile.Close() + r.currentFile = nil + } +} + +func findBackupStreamSize(r io.Reader) (int64, error) { + br := winio.NewBackupStreamReader(r) + for { + hdr, err := br.Next() + if err != nil { + if err == io.EOF { + err = nil + } + return 0, err + } + if hdr.Id == winio.BackupData { + return hdr.Size, nil + } + } +} + +func (r *LegacyLayerReader) Next() (path string, size int64, fileInfo *winio.FileBasicInfo, err error) { + r.reset() + r.proceed <- true + fe := <-r.result + if fe == nil { + err = errors.New("LegacyLayerReader closed") + return + } + if fe.err != nil { + err = fe.err + return + } + + path, err = filepath.Rel(r.root, fe.path) + if err != nil { + return + } + + if fe.fi == nil { + // This is a tombstone. Return a nil fileInfo. + return + } + + if fe.fi.IsDir() && strings.HasPrefix(path, `Files\`) { + fe.path += ".$wcidirs$" + } + + f, err := openFileOrDir(fe.path, syscall.GENERIC_READ, syscall.OPEN_EXISTING) + if err != nil { + return + } + defer func() { + if f != nil { + f.Close() + } + }() + + fileInfo, err = winio.GetFileBasicInfo(f) + if err != nil { + return + } + + if !strings.HasPrefix(path, `Files\`) { + size = fe.fi.Size() + r.backupReader = winio.NewBackupFileReader(f, false) + if path == "Hives" || path == "Files" { + // The Hives directory has a non-deterministic file time because of the + // nature of the import process. Use the times from System_Delta. + var g *os.File + g, err = os.Open(filepath.Join(r.root, `Hives\System_Delta`)) + if err != nil { + return + } + attr := fileInfo.FileAttributes + fileInfo, err = winio.GetFileBasicInfo(g) + g.Close() + if err != nil { + return + } + fileInfo.FileAttributes = attr + } + + // The creation time and access time get reset for files outside of the Files path. + fileInfo.CreationTime = fileInfo.LastWriteTime + fileInfo.LastAccessTime = fileInfo.LastWriteTime + + } else { + beginning := int64(0) + if !r.isTP4Format { + // In TP5, the file attributes were added before the backup stream + var attr uint32 + err = binary.Read(f, binary.LittleEndian, &attr) + if err != nil { + return + } + fileInfo.FileAttributes = uintptr(attr) + beginning = 4 + } + + // Find the accurate file size. + if !fe.fi.IsDir() { + size, err = findBackupStreamSize(f) + if err != nil { + err = &os.PathError{"findBackupStreamSize", fe.path, err} + return + } + } + + // Return back to the beginning of the backup stream. + _, err = f.Seek(beginning, 0) + if err != nil { + return + } + } + + r.currentFile = f + f = nil + return +} + +func (r *LegacyLayerReader) Read(b []byte) (int, error) { + if r.backupReader == nil { + if r.currentFile == nil { + return 0, io.EOF + } + return r.currentFile.Read(b) + } + return r.backupReader.Read(b) +} + +func (r *LegacyLayerReader) Close() error { + r.proceed <- false + <-r.result + r.reset() + return nil +} + +type LegacyLayerWriter struct { + root string + currentFile *os.File + backupWriter *winio.BackupFileWriter + tombstones []string + isTP4Format bool +} + +// NewLegacyLayerWriter returns a LayerWriter that can write the TP4 transport format +// to disk. +func NewLegacyLayerWriter(root string) *LegacyLayerWriter { + return &LegacyLayerWriter{ + root: root, + isTP4Format: IsTP4(), + } +} + +func (w *LegacyLayerWriter) reset() { + if w.backupWriter != nil { + w.backupWriter.Close() + w.backupWriter = nil + } + if w.currentFile != nil { + w.currentFile.Close() + w.currentFile = nil + } +} + +func (w *LegacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) error { + w.reset() + path := filepath.Join(w.root, name) + + createDisposition := uint32(syscall.CREATE_NEW) + if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 { + err := os.Mkdir(path, 0) + if err != nil { + return err + } + path += ".$wcidirs$" + } + + f, err := openFileOrDir(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, createDisposition) + if err != nil { + return err + } + defer func() { + if f != nil { + f.Close() + os.Remove(path) + } + }() + + strippedFi := *fileInfo + strippedFi.FileAttributes = 0 + err = winio.SetFileBasicInfo(f, &strippedFi) + if err != nil { + return err + } + + if strings.HasPrefix(name, `Hives\`) { + w.backupWriter = winio.NewBackupFileWriter(f, false) + } else { + if !w.isTP4Format { + // In TP5, the file attributes were added to the header + err = binary.Write(f, binary.LittleEndian, uint32(fileInfo.FileAttributes)) + if err != nil { + return err + } + } + } + + w.currentFile = f + f = nil + return nil +} + +func (w *LegacyLayerWriter) Remove(name string) error { + w.tombstones = append(w.tombstones, name) + return nil +} + +func (w *LegacyLayerWriter) Write(b []byte) (int, error) { + if w.backupWriter == nil { + if w.currentFile == nil { + return 0, errors.New("closed") + } + return w.currentFile.Write(b) + } + return w.backupWriter.Write(b) +} + +func (w *LegacyLayerWriter) Close() error { + w.reset() + tf, err := os.Create(filepath.Join(w.root, "tombstones.txt")) + if err != nil { + return err + } + defer tf.Close() + _, err = tf.Write([]byte("\xef\xbb\xbfVersion 1.0\n")) + if err != nil { + return err + } + for _, t := range w.tombstones { + _, err = tf.Write([]byte(filepath.Join(`\`, t) + "\n")) + if err != nil { + return err + } + } + return nil +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/mksyscall_windows.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/mksyscall_windows.go index 652074c7f1..7c9a00110a 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/mksyscall_windows.go +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/mksyscall_windows.go @@ -294,6 +294,9 @@ func (r *Rets) SetErrorCode() string { const code = `if r0 != 0 { %s = %sErrno(r0) }` + const hrCode = `if int32(r0) < 0 { + %s = %sErrno(win32FromHresult(r0)) + }` if r.Name == "" && !r.ReturnsError { return "" } @@ -301,7 +304,11 @@ func (r *Rets) SetErrorCode() string { return r.useLongHandleErrorCode("r1") } if r.Type == "error" { - return fmt.Sprintf(code, r.Name, syscalldot()) + if r.Name == "hr" { + return fmt.Sprintf(hrCode, r.Name, syscalldot()) + } else { + return fmt.Sprintf(code, r.Name, syscalldot()) + } } s := "" switch { diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/processimage.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/processimage.go new file mode 100644 index 0000000000..fadb1b92c5 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/processimage.go @@ -0,0 +1,23 @@ +package hcsshim + +import "os" + +// ProcessBaseLayer post-processes a base layer that has had its files extracted. +// The files should have been extracted to \Files. +func ProcessBaseLayer(path string) error { + err := processBaseImage(path) + if err != nil { + return &os.PathError{Op: "ProcessBaseLayer", Path: path, Err: err} + } + return nil +} + +// ProcessUtilityVMImage post-processes a utility VM image that has had its files extracted. +// The files should have been extracted to \Files. +func ProcessUtilityVMImage(path string) error { + err := processUtilityImage(path) + if err != nil { + return &os.PathError{Op: "ProcessUtilityVMImage", Path: path, Err: err} + } + return nil +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/version.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/version.go new file mode 100644 index 0000000000..ae10c23d42 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/version.go @@ -0,0 +1,7 @@ +package hcsshim + +// IsTP4 returns whether the currently running Windows build is at least TP4. +func IsTP4() bool { + // HNSCall was not present in TP4 + return procHNSCall.Find() != nil +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/zhcsshim.go b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/zhcsshim.go index 15528aaa23..03924b6fcc 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/zhcsshim.go +++ b/libnetwork/Godeps/_workspace/src/github.com/Microsoft/hcsshim/zhcsshim.go @@ -2,7 +2,11 @@ package hcsshim -import "unsafe" +import ( + "unsafe" + + "github.com/Microsoft/go-winio" +) import "syscall" var _ unsafe.Pointer @@ -26,6 +30,16 @@ var ( procNameToGuid = modvmcompute.NewProc("NameToGuid") procPrepareLayer = modvmcompute.NewProc("PrepareLayer") procUnprepareLayer = modvmcompute.NewProc("UnprepareLayer") + procProcessBaseImage = modvmcompute.NewProc("ProcessBaseImage") + procProcessUtilityImage = modvmcompute.NewProc("ProcessUtilityImage") + procImportLayerBegin = modvmcompute.NewProc("ImportLayerBegin") + procImportLayerNext = modvmcompute.NewProc("ImportLayerNext") + procImportLayerWrite = modvmcompute.NewProc("ImportLayerWrite") + procImportLayerEnd = modvmcompute.NewProc("ImportLayerEnd") + procExportLayerBegin = modvmcompute.NewProc("ExportLayerBegin") + procExportLayerNext = modvmcompute.NewProc("ExportLayerNext") + procExportLayerRead = modvmcompute.NewProc("ExportLayerRead") + procExportLayerEnd = modvmcompute.NewProc("ExportLayerEnd") procCreateComputeSystem = modvmcompute.NewProc("CreateComputeSystem") procCreateProcessWithStdHandlesInComputeSystem = modvmcompute.NewProc("CreateProcessWithStdHandlesInComputeSystem") procResizeConsoleInComputeSystem = modvmcompute.NewProc("ResizeConsoleInComputeSystem") @@ -56,8 +70,8 @@ func _activateLayer(info *driverInfo, id *uint16) (hr error) { return } r0, _, _ := syscall.Syscall(procActivateLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -85,8 +99,8 @@ func _copyLayer(info *driverInfo, srcId *uint16, dstId *uint16, descriptors []WC return } r0, _, _ := syscall.Syscall6(procCopyLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(srcId)), uintptr(unsafe.Pointer(dstId)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -110,8 +124,8 @@ func _createLayer(info *driverInfo, id *uint16, parent *uint16) (hr error) { return } r0, _, _ := syscall.Syscall(procCreateLayer.Addr(), 3, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(parent))) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -139,8 +153,8 @@ func _createSandboxLayer(info *driverInfo, id *uint16, parent *uint16, descripto return } r0, _, _ := syscall.Syscall6(procCreateSandboxLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(parent)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -159,8 +173,8 @@ func _deactivateLayer(info *driverInfo, id *uint16) (hr error) { return } r0, _, _ := syscall.Syscall(procDeactivateLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -179,8 +193,8 @@ func _destroyLayer(info *driverInfo, id *uint16) (hr error) { return } r0, _, _ := syscall.Syscall(procDestroyLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -208,8 +222,8 @@ func _exportLayer(info *driverInfo, id *uint16, path *uint16, descriptors []WC_L return } r0, _, _ := syscall.Syscall6(procExportLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -228,8 +242,8 @@ func _getLayerMountPath(info *driverInfo, id *uint16, length *uintptr, buffer *u return } r0, _, _ := syscall.Syscall6(procGetLayerMountPath.Addr(), 4, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(length)), uintptr(unsafe.Pointer(buffer)), 0, 0) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -239,8 +253,8 @@ func getBaseImages(buffer **uint16) (hr error) { return } r0, _, _ := syscall.Syscall(procGetBaseImages.Addr(), 1, uintptr(unsafe.Pointer(buffer)), 0, 0) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -268,8 +282,8 @@ func _importLayer(info *driverInfo, id *uint16, path *uint16, descriptors []WC_L return } r0, _, _ := syscall.Syscall6(procImportLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -288,8 +302,8 @@ func _layerExists(info *driverInfo, id *uint16, exists *uint32) (hr error) { return } r0, _, _ := syscall.Syscall(procLayerExists.Addr(), 3, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(exists))) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -308,8 +322,8 @@ func _nameToGuid(name *uint16, guid *GUID) (hr error) { return } r0, _, _ := syscall.Syscall(procNameToGuid.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(guid)), 0) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -332,8 +346,8 @@ func _prepareLayer(info *driverInfo, id *uint16, descriptors []WC_LAYER_DESCRIPT return } r0, _, _ := syscall.Syscall6(procPrepareLayer.Addr(), 4, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(_p1)), uintptr(len(descriptors)), 0, 0) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -352,8 +366,179 @@ func _unprepareLayer(info *driverInfo, id *uint16) (hr error) { return } r0, _, _ := syscall.Syscall(procUnprepareLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) + } + return +} + +func processBaseImage(path string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(path) + if hr != nil { + return + } + return _processBaseImage(_p0) +} + +func _processBaseImage(path *uint16) (hr error) { + if hr = procProcessBaseImage.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procProcessBaseImage.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) + } + return +} + +func processUtilityImage(path string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(path) + if hr != nil { + return + } + return _processUtilityImage(_p0) +} + +func _processUtilityImage(path *uint16) (hr error) { + if hr = procProcessUtilityImage.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procProcessUtilityImage.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) + } + return +} + +func importLayerBegin(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _importLayerBegin(info, _p0, descriptors, context) +} + +func _importLayerBegin(info *driverInfo, id *uint16, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) { + var _p1 *WC_LAYER_DESCRIPTOR + if len(descriptors) > 0 { + _p1 = &descriptors[0] + } + if hr = procImportLayerBegin.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procImportLayerBegin.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(_p1)), uintptr(len(descriptors)), uintptr(unsafe.Pointer(context)), 0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) + } + return +} + +func importLayerNext(context uintptr, fileName string, fileInfo *winio.FileBasicInfo) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(fileName) + if hr != nil { + return + } + return _importLayerNext(context, _p0, fileInfo) +} + +func _importLayerNext(context uintptr, fileName *uint16, fileInfo *winio.FileBasicInfo) (hr error) { + if hr = procImportLayerNext.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procImportLayerNext.Addr(), 3, uintptr(context), uintptr(unsafe.Pointer(fileName)), uintptr(unsafe.Pointer(fileInfo))) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) + } + return +} + +func importLayerWrite(context uintptr, buffer []byte) (hr error) { + var _p0 *byte + if len(buffer) > 0 { + _p0 = &buffer[0] + } + if hr = procImportLayerWrite.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procImportLayerWrite.Addr(), 3, uintptr(context), uintptr(unsafe.Pointer(_p0)), uintptr(len(buffer))) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) + } + return +} + +func importLayerEnd(context uintptr) (hr error) { + if hr = procImportLayerEnd.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procImportLayerEnd.Addr(), 1, uintptr(context), 0, 0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) + } + return +} + +func exportLayerBegin(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _exportLayerBegin(info, _p0, descriptors, context) +} + +func _exportLayerBegin(info *driverInfo, id *uint16, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) { + var _p1 *WC_LAYER_DESCRIPTOR + if len(descriptors) > 0 { + _p1 = &descriptors[0] + } + if hr = procExportLayerBegin.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procExportLayerBegin.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(_p1)), uintptr(len(descriptors)), uintptr(unsafe.Pointer(context)), 0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) + } + return +} + +func exportLayerNext(context uintptr, fileName **uint16, fileInfo *winio.FileBasicInfo, fileSize *int64, deleted *uint32) (hr error) { + if hr = procExportLayerNext.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procExportLayerNext.Addr(), 5, uintptr(context), uintptr(unsafe.Pointer(fileName)), uintptr(unsafe.Pointer(fileInfo)), uintptr(unsafe.Pointer(fileSize)), uintptr(unsafe.Pointer(deleted)), 0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) + } + return +} + +func exportLayerRead(context uintptr, buffer []byte, bytesRead *uint32) (hr error) { + var _p0 *byte + if len(buffer) > 0 { + _p0 = &buffer[0] + } + if hr = procExportLayerRead.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procExportLayerRead.Addr(), 4, uintptr(context), uintptr(unsafe.Pointer(_p0)), uintptr(len(buffer)), uintptr(unsafe.Pointer(bytesRead)), 0, 0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) + } + return +} + +func exportLayerEnd(context uintptr) (hr error) { + if hr = procExportLayerEnd.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procExportLayerEnd.Addr(), 1, uintptr(context), 0, 0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -377,8 +562,8 @@ func _createComputeSystem(id *uint16, configuration *uint16) (hr error) { return } r0, _, _ := syscall.Syscall(procCreateComputeSystem.Addr(), 2, uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(configuration)), 0) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -402,8 +587,8 @@ func _createProcessWithStdHandlesInComputeSystem(id *uint16, paramsJson *uint16, return } r0, _, _ := syscall.Syscall6(procCreateProcessWithStdHandlesInComputeSystem.Addr(), 6, uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(paramsJson)), uintptr(unsafe.Pointer(pid)), uintptr(unsafe.Pointer(stdin)), uintptr(unsafe.Pointer(stdout)), uintptr(unsafe.Pointer(stderr))) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -422,8 +607,8 @@ func _resizeConsoleInComputeSystem(id *uint16, pid uint32, height uint16, width return } r0, _, _ := syscall.Syscall6(procResizeConsoleInComputeSystem.Addr(), 5, uintptr(unsafe.Pointer(id)), uintptr(pid), uintptr(height), uintptr(width), uintptr(flags), 0) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -442,8 +627,8 @@ func _shutdownComputeSystem(id *uint16, timeout uint32) (hr error) { return } r0, _, _ := syscall.Syscall(procShutdownComputeSystem.Addr(), 2, uintptr(unsafe.Pointer(id)), uintptr(timeout), 0) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -462,8 +647,8 @@ func _startComputeSystem(id *uint16) (hr error) { return } r0, _, _ := syscall.Syscall(procStartComputeSystem.Addr(), 1, uintptr(unsafe.Pointer(id)), 0, 0) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -482,8 +667,8 @@ func _terminateComputeSystem(id *uint16) (hr error) { return } r0, _, _ := syscall.Syscall(procTerminateComputeSystem.Addr(), 1, uintptr(unsafe.Pointer(id)), 0, 0) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -502,8 +687,8 @@ func _terminateProcessInComputeSystem(id *uint16, pid uint32) (hr error) { return } r0, _, _ := syscall.Syscall(procTerminateProcessInComputeSystem.Addr(), 2, uintptr(unsafe.Pointer(id)), uintptr(pid), 0) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -522,8 +707,8 @@ func _waitForProcessInComputeSystem(id *uint16, pid uint32, timeout uint32, exit return } r0, _, _ := syscall.Syscall6(procWaitForProcessInComputeSystem.Addr(), 4, uintptr(unsafe.Pointer(id)), uintptr(pid), uintptr(timeout), uintptr(unsafe.Pointer(exitCode)), 0, 0) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } @@ -552,8 +737,8 @@ func __hnsCall(method *uint16, path *uint16, object *uint16, response **uint16) return } r0, _, _ := syscall.Syscall6(procHNSCall.Addr(), 4, uintptr(unsafe.Pointer(method)), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(object)), uintptr(unsafe.Pointer(response)), 0, 0) - if r0 != 0 { - hr = syscall.Errno(r0) + if int32(r0) < 0 { + hr = syscall.Errno(win32FromHresult(r0)) } return } diff --git a/libnetwork/drivers/windows/labels.go b/libnetwork/drivers/windows/labels.go index 7cccf2011c..bfc4d368cc 100644 --- a/libnetwork/drivers/windows/labels.go +++ b/libnetwork/drivers/windows/labels.go @@ -9,4 +9,7 @@ const ( // RoutingDomain of the network RoutingDomain = "com.docker.network.windowsshim.routingdomain" + + // Interface of the network + Interface = "com.docker.network.windowsshim.interface" ) diff --git a/libnetwork/drivers/windows/windows.go b/libnetwork/drivers/windows/windows.go index 231e6a2d68..c3a478523e 100644 --- a/libnetwork/drivers/windows/windows.go +++ b/libnetwork/drivers/windows/windows.go @@ -29,11 +29,12 @@ import ( // networkConfiguration for network specific configuration type networkConfiguration struct { - ID string - Type string - Name string - HnsID string - RDID string + ID string + Type string + Name string + HnsID string + RDID string + NetworkAdapterName string } // endpointConfiguration represents the user specified configuration for the sandbox endpoint @@ -125,6 +126,8 @@ func (d *driver) parseNetworkOptions(id string, genericOptions map[string]string config.HnsID = value case RoutingDomain: config.RDID = value + case Interface: + config.NetworkAdapterName = value } } @@ -197,9 +200,10 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Dat } network := &hcsshim.HNSNetwork{ - Name: config.Name, - Type: d.name, - Subnets: subnets, + Name: config.Name, + Type: d.name, + Subnets: subnets, + NetworkAdapterName: config.NetworkAdapterName, } if network.Name == "" { @@ -364,8 +368,10 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, ec, err := parseEndpointOptions(epOptions) - if err != nil { - return err + macAddress := ifInfo.MacAddress() + // Use the macaddress if it was provided + if macAddress != nil { + endpointStruct.MacAddress = strings.Replace(macAddress.String(), ":", "-", -1) } endpointStruct.Policies, err = convertPortBindings(ec.PortBindings) diff --git a/libnetwork/ipams/windowsipam/windowsipam.go b/libnetwork/ipams/windowsipam/windowsipam.go index a92d5ccd77..661fd6a464 100644 --- a/libnetwork/ipams/windowsipam/windowsipam.go +++ b/libnetwork/ipams/windowsipam/windowsipam.go @@ -74,12 +74,13 @@ func (a *allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[s // TODO Windows: Remove this once the bug in docker daemon is fixed // that causes it to throw an exception on nil gateway - if opts[ipamapi.RequestAddressType] == netlabel.Gateway { + if prefAddress != nil { + return &net.IPNet{IP: prefAddress, Mask: ipNet.Mask}, nil, nil + } else if opts[ipamapi.RequestAddressType] == netlabel.Gateway { return ipNet, nil, nil - } else if prefAddress == nil { + } else { return nil, nil, nil } - return &net.IPNet{IP: prefAddress, Mask: ipNet.Mask}, nil, nil } // ReleaseAddress releases the address - always succeeds diff --git a/libnetwork/ipams/windowsipam/windowsipam_test.go b/libnetwork/ipams/windowsipam/windowsipam_test.go index 3fee5e690c..0dcce277ab 100644 --- a/libnetwork/ipams/windowsipam/windowsipam_test.go +++ b/libnetwork/ipams/windowsipam/windowsipam_test.go @@ -4,6 +4,8 @@ import ( "net" "testing" + "github.com/docker/libnetwork/ipamapi" + "github.com/docker/libnetwork/netlabel" _ "github.com/docker/libnetwork/testutils" "github.com/docker/libnetwork/types" ) @@ -64,6 +66,17 @@ func TestWindowsIPAM(t *testing.T) { t.Fatalf("Unexpected data returned. Expected %v . Got: %v ", requestAddress, ip.IP) } + requestOptions := map[string]string{} + requestOptions[ipamapi.RequestAddressType] = netlabel.Gateway + ip, _, err = a.RequestAddress(requestPool.String(), requestAddress, requestOptions) + if err != nil { + t.Fatal(err) + } + + if !ip.IP.Equal(requestAddress) { + t.Fatalf("Unexpected data returned. Expected %v . Got: %v ", requestAddress, ip.IP) + } + err = a.ReleaseAddress(requestPool.String(), requestAddress) if err != nil { t.Fatal(err)