## Elevator Pitch
@@ -126,7 +135,7 @@ The script installs downloaded binary to `$HOME/.local/bin` directory by default
### Go
-Required Go Version >= **1.16**
+Required Go Version >= **1.19**
```sh
go install github.com/jesseduffield/lazydocker@latest
@@ -287,12 +296,6 @@ By default we only show logs from the last hour, so that we're not putting too m
If you are running lazydocker in Docker container, it is a know bug, that you can't see logs or CPU usage.
-### Why isn't my docker-compose environment being used?
-
-By default Compose V1 (`docker-compose` with the hyphen) is used as the docker-compose command. You will need to make sure you have the `docker-compose` command available for lazydocker to be able to use.
-
-If you use Compose V2 (`docker compose` without the hyphen), alternatively, you can change the docker-compose command used via the `commandTemplates.dockerCompose` config value.
-
## Alternatives
- [docui](https://github.com/skanehira/docui) - Skanehira beat me to the punch on making a docker terminal UI, so definitely check out that repo as well! I think the two repos can live in harmony though: lazydocker is more about managing existing containers/services, and docui is more about creating and configuring them.
diff --git a/docs/Config.md b/docs/Config.md
index 48e15f69..e43bdf90 100644
--- a/docs/Config.md
+++ b/docs/Config.md
@@ -32,7 +32,8 @@ to the top of your config file or via [Visual Studio Code settings.json config][
```yml
gui:
scrollHeight: 2
- language: 'auto' # one of 'auto' | 'en' | 'pl' | 'nl' | 'de' | 'tr'
+ language: "auto" # one of 'auto' | 'en' | 'pl' | 'nl' | 'de' | 'tr'
+ border: "rounded" # one of 'rounded' | 'single' | 'double' | 'hidden'
theme:
activeBorderColor:
- green
@@ -64,7 +65,7 @@ logs:
since: '60m' # set to '' to show all logs
tail: '' # set to 200 to show last 200 lines of logs
commandTemplates:
- dockerCompose: docker-compose
+ dockerCompose: docker compose # Determines the Docker Compose command to run, referred to as .DockerCompose in commandTemplates
restartService: '{{ .DockerCompose }} restart {{ .Service.Name }}'
up: '{{ .DockerCompose }} up -d'
down: '{{ .DockerCompose }} down'
diff --git a/go.mod b/go.mod
index b8e7d211..0376b293 100644
--- a/go.mod
+++ b/go.mod
@@ -1,68 +1,77 @@
module github.com/jesseduffield/lazydocker
-go 1.20
+go 1.21
require (
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
- github.com/docker/cli v20.10.15+incompatible
- github.com/docker/docker v20.10.27-0.20230918140811-29a0e76e6495+incompatible
+ github.com/docker/cli v27.0.3+incompatible
+ github.com/docker/docker v27.0.3+incompatible
github.com/fatih/color v1.10.0
- github.com/go-errors/errors v1.4.2
+ github.com/go-errors/errors v1.5.1
github.com/gookit/color v1.5.0
- github.com/imdario/mergo v0.3.8
+ github.com/imdario/mergo v0.3.16
github.com/integrii/flaggy v1.4.0
github.com/jesseduffield/asciigraph v0.0.0-20190605104717-6d88e39309ee
- github.com/jesseduffield/gocui v0.3.1-0.20221023185936-ef06450f4fdc
+ github.com/jesseduffield/gocui v0.3.1-0.20240418080333-8cd33929c513
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
github.com/jesseduffield/lazycore v0.0.0-20221023210126-718a4caea996
github.com/jesseduffield/yaml v0.0.0-20190702115811-b900b7e08b56
- github.com/mattn/go-runewidth v0.0.14
+ github.com/mattn/go-runewidth v0.0.15
github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767
github.com/mgutz/str v1.2.0
github.com/pmezard/go-difflib v1.0.0
github.com/samber/lo v1.31.0
github.com/sasha-s/go-deadlock v0.3.1
- github.com/sirupsen/logrus v1.4.2
+ github.com/sirupsen/logrus v1.9.3
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
- github.com/stretchr/testify v1.8.0
+ github.com/stretchr/testify v1.9.0
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
)
require (
- github.com/Microsoft/go-winio v0.4.14 // indirect
+ github.com/Microsoft/go-winio v0.6.2 // indirect
+ github.com/containerd/log v0.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
- github.com/docker/distribution v2.8.2+incompatible // indirect
+ github.com/distribution/reference v0.6.0 // indirect
github.com/docker/docker-credential-helpers v0.8.0 // indirect
- github.com/docker/go-connections v0.4.0 // indirect
- github.com/docker/go-units v0.4.0 // indirect
+ github.com/docker/go-connections v0.5.0 // indirect
+ github.com/docker/go-units v0.5.0 // indirect
+ github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fvbommel/sortorder v1.1.0 // indirect
- github.com/gdamore/encoding v1.0.0 // indirect
- github.com/gdamore/tcell/v2 v2.5.3 // indirect
+ github.com/gdamore/encoding v1.0.1 // indirect
+ github.com/gdamore/tcell/v2 v2.7.4 // indirect
+ github.com/go-logr/logr v1.4.2 // indirect
+ github.com/go-logr/stdr v1.2.2 // indirect
github.com/goccy/go-yaml v1.11.0
github.com/gogo/protobuf v1.3.2 // indirect
- github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
- github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
- github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect
+ github.com/moby/docker-image-spec v1.3.1 // indirect
+ github.com/moby/term v0.5.0 // indirect
+ github.com/morikuni/aec v1.0.0 // indirect
github.com/onsi/ginkgo v1.8.0 // indirect
github.com/onsi/gomega v1.5.0 // indirect
- github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
- github.com/opencontainers/image-spec v1.0.2 // indirect
+ github.com/opencontainers/go-digest v1.0.0 // indirect
+ github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
github.com/pkg/errors v0.9.1 // indirect
- github.com/rivo/uniseg v0.4.2 // indirect
+ github.com/rivo/uniseg v0.4.7 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
- golang.org/x/exp v0.0.0-20220428152302-39d4317da171 // indirect
- golang.org/x/net v0.17.0 // indirect
- golang.org/x/sys v0.13.0 // indirect
- golang.org/x/term v0.13.0 // indirect
- golang.org/x/text v0.13.0 // indirect
- golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
- gopkg.in/yaml.v2 v2.2.2 // indirect
+ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
+ go.opentelemetry.io/otel v1.28.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 // indirect
+ go.opentelemetry.io/otel/metric v1.28.0 // indirect
+ go.opentelemetry.io/otel/sdk v1.28.0 // indirect
+ go.opentelemetry.io/otel/trace v1.28.0 // indirect
+ golang.org/x/crypto v0.24.0 // indirect
+ golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
+ golang.org/x/sys v0.21.0 // indirect
+ golang.org/x/term v0.21.0 // indirect
+ golang.org/x/text v0.16.0 // indirect
+ golang.org/x/time v0.5.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
- gotest.tools/v3 v3.2.0 // indirect
+ gotest.tools/v3 v3.5.1 // indirect
)
diff --git a/go.sum b/go.sum
index 748a5d21..ffacfe7f 100644
--- a/go.sum
+++ b/go.sum
@@ -1,66 +1,81 @@
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
-github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
-github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
+github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
+github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c h1:YDsGA6tou+tAxVe0Dre29iSbQ8TrWdWfwOisKArJT5E=
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c/go.mod h1:tMoSueLQlMf0TCldjrJLNIjAc5qAOIcHt5REi88/Ygo=
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1 h1:1fx+RA5lk1ZkzPAUP7DEgZnVHYxEcHO77vQO/V8z/2Q=
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1/go.mod h1:z0nyIb42Zs97wyX1V+8MbEFhHeTw1OgFQfR6q57ZuHc=
+github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
+github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do=
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc=
-github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
+github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/docker/cli v20.10.15+incompatible h1:HGO75iIgpyuG1m0hw0Kp7hY5o7XELmY1rsT9gIptOSU=
-github.com/docker/cli v20.10.15+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
-github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
-github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/docker v20.10.27-0.20230918140811-29a0e76e6495+incompatible h1:pgbpJ4kql71BzDNYSe18E489Ni4vn4yQ+0Xj8567Lvc=
-github.com/docker/docker v20.10.27-0.20230918140811-29a0e76e6495+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
+github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
+github.com/docker/cli v27.0.3+incompatible h1:usGs0/BoBW8MWxGeEtqPMkzOY56jZ6kYlSN5BLDioCQ=
+github.com/docker/cli v27.0.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/docker v27.0.3+incompatible h1:aBGI9TeQ4MPlhquTQKq9XbK79rKFVwXNUAYz9aXyEBE=
+github.com/docker/docker v27.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8=
github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40=
-github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
-github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
-github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
-github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
+github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
+github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
+github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
+github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
+github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw=
github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
-github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
-github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
-github.com/gdamore/tcell/v2 v2.5.3 h1:b9XQrT6QGbgI7JvZOJXFNczOQeIYbo8BfeSMzt2sAV0=
-github.com/gdamore/tcell/v2 v2.5.3/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo=
+github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
+github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
+github.com/gdamore/tcell/v2 v2.7.4 h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAYU=
+github.com/gdamore/tcell/v2 v2.7.4/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg=
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
-github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
-github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
+github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
+github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
+github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
+github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
+github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/goccy/go-yaml v1.11.0 h1:n7Z+zx8S9f9KgzG6KtQKf+kwqXZlLNR2F6018Dgau54=
github.com/goccy/go-yaml v1.11.0/go.mod h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw=
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
-github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
+github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/integrii/flaggy v1.4.0 h1:A1x7SYx4jqu5NSrY14z8Z+0UyX2S5ygfJJrfolWR3zM=
github.com/integrii/flaggy v1.4.0/go.mod h1:tnTxHeTJbah0gQ6/K0RW0J7fMUBk9MCF5blhm43LNpI=
github.com/jesseduffield/asciigraph v0.0.0-20190605104717-6d88e39309ee h1:7Zi/OQlGbMz4MT2V1+prN/gv1C64NDyVb/MbJnS0ZfA=
github.com/jesseduffield/asciigraph v0.0.0-20190605104717-6d88e39309ee/go.mod h1:Z9UKHveKXXgyo8ME7R8yxh/BUTFOK+FgfWKlhy8oOAg=
-github.com/jesseduffield/gocui v0.3.1-0.20221023185936-ef06450f4fdc h1:Gi/uDpmlB5XbyAzqr0RUFtK+2bVpUKkdR4soj/JKU1c=
-github.com/jesseduffield/gocui v0.3.1-0.20221023185936-ef06450f4fdc/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
+github.com/jesseduffield/gocui v0.3.1-0.20240418080333-8cd33929c513 h1:Y1bw5iItrsDCumATc/rklIJ/6K+68ieiWZJedhrNuXo=
+github.com/jesseduffield/gocui v0.3.1-0.20240418080333-8cd33929c513/go.mod h1:XtEbqCbn45keRXEu+OMZkjN5gw6AEob59afsgHjokZ8=
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 h1:jmpr7KpX2+2GRiE91zTgfq49QvgiqB0nbmlwZ8UnOx0=
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo=
github.com/jesseduffield/lazycore v0.0.0-20221023210126-718a4caea996 h1:CH1en6GpXSwnXl5Ehc4WX1NpS3uw9qbi7o9A4T2YYmA=
@@ -69,144 +84,177 @@ github.com/jesseduffield/yaml v0.0.0-20190702115811-b900b7e08b56 h1:33wSxJWU/f2T
github.com/jesseduffield/yaml v0.0.0-20190702115811-b900b7e08b56/go.mod h1:FZJBwOhE+RXz8EVZfY+xnbCw2cVOwxlK3/aIi581z/s=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
-github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
-github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
+github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
-github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
-github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
-github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
+github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767 h1:BrhJNdEFWGuiJk/3/SwsG5Rex3zjFxYsDi2bpd7382Y=
github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767/go.mod h1:ct+byCpkFokm4J0tiuAvB8cf2ttm6GcCe89Yr25nGKg=
github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
-github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=
-github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
-github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
-github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
+github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
+github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
+github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
+github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
+github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
+github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
-github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
-github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
-github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
+github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
+github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
+github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
+github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
-github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
+github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/samber/lo v1.31.0 h1:Sfa+/064Tdo4SvlohQUQzBhgSer9v/coGvKQI/XLWAM=
github.com/samber/lo v1.31.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8=
github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
-github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
-github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
-github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
+github.com/thoas/go-funk v0.9.1/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
+go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
+go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 h1:j9+03ymgYhPKmeXGk5Zu+cIZOlVzd9Zv7QIiyItjFBU=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0/go.mod h1:Y5+XiUG4Emn1hTfciPzGPJaSI+RpDts6BnCIir0SLqk=
+go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
+go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
+go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
+go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
+go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
+go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
+go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
+go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
-golang.org/x/exp v0.0.0-20220428152302-39d4317da171 h1:TfdoLivD44QwvssI9Sv1xwa5DcL5XQr4au4sZ2F2NV4=
-golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
+golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
+golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
+golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
-golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
+golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
-golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
+golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
+golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
-golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
+golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0=
+google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
+google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
+google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
+google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
+google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
-gotest.tools/v3 v3.2.0 h1:I0DwBVMGAx26dttAj1BtJLAkVGncrkkUXfJLC4Flt/I=
-gotest.tools/v3 v3.2.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
+gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
+gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
diff --git a/pkg/commands/container.go b/pkg/commands/container.go
index b45feef7..997175f2 100644
--- a/pkg/commands/container.go
+++ b/pkg/commands/container.go
@@ -43,7 +43,7 @@ type Container struct {
}
// Remove removes the container
-func (c *Container) Remove(options dockerTypes.ContainerRemoveOptions) error {
+func (c *Container) Remove(options container.RemoveOptions) error {
c.Log.Warn(fmt.Sprintf("removing container %s", c.Name))
if err := c.Client.ContainerRemove(context.Background(), c.ID, options); err != nil {
if strings.Contains(err.Error(), "Stop the container before attempting removal or force remove") {
@@ -62,7 +62,7 @@ func (c *Container) Remove(options dockerTypes.ContainerRemoveOptions) error {
// Stop stops the container
func (c *Container) Stop() error {
c.Log.Warn(fmt.Sprintf("stopping container %s", c.Name))
- return c.Client.ContainerStop(context.Background(), c.ID, nil)
+ return c.Client.ContainerStop(context.Background(), c.ID, container.StopOptions{})
}
// Pause pauses the container
@@ -80,7 +80,7 @@ func (c *Container) Unpause() error {
// Restart restarts the container
func (c *Container) Restart() error {
c.Log.Warn(fmt.Sprintf("restarting container %s", c.Name))
- return c.Client.ContainerRestart(context.Background(), c.ID, nil)
+ return c.Client.ContainerRestart(context.Background(), c.ID, container.StopOptions{})
}
// Attach attaches the container
@@ -140,7 +140,9 @@ func (c *Container) RenderTop(ctx context.Context) (string, error) {
return utils.RenderTable(append([][]string{result.Titles}, result.Processes...))
}
-// DetailsLoaded tells us whether we have yet loaded the details for a container. Because this is an asynchronous operation, sometimes we have the container before we have its details.
+// DetailsLoaded tells us whether we have yet loaded the details for a container.
+// Sometimes it takes some time for a container to have its details loaded
+// after it starts.
func (c *Container) DetailsLoaded() bool {
return c.Details.ContainerJSONBase != nil
}
diff --git a/pkg/commands/docker.go b/pkg/commands/docker.go
index 61d16879..0367ab24 100644
--- a/pkg/commands/docker.go
+++ b/pkg/commands/docker.go
@@ -10,12 +10,13 @@ import (
"os"
"os/exec"
"strings"
+ "sync"
"time"
cliconfig "github.com/docker/cli/cli/config"
ddocker "github.com/docker/cli/cli/context/docker"
ctxstore "github.com/docker/cli/cli/context/store"
- dockerTypes "github.com/docker/docker/api/types"
+ "github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
"github.com/imdario/mergo"
"github.com/jesseduffield/lazydocker/pkg/commands/ssh"
@@ -27,7 +28,8 @@ import (
)
const (
- APIVersion = "1.25"
+ APIVersion = "1.25"
+ dockerHostEnvKey = "DOCKER_HOST"
)
// DockerCommand is our main docker interface
@@ -108,12 +110,7 @@ func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Translat
Closers: []io.Closer{tunnelCloser},
}
- command := utils.ApplyTemplate(
- config.UserConfig.CommandTemplates.CheckDockerComposeConfig,
- dockerCommand.NewCommandObject(CommandObject{}),
- )
-
- log.Warn(command)
+ dockerCommand.setDockerComposeCommand(config)
err = osCommand.RunCommand(
utils.ApplyTemplate(
@@ -129,6 +126,18 @@ func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Translat
return dockerCommand, nil
}
+func (c *DockerCommand) setDockerComposeCommand(config *config.AppConfig) {
+ if config.UserConfig.CommandTemplates.DockerCompose != "docker compose" {
+ return
+ }
+
+ // it's possible that a user is still using docker-compose, so we'll check if 'docker comopose' is available, and if not, we'll fall back to 'docker-compose'
+ err := c.OSCommand.RunCommand("docker compose version")
+ if err != nil {
+ config.UserConfig.CommandTemplates.DockerCompose = "docker-compose"
+ }
+}
+
func (c *DockerCommand) Close() error {
return utils.CloseMany(c.Closers)
}
@@ -195,9 +204,9 @@ func (c *DockerCommand) RefreshContainersAndServices(currentServices []*Service,
func (c *DockerCommand) assignContainersToServices(containers []*Container, services []*Service) {
L:
for _, service := range services {
- for _, container := range containers {
- if !container.OneOff && container.ServiceName == service.Name {
- service.Container = container
+ for _, ctr := range containers {
+ if !ctr.OneOff && ctr.ServiceName == service.Name {
+ service.Container = ctr
continue L
}
}
@@ -210,19 +219,19 @@ func (c *DockerCommand) GetContainers(existingContainers []*Container) ([]*Conta
c.ContainerMutex.Lock()
defer c.ContainerMutex.Unlock()
- containers, err := c.Client.ContainerList(context.Background(), dockerTypes.ContainerListOptions{All: true})
+ containers, err := c.Client.ContainerList(context.Background(), container.ListOptions{All: true})
if err != nil {
return nil, err
}
ownContainers := make([]*Container, len(containers))
- for i, container := range containers {
+ for i, ctr := range containers {
var newContainer *Container
- // check if we already data stored against the container
+ // check if we already have data stored against the container
for _, existingContainer := range existingContainers {
- if existingContainer.ID == container.ID {
+ if existingContainer.ID == ctr.ID {
newContainer = existingContainer
break
}
@@ -231,7 +240,7 @@ func (c *DockerCommand) GetContainers(existingContainers []*Container) ([]*Conta
// initialise the container if it's completely new
if newContainer == nil {
newContainer = &Container{
- ID: container.ID,
+ ID: ctr.ID,
Client: c.Client,
OSCommand: c.OSCommand,
Log: c.Log,
@@ -240,21 +249,23 @@ func (c *DockerCommand) GetContainers(existingContainers []*Container) ([]*Conta
}
}
- newContainer.Container = container
+ newContainer.Container = ctr
// if the container is made with a name label we will use that
- if name, ok := container.Labels["name"]; ok {
+ if name, ok := ctr.Labels["name"]; ok {
newContainer.Name = name
} else {
- newContainer.Name = strings.TrimLeft(container.Names[0], "/")
+ newContainer.Name = strings.TrimLeft(ctr.Names[0], "/")
}
- newContainer.ServiceName = container.Labels["com.docker.compose.service"]
- newContainer.ProjectName = container.Labels["com.docker.compose.project"]
- newContainer.ContainerNumber = container.Labels["com.docker.compose.container"]
- newContainer.OneOff = container.Labels["com.docker.compose.oneoff"] == "True"
+ newContainer.ServiceName = ctr.Labels["com.docker.compose.service"]
+ newContainer.ProjectName = ctr.Labels["com.docker.compose.project"]
+ newContainer.ContainerNumber = ctr.Labels["com.docker.compose.container"]
+ newContainer.OneOff = ctr.Labels["com.docker.compose.oneoff"] == "True"
ownContainers[i] = newContainer
}
+ c.SetContainerDetails(ownContainers)
+
return ownContainers, nil
}
@@ -289,24 +300,35 @@ func (c *DockerCommand) GetServices() ([]*Service, error) {
return services, nil
}
-// UpdateContainerDetails attaches the details returned from docker inspect to each of the containers
-// this contains a bit more info than what you get from the go-docker client
-func (c *DockerCommand) UpdateContainerDetails(containers []*Container) error {
+func (c *DockerCommand) RefreshContainerDetails(containers []*Container) error {
c.ContainerMutex.Lock()
defer c.ContainerMutex.Unlock()
- for _, container := range containers {
- details, err := c.Client.ContainerInspect(context.Background(), container.ID)
- if err != nil {
- c.Log.Error(err)
- } else {
- container.Details = details
- }
- }
+ c.SetContainerDetails(containers)
return nil
}
+// Attaches the details returned from docker inspect to each of the containers
+// this contains a bit more info than what you get from the go-docker client
+func (c *DockerCommand) SetContainerDetails(containers []*Container) {
+ wg := sync.WaitGroup{}
+ for _, ctr := range containers {
+ ctr := ctr
+ wg.Add(1)
+ go func() {
+ details, err := c.Client.ContainerInspect(context.Background(), ctr.ID)
+ if err != nil {
+ c.Log.Error(err)
+ } else {
+ ctr.Details = details
+ }
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+}
+
// ViewAllLogs attaches to a subprocess viewing all the logs from docker-compose
func (c *DockerCommand) ViewAllLogs() (*exec.Cmd, error) {
cmd := c.OSCommand.ExecutableFromString(
@@ -356,7 +378,8 @@ func determineDockerHost() (string, error) {
currentContext = cf.CurrentContext
}
- if currentContext == "" {
+ // On some systems (windows) `default` is stored in the docker config as the currentContext.
+ if currentContext == "" || currentContext == "default" {
// If a docker context is neither specified via the "DOCKER_CONTEXT" environment variable nor via the
// $HOME/.docker/config file, then we fall back to connecting to the "default docker host" meant for
// the host operating system.
diff --git a/pkg/commands/image.go b/pkg/commands/image.go
index 5e29f198..789733d0 100644
--- a/pkg/commands/image.go
+++ b/pkg/commands/image.go
@@ -4,14 +4,12 @@ import (
"context"
"strings"
- "github.com/docker/docker/api/types/image"
- "github.com/samber/lo"
-
- dockerTypes "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
+ "github.com/docker/docker/api/types/image"
"github.com/docker/docker/client"
"github.com/fatih/color"
"github.com/jesseduffield/lazydocker/pkg/utils"
+ "github.com/samber/lo"
"github.com/sirupsen/logrus"
)
@@ -20,7 +18,7 @@ type Image struct {
Name string
Tag string
ID string
- Image dockerTypes.ImageSummary
+ Image image.Summary
Client *client.Client
OSCommand *OSCommand
Log *logrus.Entry
@@ -28,7 +26,7 @@ type Image struct {
}
// Remove removes the image
-func (i *Image) Remove(options dockerTypes.ImageRemoveOptions) error {
+func (i *Image) Remove(options image.RemoveOptions) error {
if _, err := i.Client.ImageRemove(context.Background(), i.ID, options); err != nil {
return err
}
@@ -94,16 +92,16 @@ func (i *Image) RenderHistory() (string, error) {
// RefreshImages returns a slice of docker images
func (c *DockerCommand) RefreshImages() ([]*Image, error) {
- images, err := c.Client.ImageList(context.Background(), dockerTypes.ImageListOptions{})
+ images, err := c.Client.ImageList(context.Background(), image.ListOptions{})
if err != nil {
return nil, err
}
ownImages := make([]*Image, len(images))
- for i, image := range images {
+ for i, img := range images {
firstTag := ""
- tags := image.RepoTags
+ tags := img.RepoTags
if len(tags) > 0 {
firstTag = tags[0]
}
@@ -124,10 +122,10 @@ func (c *DockerCommand) RefreshImages() ([]*Image, error) {
}
ownImages[i] = &Image{
- ID: image.ID,
+ ID: img.ID,
Name: name,
Tag: tag,
- Image: image,
+ Image: img,
Client: c.Client,
OSCommand: c.OSCommand,
Log: c.Log,
diff --git a/pkg/commands/network.go b/pkg/commands/network.go
index c1c18593..74379ef0 100644
--- a/pkg/commands/network.go
+++ b/pkg/commands/network.go
@@ -3,8 +3,8 @@ package commands
import (
"context"
- dockerTypes "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
+ "github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
"github.com/sirupsen/logrus"
)
@@ -12,7 +12,7 @@ import (
// Network : A docker Network
type Network struct {
Name string
- Network dockerTypes.NetworkResource
+ Network network.Inspect
Client *client.Client
OSCommand *OSCommand
Log *logrus.Entry
@@ -21,17 +21,17 @@ type Network struct {
// RefreshNetworks gets the networks and stores them
func (c *DockerCommand) RefreshNetworks() ([]*Network, error) {
- networks, err := c.Client.NetworkList(context.Background(), dockerTypes.NetworkListOptions{})
+ networks, err := c.Client.NetworkList(context.Background(), network.ListOptions{})
if err != nil {
return nil, err
}
ownNetworks := make([]*Network, len(networks))
- for i, network := range networks {
+ for i, nw := range networks {
ownNetworks[i] = &Network{
- Name: network.Name,
- Network: network,
+ Name: nw.Name,
+ Network: nw,
Client: c.Client,
OSCommand: c.OSCommand,
Log: c.Log,
diff --git a/pkg/commands/service.go b/pkg/commands/service.go
index 368b0dab..755b7f5f 100644
--- a/pkg/commands/service.go
+++ b/pkg/commands/service.go
@@ -4,7 +4,7 @@ import (
"context"
"os/exec"
- dockerTypes "github.com/docker/docker/api/types"
+ "github.com/docker/docker/api/types/container"
"github.com/jesseduffield/lazydocker/pkg/utils"
"github.com/sirupsen/logrus"
)
@@ -20,7 +20,7 @@ type Service struct {
}
// Remove removes the service's containers
-func (s *Service) Remove(options dockerTypes.ContainerRemoveOptions) error {
+func (s *Service) Remove(options container.RemoveOptions) error {
return s.Container.Remove(options)
}
@@ -39,7 +39,7 @@ func (s *Service) Restart() error {
return s.runCommand(s.OSCommand.Config.UserConfig.CommandTemplates.RestartService)
}
-// Restart starts the service
+// Start starts the service
func (s *Service) Start() error {
return s.runCommand(s.OSCommand.Config.UserConfig.CommandTemplates.StartService)
}
diff --git a/pkg/commands/volume.go b/pkg/commands/volume.go
index e49736b8..169ba8d5 100644
--- a/pkg/commands/volume.go
+++ b/pkg/commands/volume.go
@@ -3,8 +3,8 @@ package commands
import (
"context"
- dockerTypes "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
+ "github.com/docker/docker/api/types/volume"
"github.com/docker/docker/client"
"github.com/sirupsen/logrus"
)
@@ -12,7 +12,7 @@ import (
// Volume : A docker Volume
type Volume struct {
Name string
- Volume *dockerTypes.Volume
+ Volume *volume.Volume
Client *client.Client
OSCommand *OSCommand
Log *logrus.Entry
@@ -21,7 +21,7 @@ type Volume struct {
// RefreshVolumes gets the volumes and stores them
func (c *DockerCommand) RefreshVolumes() ([]*Volume, error) {
- result, err := c.Client.VolumeList(context.Background(), filters.Args{})
+ result, err := c.Client.VolumeList(context.Background(), volume.ListOptions{})
if err != nil {
return nil, err
}
@@ -30,10 +30,10 @@ func (c *DockerCommand) RefreshVolumes() ([]*Volume, error) {
ownVolumes := make([]*Volume, len(volumes))
- for i, volume := range volumes {
+ for i, vol := range volumes {
ownVolumes[i] = &Volume{
- Name: volume.Name,
- Volume: volume,
+ Name: vol.Name,
+ Volume: vol,
Client: c.Client,
OSCommand: c.OSCommand,
Log: c.Log,
diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go
index d35a244d..9c0ad474 100644
--- a/pkg/config/app_config.go
+++ b/pkg/config/app_config.go
@@ -138,6 +138,10 @@ type GuiConfig struct {
// containers panel. "long": full words (default), "short": one or two characters,
// "icon": unicode emoji.
ContainerStatusHealthStyle string `yaml:"containerStatusHealthStyle"`
+
+ // Window border style.
+ // One of 'rounded' (default) | 'single' | 'double' | 'hidden'
+ Border string `yaml:"border"`
}
// CommandTemplatesConfig determines what commands actually get called when we
@@ -164,7 +168,7 @@ type CommandTemplatesConfig struct {
// DockerCompose is for your docker-compose command. You may want to combine a
// few different docker-compose.yml files together, in which case you can set
- // this to "docker-compose -f foo/docker-compose.yml -f
+ // this to "docker compose -f foo/docker-compose.yml -f
// bah/docker-compose.yml". The reason that the other docker-compose command
// templates all start with {{ .DockerCompose }} is so that they can make use
// of whatever you've set in this value rather than you having to copy and
@@ -196,7 +200,7 @@ type CommandTemplatesConfig struct {
// and ensure they're running before trying to run the service at hand
RecreateService string `yaml:"recreateService,omitempty"`
- // AllLogs is for showing what you get from doing `docker-compose logs`. It
+ // AllLogs is for showing what you get from doing `docker compose logs`. It
// combines all the logs together
AllLogs string `yaml:"allLogs,omitempty"`
@@ -381,7 +385,7 @@ func GetDefaultConfig() UserConfig {
Tail: "",
},
CommandTemplates: CommandTemplatesConfig{
- DockerCompose: "docker-compose",
+ DockerCompose: "docker compose",
RestartService: "{{ .DockerCompose }} restart {{ .Service.Name }}",
StartService: "{{ .DockerCompose }} start {{ .Service.Name }}",
Up: "{{ .DockerCompose }} up -d",
@@ -498,7 +502,7 @@ func NewAppConfig(name, version, commit, date string, buildSource string, debugg
return nil, err
}
- // Pass compose files as individual -f flags to docker-compose
+ // Pass compose files as individual -f flags to docker compose
if len(composeFiles) > 0 {
userConfig.CommandTemplates.DockerCompose += " -f " + strings.Join(composeFiles, " -f ")
}
diff --git a/pkg/config/app_config_test.go b/pkg/config/app_config_test.go
index 1aa05db6..5e7c2922 100644
--- a/pkg/config/app_config_test.go
+++ b/pkg/config/app_config_test.go
@@ -15,7 +15,7 @@ func TestDockerComposeCommandNoFiles(t *testing.T) {
}
actual := conf.UserConfig.CommandTemplates.DockerCompose
- expected := "docker-compose"
+ expected := "docker compose"
if actual != expected {
t.Fatalf("Expected %s but got %s", expected, actual)
}
@@ -29,7 +29,7 @@ func TestDockerComposeCommandSingleFile(t *testing.T) {
}
actual := conf.UserConfig.CommandTemplates.DockerCompose
- expected := "docker-compose -f one.yml"
+ expected := "docker compose -f one.yml"
if actual != expected {
t.Fatalf("Expected %s but got %s", expected, actual)
}
@@ -43,7 +43,7 @@ func TestDockerComposeCommandMultipleFiles(t *testing.T) {
}
actual := conf.UserConfig.CommandTemplates.DockerCompose
- expected := "docker-compose -f one.yml -f two.yml -f three.yml"
+ expected := "docker compose -f one.yml -f two.yml -f three.yml"
if actual != expected {
t.Fatalf("Expected %s but got %s", expected, actual)
}
diff --git a/pkg/gui/confirmation_panel.go b/pkg/gui/confirmation_panel.go
index 0db7e787..6ed53553 100644
--- a/pkg/gui/confirmation_panel.go
+++ b/pkg/gui/confirmation_panel.go
@@ -64,7 +64,7 @@ func (gui *Gui) getConfirmationPanelDimensions(wrap bool, prompt string) (int, i
func (gui *Gui) createPromptPanel(title string, handleConfirm func(*gocui.Gui, *gocui.View) error) error {
gui.onNewPopupPanel()
- err := gui.prepareConfirmationPanel(title, "", false)
+ err := gui.prepareConfirmationPanel(title, "")
if err != nil {
return err
}
@@ -72,17 +72,13 @@ func (gui *Gui) createPromptPanel(title string, handleConfirm func(*gocui.Gui, *
return gui.setKeyBindings(gui.g, handleConfirm, nil)
}
-func (gui *Gui) prepareConfirmationPanel(title, prompt string, hasLoader bool) error {
+func (gui *Gui) prepareConfirmationPanel(title, prompt string) error {
x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(true, prompt)
confirmationView := gui.Views.Confirmation
_, err := gui.g.SetView("confirmation", x0, y0, x1, y1, 0)
if err != nil {
return err
}
- confirmationView.HasLoader = hasLoader
- if hasLoader {
- gui.g.StartTicking()
- }
confirmationView.Title = title
confirmationView.Visible = true
gui.g.Update(func(g *gocui.Gui) error {
@@ -100,10 +96,10 @@ func (gui *Gui) onNewPopupPanel() {
// The golangcilint unparam linter complains that handleClose is alwans nil but one day it won't be nil.
// nolint:unparam
func (gui *Gui) createConfirmationPanel(title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error {
- return gui.createPopupPanel(title, prompt, false, handleConfirm, handleClose)
+ return gui.createPopupPanel(title, prompt, handleConfirm, handleClose)
}
-func (gui *Gui) createPopupPanel(title, prompt string, hasLoader bool, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error {
+func (gui *Gui) createPopupPanel(title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error {
gui.onNewPopupPanel()
gui.g.Update(func(g *gocui.Gui) error {
if gui.currentViewName() == "confirmation" {
@@ -111,7 +107,7 @@ func (gui *Gui) createPopupPanel(title, prompt string, hasLoader bool, handleCon
gui.Log.Error(err.Error())
}
}
- err := gui.prepareConfirmationPanel(title, prompt, hasLoader)
+ err := gui.prepareConfirmationPanel(title, prompt)
if err != nil {
return err
}
diff --git a/pkg/gui/container_logs.go b/pkg/gui/container_logs.go
index bb2feb85..bdb81608 100644
--- a/pkg/gui/container_logs.go
+++ b/pkg/gui/container_logs.go
@@ -8,7 +8,7 @@ import (
"os/signal"
"time"
- dockerTypes "github.com/docker/docker/api/types"
+ "github.com/docker/docker/api/types/container"
"github.com/docker/docker/pkg/stdcopy"
"github.com/fatih/color"
"github.com/jesseduffield/lazydocker/pkg/commands"
@@ -104,8 +104,8 @@ func (gui *Gui) promptToReturn() {
}
}
-func (gui *Gui) writeContainerLogs(container *commands.Container, ctx context.Context, writer io.Writer) error {
- readCloser, err := gui.DockerCommand.Client.ContainerLogs(ctx, container.ID, dockerTypes.ContainerLogsOptions{
+func (gui *Gui) writeContainerLogs(ctr *commands.Container, ctx context.Context, writer io.Writer) error {
+ readCloser, err := gui.DockerCommand.Client.ContainerLogs(ctx, ctr.ID, container.LogsOptions{
ShowStdout: true,
ShowStderr: true,
Timestamps: gui.Config.UserConfig.Logs.Timestamps,
@@ -114,10 +114,29 @@ func (gui *Gui) writeContainerLogs(container *commands.Container, ctx context.Co
Follow: true,
})
if err != nil {
+ gui.Log.Error(err)
return err
}
+ defer readCloser.Close()
- if container.DetailsLoaded() && container.Details.Config.Tty {
+ if !ctr.DetailsLoaded() {
+ // loop until the details load or context is cancelled, using timer
+ ticker := time.NewTicker(time.Millisecond * 100)
+ defer ticker.Stop()
+ outer:
+ for {
+ select {
+ case <-ctx.Done():
+ return nil
+ case <-ticker.C:
+ if ctr.DetailsLoaded() {
+ break outer
+ }
+ }
+ }
+ }
+
+ if ctr.Details.Config.Tty {
_, err = io.Copy(writer, readCloser)
if err != nil {
return err
diff --git a/pkg/gui/containers_panel.go b/pkg/gui/containers_panel.go
index 24965c6c..92aa95b3 100644
--- a/pkg/gui/containers_panel.go
+++ b/pkg/gui/containers_panel.go
@@ -6,7 +6,7 @@ import (
"strings"
"time"
- dockerTypes "github.com/docker/docker/api/types"
+ "github.com/docker/docker/api/types/container"
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazydocker/pkg/commands"
@@ -303,19 +303,19 @@ func (gui *Gui) handleHideStoppedContainers(g *gocui.Gui, v *gocui.View) error {
}
func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error {
- container, err := gui.Panels.Containers.GetSelectedItem()
+ ctr, err := gui.Panels.Containers.GetSelectedItem()
if err != nil {
return nil
}
- handleMenuPress := func(configOptions dockerTypes.ContainerRemoveOptions) error {
+ handleMenuPress := func(configOptions container.RemoveOptions) error {
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
- if err := container.Remove(configOptions); err != nil {
+ if err := ctr.Remove(configOptions); err != nil {
if commands.HasErrorCode(err, commands.MustStopContainer) {
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.MustForceToRemoveContainer, func(g *gocui.Gui, v *gocui.View) error {
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
configOptions.Force = true
- return container.Remove(configOptions)
+ return ctr.Remove(configOptions)
})
}, nil)
}
@@ -327,12 +327,12 @@ func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error {
menuItems := []*types.MenuItem{
{
- LabelColumns: []string{gui.Tr.Remove, "docker rm " + container.ID[1:10]},
- OnPress: func() error { return handleMenuPress(dockerTypes.ContainerRemoveOptions{}) },
+ LabelColumns: []string{gui.Tr.Remove, "docker rm " + ctr.ID[1:10]},
+ OnPress: func() error { return handleMenuPress(container.RemoveOptions{}) },
},
{
- LabelColumns: []string{gui.Tr.RemoveWithVolumes, "docker rm --volumes " + container.ID[1:10]},
- OnPress: func() error { return handleMenuPress(dockerTypes.ContainerRemoveOptions{RemoveVolumes: true}) },
+ LabelColumns: []string{gui.Tr.RemoveWithVolumes, "docker rm --volumes " + ctr.ID[1:10]},
+ OnPress: func() error { return handleMenuPress(container.RemoveOptions{RemoveVolumes: true}) },
},
}
@@ -359,23 +359,23 @@ func (gui *Gui) PauseContainer(container *commands.Container) error {
}
func (gui *Gui) handleContainerPause(g *gocui.Gui, v *gocui.View) error {
- container, err := gui.Panels.Containers.GetSelectedItem()
+ ctr, err := gui.Panels.Containers.GetSelectedItem()
if err != nil {
return nil
}
- return gui.PauseContainer(container)
+ return gui.PauseContainer(ctr)
}
func (gui *Gui) handleContainerStop(g *gocui.Gui, v *gocui.View) error {
- container, err := gui.Panels.Containers.GetSelectedItem()
+ ctr, err := gui.Panels.Containers.GetSelectedItem()
if err != nil {
return nil
}
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.StopContainer, func(g *gocui.Gui, v *gocui.View) error {
return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error {
- if err := container.Stop(); err != nil {
+ if err := ctr.Stop(); err != nil {
return gui.createErrorPanel(err.Error())
}
@@ -385,13 +385,13 @@ func (gui *Gui) handleContainerStop(g *gocui.Gui, v *gocui.View) error {
}
func (gui *Gui) handleContainerRestart(g *gocui.Gui, v *gocui.View) error {
- container, err := gui.Panels.Containers.GetSelectedItem()
+ ctr, err := gui.Panels.Containers.GetSelectedItem()
if err != nil {
return nil
}
return gui.WithWaitingStatus(gui.Tr.RestartingStatus, func() error {
- if err := container.Restart(); err != nil {
+ if err := ctr.Restart(); err != nil {
return gui.createErrorPanel(err.Error())
}
@@ -400,12 +400,12 @@ func (gui *Gui) handleContainerRestart(g *gocui.Gui, v *gocui.View) error {
}
func (gui *Gui) handleContainerAttach(g *gocui.Gui, v *gocui.View) error {
- container, err := gui.Panels.Containers.GetSelectedItem()
+ ctr, err := gui.Panels.Containers.GetSelectedItem()
if err != nil {
return nil
}
- c, err := container.Attach()
+ c, err := ctr.Attach()
if err != nil {
return gui.createErrorPanel(err.Error())
}
@@ -426,23 +426,23 @@ func (gui *Gui) handlePruneContainers() error {
}
func (gui *Gui) handleContainerViewLogs(g *gocui.Gui, v *gocui.View) error {
- container, err := gui.Panels.Containers.GetSelectedItem()
+ ctr, err := gui.Panels.Containers.GetSelectedItem()
if err != nil {
return nil
}
- gui.renderLogsToStdout(container)
+ gui.renderLogsToStdout(ctr)
return nil
}
func (gui *Gui) handleContainersExecShell(g *gocui.Gui, v *gocui.View) error {
- container, err := gui.Panels.Containers.GetSelectedItem()
+ ctr, err := gui.Panels.Containers.GetSelectedItem()
if err != nil {
return nil
}
- return gui.containerExecShell(container)
+ return gui.containerExecShell(ctr)
}
func (gui *Gui) containerExecShell(container *commands.Container) error {
@@ -458,13 +458,13 @@ func (gui *Gui) containerExecShell(container *commands.Container) error {
}
func (gui *Gui) handleContainersCustomCommand(g *gocui.Gui, v *gocui.View) error {
- container, err := gui.Panels.Containers.GetSelectedItem()
+ ctr, err := gui.Panels.Containers.GetSelectedItem()
if err != nil {
return nil
}
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
- Container: container,
+ Container: ctr,
})
customCommands := gui.Config.UserConfig.CustomCommands.Containers
@@ -475,8 +475,8 @@ func (gui *Gui) handleContainersCustomCommand(g *gocui.Gui, v *gocui.View) error
func (gui *Gui) handleStopContainers() error {
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmStopContainers, func(g *gocui.Gui, v *gocui.View) error {
return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error {
- for _, container := range gui.Panels.Containers.List.GetAllItems() {
- if err := container.Stop(); err != nil {
+ for _, ctr := range gui.Panels.Containers.List.GetAllItems() {
+ if err := ctr.Stop(); err != nil {
gui.Log.Error(err)
}
}
@@ -489,8 +489,8 @@ func (gui *Gui) handleStopContainers() error {
func (gui *Gui) handleRemoveContainers() error {
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmRemoveContainers, func(g *gocui.Gui, v *gocui.View) error {
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
- for _, container := range gui.Panels.Containers.List.GetAllItems() {
- if err := container.Remove(dockerTypes.ContainerRemoveOptions{Force: true}); err != nil {
+ for _, ctr := range gui.Panels.Containers.List.GetAllItems() {
+ if err := ctr.Remove(container.RemoveOptions{Force: true}); err != nil {
gui.Log.Error(err)
}
}
@@ -524,21 +524,21 @@ func (gui *Gui) handleContainersBulkCommand(g *gocui.Gui, v *gocui.View) error {
// Open first port in browser
func (gui *Gui) handleContainersOpenInBrowserCommand(g *gocui.Gui, v *gocui.View) error {
- container, err := gui.Panels.Containers.GetSelectedItem()
+ ctr, err := gui.Panels.Containers.GetSelectedItem()
if err != nil {
return nil
}
- return gui.openContainerInBrowser(container)
+ return gui.openContainerInBrowser(ctr)
}
-func (gui *Gui) openContainerInBrowser(container *commands.Container) error {
+func (gui *Gui) openContainerInBrowser(ctr *commands.Container) error {
// skip if no any ports
- if len(container.Container.Ports) == 0 {
+ if len(ctr.Container.Ports) == 0 {
return nil
}
// skip if the first port is not published
- port := container.Container.Ports[0]
+ port := ctr.Container.Ports[0]
if port.IP == "" {
return nil
}
diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go
index 4797fb7f..fa6199f3 100644
--- a/pkg/gui/gui.go
+++ b/pkg/gui/gui.go
@@ -6,7 +6,7 @@ import (
"strings"
"time"
- dockerTypes "github.com/docker/docker/api/types"
+ "github.com/docker/docker/api/types/events"
"github.com/go-errors/errors"
@@ -23,9 +23,6 @@ import (
"github.com/sirupsen/logrus"
)
-// OverlappingEdges determines if panel edges overlap
-var OverlappingEdges = false
-
// Gui wraps the gocui Gui object which handles rendering and events
type Gui struct {
g *gocui.Gui
@@ -176,11 +173,9 @@ func (gui *Gui) goEvery(interval time.Duration, function func() error) {
ticker := time.NewTicker(interval)
defer ticker.Stop()
for range ticker.C {
- if gui.PauseBackgroundThreads {
- return
+ if !gui.PauseBackgroundThreads {
+ _ = function()
}
-
- _ = function()
}
}()
}
@@ -190,7 +185,10 @@ func (gui *Gui) Run() error {
// closing our task manager which in turn closes the current task if there is any, so we aren't leaving processes lying around after closing lazydocker
defer gui.taskManager.Close()
- g, err := gocui.NewGui(gocui.OutputTrue, OverlappingEdges, gocui.NORMAL, false, map[rune]string{})
+ g, err := gocui.NewGui(gocui.NewGuiOpts{
+ OutputMode: gocui.OutputTrue,
+ RuneReplacements: map[rune]string{},
+ })
if err != nil {
return err
}
@@ -294,7 +292,7 @@ func (gui *Gui) setPanels() {
}
func (gui *Gui) updateContainerDetails() error {
- return gui.DockerCommand.UpdateContainerDetails(gui.Panels.Containers.List.GetAllItems())
+ return gui.DockerCommand.RefreshContainerDetails(gui.Panels.Containers.List.GetAllItems())
}
func (gui *Gui) refresh() {
@@ -338,7 +336,7 @@ func (gui *Gui) listenForEvents(ctx context.Context, refresh func()) {
outer:
for {
- messageChan, errChan := gui.DockerCommand.Client.Events(context.Background(), dockerTypes.EventsOptions{})
+ messageChan, errChan := gui.DockerCommand.Client.Events(context.Background(), events.ListOptions{})
if errorCount > 0 {
select {
@@ -498,7 +496,11 @@ func (gui *Gui) monitorContainerStats(ctx context.Context) {
// and panels to exist for us to know what keybindings there are, so we invoke
// gocui in headless mode and create them.
func (gui *Gui) SetupFakeGui() {
- g, err := gocui.NewGui(gocui.OutputTrue, false, gocui.NORMAL, true, map[rune]string{})
+ g, err := gocui.NewGui(gocui.NewGuiOpts{
+ OutputMode: gocui.OutputTrue,
+ RuneReplacements: map[rune]string{},
+ Headless: true,
+ })
if err != nil {
panic(err)
}
diff --git a/pkg/gui/images_panel.go b/pkg/gui/images_panel.go
index 6ef50fa9..7b0d5643 100644
--- a/pkg/gui/images_panel.go
+++ b/pkg/gui/images_panel.go
@@ -5,7 +5,7 @@ import (
"strings"
"time"
- dockerTypes "github.com/docker/docker/api/types"
+ "github.com/docker/docker/api/types/image"
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazydocker/pkg/commands"
@@ -123,37 +123,37 @@ func (gui *Gui) handleImagesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
type removeImageOption struct {
description string
command string
- configOptions dockerTypes.ImageRemoveOptions
+ configOptions image.RemoveOptions
}
- image, err := gui.Panels.Images.GetSelectedItem()
+ img, err := gui.Panels.Images.GetSelectedItem()
if err != nil {
return nil
}
- shortSha := image.ID[7:17]
+ shortSha := img.ID[7:17]
// TODO: have a way of toggling in a menu instead of showing each permutation as a separate menu item
options := []*removeImageOption{
{
description: gui.Tr.Remove,
command: "docker image rm " + shortSha,
- configOptions: dockerTypes.ImageRemoveOptions{PruneChildren: true, Force: false},
+ configOptions: image.RemoveOptions{PruneChildren: true, Force: false},
},
{
description: gui.Tr.RemoveWithoutPrune,
command: "docker image rm --no-prune " + shortSha,
- configOptions: dockerTypes.ImageRemoveOptions{PruneChildren: false, Force: false},
+ configOptions: image.RemoveOptions{PruneChildren: false, Force: false},
},
{
description: gui.Tr.RemoveWithForce,
command: "docker image rm --force " + shortSha,
- configOptions: dockerTypes.ImageRemoveOptions{PruneChildren: true, Force: true},
+ configOptions: image.RemoveOptions{PruneChildren: true, Force: true},
},
{
description: gui.Tr.RemoveWithoutPruneWithForce,
command: "docker image rm --no-prune --force " + shortSha,
- configOptions: dockerTypes.ImageRemoveOptions{PruneChildren: false, Force: true},
+ configOptions: image.RemoveOptions{PruneChildren: false, Force: true},
},
}
@@ -164,7 +164,7 @@ func (gui *Gui) handleImagesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
color.New(color.FgRed).Sprint(option.command),
},
OnPress: func() error {
- if err := image.Remove(option.configOptions); err != nil {
+ if err := img.Remove(option.configOptions); err != nil {
return gui.createErrorPanel(err.Error())
}
@@ -192,13 +192,13 @@ func (gui *Gui) handlePruneImages() error {
}
func (gui *Gui) handleImagesCustomCommand(g *gocui.Gui, v *gocui.View) error {
- image, err := gui.Panels.Images.GetSelectedItem()
+ img, err := gui.Panels.Images.GetSelectedItem()
if err != nil {
return nil
}
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
- Image: image,
+ Image: img,
})
customCommands := gui.Config.UserConfig.CustomCommands.Images
diff --git a/pkg/gui/views.go b/pkg/gui/views.go
index 8cadf3c7..60cf7201 100644
--- a/pkg/gui/views.go
+++ b/pkg/gui/views.go
@@ -92,12 +92,23 @@ func (gui *Gui) orderedViewNameMappings() []viewNameMapping {
}
func (gui *Gui) createAllViews() error {
+ frameRunes := []rune{'─', '│', '╭', '╮', '╰', '╯'}
+ switch gui.Config.UserConfig.Gui.Border {
+ case "single":
+ frameRunes = []rune{'─', '│', '┌', '┐', '└', '┘'}
+ case "double":
+ frameRunes = []rune{'═', '║', '╔', '╗', '╚', '╝'}
+ case "hidden":
+ frameRunes = []rune{' ', ' ', ' ', ' ', ' ', ' '}
+ }
+
var err error
for _, mapping := range gui.orderedViewNameMappings() {
*mapping.viewPtr, err = gui.prepareView(mapping.name)
if err != nil && err.Error() != UNKNOWN_VIEW_ERROR_MSG {
return err
}
+ (*mapping.viewPtr).FrameRunes = frameRunes
(*mapping.viewPtr).FgColor = gocui.ColorDefault
}
diff --git a/pkg/i18n/portuguese.go b/pkg/i18n/portuguese.go
index a9f263ec..36d9bcfd 100644
--- a/pkg/i18n/portuguese.go
+++ b/pkg/i18n/portuguese.go
@@ -121,7 +121,7 @@ func portugueseSet() TranslationSet {
ConfirmStopContainers: "Tem certeza que deseja parar todos os contêineres?",
ConfirmRemoveContainers: "Tem certeza que deseja remover todos os contêineres?",
ConfirmPruneVolumes: "Tem certeza que deseja destruir todos os volumes não utilizados?",
- ConfirmPruneNetworks: "Tem certeza que deseja destruir todos as redes não utilizadas?",
+ ConfirmPruneNetworks: "Tem certeza que deseja destruir todas as redes não utilizadas?",
StopService: "Tem certeza que deseja parar os contêineres deste serviço?",
StopContainer: "Tem certeza que deseja parar este contêiner?",
PressEnterToReturn: "Pressione enter para retornar ao lazydocker (este prompt pode ser desativado em sua configuração definindo `gui.returnImmediately: true`)",
diff --git a/pkg/tasks/tasks.go b/pkg/tasks/tasks.go
index e6b9d4ac..06fac38f 100644
--- a/pkg/tasks/tasks.go
+++ b/pkg/tasks/tasks.go
@@ -65,9 +65,12 @@ func (t *TaskManager) NewTask(f func(ctx context.Context)) error {
t.waitingMutex.Lock()
defer t.waitingMutex.Unlock()
+ t.taskIDMutex.Lock()
if taskID < t.newTaskId {
+ t.taskIDMutex.Unlock()
return
}
+ t.taskIDMutex.Unlock()
ctx, cancel := context.WithCancel(context.Background())
notifyStopped := make(chan struct{})
diff --git a/test.sh b/test.sh
old mode 100644
new mode 100755
diff --git a/vendor/github.com/Microsoft/go-winio/.gitattributes b/vendor/github.com/Microsoft/go-winio/.gitattributes
new file mode 100644
index 00000000..94f480de
--- /dev/null
+++ b/vendor/github.com/Microsoft/go-winio/.gitattributes
@@ -0,0 +1 @@
+* text=auto eol=lf
\ No newline at end of file
diff --git a/vendor/github.com/Microsoft/go-winio/.gitignore b/vendor/github.com/Microsoft/go-winio/.gitignore
index b883f1fd..815e2066 100644
--- a/vendor/github.com/Microsoft/go-winio/.gitignore
+++ b/vendor/github.com/Microsoft/go-winio/.gitignore
@@ -1 +1,10 @@
+.vscode/
+
*.exe
+
+# testing
+testdata
+
+# go workspaces
+go.work
+go.work.sum
diff --git a/vendor/github.com/Microsoft/go-winio/.golangci.yml b/vendor/github.com/Microsoft/go-winio/.golangci.yml
new file mode 100644
index 00000000..faedfe93
--- /dev/null
+++ b/vendor/github.com/Microsoft/go-winio/.golangci.yml
@@ -0,0 +1,147 @@
+linters:
+ enable:
+ # style
+ - containedctx # struct contains a context
+ - dupl # duplicate code
+ - errname # erorrs are named correctly
+ - nolintlint # "//nolint" directives are properly explained
+ - revive # golint replacement
+ - unconvert # unnecessary conversions
+ - wastedassign
+
+ # bugs, performance, unused, etc ...
+ - contextcheck # function uses a non-inherited context
+ - errorlint # errors not wrapped for 1.13
+ - exhaustive # check exhaustiveness of enum switch statements
+ - gofmt # files are gofmt'ed
+ - gosec # security
+ - nilerr # returns nil even with non-nil error
+ - thelper # test helpers without t.Helper()
+ - unparam # unused function params
+
+issues:
+ exclude-dirs:
+ - pkg/etw/sample
+
+ exclude-rules:
+ # err is very often shadowed in nested scopes
+ - linters:
+ - govet
+ text: '^shadow: declaration of "err" shadows declaration'
+
+ # ignore long lines for skip autogen directives
+ - linters:
+ - revive
+ text: "^line-length-limit: "
+ source: "^//(go:generate|sys) "
+
+ #TODO: remove after upgrading to go1.18
+ # ignore comment spacing for nolint and sys directives
+ - linters:
+ - revive
+ text: "^comment-spacings: no space between comment delimiter and comment text"
+ source: "//(cspell:|nolint:|sys |todo)"
+
+ # not on go 1.18 yet, so no any
+ - linters:
+ - revive
+ text: "^use-any: since GO 1.18 'interface{}' can be replaced by 'any'"
+
+ # allow unjustified ignores of error checks in defer statements
+ - linters:
+ - nolintlint
+ text: "^directive `//nolint:errcheck` should provide explanation"
+ source: '^\s*defer '
+
+ # allow unjustified ignores of error lints for io.EOF
+ - linters:
+ - nolintlint
+ text: "^directive `//nolint:errorlint` should provide explanation"
+ source: '[=|!]= io.EOF'
+
+
+linters-settings:
+ exhaustive:
+ default-signifies-exhaustive: true
+ govet:
+ enable-all: true
+ disable:
+ # struct order is often for Win32 compat
+ # also, ignore pointer bytes/GC issues for now until performance becomes an issue
+ - fieldalignment
+ nolintlint:
+ require-explanation: true
+ require-specific: true
+ revive:
+ # revive is more configurable than static check, so likely the preferred alternative to static-check
+ # (once the perf issue is solved: https://github.com/golangci/golangci-lint/issues/2997)
+ enable-all-rules:
+ true
+ # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md
+ rules:
+ # rules with required arguments
+ - name: argument-limit
+ disabled: true
+ - name: banned-characters
+ disabled: true
+ - name: cognitive-complexity
+ disabled: true
+ - name: cyclomatic
+ disabled: true
+ - name: file-header
+ disabled: true
+ - name: function-length
+ disabled: true
+ - name: function-result-limit
+ disabled: true
+ - name: max-public-structs
+ disabled: true
+ # geneally annoying rules
+ - name: add-constant # complains about any and all strings and integers
+ disabled: true
+ - name: confusing-naming # we frequently use "Foo()" and "foo()" together
+ disabled: true
+ - name: flag-parameter # excessive, and a common idiom we use
+ disabled: true
+ - name: unhandled-error # warns over common fmt.Print* and io.Close; rely on errcheck instead
+ disabled: true
+ # general config
+ - name: line-length-limit
+ arguments:
+ - 140
+ - name: var-naming
+ arguments:
+ - []
+ - - CID
+ - CRI
+ - CTRD
+ - DACL
+ - DLL
+ - DOS
+ - ETW
+ - FSCTL
+ - GCS
+ - GMSA
+ - HCS
+ - HV
+ - IO
+ - LCOW
+ - LDAP
+ - LPAC
+ - LTSC
+ - MMIO
+ - NT
+ - OCI
+ - PMEM
+ - PWSH
+ - RX
+ - SACl
+ - SID
+ - SMB
+ - TX
+ - VHD
+ - VHDX
+ - VMID
+ - VPCI
+ - WCOW
+ - WIM
diff --git a/vendor/github.com/Microsoft/go-winio/CODEOWNERS b/vendor/github.com/Microsoft/go-winio/CODEOWNERS
new file mode 100644
index 00000000..ae1b4942
--- /dev/null
+++ b/vendor/github.com/Microsoft/go-winio/CODEOWNERS
@@ -0,0 +1 @@
+ * @microsoft/containerplat
diff --git a/vendor/github.com/Microsoft/go-winio/README.md b/vendor/github.com/Microsoft/go-winio/README.md
index 56800105..7474b4f0 100644
--- a/vendor/github.com/Microsoft/go-winio/README.md
+++ b/vendor/github.com/Microsoft/go-winio/README.md
@@ -1,4 +1,4 @@
-# go-winio
+# go-winio [](https://github.com/microsoft/go-winio/actions/workflows/ci.yml)
This repository contains utilities for efficiently performing Win32 IO operations in
Go. Currently, this is focused on accessing named pipes and other file handles, and
@@ -11,12 +11,79 @@ package.
Please see the LICENSE file for licensing information.
-This project has adopted the [Microsoft Open Source Code of
-Conduct](https://opensource.microsoft.com/codeofconduct/). For more information
-see the [Code of Conduct
-FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact
-[opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional
-questions or comments.
+## Contributing
-Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe
-for another named pipe implementation.
+This project welcomes contributions and suggestions.
+Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that
+you have the right to, and actually do, grant us the rights to use your contribution.
+For details, visit [Microsoft CLA](https://cla.microsoft.com).
+
+When you submit a pull request, a CLA-bot will automatically determine whether you need to
+provide a CLA and decorate the PR appropriately (e.g., label, comment).
+Simply follow the instructions provided by the bot.
+You will only need to do this once across all repos using our CLA.
+
+Additionally, the pull request pipeline requires the following steps to be performed before
+mergining.
+
+### Code Sign-Off
+
+We require that contributors sign their commits using [`git commit --signoff`][git-commit-s]
+to certify they either authored the work themselves or otherwise have permission to use it in this project.
+
+A range of commits can be signed off using [`git rebase --signoff`][git-rebase-s].
+
+Please see [the developer certificate](https://developercertificate.org) for more info,
+as well as to make sure that you can attest to the rules listed.
+Our CI uses the DCO Github app to ensure that all commits in a given PR are signed-off.
+
+### Linting
+
+Code must pass a linting stage, which uses [`golangci-lint`][lint].
+The linting settings are stored in [`.golangci.yaml`](./.golangci.yaml), and can be run
+automatically with VSCode by adding the following to your workspace or folder settings:
+
+```json
+ "go.lintTool": "golangci-lint",
+ "go.lintOnSave": "package",
+```
+
+Additional editor [integrations options are also available][lint-ide].
+
+Alternatively, `golangci-lint` can be [installed locally][lint-install] and run from the repo root:
+
+```shell
+# use . or specify a path to only lint a package
+# to show all lint errors, use flags "--max-issues-per-linter=0 --max-same-issues=0"
+> golangci-lint run ./...
+```
+
+### Go Generate
+
+The pipeline checks that auto-generated code, via `go generate`, are up to date.
+
+This can be done for the entire repo:
+
+```shell
+> go generate ./...
+```
+
+## Code of Conduct
+
+This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
+For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
+contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
+
+## Special Thanks
+
+Thanks to [natefinch][natefinch] for the inspiration for this library.
+See [npipe](https://github.com/natefinch/npipe) for another named pipe implementation.
+
+[lint]: https://golangci-lint.run/
+[lint-ide]: https://golangci-lint.run/usage/integrations/#editor-integration
+[lint-install]: https://golangci-lint.run/usage/install/#local-installation
+
+[git-commit-s]: https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s
+[git-rebase-s]: https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---signoff
+
+[natefinch]: https://github.com/natefinch
diff --git a/vendor/github.com/Microsoft/go-winio/SECURITY.md b/vendor/github.com/Microsoft/go-winio/SECURITY.md
new file mode 100644
index 00000000..869fdfe2
--- /dev/null
+++ b/vendor/github.com/Microsoft/go-winio/SECURITY.md
@@ -0,0 +1,41 @@
+
+
+## Security
+
+Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
+
+If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
+
+## Reporting Security Issues
+
+**Please do not report security vulnerabilities through public GitHub issues.**
+
+Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
+
+If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
+
+You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
+
+Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
+
+ * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
+ * Full paths of source file(s) related to the manifestation of the issue
+ * The location of the affected source code (tag/branch/commit or direct URL)
+ * Any special configuration required to reproduce the issue
+ * Step-by-step instructions to reproduce the issue
+ * Proof-of-concept or exploit code (if possible)
+ * Impact of the issue, including how an attacker might exploit the issue
+
+This information will help us triage your report more quickly.
+
+If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
+
+## Preferred Languages
+
+We prefer all communications to be in English.
+
+## Policy
+
+Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
+
+
diff --git a/vendor/github.com/Microsoft/go-winio/backup.go b/vendor/github.com/Microsoft/go-winio/backup.go
index 2be34af4..b54341da 100644
--- a/vendor/github.com/Microsoft/go-winio/backup.go
+++ b/vendor/github.com/Microsoft/go-winio/backup.go
@@ -1,3 +1,4 @@
+//go:build windows
// +build windows
package winio
@@ -7,15 +8,16 @@ import (
"errors"
"fmt"
"io"
- "io/ioutil"
"os"
"runtime"
- "syscall"
"unicode/utf16"
+
+ "github.com/Microsoft/go-winio/internal/fs"
+ "golang.org/x/sys/windows"
)
-//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
+//sys backupRead(h windows.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
+//sys backupWrite(h windows.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
const (
BackupData = uint32(iota + 1)
@@ -24,7 +26,7 @@ const (
BackupAlternateData
BackupLink
BackupPropertyData
- BackupObjectId
+ BackupObjectId //revive:disable-line:var-naming ID, not Id
BackupReparseData
BackupSparseBlock
BackupTxfsData
@@ -34,14 +36,16 @@ const (
StreamSparseAttributes = uint32(8)
)
+//nolint:revive // var-naming: ALL_CAPS
const (
- WRITE_DAC = 0x40000
- WRITE_OWNER = 0x80000
- ACCESS_SYSTEM_SECURITY = 0x1000000
+ WRITE_DAC = windows.WRITE_DAC
+ WRITE_OWNER = windows.WRITE_OWNER
+ ACCESS_SYSTEM_SECURITY = windows.ACCESS_SYSTEM_SECURITY
)
// BackupHeader represents a backup stream of a file.
type BackupHeader struct {
+ //revive:disable-next-line:var-naming ID, not Id
Id uint32 // The backup stream ID
Attributes uint32 // Stream attributes
Size int64 // The size of the stream in bytes
@@ -49,8 +53,8 @@ type BackupHeader struct {
Offset int64 // The offset of the stream in the file (for BackupSparseBlock only).
}
-type win32StreamId struct {
- StreamId uint32
+type win32StreamID struct {
+ StreamID uint32
Attributes uint32
Size uint64
NameSize uint32
@@ -71,7 +75,7 @@ func NewBackupStreamReader(r io.Reader) *BackupStreamReader {
// Next returns the next backup stream and prepares for calls to Read(). 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 r.bytesLeft > 0 { //nolint:nestif // todo: flatten this
if s, ok := r.r.(io.Seeker); ok {
// Make sure Seek on io.SeekCurrent sometimes succeeds
// before trying the actual seek.
@@ -82,16 +86,16 @@ func (r *BackupStreamReader) Next() (*BackupHeader, error) {
r.bytesLeft = 0
}
}
- if _, err := io.Copy(ioutil.Discard, r); err != nil {
+ if _, err := io.Copy(io.Discard, r); err != nil {
return nil, err
}
}
- var wsi win32StreamId
+ var wsi win32StreamID
if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil {
return nil, err
}
hdr := &BackupHeader{
- Id: wsi.StreamId,
+ Id: wsi.StreamID,
Attributes: wsi.Attributes,
Size: int64(wsi.Size),
}
@@ -100,9 +104,9 @@ func (r *BackupStreamReader) Next() (*BackupHeader, error) {
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
return nil, err
}
- hdr.Name = syscall.UTF16ToString(name)
+ hdr.Name = windows.UTF16ToString(name)
}
- if wsi.StreamId == BackupSparseBlock {
+ if wsi.StreamID == BackupSparseBlock {
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
return nil, err
}
@@ -147,8 +151,8 @@ func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error {
return fmt.Errorf("missing %d bytes", w.bytesLeft)
}
name := utf16.Encode([]rune(hdr.Name))
- wsi := win32StreamId{
- StreamId: hdr.Id,
+ wsi := win32StreamID{
+ StreamID: hdr.Id,
Attributes: hdr.Attributes,
Size: uint64(hdr.Size),
NameSize: uint32(len(name) * 2),
@@ -201,9 +205,9 @@ func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader {
// 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)
+ err := backupRead(windows.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
if err != nil {
- return 0, &os.PathError{"BackupRead", r.f.Name(), err}
+ return 0, &os.PathError{Op: "BackupRead", Path: r.f.Name(), Err: err}
}
runtime.KeepAlive(r.f)
if bytesRead == 0 {
@@ -216,7 +220,7 @@ func (r *BackupFileReader) Read(b []byte) (int, error) {
// the underlying file.
func (r *BackupFileReader) Close() error {
if r.ctx != 0 {
- backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
+ _ = backupRead(windows.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
runtime.KeepAlive(r.f)
r.ctx = 0
}
@@ -240,9 +244,9 @@ func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
// 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)
+ err := backupWrite(windows.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
if err != nil {
- return 0, &os.PathError{"BackupWrite", w.f.Name(), err}
+ return 0, &os.PathError{Op: "BackupWrite", Path: w.f.Name(), Err: err}
}
runtime.KeepAlive(w.f)
if int(bytesWritten) != len(b) {
@@ -255,7 +259,7 @@ func (w *BackupFileWriter) Write(b []byte) (int, error) {
// 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)
+ _ = backupWrite(windows.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
runtime.KeepAlive(w.f)
w.ctx = 0
}
@@ -267,11 +271,14 @@ func (w *BackupFileWriter) Close() error {
//
// If the file opened was a directory, it cannot be used with Readdir().
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
- winPath, err := syscall.UTF16FromString(path)
- if err != nil {
- return nil, err
- }
- h, err := syscall.CreateFile(&winPath[0], access, share, nil, createmode, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0)
+ h, err := fs.CreateFile(path,
+ fs.AccessMask(access),
+ fs.FileShareMode(share),
+ nil,
+ fs.FileCreationDisposition(createmode),
+ fs.FILE_FLAG_BACKUP_SEMANTICS|fs.FILE_FLAG_OPEN_REPARSE_POINT,
+ 0,
+ )
if err != nil {
err = &os.PathError{Op: "open", Path: path, Err: err}
return nil, err
diff --git a/vendor/github.com/Microsoft/go-winio/doc.go b/vendor/github.com/Microsoft/go-winio/doc.go
new file mode 100644
index 00000000..1f5bfe2d
--- /dev/null
+++ b/vendor/github.com/Microsoft/go-winio/doc.go
@@ -0,0 +1,22 @@
+// This package provides utilities for efficiently performing Win32 IO operations in Go.
+// Currently, this package is provides support for genreal IO and management of
+// - named pipes
+// - files
+// - [Hyper-V sockets]
+//
+// This code is similar to Go's [net] package, and uses IO completion ports to avoid
+// blocking IO on system threads, allowing Go to reuse the thread to schedule other goroutines.
+//
+// This limits support to Windows Vista and newer operating systems.
+//
+// Additionally, this package provides support for:
+// - creating and managing GUIDs
+// - writing to [ETW]
+// - opening and manageing VHDs
+// - parsing [Windows Image files]
+// - auto-generating Win32 API code
+//
+// [Hyper-V sockets]: https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service
+// [ETW]: https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/event-tracing-for-windows--etw-
+// [Windows Image files]: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/work-with-windows-images
+package winio
diff --git a/vendor/github.com/Microsoft/go-winio/ea.go b/vendor/github.com/Microsoft/go-winio/ea.go
index 4051c1b3..e104dbdf 100644
--- a/vendor/github.com/Microsoft/go-winio/ea.go
+++ b/vendor/github.com/Microsoft/go-winio/ea.go
@@ -33,7 +33,7 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
if err != nil {
err = errInvalidEaBuffer
- return
+ return ea, nb, err
}
nameOffset := fileFullEaInformationSize
@@ -43,7 +43,7 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
nextOffset := int(info.NextEntryOffset)
if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) {
err = errInvalidEaBuffer
- return
+ return ea, nb, err
}
ea.Name = string(b[nameOffset : nameOffset+nameLen])
@@ -52,7 +52,7 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
if info.NextEntryOffset != 0 {
nb = b[info.NextEntryOffset:]
}
- return
+ return ea, nb, err
}
// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
@@ -67,7 +67,7 @@ func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
eas = append(eas, ea)
b = nb
}
- return
+ return eas, err
}
func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
diff --git a/vendor/github.com/Microsoft/go-winio/file.go b/vendor/github.com/Microsoft/go-winio/file.go
index 0385e410..fe82a180 100644
--- a/vendor/github.com/Microsoft/go-winio/file.go
+++ b/vendor/github.com/Microsoft/go-winio/file.go
@@ -1,3 +1,4 @@
+//go:build windows
// +build windows
package winio
@@ -10,31 +11,15 @@ import (
"sync/atomic"
"syscall"
"time"
+
+ "golang.org/x/sys/windows"
)
-//sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx
-//sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort
-//sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
-//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
-//sys wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
-
-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) }
-func (b *atomicBool) swap(new bool) bool {
- var newInt int32
- if new {
- newInt = 1
- }
- return atomic.SwapInt32((*int32)(b), newInt) == 1
-}
-
-const (
- cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1
- cFILE_SKIP_SET_EVENT_ON_HANDLE = 2
-)
+//sys cancelIoEx(file windows.Handle, o *windows.Overlapped) (err error) = CancelIoEx
+//sys createIoCompletionPort(file windows.Handle, port windows.Handle, key uintptr, threadCount uint32) (newport windows.Handle, err error) = CreateIoCompletionPort
+//sys getQueuedCompletionStatus(port windows.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
+//sys setFileCompletionNotificationModes(h windows.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
+//sys wsaGetOverlappedResult(h windows.Handle, o *windows.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
var (
ErrFileClosed = errors.New("file has already been closed")
@@ -43,29 +28,29 @@ var (
type timeoutError struct{}
-func (e *timeoutError) Error() string { return "i/o timeout" }
-func (e *timeoutError) Timeout() bool { return true }
-func (e *timeoutError) Temporary() bool { return true }
+func (*timeoutError) Error() string { return "i/o timeout" }
+func (*timeoutError) Timeout() bool { return true }
+func (*timeoutError) Temporary() bool { return true }
type timeoutChan chan struct{}
var ioInitOnce sync.Once
-var ioCompletionPort syscall.Handle
+var ioCompletionPort windows.Handle
-// ioResult contains the result of an asynchronous IO operation
+// ioResult contains the result of an asynchronous IO operation.
type ioResult struct {
bytes uint32
err error
}
-// ioOperation represents an outstanding asynchronous Win32 IO
+// ioOperation represents an outstanding asynchronous Win32 IO.
type ioOperation struct {
- o syscall.Overlapped
+ o windows.Overlapped
ch chan ioResult
}
-func initIo() {
- h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff)
+func initIO() {
+ h, err := createIoCompletionPort(windows.InvalidHandle, 0, 0, 0xffffffff)
if err != nil {
panic(err)
}
@@ -76,10 +61,10 @@ func initIo() {
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
// It takes ownership of this handle and will close it if it is garbage collected.
type win32File struct {
- handle syscall.Handle
+ handle windows.Handle
wg sync.WaitGroup
wgLock sync.RWMutex
- closing atomicBool
+ closing atomic.Bool
socket bool
readDeadline deadlineHandler
writeDeadline deadlineHandler
@@ -90,18 +75,18 @@ type deadlineHandler struct {
channel timeoutChan
channelLock sync.RWMutex
timer *time.Timer
- timedout atomicBool
+ timedout atomic.Bool
}
-// makeWin32File makes a new win32File from an existing file handle
-func makeWin32File(h syscall.Handle) (*win32File, error) {
+// makeWin32File makes a new win32File from an existing file handle.
+func makeWin32File(h windows.Handle) (*win32File, error) {
f := &win32File{handle: h}
- ioInitOnce.Do(initIo)
+ ioInitOnce.Do(initIO)
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
if err != nil {
return nil, err
}
- err = setFileCompletionNotificationModes(h, cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS|cFILE_SKIP_SET_EVENT_ON_HANDLE)
+ err = setFileCompletionNotificationModes(h, windows.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS|windows.FILE_SKIP_SET_EVENT_ON_HANDLE)
if err != nil {
return nil, err
}
@@ -110,7 +95,12 @@ func makeWin32File(h syscall.Handle) (*win32File, error) {
return f, nil
}
+// Deprecated: use NewOpenFile instead.
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
+ return NewOpenFile(windows.Handle(h))
+}
+
+func NewOpenFile(h windows.Handle) (io.ReadWriteCloser, error) {
// If we return the result of makeWin32File directly, it can result in an
// interface-wrapped nil, rather than a nil interface value.
f, err := makeWin32File(h)
@@ -120,17 +110,17 @@ func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
return f, nil
}
-// closeHandle closes the resources associated with a Win32 handle
+// closeHandle closes the resources associated with a Win32 handle.
func (f *win32File) closeHandle() {
f.wgLock.Lock()
// Atomically set that we are closing, releasing the resources only once.
- if !f.closing.swap(true) {
+ if !f.closing.Swap(true) {
f.wgLock.Unlock()
// cancel all IO and wait for it to complete
- cancelIoEx(f.handle, nil)
+ _ = cancelIoEx(f.handle, nil)
f.wg.Wait()
// at this point, no new IO can start
- syscall.Close(f.handle)
+ windows.Close(f.handle)
f.handle = 0
} else {
f.wgLock.Unlock()
@@ -143,11 +133,16 @@ func (f *win32File) Close() error {
return nil
}
-// prepareIo prepares for a new IO operation.
+// IsClosed checks if the file has been closed.
+func (f *win32File) IsClosed() bool {
+ return f.closing.Load()
+}
+
+// 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) {
+func (f *win32File) prepareIO() (*ioOperation, error) {
f.wgLock.RLock()
- if f.closing.isSet() {
+ if f.closing.Load() {
f.wgLock.RUnlock()
return nil, ErrFileClosed
}
@@ -158,13 +153,13 @@ func (f *win32File) prepareIo() (*ioOperation, error) {
return c, nil
}
-// ioCompletionProcessor processes completed async IOs forever
-func ioCompletionProcessor(h syscall.Handle) {
+// ioCompletionProcessor processes completed async IOs forever.
+func ioCompletionProcessor(h windows.Handle) {
for {
var bytes uint32
var key uintptr
var op *ioOperation
- err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE)
+ err := getQueuedCompletionStatus(h, &bytes, &key, &op, windows.INFINITE)
if op == nil {
panic(err)
}
@@ -172,15 +167,17 @@ func ioCompletionProcessor(h syscall.Handle) {
}
}
-// asyncIo processes the return value from ReadFile or WriteFile, blocking until
+// todo: helsaawy - create an asyncIO version that takes a context
+
+// asyncIO processes the return value from ReadFile or WriteFile, blocking until
// the operation has actually completed.
-func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) {
- if err != syscall.ERROR_IO_PENDING {
+func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) {
+ if err != windows.ERROR_IO_PENDING { //nolint:errorlint // err is Errno
return int(bytes), err
}
- if f.closing.isSet() {
- cancelIoEx(f.handle, &c.o)
+ if f.closing.Load() {
+ _ = cancelIoEx(f.handle, &c.o)
}
var timeout timeoutChan
@@ -194,8 +191,8 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er
select {
case r = <-c.ch:
err = r.err
- if err == syscall.ERROR_OPERATION_ABORTED {
- if f.closing.isSet() {
+ if err == windows.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
+ if f.closing.Load() {
err = ErrFileClosed
}
} else if err != nil && f.socket {
@@ -204,10 +201,10 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er
err = wsaGetOverlappedResult(f.handle, &c.o, &bytes, false, &flags)
}
case <-timeout:
- cancelIoEx(f.handle, &c.o)
+ _ = cancelIoEx(f.handle, &c.o)
r = <-c.ch
err = r.err
- if err == syscall.ERROR_OPERATION_ABORTED {
+ if err == windows.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
err = ErrTimeout
}
}
@@ -215,52 +212,52 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er
// runtime.KeepAlive is needed, as c is passed via native
// code to ioCompletionProcessor, c must remain alive
// until the channel read is complete.
+ // todo: (de)allocate *ioOperation via win32 heap functions, instead of needing to KeepAlive?
runtime.KeepAlive(c)
return int(r.bytes), err
}
// Read reads from a file handle.
func (f *win32File) Read(b []byte) (int, error) {
- c, err := f.prepareIo()
+ c, err := f.prepareIO()
if err != nil {
return 0, err
}
defer f.wg.Done()
- if f.readDeadline.timedout.isSet() {
+ if f.readDeadline.timedout.Load() {
return 0, ErrTimeout
}
var bytes uint32
- err = syscall.ReadFile(f.handle, b, &bytes, &c.o)
- n, err := f.asyncIo(c, &f.readDeadline, bytes, err)
+ err = windows.ReadFile(f.handle, b, &bytes, &c.o)
+ n, err := f.asyncIO(c, &f.readDeadline, bytes, err)
runtime.KeepAlive(b)
// Handle EOF conditions.
if err == nil && n == 0 && len(b) != 0 {
return 0, io.EOF
- } else if err == syscall.ERROR_BROKEN_PIPE {
+ } else if err == windows.ERROR_BROKEN_PIPE { //nolint:errorlint // err is Errno
return 0, io.EOF
- } else {
- return n, err
}
+ return n, err
}
// Write writes to a file handle.
func (f *win32File) Write(b []byte) (int, error) {
- c, err := f.prepareIo()
+ c, err := f.prepareIO()
if err != nil {
return 0, err
}
defer f.wg.Done()
- if f.writeDeadline.timedout.isSet() {
+ if f.writeDeadline.timedout.Load() {
return 0, ErrTimeout
}
var bytes uint32
- err = syscall.WriteFile(f.handle, b, &bytes, &c.o)
- n, err := f.asyncIo(c, &f.writeDeadline, bytes, err)
+ err = windows.WriteFile(f.handle, b, &bytes, &c.o)
+ n, err := f.asyncIO(c, &f.writeDeadline, bytes, err)
runtime.KeepAlive(b)
return n, err
}
@@ -274,7 +271,7 @@ func (f *win32File) SetWriteDeadline(deadline time.Time) error {
}
func (f *win32File) Flush() error {
- return syscall.FlushFileBuffers(f.handle)
+ return windows.FlushFileBuffers(f.handle)
}
func (f *win32File) Fd() uintptr {
@@ -291,7 +288,7 @@ func (d *deadlineHandler) set(deadline time.Time) error {
}
d.timer = nil
}
- d.timedout.setFalse()
+ d.timedout.Store(false)
select {
case <-d.channel:
@@ -306,7 +303,7 @@ func (d *deadlineHandler) set(deadline time.Time) error {
}
timeoutIO := func() {
- d.timedout.setTrue()
+ d.timedout.Store(true)
close(d.channel)
}
diff --git a/vendor/github.com/Microsoft/go-winio/fileinfo.go b/vendor/github.com/Microsoft/go-winio/fileinfo.go
index ada2fbab..c860eb99 100644
--- a/vendor/github.com/Microsoft/go-winio/fileinfo.go
+++ b/vendor/github.com/Microsoft/go-winio/fileinfo.go
@@ -1,3 +1,4 @@
+//go:build windows
// +build windows
package winio
@@ -5,44 +6,83 @@ package winio
import (
"os"
"runtime"
- "syscall"
"unsafe"
-)
-//sys getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = GetFileInformationByHandleEx
-//sys setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = SetFileInformationByHandle
-
-const (
- fileBasicInfo = 0
- fileIDInfo = 0x12
+ "golang.org/x/sys/windows"
)
// FileBasicInfo contains file access time and file attributes information.
type FileBasicInfo struct {
- CreationTime, LastAccessTime, LastWriteTime, ChangeTime syscall.Filetime
+ CreationTime, LastAccessTime, LastWriteTime, ChangeTime windows.Filetime
FileAttributes uint32
- pad uint32 // padding
+ _ uint32 // padding
+}
+
+// alignedFileBasicInfo is a FileBasicInfo, but aligned to uint64 by containing
+// uint64 rather than windows.Filetime. Filetime contains two uint32s. uint64
+// alignment is necessary to pass this as FILE_BASIC_INFO.
+type alignedFileBasicInfo struct {
+ CreationTime, LastAccessTime, LastWriteTime, ChangeTime uint64
+ FileAttributes uint32
+ _ uint32 // padding
}
// GetFileBasicInfo retrieves times and attributes for a file.
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
- bi := &FileBasicInfo{}
- if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
+ bi := &alignedFileBasicInfo{}
+ if err := windows.GetFileInformationByHandleEx(
+ windows.Handle(f.Fd()),
+ windows.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
+ // Reinterpret the alignedFileBasicInfo as a FileBasicInfo so it matches the
+ // public API of this module. The data may be unnecessarily aligned.
+ return (*FileBasicInfo)(unsafe.Pointer(bi)), nil
}
// SetFileBasicInfo sets times and attributes for a file.
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
- if err := setFileInformationByHandle(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
+ // Create an alignedFileBasicInfo based on a FileBasicInfo. The copy is
+ // suitable to pass to GetFileInformationByHandleEx.
+ biAligned := *(*alignedFileBasicInfo)(unsafe.Pointer(bi))
+ if err := windows.SetFileInformationByHandle(
+ windows.Handle(f.Fd()),
+ windows.FileBasicInfo,
+ (*byte)(unsafe.Pointer(&biAligned)),
+ uint32(unsafe.Sizeof(biAligned)),
+ ); err != nil {
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
}
runtime.KeepAlive(f)
return nil
}
+// FileStandardInfo contains extended information for the file.
+// FILE_STANDARD_INFO in WinBase.h
+// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_standard_info
+type FileStandardInfo struct {
+ AllocationSize, EndOfFile int64
+ NumberOfLinks uint32
+ DeletePending, Directory bool
+}
+
+// GetFileStandardInfo retrieves ended information for the file.
+func GetFileStandardInfo(f *os.File) (*FileStandardInfo, error) {
+ si := &FileStandardInfo{}
+ if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()),
+ windows.FileStandardInfo,
+ (*byte)(unsafe.Pointer(si)),
+ uint32(unsafe.Sizeof(*si))); err != nil {
+ return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
+ }
+ runtime.KeepAlive(f)
+ return si, nil
+}
+
// FileIDInfo contains the volume serial number and file ID for a file. This pair should be
// unique on a system.
type FileIDInfo struct {
@@ -53,7 +93,12 @@ type FileIDInfo struct {
// GetFileID retrieves the unique (volume, file ID) pair for a file.
func GetFileID(f *os.File) (*FileIDInfo, error) {
fileID := &FileIDInfo{}
- if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileIDInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil {
+ if err := windows.GetFileInformationByHandleEx(
+ windows.Handle(f.Fd()),
+ windows.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)
diff --git a/vendor/github.com/Microsoft/go-winio/hvsock.go b/vendor/github.com/Microsoft/go-winio/hvsock.go
index dbfe790e..c4fdd9d4 100644
--- a/vendor/github.com/Microsoft/go-winio/hvsock.go
+++ b/vendor/github.com/Microsoft/go-winio/hvsock.go
@@ -1,24 +1,99 @@
+//go:build windows
+// +build windows
+
package winio
import (
+ "context"
+ "errors"
"fmt"
"io"
"net"
"os"
- "syscall"
"time"
"unsafe"
+ "golang.org/x/sys/windows"
+
+ "github.com/Microsoft/go-winio/internal/socket"
"github.com/Microsoft/go-winio/pkg/guid"
)
-//sys bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind
+const afHVSock = 34 // AF_HYPERV
-const (
- afHvSock = 34 // AF_HYPERV
+// Well known Service and VM IDs
+// https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service#vmid-wildcards
- socketError = ^uintptr(0)
-)
+// HvsockGUIDWildcard is the wildcard VmId for accepting connections from all partitions.
+func HvsockGUIDWildcard() guid.GUID { // 00000000-0000-0000-0000-000000000000
+ return guid.GUID{}
+}
+
+// HvsockGUIDBroadcast is the wildcard VmId for broadcasting sends to all partitions.
+func HvsockGUIDBroadcast() guid.GUID { // ffffffff-ffff-ffff-ffff-ffffffffffff
+ return guid.GUID{
+ Data1: 0xffffffff,
+ Data2: 0xffff,
+ Data3: 0xffff,
+ Data4: [8]uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ }
+}
+
+// HvsockGUIDLoopback is the Loopback VmId for accepting connections to the same partition as the connector.
+func HvsockGUIDLoopback() guid.GUID { // e0e16197-dd56-4a10-9195-5ee7a155a838
+ return guid.GUID{
+ Data1: 0xe0e16197,
+ Data2: 0xdd56,
+ Data3: 0x4a10,
+ Data4: [8]uint8{0x91, 0x95, 0x5e, 0xe7, 0xa1, 0x55, 0xa8, 0x38},
+ }
+}
+
+// HvsockGUIDSiloHost is the address of a silo's host partition:
+// - The silo host of a hosted silo is the utility VM.
+// - The silo host of a silo on a physical host is the physical host.
+func HvsockGUIDSiloHost() guid.GUID { // 36bd0c5c-7276-4223-88ba-7d03b654c568
+ return guid.GUID{
+ Data1: 0x36bd0c5c,
+ Data2: 0x7276,
+ Data3: 0x4223,
+ Data4: [8]byte{0x88, 0xba, 0x7d, 0x03, 0xb6, 0x54, 0xc5, 0x68},
+ }
+}
+
+// HvsockGUIDChildren is the wildcard VmId for accepting connections from the connector's child partitions.
+func HvsockGUIDChildren() guid.GUID { // 90db8b89-0d35-4f79-8ce9-49ea0ac8b7cd
+ return guid.GUID{
+ Data1: 0x90db8b89,
+ Data2: 0xd35,
+ Data3: 0x4f79,
+ Data4: [8]uint8{0x8c, 0xe9, 0x49, 0xea, 0xa, 0xc8, 0xb7, 0xcd},
+ }
+}
+
+// HvsockGUIDParent is the wildcard VmId for accepting connections from the connector's parent partition.
+// Listening on this VmId accepts connection from:
+// - Inside silos: silo host partition.
+// - Inside hosted silo: host of the VM.
+// - Inside VM: VM host.
+// - Physical host: Not supported.
+func HvsockGUIDParent() guid.GUID { // a42e7cda-d03f-480c-9cc2-a4de20abb878
+ return guid.GUID{
+ Data1: 0xa42e7cda,
+ Data2: 0xd03f,
+ Data3: 0x480c,
+ Data4: [8]uint8{0x9c, 0xc2, 0xa4, 0xde, 0x20, 0xab, 0xb8, 0x78},
+ }
+}
+
+// hvsockVsockServiceTemplate is the Service GUID used for the VSOCK protocol.
+func hvsockVsockServiceTemplate() guid.GUID { // 00000000-facb-11e6-bd58-64006a7986d3
+ return guid.GUID{
+ Data2: 0xfacb,
+ Data3: 0x11e6,
+ Data4: [8]uint8{0xbd, 0x58, 0x64, 0x00, 0x6a, 0x79, 0x86, 0xd3},
+ }
+}
// An HvsockAddr is an address for a AF_HYPERV socket.
type HvsockAddr struct {
@@ -33,8 +108,10 @@ type rawHvsockAddr struct {
ServiceID guid.GUID
}
+var _ socket.RawSockaddr = &rawHvsockAddr{}
+
// Network returns the address's network name, "hvsock".
-func (addr *HvsockAddr) Network() string {
+func (*HvsockAddr) Network() string {
return "hvsock"
}
@@ -44,14 +121,14 @@ func (addr *HvsockAddr) String() string {
// VsockServiceID returns an hvsock service ID corresponding to the specified AF_VSOCK port.
func VsockServiceID(port uint32) guid.GUID {
- g, _ := guid.FromString("00000000-facb-11e6-bd58-64006a7986d3")
+ g := hvsockVsockServiceTemplate() // make a copy
g.Data1 = port
return g
}
func (addr *HvsockAddr) raw() rawHvsockAddr {
return rawHvsockAddr{
- Family: afHvSock,
+ Family: afHVSock,
VMID: addr.VMID,
ServiceID: addr.ServiceID,
}
@@ -62,26 +139,54 @@ func (addr *HvsockAddr) fromRaw(raw *rawHvsockAddr) {
addr.ServiceID = raw.ServiceID
}
+// Sockaddr returns a pointer to and the size of this struct.
+//
+// Implements the [socket.RawSockaddr] interface, and allows use in
+// [socket.Bind] and [socket.ConnectEx].
+func (r *rawHvsockAddr) Sockaddr() (unsafe.Pointer, int32, error) {
+ return unsafe.Pointer(r), int32(unsafe.Sizeof(rawHvsockAddr{})), nil
+}
+
+// Sockaddr interface allows use with `sockets.Bind()` and `.ConnectEx()`.
+func (r *rawHvsockAddr) FromBytes(b []byte) error {
+ n := int(unsafe.Sizeof(rawHvsockAddr{}))
+
+ if len(b) < n {
+ return fmt.Errorf("got %d, want %d: %w", len(b), n, socket.ErrBufferSize)
+ }
+
+ copy(unsafe.Slice((*byte)(unsafe.Pointer(r)), n), b[:n])
+ if r.Family != afHVSock {
+ return fmt.Errorf("got %d, want %d: %w", r.Family, afHVSock, socket.ErrAddrFamily)
+ }
+
+ return nil
+}
+
// HvsockListener is a socket listener for the AF_HYPERV address family.
type HvsockListener struct {
sock *win32File
addr HvsockAddr
}
+var _ net.Listener = &HvsockListener{}
+
// HvsockConn is a connected socket of the AF_HYPERV address family.
type HvsockConn struct {
sock *win32File
local, remote HvsockAddr
}
-func newHvSocket() (*win32File, error) {
- fd, err := syscall.Socket(afHvSock, syscall.SOCK_STREAM, 1)
+var _ net.Conn = &HvsockConn{}
+
+func newHVSocket() (*win32File, error) {
+ fd, err := windows.Socket(afHVSock, windows.SOCK_STREAM, 1)
if err != nil {
return nil, os.NewSyscallError("socket", err)
}
f, err := makeWin32File(fd)
if err != nil {
- syscall.Close(fd)
+ windows.Close(fd)
return nil, err
}
f.socket = true
@@ -91,16 +196,24 @@ func newHvSocket() (*win32File, error) {
// ListenHvsock listens for connections on the specified hvsock address.
func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) {
l := &HvsockListener{addr: *addr}
- sock, err := newHvSocket()
+
+ var sock *win32File
+ sock, err = newHVSocket()
if err != nil {
return nil, l.opErr("listen", err)
}
+ defer func() {
+ if err != nil {
+ _ = sock.Close()
+ }
+ }()
+
sa := addr.raw()
- err = bind(sock.handle, unsafe.Pointer(&sa), int32(unsafe.Sizeof(sa)))
+ err = socket.Bind(sock.handle, &sa)
if err != nil {
return nil, l.opErr("listen", os.NewSyscallError("socket", err))
}
- err = syscall.Listen(sock.handle, 16)
+ err = windows.Listen(sock.handle, 16)
if err != nil {
return nil, l.opErr("listen", os.NewSyscallError("listen", err))
}
@@ -118,7 +231,7 @@ func (l *HvsockListener) Addr() net.Addr {
// Accept waits for the next connection and returns it.
func (l *HvsockListener) Accept() (_ net.Conn, err error) {
- sock, err := newHvSocket()
+ sock, err := newHVSocket()
if err != nil {
return nil, l.opErr("accept", err)
}
@@ -127,27 +240,42 @@ func (l *HvsockListener) Accept() (_ net.Conn, err error) {
sock.Close()
}
}()
- c, err := l.sock.prepareIo()
+ c, err := l.sock.prepareIO()
if err != nil {
return nil, l.opErr("accept", err)
}
defer l.sock.wg.Done()
// AcceptEx, per documentation, requires an extra 16 bytes per address.
+ //
+ // https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-acceptex
const addrlen = uint32(16 + unsafe.Sizeof(rawHvsockAddr{}))
var addrbuf [addrlen * 2]byte
var bytes uint32
- err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0, addrlen, addrlen, &bytes, &c.o)
- _, err = l.sock.asyncIo(c, nil, bytes, err)
- if err != nil {
+ err = windows.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0 /* rxdatalen */, addrlen, addrlen, &bytes, &c.o)
+ if _, err = l.sock.asyncIO(c, nil, bytes, err); err != nil {
return nil, l.opErr("accept", os.NewSyscallError("acceptex", err))
}
+
conn := &HvsockConn{
sock: sock,
}
+ // The local address returned in the AcceptEx buffer is the same as the Listener socket's
+ // address. However, the service GUID reported by GetSockName is different from the Listeners
+ // socket, and is sometimes the same as the local address of the socket that dialed the
+ // address, with the service GUID.Data1 incremented, but othertimes is different.
+ // todo: does the local address matter? is the listener's address or the actual address appropriate?
conn.local.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[0])))
conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen])))
+
+ // initialize the accepted socket and update its properties with those of the listening socket
+ if err = windows.Setsockopt(sock.handle,
+ windows.SOL_SOCKET, windows.SO_UPDATE_ACCEPT_CONTEXT,
+ (*byte)(unsafe.Pointer(&l.sock.handle)), int32(unsafe.Sizeof(l.sock.handle))); err != nil {
+ return nil, conn.opErr("accept", os.NewSyscallError("setsockopt", err))
+ }
+
sock = nil
return conn, nil
}
@@ -157,54 +285,183 @@ func (l *HvsockListener) Close() error {
return l.sock.Close()
}
-/* Need to finish ConnectEx handling
-func DialHvsock(ctx context.Context, addr *HvsockAddr) (*HvsockConn, error) {
- sock, err := newHvSocket()
+// HvsockDialer configures and dials a Hyper-V Socket (ie, [HvsockConn]).
+type HvsockDialer struct {
+ // Deadline is the time the Dial operation must connect before erroring.
+ Deadline time.Time
+
+ // Retries is the number of additional connects to try if the connection times out, is refused,
+ // or the host is unreachable
+ Retries uint
+
+ // RetryWait is the time to wait after a connection error to retry
+ RetryWait time.Duration
+
+ rt *time.Timer // redial wait timer
+}
+
+// Dial the Hyper-V socket at addr.
+//
+// See [HvsockDialer.Dial] for more information.
+func Dial(ctx context.Context, addr *HvsockAddr) (conn *HvsockConn, err error) {
+ return (&HvsockDialer{}).Dial(ctx, addr)
+}
+
+// Dial attempts to connect to the Hyper-V socket at addr, and returns a connection if successful.
+// Will attempt (HvsockDialer).Retries if dialing fails, waiting (HvsockDialer).RetryWait between
+// retries.
+//
+// Dialing can be cancelled either by providing (HvsockDialer).Deadline, or cancelling ctx.
+func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *HvsockConn, err error) {
+ op := "dial"
+ // create the conn early to use opErr()
+ conn = &HvsockConn{
+ remote: *addr,
+ }
+
+ if !d.Deadline.IsZero() {
+ var cancel context.CancelFunc
+ ctx, cancel = context.WithDeadline(ctx, d.Deadline)
+ defer cancel()
+ }
+
+ // preemptive timeout/cancellation check
+ if err = ctx.Err(); err != nil {
+ return nil, conn.opErr(op, err)
+ }
+
+ sock, err := newHVSocket()
if err != nil {
- return nil, err
+ return nil, conn.opErr(op, err)
}
defer func() {
if sock != nil {
sock.Close()
}
}()
- c, err := sock.prepareIo()
+
+ sa := addr.raw()
+ err = socket.Bind(sock.handle, &sa)
if err != nil {
- return nil, err
+ return nil, conn.opErr(op, os.NewSyscallError("bind", err))
+ }
+
+ c, err := sock.prepareIO()
+ if err != nil {
+ return nil, conn.opErr(op, err)
}
defer sock.wg.Done()
var bytes uint32
- err = windows.ConnectEx(windows.Handle(sock.handle), sa, nil, 0, &bytes, &c.o)
- _, err = sock.asyncIo(ctx, c, nil, bytes, err)
+ for i := uint(0); i <= d.Retries; i++ {
+ err = socket.ConnectEx(
+ sock.handle,
+ &sa,
+ nil, // sendBuf
+ 0, // sendDataLen
+ &bytes,
+ (*windows.Overlapped)(unsafe.Pointer(&c.o)))
+ _, err = sock.asyncIO(c, nil, bytes, err)
+ if i < d.Retries && canRedial(err) {
+ if err = d.redialWait(ctx); err == nil {
+ continue
+ }
+ }
+ break
+ }
if err != nil {
- return nil, err
+ return nil, conn.opErr(op, os.NewSyscallError("connectex", err))
}
- conn := &HvsockConn{
- sock: sock,
- remote: *addr,
+
+ // update the connection properties, so shutdown can be used
+ if err = windows.Setsockopt(
+ sock.handle,
+ windows.SOL_SOCKET,
+ windows.SO_UPDATE_CONNECT_CONTEXT,
+ nil, // optvalue
+ 0, // optlen
+ ); err != nil {
+ return nil, conn.opErr(op, os.NewSyscallError("setsockopt", err))
}
+
+ // get the local name
+ var sal rawHvsockAddr
+ err = socket.GetSockName(sock.handle, &sal)
+ if err != nil {
+ return nil, conn.opErr(op, os.NewSyscallError("getsockname", err))
+ }
+ conn.local.fromRaw(&sal)
+
+ // one last check for timeout, since asyncIO doesn't check the context
+ if err = ctx.Err(); err != nil {
+ return nil, conn.opErr(op, err)
+ }
+
+ conn.sock = sock
sock = nil
+
return conn, nil
}
-*/
+
+// redialWait waits before attempting to redial, resetting the timer as appropriate.
+func (d *HvsockDialer) redialWait(ctx context.Context) (err error) {
+ if d.RetryWait == 0 {
+ return nil
+ }
+
+ if d.rt == nil {
+ d.rt = time.NewTimer(d.RetryWait)
+ } else {
+ // should already be stopped and drained
+ d.rt.Reset(d.RetryWait)
+ }
+
+ select {
+ case <-ctx.Done():
+ case <-d.rt.C:
+ return nil
+ }
+
+ // stop and drain the timer
+ if !d.rt.Stop() {
+ <-d.rt.C
+ }
+ return ctx.Err()
+}
+
+// assumes error is a plain, unwrapped windows.Errno provided by direct syscall.
+func canRedial(err error) bool {
+ //nolint:errorlint // guaranteed to be an Errno
+ switch err {
+ case windows.WSAECONNREFUSED, windows.WSAENETUNREACH, windows.WSAETIMEDOUT,
+ windows.ERROR_CONNECTION_REFUSED, windows.ERROR_CONNECTION_UNAVAIL:
+ return true
+ default:
+ return false
+ }
+}
func (conn *HvsockConn) opErr(op string, err error) error {
+ // translate from "file closed" to "socket closed"
+ if errors.Is(err, ErrFileClosed) {
+ err = socket.ErrSocketClosed
+ }
return &net.OpError{Op: op, Net: "hvsock", Source: &conn.local, Addr: &conn.remote, Err: err}
}
func (conn *HvsockConn) Read(b []byte) (int, error) {
- c, err := conn.sock.prepareIo()
+ c, err := conn.sock.prepareIO()
if err != nil {
return 0, conn.opErr("read", err)
}
defer conn.sock.wg.Done()
- buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
+ buf := windows.WSABuf{Buf: &b[0], Len: uint32(len(b))}
var flags, bytes uint32
- err = syscall.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil)
- n, err := conn.sock.asyncIo(c, &conn.sock.readDeadline, bytes, err)
+ err = windows.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil)
+ n, err := conn.sock.asyncIO(c, &conn.sock.readDeadline, bytes, err)
if err != nil {
- if _, ok := err.(syscall.Errno); ok {
- err = os.NewSyscallError("wsarecv", err)
+ var eno windows.Errno
+ if errors.As(err, &eno) {
+ err = os.NewSyscallError("wsarecv", eno)
}
return 0, conn.opErr("read", err)
} else if n == 0 {
@@ -227,18 +484,19 @@ func (conn *HvsockConn) Write(b []byte) (int, error) {
}
func (conn *HvsockConn) write(b []byte) (int, error) {
- c, err := conn.sock.prepareIo()
+ c, err := conn.sock.prepareIO()
if err != nil {
return 0, conn.opErr("write", err)
}
defer conn.sock.wg.Done()
- buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
+ buf := windows.WSABuf{Buf: &b[0], Len: uint32(len(b))}
var bytes uint32
- err = syscall.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil)
- n, err := conn.sock.asyncIo(c, &conn.sock.writeDeadline, bytes, err)
+ err = windows.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil)
+ n, err := conn.sock.asyncIO(c, &conn.sock.writeDeadline, bytes, err)
if err != nil {
- if _, ok := err.(syscall.Errno); ok {
- err = os.NewSyscallError("wsasend", err)
+ var eno windows.Errno
+ if errors.As(err, &eno) {
+ err = os.NewSyscallError("wsasend", eno)
}
return 0, conn.opErr("write", err)
}
@@ -250,29 +508,43 @@ func (conn *HvsockConn) Close() error {
return conn.sock.Close()
}
+func (conn *HvsockConn) IsClosed() bool {
+ return conn.sock.IsClosed()
+}
+
+// shutdown disables sending or receiving on a socket.
func (conn *HvsockConn) shutdown(how int) error {
- err := syscall.Shutdown(conn.sock.handle, syscall.SHUT_RD)
+ if conn.IsClosed() {
+ return socket.ErrSocketClosed
+ }
+
+ err := windows.Shutdown(conn.sock.handle, how)
if err != nil {
+ // If the connection was closed, shutdowns fail with "not connected"
+ if errors.Is(err, windows.WSAENOTCONN) ||
+ errors.Is(err, windows.WSAESHUTDOWN) {
+ err = socket.ErrSocketClosed
+ }
return os.NewSyscallError("shutdown", err)
}
return nil
}
-// CloseRead shuts down the read end of the socket.
+// CloseRead shuts down the read end of the socket, preventing future read operations.
func (conn *HvsockConn) CloseRead() error {
- err := conn.shutdown(syscall.SHUT_RD)
+ err := conn.shutdown(windows.SHUT_RD)
if err != nil {
- return conn.opErr("close", err)
+ return conn.opErr("closeread", err)
}
return nil
}
-// CloseWrite shuts down the write end of the socket, notifying the other endpoint that
-// no more data will be written.
+// CloseWrite shuts down the write end of the socket, preventing future write operations and
+// notifying the other endpoint that no more data will be written.
func (conn *HvsockConn) CloseWrite() error {
- err := conn.shutdown(syscall.SHUT_WR)
+ err := conn.shutdown(windows.SHUT_WR)
if err != nil {
- return conn.opErr("close", err)
+ return conn.opErr("closewrite", err)
}
return nil
}
@@ -289,8 +561,13 @@ func (conn *HvsockConn) RemoteAddr() net.Addr {
// SetDeadline implements the net.Conn SetDeadline method.
func (conn *HvsockConn) SetDeadline(t time.Time) error {
- conn.SetReadDeadline(t)
- conn.SetWriteDeadline(t)
+ // todo: implement `SetDeadline` for `win32File`
+ if err := conn.SetReadDeadline(t); err != nil {
+ return fmt.Errorf("set read deadline: %w", err)
+ }
+ if err := conn.SetWriteDeadline(t); err != nil {
+ return fmt.Errorf("set write deadline: %w", err)
+ }
return nil
}
diff --git a/vendor/github.com/Microsoft/go-winio/internal/fs/doc.go b/vendor/github.com/Microsoft/go-winio/internal/fs/doc.go
new file mode 100644
index 00000000..1f653881
--- /dev/null
+++ b/vendor/github.com/Microsoft/go-winio/internal/fs/doc.go
@@ -0,0 +1,2 @@
+// This package contains Win32 filesystem functionality.
+package fs
diff --git a/vendor/github.com/Microsoft/go-winio/internal/fs/fs.go b/vendor/github.com/Microsoft/go-winio/internal/fs/fs.go
new file mode 100644
index 00000000..0cd9621d
--- /dev/null
+++ b/vendor/github.com/Microsoft/go-winio/internal/fs/fs.go
@@ -0,0 +1,262 @@
+//go:build windows
+
+package fs
+
+import (
+ "golang.org/x/sys/windows"
+
+ "github.com/Microsoft/go-winio/internal/stringbuffer"
+)
+
+//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go fs.go
+
+// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
+//sys CreateFile(name string, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateFileW
+
+const NullHandle windows.Handle = 0
+
+// AccessMask defines standard, specific, and generic rights.
+//
+// Used with CreateFile and NtCreateFile (and co.).
+//
+// Bitmask:
+// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
+// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+// +---------------+---------------+-------------------------------+
+// |G|G|G|G|Resvd|A| StandardRights| SpecificRights |
+// |R|W|E|A| |S| | |
+// +-+-------------+---------------+-------------------------------+
+//
+// GR Generic Read
+// GW Generic Write
+// GE Generic Exectue
+// GA Generic All
+// Resvd Reserved
+// AS Access Security System
+//
+// https://learn.microsoft.com/en-us/windows/win32/secauthz/access-mask
+//
+// https://learn.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights
+//
+// https://learn.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants
+type AccessMask = windows.ACCESS_MASK
+
+//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
+const (
+ // Not actually any.
+ //
+ // For CreateFile: "query certain metadata such as file, directory, or device attributes without accessing that file or device"
+ // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew#parameters
+ FILE_ANY_ACCESS AccessMask = 0
+
+ GENERIC_READ AccessMask = 0x8000_0000
+ GENERIC_WRITE AccessMask = 0x4000_0000
+ GENERIC_EXECUTE AccessMask = 0x2000_0000
+ GENERIC_ALL AccessMask = 0x1000_0000
+ ACCESS_SYSTEM_SECURITY AccessMask = 0x0100_0000
+
+ // Specific Object Access
+ // from ntioapi.h
+
+ FILE_READ_DATA AccessMask = (0x0001) // file & pipe
+ FILE_LIST_DIRECTORY AccessMask = (0x0001) // directory
+
+ FILE_WRITE_DATA AccessMask = (0x0002) // file & pipe
+ FILE_ADD_FILE AccessMask = (0x0002) // directory
+
+ FILE_APPEND_DATA AccessMask = (0x0004) // file
+ FILE_ADD_SUBDIRECTORY AccessMask = (0x0004) // directory
+ FILE_CREATE_PIPE_INSTANCE AccessMask = (0x0004) // named pipe
+
+ FILE_READ_EA AccessMask = (0x0008) // file & directory
+ FILE_READ_PROPERTIES AccessMask = FILE_READ_EA
+
+ FILE_WRITE_EA AccessMask = (0x0010) // file & directory
+ FILE_WRITE_PROPERTIES AccessMask = FILE_WRITE_EA
+
+ FILE_EXECUTE AccessMask = (0x0020) // file
+ FILE_TRAVERSE AccessMask = (0x0020) // directory
+
+ FILE_DELETE_CHILD AccessMask = (0x0040) // directory
+
+ FILE_READ_ATTRIBUTES AccessMask = (0x0080) // all
+
+ FILE_WRITE_ATTRIBUTES AccessMask = (0x0100) // all
+
+ FILE_ALL_ACCESS AccessMask = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF)
+ FILE_GENERIC_READ AccessMask = (STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE)
+ FILE_GENERIC_WRITE AccessMask = (STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE)
+ FILE_GENERIC_EXECUTE AccessMask = (STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE)
+
+ SPECIFIC_RIGHTS_ALL AccessMask = 0x0000FFFF
+
+ // Standard Access
+ // from ntseapi.h
+
+ DELETE AccessMask = 0x0001_0000
+ READ_CONTROL AccessMask = 0x0002_0000
+ WRITE_DAC AccessMask = 0x0004_0000
+ WRITE_OWNER AccessMask = 0x0008_0000
+ SYNCHRONIZE AccessMask = 0x0010_0000
+
+ STANDARD_RIGHTS_REQUIRED AccessMask = 0x000F_0000
+
+ STANDARD_RIGHTS_READ AccessMask = READ_CONTROL
+ STANDARD_RIGHTS_WRITE AccessMask = READ_CONTROL
+ STANDARD_RIGHTS_EXECUTE AccessMask = READ_CONTROL
+
+ STANDARD_RIGHTS_ALL AccessMask = 0x001F_0000
+)
+
+type FileShareMode uint32
+
+//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
+const (
+ FILE_SHARE_NONE FileShareMode = 0x00
+ FILE_SHARE_READ FileShareMode = 0x01
+ FILE_SHARE_WRITE FileShareMode = 0x02
+ FILE_SHARE_DELETE FileShareMode = 0x04
+ FILE_SHARE_VALID_FLAGS FileShareMode = 0x07
+)
+
+type FileCreationDisposition uint32
+
+//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
+const (
+ // from winbase.h
+
+ CREATE_NEW FileCreationDisposition = 0x01
+ CREATE_ALWAYS FileCreationDisposition = 0x02
+ OPEN_EXISTING FileCreationDisposition = 0x03
+ OPEN_ALWAYS FileCreationDisposition = 0x04
+ TRUNCATE_EXISTING FileCreationDisposition = 0x05
+)
+
+// Create disposition values for NtCreate*
+type NTFileCreationDisposition uint32
+
+//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
+const (
+ // From ntioapi.h
+
+ FILE_SUPERSEDE NTFileCreationDisposition = 0x00
+ FILE_OPEN NTFileCreationDisposition = 0x01
+ FILE_CREATE NTFileCreationDisposition = 0x02
+ FILE_OPEN_IF NTFileCreationDisposition = 0x03
+ FILE_OVERWRITE NTFileCreationDisposition = 0x04
+ FILE_OVERWRITE_IF NTFileCreationDisposition = 0x05
+ FILE_MAXIMUM_DISPOSITION NTFileCreationDisposition = 0x05
+)
+
+// CreateFile and co. take flags or attributes together as one parameter.
+// Define alias until we can use generics to allow both
+//
+// https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
+type FileFlagOrAttribute uint32
+
+//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
+const (
+ // from winnt.h
+
+ FILE_FLAG_WRITE_THROUGH FileFlagOrAttribute = 0x8000_0000
+ FILE_FLAG_OVERLAPPED FileFlagOrAttribute = 0x4000_0000
+ FILE_FLAG_NO_BUFFERING FileFlagOrAttribute = 0x2000_0000
+ FILE_FLAG_RANDOM_ACCESS FileFlagOrAttribute = 0x1000_0000
+ FILE_FLAG_SEQUENTIAL_SCAN FileFlagOrAttribute = 0x0800_0000
+ FILE_FLAG_DELETE_ON_CLOSE FileFlagOrAttribute = 0x0400_0000
+ FILE_FLAG_BACKUP_SEMANTICS FileFlagOrAttribute = 0x0200_0000
+ FILE_FLAG_POSIX_SEMANTICS FileFlagOrAttribute = 0x0100_0000
+ FILE_FLAG_OPEN_REPARSE_POINT FileFlagOrAttribute = 0x0020_0000
+ FILE_FLAG_OPEN_NO_RECALL FileFlagOrAttribute = 0x0010_0000
+ FILE_FLAG_FIRST_PIPE_INSTANCE FileFlagOrAttribute = 0x0008_0000
+)
+
+// NtCreate* functions take a dedicated CreateOptions parameter.
+//
+// https://learn.microsoft.com/en-us/windows/win32/api/Winternl/nf-winternl-ntcreatefile
+//
+// https://learn.microsoft.com/en-us/windows/win32/devnotes/nt-create-named-pipe-file
+type NTCreateOptions uint32
+
+//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
+const (
+ // From ntioapi.h
+
+ FILE_DIRECTORY_FILE NTCreateOptions = 0x0000_0001
+ FILE_WRITE_THROUGH NTCreateOptions = 0x0000_0002
+ FILE_SEQUENTIAL_ONLY NTCreateOptions = 0x0000_0004
+ FILE_NO_INTERMEDIATE_BUFFERING NTCreateOptions = 0x0000_0008
+
+ FILE_SYNCHRONOUS_IO_ALERT NTCreateOptions = 0x0000_0010
+ FILE_SYNCHRONOUS_IO_NONALERT NTCreateOptions = 0x0000_0020
+ FILE_NON_DIRECTORY_FILE NTCreateOptions = 0x0000_0040
+ FILE_CREATE_TREE_CONNECTION NTCreateOptions = 0x0000_0080
+
+ FILE_COMPLETE_IF_OPLOCKED NTCreateOptions = 0x0000_0100
+ FILE_NO_EA_KNOWLEDGE NTCreateOptions = 0x0000_0200
+ FILE_DISABLE_TUNNELING NTCreateOptions = 0x0000_0400
+ FILE_RANDOM_ACCESS NTCreateOptions = 0x0000_0800
+
+ FILE_DELETE_ON_CLOSE NTCreateOptions = 0x0000_1000
+ FILE_OPEN_BY_FILE_ID NTCreateOptions = 0x0000_2000
+ FILE_OPEN_FOR_BACKUP_INTENT NTCreateOptions = 0x0000_4000
+ FILE_NO_COMPRESSION NTCreateOptions = 0x0000_8000
+)
+
+type FileSQSFlag = FileFlagOrAttribute
+
+//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
+const (
+ // from winbase.h
+
+ SECURITY_ANONYMOUS FileSQSFlag = FileSQSFlag(SecurityAnonymous << 16)
+ SECURITY_IDENTIFICATION FileSQSFlag = FileSQSFlag(SecurityIdentification << 16)
+ SECURITY_IMPERSONATION FileSQSFlag = FileSQSFlag(SecurityImpersonation << 16)
+ SECURITY_DELEGATION FileSQSFlag = FileSQSFlag(SecurityDelegation << 16)
+
+ SECURITY_SQOS_PRESENT FileSQSFlag = 0x0010_0000
+ SECURITY_VALID_SQOS_FLAGS FileSQSFlag = 0x001F_0000
+)
+
+// GetFinalPathNameByHandle flags
+//
+// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew#parameters
+type GetFinalPathFlag uint32
+
+//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
+const (
+ GetFinalPathDefaultFlag GetFinalPathFlag = 0x0
+
+ FILE_NAME_NORMALIZED GetFinalPathFlag = 0x0
+ FILE_NAME_OPENED GetFinalPathFlag = 0x8
+
+ VOLUME_NAME_DOS GetFinalPathFlag = 0x0
+ VOLUME_NAME_GUID GetFinalPathFlag = 0x1
+ VOLUME_NAME_NT GetFinalPathFlag = 0x2
+ VOLUME_NAME_NONE GetFinalPathFlag = 0x4
+)
+
+// getFinalPathNameByHandle facilitates calling the Windows API GetFinalPathNameByHandle
+// with the given handle and flags. It transparently takes care of creating a buffer of the
+// correct size for the call.
+//
+// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew
+func GetFinalPathNameByHandle(h windows.Handle, flags GetFinalPathFlag) (string, error) {
+ b := stringbuffer.NewWString()
+ //TODO: can loop infinitely if Win32 keeps returning the same (or a larger) n?
+ for {
+ n, err := windows.GetFinalPathNameByHandle(h, b.Pointer(), b.Cap(), uint32(flags))
+ if err != nil {
+ return "", err
+ }
+ // If the buffer wasn't large enough, n will be the total size needed (including null terminator).
+ // Resize and try again.
+ if n > b.Cap() {
+ b.ResizeTo(n)
+ continue
+ }
+ // If the buffer is large enough, n will be the size not including the null terminator.
+ // Convert to a Go string and return.
+ return b.String(), nil
+ }
+}
diff --git a/vendor/github.com/Microsoft/go-winio/internal/fs/security.go b/vendor/github.com/Microsoft/go-winio/internal/fs/security.go
new file mode 100644
index 00000000..81760ac6
--- /dev/null
+++ b/vendor/github.com/Microsoft/go-winio/internal/fs/security.go
@@ -0,0 +1,12 @@
+package fs
+
+// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level
+type SecurityImpersonationLevel int32 // C default enums underlying type is `int`, which is Go `int32`
+
+// Impersonation levels
+const (
+ SecurityAnonymous SecurityImpersonationLevel = 0
+ SecurityIdentification SecurityImpersonationLevel = 1
+ SecurityImpersonation SecurityImpersonationLevel = 2
+ SecurityDelegation SecurityImpersonationLevel = 3
+)
diff --git a/vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go
new file mode 100644
index 00000000..a94e234c
--- /dev/null
+++ b/vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go
@@ -0,0 +1,61 @@
+//go:build windows
+
+// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT.
+
+package fs
+
+import (
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+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)
+ errERROR_EINVAL error = syscall.EINVAL
+)
+
+// errnoErr returns common boxed Errno values, to prevent
+// allocations at runtime.
+func errnoErr(e syscall.Errno) error {
+ switch e {
+ case 0:
+ return errERROR_EINVAL
+ case errnoERROR_IO_PENDING:
+ return errERROR_IO_PENDING
+ }
+ return e
+}
+
+var (
+ modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
+
+ procCreateFileW = modkernel32.NewProc("CreateFileW")
+)
+
+func CreateFile(name string, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
+ var _p0 *uint16
+ _p0, err = syscall.UTF16PtrFromString(name)
+ if err != nil {
+ return
+ }
+ return _CreateFile(_p0, access, mode, sa, createmode, attrs, templatefile)
+}
+
+func _CreateFile(name *uint16, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
+ r0, _, e1 := syscall.SyscallN(procCreateFileW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile))
+ handle = windows.Handle(r0)
+ if handle == windows.InvalidHandle {
+ err = errnoErr(e1)
+ }
+ return
+}
diff --git a/vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go b/vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go
new file mode 100644
index 00000000..7e82f9af
--- /dev/null
+++ b/vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go
@@ -0,0 +1,20 @@
+package socket
+
+import (
+ "unsafe"
+)
+
+// RawSockaddr allows structs to be used with [Bind] and [ConnectEx]. The
+// struct must meet the Win32 sockaddr requirements specified here:
+// https://docs.microsoft.com/en-us/windows/win32/winsock/sockaddr-2
+//
+// Specifically, the struct size must be least larger than an int16 (unsigned short)
+// for the address family.
+type RawSockaddr interface {
+ // Sockaddr returns a pointer to the RawSockaddr and its struct size, allowing
+ // for the RawSockaddr's data to be overwritten by syscalls (if necessary).
+ //
+ // It is the callers responsibility to validate that the values are valid; invalid
+ // pointers or size can cause a panic.
+ Sockaddr() (unsafe.Pointer, int32, error)
+}
diff --git a/vendor/github.com/Microsoft/go-winio/internal/socket/socket.go b/vendor/github.com/Microsoft/go-winio/internal/socket/socket.go
new file mode 100644
index 00000000..88580d97
--- /dev/null
+++ b/vendor/github.com/Microsoft/go-winio/internal/socket/socket.go
@@ -0,0 +1,177 @@
+//go:build windows
+
+package socket
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "sync"
+ "syscall"
+ "unsafe"
+
+ "github.com/Microsoft/go-winio/pkg/guid"
+ "golang.org/x/sys/windows"
+)
+
+//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go socket.go
+
+//sys getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) [failretval==socketError] = ws2_32.getsockname
+//sys getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) [failretval==socketError] = ws2_32.getpeername
+//sys bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind
+
+const socketError = uintptr(^uint32(0))
+
+var (
+ // todo(helsaawy): create custom error types to store the desired vs actual size and addr family?
+
+ ErrBufferSize = errors.New("buffer size")
+ ErrAddrFamily = errors.New("address family")
+ ErrInvalidPointer = errors.New("invalid pointer")
+ ErrSocketClosed = fmt.Errorf("socket closed: %w", net.ErrClosed)
+)
+
+// todo(helsaawy): replace these with generics, ie: GetSockName[S RawSockaddr](s windows.Handle) (S, error)
+
+// GetSockName writes the local address of socket s to the [RawSockaddr] rsa.
+// If rsa is not large enough, the [windows.WSAEFAULT] is returned.
+func GetSockName(s windows.Handle, rsa RawSockaddr) error {
+ ptr, l, err := rsa.Sockaddr()
+ if err != nil {
+ return fmt.Errorf("could not retrieve socket pointer and size: %w", err)
+ }
+
+ // although getsockname returns WSAEFAULT if the buffer is too small, it does not set
+ // &l to the correct size, so--apart from doubling the buffer repeatedly--there is no remedy
+ return getsockname(s, ptr, &l)
+}
+
+// GetPeerName returns the remote address the socket is connected to.
+//
+// See [GetSockName] for more information.
+func GetPeerName(s windows.Handle, rsa RawSockaddr) error {
+ ptr, l, err := rsa.Sockaddr()
+ if err != nil {
+ return fmt.Errorf("could not retrieve socket pointer and size: %w", err)
+ }
+
+ return getpeername(s, ptr, &l)
+}
+
+func Bind(s windows.Handle, rsa RawSockaddr) (err error) {
+ ptr, l, err := rsa.Sockaddr()
+ if err != nil {
+ return fmt.Errorf("could not retrieve socket pointer and size: %w", err)
+ }
+
+ return bind(s, ptr, l)
+}
+
+// "golang.org/x/sys/windows".ConnectEx and .Bind only accept internal implementations of the
+// their sockaddr interface, so they cannot be used with HvsockAddr
+// Replicate functionality here from
+// https://cs.opensource.google/go/x/sys/+/master:windows/syscall_windows.go
+
+// The function pointers to `AcceptEx`, `ConnectEx` and `GetAcceptExSockaddrs` must be loaded at
+// runtime via a WSAIoctl call:
+// https://docs.microsoft.com/en-us/windows/win32/api/Mswsock/nc-mswsock-lpfn_connectex#remarks
+
+type runtimeFunc struct {
+ id guid.GUID
+ once sync.Once
+ addr uintptr
+ err error
+}
+
+func (f *runtimeFunc) Load() error {
+ f.once.Do(func() {
+ var s windows.Handle
+ s, f.err = windows.Socket(windows.AF_INET, windows.SOCK_STREAM, windows.IPPROTO_TCP)
+ if f.err != nil {
+ return
+ }
+ defer windows.CloseHandle(s) //nolint:errcheck
+
+ var n uint32
+ f.err = windows.WSAIoctl(s,
+ windows.SIO_GET_EXTENSION_FUNCTION_POINTER,
+ (*byte)(unsafe.Pointer(&f.id)),
+ uint32(unsafe.Sizeof(f.id)),
+ (*byte)(unsafe.Pointer(&f.addr)),
+ uint32(unsafe.Sizeof(f.addr)),
+ &n,
+ nil, // overlapped
+ 0, // completionRoutine
+ )
+ })
+ return f.err
+}
+
+var (
+ // todo: add `AcceptEx` and `GetAcceptExSockaddrs`
+ WSAID_CONNECTEX = guid.GUID{ //revive:disable-line:var-naming ALL_CAPS
+ Data1: 0x25a207b9,
+ Data2: 0xddf3,
+ Data3: 0x4660,
+ Data4: [8]byte{0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e},
+ }
+
+ connectExFunc = runtimeFunc{id: WSAID_CONNECTEX}
+)
+
+func ConnectEx(
+ fd windows.Handle,
+ rsa RawSockaddr,
+ sendBuf *byte,
+ sendDataLen uint32,
+ bytesSent *uint32,
+ overlapped *windows.Overlapped,
+) error {
+ if err := connectExFunc.Load(); err != nil {
+ return fmt.Errorf("failed to load ConnectEx function pointer: %w", err)
+ }
+ ptr, n, err := rsa.Sockaddr()
+ if err != nil {
+ return err
+ }
+ return connectEx(fd, ptr, n, sendBuf, sendDataLen, bytesSent, overlapped)
+}
+
+// BOOL LpfnConnectex(
+// [in] SOCKET s,
+// [in] const sockaddr *name,
+// [in] int namelen,
+// [in, optional] PVOID lpSendBuffer,
+// [in] DWORD dwSendDataLength,
+// [out] LPDWORD lpdwBytesSent,
+// [in] LPOVERLAPPED lpOverlapped
+// )
+
+func connectEx(
+ s windows.Handle,
+ name unsafe.Pointer,
+ namelen int32,
+ sendBuf *byte,
+ sendDataLen uint32,
+ bytesSent *uint32,
+ overlapped *windows.Overlapped,
+) (err error) {
+ r1, _, e1 := syscall.SyscallN(connectExFunc.addr,
+ uintptr(s),
+ uintptr(name),
+ uintptr(namelen),
+ uintptr(unsafe.Pointer(sendBuf)),
+ uintptr(sendDataLen),
+ uintptr(unsafe.Pointer(bytesSent)),
+ uintptr(unsafe.Pointer(overlapped)),
+ )
+
+ if r1 == 0 {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return err
+}
diff --git a/vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go
new file mode 100644
index 00000000..e1504126
--- /dev/null
+++ b/vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go
@@ -0,0 +1,69 @@
+//go:build windows
+
+// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT.
+
+package socket
+
+import (
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+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)
+ errERROR_EINVAL error = syscall.EINVAL
+)
+
+// errnoErr returns common boxed Errno values, to prevent
+// allocations at runtime.
+func errnoErr(e syscall.Errno) error {
+ switch e {
+ case 0:
+ return errERROR_EINVAL
+ case errnoERROR_IO_PENDING:
+ return errERROR_IO_PENDING
+ }
+ return e
+}
+
+var (
+ modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
+
+ procbind = modws2_32.NewProc("bind")
+ procgetpeername = modws2_32.NewProc("getpeername")
+ procgetsockname = modws2_32.NewProc("getsockname")
+)
+
+func bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) {
+ r1, _, e1 := syscall.SyscallN(procbind.Addr(), uintptr(s), uintptr(name), uintptr(namelen))
+ if r1 == socketError {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
+ r1, _, e1 := syscall.SyscallN(procgetpeername.Addr(), uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
+ if r1 == socketError {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
+ r1, _, e1 := syscall.SyscallN(procgetsockname.Addr(), uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
+ if r1 == socketError {
+ err = errnoErr(e1)
+ }
+ return
+}
diff --git a/vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go b/vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go
new file mode 100644
index 00000000..42ebc019
--- /dev/null
+++ b/vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go
@@ -0,0 +1,132 @@
+package stringbuffer
+
+import (
+ "sync"
+ "unicode/utf16"
+)
+
+// TODO: worth exporting and using in mkwinsyscall?
+
+// Uint16BufferSize is the buffer size in the pool, chosen somewhat arbitrarily to accommodate
+// large path strings:
+// MAX_PATH (260) + size of volume GUID prefix (49) + null terminator = 310.
+const MinWStringCap = 310
+
+// use *[]uint16 since []uint16 creates an extra allocation where the slice header
+// is copied to heap and then referenced via pointer in the interface header that sync.Pool
+// stores.
+var pathPool = sync.Pool{ // if go1.18+ adds Pool[T], use that to store []uint16 directly
+ New: func() interface{} {
+ b := make([]uint16, MinWStringCap)
+ return &b
+ },
+}
+
+func newBuffer() []uint16 { return *(pathPool.Get().(*[]uint16)) }
+
+// freeBuffer copies the slice header data, and puts a pointer to that in the pool.
+// This avoids taking a pointer to the slice header in WString, which can be set to nil.
+func freeBuffer(b []uint16) { pathPool.Put(&b) }
+
+// WString is a wide string buffer ([]uint16) meant for storing UTF-16 encoded strings
+// for interacting with Win32 APIs.
+// Sizes are specified as uint32 and not int.
+//
+// It is not thread safe.
+type WString struct {
+ // type-def allows casting to []uint16 directly, use struct to prevent that and allow adding fields in the future.
+
+ // raw buffer
+ b []uint16
+}
+
+// NewWString returns a [WString] allocated from a shared pool with an
+// initial capacity of at least [MinWStringCap].
+// Since the buffer may have been previously used, its contents are not guaranteed to be empty.
+//
+// The buffer should be freed via [WString.Free]
+func NewWString() *WString {
+ return &WString{
+ b: newBuffer(),
+ }
+}
+
+func (b *WString) Free() {
+ if b.empty() {
+ return
+ }
+ freeBuffer(b.b)
+ b.b = nil
+}
+
+// ResizeTo grows the buffer to at least c and returns the new capacity, freeing the
+// previous buffer back into pool.
+func (b *WString) ResizeTo(c uint32) uint32 {
+ // already sufficient (or n is 0)
+ if c <= b.Cap() {
+ return b.Cap()
+ }
+
+ if c <= MinWStringCap {
+ c = MinWStringCap
+ }
+ // allocate at-least double buffer size, as is done in [bytes.Buffer] and other places
+ if c <= 2*b.Cap() {
+ c = 2 * b.Cap()
+ }
+
+ b2 := make([]uint16, c)
+ if !b.empty() {
+ copy(b2, b.b)
+ freeBuffer(b.b)
+ }
+ b.b = b2
+ return c
+}
+
+// Buffer returns the underlying []uint16 buffer.
+func (b *WString) Buffer() []uint16 {
+ if b.empty() {
+ return nil
+ }
+ return b.b
+}
+
+// Pointer returns a pointer to the first uint16 in the buffer.
+// If the [WString.Free] has already been called, the pointer will be nil.
+func (b *WString) Pointer() *uint16 {
+ if b.empty() {
+ return nil
+ }
+ return &b.b[0]
+}
+
+// String returns the returns the UTF-8 encoding of the UTF-16 string in the buffer.
+//
+// It assumes that the data is null-terminated.
+func (b *WString) String() string {
+ // Using [windows.UTF16ToString] would require importing "golang.org/x/sys/windows"
+ // and would make this code Windows-only, which makes no sense.
+ // So copy UTF16ToString code into here.
+ // If other windows-specific code is added, switch to [windows.UTF16ToString]
+
+ s := b.b
+ for i, v := range s {
+ if v == 0 {
+ s = s[:i]
+ break
+ }
+ }
+ return string(utf16.Decode(s))
+}
+
+// Cap returns the underlying buffer capacity.
+func (b *WString) Cap() uint32 {
+ if b.empty() {
+ return 0
+ }
+ return b.cap()
+}
+
+func (b *WString) cap() uint32 { return uint32(cap(b.b)) }
+func (b *WString) empty() bool { return b == nil || b.cap() == 0 }
diff --git a/vendor/github.com/Microsoft/go-winio/pipe.go b/vendor/github.com/Microsoft/go-winio/pipe.go
index d6a46f6a..a2da6639 100644
--- a/vendor/github.com/Microsoft/go-winio/pipe.go
+++ b/vendor/github.com/Microsoft/go-winio/pipe.go
@@ -1,3 +1,4 @@
+//go:build windows
// +build windows
package winio
@@ -10,26 +11,52 @@ import (
"net"
"os"
"runtime"
- "syscall"
"time"
"unsafe"
+
+ "golang.org/x/sys/windows"
+
+ "github.com/Microsoft/go-winio/internal/fs"
)
-//sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
-//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
-//sys createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW
-//sys 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 ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) = ntdll.NtCreateNamedPipeFile
-//sys rtlNtStatusToDosError(status ntstatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
-//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) = ntdll.RtlDosPathNameToNtPathName_U
-//sys rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) = ntdll.RtlDefaultNpAcl
+//sys connectNamedPipe(pipe windows.Handle, o *windows.Overlapped) (err error) = ConnectNamedPipe
+//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateNamedPipeW
+//sys disconnectNamedPipe(pipe windows.Handle) (err error) = DisconnectNamedPipe
+//sys getNamedPipeInfo(pipe windows.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
+//sys getNamedPipeHandleState(pipe windows.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
+//sys ntCreateNamedPipeFile(pipe *windows.Handle, access ntAccessMask, oa *objectAttributes, iosb *ioStatusBlock, share ntFileShareMode, disposition ntFileCreationDisposition, options ntFileOptions, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) = ntdll.NtCreateNamedPipeFile
+//sys rtlNtStatusToDosError(status ntStatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
+//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) = ntdll.RtlDosPathNameToNtPathName_U
+//sys rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) = ntdll.RtlDefaultNpAcl
+
+type PipeConn interface {
+ net.Conn
+ Disconnect() error
+ Flush() error
+}
+
+// type aliases for mkwinsyscall code
+type (
+ ntAccessMask = fs.AccessMask
+ ntFileShareMode = fs.FileShareMode
+ ntFileCreationDisposition = fs.NTFileCreationDisposition
+ ntFileOptions = fs.NTCreateOptions
+)
type ioStatusBlock struct {
Status, Information uintptr
}
+// typedef struct _OBJECT_ATTRIBUTES {
+// ULONG Length;
+// HANDLE RootDirectory;
+// PUNICODE_STRING ObjectName;
+// ULONG Attributes;
+// PVOID SecurityDescriptor;
+// PVOID SecurityQualityOfService;
+// } OBJECT_ATTRIBUTES;
+//
+// https://learn.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_object_attributes
type objectAttributes struct {
Length uintptr
RootDirectory uintptr
@@ -45,51 +72,39 @@ type unicodeString struct {
Buffer uintptr
}
+// typedef struct _SECURITY_DESCRIPTOR {
+// BYTE Revision;
+// BYTE Sbz1;
+// SECURITY_DESCRIPTOR_CONTROL Control;
+// PSID Owner;
+// PSID Group;
+// PACL Sacl;
+// PACL Dacl;
+// } SECURITY_DESCRIPTOR, *PISECURITY_DESCRIPTOR;
+//
+// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-security_descriptor
type securityDescriptor struct {
Revision byte
Sbz1 byte
Control uint16
Owner uintptr
Group uintptr
- Sacl uintptr
- Dacl uintptr
+ Sacl uintptr //revive:disable-line:var-naming SACL, not Sacl
+ Dacl uintptr //revive:disable-line:var-naming DACL, not Dacl
}
-type ntstatus int32
+type ntStatus int32
-func (status ntstatus) Err() error {
+func (status ntStatus) Err() error {
if status >= 0 {
return nil
}
return rtlNtStatusToDosError(status)
}
-const (
- cERROR_PIPE_BUSY = syscall.Errno(231)
- cERROR_NO_DATA = syscall.Errno(232)
- cERROR_PIPE_CONNECTED = syscall.Errno(535)
- cERROR_SEM_TIMEOUT = syscall.Errno(121)
-
- cSECURITY_SQOS_PRESENT = 0x100000
- cSECURITY_ANONYMOUS = 0
-
- cPIPE_TYPE_MESSAGE = 4
-
- cPIPE_READMODE_MESSAGE = 2
-
- cFILE_OPEN = 1
- cFILE_CREATE = 2
-
- cFILE_PIPE_MESSAGE_TYPE = 1
- cFILE_PIPE_REJECT_REMOTE_CLIENTS = 2
-
- cSE_DACL_PRESENT = 4
-)
-
var (
// ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
- // This error should match net.errClosing since docker takes a dependency on its text.
- ErrPipeListenerClosed = errors.New("use of closed network connection")
+ ErrPipeListenerClosed = net.ErrClosed
errPipeWriteClosed = errors.New("pipe has been closed for write")
)
@@ -99,6 +114,8 @@ type win32Pipe struct {
path string
}
+var _ PipeConn = (*win32Pipe)(nil)
+
type win32MessageBytePipe struct {
win32Pipe
writeClosed bool
@@ -116,9 +133,14 @@ func (f *win32Pipe) RemoteAddr() net.Addr {
}
func (f *win32Pipe) SetDeadline(t time.Time) error {
- f.SetReadDeadline(t)
- f.SetWriteDeadline(t)
- return nil
+ if err := f.SetReadDeadline(t); err != nil {
+ return err
+ }
+ return f.SetWriteDeadline(t)
+}
+
+func (f *win32Pipe) Disconnect() error {
+ return disconnectNamedPipe(f.win32File.handle)
}
// CloseWrite closes the write side of a message pipe in byte mode.
@@ -157,14 +179,14 @@ func (f *win32MessageBytePipe) Read(b []byte) (int, error) {
return 0, io.EOF
}
n, err := f.win32File.Read(b)
- if err == io.EOF {
+ if err == io.EOF { //nolint:errorlint
// 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
- } else if err == syscall.ERROR_MORE_DATA {
+ } else if err == windows.ERROR_MORE_DATA { //nolint:errorlint // err is Errno
// ERROR_MORE_DATA indicates that the pipe's read mode is message mode
// and the message still has more bytes. Treat this as a success, since
// this package presents all named pipes as byte streams.
@@ -173,7 +195,7 @@ func (f *win32MessageBytePipe) Read(b []byte) (int, error) {
return n, err
}
-func (s pipeAddress) Network() string {
+func (pipeAddress) Network() string {
return "pipe"
}
@@ -182,22 +204,29 @@ func (s pipeAddress) String() string {
}
// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
-func tryDialPipe(ctx context.Context, path *string) (syscall.Handle, error) {
+func tryDialPipe(ctx context.Context, path *string, access fs.AccessMask, impLevel PipeImpLevel) (windows.Handle, error) {
for {
select {
case <-ctx.Done():
- return syscall.Handle(0), ctx.Err()
+ return windows.Handle(0), ctx.Err()
default:
- h, err := createFile(*path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
+ h, err := fs.CreateFile(*path,
+ access,
+ 0, // mode
+ nil, // security attributes
+ fs.OPEN_EXISTING,
+ fs.FILE_FLAG_OVERLAPPED|fs.SECURITY_SQOS_PRESENT|fs.FileSQSFlag(impLevel),
+ 0, // template file handle
+ )
if err == nil {
return h, nil
}
- if err != cERROR_PIPE_BUSY {
+ if err != windows.ERROR_PIPE_BUSY { //nolint:errorlint // err is Errno
return h, &os.PathError{Err: err, Op: "open", Path: *path}
}
// Wait 10 msec and try again. This is a rather simplistic
// view, as we always try each 10 milliseconds.
- time.Sleep(time.Millisecond * 10)
+ time.Sleep(10 * time.Millisecond)
}
}
}
@@ -210,11 +239,12 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
if timeout != nil {
absTimeout = time.Now().Add(*timeout)
} else {
- absTimeout = time.Now().Add(time.Second * 2)
+ absTimeout = time.Now().Add(2 * time.Second)
}
- ctx, _ := context.WithDeadline(context.Background(), absTimeout)
+ ctx, cancel := context.WithDeadline(context.Background(), absTimeout)
+ defer cancel()
conn, err := DialPipeContext(ctx, path)
- if err == context.DeadlineExceeded {
+ if errors.Is(err, context.DeadlineExceeded) {
return nil, ErrTimeout
}
return conn, err
@@ -223,9 +253,33 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
// DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
// cancellation or timeout.
func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
+ return DialPipeAccess(ctx, path, uint32(fs.GENERIC_READ|fs.GENERIC_WRITE))
+}
+
+// PipeImpLevel is an enumeration of impersonation levels that may be set
+// when calling DialPipeAccessImpersonation.
+type PipeImpLevel uint32
+
+const (
+ PipeImpLevelAnonymous = PipeImpLevel(fs.SECURITY_ANONYMOUS)
+ PipeImpLevelIdentification = PipeImpLevel(fs.SECURITY_IDENTIFICATION)
+ PipeImpLevelImpersonation = PipeImpLevel(fs.SECURITY_IMPERSONATION)
+ PipeImpLevelDelegation = PipeImpLevel(fs.SECURITY_DELEGATION)
+)
+
+// DialPipeAccess attempts to connect to a named pipe by `path` with `access` until `ctx`
+// cancellation or timeout.
+func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) {
+ return DialPipeAccessImpLevel(ctx, path, access, PipeImpLevelAnonymous)
+}
+
+// DialPipeAccessImpLevel attempts to connect to a named pipe by `path` with
+// `access` at `impLevel` until `ctx` cancellation or timeout. The other
+// DialPipe* implementations use PipeImpLevelAnonymous.
+func DialPipeAccessImpLevel(ctx context.Context, path string, access uint32, impLevel PipeImpLevel) (net.Conn, error) {
var err error
- var h syscall.Handle
- h, err = tryDialPipe(ctx, &path)
+ var h windows.Handle
+ h, err = tryDialPipe(ctx, &path, fs.AccessMask(access), impLevel)
if err != nil {
return nil, err
}
@@ -238,13 +292,13 @@ func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
f, err := makeWin32File(h)
if err != nil {
- syscall.Close(h)
+ windows.Close(h)
return nil, err
}
// If the pipe is in message mode, return a message byte pipe, which
// supports CloseWrite().
- if flags&cPIPE_TYPE_MESSAGE != 0 {
+ if flags&windows.PIPE_TYPE_MESSAGE != 0 {
return &win32MessageBytePipe{
win32Pipe: win32Pipe{win32File: f, path: path},
}, nil
@@ -258,7 +312,7 @@ type acceptResponse struct {
}
type win32PipeListener struct {
- firstHandle syscall.Handle
+ firstHandle windows.Handle
path string
config PipeConfig
acceptCh chan (chan acceptResponse)
@@ -266,8 +320,8 @@ type win32PipeListener struct {
doneCh chan int
}
-func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (syscall.Handle, error) {
- path16, err := syscall.UTF16FromString(path)
+func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (windows.Handle, error) {
+ path16, err := windows.UTF16FromString(path)
if err != nil {
return 0, &os.PathError{Op: "open", Path: path, Err: err}
}
@@ -276,59 +330,81 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy
oa.Length = unsafe.Sizeof(oa)
var ntPath unicodeString
- if err := rtlDosPathNameToNtPathName(&path16[0], &ntPath, 0, 0).Err(); err != nil {
+ if err := rtlDosPathNameToNtPathName(&path16[0],
+ &ntPath,
+ 0,
+ 0,
+ ).Err(); err != nil {
return 0, &os.PathError{Op: "open", Path: path, Err: err}
}
- defer localFree(ntPath.Buffer)
+ defer windows.LocalFree(windows.Handle(ntPath.Buffer)) //nolint:errcheck
oa.ObjectName = &ntPath
+ oa.Attributes = windows.OBJ_CASE_INSENSITIVE
// The security descriptor is only needed for the first pipe.
if first {
if sd != nil {
- len := uint32(len(sd))
- sdb := localAlloc(0, len)
- defer localFree(sdb)
+ //todo: does `sdb` need to be allocated on the heap, or can go allocate it?
+ l := uint32(len(sd))
+ sdb, err := windows.LocalAlloc(0, l)
+ if err != nil {
+ return 0, fmt.Errorf("LocalAlloc for security descriptor with of length %d: %w", l, err)
+ }
+ defer windows.LocalFree(windows.Handle(sdb)) //nolint:errcheck
copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd)
oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb))
} else {
// Construct the default named pipe security descriptor.
var dacl uintptr
if err := rtlDefaultNpAcl(&dacl).Err(); err != nil {
- return 0, fmt.Errorf("getting default named pipe ACL: %s", err)
+ return 0, fmt.Errorf("getting default named pipe ACL: %w", err)
}
- defer localFree(dacl)
+ defer windows.LocalFree(windows.Handle(dacl)) //nolint:errcheck
sdb := &securityDescriptor{
Revision: 1,
- Control: cSE_DACL_PRESENT,
+ Control: windows.SE_DACL_PRESENT,
Dacl: dacl,
}
oa.SecurityDescriptor = sdb
}
}
- typ := uint32(cFILE_PIPE_REJECT_REMOTE_CLIENTS)
+ typ := uint32(windows.FILE_PIPE_REJECT_REMOTE_CLIENTS)
if c.MessageMode {
- typ |= cFILE_PIPE_MESSAGE_TYPE
+ typ |= windows.FILE_PIPE_MESSAGE_TYPE
}
- disposition := uint32(cFILE_OPEN)
- access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | syscall.SYNCHRONIZE)
+ disposition := fs.FILE_OPEN
+ access := fs.GENERIC_READ | fs.GENERIC_WRITE | fs.SYNCHRONIZE
if first {
- disposition = cFILE_CREATE
+ disposition = fs.FILE_CREATE
// By not asking for read or write access, the named pipe file system
// will put this pipe into an initially disconnected state, blocking
// client connections until the next call with first == false.
- access = syscall.SYNCHRONIZE
+ access = fs.SYNCHRONIZE
}
timeout := int64(-50 * 10000) // 50ms
var (
- h syscall.Handle
+ h windows.Handle
iosb ioStatusBlock
)
- err = ntCreateNamedPipeFile(&h, access, &oa, &iosb, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE, disposition, 0, typ, 0, 0, 0xffffffff, uint32(c.InputBufferSize), uint32(c.OutputBufferSize), &timeout).Err()
+ err = ntCreateNamedPipeFile(&h,
+ access,
+ &oa,
+ &iosb,
+ fs.FILE_SHARE_READ|fs.FILE_SHARE_WRITE,
+ disposition,
+ 0,
+ typ,
+ 0,
+ 0,
+ 0xffffffff,
+ uint32(c.InputBufferSize),
+ uint32(c.OutputBufferSize),
+ &timeout).Err()
if err != nil {
return 0, &os.PathError{Op: "open", Path: path, Err: err}
}
@@ -344,7 +420,7 @@ func (l *win32PipeListener) makeServerPipe() (*win32File, error) {
}
f, err := makeWin32File(h)
if err != nil {
- syscall.Close(h)
+ windows.Close(h)
return nil, err
}
return f, nil
@@ -373,7 +449,7 @@ func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) {
p.Close()
p = nil
err = <-ch
- if err == nil || err == ErrFileClosed {
+ if err == nil || err == ErrFileClosed { //nolint:errorlint // err is Errno
err = ErrPipeListenerClosed
}
}
@@ -395,15 +471,15 @@ func (l *win32PipeListener) listenerRoutine() {
p, err = l.makeConnectedServerPipe()
// If the connection was immediately closed by the client, try
// again.
- if err != cERROR_NO_DATA {
+ if err != windows.ERROR_NO_DATA { //nolint:errorlint // err is Errno
break
}
}
responseCh <- acceptResponse{p, err}
- closed = err == ErrPipeListenerClosed
+ closed = err == ErrPipeListenerClosed //nolint:errorlint // err is Errno
}
}
- syscall.Close(l.firstHandle)
+ windows.Close(l.firstHandle)
l.firstHandle = 0
// Notify Close() and Accept() callers that the handle has been closed.
close(l.doneCh)
@@ -422,10 +498,10 @@ type PipeConfig struct {
// when the pipe is in message mode.
MessageMode bool
- // InputBufferSize specifies the size the input buffer, in bytes.
+ // InputBufferSize specifies the size of the input buffer, in bytes.
InputBufferSize int32
- // OutputBufferSize specifies the size the input buffer, in bytes.
+ // OutputBufferSize specifies the size of the output buffer, in bytes.
OutputBufferSize int32
}
@@ -462,15 +538,15 @@ func ListenPipe(path string, c *PipeConfig) (net.Listener, error) {
}
func connectPipe(p *win32File) error {
- c, err := p.prepareIo()
+ c, err := p.prepareIO()
if err != nil {
return err
}
defer p.wg.Done()
err = connectNamedPipe(p.handle, &c.o)
- _, err = p.asyncIo(c, nil, 0, err)
- if err != nil && err != cERROR_PIPE_CONNECTED {
+ _, err = p.asyncIO(c, nil, 0, err)
+ if err != nil && err != windows.ERROR_PIPE_CONNECTED { //nolint:errorlint // err is Errno
return err
}
return nil
diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go
index 58640657..48ce4e92 100644
--- a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go
+++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go
@@ -7,26 +7,26 @@ package guid
import (
"crypto/rand"
- "crypto/sha1"
+ "crypto/sha1" //nolint:gosec // not used for secure application
"encoding"
"encoding/binary"
"fmt"
"strconv"
-
- "golang.org/x/sys/windows"
)
+//go:generate go run golang.org/x/tools/cmd/stringer -type=Variant -trimprefix=Variant -linecomment
+
// Variant specifies which GUID variant (or "type") of the GUID. It determines
// how the entirety of the rest of the GUID is interpreted.
type Variant uint8
-// The variants specified by RFC 4122.
+// The variants specified by RFC 4122 section 4.1.1.
const (
// VariantUnknown specifies a GUID variant which does not conform to one of
// the variant encodings specified in RFC 4122.
VariantUnknown Variant = iota
VariantNCS
- VariantRFC4122
+ VariantRFC4122 // RFC 4122
VariantMicrosoft
VariantFuture
)
@@ -36,16 +36,13 @@ const (
// hash of an input string.
type Version uint8
+func (v Version) String() string {
+ return strconv.FormatUint(uint64(v), 10)
+}
+
var _ = (encoding.TextMarshaler)(GUID{})
var _ = (encoding.TextUnmarshaler)(&GUID{})
-// GUID represents a GUID/UUID. It has the same structure as
-// golang.org/x/sys/windows.GUID so that it can be used with functions expecting
-// that type. It is defined as its own type so that stringification and
-// marshaling can be supported. The representation matches that used by native
-// Windows code.
-type GUID windows.GUID
-
// NewV4 returns a new version 4 (pseudorandom) GUID, as defined by RFC 4122.
func NewV4() (GUID, error) {
var b [16]byte
@@ -68,7 +65,7 @@ func NewV4() (GUID, error) {
// big-endian UTF16 stream of bytes. If that is desired, the string can be
// encoded as such before being passed to this function.
func NewV5(namespace GUID, name []byte) (GUID, error) {
- b := sha1.New()
+ b := sha1.New() //nolint:gosec // not used for secure application
namespaceBytes := namespace.ToArray()
b.Write(namespaceBytes[:])
b.Write(name)
diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go
new file mode 100644
index 00000000..805bd354
--- /dev/null
+++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go
@@ -0,0 +1,16 @@
+//go:build !windows
+// +build !windows
+
+package guid
+
+// GUID represents a GUID/UUID. It has the same structure as
+// golang.org/x/sys/windows.GUID so that it can be used with functions expecting
+// that type. It is defined as its own type as that is only available to builds
+// targeted at `windows`. The representation matches that used by native Windows
+// code.
+type GUID struct {
+ Data1 uint32
+ Data2 uint16
+ Data3 uint16
+ Data4 [8]byte
+}
diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go
new file mode 100644
index 00000000..27e45ee5
--- /dev/null
+++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go
@@ -0,0 +1,13 @@
+//go:build windows
+// +build windows
+
+package guid
+
+import "golang.org/x/sys/windows"
+
+// GUID represents a GUID/UUID. It has the same structure as
+// golang.org/x/sys/windows.GUID so that it can be used with functions expecting
+// that type. It is defined as its own type so that stringification and
+// marshaling can be supported. The representation matches that used by native
+// Windows code.
+type GUID windows.GUID
diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go
new file mode 100644
index 00000000..4076d313
--- /dev/null
+++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go
@@ -0,0 +1,27 @@
+// Code generated by "stringer -type=Variant -trimprefix=Variant -linecomment"; DO NOT EDIT.
+
+package guid
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[VariantUnknown-0]
+ _ = x[VariantNCS-1]
+ _ = x[VariantRFC4122-2]
+ _ = x[VariantMicrosoft-3]
+ _ = x[VariantFuture-4]
+}
+
+const _Variant_name = "UnknownNCSRFC 4122MicrosoftFuture"
+
+var _Variant_index = [...]uint8{0, 7, 10, 18, 27, 33}
+
+func (i Variant) String() string {
+ if i >= Variant(len(_Variant_index)-1) {
+ return "Variant(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _Variant_name[_Variant_index[i]:_Variant_index[i+1]]
+}
diff --git a/vendor/github.com/Microsoft/go-winio/privilege.go b/vendor/github.com/Microsoft/go-winio/privilege.go
index 9c83d36f..d9b90b6e 100644
--- a/vendor/github.com/Microsoft/go-winio/privilege.go
+++ b/vendor/github.com/Microsoft/go-winio/privilege.go
@@ -1,3 +1,4 @@
+//go:build windows
// +build windows
package winio
@@ -8,7 +9,6 @@ import (
"fmt"
"runtime"
"sync"
- "syscall"
"unicode/utf16"
"golang.org/x/sys/windows"
@@ -17,26 +17,22 @@ import (
//sys adjustTokenPrivileges(token windows.Token, 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 *windows.Token) (err error) = advapi32.OpenThreadToken
-//sys getCurrentThread() (h syscall.Handle) = GetCurrentThread
+//sys openThreadToken(thread windows.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
+//sys getCurrentThread() (h windows.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
+ //revive:disable-next-line:var-naming ALL_CAPS
+ SE_PRIVILEGE_ENABLED = windows.SE_PRIVILEGE_ENABLED
- ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300
+ //revive:disable-next-line:var-naming ALL_CAPS
+ ERROR_NOT_ALL_ASSIGNED windows.Errno = windows.ERROR_NOT_ALL_ASSIGNED
- SeBackupPrivilege = "SeBackupPrivilege"
- SeRestorePrivilege = "SeRestorePrivilege"
-)
-
-const (
- securityAnonymous = iota
- securityIdentification
- securityImpersonation
- securityDelegation
+ SeBackupPrivilege = "SeBackupPrivilege"
+ SeRestorePrivilege = "SeRestorePrivilege"
+ SeSecurityPrivilege = "SeSecurityPrivilege"
)
var (
@@ -50,11 +46,9 @@ type PrivilegeError struct {
}
func (e *PrivilegeError) Error() string {
- s := ""
+ s := "Could not enable privilege "
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 {
@@ -93,7 +87,7 @@ func RunWithPrivileges(names []string, fn func() error) error {
}
func mapPrivileges(names []string) ([]uint64, error) {
- var privileges []uint64
+ privileges := make([]uint64, 0, len(names))
privNameMutex.Lock()
defer privNameMutex.Unlock()
for _, name := range names {
@@ -126,7 +120,7 @@ func enableDisableProcessPrivilege(names []string, action uint32) error {
return err
}
- p, _ := windows.GetCurrentProcess()
+ p := windows.CurrentProcess()
var token windows.Token
err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token)
if err != nil {
@@ -139,10 +133,10 @@ func enableDisableProcessPrivilege(names []string, action uint32) error {
func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error {
var b bytes.Buffer
- binary.Write(&b, binary.LittleEndian, uint32(len(privileges)))
+ _ = binary.Write(&b, binary.LittleEndian, uint32(len(privileges)))
for _, p := range privileges {
- binary.Write(&b, binary.LittleEndian, p)
- binary.Write(&b, binary.LittleEndian, action)
+ _ = binary.Write(&b, binary.LittleEndian, p)
+ _ = binary.Write(&b, binary.LittleEndian, action)
}
prevState := make([]byte, b.Len())
reqSize := uint32(0)
@@ -150,7 +144,7 @@ func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) e
if !success {
return err
}
- if err == ERROR_NOT_ALL_ASSIGNED {
+ if err == ERROR_NOT_ALL_ASSIGNED { //nolint:errorlint // err is Errno
return &PrivilegeError{privileges}
}
return nil
@@ -176,13 +170,13 @@ func getPrivilegeName(luid uint64) string {
}
func newThreadToken() (windows.Token, error) {
- err := impersonateSelf(securityImpersonation)
+ err := impersonateSelf(windows.SecurityImpersonation)
if err != nil {
return 0, err
}
var token windows.Token
- err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token)
+ err = openThreadToken(getCurrentThread(), windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, false, &token)
if err != nil {
rerr := revertToSelf()
if rerr != nil {
diff --git a/vendor/github.com/Microsoft/go-winio/reparse.go b/vendor/github.com/Microsoft/go-winio/reparse.go
index fc1ee4d3..67d1a104 100644
--- a/vendor/github.com/Microsoft/go-winio/reparse.go
+++ b/vendor/github.com/Microsoft/go-winio/reparse.go
@@ -1,3 +1,6 @@
+//go:build windows
+// +build windows
+
package winio
import (
@@ -113,16 +116,16 @@ func EncodeReparsePoint(rp *ReparsePoint) []byte {
}
var b bytes.Buffer
- binary.Write(&b, binary.LittleEndian, &data)
+ _ = 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, flags)
}
- binary.Write(&b, binary.LittleEndian, ntTarget16)
- binary.Write(&b, binary.LittleEndian, target16)
+ _ = binary.Write(&b, binary.LittleEndian, ntTarget16)
+ _ = binary.Write(&b, binary.LittleEndian, target16)
return b.Bytes()
}
diff --git a/vendor/github.com/Microsoft/go-winio/sd.go b/vendor/github.com/Microsoft/go-winio/sd.go
index db1b370a..c3685e98 100644
--- a/vendor/github.com/Microsoft/go-winio/sd.go
+++ b/vendor/github.com/Microsoft/go-winio/sd.go
@@ -1,22 +1,20 @@
+//go:build windows
// +build windows
package winio
import (
- "syscall"
+ "errors"
+ "fmt"
"unsafe"
+
+ "golang.org/x/sys/windows"
)
//sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW
+//sys lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountSidW
//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
-//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
-//sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW
-//sys localFree(mem uintptr) = LocalFree
-//sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
-
-const (
- cERROR_NONE_MAPPED = syscall.Errno(1332)
-)
+//sys convertStringSidToSid(str *uint16, sid **byte) (err error) = advapi32.ConvertStringSidToSidW
type AccountLookupError struct {
Name string
@@ -28,8 +26,10 @@ func (e *AccountLookupError) Error() string {
return "lookup account: empty account name specified"
}
var s string
- switch e.Err {
- case cERROR_NONE_MAPPED:
+ switch {
+ case errors.Is(e.Err, windows.ERROR_INVALID_SID):
+ s = "the security ID structure is invalid"
+ case errors.Is(e.Err, windows.ERROR_NONE_MAPPED):
s = "not found"
default:
s = e.Err.Error()
@@ -37,6 +37,8 @@ func (e *AccountLookupError) Error() string {
return "lookup account " + e.Name + ": " + s
}
+func (e *AccountLookupError) Unwrap() error { return e.Err }
+
type SddlConversionError struct {
Sddl string
Err error
@@ -46,15 +48,19 @@ func (e *SddlConversionError) Error() string {
return "convert " + e.Sddl + ": " + e.Err.Error()
}
+func (e *SddlConversionError) Unwrap() error { return e.Err }
+
// LookupSidByName looks up the SID of an account by name
+//
+//revive:disable-next-line:var-naming SID, not Sid
func LookupSidByName(name string) (sid string, err error) {
if name == "" {
- return "", &AccountLookupError{name, cERROR_NONE_MAPPED}
+ return "", &AccountLookupError{name, windows.ERROR_NONE_MAPPED}
}
var sidSize, sidNameUse, refDomainSize uint32
err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse)
- if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER {
+ if err != nil && err != windows.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno
return "", &AccountLookupError{name, err}
}
sidBuffer := make([]byte, sidSize)
@@ -68,31 +74,60 @@ func LookupSidByName(name string) (sid string, err error) {
if err != nil {
return "", &AccountLookupError{name, err}
}
- sid = syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
- localFree(uintptr(unsafe.Pointer(strBuffer)))
+ sid = windows.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
+ _, _ = windows.LocalFree(windows.Handle(unsafe.Pointer(strBuffer)))
return sid, nil
}
-func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
- var sdBuffer uintptr
- err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil)
- if err != nil {
- return nil, &SddlConversionError{sddl, err}
+// LookupNameBySid looks up the name of an account by SID
+//
+//revive:disable-next-line:var-naming SID, not Sid
+func LookupNameBySid(sid string) (name string, err error) {
+ if sid == "" {
+ return "", &AccountLookupError{sid, windows.ERROR_NONE_MAPPED}
}
- defer localFree(sdBuffer)
- sd := make([]byte, getSecurityDescriptorLength(sdBuffer))
- copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)])
- return sd, nil
+
+ sidBuffer, err := windows.UTF16PtrFromString(sid)
+ if err != nil {
+ return "", &AccountLookupError{sid, err}
+ }
+
+ var sidPtr *byte
+ if err = convertStringSidToSid(sidBuffer, &sidPtr); err != nil {
+ return "", &AccountLookupError{sid, err}
+ }
+ defer windows.LocalFree(windows.Handle(unsafe.Pointer(sidPtr))) //nolint:errcheck
+
+ var nameSize, refDomainSize, sidNameUse uint32
+ err = lookupAccountSid(nil, sidPtr, nil, &nameSize, nil, &refDomainSize, &sidNameUse)
+ if err != nil && err != windows.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno
+ return "", &AccountLookupError{sid, err}
+ }
+
+ nameBuffer := make([]uint16, nameSize)
+ refDomainBuffer := make([]uint16, refDomainSize)
+ err = lookupAccountSid(nil, sidPtr, &nameBuffer[0], &nameSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse)
+ if err != nil {
+ return "", &AccountLookupError{sid, err}
+ }
+
+ name = windows.UTF16ToString(nameBuffer)
+ return name, nil
+}
+
+func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
+ sd, err := windows.SecurityDescriptorFromString(sddl)
+ if err != nil {
+ return nil, &SddlConversionError{Sddl: sddl, Err: err}
+ }
+ b := unsafe.Slice((*byte)(unsafe.Pointer(sd)), sd.Length())
+ return b, 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
+ if l := int(unsafe.Sizeof(windows.SECURITY_DESCRIPTOR{})); len(sd) < l {
+ return "", fmt.Errorf("SecurityDescriptor (%d) smaller than expected (%d): %w", len(sd), l, windows.ERROR_INCORRECT_SIZE)
}
- defer localFree(uintptr(unsafe.Pointer(sddl)))
- return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(sddl))[:]), nil
+ s := (*windows.SECURITY_DESCRIPTOR)(unsafe.Pointer(&sd[0]))
+ return s.String(), nil
}
diff --git a/vendor/github.com/Microsoft/go-winio/syscall.go b/vendor/github.com/Microsoft/go-winio/syscall.go
index 5cb52bc7..a6ca111b 100644
--- a/vendor/github.com/Microsoft/go-winio/syscall.go
+++ b/vendor/github.com/Microsoft/go-winio/syscall.go
@@ -1,3 +1,5 @@
+//go:build windows
+
package winio
-//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go hvsock.go
+//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go ./*.go
diff --git a/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
index e26b01fa..89b66eda 100644
--- a/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
+++ b/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
@@ -1,4 +1,6 @@
-// Code generated by 'go generate'; DO NOT EDIT.
+//go:build windows
+
+// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT.
package winio
@@ -19,6 +21,7 @@ const (
var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
+ errERROR_EINVAL error = syscall.EINVAL
)
// errnoErr returns common boxed Errno values, to prevent
@@ -26,237 +29,86 @@ var (
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
- return nil
+ return errERROR_EINVAL
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")
- modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
- modntdll = windows.NewLazySystemDLL("ntdll.dll")
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
+ modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
+ modntdll = windows.NewLazySystemDLL("ntdll.dll")
+ modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
- procCancelIoEx = modkernel32.NewProc("CancelIoEx")
- procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
- procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
- procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
- procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult")
- procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
- procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
- procCreateFileW = modkernel32.NewProc("CreateFileW")
- procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
- procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
- procLocalAlloc = modkernel32.NewProc("LocalAlloc")
- procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
- procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
- procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U")
- procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl")
- 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")
- procbind = modws2_32.NewProc("bind")
+ procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
+ procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
+ procConvertStringSidToSidW = modadvapi32.NewProc("ConvertStringSidToSidW")
+ procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
+ procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
+ procLookupAccountSidW = modadvapi32.NewProc("LookupAccountSidW")
+ procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW")
+ procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW")
+ procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
+ procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
+ procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
+ procBackupRead = modkernel32.NewProc("BackupRead")
+ procBackupWrite = modkernel32.NewProc("BackupWrite")
+ procCancelIoEx = modkernel32.NewProc("CancelIoEx")
+ procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
+ procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
+ procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
+ procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe")
+ procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
+ procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
+ procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
+ procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
+ procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
+ procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
+ procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl")
+ procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U")
+ procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
+ procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult")
)
-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 = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
- }
- return
-}
-
-func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) {
- r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0)
- newport = syscall.Handle(r0)
- if newport == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
- }
- return
-}
-
-func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
- r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
- }
- return
-}
-
-func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) {
- r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
- }
- return
-}
-
-func wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) {
+func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) {
var _p0 uint32
- if wait {
+ if releaseAll {
_p0 = 1
- } else {
- _p0 = 0
}
- r1, _, e1 := syscall.Syscall6(procWSAGetOverlappedResult.Addr(), 5, uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)), 0)
+ r0, _, e1 := syscall.SyscallN(procAdjustTokenPrivileges.Addr(), uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
+ success = r0 != 0
+ if true {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func convertSidToStringSid(sid *byte, str **uint16) (err error) {
+ r1, _, e1 := syscall.SyscallN(procConvertSidToStringSidW.Addr(), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)))
if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
+ err = errnoErr(e1)
}
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)
+func convertStringSidToSid(str *uint16, sid **byte) (err error) {
+ r1, _, e1 := syscall.SyscallN(procConvertStringSidToSidW.Addr(), uintptr(unsafe.Pointer(str)), uintptr(unsafe.Pointer(sid)))
if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
+ err = errnoErr(e1)
}
return
}
-func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
- var _p0 *uint16
- _p0, err = syscall.UTF16PtrFromString(name)
- if err != nil {
- return
- }
- return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
-}
-
-func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
- r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0)
- handle = syscall.Handle(r0)
- if handle == syscall.InvalidHandle {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
- }
- return
-}
-
-func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
- var _p0 *uint16
- _p0, err = syscall.UTF16PtrFromString(name)
- if err != nil {
- return
- }
- return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile)
-}
-
-func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
- r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
- handle = syscall.Handle(r0)
- if handle == syscall.InvalidHandle {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
- }
- return
-}
-
-func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
- r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0)
+func impersonateSelf(level uint32) (err error) {
+ r1, _, e1 := syscall.SyscallN(procImpersonateSelf.Addr(), uintptr(level))
if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
+ err = errnoErr(e1)
}
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 = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
- }
- 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 ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) {
- r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0)
- status = ntstatus(r0)
- return
-}
-
-func rtlNtStatusToDosError(status ntstatus) (winerr error) {
- r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0)
- if r0 != 0 {
- winerr = syscall.Errno(r0)
- }
- return
-}
-
-func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) {
- r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved), 0, 0)
- status = ntstatus(r0)
- return
-}
-
-func rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) {
- r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(dacl)), 0, 0)
- status = ntstatus(r0)
- 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)
@@ -267,164 +119,55 @@ func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSiz
}
func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
- r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0)
+ r1, _, e1 := syscall.SyscallN(procLookupAccountNameW.Addr(), 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)))
if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
+ err = errnoErr(e1)
}
return
}
-func convertSidToStringSid(sid *byte, str **uint16) (err error) {
- r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0)
+func lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
+ r1, _, e1 := syscall.SyscallN(procLookupAccountSidW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)))
if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
+ err = errnoErr(e1)
}
return
}
-func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) {
+func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
var _p0 *uint16
- _p0, err = syscall.UTF16PtrFromString(str)
+ _p0, err = syscall.UTF16PtrFromString(systemName)
if err != nil {
return
}
- return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size)
+ return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId)
}
-func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) {
- r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0)
+func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
+ r1, _, e1 := syscall.SyscallN(procLookupPrivilegeDisplayNameW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)))
if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
+ err = errnoErr(e1)
}
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)
+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.SyscallN(procLookupPrivilegeNameW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)))
if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
+ err = errnoErr(e1)
}
return
}
-func localFree(mem uintptr) {
- syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0)
- return
-}
-
-func getSecurityDescriptorLength(sd uintptr) (len uint32) {
- r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0)
- len = uint32(r0)
- return
-}
-
-func getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) {
- r1, _, e1 := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(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 = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
- }
- return
-}
-
-func adjustTokenPrivileges(token windows.Token, 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 = errnoErr(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 = errnoErr(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 = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
- }
- return
-}
-
-func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (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 = errnoErr(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)
@@ -440,60 +183,34 @@ func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err err
}
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)))
+ r1, _, e1 := syscall.SyscallN(procLookupPrivilegeValueW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
+ err = errnoErr(e1)
}
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
+func openThreadToken(thread windows.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) {
+ var _p0 uint32
+ if openAsSelf {
+ _p0 = 1
}
- 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)
+ r1, _, e1 := syscall.SyscallN(procOpenThreadToken.Addr(), uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)))
if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
+ err = errnoErr(e1)
}
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)
+func revertToSelf() (err error) {
+ r1, _, e1 := syscall.SyscallN(procRevertToSelf.Addr())
if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
+ err = errnoErr(e1)
}
return
}
-func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
+func backupRead(h windows.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
var _p0 *byte
if len(b) > 0 {
_p0 = &b[0]
@@ -501,27 +218,19 @@ func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, proce
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)
+ r1, _, e1 := syscall.SyscallN(procBackupRead.Addr(), uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)))
if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
+ err = errnoErr(e1)
}
return
}
-func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
+func backupWrite(h windows.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
var _p0 *byte
if len(b) > 0 {
_p0 = &b[0]
@@ -529,34 +238,141 @@ func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, p
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)
+ r1, _, e1 := syscall.SyscallN(procBackupWrite.Addr(), uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)))
if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
+ err = errnoErr(e1)
}
return
}
-func bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) {
- r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
- if r1 == socketError {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
+func cancelIoEx(file windows.Handle, o *windows.Overlapped) (err error) {
+ r1, _, e1 := syscall.SyscallN(procCancelIoEx.Addr(), uintptr(file), uintptr(unsafe.Pointer(o)))
+ if r1 == 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func connectNamedPipe(pipe windows.Handle, o *windows.Overlapped) (err error) {
+ r1, _, e1 := syscall.SyscallN(procConnectNamedPipe.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(o)))
+ if r1 == 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func createIoCompletionPort(file windows.Handle, port windows.Handle, key uintptr, threadCount uint32) (newport windows.Handle, err error) {
+ r0, _, e1 := syscall.SyscallN(procCreateIoCompletionPort.Addr(), uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount))
+ newport = windows.Handle(r0)
+ if newport == 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) {
+ var _p0 *uint16
+ _p0, err = syscall.UTF16PtrFromString(name)
+ if err != nil {
+ return
+ }
+ return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
+}
+
+func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) {
+ r0, _, e1 := syscall.SyscallN(procCreateNamedPipeW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)))
+ handle = windows.Handle(r0)
+ if handle == windows.InvalidHandle {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func disconnectNamedPipe(pipe windows.Handle) (err error) {
+ r1, _, e1 := syscall.SyscallN(procDisconnectNamedPipe.Addr(), uintptr(pipe))
+ if r1 == 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func getCurrentThread() (h windows.Handle) {
+ r0, _, _ := syscall.SyscallN(procGetCurrentThread.Addr())
+ h = windows.Handle(r0)
+ return
+}
+
+func getNamedPipeHandleState(pipe windows.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
+ r1, _, e1 := syscall.SyscallN(procGetNamedPipeHandleStateW.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize))
+ if r1 == 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func getNamedPipeInfo(pipe windows.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
+ r1, _, e1 := syscall.SyscallN(procGetNamedPipeInfo.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)))
+ if r1 == 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func getQueuedCompletionStatus(port windows.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
+ r1, _, e1 := syscall.SyscallN(procGetQueuedCompletionStatus.Addr(), uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout))
+ if r1 == 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func setFileCompletionNotificationModes(h windows.Handle, flags uint8) (err error) {
+ r1, _, e1 := syscall.SyscallN(procSetFileCompletionNotificationModes.Addr(), uintptr(h), uintptr(flags))
+ if r1 == 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func ntCreateNamedPipeFile(pipe *windows.Handle, access ntAccessMask, oa *objectAttributes, iosb *ioStatusBlock, share ntFileShareMode, disposition ntFileCreationDisposition, options ntFileOptions, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) {
+ r0, _, _ := syscall.SyscallN(procNtCreateNamedPipeFile.Addr(), uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)))
+ status = ntStatus(r0)
+ return
+}
+
+func rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) {
+ r0, _, _ := syscall.SyscallN(procRtlDefaultNpAcl.Addr(), uintptr(unsafe.Pointer(dacl)))
+ status = ntStatus(r0)
+ return
+}
+
+func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) {
+ r0, _, _ := syscall.SyscallN(procRtlDosPathNameToNtPathName_U.Addr(), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved))
+ status = ntStatus(r0)
+ return
+}
+
+func rtlNtStatusToDosError(status ntStatus) (winerr error) {
+ r0, _, _ := syscall.SyscallN(procRtlNtStatusToDosErrorNoTeb.Addr(), uintptr(status))
+ if r0 != 0 {
+ winerr = syscall.Errno(r0)
+ }
+ return
+}
+
+func wsaGetOverlappedResult(h windows.Handle, o *windows.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) {
+ var _p0 uint32
+ if wait {
+ _p0 = 1
+ }
+ r1, _, e1 := syscall.SyscallN(procWSAGetOverlappedResult.Addr(), uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)))
+ if r1 == 0 {
+ err = errnoErr(e1)
}
return
}
diff --git a/vendor/github.com/containerd/log/.golangci.yml b/vendor/github.com/containerd/log/.golangci.yml
new file mode 100644
index 00000000..a695775d
--- /dev/null
+++ b/vendor/github.com/containerd/log/.golangci.yml
@@ -0,0 +1,30 @@
+linters:
+ enable:
+ - exportloopref # Checks for pointers to enclosing loop variables
+ - gofmt
+ - goimports
+ - gosec
+ - ineffassign
+ - misspell
+ - nolintlint
+ - revive
+ - staticcheck
+ - tenv # Detects using os.Setenv instead of t.Setenv since Go 1.17
+ - unconvert
+ - unused
+ - vet
+ - dupword # Checks for duplicate words in the source code
+ disable:
+ - errcheck
+
+run:
+ timeout: 5m
+ skip-dirs:
+ - api
+ - cluster
+ - design
+ - docs
+ - docs/man
+ - releases
+ - reports
+ - test # e2e scripts
diff --git a/vendor/github.com/opencontainers/go-digest/LICENSE.code b/vendor/github.com/containerd/log/LICENSE
similarity index 99%
rename from vendor/github.com/opencontainers/go-digest/LICENSE.code
rename to vendor/github.com/containerd/log/LICENSE
index 0ea3ff81..584149b6 100644
--- a/vendor/github.com/opencontainers/go-digest/LICENSE.code
+++ b/vendor/github.com/containerd/log/LICENSE
@@ -176,7 +176,7 @@
END OF TERMS AND CONDITIONS
- Copyright 2016 Docker, Inc.
+ Copyright The containerd Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/vendor/github.com/containerd/log/README.md b/vendor/github.com/containerd/log/README.md
new file mode 100644
index 00000000..00e08498
--- /dev/null
+++ b/vendor/github.com/containerd/log/README.md
@@ -0,0 +1,17 @@
+# log
+
+A Go package providing a common logging interface across containerd repositories and a way for clients to use and configure logging in containerd packages.
+
+This package is not intended to be used as a standalone logging package outside of the containerd ecosystem and is intended as an interface wrapper around a logging implementation.
+In the future this package may be replaced with a common go logging interface.
+
+## Project details
+
+**log** is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
+As a containerd sub-project, you will find the:
+ * [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md),
+ * [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS),
+ * and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md)
+
+information in our [`containerd/project`](https://github.com/containerd/project) repository.
+
diff --git a/vendor/github.com/containerd/log/context.go b/vendor/github.com/containerd/log/context.go
new file mode 100644
index 00000000..20153066
--- /dev/null
+++ b/vendor/github.com/containerd/log/context.go
@@ -0,0 +1,182 @@
+/*
+ Copyright The containerd Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+// Package log provides types and functions related to logging, passing
+// loggers through a context, and attaching context to the logger.
+//
+// # Transitional types
+//
+// This package contains various types that are aliases for types in [logrus].
+// These aliases are intended for transitioning away from hard-coding logrus
+// as logging implementation. Consumers of this package are encouraged to use
+// the type-aliases from this package instead of directly using their logrus
+// equivalent.
+//
+// The intent is to replace these aliases with locally defined types and
+// interfaces once all consumers are no longer directly importing logrus
+// types.
+//
+// IMPORTANT: due to the transitional purpose of this package, it is not
+// guaranteed for the full logrus API to be provided in the future. As
+// outlined, these aliases are provided as a step to transition away from
+// a specific implementation which, as a result, exposes the full logrus API.
+// While no decisions have been made on the ultimate design and interface
+// provided by this package, we do not expect carrying "less common" features.
+package log
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/sirupsen/logrus"
+)
+
+// G is a shorthand for [GetLogger].
+//
+// We may want to define this locally to a package to get package tagged log
+// messages.
+var G = GetLogger
+
+// L is an alias for the standard logger.
+var L = &Entry{
+ Logger: logrus.StandardLogger(),
+ // Default is three fields plus a little extra room.
+ Data: make(Fields, 6),
+}
+
+type loggerKey struct{}
+
+// Fields type to pass to "WithFields".
+type Fields = map[string]any
+
+// Entry is a logging entry. It contains all the fields passed with
+// [Entry.WithFields]. It's finally logged when Trace, Debug, Info, Warn,
+// Error, Fatal or Panic is called on it. These objects can be reused and
+// passed around as much as you wish to avoid field duplication.
+//
+// Entry is a transitional type, and currently an alias for [logrus.Entry].
+type Entry = logrus.Entry
+
+// RFC3339NanoFixed is [time.RFC3339Nano] with nanoseconds padded using
+// zeros to ensure the formatted time is always the same number of
+// characters.
+const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
+
+// Level is a logging level.
+type Level = logrus.Level
+
+// Supported log levels.
+const (
+ // TraceLevel level. Designates finer-grained informational events
+ // than [DebugLevel].
+ TraceLevel Level = logrus.TraceLevel
+
+ // DebugLevel level. Usually only enabled when debugging. Very verbose
+ // logging.
+ DebugLevel Level = logrus.DebugLevel
+
+ // InfoLevel level. General operational entries about what's going on
+ // inside the application.
+ InfoLevel Level = logrus.InfoLevel
+
+ // WarnLevel level. Non-critical entries that deserve eyes.
+ WarnLevel Level = logrus.WarnLevel
+
+ // ErrorLevel level. Logs errors that should definitely be noted.
+ // Commonly used for hooks to send errors to an error tracking service.
+ ErrorLevel Level = logrus.ErrorLevel
+
+ // FatalLevel level. Logs and then calls "logger.Exit(1)". It exits
+ // even if the logging level is set to Panic.
+ FatalLevel Level = logrus.FatalLevel
+
+ // PanicLevel level. This is the highest level of severity. Logs and
+ // then calls panic with the message passed to Debug, Info, ...
+ PanicLevel Level = logrus.PanicLevel
+)
+
+// SetLevel sets log level globally. It returns an error if the given
+// level is not supported.
+//
+// level can be one of:
+//
+// - "trace" ([TraceLevel])
+// - "debug" ([DebugLevel])
+// - "info" ([InfoLevel])
+// - "warn" ([WarnLevel])
+// - "error" ([ErrorLevel])
+// - "fatal" ([FatalLevel])
+// - "panic" ([PanicLevel])
+func SetLevel(level string) error {
+ lvl, err := logrus.ParseLevel(level)
+ if err != nil {
+ return err
+ }
+
+ L.Logger.SetLevel(lvl)
+ return nil
+}
+
+// GetLevel returns the current log level.
+func GetLevel() Level {
+ return L.Logger.GetLevel()
+}
+
+// OutputFormat specifies a log output format.
+type OutputFormat string
+
+// Supported log output formats.
+const (
+ // TextFormat represents the text logging format.
+ TextFormat OutputFormat = "text"
+
+ // JSONFormat represents the JSON logging format.
+ JSONFormat OutputFormat = "json"
+)
+
+// SetFormat sets the log output format ([TextFormat] or [JSONFormat]).
+func SetFormat(format OutputFormat) error {
+ switch format {
+ case TextFormat:
+ L.Logger.SetFormatter(&logrus.TextFormatter{
+ TimestampFormat: RFC3339NanoFixed,
+ FullTimestamp: true,
+ })
+ return nil
+ case JSONFormat:
+ L.Logger.SetFormatter(&logrus.JSONFormatter{
+ TimestampFormat: RFC3339NanoFixed,
+ })
+ return nil
+ default:
+ return fmt.Errorf("unknown log format: %s", format)
+ }
+}
+
+// WithLogger returns a new context with the provided logger. Use in
+// combination with logger.WithField(s) for great effect.
+func WithLogger(ctx context.Context, logger *Entry) context.Context {
+ return context.WithValue(ctx, loggerKey{}, logger.WithContext(ctx))
+}
+
+// GetLogger retrieves the current logger from the context. If no logger is
+// available, the default logger is returned.
+func GetLogger(ctx context.Context) *Entry {
+ if logger := ctx.Value(loggerKey{}); logger != nil {
+ return logger.(*Entry)
+ }
+ return L.WithContext(ctx)
+}
diff --git a/vendor/github.com/distribution/reference/.gitattributes b/vendor/github.com/distribution/reference/.gitattributes
new file mode 100644
index 00000000..d207b180
--- /dev/null
+++ b/vendor/github.com/distribution/reference/.gitattributes
@@ -0,0 +1 @@
+*.go text eol=lf
diff --git a/vendor/github.com/distribution/reference/.gitignore b/vendor/github.com/distribution/reference/.gitignore
new file mode 100644
index 00000000..dc07e6b0
--- /dev/null
+++ b/vendor/github.com/distribution/reference/.gitignore
@@ -0,0 +1,2 @@
+# Cover profiles
+*.out
diff --git a/vendor/github.com/distribution/reference/.golangci.yml b/vendor/github.com/distribution/reference/.golangci.yml
new file mode 100644
index 00000000..793f0bb7
--- /dev/null
+++ b/vendor/github.com/distribution/reference/.golangci.yml
@@ -0,0 +1,18 @@
+linters:
+ enable:
+ - bodyclose
+ - dupword # Checks for duplicate words in the source code
+ - gofmt
+ - goimports
+ - ineffassign
+ - misspell
+ - revive
+ - staticcheck
+ - unconvert
+ - unused
+ - vet
+ disable:
+ - errcheck
+
+run:
+ deadline: 2m
diff --git a/vendor/github.com/distribution/reference/CODE-OF-CONDUCT.md b/vendor/github.com/distribution/reference/CODE-OF-CONDUCT.md
new file mode 100644
index 00000000..48f6704c
--- /dev/null
+++ b/vendor/github.com/distribution/reference/CODE-OF-CONDUCT.md
@@ -0,0 +1,5 @@
+# Code of Conduct
+
+We follow the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md).
+
+Please contact the [CNCF Code of Conduct Committee](mailto:conduct@cncf.io) in order to report violations of the Code of Conduct.
diff --git a/vendor/github.com/distribution/reference/CONTRIBUTING.md b/vendor/github.com/distribution/reference/CONTRIBUTING.md
new file mode 100644
index 00000000..ab219466
--- /dev/null
+++ b/vendor/github.com/distribution/reference/CONTRIBUTING.md
@@ -0,0 +1,114 @@
+# Contributing to the reference library
+
+## Community help
+
+If you need help, please ask in the [#distribution](https://cloud-native.slack.com/archives/C01GVR8SY4R) channel on CNCF community slack.
+[Click here for an invite to the CNCF community slack](https://slack.cncf.io/)
+
+## Reporting security issues
+
+The maintainers take security seriously. If you discover a security
+issue, please bring it to their attention right away!
+
+Please **DO NOT** file a public issue, instead send your report privately to
+[cncf-distribution-security@lists.cncf.io](mailto:cncf-distribution-security@lists.cncf.io).
+
+## Reporting an issue properly
+
+By following these simple rules you will get better and faster feedback on your issue.
+
+ - search the bugtracker for an already reported issue
+
+### If you found an issue that describes your problem:
+
+ - please read other user comments first, and confirm this is the same issue: a given error condition might be indicative of different problems - you may also find a workaround in the comments
+ - please refrain from adding "same thing here" or "+1" comments
+ - you don't need to comment on an issue to get notified of updates: just hit the "subscribe" button
+ - comment if you have some new, technical and relevant information to add to the case
+ - __DO NOT__ comment on closed issues or merged PRs. If you think you have a related problem, open up a new issue and reference the PR or issue.
+
+### If you have not found an existing issue that describes your problem:
+
+ 1. create a new issue, with a succinct title that describes your issue:
+ - bad title: "It doesn't work with my docker"
+ - good title: "Private registry push fail: 400 error with E_INVALID_DIGEST"
+ 2. copy the output of (or similar for other container tools):
+ - `docker version`
+ - `docker info`
+ - `docker exec registry --version`
+ 3. copy the command line you used to launch your Registry
+ 4. restart your docker daemon in debug mode (add `-D` to the daemon launch arguments)
+ 5. reproduce your problem and get your docker daemon logs showing the error
+ 6. if relevant, copy your registry logs that show the error
+ 7. provide any relevant detail about your specific Registry configuration (e.g., storage backend used)
+ 8. indicate if you are using an enterprise proxy, Nginx, or anything else between you and your Registry
+
+## Contributing Code
+
+Contributions should be made via pull requests. Pull requests will be reviewed
+by one or more maintainers or reviewers and merged when acceptable.
+
+You should follow the basic GitHub workflow:
+
+ 1. Use your own [fork](https://help.github.com/en/articles/about-forks)
+ 2. Create your [change](https://github.com/containerd/project/blob/master/CONTRIBUTING.md#successful-changes)
+ 3. Test your code
+ 4. [Commit](https://github.com/containerd/project/blob/master/CONTRIBUTING.md#commit-messages) your work, always [sign your commits](https://github.com/containerd/project/blob/master/CONTRIBUTING.md#commit-messages)
+ 5. Push your change to your fork and create a [Pull Request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork)
+
+Refer to [containerd's contribution guide](https://github.com/containerd/project/blob/master/CONTRIBUTING.md#successful-changes)
+for tips on creating a successful contribution.
+
+## Sign your work
+
+The sign-off is a simple line at the end of the explanation for the patch. Your
+signature certifies that you wrote the patch or otherwise have the right to pass
+it on as an open-source patch. The rules are pretty simple: if you can certify
+the below (from [developercertificate.org](http://developercertificate.org/)):
+
+```
+Developer Certificate of Origin
+Version 1.1
+
+Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
+660 York Street, Suite 102,
+San Francisco, CA 94110 USA
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+Developer's Certificate of Origin 1.1
+
+By making a contribution to this project, I certify that:
+
+(a) The contribution was created in whole or in part by me and I
+ have the right to submit it under the open source license
+ indicated in the file; or
+
+(b) The contribution is based upon previous work that, to the best
+ of my knowledge, is covered under an appropriate open source
+ license and I have the right under that license to submit that
+ work with modifications, whether created in whole or in part
+ by me, under the same open source license (unless I am
+ permitted to submit under a different license), as indicated
+ in the file; or
+
+(c) The contribution was provided directly to me by some other
+ person who certified (a), (b) or (c) and I have not modified
+ it.
+
+(d) I understand and agree that this project and the contribution
+ are public and that a record of the contribution (including all
+ personal information I submit with it, including my sign-off) is
+ maintained indefinitely and may be redistributed consistent with
+ this project or the open source license(s) involved.
+```
+
+Then you just add a line to every git commit message:
+
+ Signed-off-by: Joe Smith
+
+Use your real name (sorry, no pseudonyms or anonymous contributions.)
+
+If you set your `user.name` and `user.email` git configs, you can sign your
+commit automatically with `git commit -s`.
diff --git a/vendor/github.com/distribution/reference/GOVERNANCE.md b/vendor/github.com/distribution/reference/GOVERNANCE.md
new file mode 100644
index 00000000..200045b0
--- /dev/null
+++ b/vendor/github.com/distribution/reference/GOVERNANCE.md
@@ -0,0 +1,144 @@
+# distribution/reference Project Governance
+
+Distribution [Code of Conduct](./CODE-OF-CONDUCT.md) can be found here.
+
+For specific guidance on practical contribution steps please
+see our [CONTRIBUTING.md](./CONTRIBUTING.md) guide.
+
+## Maintainership
+
+There are different types of maintainers, with different responsibilities, but
+all maintainers have 3 things in common:
+
+1) They share responsibility in the project's success.
+2) They have made a long-term, recurring time investment to improve the project.
+3) They spend that time doing whatever needs to be done, not necessarily what
+is the most interesting or fun.
+
+Maintainers are often under-appreciated, because their work is harder to appreciate.
+It's easy to appreciate a really cool and technically advanced feature. It's harder
+to appreciate the absence of bugs, the slow but steady improvement in stability,
+or the reliability of a release process. But those things distinguish a good
+project from a great one.
+
+## Reviewers
+
+A reviewer is a core role within the project.
+They share in reviewing issues and pull requests and their LGTM counts towards the
+required LGTM count to merge a code change into the project.
+
+Reviewers are part of the organization but do not have write access.
+Becoming a reviewer is a core aspect in the journey to becoming a maintainer.
+
+## Adding maintainers
+
+Maintainers are first and foremost contributors that have shown they are
+committed to the long term success of a project. Contributors wanting to become
+maintainers are expected to be deeply involved in contributing code, pull
+request review, and triage of issues in the project for more than three months.
+
+Just contributing does not make you a maintainer, it is about building trust
+with the current maintainers of the project and being a person that they can
+depend on and trust to make decisions in the best interest of the project.
+
+Periodically, the existing maintainers curate a list of contributors that have
+shown regular activity on the project over the prior months. From this list,
+maintainer candidates are selected and proposed in a pull request or a
+maintainers communication channel.
+
+After a candidate has been announced to the maintainers, the existing
+maintainers are given five business days to discuss the candidate, raise
+objections and cast their vote. Votes may take place on the communication
+channel or via pull request comment. Candidates must be approved by at least 66%
+of the current maintainers by adding their vote on the mailing list. The
+reviewer role has the same process but only requires 33% of current maintainers.
+Only maintainers of the repository that the candidate is proposed for are
+allowed to vote.
+
+If a candidate is approved, a maintainer will contact the candidate to invite
+the candidate to open a pull request that adds the contributor to the
+MAINTAINERS file. The voting process may take place inside a pull request if a
+maintainer has already discussed the candidacy with the candidate and a
+maintainer is willing to be a sponsor by opening the pull request. The candidate
+becomes a maintainer once the pull request is merged.
+
+## Stepping down policy
+
+Life priorities, interests, and passions can change. If you're a maintainer but
+feel you must remove yourself from the list, inform other maintainers that you
+intend to step down, and if possible, help find someone to pick up your work.
+At the very least, ensure your work can be continued where you left off.
+
+After you've informed other maintainers, create a pull request to remove
+yourself from the MAINTAINERS file.
+
+## Removal of inactive maintainers
+
+Similar to the procedure for adding new maintainers, existing maintainers can
+be removed from the list if they do not show significant activity on the
+project. Periodically, the maintainers review the list of maintainers and their
+activity over the last three months.
+
+If a maintainer has shown insufficient activity over this period, a neutral
+person will contact the maintainer to ask if they want to continue being
+a maintainer. If the maintainer decides to step down as a maintainer, they
+open a pull request to be removed from the MAINTAINERS file.
+
+If the maintainer wants to remain a maintainer, but is unable to perform the
+required duties they can be removed with a vote of at least 66% of the current
+maintainers. In this case, maintainers should first propose the change to
+maintainers via the maintainers communication channel, then open a pull request
+for voting. The voting period is five business days. The voting pull request
+should not come as a surpise to any maintainer and any discussion related to
+performance must not be discussed on the pull request.
+
+## How are decisions made?
+
+Docker distribution is an open-source project with an open design philosophy.
+This means that the repository is the source of truth for EVERY aspect of the
+project, including its philosophy, design, road map, and APIs. *If it's part of
+the project, it's in the repo. If it's in the repo, it's part of the project.*
+
+As a result, all decisions can be expressed as changes to the repository. An
+implementation change is a change to the source code. An API change is a change
+to the API specification. A philosophy change is a change to the philosophy
+manifesto, and so on.
+
+All decisions affecting distribution, big and small, follow the same 3 steps:
+
+* Step 1: Open a pull request. Anyone can do this.
+
+* Step 2: Discuss the pull request. Anyone can do this.
+
+* Step 3: Merge or refuse the pull request. Who does this depends on the nature
+of the pull request and which areas of the project it affects.
+
+## Helping contributors with the DCO
+
+The [DCO or `Sign your work`](./CONTRIBUTING.md#sign-your-work)
+requirement is not intended as a roadblock or speed bump.
+
+Some contributors are not as familiar with `git`, or have used a web
+based editor, and thus asking them to `git commit --amend -s` is not the best
+way forward.
+
+In this case, maintainers can update the commits based on clause (c) of the DCO.
+The most trivial way for a contributor to allow the maintainer to do this, is to
+add a DCO signature in a pull requests's comment, or a maintainer can simply
+note that the change is sufficiently trivial that it does not substantially
+change the existing contribution - i.e., a spelling change.
+
+When you add someone's DCO, please also add your own to keep a log.
+
+## I'm a maintainer. Should I make pull requests too?
+
+Yes. Nobody should ever push to master directly. All changes should be
+made through a pull request.
+
+## Conflict Resolution
+
+If you have a technical dispute that you feel has reached an impasse with a
+subset of the community, any contributor may open an issue, specifically
+calling for a resolution vote of the current core maintainers to resolve the
+dispute. The same voting quorums required (2/3) for adding and removing
+maintainers will apply to conflict resolution.
diff --git a/vendor/github.com/docker/distribution/LICENSE b/vendor/github.com/distribution/reference/LICENSE
similarity index 100%
rename from vendor/github.com/docker/distribution/LICENSE
rename to vendor/github.com/distribution/reference/LICENSE
diff --git a/vendor/github.com/distribution/reference/MAINTAINERS b/vendor/github.com/distribution/reference/MAINTAINERS
new file mode 100644
index 00000000..9e0a60c8
--- /dev/null
+++ b/vendor/github.com/distribution/reference/MAINTAINERS
@@ -0,0 +1,26 @@
+# Distribution project maintainers & reviewers
+#
+# See GOVERNANCE.md for maintainer versus reviewer roles
+#
+# MAINTAINERS (cncf-distribution-maintainers@lists.cncf.io)
+# GitHub ID, Name, Email address
+"chrispat","Chris Patterson","chrispat@github.com"
+"clarkbw","Bryan Clark","clarkbw@github.com"
+"corhere","Cory Snider","csnider@mirantis.com"
+"deleteriousEffect","Hayley Swimelar","hswimelar@gitlab.com"
+"heww","He Weiwei","hweiwei@vmware.com"
+"joaodrp","João Pereira","jpereira@gitlab.com"
+"justincormack","Justin Cormack","justin.cormack@docker.com"
+"squizzi","Kyle Squizzato","ksquizzato@mirantis.com"
+"milosgajdos","Milos Gajdos","milosthegajdos@gmail.com"
+"sargun","Sargun Dhillon","sargun@sargun.me"
+"wy65701436","Wang Yan","wangyan@vmware.com"
+"stevelasker","Steve Lasker","steve.lasker@microsoft.com"
+#
+# REVIEWERS
+# GitHub ID, Name, Email address
+"dmcgowan","Derek McGowan","derek@mcgstyle.net"
+"stevvooe","Stephen Day","stevvooe@gmail.com"
+"thajeztah","Sebastiaan van Stijn","github@gone.nl"
+"DavidSpek", "David van der Spek", "vanderspek.david@gmail.com"
+"Jamstah", "James Hewitt", "james.hewitt@gmail.com"
diff --git a/vendor/github.com/distribution/reference/Makefile b/vendor/github.com/distribution/reference/Makefile
new file mode 100644
index 00000000..c78576b7
--- /dev/null
+++ b/vendor/github.com/distribution/reference/Makefile
@@ -0,0 +1,25 @@
+# Project packages.
+PACKAGES=$(shell go list ./...)
+
+# Flags passed to `go test`
+BUILDFLAGS ?=
+TESTFLAGS ?=
+
+.PHONY: all build test coverage
+.DEFAULT: all
+
+all: build
+
+build: ## no binaries to build, so just check compilation suceeds
+ go build ${BUILDFLAGS} ./...
+
+test: ## run tests
+ go test ${TESTFLAGS} ./...
+
+coverage: ## generate coverprofiles from the unit tests
+ rm -f coverage.txt
+ go test ${TESTFLAGS} -cover -coverprofile=cover.out ./...
+
+.PHONY: help
+help:
+ @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_\/%-]+:.*?##/ { printf " \033[36m%-27s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
diff --git a/vendor/github.com/distribution/reference/README.md b/vendor/github.com/distribution/reference/README.md
new file mode 100644
index 00000000..172a02e0
--- /dev/null
+++ b/vendor/github.com/distribution/reference/README.md
@@ -0,0 +1,30 @@
+# Distribution reference
+
+Go library to handle references to container images.
+
+
+
+[](https://github.com/distribution/reference/actions?query=workflow%3ACI)
+[](https://pkg.go.dev/github.com/distribution/reference)
+[](LICENSE)
+[](https://codecov.io/gh/distribution/reference)
+[](https://app.fossa.com/projects/custom%2B162%2Fgithub.com%2Fdistribution%2Freference?ref=badge_shield)
+
+This repository contains a library for handling references to container images held in container registries. Please see [godoc](https://pkg.go.dev/github.com/distribution/reference) for details.
+
+## Contribution
+
+Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute
+issues, fixes, and patches to this project.
+
+## Communication
+
+For async communication and long running discussions please use issues and pull requests on the github repo.
+This will be the best place to discuss design and implementation.
+
+For sync communication we have a #distribution channel in the [CNCF Slack](https://slack.cncf.io/)
+that everyone is welcome to join and chat about development.
+
+## Licenses
+
+The distribution codebase is released under the [Apache 2.0 license](LICENSE).
diff --git a/vendor/github.com/distribution/reference/SECURITY.md b/vendor/github.com/distribution/reference/SECURITY.md
new file mode 100644
index 00000000..aaf983c0
--- /dev/null
+++ b/vendor/github.com/distribution/reference/SECURITY.md
@@ -0,0 +1,7 @@
+# Security Policy
+
+## Reporting a Vulnerability
+
+The maintainers take security seriously. If you discover a security issue, please bring it to their attention right away!
+
+Please DO NOT file a public issue, instead send your report privately to cncf-distribution-security@lists.cncf.io.
diff --git a/vendor/github.com/distribution/reference/distribution-logo.svg b/vendor/github.com/distribution/reference/distribution-logo.svg
new file mode 100644
index 00000000..cc9f4073
--- /dev/null
+++ b/vendor/github.com/distribution/reference/distribution-logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/vendor/github.com/docker/distribution/reference/helpers.go b/vendor/github.com/distribution/reference/helpers.go
similarity index 94%
rename from vendor/github.com/docker/distribution/reference/helpers.go
rename to vendor/github.com/distribution/reference/helpers.go
index 978df7ea..d10c7ef8 100644
--- a/vendor/github.com/docker/distribution/reference/helpers.go
+++ b/vendor/github.com/distribution/reference/helpers.go
@@ -32,7 +32,7 @@ func FamiliarString(ref Reference) string {
}
// FamiliarMatch reports whether ref matches the specified pattern.
-// See https://godoc.org/path#Match for supported patterns.
+// See [path.Match] for supported patterns.
func FamiliarMatch(pattern string, ref Reference) (bool, error) {
matched, err := path.Match(pattern, FamiliarString(ref))
if namedRef, isNamed := ref.(Named); isNamed && !matched {
diff --git a/vendor/github.com/distribution/reference/normalize.go b/vendor/github.com/distribution/reference/normalize.go
new file mode 100644
index 00000000..f4128314
--- /dev/null
+++ b/vendor/github.com/distribution/reference/normalize.go
@@ -0,0 +1,255 @@
+package reference
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/opencontainers/go-digest"
+)
+
+const (
+ // legacyDefaultDomain is the legacy domain for Docker Hub (which was
+ // originally named "the Docker Index"). This domain is still used for
+ // authentication and image search, which were part of the "v1" Docker
+ // registry specification.
+ //
+ // This domain will continue to be supported, but there are plans to consolidate
+ // legacy domains to new "canonical" domains. Once those domains are decided
+ // on, we must update the normalization functions, but preserve compatibility
+ // with existing installs, clients, and user configuration.
+ legacyDefaultDomain = "index.docker.io"
+
+ // defaultDomain is the default domain used for images on Docker Hub.
+ // It is used to normalize "familiar" names to canonical names, for example,
+ // to convert "ubuntu" to "docker.io/library/ubuntu:latest".
+ //
+ // Note that actual domain of Docker Hub's registry is registry-1.docker.io.
+ // This domain will continue to be supported, but there are plans to consolidate
+ // legacy domains to new "canonical" domains. Once those domains are decided
+ // on, we must update the normalization functions, but preserve compatibility
+ // with existing installs, clients, and user configuration.
+ defaultDomain = "docker.io"
+
+ // officialRepoPrefix is the namespace used for official images on Docker Hub.
+ // It is used to normalize "familiar" names to canonical names, for example,
+ // to convert "ubuntu" to "docker.io/library/ubuntu:latest".
+ officialRepoPrefix = "library/"
+
+ // defaultTag is the default tag if no tag is provided.
+ defaultTag = "latest"
+)
+
+// normalizedNamed represents a name which has been
+// normalized and has a familiar form. A familiar name
+// is what is used in Docker UI. An example normalized
+// name is "docker.io/library/ubuntu" and corresponding
+// familiar name of "ubuntu".
+type normalizedNamed interface {
+ Named
+ Familiar() Named
+}
+
+// ParseNormalizedNamed parses a string into a named reference
+// transforming a familiar name from Docker UI to a fully
+// qualified reference. If the value may be an identifier
+// use ParseAnyReference.
+func ParseNormalizedNamed(s string) (Named, error) {
+ if ok := anchoredIdentifierRegexp.MatchString(s); ok {
+ return nil, fmt.Errorf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings", s)
+ }
+ domain, remainder := splitDockerDomain(s)
+ var remote string
+ if tagSep := strings.IndexRune(remainder, ':'); tagSep > -1 {
+ remote = remainder[:tagSep]
+ } else {
+ remote = remainder
+ }
+ if strings.ToLower(remote) != remote {
+ return nil, fmt.Errorf("invalid reference format: repository name (%s) must be lowercase", remote)
+ }
+
+ ref, err := Parse(domain + "/" + remainder)
+ if err != nil {
+ return nil, err
+ }
+ named, isNamed := ref.(Named)
+ if !isNamed {
+ return nil, fmt.Errorf("reference %s has no name", ref.String())
+ }
+ return named, nil
+}
+
+// namedTaggedDigested is a reference that has both a tag and a digest.
+type namedTaggedDigested interface {
+ NamedTagged
+ Digested
+}
+
+// ParseDockerRef normalizes the image reference following the docker convention,
+// which allows for references to contain both a tag and a digest. It returns a
+// reference that is either tagged or digested. For references containing both
+// a tag and a digest, it returns a digested reference. For example, the following
+// reference:
+//
+// docker.io/library/busybox:latest@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa
+//
+// Is returned as a digested reference (with the ":latest" tag removed):
+//
+// docker.io/library/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa
+//
+// References that are already "tagged" or "digested" are returned unmodified:
+//
+// // Already a digested reference
+// docker.io/library/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa
+//
+// // Already a named reference
+// docker.io/library/busybox:latest
+func ParseDockerRef(ref string) (Named, error) {
+ named, err := ParseNormalizedNamed(ref)
+ if err != nil {
+ return nil, err
+ }
+ if canonical, ok := named.(namedTaggedDigested); ok {
+ // The reference is both tagged and digested; only return digested.
+ newNamed, err := WithName(canonical.Name())
+ if err != nil {
+ return nil, err
+ }
+ return WithDigest(newNamed, canonical.Digest())
+ }
+ return TagNameOnly(named), nil
+}
+
+// splitDockerDomain splits a repository name to domain and remote-name.
+// If no valid domain is found, the default domain is used. Repository name
+// needs to be already validated before.
+func splitDockerDomain(name string) (domain, remoteName string) {
+ maybeDomain, maybeRemoteName, ok := strings.Cut(name, "/")
+ if !ok {
+ // Fast-path for single element ("familiar" names), such as "ubuntu"
+ // or "ubuntu:latest". Familiar names must be handled separately, to
+ // prevent them from being handled as "hostname:port".
+ //
+ // Canonicalize them as "docker.io/library/name[:tag]"
+
+ // FIXME(thaJeztah): account for bare "localhost" or "example.com" names, which SHOULD be considered a domain.
+ return defaultDomain, officialRepoPrefix + name
+ }
+
+ switch {
+ case maybeDomain == localhost:
+ // localhost is a reserved namespace and always considered a domain.
+ domain, remoteName = maybeDomain, maybeRemoteName
+ case maybeDomain == legacyDefaultDomain:
+ // canonicalize the Docker Hub and legacy "Docker Index" domains.
+ domain, remoteName = defaultDomain, maybeRemoteName
+ case strings.ContainsAny(maybeDomain, ".:"):
+ // Likely a domain or IP-address:
+ //
+ // - contains a "." (e.g., "example.com" or "127.0.0.1")
+ // - contains a ":" (e.g., "example:5000", "::1", or "[::1]:5000")
+ domain, remoteName = maybeDomain, maybeRemoteName
+ case strings.ToLower(maybeDomain) != maybeDomain:
+ // Uppercase namespaces are not allowed, so if the first element
+ // is not lowercase, we assume it to be a domain-name.
+ domain, remoteName = maybeDomain, maybeRemoteName
+ default:
+ // None of the above: it's not a domain, so use the default, and
+ // use the name input the remote-name.
+ domain, remoteName = defaultDomain, name
+ }
+
+ if domain == defaultDomain && !strings.ContainsRune(remoteName, '/') {
+ // Canonicalize "familiar" names, but only on Docker Hub, not
+ // on other domains:
+ //
+ // "docker.io/ubuntu[:tag]" => "docker.io/library/ubuntu[:tag]"
+ remoteName = officialRepoPrefix + remoteName
+ }
+
+ return domain, remoteName
+}
+
+// familiarizeName returns a shortened version of the name familiar
+// to the Docker UI. Familiar names have the default domain
+// "docker.io" and "library/" repository prefix removed.
+// For example, "docker.io/library/redis" will have the familiar
+// name "redis" and "docker.io/dmcgowan/myapp" will be "dmcgowan/myapp".
+// Returns a familiarized named only reference.
+func familiarizeName(named namedRepository) repository {
+ repo := repository{
+ domain: named.Domain(),
+ path: named.Path(),
+ }
+
+ if repo.domain == defaultDomain {
+ repo.domain = ""
+ // Handle official repositories which have the pattern "library/"
+ if strings.HasPrefix(repo.path, officialRepoPrefix) {
+ // TODO(thaJeztah): this check may be too strict, as it assumes the
+ // "library/" namespace does not have nested namespaces. While this
+ // is true (currently), technically it would be possible for Docker
+ // Hub to use those (e.g. "library/distros/ubuntu:latest").
+ // See https://github.com/distribution/distribution/pull/3769#issuecomment-1302031785.
+ if remainder := strings.TrimPrefix(repo.path, officialRepoPrefix); !strings.ContainsRune(remainder, '/') {
+ repo.path = remainder
+ }
+ }
+ }
+ return repo
+}
+
+func (r reference) Familiar() Named {
+ return reference{
+ namedRepository: familiarizeName(r.namedRepository),
+ tag: r.tag,
+ digest: r.digest,
+ }
+}
+
+func (r repository) Familiar() Named {
+ return familiarizeName(r)
+}
+
+func (t taggedReference) Familiar() Named {
+ return taggedReference{
+ namedRepository: familiarizeName(t.namedRepository),
+ tag: t.tag,
+ }
+}
+
+func (c canonicalReference) Familiar() Named {
+ return canonicalReference{
+ namedRepository: familiarizeName(c.namedRepository),
+ digest: c.digest,
+ }
+}
+
+// TagNameOnly adds the default tag "latest" to a reference if it only has
+// a repo name.
+func TagNameOnly(ref Named) Named {
+ if IsNameOnly(ref) {
+ namedTagged, err := WithTag(ref, defaultTag)
+ if err != nil {
+ // Default tag must be valid, to create a NamedTagged
+ // type with non-validated input the WithTag function
+ // should be used instead
+ panic(err)
+ }
+ return namedTagged
+ }
+ return ref
+}
+
+// ParseAnyReference parses a reference string as a possible identifier,
+// full digest, or familiar name.
+func ParseAnyReference(ref string) (Reference, error) {
+ if ok := anchoredIdentifierRegexp.MatchString(ref); ok {
+ return digestReference("sha256:" + ref), nil
+ }
+ if dgst, err := digest.Parse(ref); err == nil {
+ return digestReference(dgst), nil
+ }
+
+ return ParseNormalizedNamed(ref)
+}
diff --git a/vendor/github.com/docker/distribution/reference/reference.go b/vendor/github.com/distribution/reference/reference.go
similarity index 88%
rename from vendor/github.com/docker/distribution/reference/reference.go
rename to vendor/github.com/distribution/reference/reference.go
index b7cd00b0..900398bd 100644
--- a/vendor/github.com/docker/distribution/reference/reference.go
+++ b/vendor/github.com/distribution/reference/reference.go
@@ -4,11 +4,14 @@
// Grammar
//
// reference := name [ ":" tag ] [ "@" digest ]
-// name := [domain '/'] path-component ['/' path-component]*
-// domain := domain-component ['.' domain-component]* [':' port-number]
+// name := [domain '/'] remote-name
+// domain := host [':' port-number]
+// host := domain-name | IPv4address | \[ IPv6address \] ; rfc3986 appendix-A
+// domain-name := domain-component ['.' domain-component]*
// domain-component := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
// port-number := /[0-9]+/
// path-component := alpha-numeric [separator alpha-numeric]*
+// path (or "remote-name") := path-component ['/' path-component]*
// alpha-numeric := /[a-z0-9]+/
// separator := /[_.]|__|[-]*/
//
@@ -21,7 +24,6 @@
// digest-hex := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value
//
// identifier := /[a-f0-9]{64}/
-// short-identifier := /[a-f0-9]{6,64}/
package reference
import (
@@ -33,8 +35,13 @@ import (
)
const (
+ // RepositoryNameTotalLengthMax is the maximum total number of characters in a repository name.
+ RepositoryNameTotalLengthMax = 255
+
// NameTotalLengthMax is the maximum total number of characters in a repository name.
- NameTotalLengthMax = 255
+ //
+ // Deprecated: use [RepositoryNameTotalLengthMax] instead.
+ NameTotalLengthMax = RepositoryNameTotalLengthMax
)
var (
@@ -53,8 +60,8 @@ var (
// ErrNameEmpty is returned for empty, invalid repository names.
ErrNameEmpty = errors.New("repository name must have at least one component")
- // ErrNameTooLong is returned when a repository name is longer than NameTotalLengthMax.
- ErrNameTooLong = fmt.Errorf("repository name must not be more than %v characters", NameTotalLengthMax)
+ // ErrNameTooLong is returned when a repository name is longer than RepositoryNameTotalLengthMax.
+ ErrNameTooLong = fmt.Errorf("repository name must not be more than %v characters", RepositoryNameTotalLengthMax)
// ErrNameNotCanonical is returned when a name is not canonical.
ErrNameNotCanonical = errors.New("repository name must be canonical")
@@ -145,7 +152,7 @@ type namedRepository interface {
Path() string
}
-// Domain returns the domain part of the Named reference
+// Domain returns the domain part of the [Named] reference.
func Domain(named Named) string {
if r, ok := named.(namedRepository); ok {
return r.Domain()
@@ -154,7 +161,7 @@ func Domain(named Named) string {
return domain
}
-// Path returns the name without the domain part of the Named reference
+// Path returns the name without the domain part of the [Named] reference.
func Path(named Named) (name string) {
if r, ok := named.(namedRepository); ok {
return r.Path()
@@ -163,6 +170,9 @@ func Path(named Named) (name string) {
return path
}
+// splitDomain splits a named reference into a hostname and path string.
+// If no valid hostname is found, the hostname is empty and the full value
+// is returned as name
func splitDomain(name string) (string, string) {
match := anchoredNameRegexp.FindStringSubmatch(name)
if len(match) != 3 {
@@ -171,21 +181,8 @@ func splitDomain(name string) (string, string) {
return match[1], match[2]
}
-// SplitHostname splits a named reference into a
-// hostname and name string. If no valid hostname is
-// found, the hostname is empty and the full value
-// is returned as name
-// DEPRECATED: Use Domain or Path
-func SplitHostname(named Named) (string, string) {
- if r, ok := named.(namedRepository); ok {
- return r.Domain(), r.Path()
- }
- return splitDomain(named.Name())
-}
-
// Parse parses s and returns a syntactically valid Reference.
// If an error was encountered it is returned, along with a nil Reference.
-// NOTE: Parse will not handle short digests.
func Parse(s string) (Reference, error) {
matches := ReferenceRegexp.FindStringSubmatch(s)
if matches == nil {
@@ -198,10 +195,6 @@ func Parse(s string) (Reference, error) {
return nil, ErrReferenceInvalidFormat
}
- if len(matches[1]) > NameTotalLengthMax {
- return nil, ErrNameTooLong
- }
-
var repo repository
nameMatch := anchoredNameRegexp.FindStringSubmatch(matches[1])
@@ -213,6 +206,10 @@ func Parse(s string) (Reference, error) {
repo.path = matches[1]
}
+ if len(repo.path) > RepositoryNameTotalLengthMax {
+ return nil, ErrNameTooLong
+ }
+
ref := reference{
namedRepository: repo,
tag: matches[2],
@@ -237,7 +234,6 @@ func Parse(s string) (Reference, error) {
// the Named interface. The reference must have a name and be in the canonical
// form, otherwise an error is returned.
// If an error was encountered it is returned, along with a nil Reference.
-// NOTE: ParseNamed will not handle short digests.
func ParseNamed(s string) (Named, error) {
named, err := ParseNormalizedNamed(s)
if err != nil {
@@ -252,14 +248,15 @@ func ParseNamed(s string) (Named, error) {
// WithName returns a named object representing the given string. If the input
// is invalid ErrReferenceInvalidFormat will be returned.
func WithName(name string) (Named, error) {
- if len(name) > NameTotalLengthMax {
- return nil, ErrNameTooLong
- }
-
match := anchoredNameRegexp.FindStringSubmatch(name)
if match == nil || len(match) != 3 {
return nil, ErrReferenceInvalidFormat
}
+
+ if len(match[2]) > RepositoryNameTotalLengthMax {
+ return nil, ErrNameTooLong
+ }
+
return repository{
domain: match[1],
path: match[2],
@@ -320,11 +317,13 @@ func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
// TrimNamed removes any tag or digest from the named reference.
func TrimNamed(ref Named) Named {
- domain, path := SplitHostname(ref)
- return repository{
- domain: domain,
- path: path,
+ repo := repository{}
+ if r, ok := ref.(namedRepository); ok {
+ repo.domain, repo.path = r.Domain(), r.Path()
+ } else {
+ repo.domain, repo.path = splitDomain(ref.Name())
}
+ return repo
}
func getBestReferenceType(ref reference) Reference {
diff --git a/vendor/github.com/distribution/reference/regexp.go b/vendor/github.com/distribution/reference/regexp.go
new file mode 100644
index 00000000..65bc49d7
--- /dev/null
+++ b/vendor/github.com/distribution/reference/regexp.go
@@ -0,0 +1,163 @@
+package reference
+
+import (
+ "regexp"
+ "strings"
+)
+
+// DigestRegexp matches well-formed digests, including algorithm (e.g. "sha256:").
+var DigestRegexp = regexp.MustCompile(digestPat)
+
+// DomainRegexp matches hostname or IP-addresses, optionally including a port
+// number. It defines the structure of potential domain components that may be
+// part of image names. This is purposely a subset of what is allowed by DNS to
+// ensure backwards compatibility with Docker image names. It may be a subset of
+// DNS domain name, an IPv4 address in decimal format, or an IPv6 address between
+// square brackets (excluding zone identifiers as defined by [RFC 6874] or special
+// addresses such as IPv4-Mapped).
+//
+// [RFC 6874]: https://www.rfc-editor.org/rfc/rfc6874.
+var DomainRegexp = regexp.MustCompile(domainAndPort)
+
+// IdentifierRegexp is the format for string identifier used as a
+// content addressable identifier using sha256. These identifiers
+// are like digests without the algorithm, since sha256 is used.
+var IdentifierRegexp = regexp.MustCompile(identifier)
+
+// NameRegexp is the format for the name component of references, including
+// an optional domain and port, but without tag or digest suffix.
+var NameRegexp = regexp.MustCompile(namePat)
+
+// ReferenceRegexp is the full supported format of a reference. The regexp
+// is anchored and has capturing groups for name, tag, and digest
+// components.
+var ReferenceRegexp = regexp.MustCompile(referencePat)
+
+// TagRegexp matches valid tag names. From [docker/docker:graph/tags.go].
+//
+// [docker/docker:graph/tags.go]: https://github.com/moby/moby/blob/v1.6.0/graph/tags.go#L26-L28
+var TagRegexp = regexp.MustCompile(tag)
+
+const (
+ // alphanumeric defines the alphanumeric atom, typically a
+ // component of names. This only allows lower case characters and digits.
+ alphanumeric = `[a-z0-9]+`
+
+ // separator defines the separators allowed to be embedded in name
+ // components. This allows one period, one or two underscore and multiple
+ // dashes. Repeated dashes and underscores are intentionally treated
+ // differently. In order to support valid hostnames as name components,
+ // supporting repeated dash was added. Additionally double underscore is
+ // now allowed as a separator to loosen the restriction for previously
+ // supported names.
+ separator = `(?:[._]|__|[-]+)`
+
+ // localhost is treated as a special value for domain-name. Any other
+ // domain-name without a "." or a ":port" are considered a path component.
+ localhost = `localhost`
+
+ // domainNameComponent restricts the registry domain component of a
+ // repository name to start with a component as defined by DomainRegexp.
+ domainNameComponent = `(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`
+
+ // optionalPort matches an optional port-number including the port separator
+ // (e.g. ":80").
+ optionalPort = `(?::[0-9]+)?`
+
+ // tag matches valid tag names. From docker/docker:graph/tags.go.
+ tag = `[\w][\w.-]{0,127}`
+
+ // digestPat matches well-formed digests, including algorithm (e.g. "sha256:").
+ //
+ // TODO(thaJeztah): this should follow the same rules as https://pkg.go.dev/github.com/opencontainers/go-digest@v1.0.0#DigestRegexp
+ // so that go-digest defines the canonical format. Note that the go-digest is
+ // more relaxed:
+ // - it allows multiple algorithms (e.g. "sha256+b64:") to allow
+ // future expansion of supported algorithms.
+ // - it allows the "" value to use urlsafe base64 encoding as defined
+ // in [rfc4648, section 5].
+ //
+ // [rfc4648, section 5]: https://www.rfc-editor.org/rfc/rfc4648#section-5.
+ digestPat = `[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`
+
+ // identifier is the format for a content addressable identifier using sha256.
+ // These identifiers are like digests without the algorithm, since sha256 is used.
+ identifier = `([a-f0-9]{64})`
+
+ // ipv6address are enclosed between square brackets and may be represented
+ // in many ways, see rfc5952. Only IPv6 in compressed or uncompressed format
+ // are allowed, IPv6 zone identifiers (rfc6874) or Special addresses such as
+ // IPv4-Mapped are deliberately excluded.
+ ipv6address = `\[(?:[a-fA-F0-9:]+)\]`
+)
+
+var (
+ // domainName defines the structure of potential domain components
+ // that may be part of image names. This is purposely a subset of what is
+ // allowed by DNS to ensure backwards compatibility with Docker image
+ // names. This includes IPv4 addresses on decimal format.
+ domainName = domainNameComponent + anyTimes(`\.`+domainNameComponent)
+
+ // host defines the structure of potential domains based on the URI
+ // Host subcomponent on rfc3986. It may be a subset of DNS domain name,
+ // or an IPv4 address in decimal format, or an IPv6 address between square
+ // brackets (excluding zone identifiers as defined by rfc6874 or special
+ // addresses such as IPv4-Mapped).
+ host = `(?:` + domainName + `|` + ipv6address + `)`
+
+ // allowed by the URI Host subcomponent on rfc3986 to ensure backwards
+ // compatibility with Docker image names.
+ domainAndPort = host + optionalPort
+
+ // anchoredTagRegexp matches valid tag names, anchored at the start and
+ // end of the matched string.
+ anchoredTagRegexp = regexp.MustCompile(anchored(tag))
+
+ // anchoredDigestRegexp matches valid digests, anchored at the start and
+ // end of the matched string.
+ anchoredDigestRegexp = regexp.MustCompile(anchored(digestPat))
+
+ // pathComponent restricts path-components to start with an alphanumeric
+ // character, with following parts able to be separated by a separator
+ // (one period, one or two underscore and multiple dashes).
+ pathComponent = alphanumeric + anyTimes(separator+alphanumeric)
+
+ // remoteName matches the remote-name of a repository. It consists of one
+ // or more forward slash (/) delimited path-components:
+ //
+ // pathComponent[[/pathComponent] ...] // e.g., "library/ubuntu"
+ remoteName = pathComponent + anyTimes(`/`+pathComponent)
+ namePat = optional(domainAndPort+`/`) + remoteName
+
+ // anchoredNameRegexp is used to parse a name value, capturing the
+ // domain and trailing components.
+ anchoredNameRegexp = regexp.MustCompile(anchored(optional(capture(domainAndPort), `/`), capture(remoteName)))
+
+ referencePat = anchored(capture(namePat), optional(`:`, capture(tag)), optional(`@`, capture(digestPat)))
+
+ // anchoredIdentifierRegexp is used to check or match an
+ // identifier value, anchored at start and end of string.
+ anchoredIdentifierRegexp = regexp.MustCompile(anchored(identifier))
+)
+
+// optional wraps the expression in a non-capturing group and makes the
+// production optional.
+func optional(res ...string) string {
+ return `(?:` + strings.Join(res, "") + `)?`
+}
+
+// anyTimes wraps the expression in a non-capturing group that can occur
+// any number of times.
+func anyTimes(res ...string) string {
+ return `(?:` + strings.Join(res, "") + `)*`
+}
+
+// capture wraps the expression in a capturing group.
+func capture(res ...string) string {
+ return `(` + strings.Join(res, "") + `)`
+}
+
+// anchored anchors the regular expression by adding start and end delimiters.
+func anchored(res ...string) string {
+ return `^` + strings.Join(res, "") + `$`
+}
diff --git a/vendor/github.com/distribution/reference/sort.go b/vendor/github.com/distribution/reference/sort.go
new file mode 100644
index 00000000..416c37b0
--- /dev/null
+++ b/vendor/github.com/distribution/reference/sort.go
@@ -0,0 +1,75 @@
+/*
+ Copyright The containerd Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package reference
+
+import (
+ "sort"
+)
+
+// Sort sorts string references preferring higher information references.
+//
+// The precedence is as follows:
+//
+// 1. [Named] + [Tagged] + [Digested] (e.g., "docker.io/library/busybox:latest@sha256:")
+// 2. [Named] + [Tagged] (e.g., "docker.io/library/busybox:latest")
+// 3. [Named] + [Digested] (e.g., "docker.io/library/busybo@sha256:")
+// 4. [Named] (e.g., "docker.io/library/busybox")
+// 5. [Digested] (e.g., "docker.io@sha256:")
+// 6. Parse error
+func Sort(references []string) []string {
+ var prefs []Reference
+ var bad []string
+
+ for _, ref := range references {
+ pref, err := ParseAnyReference(ref)
+ if err != nil {
+ bad = append(bad, ref)
+ } else {
+ prefs = append(prefs, pref)
+ }
+ }
+ sort.Slice(prefs, func(a, b int) bool {
+ ar := refRank(prefs[a])
+ br := refRank(prefs[b])
+ if ar == br {
+ return prefs[a].String() < prefs[b].String()
+ }
+ return ar < br
+ })
+ sort.Strings(bad)
+ var refs []string
+ for _, pref := range prefs {
+ refs = append(refs, pref.String())
+ }
+ return append(refs, bad...)
+}
+
+func refRank(ref Reference) uint8 {
+ if _, ok := ref.(Named); ok {
+ if _, ok = ref.(Tagged); ok {
+ if _, ok = ref.(Digested); ok {
+ return 1
+ }
+ return 2
+ }
+ if _, ok = ref.(Digested); ok {
+ return 3
+ }
+ return 4
+ }
+ return 5
+}
diff --git a/vendor/github.com/docker/cli/AUTHORS b/vendor/github.com/docker/cli/AUTHORS
index 8990f85b..ad1abd49 100644
--- a/vendor/github.com/docker/cli/AUTHORS
+++ b/vendor/github.com/docker/cli/AUTHORS
@@ -1,9 +1,11 @@
-# This file lists all individuals having contributed content to the repository.
-# For how it is generated, see `scripts/docs/generate-authors.sh`.
+# File @generated by scripts/docs/generate-authors.sh. DO NOT EDIT.
+# This file lists all contributors to the repository.
+# See scripts/docs/generate-authors.sh to make modifications.
+A. Lester Buck III
Aanand Prasad
Aaron L. Xu
-Aaron Lehmann
+Aaron Lehmann
Aaron.L.Xu
Abdur Rehman
Abhinandan Prativadi
@@ -15,6 +17,7 @@ Adolfo Ochagavía
Adrian Plata
Adrien Duermael
Adrien Folie
+Adyanth Hosavalike
Ahmet Alp Balkan
Aidan Feldman
Aidan Hobson Sayers
@@ -23,23 +26,31 @@ Akhil Mohan
Akihiro Suda
Akim Demaille
Alan Thompson
+Alano Terblanche
Albert Callarisa
-Albin Kerouanton
+Alberto Roura
+Albin Kerouanton
Aleksa Sarai
Aleksander Piotrowski
Alessandro Boch
+Alex Couture-Beil
Alex Mavrogiannis
Alex Mayer
Alexander Boyd
+Alexander Chneerov
Alexander Larsson
-Alexander Morozov
+Alexander Morozov
Alexander Ryabov
Alexandre González
+Alexey Igrychev
+Alexis Couvreur
Alfred Landrum
+Ali Rostami
Alicia Lauerman
Allen Sun
Alvin Deng
Amen Belayneh
+Amey Shrivastava <72866602+AmeyShrivastava@users.noreply.github.com>
Amir Goldstein
Amit Krishnan
Amit Shukla
@@ -48,11 +59,14 @@ Anca Iordache
Anda Xu
Andrea Luzzardi
Andreas Köhler
+Andres G. Aragoneses
+Andres Leon Rangel
Andrew France
Andrew Hsu
Andrew Macpherson
Andrew McDonnell
Andrew Po
+Andrew-Zipperer
Andrey Petrov
Andrii Berehuliak
André Martins
@@ -67,27 +81,37 @@ Antonis Kalipetis
Anusha Ragunathan
Ao Li
Arash Deshmeh
-Arko Dasgupta
-Arnaud Porterie
+Arko Dasgupta
+Arnaud Porterie
+Arnaud Rebillout
Arthur Peka
+Ashly Mathew
Ashwini Oruganti
+Aslam Ahemad
Azat Khuyiyakhmetov
Bardia Keyoumarsi
Barnaby Gray
Bastiaan Bakker
BastianHofmann
+Ben Bodenmiller
Ben Bonnefoy
Ben Creasy
Ben Firshman
Benjamin Boudreau
+Benjamin Böhmke
+Benjamin Nater
Benoit Sigoure
Bhumika Bayani
Bill Wang
Bin Liu
Bingshen Wang
+Bishal Das
+Bjorn Neergaard
Boaz Shuster
+Boban Acimovic
Bogdan Anton
Boris Pruessmann
+Brad Baker
Bradley Cicenas
Brandon Mitchell
Brandon Philips
@@ -95,16 +119,20 @@ Brent Salisbury
Bret Fisher
Brian (bex) Exelbierd
Brian Goff
+Brian Tracy
Brian Wieder
+Bruno Sousa
Bryan Bess
Bryan Boreham
Bryan Murphy
bryfry
+Calvin Liu
Cameron Spear
Cao Weiwei
Carlo Mion
Carlos Alexandro Becker
Carlos de Paula
+Casey Korver
Ce Gao
Cedric Davies
Cezar Sa Espinola
@@ -114,15 +142,20 @@ Charles Chan
Charles Law
Charles Smith
Charlie Drage
+Charlotte Mach
ChaYoung You
+Chee Hau Lim
Chen Chuanliang
Chen Hanxiao
Chen Mingjie
Chen Qiu
+Chris Chinchilla
+Chris Couzens
Chris Gavin
Chris Gibson
Chris McKinnel
Chris Snow
+Chris Vermilion
Chris Weyl
Christian Persson
Christian Stefanescu
@@ -131,6 +164,9 @@ Christophe Vidal
Christopher Biscardi
Christopher Crone
Christopher Jones
+Christopher Petito <47751006+krissetto@users.noreply.github.com>
+Christopher Petito
+Christopher Svensson
Christy Norman
Chun Chen
Clinton Kitson
@@ -139,8 +175,12 @@ Colin Hebert
Collin Guarino
Colm Hally
Comical Derskeal <27731088+derskeal@users.noreply.github.com>
+Conner Crosby
Corey Farrell
Corey Quon
+Cory Bennet
+Cory Snider
+Craig Osterhout
Craig Wilhite
Cristian Staretu
Daehyeok Mun
@@ -149,6 +189,7 @@ Daisuke Ito
dalanlan
Damien Nadé
Dan Cotora
+Danial Gharib
Daniel Artine
Daniel Cassidy
Daniel Dao
@@ -170,11 +211,14 @@ Dattatraya Kumbhar
Dave Goodchild
Dave Henderson
Dave Tucker
+David Alvarez
David Beitey
David Calavera
David Cramer
David Dooling
David Gageot
+David Karlsson
+David le Blanc
David Lechner
David Scott
David Sheets
@@ -186,7 +230,9 @@ Denis Defreyne
Denis Gladkikh
Denis Ollier
Dennis Docter
-Derek McGowan
+dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
+Derek McGowan
+Des Preston
Deshi Xiao
Dharmit Shah
Dhawal Yogesh Bhanushali
@@ -196,27 +242,33 @@ Dimitry Andric
Ding Fei
Diogo Monica
Djordje Lukic
+Dmitriy Fishman
Dmitry Gusev
Dmitry Smirnov
Dmitry V. Krivenok
Dominik Braun
Don Kjer
Dong Chen
+DongGeon Lee
Doug Davis
Drew Erny
Ed Costello
+Ed Morley <501702+edmorley@users.noreply.github.com>
Elango Sivanandam
Eli Uriegas
Eli Uriegas
Elias Faxö
Elliot Luo <956941328@qq.com>
+Eric Bode
Eric Curtin
+Eric Engestrom
Eric G. Noriega
Eric Rosenberg
Eric Sage
Eric-Olivier Lamey
Erica Windisch
Erik Hollensbe
+Erik Humphrey
Erik St. Martin
Essam A. Hassan
Ethan Haynes
@@ -225,12 +277,15 @@ Eugene Yakubovich
Evan Allrich
Evan Hazlett
Evan Krall
+Evan Lezar
Evelyn Xu
Everett Toews
Fabio Falci
Fabrizio Soppelsa
+Felix Geyer
Felix Hupfeld
Felix Rabe
+fezzik1620
Filip Jareš