diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 8345e5b56..bc831489c 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -8,8 +8,8 @@ "Deps": [ { "ImportPath": "github.com/Microsoft/go-winio", - "Comment": "v0.3.8", - "Rev": "fff283ad5116362ca252298cfc9b95828956d85d" + "Comment": "v0.4.1", + "Rev": "706941bedd2d9b3a8c88e4022bd0078101f233f2" }, { "ImportPath": "github.com/Sirupsen/logrus", diff --git a/cmd/cri-containerd/cri_containerd.go b/cmd/cri-containerd/cri_containerd.go index 1c86cc325..5ddb6e6a2 100644 --- a/cmd/cri-containerd/cri_containerd.go +++ b/cmd/cri-containerd/cri_containerd.go @@ -37,14 +37,8 @@ func main() { os.Exit(0) } - glog.V(2).Infof("Connect to containerd endpoint %q with timeout %v", o.ContainerdEndpoint, o.ContainerdConnectionTimeout) - conn, err := server.ConnectToContainerd(o.ContainerdEndpoint, o.ContainerdConnectionTimeout) - if err != nil { - glog.Exitf("Failed to connect containerd endpoint %q: %v", o.ContainerdEndpoint, err) - } - glog.V(2).Infof("Run cri-containerd grpc server on socket %q", o.SocketPath) - service, err := server.NewCRIContainerdService(conn, o.ContainerdEndpoint, o.RootDir, o.NetworkPluginBinDir, o.NetworkPluginConfDir) + service, err := server.NewCRIContainerdService(o.ContainerdEndpoint, o.RootDir, o.NetworkPluginBinDir, o.NetworkPluginConfDir) if err != nil { glog.Exitf("Failed to create CRI containerd service %+v: %v", o, err) } diff --git a/hack/verify-lint.sh b/hack/verify-lint.sh index 12df1702b..d2ad34406 100755 --- a/hack/verify-lint.sh +++ b/hack/verify-lint.sh @@ -24,6 +24,7 @@ for d in $(find . -type d -a \( -iwholename './pkg*' -o -iwholename './cmd*' \)) --exclude='.*_test\.go:.*error return value not checked.*\(errcheck\)$' \ --exclude='duplicate of.*_test.go.*\(dupl\)$' \ --exclude='.*/mock_.*\.go:.*\(golint\)$' \ + --exclude='declaration of "err" shadows declaration.*\(vetshadow\)$' \ --disable=aligncheck \ --disable=gotype \ --disable=gas \ diff --git a/pkg/server/container_create.go b/pkg/server/container_create.go index ae1383371..5a762a8bb 100644 --- a/pkg/server/container_create.go +++ b/pkg/server/container_create.go @@ -58,7 +58,7 @@ func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.C // the same container. id := generateID() name := makeContainerName(config.GetMetadata(), sandboxConfig.GetMetadata()) - if err = c.containerNameIndex.Reserve(name, id); err != nil { + if err := c.containerNameIndex.Reserve(name, id); err != nil { return nil, fmt.Errorf("failed to reserve container name %q: %v", name, err) } defer func() { @@ -101,17 +101,17 @@ func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.C // Prepare container rootfs. if config.GetLinux().GetSecurityContext().GetReadonlyRootfs() { - if _, err = c.snapshotService.View(ctx, id, imageMeta.ChainID); err != nil { + if _, err := c.snapshotService.View(ctx, id, imageMeta.ChainID); err != nil { return nil, fmt.Errorf("failed to view container rootfs %q: %v", imageMeta.ChainID, err) } } else { - if _, err = c.snapshotService.Prepare(ctx, id, imageMeta.ChainID); err != nil { + if _, err := c.snapshotService.Prepare(ctx, id, imageMeta.ChainID); err != nil { return nil, fmt.Errorf("failed to prepare container rootfs %q: %v", imageMeta.ChainID, err) } } defer func() { if retErr != nil { - if err = c.snapshotService.Remove(ctx, id); err != nil { + if err := c.snapshotService.Remove(ctx, id); err != nil { glog.Errorf("Failed to remove container snapshot %q: %v", id, err) } } @@ -120,14 +120,14 @@ func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.C // Create container root directory. containerRootDir := getContainerRootDir(c.rootDir, id) - if err = c.os.MkdirAll(containerRootDir, 0755); err != nil { + if err := c.os.MkdirAll(containerRootDir, 0755); err != nil { return nil, fmt.Errorf("failed to create container root directory %q: %v", containerRootDir, err) } defer func() { if retErr != nil { // Cleanup the container root directory. - if err = c.os.RemoveAll(containerRootDir); err != nil { + if err := c.os.RemoveAll(containerRootDir); err != nil { glog.Errorf("Failed to remove container root directory %q: %v", containerRootDir, err) } diff --git a/pkg/server/container_create_test.go b/pkg/server/container_create_test.go index 82d66e817..137577d6c 100644 --- a/pkg/server/container_create_test.go +++ b/pkg/server/container_create_test.go @@ -552,11 +552,11 @@ func TestCreateContainer(t *testing.T) { "container name should be released") } assert.Empty(t, fakeSnapshotClient.ListMounts(), "snapshot should be cleaned up") - listResp, listerr := fake.List(context.Background(), &containers.ListContainersRequest{}) - assert.NoError(t, listerr) + listResp, err := fake.List(context.Background(), &containers.ListContainersRequest{}) + assert.NoError(t, err) assert.Empty(t, listResp.Containers, "containerd container should be cleaned up") - metas, metaserr := c.containerStore.List() - assert.NoError(t, metaserr) + metas, err := c.containerStore.List() + assert.NoError(t, err) assert.Empty(t, metas, "container metadata should not be created") continue } diff --git a/pkg/server/container_remove_test.go b/pkg/server/container_remove_test.go index 36f422207..9dafa51ca 100644 --- a/pkg/server/container_remove_test.go +++ b/pkg/server/container_remove_test.go @@ -202,8 +202,8 @@ func TestRemoveContainer(t *testing.T) { if !test.expectUnsetRemoving { continue } - meta, metaerr := c.containerStore.Get(testID) - assert.NoError(t, metaerr) + meta, err := c.containerStore.Get(testID) + assert.NoError(t, err) require.NotNil(t, meta) // Also covers resetContainerRemoving. assert.False(t, meta.Removing, "removing state should be unset") diff --git a/pkg/server/container_start.go b/pkg/server/container_start.go index 01836a221..d31d36950 100644 --- a/pkg/server/container_start.go +++ b/pkg/server/container_start.go @@ -137,12 +137,12 @@ func (c *criContainerdService) startContainer(ctx context.Context, id string, me if config.GetLogPath() != "" { // Only generate container log when log path is specified. logPath := filepath.Join(sandboxConfig.GetLogDirectory(), config.GetLogPath()) - if err = c.agentFactory.NewContainerLogger(logPath, agents.Stdout, stdoutPipe).Start(); err != nil { + if err := c.agentFactory.NewContainerLogger(logPath, agents.Stdout, stdoutPipe).Start(); err != nil { return fmt.Errorf("failed to start container stdout logger: %v", err) } // Only redirect stderr when there is no tty. if !config.GetTty() { - if err = c.agentFactory.NewContainerLogger(logPath, agents.Stderr, stderrPipe).Start(); err != nil { + if err := c.agentFactory.NewContainerLogger(logPath, agents.Stderr, stderrPipe).Start(); err != nil { return fmt.Errorf("failed to start container stderr logger: %v", err) } } diff --git a/pkg/server/container_start_test.go b/pkg/server/container_start_test.go index 7ea2b3130..7bfca5fab 100644 --- a/pkg/server/container_start_test.go +++ b/pkg/server/container_start_test.go @@ -223,7 +223,7 @@ func TestStartContainer(t *testing.T) { assert.EqualValues(t, errorStartExitCode, meta.ExitCode) assert.Equal(t, errorStartReason, meta.Reason) assert.NotEmpty(t, meta.Message) - _, err = fake.Info(context.Background(), &execution.InfoRequest{ContainerID: testID}) + _, err := fake.Info(context.Background(), &execution.InfoRequest{ContainerID: testID}) assert.True(t, isContainerdGRPCNotFoundError(err), "containerd task should be cleaned up when fail to start") continue diff --git a/pkg/server/helpers.go b/pkg/server/helpers.go index 8d36c539e..430866abb 100644 --- a/pkg/server/helpers.go +++ b/pkg/server/helpers.go @@ -346,21 +346,21 @@ func (c *criContainerdService) localResolve(ctx context.Context, ref string) (*m _, err := imagedigest.Parse(ref) if err != nil { // ref is not image id, try to resolve it locally. - normalized, nerr := normalizeImageRef(ref) + normalized, err := normalizeImageRef(ref) if err != nil { - return nil, fmt.Errorf("invalid image reference %q: %v", ref, nerr) + return nil, fmt.Errorf("invalid image reference %q: %v", ref, err) } - image, gerr := c.imageStoreService.Get(ctx, normalized.String()) - if gerr != nil { - if containerdmetadata.IsNotFound(gerr) { + image, err := c.imageStoreService.Get(ctx, normalized.String()) + if err != nil { + if containerdmetadata.IsNotFound(err) { return nil, nil } return nil, fmt.Errorf("an error occurred when getting image %q from containerd image store: %v", - normalized.String(), gerr) + normalized.String(), err) } - desc, cerr := image.Config(ctx, c.contentStoreService) - if cerr != nil { - return nil, fmt.Errorf("failed to get image config descriptor: %v", cerr) + desc, err := image.Config(ctx, c.contentStoreService) + if err != nil { + return nil, fmt.Errorf("failed to get image config descriptor: %v", err) } ref = desc.Digest.String() } diff --git a/pkg/server/image_pull.go b/pkg/server/image_pull.go index 9ba7e85d1..bb38af391 100644 --- a/pkg/server/image_pull.go +++ b/pkg/server/image_pull.go @@ -118,7 +118,7 @@ func (c *criContainerdService) PullImage(ctx context.Context, r *runtime.PullIma // recover in-memory state during startup. if err == nil { // Update existing image metadata. - if err = c.imageMetadataStore.Update(imageID, func(m metadata.ImageMetadata) (metadata.ImageMetadata, error) { + if err := c.imageMetadataStore.Update(imageID, func(m metadata.ImageMetadata) (metadata.ImageMetadata, error) { updateImageMetadata(&m, repoTag, repoDigest) return m, nil }); err != nil { @@ -246,7 +246,7 @@ func (c *criContainerdService) pullImage(ctx context.Context, ref string) ( glog.V(5).Infof("Dispatch for %q returns error: %v", ref, err) } // Wait for the image pulling to finish - if err = c.waitForResourcesDownloading(ctx, resources.all()); err != nil { + if err := c.waitForResourcesDownloading(ctx, resources.all()); err != nil { return "", "", fmt.Errorf("failed to wait for image %q downloading: %v", ref, err) } glog.V(4).Infof("Finished downloading resources for image %q", ref) @@ -283,7 +283,7 @@ func (c *criContainerdService) pullImage(ctx context.Context, ref string) ( return "", "", fmt.Errorf("readblob failed for manifest digest %q: %v", manifestDigest, err) } var manifest imagespec.Manifest - if err = json.Unmarshal(p, &manifest); err != nil { + if err := json.Unmarshal(p, &manifest); err != nil { return "", "", fmt.Errorf("unmarshal blob to manifest failed for manifest digest %q: %v", manifestDigest, err) } diff --git a/pkg/server/sandbox_remove.go b/pkg/server/sandbox_remove.go index 5df17a425..298bb888e 100644 --- a/pkg/server/sandbox_remove.go +++ b/pkg/server/sandbox_remove.go @@ -64,7 +64,7 @@ func (c *criContainerdService) RemovePodSandbox(ctx context.Context, r *runtime. } // Remove sandbox container snapshot. - if err = c.snapshotService.Remove(ctx, id); err != nil { + if err := c.snapshotService.Remove(ctx, id); err != nil { if !snapshot.IsNotExist(err) { return nil, fmt.Errorf("failed to remove sandbox container snapshot %q: %v", id, err) } diff --git a/pkg/server/sandbox_run.go b/pkg/server/sandbox_run.go index 343a84513..3e15e44b7 100644 --- a/pkg/server/sandbox_run.go +++ b/pkg/server/sandbox_run.go @@ -94,7 +94,7 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run } defer func() { if retErr != nil { - if err = c.snapshotService.Remove(ctx, id); err != nil { + if err := c.snapshotService.Remove(ctx, id); err != nil { glog.Errorf("Failed to remove sandbox container snapshot %q: %v", id, err) } } @@ -135,7 +135,7 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run } defer func() { if retErr != nil { - if _, err = c.containerService.Delete(ctx, &containers.DeleteContainerRequest{ID: id}); err != nil { + if _, err := c.containerService.Delete(ctx, &containers.DeleteContainerRequest{ID: id}); err != nil { glog.Errorf("Failed to delete containerd container%q: %v", id, err) } } @@ -144,14 +144,14 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run // Create sandbox container root directory. // Prepare streaming named pipe. sandboxRootDir := getSandboxRootDir(c.rootDir, id) - if err = c.os.MkdirAll(sandboxRootDir, 0755); err != nil { + if err := c.os.MkdirAll(sandboxRootDir, 0755); err != nil { return nil, fmt.Errorf("failed to create sandbox root directory %q: %v", sandboxRootDir, err) } defer func() { if retErr != nil { // Cleanup the sandbox root directory. - if err = c.os.RemoveAll(sandboxRootDir); err != nil { + if err := c.os.RemoveAll(sandboxRootDir); err != nil { glog.Errorf("Failed to remove sandbox root directory %q: %v", sandboxRootDir, err) } @@ -170,10 +170,10 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run stderrPipe.Close() } }() - if err = c.agentFactory.NewSandboxLogger(stdoutPipe).Start(); err != nil { + if err := c.agentFactory.NewSandboxLogger(stdoutPipe).Start(); err != nil { return nil, fmt.Errorf("failed to start sandbox stdout logger: %v", err) } - if err = c.agentFactory.NewSandboxLogger(stderrPipe).Start(); err != nil { + if err := c.agentFactory.NewSandboxLogger(stderrPipe).Start(); err != nil { return nil, fmt.Errorf("failed to start sandbox stderr logger: %v", err) } diff --git a/pkg/server/server.go b/pkg/server/server.go index bf15bd7b8..4d5aac77a 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -21,22 +21,15 @@ import ( "net" "os" "syscall" - "time" - "github.com/containerd/containerd/namespaces" "github.com/golang/glog" - "golang.org/x/net/context" "google.golang.org/grpc" runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1" "k8s.io/kubernetes/pkg/util/interrupt" ) -const ( - // unixProtocol is the network protocol of unix socket. - unixProtocol = "unix" - // k8sContainerdNamespace is the namespace we use to connect containerd. - k8sContainerdNamespace = "k8s.io" -) +// unixProtocol is the network protocol of unix socket. +const unixProtocol = "unix" // CRIContainerdServer is the grpc server of cri-containerd. type CRIContainerdServer struct { @@ -79,38 +72,3 @@ func (s *CRIContainerdServer) Run() error { h := interrupt.New(nil, s.server.Stop) return h.Run(func() error { return s.server.Serve(l) }) } - -// ConnectToContainerd returns a grpc client for containerd. -func ConnectToContainerd(path string, connectionTimeout time.Duration) (*grpc.ClientConn, error) { - // get the containerd client - dialOpts := []grpc.DialOption{ - grpc.WithInsecure(), - grpc.WithTimeout(connectionTimeout), - grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { - return net.DialTimeout(unixProtocol, path, timeout) - }), - grpc.WithUnaryInterceptor(grpc.UnaryClientInterceptor(unary)), - grpc.WithStreamInterceptor(grpc.StreamClientInterceptor(stream)), - } - return grpc.Dial(fmt.Sprintf("%s://%s", unixProtocol, path), dialOpts...) -} - -// TODO(random-liu): Get rid of following functions after switching to containerd client. -// unary is a wrapper to apply kubernetes namespace in each grpc unary call. -func unary(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { - _, ok := namespaces.Namespace(ctx) - if !ok { - ctx = namespaces.WithNamespace(ctx, k8sContainerdNamespace) - } - return invoker(ctx, method, req, reply, cc, opts...) -} - -// stream is a wrapper to apply kubernetes namespace in each grpc stream call. -func stream(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { - _, ok := namespaces.Namespace(ctx) - if !ok { - ctx = namespaces.WithNamespace(ctx, k8sContainerdNamespace) - } - - return streamer(ctx, desc, cc, method, opts...) -} diff --git a/pkg/server/service.go b/pkg/server/service.go index 7d438bc51..8bd8c0d08 100644 --- a/pkg/server/service.go +++ b/pkg/server/service.go @@ -21,22 +21,14 @@ import ( "github.com/containerd/containerd" "github.com/containerd/containerd/api/services/containers" - contentapi "github.com/containerd/containerd/api/services/content" - diffapi "github.com/containerd/containerd/api/services/diff" "github.com/containerd/containerd/api/services/execution" - imagesapi "github.com/containerd/containerd/api/services/images" - snapshotapi "github.com/containerd/containerd/api/services/snapshot" versionapi "github.com/containerd/containerd/api/services/version" "github.com/containerd/containerd/content" "github.com/containerd/containerd/images" - contentservice "github.com/containerd/containerd/services/content" diffservice "github.com/containerd/containerd/services/diff" - imagesservice "github.com/containerd/containerd/services/images" - snapshotservice "github.com/containerd/containerd/services/snapshot" "github.com/containerd/containerd/snapshot" "github.com/docker/docker/pkg/truncindex" "github.com/kubernetes-incubator/cri-o/pkg/ocicni" - "google.golang.org/grpc" healthapi "google.golang.org/grpc/health/grpc_health_v1" runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1" @@ -47,6 +39,9 @@ import ( "github.com/kubernetes-incubator/cri-containerd/pkg/server/agents" ) +// k8sContainerdNamespace is the namespace we use to connect containerd. +const k8sContainerdNamespace = "k8s.io" + // CRIContainerdService is the interface implement CRI remote service server. type CRIContainerdService interface { Start() @@ -105,12 +100,12 @@ type criContainerdService struct { } // NewCRIContainerdService returns a new instance of CRIContainerdService -func NewCRIContainerdService(conn *grpc.ClientConn, containerdEndpoint, rootDir, networkPluginBinDir, networkPluginConfDir string) (CRIContainerdService, error) { +func NewCRIContainerdService(containerdEndpoint, rootDir, networkPluginBinDir, networkPluginConfDir string) (CRIContainerdService, error) { // TODO(random-liu): [P2] Recover from runtime state and metadata store. - client, err := containerd.New(containerdEndpoint) + client, err := containerd.New(containerdEndpoint, containerd.WithDefaultNamespace(k8sContainerdNamespace)) if err != nil { - return nil, fmt.Errorf("failed to initialize client: %v", err) + return nil, fmt.Errorf("failed to initialize containerd client with endpoint %q: %v", containerdEndpoint, err) } c := &criContainerdService{ @@ -126,14 +121,14 @@ func NewCRIContainerdService(conn *grpc.ClientConn, containerdEndpoint, rootDir, sandboxIDIndex: truncindex.NewTruncIndex(nil), // TODO(random-liu): Add container id index. containerNameIndex: registrar.NewRegistrar(), - containerService: containers.NewContainersClient(conn), - taskService: execution.NewTasksClient(conn), - imageStoreService: imagesservice.NewStoreFromClient(imagesapi.NewImagesClient(conn)), - contentStoreService: contentservice.NewStoreFromClient(contentapi.NewContentClient(conn)), - snapshotService: snapshotservice.NewSnapshotterFromClient(snapshotapi.NewSnapshotClient(conn)), - diffService: diffservice.NewDiffServiceFromClient(diffapi.NewDiffClient(conn)), - versionService: versionapi.NewVersionClient(conn), - healthService: healthapi.NewHealthClient(conn), + containerService: client.ContainerService(), + taskService: client.TaskService(), + imageStoreService: client.ImageService(), + contentStoreService: client.ContentStore(), + snapshotService: client.SnapshotService(), + diffService: client.DiffService(), + versionService: client.VersionService(), + healthService: client.HealthService(), agentFactory: agents.NewAgentFactory(), client: client, } diff --git a/vendor/github.com/Microsoft/go-winio/backup.go b/vendor/github.com/Microsoft/go-winio/backup.go index ceedee1f4..27d6ace0c 100644 --- a/vendor/github.com/Microsoft/go-winio/backup.go +++ b/vendor/github.com/Microsoft/go-winio/backup.go @@ -185,7 +185,6 @@ type BackupFileReader struct { // 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 } @@ -196,6 +195,7 @@ func (r *BackupFileReader) Read(b []byte) (int, error) { if err != nil { return 0, &os.PathError{"BackupRead", r.f.Name(), err} } + runtime.KeepAlive(r.f) if bytesRead == 0 { return 0, io.EOF } @@ -207,6 +207,7 @@ func (r *BackupFileReader) Read(b []byte) (int, error) { func (r *BackupFileReader) Close() error { if r.ctx != 0 { backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx) + runtime.KeepAlive(r.f) r.ctx = 0 } return nil @@ -223,7 +224,6 @@ type BackupFileWriter struct { // 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 } @@ -234,6 +234,7 @@ func (w *BackupFileWriter) Write(b []byte) (int, error) { if err != nil { return 0, &os.PathError{"BackupWrite", w.f.Name(), err} } + runtime.KeepAlive(w.f) if int(bytesWritten) != len(b) { return int(bytesWritten), errors.New("not all bytes could be written") } @@ -245,6 +246,7 @@ func (w *BackupFileWriter) Write(b []byte) (int, error) { func (w *BackupFileWriter) Close() error { if w.ctx != 0 { backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx) + runtime.KeepAlive(w.f) w.ctx = 0 } return nil diff --git a/vendor/github.com/Microsoft/go-winio/file.go b/vendor/github.com/Microsoft/go-winio/file.go index 8c15e4124..613f31b52 100644 --- a/vendor/github.com/Microsoft/go-winio/file.go +++ b/vendor/github.com/Microsoft/go-winio/file.go @@ -7,6 +7,7 @@ import ( "io" "runtime" "sync" + "sync/atomic" "syscall" "time" ) @@ -17,6 +18,12 @@ import ( //sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes //sys timeBeginPeriod(period uint32) (n int32) = winmm.timeBeginPeriod +type atomicBool int32 + +func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 } +func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) } +func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) } + const ( cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1 cFILE_SKIP_SET_EVENT_ON_HANDLE = 2 @@ -33,6 +40,8 @@ func (e *timeoutError) Error() string { return "i/o timeout" } func (e *timeoutError) Timeout() bool { return true } func (e *timeoutError) Temporary() bool { return true } +type timeoutChan chan struct{} + var ioInitOnce sync.Once var ioCompletionPort syscall.Handle @@ -63,8 +72,16 @@ type win32File struct { handle syscall.Handle wg sync.WaitGroup closing bool - readDeadline time.Time - writeDeadline time.Time + readDeadline deadlineHandler + writeDeadline deadlineHandler +} + +type deadlineHandler struct { + setLock sync.Mutex + channel timeoutChan + channelLock sync.RWMutex + timer *time.Timer + timedout atomicBool } // makeWin32File makes a new win32File from an existing file handle @@ -79,7 +96,8 @@ func makeWin32File(h syscall.Handle) (*win32File, error) { if err != nil { return nil, err } - runtime.SetFinalizer(f, (*win32File).closeHandle) + f.readDeadline.channel = make(timeoutChan) + f.writeDeadline.channel = make(timeoutChan) return f, nil } @@ -103,11 +121,11 @@ func (f *win32File) closeHandle() { // Close closes a win32File. func (f *win32File) Close() error { f.closeHandle() - runtime.SetFinalizer(f, nil) return nil } -// prepareIo prepares for a new IO operation +// prepareIo prepares for a new IO operation. +// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning. func (f *win32File) prepareIo() (*ioOperation, error) { f.wg.Add(1) if f.closing { @@ -136,47 +154,45 @@ func ioCompletionProcessor(h syscall.Handle) { // asyncIo processes the return value from ReadFile or WriteFile, blocking until // the operation has actually completed. -func (f *win32File) asyncIo(c *ioOperation, deadline time.Time, bytes uint32, err error) (int, error) { +func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) { if err != syscall.ERROR_IO_PENDING { - f.wg.Done() return int(bytes), err - } else { - var r ioResult - wait := true - timedout := false - if f.closing { - cancelIoEx(f.handle, &c.o) - } else if !deadline.IsZero() { - now := time.Now() - if !deadline.After(now) { - timedout = true - } else { - timeout := time.After(deadline.Sub(now)) - select { - case r = <-c.ch: - wait = false - case <-timeout: - timedout = true - } - } - } - if timedout { - cancelIoEx(f.handle, &c.o) - } - if wait { - r = <-c.ch - } + } + + if f.closing { + cancelIoEx(f.handle, &c.o) + } + + var timeout timeoutChan + if d != nil { + d.channelLock.Lock() + timeout = d.channel + d.channelLock.Unlock() + } + + var r ioResult + select { + case r = <-c.ch: err = r.err if err == syscall.ERROR_OPERATION_ABORTED { if f.closing { err = ErrFileClosed - } else if timedout { - err = ErrTimeout } } - f.wg.Done() - return int(r.bytes), err + case <-timeout: + cancelIoEx(f.handle, &c.o) + r = <-c.ch + err = r.err + if err == syscall.ERROR_OPERATION_ABORTED { + err = ErrTimeout + } } + + // runtime.KeepAlive is needed, as c is passed via native + // code to ioCompletionProcessor, c must remain alive + // until the channel read is complete. + runtime.KeepAlive(c) + return int(r.bytes), err } // Read reads from a file handle. @@ -185,9 +201,16 @@ func (f *win32File) Read(b []byte) (int, error) { if err != nil { return 0, err } + defer f.wg.Done() + + if f.readDeadline.timedout.isSet() { + return 0, ErrTimeout + } + var bytes uint32 err = syscall.ReadFile(f.handle, b, &bytes, &c.o) - n, err := f.asyncIo(c, f.readDeadline, bytes, err) + n, err := f.asyncIo(c, &f.readDeadline, bytes, err) + runtime.KeepAlive(b) // Handle EOF conditions. if err == nil && n == 0 && len(b) != 0 { @@ -205,17 +228,68 @@ func (f *win32File) Write(b []byte) (int, error) { if err != nil { return 0, err } + defer f.wg.Done() + + if f.writeDeadline.timedout.isSet() { + return 0, ErrTimeout + } + var bytes uint32 err = syscall.WriteFile(f.handle, b, &bytes, &c.o) - return f.asyncIo(c, f.writeDeadline, bytes, err) + n, err := f.asyncIo(c, &f.writeDeadline, bytes, err) + runtime.KeepAlive(b) + return n, err } -func (f *win32File) SetReadDeadline(t time.Time) error { - f.readDeadline = t - return nil +func (f *win32File) SetReadDeadline(deadline time.Time) error { + return f.readDeadline.set(deadline) } -func (f *win32File) SetWriteDeadline(t time.Time) error { - f.writeDeadline = t +func (f *win32File) SetWriteDeadline(deadline time.Time) error { + return f.writeDeadline.set(deadline) +} + +func (f *win32File) Flush() error { + return syscall.FlushFileBuffers(f.handle) +} + +func (d *deadlineHandler) set(deadline time.Time) error { + d.setLock.Lock() + defer d.setLock.Unlock() + + if d.timer != nil { + if !d.timer.Stop() { + <-d.channel + } + d.timer = nil + } + d.timedout.setFalse() + + select { + case <-d.channel: + d.channelLock.Lock() + d.channel = make(chan struct{}) + d.channelLock.Unlock() + default: + } + + if deadline.IsZero() { + return nil + } + + timeoutIO := func() { + d.timedout.setTrue() + close(d.channel) + } + + now := time.Now() + duration := deadline.Sub(now) + if deadline.After(now) { + // Deadline is in the future, set a timer to wait + d.timer = time.AfterFunc(duration, timeoutIO) + } else { + // Deadline is in the past. Cancel all pending IO now. + timeoutIO() + } return nil } diff --git a/vendor/github.com/Microsoft/go-winio/fileinfo.go b/vendor/github.com/Microsoft/go-winio/fileinfo.go index a822e4314..b1d60abb8 100644 --- a/vendor/github.com/Microsoft/go-winio/fileinfo.go +++ b/vendor/github.com/Microsoft/go-winio/fileinfo.go @@ -4,6 +4,7 @@ package winio import ( "os" + "runtime" "syscall" "unsafe" ) @@ -28,6 +29,7 @@ func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) { if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} } + runtime.KeepAlive(f) return bi, nil } @@ -36,6 +38,7 @@ func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error { if err := setFileInformationByHandle(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err} } + runtime.KeepAlive(f) return nil } @@ -52,5 +55,6 @@ func GetFileID(f *os.File) (*FileIDInfo, error) { if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileIDInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil { return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} } + runtime.KeepAlive(f) return fileID, nil } diff --git a/vendor/github.com/Microsoft/go-winio/pipe.go b/vendor/github.com/Microsoft/go-winio/pipe.go index b85b2eef4..fa270a310 100644 --- a/vendor/github.com/Microsoft/go-winio/pipe.go +++ b/vendor/github.com/Microsoft/go-winio/pipe.go @@ -18,10 +18,12 @@ import ( //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 +//sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc +//sys copyMemory(dst uintptr, src uintptr, length uint32) = RtlCopyMemory type securityAttributes struct { Length uint32 - SecurityDescriptor *byte + SecurityDescriptor uintptr InheritHandle uint32 } @@ -87,7 +89,11 @@ func (f *win32MessageBytePipe) CloseWrite() error { if f.writeClosed { return errPipeWriteClosed } - _, err := f.win32File.Write(nil) + err := f.win32File.Flush() + if err != nil { + return err + } + _, err = f.win32File.Write(nil) if err != nil { return err } @@ -227,12 +233,15 @@ func makeServerPipeHandle(path string, securityDescriptor []byte, c *PipeConfig, mode |= cPIPE_TYPE_MESSAGE } - var sa securityAttributes - sa.Length = uint32(unsafe.Sizeof(sa)) + sa := &securityAttributes{} + sa.Length = uint32(unsafe.Sizeof(*sa)) if securityDescriptor != nil { - sa.SecurityDescriptor = &securityDescriptor[0] + len := uint32(len(securityDescriptor)) + sa.SecurityDescriptor = localAlloc(0, len) + defer localFree(sa.SecurityDescriptor) + copyMemory(sa.SecurityDescriptor, uintptr(unsafe.Pointer(&securityDescriptor[0])), len) } - h, err := createNamedPipe(path, flags, mode, cPIPE_UNLIMITED_INSTANCES, uint32(c.OutputBufferSize), uint32(c.InputBufferSize), 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{Op: "open", Path: path, Err: err} } @@ -358,8 +367,10 @@ func connectPipe(p *win32File) error { if err != nil { return err } + defer p.wg.Done() + err = connectNamedPipe(p.handle, &c.o) - _, err = p.asyncIo(c, time.Time{}, 0, err) + _, err = p.asyncIo(c, nil, 0, err) if err != nil && err != cERROR_PIPE_CONNECTED { return err } diff --git a/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go index c5e369bae..857122a3e 100644 --- a/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go +++ b/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go @@ -11,6 +11,31 @@ import ( var _ unsafe.Pointer +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + var ( modkernel32 = windows.NewLazySystemDLL("kernel32.dll") modwinmm = windows.NewLazySystemDLL("winmm.dll") @@ -27,6 +52,8 @@ var ( procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW") procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo") procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW") + procLocalAlloc = modkernel32.NewProc("LocalAlloc") + procRtlCopyMemory = modkernel32.NewProc("RtlCopyMemory") procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW") procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW") procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW") @@ -51,7 +78,7 @@ func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) { r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0) if r1 == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -64,7 +91,7 @@ func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintpt newport = syscall.Handle(r0) if newport == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -76,7 +103,7 @@ func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0) if r1 == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -88,7 +115,7 @@ func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err erro r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0) if r1 == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -106,7 +133,7 @@ func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) { r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0) if r1 == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -128,7 +155,7 @@ func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances handle = syscall.Handle(r0) if handle == syscall.InvalidHandle { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -150,7 +177,7 @@ func _createFile(name *uint16, access uint32, mode uint32, sa *securityAttribute handle = syscall.Handle(r0) if handle == syscall.InvalidHandle { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -171,7 +198,7 @@ func _waitNamedPipe(name *uint16, timeout uint32) (err error) { r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0) if r1 == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -183,7 +210,7 @@ func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSiz 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) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -195,7 +222,7 @@ func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *u 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) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -203,6 +230,17 @@ func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *u return } +func localAlloc(uFlags uint32, length uint32) (ptr uintptr) { + r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0) + ptr = uintptr(r0) + return +} + +func copyMemory(dst uintptr, src uintptr, length uint32) { + syscall.Syscall(procRtlCopyMemory.Addr(), 3, uintptr(dst), uintptr(src), uintptr(length)) + 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) @@ -216,7 +254,7 @@ func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidS r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0) if r1 == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -228,7 +266,7 @@ func convertSidToStringSid(sid *byte, str **uint16) (err error) { r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0) if r1 == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -249,7 +287,7 @@ func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0) if r1 == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -261,7 +299,7 @@ func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint 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) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -284,7 +322,7 @@ func getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, 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) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -296,7 +334,7 @@ func setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, si 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) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -315,7 +353,7 @@ func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, ou success = r0 != 0 if true { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -327,7 +365,7 @@ 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) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -339,7 +377,7 @@ func revertToSelf() (err error) { r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0) if r1 == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -357,7 +395,7 @@ func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, 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) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -389,7 +427,7 @@ func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err 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) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -410,7 +448,7 @@ func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size 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) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -431,7 +469,7 @@ func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint1 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) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -459,7 +497,7 @@ func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, proce 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) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -487,7 +525,7 @@ func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, p 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) + err = errnoErr(e1) } else { err = syscall.EINVAL }